Compare commits

...

18 Commits

Author SHA1 Message Date
Yunfan Li
8d09a08dfd Preparation for project transfer 2025-10-14 00:05:16 +08:00
kadeshar
525eceb5a2 Merge pull request #1728 from Wishmaster117/Fix-Opcode-dispatch,-trusts-every-queued-packet-to-have-a-handler
Fix Opcode dispatch, trusts every queued packet to have a handler
2025-10-11 13:22:51 +02:00
kadeshar
bd13d6be80 Merge pull request #1727 from Wishmaster117/Prevent-Crash-if-sTaxiPathStore.LookupEntry-return-nullptr
Prevent Crash if sTaxiPathStore.LookupEntry return nullptr
2025-10-11 13:21:51 +02:00
Wishmaster117
d0ac9452f4 Fix Opcode dispatch, trusts every queued packet to have a handler 2025-10-11 11:26:09 +02:00
Wishmaster117
8a30d10617 Prevent Crash if sTaxiPathStore.LookupEntry return nullptr 2025-10-11 10:57:54 +02:00
kadeshar
5a0c27637e Merge pull request #1708 from hermensbas/feature/removeFromGroup_replaced_with_worldpackets
[fix crash] Crash on removeFromGroup
2025-10-10 19:59:21 +02:00
kadeshar
cea1e90f57 Merge pull request #1714 from avirar/fix/remove-auras-before-teleport
[fix crash] several crashes
2025-10-10 19:43:34 +02:00
kadeshar
1fb66e9d75 - Fixed issues in ai_playerbot_texts table scripts (#1723) 2025-10-09 23:45:58 +02:00
bash
31ed5cbb65 fixes 2025-10-09 20:52:32 +02:00
kadeshar
5b128b3300 - Update method in QueryItemUsageForEquip (#1701) 2025-10-09 08:02:44 +02:00
avirar
5681f29060 Merge branch 'liyunfan1223:master' into fix/remove-auras-before-teleport 2025-10-07 11:13:57 +11:00
bash
cf4f0f6dc7 renamed function name 2025-10-06 21:07:43 +02:00
root
c90b155a70 fix: Replace static m_botReleaseTimes with per-bot storage to prevent race condition
Fixes a thread safety issue where multiple bots dying in battlegrounds
simultaneously would corrupt the shared static unordered_map, causing
segmentation faults.

Changes:
- Remove: static m_botReleaseTimes map from AutoReleaseSpiritAction
- Add: bgReleaseAttemptTime member to PlayerbotAI (per-bot storage)
- Update: All references to use per-bot storage instead of static map

Why this fixes the crash:
- Each PlayerbotAI instance is accessed by only one map update thread
- No cross-thread access to shared data structures
- No mutex/locking required - thread-safe by design
- Automatic cleanup when bot is destroyed

Thread-safe solution: Per-bot state eliminates race conditions without
performance overhead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-06 11:22:45 +11:00
bash
780f6d60e0 fix build errors 2025-10-05 23:49:13 +02:00
bash
1faf20f567 removeFromGroup replaced with worldpackets 2025-10-05 20:05:56 +02:00
root
d26c2a3549 fix: Clean visibility references before bot teleport to prevent crash
Add PLAYERHOOK_ON_BEFORE_TELEPORT to proactively clean visibility
references when a bot teleports between maps. This prevents a race
condition where:
1. Bot A teleports and its visible objects start getting cleaned up
2. Bot B is simultaneously updating visibility and tries to access
   objects in Bot A's old visibility map
3. Those objects may already be freed, causing a segmentation fault
   at GridNotifiers.cpp:65 in IsWorldObjectOutOfSightRange()

The fix only affects bots to avoid changing behavior for real players.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 16:21:09 +11:00
avirar
21ea3a7226 Merge branch 'liyunfan1223:master' into fix/remove-auras-before-teleport 2025-10-04 09:42:21 +10:00
root
387c491265 fix(Playerbots): Remove auras before teleporting to prevent crash
Add RemoveAurasWithInterruptFlags call before all TeleportTo operations
to prevent race condition crash in battlegrounds.

The crash occurs when area auras (like "Entering Battleground") are
queued for removal in Aura::UpdateTargetMap's targetsToRemove list,
but the unit is deleted before the 500ms update cycle completes,
causing SIGSEGV when accessing the dangling pointer.

This fix removes auras with AURA_INTERRUPT_FLAG_TELEPORTED and
AURA_INTERRUPT_FLAG_CHANGE_MAP before teleporting, matching the
behavior in Player::TeleportTo for real players.

Affected locations:
- BattleGround join/teleport
- Spirit healer/graveyard teleport
- Corpse resurrection teleport
- Meeting stone teleport
- Master follow teleport
- RPG unstuck teleport
- Random bot teleport
- Chat command teleport

Raid-specific teleports excluded as they require separate testing.
2025-10-03 15:58:36 +10:00
24 changed files with 2131 additions and 98 deletions

View File

@@ -38,7 +38,7 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Set reusable strings
@@ -50,7 +50,7 @@ jobs:
- name: Checkout Playerbot Module
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: Cache

View File

@@ -22,12 +22,12 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v4
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Checkout Playerbot Module
uses: actions/checkout@v4
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: Cache
uses: actions/cache@v4

View File

@@ -23,12 +23,12 @@ jobs:
- name: Checkout AzerothCore
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/azerothcore-wotlk'
repository: 'mod-playerbots/azerothcore-wotlk'
ref: 'Playerbot'
- name: Checkout Playerbot Module
uses: actions/checkout@v3
with:
repository: 'liyunfan1223/mod-playerbots'
repository: 'mod-playerbots/mod-playerbots'
path: 'modules/mod-playerbots'
- name: ccache
uses: hendrikmuhs/ccache-action@v1.2.13

View File

@@ -1,7 +1,7 @@
<p align="center">
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README.md">English</a>
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README.md">English</a>
|
<a href="https://github.com/liyunfan1223/mod-playerbots/blob/master/README_CN.md">中文</a>
<a href="https://github.com/mod-playerbots/mod-playerbots/blob/master/README_CN.md">中文</a>
|
<a href="https://github.com/brighton-chi/mod-playerbots/blob/readme/README_ES.md">Español</a>
</p>
@@ -12,13 +12,13 @@
</div>
<div align="center">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
<img src="https://github.com/liyunfan1223/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/macos_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/core_build.yml/badge.svg">
<img src="https://github.com/mod-playerbots/mod-playerbots/actions/workflows/windows_build.yml/badge.svg">
</div>
# Playerbots Module
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot).
`mod-playerbots` is an [AzerothCore](https://www.azerothcore.org/) module that adds player-like bots to a server. The project is based off [IKE3's Playerbots](https://github.com/ike3/mangosbot) and requires a custom branch of AzerothCore to compile and run: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot).
Features include:
@@ -28,7 +28,7 @@ Features include:
- Highly configurable settings to define how bots behave;
- Excellent performance, even when running thousands of bots.
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/liyunfan1223/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
**This project is still under development**. If you encounter any errors or experience crashes, we kindly request that you [report them as GitHub issues](https://github.com/mod-playerbots/mod-playerbots/issues/new?template=bug_report.md). Your valuable feedback will help us improve this project collaboratively.
`mod-playerbots` has a **[Discord server](https://discord.gg/NQm5QShwf9)** where you can discuss the project, ask questions, and get involved in the community!
@@ -36,12 +36,12 @@ Features include:
### Classic Installation
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
As noted above, `mod-playerbots` requires a custom branch of AzerothCore: [mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). To install the module, simply run:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
For more information, refer to the [AzerothCore Installation Guide](https://www.azerothcore.org/wiki/installation) and [Installing a Module](https://www.azerothcore.org/wiki/installing-a-module) pages.
@@ -51,9 +51,9 @@ For more information, refer to the [AzerothCore Installation Guide](https://www.
**Docker installation is considered experimental.** To install the module on a Docker installation, run:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
Afterwards, create a `docker-compose.override.yml` file in the `azerothcore-wotlk` directory. This override file allows for mounting the modules directory to the `ac-worldserver` service which is required for it to run. Put the following inside and save:
@@ -77,21 +77,21 @@ services:
- ./modules:/azerothcore/modules:ro
```
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/liyunfan1223/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
For example, to double the experience gain rate per kill, take the setting `Rate.XP.Kill = 1` from [woldserver.conf](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/src/server/apps/worldserver/worldserver.conf.dist), convert it to an environment variable, and change it to the desired setting in the override file to get `AC_RATE_XP_KILL: "2"`. If you wanted to disable random bots from logging in automatically, take the `AiPlayerbot.RandomBotAutologin = 1` setting from [playerbots.conf](https://github.com/mod-playerbots/mod-playerbots/blob/master/conf/playerbots.conf.dist) and do the same to get `AC_AI_PLAYERBOT_RANDOM_BOT_AUTOLOGIN: "0"`. For more information on how to configure Azerothcore, Playerbots, and other module settings as environment variables in Docker Compose, see the "Configuring AzerothCore in Containers" section in the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) guide.
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/liyunfan1223/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
Before building, consider setting the database password. One way to do this is to create a `.env` file in the root `azerothcore-wotlk` directory using the [template](https://github.com/mod-playerbots/azerothcore-wotlk/blob/Playerbot/conf/dist/env.docker). This file also allows you to set the user and group Docker uses for the services in case you run into any permissions issues, which are the most common cause for Docker installation problems.
Use `docker compose up -d --build` to build and run the server. For more information, including how to create an account and taking backups, refer to the [Install With Docker](https://www.azerothcore.org/wiki/install-with-docker) page.
## Documentation
The [Playerbots Wiki](https://github.com/liyunfan1223/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
The [Playerbots Wiki](https://github.com/mod-playerbots/mod-playerbots/wiki) contains an extensive overview of addons, commands, raids with programmed bot strategies, and recommended performance configurations. Please note that documentation may be incomplete or out-of-date in some sections. Contributions are welcome.
## Frequently Asked Questions
- **Why aren't my bots casting spells?** Please make sure that the necessary English DBC file (enUS) is present.
- **What platforms are supported?** We support Ubuntu, Windows, and macOS. Other Linux distros may work, but will not receive support.
- **Why isn't my source compiling?** Please ensure that you are compiling with the required [custom branch of AzerothCore](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/liyunfan1223/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
- **Why isn't my source compiling?** Please ensure that you are compiling with the required [custom branch of AzerothCore](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot). Additionally, please [check the build status of our CI](https://github.com/mod-playerbots/mod-playerbots/actions). If the latest build is failing, rever to the last successful commit until we address the issue.
## Addons
@@ -107,6 +107,6 @@ Typically, bots are controlled via chat commands. For larger bot groups, this ca
Also, a thank you to the many contributors who've helped build this project:
<a href="https://github.com/liyunfan1223/mod-playerbots/graphs/contributors">
<img src="https://contrib.rocks/image?repo=liyunfan1223/mod-playerbots" />
<a href="https://github.com/mod-playerbots/mod-playerbots/graphs/contributors">
<img src="https://contrib.rocks/image?repo=mod-playerbots/mod-playerbots" />
</a>

View File

@@ -8,16 +8,16 @@
## 安装
请注意此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性您必须使用我fork的自定义分支来编译它可以在这里找到[liyunfan1223/azerothcore-wotlk/tree/Playerbot](https://github.com/liyunfan1223/azerothcore-wotlk/tree/Playerbot)。
请注意此模块需要对AzerothCore进行特定的自定义更改。为了确保兼容性您必须使用我fork的自定义分支来编译它可以在这里找到[mod-playerbots/azerothcore-wotlk/tree/Playerbot](https://github.com/mod-playerbots/azerothcore-wotlk/tree/Playerbot)。
要安装此模块请参考AzerothCore Wiki的详细说明[AzerothCore安装指南](https://www.azerothcore.org/wiki/installation)。
我们提供了一个简单的方法来克隆该模块:
```bash
git clone https://github.com/liyunfan1223/azerothcore-wotlk.git --branch=Playerbot
git clone https://github.com/mod-playerbots/azerothcore-wotlk.git --branch=Playerbot
cd azerothcore-wotlk/modules
git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
git clone https://github.com/mod-playerbots/mod-playerbots.git --branch=master
```
## 快速开始与文档
@@ -60,7 +60,7 @@ git clone https://github.com/liyunfan1223/mod-playerbots.git --branch=master
- 我们支持Ubuntu、Windows和macOS。
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/liyunfan1223/mod-playerbots/actions)中查看构建状态。
- 我们建立了持续集成工作流。您可以在[GitHub Actions](https://github.com/mod-playerbots/mod-playerbots/actions)中查看构建状态。
- 如果最新的构建状态失败,请恢复到上一个提交。我们将尽快解决此问题。

View File

@@ -5,7 +5,7 @@
# Overview
# "Randombot": randomly generated bots that log in separately from players and populate the world. Depending on settings, randombots may automatically grind, quest, and upgrade equipment and can be invited to groups and given commands.
# "Altbot": characters created on player accounts, which may be logged in by the player and invited to groups and given commands like randombots. Depending on settings, altbots can be limited to characters on the active player's account or in the active player's guild.
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/liyunfan1223/mod-playerbots/wiki/Playerbot-Commands.
# Information about commands to control bots and set their strategies can be found on the wiki at https://github.com/mod-playerbots/mod-playerbots/wiki/Playerbot-Commands.
####################################################################################################
# SECTION INDEX

File diff suppressed because it is too large Load Diff

View File

@@ -378,10 +378,7 @@ void PlayerbotAI::UpdateAIGroupMembership()
PlayerbotAI* leaderAI = GET_PLAYERBOT_AI(leader);
if (leaderAI && !leaderAI->IsRealPlayer())
{
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
bot->GetSession()->QueuePacket(packet);
// bot->RemoveFromGroup();
ResetStrategies();
LeaveOrDisbandGroup();
}
}
}
@@ -405,10 +402,7 @@ void PlayerbotAI::UpdateAIGroupMembership()
}
if (!hasRealPlayer)
{
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
bot->GetSession()->QueuePacket(packet);
// bot->RemoveFromGroup();
ResetStrategies();
LeaveOrDisbandGroup();
}
}
}
@@ -791,6 +785,16 @@ void PlayerbotAI::Reset(bool full)
}
}
void PlayerbotAI::LeaveOrDisbandGroup()
{
if (!bot || !bot->GetGroup() || IsRealPlayer())
return;
WorldPacket* packet = new WorldPacket(CMSG_GROUP_DISBAND);
bot->GetSession()->QueuePacket(packet);
ResetStrategies();
}
bool PlayerbotAI::IsAllowedCommand(std::string const text)
{
if (unsecuredCommands.empty())

View File

@@ -415,6 +415,7 @@ public:
void ResetStrategies(bool load = false);
void ReInitCurrentEngine();
void Reset(bool full = false);
void LeaveOrDisbandGroup();
static bool IsTank(Player* player, bool bySpec = false);
static bool IsHeal(Player* player, bool bySpec = false);
static bool IsDps(Player* player, bool bySpec = false);
@@ -601,6 +602,7 @@ public:
NewRpgInfo rpgInfo;
NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest;
time_t bgReleaseAttemptTime = 0;
// Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs);

View File

@@ -227,6 +227,12 @@ void PlayerbotHolder::HandleBotPackets(WorldSession* session)
{
OpcodeClient opcode = static_cast<OpcodeClient>(packet->GetOpcode());
ClientOpcodeHandler const* opHandle = opcodeTable[opcode];
if (!opHandle)
{
LOG_ERROR("playerbots", "Unhandled opcode {} queued for bot session {}. Packet dropped.", static_cast<uint32>(opcode), session->GetAccountId());
delete packet;
continue;
}
opHandle->Call(session, *packet);
delete packet;
}
@@ -514,7 +520,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (!groupValid)
{
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
}
}

View File

@@ -87,7 +87,8 @@ public:
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
PLAYERHOOK_ON_GIVE_EXP
PLAYERHOOK_ON_GIVE_EXP,
PLAYERHOOK_ON_BEFORE_TELEPORT
}) {}
void OnPlayerLogin(Player* player) override
@@ -105,7 +106,7 @@ public:
{
ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
"|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r");
"|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r");
}
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
@@ -121,6 +122,26 @@ public:
}
}
bool OnPlayerBeforeTeleport(Player* player, uint32 mapid, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
{
// Only apply to bots to prevent affecting real players
if (!player || !player->GetSession()->IsBot())
return true;
// If changing maps, proactively clean visibility references to prevent
// stale pointers in other players' visibility maps during the teleport.
// This fixes a race condition where:
// 1. Bot A teleports and its visible objects start getting cleaned up
// 2. Bot B is simultaneously updating visibility and tries to access objects in Bot A's old visibility map
// 3. Those objects may already be freed, causing a segmentation fault
if (player->GetMapId() != mapid && player->IsInWorld())
{
player->GetObjectVisibilityContainer().CleanVisibilityReferences();
}
return true; // Allow teleport to continue
}
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
@@ -295,7 +316,7 @@ public:
LOG_INFO("server.loading", "║ mod-playerbots is a community-driven open-source ║");
LOG_INFO("server.loading", "║ project based on AzerothCore, licensed under AGPLv3.0 ║");
LOG_INFO("server.loading", "╟──────────────────────────────────────────────────────────╢");
LOG_INFO("server.loading", "║ https://github.com/liyunfan1223/mod-playerbots ");
LOG_INFO("server.loading", "║ https://github.com/mod-playerbots/mod-playerbots ║");
LOG_INFO("server.loading", "╚══════════════════════════════════════════════════════════╝");
uint32 oldMSTime = getMSTime();

View File

@@ -1517,33 +1517,38 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
return false;
}
bool RandomPlayerbotMgr::ProcessBot(Player* player)
bool RandomPlayerbotMgr::ProcessBot(Player* bot)
{
uint32 bot = player->GetGUID().GetCounter();
if (player->InBattleground())
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
return false;
if (player->InBattlegroundQueue())
if (bot->InBattleground())
return false;
if (bot->InBattlegroundQueue())
return false;
uint32 botId = bot->GetGUID().GetCounter();
// if death revive
if (player->isDead())
if (bot->isDead())
{
if (!GetEventValue(bot, "dead"))
if (!GetEventValue(botId, "dead"))
{
uint32 randomTime =
urand(sPlayerbotAIConfig->minRandomBotReviveTime, sPlayerbotAIConfig->maxRandomBotReviveTime);
LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", player->GetName().c_str(),
LOG_DEBUG("playerbots", "Mark bot {} as dead, will be revived in {}s.", bot->GetName().c_str(),
randomTime);
SetEventValue(bot, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime);
SetEventValue(bot, "revive", 1, randomTime);
SetEventValue(botId, "dead", 1, sPlayerbotAIConfig->maxRandomBotInWorldTime);
SetEventValue(botId, "revive", 1, randomTime);
return false;
}
if (!GetEventValue(bot, "revive"))
if (!GetEventValue(botId, "revive"))
{
Revive(player);
Revive(bot);
return true;
}
@@ -1551,34 +1556,31 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
}
// leave group if leader is rndbot
Group* group = player->GetGroup();
Group* group = bot->GetGroup();
if (group && !group->isLFGGroup() && IsRandomBot(group->GetLeader()))
{
player->RemoveFromGroup();
LOG_INFO("playerbots", "Bot {} remove from group since leader is random bot.", player->GetName().c_str());
botAI->LeaveOrDisbandGroup();
LOG_INFO("playerbots", "Bot {} remove from group since leader is random bot.", bot->GetName().c_str());
}
// only randomize and teleport idle bots
bool idleBot = false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (botAI)
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
{
if (TravelTarget* target = botAI->GetAiObjectContext()->GetValue<TravelTarget*>("travel target")->Get())
{
if (target->getTravelState() == TravelState::TRAVEL_STATE_IDLE)
{
idleBot = true;
}
}
else
if (target->getTravelState() == TravelState::TRAVEL_STATE_IDLE)
{
idleBot = true;
}
}
else
{
idleBot = true;
}
if (idleBot)
{
// randomize
uint32 randomize = GetEventValue(bot, "randomize");
uint32 randomize = GetEventValue(botId, "randomize");
if (!randomize)
{
// bool randomiser = true;
@@ -1602,12 +1604,12 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
// }
// if (randomiser)
// {
Randomize(player);
LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", bot,
player->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", player->GetLevel(), player->GetName());
Randomize(bot);
LOG_DEBUG("playerbots", "Bot #{} {}:{} <{}>: randomized", botId,
bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName());
uint32 randomTime =
urand(sPlayerbotAIConfig->minRandomBotRandomizeTime, sPlayerbotAIConfig->maxRandomBotRandomizeTime);
ScheduleRandomize(bot, randomTime);
ScheduleRandomize(botId, randomTime);
return true;
}
@@ -1619,15 +1621,15 @@ bool RandomPlayerbotMgr::ProcessBot(Player* player)
// return true;
// }
uint32 teleport = GetEventValue(bot, "teleport");
uint32 teleport = GetEventValue(botId, "teleport");
if (!teleport)
{
LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", bot, player->GetName());
Refresh(player);
RandomTeleportForLevel(player);
LOG_DEBUG("playerbots", "Bot #{} <{}>: teleport for level and refresh", botId, bot->GetName());
Refresh(bot);
RandomTeleportForLevel(bot);
uint32 time = urand(sPlayerbotAIConfig->minRandomBotTeleportInterval,
sPlayerbotAIConfig->maxRandomBotTeleportInterval);
ScheduleTeleport(bot, time);
ScheduleTeleport(botId, time);
return true;
}
}
@@ -1771,6 +1773,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (botAI)
botAI->Reset(true);
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(loc.GetMapId(), x, y, z, 0);
bot->SendMovementFlagUpdate();
@@ -2376,6 +2379,10 @@ void RandomPlayerbotMgr::IncreaseLevel(Player* bot)
void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
return;
uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel;
if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
@@ -2433,7 +2440,6 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
}
SetValue(bot, "level", level);
PlayerbotFactory factory(bot, level);
factory.Randomize(false);
@@ -2455,11 +2461,10 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
PlayerbotsDatabase.Execute(stmt);
// teleport to a random inn for bot level
if (GET_PLAYERBOT_AI(bot))
GET_PLAYERBOT_AI(bot)->Reset(true);
botAI->Reset(true);
if (bot->GetGroup())
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
if (pmo)
pmo->finish();
@@ -2469,12 +2474,13 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
void RandomPlayerbotMgr::RandomizeMin(Player* bot)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (!botAI)
return;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "RandomizeMin");
uint32 level = sPlayerbotAIConfig->randomBotMinLevel;
SetValue(bot, "level", level);
PlayerbotFactory factory(bot, level);
factory.Randomize(false);
@@ -2496,11 +2502,10 @@ void RandomPlayerbotMgr::RandomizeMin(Player* bot)
PlayerbotsDatabase.Execute(stmt);
// teleport to a random inn for bot level
if (GET_PLAYERBOT_AI(bot))
GET_PLAYERBOT_AI(bot)->Reset(true);
botAI->Reset(true);
if (bot->GetGroup())
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
if (pmo)
pmo->finish();
@@ -2582,7 +2587,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
bot->SetMoney(money + 500 * sqrt(urand(1, bot->GetLevel() * 5)));
if (bot->GetGroup())
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
if (pmo)
pmo->finish();
@@ -3090,6 +3095,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player)
} while (true);
}
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
player->TeleportTo(botPos);
// player->Relocate(botPos.getX(), botPos.getY(), botPos.getZ(), botPos.getO());

View File

@@ -178,6 +178,9 @@ uint32 TravelNodePath::getPrice()
TaxiPathEntry const* taxiPath = sTaxiPathStore.LookupEntry(pathObject);
if (!taxiPath)
return 0;
return taxiPath->price;
}

View File

@@ -176,6 +176,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type)
continue;
memberBotAI->Reset();
member->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
LOG_INFO("playerbots", "Bot {} <{}>: Member of <{}>", member->GetGUID().ToString().c_str(),

View File

@@ -4289,9 +4289,15 @@ bool ArenaTactics::moveToCenter(Battleground* bg)
{
// they like to hang around at the tip of the pipes doing nothing, so we just teleport them down
if (bot->GetDistance(1333.07f, 817.18f, 13.35f) < 4)
{
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
}
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4)
{
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
}
}
break;
case BATTLEGROUND_RV:

View File

@@ -106,6 +106,7 @@ bool FollowChatShortcutAction::Execute(Event event)
else
botAI->TellMaster("You are too far away from me! I will there soon.");
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(),
master->GetOrientation()); return true;
}

View File

@@ -72,8 +72,10 @@ bool UninviteAction::Execute(Event event)
bool LeaveGroupAction::Leave(Player* player)
{
if (player && !GET_PLAYERBOT_AI(player) &&
if (player &&
!botAI &&
!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player))
return false;
bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr;
@@ -84,7 +86,7 @@ bool LeaveGroupAction::Leave(Player* player)
bool shouldStay = randomBot && bot->GetGroup() && player == bot;
if (!shouldStay)
{
bot->RemoveFromGroup();
botAI->LeaveOrDisbandGroup();
}
if (randomBot)

View File

@@ -1148,6 +1148,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
if ((target->GetMap() && target->GetMap()->IsBattlegroundOrArena()) || (bot->GetMap() &&
bot->GetMap()->IsBattlegroundOrArena())) return false;
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(target->GetMapId(), x, y, z, bot->GetOrientation());
}
else
@@ -1175,6 +1176,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
bot->CombatStop(true);
botAI->TellMasterNoFacing("I will there soon.");
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),
target->GetOrientation()); return false;
}

View File

@@ -147,6 +147,7 @@ bool AutoReleaseSpiritAction::HandleBattlegroundSpiritHealer()
// and in IOC it's not within clicking range when they res in own base
// Teleport to nearest friendly Spirit Healer when not currently in range of one.
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f);
RESET_AI_VALUE(bool, "combat::self target");
RESET_AI_VALUE(WorldPosition, "current position");
@@ -191,12 +192,11 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
{
// The below delays release to spirit with 6 seconds.
// This prevents currently casted (ranged) spells to be re-directed to the died bot's ghost.
const int32_t botId = bot->GetGUID().GetRawValue();
// If the bot already is a spirit, erase release time and return true
// If the bot already is a spirit, reset release time and return true
if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
{
m_botReleaseTimes.erase(botId);
botAI->bgReleaseAttemptTime = 0;
return true;
}
@@ -204,14 +204,13 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
const time_t now = time(nullptr);
constexpr time_t RELEASE_DELAY = 6;
auto& lastReleaseTime = m_botReleaseTimes[botId];
if (lastReleaseTime == 0)
lastReleaseTime = now;
if (botAI->bgReleaseAttemptTime == 0)
botAI->bgReleaseAttemptTime = now;
if (now - lastReleaseTime < RELEASE_DELAY)
if (now - botAI->bgReleaseAttemptTime < RELEASE_DELAY)
return false;
m_botReleaseTimes.erase(botId);
botAI->bgReleaseAttemptTime = 0;
return true;
}
@@ -244,6 +243,7 @@ int64 RepopAction::CalculateDeadTime() const
void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const
{
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f);
RESET_AI_VALUE(bool, "combat::self target");
RESET_AI_VALUE(WorldPosition, "current position");

View File

@@ -38,7 +38,6 @@ private:
bool ShouldAutoRelease() const;
bool ShouldDelayBattlegroundRelease() const;
inline static std::unordered_map<uint32_t, time_t> m_botReleaseTimes;
time_t m_bgGossipTime = 0;
};

View File

@@ -169,6 +169,7 @@ bool FindCorpseAction::Execute(Event event)
if (deadTime > delay)
{
bot->GetMotionMaster()->Clear();
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
}
@@ -350,6 +351,7 @@ bool SpiritHealerAction::Execute(Event event)
// if (!botAI->HasActivePlayerMaster())
// {
context->GetValue<uint32>("death count")->Set(dCount + 1);
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f);
// }

View File

@@ -225,6 +225,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player)
player->GetMotionMaster()->Clear();
AI_VALUE(LastMovement&, "last movement").clear();
player->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
player->TeleportTo(mapId, x, y, z, 0);
if (botAI->HasStrategy("stay", botAI->GetState()))

View File

@@ -67,6 +67,7 @@ bool NewRpgBaseAction::MoveFarTo(WorldPosition dest)
bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
zone_name);
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
return bot->TeleportTo(dest);
}

View File

@@ -236,7 +236,7 @@ ItemUsage ItemUsageValue::Calculate()
ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, int32 randomPropertyId)
{
if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK)
if (bot->BotCanUseItem(itemProto) != EQUIP_ERR_OK)
return ITEM_USAGE_NONE;
if (itemProto->InventoryType == INVTYPE_NON_EQUIP)