diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 48c44ae0..09a6ef82 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -3970,15 +3970,37 @@ bool IsAlliance(uint8 race) bool PlayerbotAI::HasRealPlayerMaster() { - if (master) +// if (master) +// { +// PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); +// return !masterBotAI || masterBotAI->IsRealPlayer(); +// } +// +// return false; + + // Removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) + /* 1) The "master" pointer can be null if the bot was created + without a master player or if the master was just removed. */ + if (!master) + return false; + + /* 2) Is the master player still present in the world? + If FindPlayer fails, we invalidate "master" and stop here. */ + if (!ObjectAccessor::FindPlayer(master->GetGUID())) { - PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master); - return !masterBotAI || masterBotAI->IsRealPlayer(); + master = nullptr; // avoids repeating the check on the next tick + return false; } - return false; + /* 3) If the master is a bot, we check that it is itself controlled + by a real player. Otherwise, it's already a real player → true. */ + if (PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master)) + return masterBotAI->IsRealPlayer(); // bot controlled by a player? + + return true; // master = real player } + bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(master); } bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); } diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 027e2bab..591512e3 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -38,6 +38,8 @@ #include "WorldSessionMgr.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) class BotInitGuard { @@ -1726,23 +1728,55 @@ void PlayerbotsMgr::RemovePlayerBotData(ObjectGuid const& guid, bool is_AI) PlayerbotAI* PlayerbotsMgr::GetPlayerbotAI(Player* player) { - if (!(sPlayerbotAIConfig->enabled) || !player) - { - return nullptr; - } - // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) { + // if (!(sPlayerbotAIConfig->enabled) || !player) + // { // return nullptr; // } - auto itr = _playerbotsAIMap.find(player->GetGUID()); - if (itr != _playerbotsAIMap.end()) - { - if (itr->second->IsBotAI()) + // // if (player->GetSession()->isLogingOut() || player->IsDuringRemoveFromWorld()) { + // // return nullptr; + // // } + // auto itr = _playerbotsAIMap.find(player->GetGUID()); + // if (itr != _playerbotsAIMap.end()) + // { + // if (itr->second->IsBotAI()) + // return reinterpret_cast(itr->second); + // } + // + // return nullptr; + + // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) + if (!sPlayerbotAIConfig->enabled || !player) + return nullptr; + + { // protected read + std::shared_lock lock(_aiMutex); + auto itr = _playerbotsAIMap.find(player->GetGUID()); + if (itr != _playerbotsAIMap.end() && itr->second->IsBotAI()) return reinterpret_cast(itr->second); } + // does the player still exist? + if (!ObjectAccessor::FindPlayer(player->GetGUID())) + RemovePlayerbotAI(player->GetGUID()); // orphaned AI -> cleanup + return nullptr; } +// removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) +void PlayerbotsMgr::RemovePlayerbotAI(ObjectGuid const& guid) +{ + std::unique_lock lock(_aiMutex); + + if (auto itr = _playerbotsAIMap.find(guid); itr != _playerbotsAIMap.end()) + { + delete itr->second; + _playerbotsAIMap.erase(itr); + LOG_DEBUG("playerbots", "Removed stale AI entry for GUID {}", static_cast(guid.GetRawValue())); + } + + _playerbotsMgrMap.erase(guid); +} + PlayerbotMgr* PlayerbotsMgr::GetPlayerbotMgr(Player* player) { if (!(sPlayerbotAIConfig->enabled) || !player) diff --git a/src/PlayerbotMgr.h b/src/PlayerbotMgr.h index 5ef31776..44343163 100644 --- a/src/PlayerbotMgr.h +++ b/src/PlayerbotMgr.h @@ -12,6 +12,7 @@ #include "PlayerbotAIBase.h" #include "QueryHolder.h" #include "QueryResult.h" +#include // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) class ChatHandler; class PlayerbotAI; @@ -114,11 +115,13 @@ public: void RemovePlayerBotData(ObjectGuid const& guid, bool is_AI); PlayerbotAI* GetPlayerbotAI(Player* player); + void RemovePlayerbotAI(ObjectGuid const& guid); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) PlayerbotMgr* GetPlayerbotMgr(Player* player); private: std::unordered_map _playerbotsAIMap; std::unordered_map _playerbotsMgrMap; + mutable std::shared_mutex _aiMutex; // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) }; #define sPlayerbotsMgr PlayerbotsMgr::instance() diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index 07b2bf94..27c4c685 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -363,6 +363,10 @@ public: void OnPlayerbotLogout(Player* player) override { + // immediate purge of the bot's AI upon disconnection + if (player && player->GetSession()->IsBot()) + sPlayerbotsMgr->RemovePlayerbotAI(player->GetGUID()); // removes a long-standing crash (0xC0000005 ACCESS_VIOLATION) + if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player)) { PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);