diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index bb5f4bdc0..b554ef7ad 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -7492,6 +7492,10 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId); sOutdoorPvPMgr.HandlePlayerEnterZone(this, newZone); +#ifdef ENABLE_PLAYERBOTS + sRandomPlayerbotMgr.OnPlayerZoneChange(this, newZone); +#endif + SendInitWorldStates(newZone); // only if really enters to new zone, not just area change, works strange... if (sWorld.getConfig(CONFIG_BOOL_WEATHER)) diff --git a/src/modules/Bots/playerbot/PlayerbotAI.cpp b/src/modules/Bots/playerbot/PlayerbotAI.cpp index 2ce5af298..30527cf94 100644 --- a/src/modules/Bots/playerbot/PlayerbotAI.cpp +++ b/src/modules/Bots/playerbot/PlayerbotAI.cpp @@ -305,6 +305,13 @@ void PlayerbotAI::UpdateAI(uint32 elapsed) return; } + if (sPlayerbotAIConfig.randomBotActiveZoneOnly && !bot->GetGroup() && sRandomPlayerbotMgr.IsRandomBot(bot) && + !sRandomPlayerbotMgr.HasRealPlayerInZone(bot->GetZoneId())) + { + SetNextCheckDelay(5000); + return; + } + if (nextAICheckDelay > sPlayerbotAIConfig.globalCoolDown && bot->IsNonMeleeSpellCasted(true, true, false) && *GetAiObjectContext()->GetValue("invalid target", "current target")) diff --git a/src/modules/Bots/playerbot/PlayerbotAIConfig.cpp b/src/modules/Bots/playerbot/PlayerbotAIConfig.cpp index c5dd9bef2..285b96c85 100644 --- a/src/modules/Bots/playerbot/PlayerbotAIConfig.cpp +++ b/src/modules/Bots/playerbot/PlayerbotAIConfig.cpp @@ -64,6 +64,7 @@ PlayerbotAIConfig::PlayerbotAIConfig() randomBotJoinLfg(false), randomBotLoginAtStartup(false), randomBotKeepGroups(false), + randomBotActiveZoneOnly(false), randomBotTeleLevel(0), logInGroupOnly(false), logValuesPerTick(false), @@ -190,6 +191,7 @@ bool PlayerbotAIConfig::Initialize() randomBotMaxLevel = config.GetIntDefault("AiPlayerbot.RandomBotMaxLevel", 255); randomBotLoginAtStartup = config.GetBoolDefault("AiPlayerbot.RandomBotLoginAtStartup", true); randomBotKeepGroups = config.GetBoolDefault("AiPlayerbot.RandomBotKeepGroups", false); + randomBotActiveZoneOnly = config.GetBoolDefault("AiPlayerbot.RandomBotActiveZoneOnly", false); randomBotTeleLevel = config.GetIntDefault("AiPlayerbot.RandomBotTeleLevel", 3); randomChangeMultiplier = config.GetFloatDefault("AiPlayerbot.RandomChangeMultiplier", 1.0); diff --git a/src/modules/Bots/playerbot/PlayerbotAIConfig.h b/src/modules/Bots/playerbot/PlayerbotAIConfig.h index a96b632e1..fb616616f 100644 --- a/src/modules/Bots/playerbot/PlayerbotAIConfig.h +++ b/src/modules/Bots/playerbot/PlayerbotAIConfig.h @@ -69,6 +69,7 @@ class PlayerbotAIConfig bool randomBotJoinLfg; ///< Indicates if random bots should join Looking For Group. bool randomBotLoginAtStartup; ///< Indicates if random bots should login at startup. bool randomBotKeepGroups; ///< Indicates if random bots should preserve groups across restarts. + bool randomBotActiveZoneOnly; ///< If true, ungrouped random bots only tick when a real player is in their zone. uint32 randomBotTeleLevel; ///< The teleport level for random bots. bool logInGroupOnly, logValuesPerTick; bool fleeingEnabled; ///< Indicates if fleeing is enabled for bots. diff --git a/src/modules/Bots/playerbot/RandomPlayerbotMgr.cpp b/src/modules/Bots/playerbot/RandomPlayerbotMgr.cpp index 2daac4064..50913d506 100755 --- a/src/modules/Bots/playerbot/RandomPlayerbotMgr.cpp +++ b/src/modules/Bots/playerbot/RandomPlayerbotMgr.cpp @@ -1004,6 +1004,16 @@ void RandomPlayerbotMgr::OnPlayerLogout(Player* player) { players.erase(i); } + + uint32 zone = player->GetZoneId(); + std::unordered_map::iterator zi = m_playerZoneCounts.find(zone); + if (zi != m_playerZoneCounts.end()) + { + if (zi->second <= 1) + m_playerZoneCounts.erase(zi); + else + zi->second--; + } } } @@ -1042,7 +1052,35 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) { players.push_back(player); sLog.outDebug("Including non-random bot player %s into random bot update", player->GetName()); + m_playerZoneCounts[player->GetZoneId()]++; + } +} + +void RandomPlayerbotMgr::OnPlayerZoneChange(Player* player, uint32 newZone) +{ + if (player->GetPlayerbotAI()) + return; + + uint32 oldZone = player->GetZoneId(); + if (oldZone == newZone) + return; + + std::unordered_map::iterator zi = m_playerZoneCounts.find(oldZone); + if (zi != m_playerZoneCounts.end()) + { + if (zi->second <= 1) + m_playerZoneCounts.erase(zi); + else + zi->second--; } + + m_playerZoneCounts[newZone]++; +} + +bool RandomPlayerbotMgr::HasRealPlayerInZone(uint32 zoneId) const +{ + std::unordered_map::const_iterator zi = m_playerZoneCounts.find(zoneId); + return zi != m_playerZoneCounts.end() && zi->second > 0; } Player* RandomPlayerbotMgr::GetRandomPlayer() diff --git a/src/modules/Bots/playerbot/RandomPlayerbotMgr.h b/src/modules/Bots/playerbot/RandomPlayerbotMgr.h index 875ebf818..d7afda47a 100644 --- a/src/modules/Bots/playerbot/RandomPlayerbotMgr.h +++ b/src/modules/Bots/playerbot/RandomPlayerbotMgr.h @@ -5,6 +5,7 @@ #include "PlayerbotAIBase.h" #include "PlayerbotMgr.h" #include +#include class WorldPacket; class Player; @@ -99,6 +100,9 @@ class RandomPlayerbotMgr : public PlayerbotHolder */ void OnPlayerLogin(Player* player); + void OnPlayerZoneChange(Player* player, uint32 newZone); + bool HasRealPlayerInZone(uint32 zoneId) const; + /** * @brief Gets a random player. * @return Pointer to the random player. @@ -235,6 +239,7 @@ class RandomPlayerbotMgr : public PlayerbotHolder set m_groupedBots; ///< Cached set of bot GUIDs currently in a group, refreshed each update cycle. std::map m_areaCreatureStatsMap; std::map, uint32> m_cellToAreaCache; + std::unordered_map m_playerZoneCounts; ///< zone_id -> real player count, for O(1) bot tick gating. }; #define sRandomPlayerbotMgr MaNGOS::Singleton::Instance() diff --git a/src/modules/Bots/playerbot/aiplayerbot.conf.dist.in b/src/modules/Bots/playerbot/aiplayerbot.conf.dist.in index 63c4bfb4d..84b989876 100644 --- a/src/modules/Bots/playerbot/aiplayerbot.conf.dist.in +++ b/src/modules/Bots/playerbot/aiplayerbot.conf.dist.in @@ -164,6 +164,10 @@ AiPlayerbot.RandomBotTeleLevel = 3 # Preserve bot group assignments across server restarts #AiPlayerbot.RandomBotKeepGroups = 0 +# Only tick ungrouped random bots when a real player is in their zone. +# Reduces CPU load on empty zones at the cost of bots being frozen there. +#AiPlayerbot.RandomBotActiveZoneOnly = 0 + # How far random bots are teleported after death #AiPlayerbot.RandomBotTeleportDistance = 1000