From 8d51092d4234e7cb606c54f557930312c974d90c Mon Sep 17 00:00:00 2001 From: bash <31279994+hermensbas@users.noreply.github.com> Date: Tue, 12 Aug 2025 22:10:47 +0200 Subject: [PATCH] As requested revert for threadfixes last few days (#1552) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Revert "[Large server fix] #1537 Serialize playerBots/botLoading with a mutex and use snapshot-based loops to fix concurrency crashes (#1540)" This reverts commit 3fff58df1a2058894e9b758be07869aec87c2c70. * Revert "[Fix] teleport to invalid map or invalid coordinates (x , y , z 200000, o ) given when teleporting player (g UI d full type player low , name , map , x , y , z , o ) (#1538)" This reverts commit ca2e2ef0dbd8dcfb16123db65ae638424550e50c. * Revert "Fix: prevent MoveSplineInitArgs::Validate velocity asserts (velocity > 0.01f) for bots, pets, and charmed units (#1534)" This reverts commit 4e3ac609bd23d991150d956d4e69ee6de2fcf2bf. * Revert "[Fix issue #1527] : startup crash in tank target selection — add TOCTOU & null-safety guards (#1532)" This reverts commit c6b0424c29b6a1bf5b3574135128d30d19838411. * Revert "[Fix issue #1528] Close small window where the “in a BG/arena” state can change between the check (InBattleground() / InArena()) and grabbing the pointer (GetBattleground()), which leads to a null dereference. (#1530)" This reverts commit 2e0a161623eaa97b7d9ceea076779ae0cabeb877. * Revert "Harden playerbot logout & packet dispatch; add null-safety in chat hooks and RPG checks (#1529)" This reverts commit e4ea8e2694b0f6d098a945c6f863526cd14f9b3f. * Revert "Dont wait to travel when in combat. (#1524)" This reverts commit ddfa919154529fee59e7ba30d2ebe29c0ae4abdf. * Revert "nullptr fix (#1523)" This reverts commit 380312ffd231fd5e663a8a17daa80dd39906e3f0. * Revert "Playerbots/LFG: fix false not eligible & dungeon 0/type 0, add clear diagnostics (#1521)" This reverts commit 872e4176137b66c83ebcb03932fa8ff1e39fd791. * Revert "nullptr exception (#1520)" This reverts commit 3d28a815089fd0a878a6a1d469db657c6030d4b2. * Revert "Removed bot freezing at startup and system message, not relevant anymore (#1519)" This reverts commit bcd6f5bc066d5e8a54f2d37b7dfc54e5db0dd2d1. --- src/BotMovementUtils.h | 35 - src/BroadcastHelper.cpp | 17 +- src/PlayerbotAI.cpp | 63 +- src/PlayerbotAI.h | 1 + src/PlayerbotMgr.cpp | 604 ++---------------- src/Playerbots.cpp | 168 +---- src/Playerbots.h | 42 -- src/RandomPlayerbotMgr.cpp | 147 +---- src/TravelMgr.cpp | 58 +- src/factory/PlayerbotFactory.cpp | 31 +- src/strategy/actions/AreaTriggerAction.cpp | 14 +- .../actions/BattleGroundJoinAction.cpp | 3 +- src/strategy/actions/BattleGroundTactics.cpp | 6 +- .../actions/MoveToTravelTargetAction.cpp | 2 +- src/strategy/actions/MovementActions.cpp | 17 - src/strategy/actions/ReleaseSpiritAction.cpp | 6 +- .../actions/ReviveFromCorpseAction.cpp | 6 +- .../raids/icecrown/RaidIccActions.cpp | 3 +- .../raids/naxxramas/RaidNaxxActions.cpp | 18 +- .../raids/ulduar/RaidUlduarActions.cpp | 16 +- .../raids/vaultofarchavon/RaidVoAActions.cpp | 8 +- src/strategy/rpg/NewRpgBaseAction.cpp | 3 +- src/strategy/values/TankTargetValue.cpp | 126 +--- src/strategy/values/TargetValue.cpp | 24 +- 24 files changed, 144 insertions(+), 1274 deletions(-) delete mode 100644 src/BotMovementUtils.h diff --git a/src/BotMovementUtils.h b/src/BotMovementUtils.h deleted file mode 100644 index 8b32fcb1..00000000 --- a/src/BotMovementUtils.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it - * and/or modify it under version 2 of the License, or (at your option), any later version. - */ - -#pragma once -#include "Unit.h" -#include "Player.h" -#include "MotionMaster.h" - -inline bool CanStartMoveSpline(Player* bot) { - if (!bot) return false; - if (!bot->IsAlive()) return false; - if (bot->IsBeingTeleported() || bot->IsInFlight()) return false; - if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL) || bot->HasRootAura() || - bot->HasStunAura() || bot->IsCharmed() || bot->isFrozen() || bot->IsPolymorphed()) - return false; - if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE) - return false; - if (bot->GetSpeed(MOVE_RUN) <= 0.01f) return false; - return true; -} - -inline bool CanStartMoveSpline(Unit* u) { - if (!u) return false; - if (!u->IsAlive()) return false; - if (u->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_CONFUSED | UNIT_STATE_FLEEING)) - return false; - if (u->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE) - return false; - if (u->GetSpeed(MOVE_RUN) <= 0.01f) return false; - return true; -} - - diff --git a/src/BroadcastHelper.cpp b/src/BroadcastHelper.cpp index 2de066f6..85bc5221 100644 --- a/src/BroadcastHelper.cpp +++ b/src/BroadcastHelper.cpp @@ -947,21 +947,8 @@ bool BroadcastHelper::BroadcastSuggestThunderfury(PlayerbotAI* ai, Player* bot) { std::map placeholders; ItemTemplate const* thunderfuryProto = sObjectMgr->GetItemTemplate(19019); - // placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto); // Old code - // [Crash fix] Protect from nil AI : a real player doesn't have PlayerbotAI. - // Before: direct deref GET_PLAYERBOT_AI(bot)->... could crash World/General. - if (auto* ai = GET_PLAYERBOT_AI(bot)) - { - if (auto* chat = ai->GetChatHelper()) - placeholders["%thunderfury_link"] = chat->FormatItem(thunderfuryProto); - else - placeholders["%thunderfury_link"] = ""; // fallback: no chat helper - } - else - { - placeholders["%thunderfury_link"] = ""; // fallback: no d'AI (real player) - } - // End crash fix + placeholders["%thunderfury_link"] = GET_PLAYERBOT_AI(bot)->GetChatHelper()->FormatItem(thunderfuryProto); + return BroadcastToChannelWithGlobalChance( ai, BOT_TEXT2("thunderfury_spam", placeholders), diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 878888e1..09a6ef82 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -57,7 +57,6 @@ #include "Unit.h" #include "UpdateTime.h" #include "Vehicle.h" -#include "BotMovementUtils.h" const int SPELL_TITAN_GRIP = 49152; @@ -721,7 +720,6 @@ void PlayerbotAI::HandleTeleportAck() bot->GetSession()->HandleMoveWorldportAck(); } // SetNextCheckDelay(urand(2000, 5000)); - SetNextCheckDelay(urand(500, 1500)); // short delay to break bursts without hindering gameplay if (sPlayerbotAIConfig->applyInstanceStrategies) ApplyInstanceStrategies(bot->GetMapId(), true); EvaluateHealerDpsStrategy(); @@ -2375,7 +2373,7 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry) return name; } -/*std::vector PlayerbotAI::GetPlayersInGroup() +std::vector PlayerbotAI::GetPlayersInGroup() { std::vector members; @@ -2394,34 +2392,6 @@ std::string PlayerbotAI::GetLocalizedGameObjectName(uint32 entry) members.push_back(ref->GetSource()); } - return members; -}*/ - -std::vector PlayerbotAI::GetPlayersInGroup() -{ - std::vector members; - - Group* group = bot->GetGroup(); - if (!group) - return members; - - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - // Celaning, we don't call 2 times GET_PLAYERBOT_AI and never reference it if nil - if (auto* ai = GET_PLAYERBOT_AI(member)) - { - // If it's a bot (not real player) => we ignor it - if (!ai->IsRealPlayer()) - continue; - } - - members.push_back(member); - } - return members; } @@ -4244,6 +4214,19 @@ bool PlayerbotAI::AllowActive(ActivityType activityType) } } + // only keep updating till initializing time has completed, + // which prevents unneeded expensive GameTime calls. + if (_isBotInitializing) + { + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.11; + + // no activity allowed during bot initialization + if (_isBotInitializing) + { + return false; + } + } + // General exceptions if (activityType == PACKET_ACTIVITY) { @@ -6327,27 +6310,11 @@ void PlayerbotAI::PetFollow() if (!pet) return; pet->AttackStop(); - /* pet->InterruptNonMeleeSpells(false); + pet->InterruptNonMeleeSpells(false); pet->ClearInPetCombat(); pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle()); - if (pet->ToPet()) - pet->ToPet()->ClearCastWhenWillAvailable();*/ - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - pet->InterruptNonMeleeSpells(false); - pet->ClearInPetCombat(); - - if (CanStartMoveSpline(pet)) - { - pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle()); - } - else - { - pet->StopMovingOnCurrentPos(); // on n’envoie pas d’ordre invalide - } - if (pet->ToPet()) pet->ToPet()->ClearCastWhenWillAvailable(); - //End Fix CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) return; diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index c7bd5e62..3fa7b5b6 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -611,6 +611,7 @@ private: Item* FindItemInInventory(std::function checkItem) const; void HandleCommands(); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); + bool _isBotInitializing = false; protected: Player* bot; diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 02410335..0b6d3967 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -36,32 +36,10 @@ #include "BroadcastHelper.h" #include "PlayerbotDbStore.h" #include "WorldSessionMgr.h" -#include "DatabaseEnv.h" -#include -#include "Log.h" +#include "DatabaseEnv.h" // Added for gender choice +#include // Added for gender choice +#include "Log.h" // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) #include // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) -#include "TravelMgr.h" -#include -#include - -static std::mutex g_botMapsMx; // protect playerBots and botLoading - -namespace { - // [Crash fix] Centralize clearing of pointer values in the AI context - static void ClearAIContextPointerValues(PlayerbotAI* ai) - { - if (!ai) return; - if (AiObjectContext* ctx = ai->GetAiObjectContext()) - { - // Known today - if (auto* tt = ctx->GetValue("travel target")) - tt->Set(nullptr); - - // TODO: add other pointer-type values here if you have any - // e.g.: ctx->GetValue("some key")->Set(nullptr); - } - } -} class BotInitGuard { @@ -109,16 +87,9 @@ public: void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId) { - /*// bot is loading + // bot is loading if (botLoading.find(playerGuid) != botLoading.end()) - return;*/ - - // bot is loading (protégé) - { - std::lock_guard lk(g_botMapsMx); - if (botLoading.find(playerGuid) != botLoading.end()) - return; - } + return; // has bot already been added? Player* bot = ObjectAccessor::FindConnectedPlayer(playerGuid); @@ -153,19 +124,10 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId PlayerbotMgr* mgr = GET_PLAYERBOT_MGR(masterPlayer); if (!mgr) { - LOG_DEBUG("mod-playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); + LOG_DEBUG("playerbots", "PlayerbotMgr not found for master player with GUID: {}", masterPlayer->GetGUID().GetRawValue()); return; } - - // read botLoading.size() locked - size_t loadingCount = 0; - { - std::lock_guard lk(g_botMapsMx); - loadingCount = botLoading.size(); - } - - // uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); - uint32 count = mgr->GetPlayerbotsCount() + static_cast(loadingCount); + uint32 count = mgr->GetPlayerbotsCount() + botLoading.size(); if (count >= sPlayerbotAIConfig->maxAddedBots) { allowed = false; @@ -181,22 +143,14 @@ void PlayerbotHolder::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccountId } return; } - // std::shared_ptr holder = - // std::make_shared(this, masterAccountId, accountId, playerGuid); - auto holder = std::make_shared(this, masterAccountId, accountId, playerGuid); + std::shared_ptr holder = + std::make_shared(this, masterAccountId, accountId, playerGuid); if (!holder->Initialize()) { return; } - // botLoading.insert(playerGuid); - // Protected insert - { - std::lock_guard lk(g_botMapsMx); - if (botLoading.find(playerGuid) != botLoading.end()) - return; // already loging - botLoading.insert(playerGuid); // we reserve the GUID - } + botLoading.insert(playerGuid); // Always login in with world session to avoid race condition sWorld->AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)) @@ -213,11 +167,7 @@ bool PlayerbotHolder::IsAccountLinked(uint32 accountId, uint32 linkedAccountId) void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder) { - // Copy immediatly holder value - const ObjectGuid guid = holder.GetGuid(); - const uint32 masterAccountId = holder.GetMasterAccountId(); uint32 botAccountId = holder.GetAccountId(); - // At login DBC locale should be what the server is set to use by default (as spells etc are hardcoded to ENUS this // allows channels to work as intended) WorldSession* botSession = new WorldSession(botAccountId, "", 0x0, nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, @@ -232,16 +182,11 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con LOG_DEBUG("mod-playerbots", "Bot player could not be loaded for account ID: {}", botAccountId); botSession->LogoutPlayer(true); delete botSession; - // botLoading.erase(holder.GetGuid()); - { - std::lock_guard lk(g_botMapsMx); - botLoading.erase(guid); - } + botLoading.erase(holder.GetGuid()); return; } - // uint32 masterAccount = holder.GetMasterAccountId(); - uint32 masterAccount = masterAccountId; // Avoid read in 'holder' after login + uint32 masterAccount = holder.GetMasterAccountId(); WorldSession* masterSession = masterAccount ? sWorldSessionMgr->FindSession(masterAccount) : nullptr; // Check if masterSession->GetPlayer() is valid @@ -254,14 +199,10 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con sRandomPlayerbotMgr->OnPlayerLogin(bot); OnBotLogin(bot); - // botLoading.erase(holder.GetGuid()); - { - std::lock_guard lk(g_botMapsMx); - botLoading.erase(guid); - } + botLoading.erase(holder.GetGuid()); } -/*void PlayerbotHolder::UpdateSessions() +void PlayerbotHolder::UpdateSessions() { for (PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr) { @@ -279,58 +220,15 @@ void PlayerbotHolder::HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder con HandleBotPackets(bot->GetSession()); } } -}*/ - -void PlayerbotHolder::UpdateSessions() -{ - PlayerBotMap botsCopy; - { - std::lock_guard lk(g_botMapsMx); - botsCopy = playerBots; - } - - for (const auto& kv : botsCopy) - { - Player* const bot = kv.second; - if (bot->IsBeingTeleported()) - { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot)) - botAI->HandleTeleportAck(); - } - else if (bot->IsInWorld()) - { - HandleBotPackets(bot->GetSession()); - } - } } -/*void PlayerbotHolder::HandleBotPackets(WorldSession* session) +void PlayerbotHolder::HandleBotPackets(WorldSession* session) { WorldPacket* packet; while (session->GetPacketQueue().next(packet)) { OpcodeClient opcode = static_cast(packet->GetOpcode()); ClientOpcodeHandler const* opHandle = opcodeTable[opcode]; - opHandle->Call(session, *packet); - delete packet; - } -}*/ - -void PlayerbotHolder::HandleBotPackets(WorldSession* session) // [Crash Fix] Secure packet dispatch (avoid calling on a null handler) -{ - WorldPacket* packet; - while (session->GetPacketQueue().next(packet)) - { - const OpcodeClient opcode = static_cast(packet->GetOpcode()); - const ClientOpcodeHandler* opHandle = opcodeTable[opcode]; - - if (!opHandle) - { - // Unknown handler: drop cleanly - delete packet; - continue; - } - opHandle->Call(session, *packet); delete packet; } @@ -351,27 +249,8 @@ void PlayerbotHolder::LogoutAllBots() } */ - /*PlayerBotMap bots = playerBots; + PlayerBotMap bots = playerBots; for (auto& itr : bots) - { - Player* bot = itr.second; - if (!bot) - continue; - - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI || botAI->IsRealPlayer()) - continue; - - LogoutPlayerBot(bot->GetGUID()); - }*/ - // Snapshot under lock for safe iteration - PlayerBotMap botsCopy; - { - std::lock_guard lk(g_botMapsMx); - botsCopy = playerBots; - } - - for (auto& itr : botsCopy) { Player* bot = itr.second; if (!bot) @@ -385,7 +264,7 @@ void PlayerbotHolder::LogoutAllBots() } } -/*void PlayerbotMgr::CancelLogout() +void PlayerbotMgr::CancelLogout() { Player* master = GetMaster(); if (!master) @@ -406,53 +285,6 @@ void PlayerbotHolder::LogoutAllBots() } } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI || botAI->IsRealPlayer()) - continue; - - if (botAI->GetMaster() != master) - continue; - - if (bot->GetSession()->isLogingOut()) - { - WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL); - bot->GetSession()->HandleLogoutCancelOpcode(data); - } - } -}*/ - -void PlayerbotMgr::CancelLogout() -{ - Player* master = GetMaster(); - if (!master) - return; - - // Snapshot of "master" bots under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) - { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI || botAI->IsRealPlayer()) - continue; - - if (bot->GetSession()->isLogingOut()) - { - WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL); - bot->GetSession()->HandleLogoutCancelOpcode(data); - botAI->TellMaster("Logout cancelled!"); - } - } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) { @@ -486,13 +318,10 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) sPlayerbotDbStore->Save(botAI); } - LOG_DEBUG("mod-playerbots", "Bot {} logging out", bot->GetName().c_str()); + LOG_DEBUG("playerbots", "Bot {} logging out", bot->GetName().c_str()); bot->SaveToDB(false, false); - // WorldSession* botWorldSessionPtr = bot->GetSession(); - WorldSession* botWorldSessionPtr = bot->GetSession(); // Small safeguard on the session (as a precaution) - if (!botWorldSessionPtr) - return; + WorldSession* botWorldSessionPtr = bot->GetSession(); WorldSession* masterWorldSessionPtr = nullptr; if (botWorldSessionPtr->isLogingOut()) @@ -525,13 +354,11 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) logout = true; } - /*TravelTarget* target = nullptr; + TravelTarget* target = nullptr; if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values. { target = botAI->GetAiObjectContext()->GetValue("travel target")->Get(); - }*/ - // [Crash fix] Centralized cleanup of pointer values in the context - ClearAIContextPointerValues(botAI); + } // Peiru: Allow bots to always instant logout to see if this resolves logout crashes logout = true; @@ -548,25 +375,19 @@ void PlayerbotHolder::LogoutPlayerBot(ObjectGuid guid) botWorldSessionPtr->HandleLogoutRequestOpcode(data); if (!bot) { - /*RemoveFromPlayerbotsMap(guid); - delete botWorldSessionPtr; - if (target) - delete target;*/ - // [Crash fix] bot can be destroyed by the logout request: clean up without touching old pointers RemoveFromPlayerbotsMap(guid); delete botWorldSessionPtr; + if (target) + delete target; } return; } else { - /*RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap + RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap delete botWorldSessionPtr; // finally delete the bot's WorldSession if (target) - delete target;*/ - // [Crash fix] no more deleting 'target' here: ownership handled by the AI/Context - RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap - delete botWorldSessionPtr; // finally delete the bot's WorldSession + delete target; } return; } // if instant logout possible, do it @@ -599,11 +420,11 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid) sPlayerbotDbStore->Save(botAI); } - LOG_DEBUG("mod-playerbots", "Bot {} logged out", bot->GetName().c_str()); + LOG_DEBUG("playerbots", "Bot {} logged out", bot->GetName().c_str()); bot->SaveToDB(false, false); - /*if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values. + if (botAI->GetAiObjectContext()) // Maybe some day re-write to delate all pointer values. { TravelTarget* target = botAI->GetAiObjectContext()->GetValue("travel target")->Get(); if (target) @@ -612,54 +433,38 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid) RemoveFromPlayerbotsMap(guid); // deletes bot player ptr inside this WorldSession PlayerBotMap - delete botAI;*/ - // [Crash fix] Centralized cleanup of pointer values in the context - ClearAIContextPointerValues(botAI); + delete botAI; } } void PlayerbotHolder::RemoveFromPlayerbotsMap(ObjectGuid guid) -// { -// playerBots.erase(guid); -// } -// Protected erase { - std::lock_guard lk(g_botMapsMx); playerBots.erase(guid); } Player* PlayerbotHolder::GetPlayerBot(ObjectGuid playerGuid) const { - std::lock_guard lk(g_botMapsMx); // We protect PlayerBotMap::const_iterator it = playerBots.find(playerGuid); - return (it == playerBots.end()) ? nullptr : it->second;// (nullptr) + return (it == playerBots.end()) ? 0 : it->second; } Player* PlayerbotHolder::GetPlayerBot(ObjectGuid::LowType lowGuid) const { ObjectGuid playerGuid = ObjectGuid::Create(lowGuid); - std::lock_guard lk(g_botMapsMx); // We protect PlayerBotMap::const_iterator it = playerBots.find(playerGuid); - return (it == playerBots.end()) ? nullptr : it->second; + return (it == playerBots.end()) ? 0 : it->second; } void PlayerbotHolder::OnBotLogin(Player* const bot) { // Prevent duplicate login - /*if (playerBots.find(bot->GetGUID()) != playerBots.end()) + if (playerBots.find(bot->GetGUID()) != playerBots.end()) { return; - }*/ - { - std::lock_guard lk(g_botMapsMx); - if (playerBots.find(bot->GetGUID()) != playerBots.end()) - return; - - playerBots[bot->GetGUID()] = bot; } sPlayerbotsMgr->AddPlayerbotData(bot, true); - // playerBots[bot->GetGUID()] = bot; + playerBots[bot->GetGUID()] = bot; OnBotLoginInternal(bot); @@ -724,9 +529,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) { botAI->ResetStrategies(!sRandomPlayerbotMgr->IsRandomBot(bot)); } - - botAI->Reset(true); // Reset transient states (incl. LFG "proposal") to avoid the "one or more players are not eligible" error after reconnect. - sPlayerbotDbStore->Load(botAI); if (master && !master->HasUnitState(UNIT_STATE_IN_FLIGHT)) @@ -735,17 +537,6 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) bot->CleanupAfterTaxiFlight(); } - // [Fix MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full: 0x00000000000019ba Type: Player Low: 6586] Ensure valid speeds before any next movement command - bot->StopMoving(); - bot->UpdateSpeed(MOVE_WALK, true); - bot->UpdateSpeed(MOVE_RUN, true); - bot->UpdateSpeed(MOVE_SWIM, true); - bot->UpdateSpeed(MOVE_FLIGHT, true); // OK even if not flying - - if (bot->GetSpeed(MOVE_RUN) <= 0.01f) // Belt-and-suspenders: if the run speed has stayed ~0, reset to the default rate - bot->SetSpeedRate(MOVE_RUN, 1.0f); - // End Fix - // check activity botAI->AllowActivity(ALL_ACTIVITY, true); @@ -757,21 +548,16 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) if (master && master->GetGroup() && !group) { Group* mgroup = master->GetGroup(); - // if (mgroup->GetMembersCount() >= 5) - if (mgroup->GetMembersCount() + 1 > 5) // only convert in raid if the add of THIS bot make group > 5 + if (mgroup->GetMembersCount() >= 5) { if (!mgroup->isRaidGroup() && !mgroup->isLFGGroup() && !mgroup->isBGGroup() && !mgroup->isBFGroup()) { mgroup->ConvertToRaid(); } - //if (mgroup->isRaidGroup()) - //{ - //mgroup->AddMember(bot); - //} - mgroup->AddMember(bot); - - LOG_DEBUG("mod-playerbots", "[GROUP] after add: members={}, isRaid={}, isLFG={}", - (int)mgroup->GetMembersCount(), mgroup->isRaidGroup() ? 1 : 0, mgroup->isLFGGroup() ? 1 : 0); + if (mgroup->isRaidGroup()) + { + mgroup->AddMember(bot); + } } else { @@ -950,11 +736,9 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje } } - // if (GET_PLAYERBOT_AI(bot)) - if (PlayerbotAI* ai = GET_PLAYERBOT_AI(bot)) // [Tidy/Crash fix] Acquire AI once and reuse; avoid multiple GET_PLAYERBOT_AI calls. + if (GET_PLAYERBOT_AI(bot)) { - // if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster()) - if (Player* master = ai->GetMaster()) + if (Player* master = GET_PLAYERBOT_AI(bot)->GetMaster()) { if (master->GetSession()->GetSecurity() <= SEC_PLAYER && sPlayerbotAIConfig->autoInitOnly && cmd != "init=auto") @@ -1374,13 +1158,8 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg // If the user requested a specific gender, skip any character that doesn't match. if (gender != -1 && GetOfflinePlayerGender(guid) != gender) continue; - /*if (botLoading.find(guid) != botLoading.end()) - continue;*/ - { - std::lock_guard lk(g_botMapsMx); - if (botLoading.find(guid) != botLoading.end()) - continue; - } + if (botLoading.find(guid) != botLoading.end()) + continue; if (ObjectAccessor::FindConnectedPlayer(guid)) continue; uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid); @@ -1444,25 +1223,12 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (charnameStr == "!" && master && master->GetSession()->GetSecurity() > SEC_GAMEMASTER) { - /*for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i) + for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i) { if (Player* bot = i->second) if (bot->IsInWorld()) bots.insert(bot->GetName()); - }*/ - // Snapshot under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator i = GetPlayerBotsBegin(); i != GetPlayerBotsEnd(); ++i) - botsCopy.push_back(i->second); - } - for (Player* const bot : botsCopy) - { - if (bot && bot->IsInWorld()) - bots.insert(bot->GetName()); - } - + } } std::vector chars = split(charnameStr, ','); @@ -1566,7 +1332,7 @@ uint32 PlayerbotHolder::GetAccountId(ObjectGuid guid) return 0; } -/*std::string const PlayerbotHolder::ListBots(Player* master) +std::string const PlayerbotHolder::ListBots(Player* master) { std::set bots; std::map classNames; @@ -1652,103 +1418,6 @@ uint32 PlayerbotHolder::GetAccountId(ObjectGuid guid) out << online[name] << name << " " << classes[name]; } - return out.str(); -}*/ - -std::string const PlayerbotHolder::ListBots(Player* master) -{ - std::set bots; - std::map classNames; - - classNames[CLASS_DEATH_KNIGHT] = "Death Knight"; - classNames[CLASS_DRUID] = "Druid"; - classNames[CLASS_HUNTER] = "Hunter"; - classNames[CLASS_MAGE] = "Mage"; - classNames[CLASS_PALADIN] = "Paladin"; - classNames[CLASS_PRIEST] = "Priest"; - classNames[CLASS_ROGUE] = "Rogue"; - classNames[CLASS_SHAMAN] = "Shaman"; - classNames[CLASS_WARLOCK] = "Warlock"; - classNames[CLASS_WARRIOR] = "Warrior"; - classNames[CLASS_DEATH_KNIGHT] = "DeathKnight"; - - std::map online; - std::vector names; - std::map classes; - - // Snapshot under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) - { - std::string const name = bot->GetName(); - bots.insert(name); - - names.push_back(name); - online[name] = "+"; - classes[name] = classNames[bot->getClass()]; - } - - if (master) - { - QueryResult results = CharacterDatabase.Query( - "SELECT class, name FROM characters WHERE account = {}", - master->GetSession()->GetAccountId()); - - if (results) - { - do - { - Field* fields = results->Fetch(); - uint8 cls = fields[0].Get(); - std::string const name = fields[1].Get(); - if (bots.find(name) == bots.end() && name != master->GetSession()->GetPlayerName()) - { - names.push_back(name); - online[name] = "-"; - classes[name] = classNames[cls]; - } - } while (results->NextRow()); - } - } - - std::sort(names.begin(), names.end()); - - if (master) - { - if (Group* group = master->GetGroup()) - { - Group::MemberSlotList const& groupSlot = group->GetMemberSlots(); - for (Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++) - { - Player* member = ObjectAccessor::FindPlayer(itr->guid); - if (member && sRandomPlayerbotMgr->IsRandomBot(member)) - { - std::string const name = member->GetName(); - - names.push_back(name); - online[name] = "+"; - classes[name] = classNames[member->getClass()]; - } - } - } - } - - std::ostringstream out; - bool first = true; - out << "Bot roster: "; - for (std::vector::iterator i = names.begin(); i != names.end(); ++i) - { - if (first) first = false; else out << ", "; - std::string const name = *i; - out << online[name] << name << " " << classes[name]; - } - return out.str(); } @@ -1775,7 +1444,7 @@ std::string const PlayerbotHolder::LookupBots(Player* master) return ret_msg; } -/*uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls) +uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls) { uint32 count = 0; for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) @@ -1787,25 +1456,6 @@ std::string const PlayerbotHolder::LookupBots(Player* master) } } return count; -}*/ - -uint32 PlayerbotHolder::GetPlayerbotsCountByClass(uint32 cls) -{ - uint32 count = 0; - - // Snapshot under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) - if (bot && bot->IsInWorld() && bot->getClass() == cls) - ++count; - - return count; } PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(master), lastErrorTell(0) {} @@ -1822,42 +1472,6 @@ void PlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) CheckTellErrors(elapsed); } -/*void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) -{ - Player* master = GetMaster(); - if (!master) - return; - - if (text.find(sPlayerbotAIConfig->commandSeparator) != std::string::npos) - { - std::vector commands; - split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str()); - for (std::vector::iterator i = commands.begin(); i != commands.end(); ++i) - { - HandleCommand(type, *i); - } - - return; - } - - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI) - botAI->HandleCommand(type, text, master); - } - - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI && botAI->GetMaster() == master) - botAI->HandleCommand(type, text, master); - } -}*/ - void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) { Player* master = GetMaster(); @@ -1869,26 +1483,21 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) std::vector commands; split(commands, text, sPlayerbotAIConfig->commandSeparator.c_str()); for (std::vector::iterator i = commands.begin(); i != commands.end(); ++i) + { HandleCommand(type, *i); + } + return; } - // Snapshot of "master" bots under lock to avoid race conditions - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) + for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { + Player* const bot = it->second; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI) botAI->HandleCommand(type, text, master); } - // Random bots : unchanges for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) { @@ -1899,65 +1508,18 @@ void PlayerbotMgr::HandleCommand(uint32 type, std::string const text) } } -/*void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) -{ - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - if (!bot) - continue; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI) - botAI->HandleMasterIncomingPacket(packet); - } - - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI && botAI->GetMaster() == GetMaster()) - botAI->HandleMasterIncomingPacket(packet); - } - - switch (packet.GetOpcode()) - { - // if master is logging out, log out all bots - case CMSG_LOGOUT_REQUEST: - { - LogoutAllBots(); - break; - } - // if master cancelled logout, cancel too - case CMSG_LOGOUT_CANCEL: - { - CancelLogout(); - break; - } - } -}*/ - void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) { - // Snapshot of "master" bots under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) + for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { + Player* const bot = it->second; if (!bot) continue; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI) botAI->HandleMasterIncomingPacket(packet); } - // Boucle random bots (inchangée) for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) { @@ -1984,8 +1546,7 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) } } - -/*void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) +void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) { for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { @@ -1995,33 +1556,6 @@ void PlayerbotMgr::HandleMasterIncomingPacket(WorldPacket const& packet) botAI->HandleMasterOutgoingPacket(packet); } - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI && botAI->GetMaster() == GetMaster()) - botAI->HandleMasterOutgoingPacket(packet); - } -}*/ -void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) -{ - // Snapshot of "master" bots under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) - { - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (botAI) - botAI->HandleMasterOutgoingPacket(packet); - } - - // Random bots loop unchanged for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) { @@ -2032,7 +1566,7 @@ void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) } } -/*void PlayerbotMgr::SaveToDB() +void PlayerbotMgr::SaveToDB() { for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) { @@ -2047,32 +1581,6 @@ void PlayerbotMgr::HandleMasterOutgoingPacket(WorldPacket const& packet) if (GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetMaster() == GetMaster()) bot->SaveToDB(false, false); } -}*/ - -void PlayerbotMgr::SaveToDB() -{ - // Snapshot of "master" bots under lock - std::vector botsCopy; - { - std::lock_guard lk(g_botMapsMx); - for (PlayerBotMap::const_iterator it = GetPlayerBotsBegin(); it != GetPlayerBotsEnd(); ++it) - botsCopy.push_back(it->second); - } - - for (Player* const bot : botsCopy) - { - if (bot) - bot->SaveToDB(false, false); - } - - for (PlayerBotMap::const_iterator it = sRandomPlayerbotMgr->GetPlayerBotsBegin(); - it != sRandomPlayerbotMgr->GetPlayerBotsEnd(); ++it) - { - Player* const bot = it->second; - PlayerbotAI* ai = GET_PLAYERBOT_AI(bot); - if (ai && ai->GetMaster() == GetMaster()) - bot->SaveToDB(false, false); - } } void PlayerbotMgr::OnBotLoginInternal(Player* const bot) @@ -2085,7 +1593,7 @@ void PlayerbotMgr::OnBotLoginInternal(Player* const bot) botAI->SetMaster(master); botAI->ResetStrategies(); - LOG_INFO("mod-playerbots", "Bot {} logged in", bot->GetName().c_str()); + LOG_INFO("playerbots", "Bot {} logged in", bot->GetName().c_str()); } void PlayerbotMgr::OnPlayerLogin(Player* player) @@ -2278,7 +1786,7 @@ void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid, bool removeMgrEntr { delete it->second; _playerbotsAIMap.erase(it); - LOG_DEBUG("mod-playerbots", "Removed stale AI for GUID {}", + LOG_DEBUG("playerbots", "Removed stale AI for GUID {}", static_cast(guid.GetRawValue())); } diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index d7fb2ea5..76b32efa 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -30,7 +30,6 @@ #include "cs_playerbots.h" #include "cmath" #include "BattleGroundTactics.h" -#include "ObjectAccessor.h" class PlayerbotsDatabaseScript : public DatabaseScript { @@ -109,7 +108,7 @@ public: "|cffcccccchttps://github.com/liyunfan1223/mod-playerbots|r"); } - /*if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) + if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) { std::string roundedTime = std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0); @@ -118,7 +117,7 @@ public: ChatHandler(player->GetSession()).SendSysMessage( "|cff00ff00Playerbots:|r bot initialization at server startup takes about '" + roundedTime + "' minutes."); - }*/ + } } } @@ -137,7 +136,7 @@ public: bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override { - /*if (type == CHAT_MSG_WHISPER) + if (type == CHAT_MSG_WHISPER) { if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver)) { @@ -145,23 +144,14 @@ public: return false; } - }*/ - - if (type == CHAT_MSG_WHISPER && receiver) // [Crash Fix] Add non-null receiver check to avoid calling on a null pointer in edge cases. - { - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver)) - { - botAI->HandleCommand(type, msg, player); - return false; - } - } + } return true; } void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override { - /*for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) { if (Player* member = itr->GetSource()) { @@ -170,18 +160,6 @@ public: botAI->HandleCommand(type, msg, player); } } - }*/ - if (!group) return; // [Crash Fix] 'group' should not be null in this hook, but this safeguard prevents a crash if the caller changes or in case of an unexpected call. - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member) continue; - - if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member)) - { - botAI->HandleCommand(type, msg, player); - } } } @@ -198,9 +176,7 @@ public: { if (bot->GetGuildId() == player->GetGuildId()) { - // GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player); - if (PlayerbotAI* ai = GET_PLAYERBOT_AI(bot)) // [Crash Fix] Possible crash source because we don't check if the returned pointer is not null - ai->HandleCommand(type, msg, player); + GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player); } } } @@ -335,7 +311,7 @@ class PlayerbotsScript : public PlayerbotScript public: PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") {} - /*bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override + bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override { bool nonBotFound = false; for (ObjectGuid const& guid : guidsList.guids) @@ -349,137 +325,7 @@ public: } return nonBotFound; - }*/ - - // New LFG Function - bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) - { - const size_t totalSlots = guidsList.guids.size(); - size_t ignoredEmpty = 0, ignoredNonPlayer = 0; - size_t offlinePlayers = 0, botPlayers = 0, realPlayers = 0; - bool groupGuidSeen = false; - - LOG_DEBUG("playerbots", "[LFG] check start: slots={}", totalSlots); - - for (size_t i = 0; i < totalSlots; ++i) - { - ObjectGuid const& guid = guidsList.guids[i]; - - // 1) Placeholders to ignore - if (guid.IsEmpty()) - { - ++ignoredEmpty; - LOG_DEBUG("playerbots", "[LFG] slot {}: -> ignored", i); - continue; - } - - // Group GUID: in the original implementation this counted as "non-bot found" - if (guid.IsGroup()) - { - groupGuidSeen = true; - LOG_DEBUG("playerbots", "[LFG] slot {}: -> counts as having a real player (compat)", i); - continue; - } - - // Other non-Player GUIDs: various placeholders, ignore them - if (!guid.IsPlayer()) - { - ++ignoredNonPlayer; - LOG_DEBUG("playerbots", "[LFG] slot {}: guid={} (non-player/high={}) -> ignored", i, - static_cast(guid.GetRawValue()), (unsigned)guid.GetHigh()); - continue; - } - - // 2) Player present? - Player* player = ObjectAccessor::FindPlayer(guid); - if (!player) - { - ++offlinePlayers; - LOG_DEBUG("playerbots", "[LFG] slot {}: player guid={} is offline/not in world", i, - static_cast(guid.GetRawValue())); - continue; - } - - // 3) Bot or real player? - if (GET_PLAYERBOT_AI(player) != nullptr) - { - ++botPlayers; - LOG_DEBUG("playerbots", "[LFG] slot {}: BOT {} (lvl {}, class {})", i, player->GetName().c_str(), - player->GetLevel(), player->getClass()); - } - else - { - ++realPlayers; - LOG_DEBUG("playerbots", "[LFG] slot {}: REAL {} (lvl {}, class {})", i, player->GetName().c_str(), - player->GetLevel(), player->getClass()); - } - } - - // "Ultra-early phase" detection: only placeholders => DO NOT VETO - const bool onlyPlaceholders = (realPlayers + botPlayers + (groupGuidSeen ? 1 : 0)) == 0 && - (ignoredEmpty + ignoredNonPlayer) == totalSlots; - - // "Soft" LFG preflight if we actually see players AND at least one offline - if (!onlyPlaceholders && offlinePlayers > 0) - { - // Find a plausible leader: prefer a real online player, otherwise any online player - Player* leader = nullptr; - - for (ObjectGuid const& guid : guidsList.guids) - if (guid.IsPlayer()) - if (Player* p = ObjectAccessor::FindPlayer(guid)) - if (GET_PLAYERBOT_AI(p) == nullptr) - { - leader = p; - break; - } - - if (!leader) - for (ObjectGuid const& guid : guidsList.guids) - if (guid.IsPlayer()) - if (Player* p = ObjectAccessor::FindPlayer(guid)) - { - leader = p; - break; - } - - if (leader) - { - Group* g = leader->GetGroup(); - if (g) - { - LOG_DEBUG("playerbots", "[LFG-RESET] group members={}, isRaid={}, isLFGGroup={}", - (int)g->GetMembersCount(), g->isRaidGroup() ? 1 : 0, g->isLFGGroup() ? 1 : 0); - - // "Soft" reset of LFG states on the bots' AI side (proposal/role-check, etc.) - for (GroupReference* ref = g->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member) - continue; - - if (PlayerbotAI* ai = GET_PLAYERBOT_AI(member)) - ai->Reset(true); - } - } - } - - LOG_DEBUG("playerbots", "[LFG] preflight soft-reset triggered (offline detected) -> allowQueue=no (retry)"); - return false; // ask the client to retry right after the reset - } - - // "Hybrid" policy: permissive if only placeholders; otherwise original logic - bool allowQueue = onlyPlaceholders ? true : ((offlinePlayers == 0) && (realPlayers >= 1 || groupGuidSeen)); - - LOG_DEBUG("playerbots", - "[LFG] summary: slots={}, real={}, bots={}, offline={}, ignored(empty+nonPlayer)={}, " - "groupGuidSeen={} -> allowQueue={}", - totalSlots, realPlayers, botPlayers, offlinePlayers, (ignoredEmpty + ignoredNonPlayer), - (groupGuidSeen ? "yes" : "no"), (allowQueue ? "yes" : "no")); - - return allowQueue; } - // End LFG void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override { diff --git a/src/Playerbots.h b/src/Playerbots.h index 3ed03f23..5b82771b 100644 --- a/src/Playerbots.h +++ b/src/Playerbots.h @@ -49,46 +49,4 @@ int strcmpi(char const* s1, char const* s2); #define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue(name)->Get() #define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue(name, param)->Get() -// ---- Safe teleport wrappers (module-only) ---- -#include "Map.h" -#include -#include "TravelMgr.h" - -inline bool TeleportToSafe(Player* p, uint32 mapId, float x, float y, float z, float o) -{ - if (!p) return false; - - // If the height is invalid (-200000) or not finite, attempt ONE correction on the same map. - if (z <= -199000.0f || !std::isfinite(z)) - { - if (p->GetMapId() == mapId && p->GetMap()) - { - float hz = p->GetMap()->GetHeight(p->GetPhaseMask(), x, y, p->GetPositionZ(), true); - if (hz > -199000.0f && std::isfinite(hz)) - z = hz; - else - return false; // still invalid -> cancel the TP - } - else - { - return false; // different map: do not "guess" the height here - } - } - return p->TeleportTo(mapId, x, y, z, o); -} - -inline bool TeleportToSafe(Player* p, Position const& pos) -{ - // Position doesn't have mapId: we keep actual bot map - return TeleportToSafe(p, p->GetMapId(), - pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), - pos.GetOrientation()); -} - -inline bool TeleportToSafe(Player* p, WorldPosition pos) -{ - return TeleportToSafe(p, pos.getMapId(), pos.getX(), pos.getY(), pos.getZ(), pos.getO()); -} -// ---- /Safe teleport wrappers ---- - #endif diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 4f1cd1bb..1b69d7f3 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -994,18 +994,9 @@ void RandomPlayerbotMgr::CheckBgQueue() isRated = ginfo.IsRated; } - /*if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID()) || + if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID()) || (player->InArena() && player->GetBattleground()->isRated())) - isRated = true;*/ - if (bgQueue.IsPlayerInvitedToRatedArena(player->GetGUID())) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 - { isRated = true; - } - else if (Battleground const* bg = player->GetBattleground()) - { - if (player->InArena() && bg->isRated()) - isRated = true; - } if (isRated) BattlegroundData[queueTypeId][bracketId].ratedArenaPlayerCount++; @@ -1020,24 +1011,15 @@ void RandomPlayerbotMgr::CheckBgQueue() else BattlegroundData[queueTypeId][bracketId].bgHordePlayerCount++; - /*// If a player has joined the BG, update the instance count in BattlegroundData (for consistency) + // If a player has joined the BG, update the instance count in BattlegroundData (for consistency) if (player->InBattleground()) { std::vector* instanceIds = nullptr; uint32 instanceId = player->GetBattleground()->GetInstanceID(); - instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;*/ - // If a player has joined the BG, update the instance count in BattlegroundData (for consistency) - if (Battleground const* bg = player->GetBattleground()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 - { - std::vector* instanceIds = nullptr; - uint32 instanceId = bg->GetInstanceID(); - - instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances; - - if (instanceIds && + instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances; + if (instanceIds && std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end()) - instanceIds->push_back(instanceId); BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size(); @@ -1100,20 +1082,10 @@ void RandomPlayerbotMgr::CheckBgQueue() isRated = ginfo.IsRated; } - /*if (bgQueue.IsPlayerInvitedToRatedArena(guid) || (bot->InArena() && bot->GetBattleground()->isRated())) - isRated = true;*/ - if (bgQueue.IsPlayerInvitedToRatedArena(guid)) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 - { + if (bgQueue.IsPlayerInvitedToRatedArena(guid) || (bot->InArena() && bot->GetBattleground()->isRated())) isRated = true; - } - else if (Battleground const* bg = bot->GetBattleground()) - { - if (bot->InArena() && bg->isRated()) - isRated = true; - } - // END [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 - - if (isRated) + + if (isRated) BattlegroundData[queueTypeId][bracketId].ratedArenaBotCount++; else BattlegroundData[queueTypeId][bracketId].skirmishArenaBotCount++; @@ -1126,15 +1098,10 @@ void RandomPlayerbotMgr::CheckBgQueue() BattlegroundData[queueTypeId][bracketId].bgHordeBotCount++; } - /*if (bot->InBattleground()) + if (bot->InBattleground()) { std::vector* instanceIds = nullptr; - uint32 instanceId = bot->GetBattleground()->GetInstanceID();*/ - if (Battleground const* bg = bot->GetBattleground()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 - { - std::vector* instanceIds = nullptr; - uint32 instanceId = bg->GetInstanceID(); - //END [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 + uint32 instanceId = bot->GetBattleground()->GetInstanceID(); bool isArena = false; bool isRated = false; @@ -1142,8 +1109,7 @@ void RandomPlayerbotMgr::CheckBgQueue() if (bot->InArena()) { isArena = true; - // if (bot->GetBattleground()->isRated()) - if (bg->isRated()) // [Crash Fix] Issue Crash in RandomPlayerbotMgr:1018 #1528 + if (bot->GetBattleground()->isRated()) { isRated = true; instanceIds = &BattlegroundData[queueTypeId][bracketId].ratedArenaInstances; @@ -1759,11 +1725,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& } // Prevent blink to be detected by visible real players - /*if (botAI->HasPlayerNearby(150.0f)) - { - break; - }*/ - if (botAI && botAI->HasPlayerNearby(150.0f)) // [Crash fix] 'botAI' can be null earlier in the function. + if (botAI->HasPlayerNearby(150.0f)) { break; } @@ -1772,8 +1734,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI) botAI->Reset(true); - //bot->TeleportTo(loc.GetMapId(), x, y, z, 0); - TeleportToSafe(bot, loc.GetMapId(), x, y, z, 0); // [Fix] Avoid silly teleports + bot->TeleportTo(loc.GetMapId(), x, y, z, 0); bot->SendMovementFlagUpdate(); if (pmo) @@ -2372,10 +2333,8 @@ 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);*/ - if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Avoid 2 calls to GET_PLAYERBOT_AI and protect the dereference. - ai->Reset(true); + if (GET_PLAYERBOT_AI(bot)) + GET_PLAYERBOT_AI(bot)->Reset(true); if (bot->GetGroup()) bot->RemoveFromGroup(); @@ -2415,10 +2374,8 @@ 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);*/ - if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Avoid 2 calls to GET_PLAYERBOT_AI and protect the dereference. - ai->Reset(true); + if (GET_PLAYERBOT_AI(bot)) + GET_PLAYERBOT_AI(bot)->Reset(true); if (bot->GetGroup()) bot->RemoveFromGroup(); @@ -2511,7 +2468,7 @@ void RandomPlayerbotMgr::Refresh(Player* bot) bool RandomPlayerbotMgr::IsRandomBot(Player* bot) { - /*if (bot && GET_PLAYERBOT_AI(bot)) + if (bot && GET_PLAYERBOT_AI(bot)) { if (GET_PLAYERBOT_AI(bot)->IsRealPlayer()) return false; @@ -2521,17 +2478,6 @@ bool RandomPlayerbotMgr::IsRandomBot(Player* bot) return IsRandomBot(bot->GetGUID().GetCounter()); } - return false;*/ - - if (bot) // [Tidy] Single AI acquisition + same logic. - { - if (auto* ai = GET_PLAYERBOT_AI(bot)) - { - if (ai->IsRealPlayer()) - return false; - } - return IsRandomBot(bot->GetGUID().GetCounter()); - } return false; } @@ -2549,7 +2495,7 @@ bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot) bool RandomPlayerbotMgr::IsAddclassBot(Player* bot) { - /*if (bot && GET_PLAYERBOT_AI(bot)) + if (bot && GET_PLAYERBOT_AI(bot)) { if (GET_PLAYERBOT_AI(bot)->IsRealPlayer()) return false; @@ -2559,17 +2505,6 @@ bool RandomPlayerbotMgr::IsAddclassBot(Player* bot) return IsAddclassBot(bot->GetGUID().GetCounter()); } - return false;*/ - - if (bot) // [Tidy] Single AI acquisition + same logic. - { - if (auto* ai = GET_PLAYERBOT_AI(bot)) - { - if (ai->IsRealPlayer()) - return false; - } - return IsAddclassBot(bot->GetGUID().GetCounter()); - } return false; } @@ -2909,9 +2844,8 @@ void RandomPlayerbotMgr::HandleCommand(uint32 type, std::string const text, Play continue; } } - // GET_PLAYERBOT_AI(bot)->HandleCommand(type, text, fromPlayer); // Possible crash source because we don't check if the returned pointer is not null - if (auto* ai = GET_PLAYERBOT_AI(bot)) // [Crash fix] Protect the call on a null AI (World/General chat path). - ai->HandleCommand(type, text, fromPlayer); + + GET_PLAYERBOT_AI(bot)->HandleCommand(type, text, fromPlayer); } } @@ -2984,7 +2918,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - /*PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI && member == player && (!botAI->GetMaster() || GET_PLAYERBOT_AI(botAI->GetMaster()))) { if (!bot->InBattleground()) @@ -2995,20 +2929,6 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) } break; - }*/ - if (auto* botAI = GET_PLAYERBOT_AI(bot)) // [Tidy] Avoid GET_PLAYERBOT_AI(...) on a potentially null master. - { - Player* master = botAI->GetMaster(); - if (member == player && (!master || GET_PLAYERBOT_AI(master))) - { - if (!bot->InBattleground()) - { - botAI->SetMaster(player); - botAI->ResetStrategies(); - botAI->TellMaster("Hello"); - } - break; - } } } } @@ -3048,8 +2968,7 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) } while (true); } - // player->TeleportTo(botPos); - TeleportToSafe(player, botPos); // [Fix] Avoid silly teleports + player->TeleportTo(botPos); // player->Relocate(botPos.getX(), botPos.getY(), botPos.getZ(), botPos.getO()); } @@ -3148,29 +3067,13 @@ void RandomPlayerbotMgr::PrintStats() lvlPerClass[bot->getClass()] += bot->GetLevel(); lvlPerRace[bot->getRace()] += bot->GetLevel(); - /*PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); + PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); if (botAI->AllowActivity()) ++active; if (botAI->GetAiObjectContext()->GetValue("random bot update")->Get()) - ++update;*/ - - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); // [Crash fix] Declare botAI in the loop scope and exit early if null, - if (!botAI) - continue; // real player / no AI → ignore this bot for stats - - if (botAI->AllowActivity()) - ++active; - - // Secure access to the context and the value - if (AiObjectContext* ctx = botAI->GetAiObjectContext()) - { - if (auto* v = ctx->GetValue("random bot update")) - if (v->Get()) - ++update; - } - // End CrashFix - + ++update; + uint32 botId = bot->GetGUID().GetCounter(); if (!GetEventValue(botId, "randomize")) ++randomize; diff --git a/src/TravelMgr.cpp b/src/TravelMgr.cpp index 6b46fb5a..69d510b9 100644 --- a/src/TravelMgr.cpp +++ b/src/TravelMgr.cpp @@ -1227,7 +1227,7 @@ std::string const QuestObjectiveTravelDestination::getTitle() return out.str(); } -/*bool RpgTravelDestination::isActive(Player* bot) // Old Code +bool RpgTravelDestination::isActive(Player* bot) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); AiObjectContext* context = botAI->GetAiObjectContext(); @@ -1264,62 +1264,6 @@ std::string const QuestObjectiveTravelDestination::getTitle() ReputationRank reaction = bot->GetReputationRank(factionEntry->faction); return reaction > REP_NEUTRAL; -}*/ - -bool RpgTravelDestination::isActive(Player* bot) -{ - // [Crash fix] Never dereference the AI if the player is real (null AI). - if (!bot) - return false; - - PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); - if (!botAI) - return false; // real player (no AI) => inactive destination - - AiObjectContext* context = botAI->GetAiObjectContext(); - if (!context) - return false; - - CreatureTemplate const* cInfo = GetCreatureTemplate(); - if (!cInfo) - return false; - - bool isUsefull = false; - - if (cInfo->npcflag & UNIT_NPC_FLAG_VENDOR) - if (AI_VALUE2_LAZY(bool, "group or", "should sell,can sell,following party,near leader")) - isUsefull = true; - - if (cInfo->npcflag & UNIT_NPC_FLAG_REPAIR) - if (AI_VALUE2_LAZY(bool, "group or", "should repair,can repair,following party,near leader")) - isUsefull = true; - - if (!isUsefull) - return false; - - // [Crash fix] Read the ignore list via 'context' and check that the Value exists - GuidSet const* ignoreList = nullptr; - if (auto* value = context->GetValue("ignore rpg target")) - ignoreList = &value->Get(); - - if (ignoreList) - { - for (ObjectGuid const& guid : *ignoreList) - { - if (guid.GetEntry() == getEntry()) - return false; - } - } - - // Secure access to the faction template - if (FactionTemplateEntry const* factionEntry = sFactionTemplateStore.LookupEntry(cInfo->faction)) - { - ReputationRank reaction = bot->GetReputationRank(factionEntry->faction); - return reaction > REP_NEUTRAL; - } - - // As a precaution, if the faction is not found, consider inactive - return false; } CreatureTemplate const* RpgTravelDestination::GetCreatureTemplate() { return sObjectMgr->GetCreatureTemplate(entry); } diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 32d495a7..0fe95176 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -164,16 +164,15 @@ void PlayerbotFactory::Init() { continue; } - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(gemId); - if (proto) { - if (proto->ItemLevel < 60) - continue; - if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) - continue; + if (proto->ItemLevel < 60) + continue; + + if (proto->Flags & ITEM_FLAG_UNIQUE_EQUIPPABLE) + { + continue; } - if (sRandomItemMgr->IsTestItem(gemId)) continue; @@ -181,11 +180,9 @@ void PlayerbotFactory::Init() { continue; } - // LOG_INFO("playerbots", "Add {} to enchantment gems", gemId); enchantGemIdCache.push_back(gemId); } - LOG_INFO("playerbots", "Loading {} enchantment gems", enchantGemIdCache.size()); } @@ -1020,16 +1017,14 @@ void PlayerbotFactory::ClearSkills() } bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0); bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0); - // unlearn default race/class skills - if (PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass())) { - for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) - { - uint32 skillId = itr->SkillId; - if (!bot->HasSkill(skillId)) - continue; - bot->SetSkill(skillId, 0, 0, 0); - } + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(bot->getRace(), bot->getClass()); + for (PlayerCreateInfoSkills::const_iterator itr = info->skills.begin(); itr != info->skills.end(); ++itr) + { + uint32 skillId = itr->SkillId; + if (!bot->HasSkill(skillId)) + continue; + bot->SetSkill(skillId, 0, 0, 0); } } diff --git a/src/strategy/actions/AreaTriggerAction.cpp b/src/strategy/actions/AreaTriggerAction.cpp index 741058b4..5937a0f7 100644 --- a/src/strategy/actions/AreaTriggerAction.cpp +++ b/src/strategy/actions/AreaTriggerAction.cpp @@ -9,7 +9,6 @@ #include "LastMovementValue.h" #include "Playerbots.h" #include "Transport.h" -#include "BotMovementUtils.h" bool ReachAreaTriggerAction::Execute(Event event) { @@ -41,18 +40,7 @@ bool ReachAreaTriggerAction::Execute(Event event) return true; } - // bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z); - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - if (CanStartMoveSpline(bot)) - { - bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z); - } - else - { - bot->StopMovingOnCurrentPos(); - botAI->SetNextCheckDelay(sPlayerbotAIConfig->reactDelay); - return false; - } + bot->GetMotionMaster()->MovePoint(at->map, at->x, at->y, at->z); float distance = bot->GetDistance(at->x, at->y, at->z); float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig->reactDelay; diff --git a/src/strategy/actions/BattleGroundJoinAction.cpp b/src/strategy/actions/BattleGroundJoinAction.cpp index 1e9d9598..09523fd2 100644 --- a/src/strategy/actions/BattleGroundJoinAction.cpp +++ b/src/strategy/actions/BattleGroundJoinAction.cpp @@ -176,8 +176,7 @@ bool BGJoinAction::gatherArenaTeam(ArenaType type) continue; memberBotAI->Reset(); - // member->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 0); - TeleportToSafe(member, 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(), member->GetName().c_str(), arenateam->GetName().c_str()); diff --git a/src/strategy/actions/BattleGroundTactics.cpp b/src/strategy/actions/BattleGroundTactics.cpp index 0dfcd823..fab932f7 100644 --- a/src/strategy/actions/BattleGroundTactics.cpp +++ b/src/strategy/actions/BattleGroundTactics.cpp @@ -4289,11 +4289,9 @@ 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->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); - TeleportToSafe(bot, bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); // [Fix] Avaid silly teleport + bot->TeleportTo(bg->GetMapId(), 1330.96f, 816.75f, 3.2f, bot->GetOrientation()); if (bot->GetDistance(1250.13f, 764.79f, 13.34f) < 4) - // bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); - TeleportToSafe(bot, bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); // [Fix] Avaid silly teleport + bot->TeleportTo(bg->GetMapId(), 1252.19f, 765.41f, 3.2f, bot->GetOrientation()); } break; case BATTLEGROUND_RV: diff --git a/src/strategy/actions/MoveToTravelTargetAction.cpp b/src/strategy/actions/MoveToTravelTargetAction.cpp index 9ae28919..6782ea3f 100644 --- a/src/strategy/actions/MoveToTravelTargetAction.cpp +++ b/src/strategy/actions/MoveToTravelTargetAction.cpp @@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event) WorldLocation location = *target->getPosition(); Group* group = bot->GetGroup(); - if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat()) + if (group && !urand(0, 1) && bot == botAI->GetGroupMaster()) { for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { diff --git a/src/strategy/actions/MovementActions.cpp b/src/strategy/actions/MovementActions.cpp index c4e45505..d3e596e3 100644 --- a/src/strategy/actions/MovementActions.cpp +++ b/src/strategy/actions/MovementActions.cpp @@ -42,7 +42,6 @@ #include "Vehicle.h" #include "WaypointMovementGenerator.h" #include "Corpse.h" -#include "BotMovementUtils.h" MovementAction::MovementAction(PlayerbotAI* botAI, std::string const name) : Action(botAI, name) { @@ -82,10 +81,6 @@ bool MovementAction::JumpTo(uint32 mapId, float x, float y, float z, MovementPri float botZ = bot->GetPositionZ(); float speed = bot->GetSpeed(MOVE_RUN); MotionMaster& mm = *bot->GetMotionMaster(); - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - if (!CanStartMoveSpline(bot)) - return false; - // End Fix mm.Clear(); mm.MoveJump(x, y, z, speed, speed, 1); AI_VALUE(LastMovement&, "last movement").Set(mapId, x, y, z, bot->GetOrientation(), 1000, priority); @@ -212,10 +207,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, if (distance > 0.01f) { MotionMaster& mm = *vehicleBase->GetMotionMaster(); // need to move vehicle, not bot - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - if (!CanStartMoveSpline(bot)) - return false; - // End Fix mm.Clear(); if (!backwards) { @@ -251,10 +242,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // botAI->InterruptSpell(); // } MotionMaster& mm = *bot->GetMotionMaster(); - //[Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - if (!CanStartMoveSpline(bot)) - return false; - // End Fix mm.Clear(); if (!backwards) { @@ -297,10 +284,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle, // } MotionMaster& mm = *bot->GetMotionMaster(); G3D::Vector3 endP = path.back(); - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - if (!CanStartMoveSpline(bot)) - return false; - // End Fix mm.Clear(); if (!backwards) { diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index 12228603..cff34f8d 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -147,8 +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->TeleportTo(bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f); - TeleportToSafe(bot, bot->GetMapId(), spiritHealer->GetPositionX(), spiritHealer->GetPositionY(), spiritHealer->GetPositionZ(), 0.f); // [Fix] Avoid silly teleport + 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"); } @@ -245,8 +244,7 @@ int64 RepopAction::CalculateDeadTime() const void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) const { - // bot->TeleportTo(graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f); - TeleportToSafe(bot, graveyard->Map, graveyard->x, graveyard->y, graveyard->z, 0.f); // [Fix] Avoid Silly teleport + 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"); } diff --git a/src/strategy/actions/ReviveFromCorpseAction.cpp b/src/strategy/actions/ReviveFromCorpseAction.cpp index f55904cb..960f438f 100644 --- a/src/strategy/actions/ReviveFromCorpseAction.cpp +++ b/src/strategy/actions/ReviveFromCorpseAction.cpp @@ -169,8 +169,7 @@ bool FindCorpseAction::Execute(Event event) if (deadTime > delay) { bot->GetMotionMaster()->Clear(); - // bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0); - TeleportToSafe(bot, moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0); // [fix] Avoid Silly Teleport + bot->TeleportTo(moveToPos.getMapId(), moveToPos.getX(), moveToPos.getY(), moveToPos.getZ(), 0); } moved = true; @@ -351,8 +350,7 @@ bool SpiritHealerAction::Execute(Event event) // if (!botAI->HasActivePlayerMaster()) // { context->GetValue("death count")->Set(dCount + 1); - // return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f); - return TeleportToSafe(bot, ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f); // [Fix] Avoid Silly teleport + return bot->TeleportTo(ClosestGrave->Map, ClosestGrave->x, ClosestGrave->y, ClosestGrave->z, 0.f); // } // LOG_INFO("playerbots", "Bot {} {}:{} <{}> can't find a spirit healer", bot->GetGUID().ToString().c_str(), diff --git a/src/strategy/raids/icecrown/RaidIccActions.cpp b/src/strategy/raids/icecrown/RaidIccActions.cpp index 0d135b59..eaad5d2f 100644 --- a/src/strategy/raids/icecrown/RaidIccActions.cpp +++ b/src/strategy/raids/icecrown/RaidIccActions.cpp @@ -957,8 +957,7 @@ bool IccGunshipTeleportHordeAction::Execute(Event event) bool IccGunshipTeleportHordeAction::TeleportTo(const Position& position) { - // return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), - return TeleportToSafe(bot, bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),// [Fix]Avoid silly teleport + return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), bot->GetOrientation()); } diff --git a/src/strategy/raids/naxxramas/RaidNaxxActions.cpp b/src/strategy/raids/naxxramas/RaidNaxxActions.cpp index 059cea79..cf12bb8e 100644 --- a/src/strategy/raids/naxxramas/RaidNaxxActions.cpp +++ b/src/strategy/raids/naxxramas/RaidNaxxActions.cpp @@ -9,7 +9,6 @@ #include "RaidNaxxStrategy.h" #include "ScriptedCreature.h" #include "SharedDefines.h" -#include "BotMovementUtils.h" bool GrobbulusGoBehindAction::Execute(Event event) { @@ -259,26 +258,11 @@ bool RazuviousUseObedienceCrystalAction::Execute(Event event) return false; } if (charm->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE) == NULL_MOTION_TYPE) - /*{ + { charm->GetMotionMaster()->Clear(); charm->GetMotionMaster()->MoveChase(target); - charm->GetAI()->AttackStart(target); - }*/ - // [Fix: MoveSplineInitArgs::Validate: expression 'velocity > 0.01f' failed for GUID Full:] - { - if (CanStartMoveSpline(charm)) - { - charm->GetMotionMaster()->Clear(); - charm->GetMotionMaster()->MoveChase(target); - } - else - { - charm->StopMoving(); - } - charm->GetAI()->AttackStart(target); } - // End Fix Aura* forceObedience = botAI->GetAura("force obedience", charm); uint32 duration_time; if (!forceObedience) diff --git a/src/strategy/raids/ulduar/RaidUlduarActions.cpp b/src/strategy/raids/ulduar/RaidUlduarActions.cpp index 8517a0bf..3d180820 100644 --- a/src/strategy/raids/ulduar/RaidUlduarActions.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarActions.cpp @@ -1357,14 +1357,10 @@ bool KologarnMarkDpsTargetAction::Execute(Event event) bool KologarnFallFromFloorAction::Execute(Event event) { - /*return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), + return bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(), - ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation());*/ - return TeleportToSafe(bot, bot->GetMapId(), ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionX(), // [Fix] Avoid silly teleport - ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionY(), - ULDUAR_KOLOGARN_RESTORE_POSITION.GetPositionZ(), - ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation()); + ULDUAR_KOLOGARN_RESTORE_POSITION.GetOrientation()); } bool KologarnFallFromFloorAction::isUseful() @@ -1411,18 +1407,14 @@ bool KologarnEyebeamAction::Execute(Event event) KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI); if (runToLeftSide) { - // teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(), - teleportedToPoint = TeleportToSafe(bot, bot->GetMapId(), - ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(), + teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionX(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionY(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetPositionZ(), ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION.GetOrientation()); } else { - // teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(), - teleportedToPoint = TeleportToSafe(bot, bot->GetMapId(), - ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(), + teleportedToPoint = bot->TeleportTo(bot->GetMapId(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionX(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionY(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetPositionZ(), ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION.GetOrientation()); diff --git a/src/strategy/raids/vaultofarchavon/RaidVoAActions.cpp b/src/strategy/raids/vaultofarchavon/RaidVoAActions.cpp index b41c670f..05d6328e 100644 --- a/src/strategy/raids/vaultofarchavon/RaidVoAActions.cpp +++ b/src/strategy/raids/vaultofarchavon/RaidVoAActions.cpp @@ -175,13 +175,9 @@ bool EmalonOverchargeAction::isUseful() bool EmalonFallFromFloorAction::Execute(Event event) { - /*return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), + return bot->TeleportTo(bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), VOA_EMALON_RESTORE_POSITION.GetPositionY(), VOA_EMALON_RESTORE_POSITION.GetPositionZ(), - VOA_EMALON_RESTORE_POSITION.GetOrientation());*/ - return TeleportToSafe(bot, bot->GetMapId(), VOA_EMALON_RESTORE_POSITION.GetPositionX(), //[Fix] Avoid Silly Teleport - VOA_EMALON_RESTORE_POSITION.GetPositionY(), - VOA_EMALON_RESTORE_POSITION.GetPositionZ(), - VOA_EMALON_RESTORE_POSITION.GetOrientation()); + VOA_EMALON_RESTORE_POSITION.GetOrientation()); } bool EmalonFallFromFloorAction::isUseful() diff --git a/src/strategy/rpg/NewRpgBaseAction.cpp b/src/strategy/rpg/NewRpgBaseAction.cpp index 9b0ab2a1..5e71c5c5 100644 --- a/src/strategy/rpg/NewRpgBaseAction.cpp +++ b/src/strategy/rpg/NewRpgBaseAction.cpp @@ -67,8 +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); - // return bot->TeleportTo(dest); - return TeleportToSafe(bot, dest); //[Fix] Avoid Silly teleport + return bot->TeleportTo(dest); } float dis = bot->GetExactDist(dest); diff --git a/src/strategy/values/TankTargetValue.cpp b/src/strategy/values/TankTargetValue.cpp index 048ef8a5..bcef1a3a 100644 --- a/src/strategy/values/TankTargetValue.cpp +++ b/src/strategy/values/TankTargetValue.cpp @@ -14,7 +14,7 @@ class FindTargetForTankStrategy : public FindNonCcTargetStrategy public: FindTargetForTankStrategy(PlayerbotAI* botAI) : FindNonCcTargetStrategy(botAI), minThreat(0) {} - /*void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override { if (!creature || !creature->IsAlive()) { @@ -37,43 +37,6 @@ public: return; } } - if (minThreat >= threat) - { - minThreat = threat; - result = creature; - } - }*/ - - void CheckAttacker(Unit* creature, ThreatMgr* threatMgr) override - { - // [Crash fix] Filter out anything that is not ready/valid - if (!creature || !creature->IsAlive() || !creature->IsInWorld() || creature->IsDuringRemoveFromWorld()) - return; - - if (!threatMgr) - return; - - Player* bot = botAI->GetBot(); - if (!bot) - return; - - float threat = threatMgr->GetThreat(bot); - - if (!result || !result->IsAlive() || !result->IsInWorld() || result->IsDuringRemoveFromWorld()) - { - // [Crash fix] If the previous target has become invalid, restart cleanly - minThreat = threat; - result = creature; - } - - // Neglect si la victime actuelle est le MT (ou s'il n'y a pas de victime) - if (HostileReference* cv = threatMgr->getCurrentVictim()) - { - Unit* victim = cv->getTarget(); - if (victim && victim->ToPlayer() && botAI->IsMainTank(victim->ToPlayer())) - return; - } - if (minThreat >= threat) { minThreat = threat; @@ -90,7 +53,7 @@ class FindTankTargetSmartStrategy : public FindTargetStrategy public: FindTankTargetSmartStrategy(PlayerbotAI* botAI) : FindTargetStrategy(botAI) {} - /*void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override + void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override { if (Group* group = botAI->GetBot()->GetGroup()) { @@ -106,32 +69,8 @@ public: { result = attacker; } - }*/ - void CheckAttacker(Unit* attacker, ThreatMgr* /*threatMgr*/) override - { - // [Crash fix] Protect against null/out-of-world/being-removed units - if (!attacker || !attacker->IsAlive() || !attacker->IsInWorld() || attacker->IsDuringRemoveFromWorld()) - return; - - if (Player* me = botAI->GetBot()) - { - if (Group* group = me->GetGroup()) - { - ObjectGuid guid = group->GetTargetIcon(4); - if (guid && attacker->GetGUID() == guid) - return; - } - } - - // [Crash fix] If 'result' has become invalid, forget it - if (result && (!result->IsAlive() || !result->IsInWorld() || result->IsDuringRemoveFromWorld())) - result = nullptr; - - if (!result || IsBetter(attacker, result)) - result = attacker; } - - /*bool IsBetter(Unit* new_unit, Unit* old_unit) + bool IsBetter(Unit* new_unit, Unit* old_unit) { Player* bot = botAI->GetBot(); // if group has multiple tanks, main tank just focus on the current target @@ -158,47 +97,8 @@ public: return new_dis < old_dis; } return new_threat < old_threat; - }*/ - bool IsBetter(Unit* new_unit, Unit* old_unit) - { - // [Crash fix] If either one is invalid, decide straight away - if (!new_unit || !new_unit->IsAlive() || !new_unit->IsInWorld() || new_unit->IsDuringRemoveFromWorld()) - return false; - if (!old_unit || !old_unit->IsAlive() || !old_unit->IsInWorld() || old_unit->IsDuringRemoveFromWorld()) - return true; - - Player* bot = botAI->GetBot(); - if (!bot) - return false; - - // if multiple tanks, logically focus on the current target - Unit* currentTarget = botAI->GetAiObjectContext()->GetValue("current target")->Get(); - if (currentTarget && botAI->IsMainTank(bot) && botAI->GetGroupTankNum(bot) > 1) - { - if (old_unit == currentTarget) - return false; - if (new_unit == currentTarget) - return true; - } - - float new_threat = new_unit->GetThreatMgr().GetThreat(bot); - float old_threat = old_unit->GetThreatMgr().GetThreat(bot); - float new_dis = bot->GetDistance(new_unit); - float old_dis = bot->GetDistance(old_unit); - - // hasAggro? -> withinMelee? -> threat - int nl = GetIntervalLevel(new_unit); - int ol = GetIntervalLevel(old_unit); - if (nl != ol) - return nl > ol; - - if (nl == 2) - return new_dis < old_dis; - - return new_threat < old_threat; } - - /*int32_t GetIntervalLevel(Unit* unit) + int32_t GetIntervalLevel(Unit* unit) { if (!botAI->HasAggro(unit)) { @@ -209,28 +109,12 @@ public: return 1; } return 0; - }*/ - int32_t GetIntervalLevel(Unit* unit) - { - // [Crash fix] Basic guards - if (!unit || !unit->IsAlive() || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) - return 0; - - if (!botAI->HasAggro(unit)) - return 2; - - if (Player* bot = botAI->GetBot()) - { - if (bot->IsWithinMeleeRange(unit)) - return 1; - } - return 0; } }; Unit* TankTargetValue::Calculate() { - // [Note] Using the "smart" strategy below. Guards have been added in CheckAttacker/IsBetter. + // FindTargetForTankStrategy strategy(botAI); FindTankTargetSmartStrategy strategy(botAI); return FindTarget(&strategy); } diff --git a/src/strategy/values/TargetValue.cpp b/src/strategy/values/TargetValue.cpp index 9b613681..ebffd328 100644 --- a/src/strategy/values/TargetValue.cpp +++ b/src/strategy/values/TargetValue.cpp @@ -14,7 +14,7 @@ Unit* FindTargetStrategy::GetResult() { return result; } -/*Unit* TargetValue::FindTarget(FindTargetStrategy* strategy) +Unit* TargetValue::FindTarget(FindTargetStrategy* strategy) { GuidVector attackers = botAI->GetAiObjectContext()->GetValue("attackers")->Get(); for (ObjectGuid const guid : attackers) @@ -27,28 +27,6 @@ Unit* FindTargetStrategy::GetResult() { return result; } strategy->CheckAttacker(unit, &ThreatMgr); } - return strategy->GetResult(); -}*/ - -Unit* TargetValue::FindTarget(FindTargetStrategy* strategy) -{ - // [Crash fix] The very first AI tick can occur before everything is "in world". - // Filter out units that are non-living / being removed / out of world. - AiObjectContext* ctx = botAI->GetAiObjectContext(); - if (!ctx) - return strategy->GetResult(); - - GuidVector attackers = ctx->GetValue("attackers")->Get(); - for (ObjectGuid const& guid : attackers) - { - Unit* unit = botAI->GetUnit(guid); - if (!unit || !unit->IsAlive() || !unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) - continue; - - ThreatMgr& threatMgrRef = unit->GetThreatMgr(); - strategy->CheckAttacker(unit, &threatMgrRef); - } - return strategy->GetResult(); }