From 9f10b1a1ef306b1926bf691f1558736d94ecc2cf Mon Sep 17 00:00:00 2001 From: agatho Date: Wed, 4 Feb 2026 05:24:09 +0100 Subject: [PATCH] fix(spawner): Allow warm pool bots to bypass MaxBots limit Warm pool and JIT bots for BG/dungeon/arena are temporary special-purpose bots that should not be blocked by the MaxBots configuration setting. Changes: - Add bypassMaxBotsLimit field to SpawnRequest struct - Pass bypassMaxBotsLimit through BotSpawner chain to AddPlayerBot - Set bypassMaxBotsLimit=true in InstanceBotPool::WarmUpBot This fixes the issue where setting MaxBots=0 (to disable world population bots) also blocked warm pool bots from spawning for battleground queues. Co-Authored-By: Claude Opus 4.5 Signed-off-by: luis --- .../Playerbot/Lifecycle/BotSpawner.cpp | 37 ++++++++++++++++--- src/modules/Playerbot/Lifecycle/BotSpawner.h | 2 +- .../Lifecycle/Instance/InstanceBotPool.cpp | 1 + .../Playerbot/Lifecycle/SpawnRequest.h | 1 + 4 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/modules/Playerbot/Lifecycle/BotSpawner.cpp b/src/modules/Playerbot/Lifecycle/BotSpawner.cpp index 8d3a24709..c4193df5c 100644 --- a/src/modules/Playerbot/Lifecycle/BotSpawner.cpp +++ b/src/modules/Playerbot/Lifecycle/BotSpawner.cpp @@ -62,6 +62,7 @@ #include "MotionMaster.h" #include "RealmList.h" #include "DB2Stores.h" +#include "DBCEnums.h" // WoW 12.0: MAP_WOWLABS, MAP_HOUSE_INTERIOR, MAP_HOUSE_NEIGHBORHOOD #include "WorldSession.h" #include "Movement/BotWorldPositioner.h" #include @@ -657,9 +658,10 @@ bool BotSpawner::SpawnBotInternal(SpawnRequest const& request) } } -bool BotSpawner::CreateBotSession(uint32 accountId, ObjectGuid characterGuid) +bool BotSpawner::CreateBotSession(uint32 accountId, ObjectGuid characterGuid, bool bypassMaxBotsLimit) { - TC_LOG_INFO("module.playerbot.spawner", " Creating bot session for account {}, character {}", accountId, characterGuid.ToString()); + TC_LOG_INFO("module.playerbot.spawner", " Creating bot session for account {}, character {}, bypassLimit={}", + accountId, characterGuid.ToString(), bypassMaxBotsLimit); // DISABLED: Legacy BotSessionMgr creates invalid account IDs // Use the BotSessionMgr to create a new bot session with ASYNC character login (legacy approach) @@ -672,7 +674,8 @@ bool BotSpawner::CreateBotSession(uint32 accountId, ObjectGuid characterGuid) // } // PRIMARY: Use the fixed native TrinityCore login approach with proper account IDs - if (!Playerbot::sBotWorldSessionMgr->AddPlayerBot(characterGuid, accountId)) + // Pass bypassMaxBotsLimit for pool/JIT bots that should bypass MaxBots config + if (!Playerbot::sBotWorldSessionMgr->AddPlayerBot(characterGuid, accountId, bypassMaxBotsLimit)) { TC_LOG_ERROR("module.playerbot.spawner", " Failed to create native WorldSession for character {}", characterGuid.ToString()); @@ -1085,10 +1088,11 @@ void BotSpawner::ContinueSpawnWithCharacter(ObjectGuid characterGuid, SpawnReque return; } - TC_LOG_INFO("module.playerbot.spawner", " Continuing spawn with character {} for account {}", characterGuid.ToString(), actualAccountId); + TC_LOG_INFO("module.playerbot.spawner", " Continuing spawn with character {} for account {} (bypassLimit={})", + characterGuid.ToString(), actualAccountId, request.bypassMaxBotsLimit); - // Create bot session - if (!CreateBotSession(actualAccountId, characterGuid)) + // Create bot session - pass bypassMaxBotsLimit for pool/JIT bots + if (!CreateBotSession(actualAccountId, characterGuid, request.bypassMaxBotsLimit)) { TC_LOG_ERROR("module.playerbot.spawner", "Failed to create bot session for character {}", characterGuid.ToString()); @@ -1401,6 +1405,27 @@ bool BotSpawner::CanSpawnInZone(uint32 zoneId) const bool BotSpawner::CanSpawnOnMap(uint32 mapId) const { + // WoW 12.0: Check map type exclusions for housing and WowLabs maps + MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); + if (mapEntry) + { + // Exclude housing maps (player housing interiors and neighborhoods) + // Exclude WowLabs maps (Plunderstorm and experimental game modes) + switch (mapEntry->InstanceType) + { + case MAP_WOWLABS: // Plunderstorm/experimental + case MAP_HOUSE_INTERIOR: // Player housing interior + case MAP_HOUSE_NEIGHBORHOOD: // Player housing neighborhood + TC_LOG_DEBUG("module.playerbot.spawner", + "CanSpawnOnMap: Rejecting map {} - map type {} is excluded (housing/WowLabs)", + mapId, static_cast(mapEntry->InstanceType)); + return false; + default: + break; + } + } + + // Check population cap uint32 mapBotCount = 0; for (auto const& [zoneId, population] : _zonePopulations) { diff --git a/src/modules/Playerbot/Lifecycle/BotSpawner.h b/src/modules/Playerbot/Lifecycle/BotSpawner.h index a39bf8bf2..c12336c35 100644 --- a/src/modules/Playerbot/Lifecycle/BotSpawner.h +++ b/src/modules/Playerbot/Lifecycle/BotSpawner.h @@ -200,7 +200,7 @@ private: // Internal spawning bool SpawnBotInternal(SpawnRequest const& request); - bool CreateBotSession(uint32 accountId, ObjectGuid characterGuid); + bool CreateBotSession(uint32 accountId, ObjectGuid characterGuid, bool bypassMaxBotsLimit = false); bool ValidateSpawnRequest(SpawnRequest const& request) const; // Phase 2: Priority assignment for spawn requests diff --git a/src/modules/Playerbot/Lifecycle/Instance/InstanceBotPool.cpp b/src/modules/Playerbot/Lifecycle/Instance/InstanceBotPool.cpp index fb135b55f..f0833d752 100644 --- a/src/modules/Playerbot/Lifecycle/Instance/InstanceBotPool.cpp +++ b/src/modules/Playerbot/Lifecycle/Instance/InstanceBotPool.cpp @@ -1773,6 +1773,7 @@ bool InstanceBotPool::WarmUpBot(ObjectGuid botGuid) request.type = SpawnRequest::SPECIFIC_CHARACTER; request.accountId = accountId; request.characterGuid = botGuid; + request.bypassMaxBotsLimit = true; // Pool bots bypass MaxBots limit - they're temporary for BG/dungeon/arena request.callback = [this, botGuid](bool success, ObjectGuid guid) { if (success) { diff --git a/src/modules/Playerbot/Lifecycle/SpawnRequest.h b/src/modules/Playerbot/Lifecycle/SpawnRequest.h index d8cfdabf0..4fb061315 100644 --- a/src/modules/Playerbot/Lifecycle/SpawnRequest.h +++ b/src/modules/Playerbot/Lifecycle/SpawnRequest.h @@ -31,6 +31,7 @@ struct SpawnRequest uint8 classFilter = 0; uint8 raceFilter = 0; uint32 maxBotsPerZone = 50; + bool bypassMaxBotsLimit = false; // For pool/JIT bots that should bypass MaxBots config // Callback on spawn completion ::std::function callback;