Compare commits

...

11 Commits

Author SHA1 Message Date
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
bash
31ed5cbb65 fixes 2025-10-09 20:52:32 +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
15 changed files with 114 additions and 66 deletions

View File

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

View File

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

View File

@ -514,7 +514,7 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
if (!groupValid) if (!groupValid)
{ {
bot->RemoveFromGroup(); botAI->LeaveOrDisbandGroup();
} }
} }

View File

@ -87,7 +87,8 @@ public:
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS, PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE, PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT, PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
PLAYERHOOK_ON_GIVE_EXP PLAYERHOOK_ON_GIVE_EXP,
PLAYERHOOK_ON_BEFORE_TELEPORT
}) {} }) {}
void OnPlayerLogin(Player* player) override void OnPlayerLogin(Player* player) override
@ -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 void OnPlayerAfterUpdate(Player* player, uint32 diff) override
{ {
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player)) if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))

View File

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

View File

@ -176,6 +176,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type)
continue; continue;
memberBotAI->Reset(); memberBotAI->Reset();
member->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0); member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0);
LOG_INFO("playerbots", "Bot {} <{}>: Member of <{}>", member->GetGUID().ToString().c_str(), 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 // 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) 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()); bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation());
}
if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4) 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()); bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation());
}
} }
break; break;
case BATTLEGROUND_RV: case BATTLEGROUND_RV:

View File

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

View File

@ -72,8 +72,10 @@ bool UninviteAction::Execute(Event event)
bool LeaveGroupAction::Leave(Player* player) bool LeaveGroupAction::Leave(Player* player)
{ {
if (player && !GET_PLAYERBOT_AI(player) && if (player &&
!botAI &&
!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player)) !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, player))
return false; return false;
bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr; bool aiMaster = GET_PLAYERBOT_AI(botAI->GetMaster()) != nullptr;
@ -84,7 +86,7 @@ bool LeaveGroupAction::Leave(Player* player)
bool shouldStay = randomBot && bot->GetGroup() && player == bot; bool shouldStay = randomBot && bot->GetGroup() && player == bot;
if (!shouldStay) if (!shouldStay)
{ {
bot->RemoveFromGroup(); botAI->LeaveOrDisbandGroup();
} }
if (randomBot) 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() && if ((target->GetMap() && target->GetMap()->IsBattlegroundOrArena()) || (bot->GetMap() &&
bot->GetMap()->IsBattlegroundOrArena())) return false; 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()); bot->TeleportTo(target->GetMapId(), x, y, z, bot->GetOrientation());
} }
else else
@ -1175,6 +1176,7 @@ bool MovementAction::Follow(Unit* target, float distance, float angle)
bot->CombatStop(true); bot->CombatStop(true);
botAI->TellMasterNoFacing("I will there soon."); 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(), bot->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),
target->GetOrientation()); return false; 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 // 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. // 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); bot->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f);
RESET_AI_VALUE(bool, "combat::self target"); RESET_AI_VALUE(bool, "combat::self target");
RESET_AI_VALUE(WorldPosition, "current position"); RESET_AI_VALUE(WorldPosition, "current position");
@ -191,12 +192,11 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
{ {
// The below delays release to spirit with 6 seconds. // 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. // 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)) if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
{ {
m_botReleaseTimes.erase(botId); botAI->bgReleaseAttemptTime = 0;
return true; return true;
} }
@ -204,14 +204,13 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
const time_t now = time(nullptr); const time_t now = time(nullptr);
constexpr time_t RELEASE_DELAY = 6; constexpr time_t RELEASE_DELAY = 6;
auto& lastReleaseTime = m_botReleaseTimes[botId]; if (botAI->bgReleaseAttemptTime == 0)
if (lastReleaseTime == 0) botAI->bgReleaseAttemptTime = now;
lastReleaseTime = now;
if (now - lastReleaseTime < RELEASE_DELAY) if (now - botAI->bgReleaseAttemptTime < RELEASE_DELAY)
return false; return false;
m_botReleaseTimes.erase(botId); botAI->bgReleaseAttemptTime = 0;
return true; return true;
} }
@ -244,6 +243,7 @@ int64 RepopAction::CalculateDeadTime() const
void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) 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); bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f);
RESET_AI_VALUE(bool, "combat::self target"); RESET_AI_VALUE(bool, "combat::self target");
RESET_AI_VALUE(WorldPosition, "current position"); RESET_AI_VALUE(WorldPosition, "current position");

View File

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

View File

@ -169,6 +169,7 @@ bool FindCorpseAction::Execute(Event event)
if (deadTime > delay) if (deadTime > delay)
{ {
bot->GetMotionMaster()->Clear(); 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); bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0);
} }
@ -350,6 +351,7 @@ bool SpiritHealerAction::Execute(Event event)
// if (!botAI->HasActivePlayerMaster()) // if (!botAI->HasActivePlayerMaster())
// { // {
context->GetValue<uint32>("death count")->Set(dCount + 1); 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); 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(); player->GetMotionMaster()->Clear();
AI_VALUE(LastMovement&, "last movement").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); player->TeleportTo(mapId, x, y, z, 0);
if (botAI->HasStrategy("stay", botAI->GetState())) 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(), bot->GetName(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), bot->GetMapId(),
dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(), dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ(), dest.getMapId(), bot->GetZoneId(),
zone_name); zone_name);
bot->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TELEPORTED | AURA_INTERRUPT_FLAG_CHANGE_MAP);
return bot->TeleportTo(dest); return bot->TeleportTo(dest);
} }