move bot logs to debug part 1 and add db for garr missions

This commit is contained in:
luis
2026-02-21 01:50:51 -03:00
parent 811a491be1
commit 95013cf470
18 changed files with 222 additions and 200 deletions
@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS `character_garrison_missions` (
`dbId` bigint unsigned NOT NULL DEFAULT '0',
`guid` bigint unsigned NOT NULL DEFAULT '0',
`missionRecID` int unsigned NOT NULL DEFAULT '0',
`offerTime` bigint NOT NULL DEFAULT '0',
`offerDuration` int NOT NULL DEFAULT '0',
`startTime` bigint NOT NULL DEFAULT '0',
`travelDuration` int NOT NULL DEFAULT '0',
`missionDuration` int NOT NULL DEFAULT '0',
`missionState` int NOT NULL DEFAULT '0',
`successChance` int NOT NULL DEFAULT '0',
PRIMARY KEY (`dbId`),
KEY `idx_guid` (`guid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+12 -12
View File
@@ -38,7 +38,7 @@ namespace Playerbot
// Validate input parameters
if (!m_bot)
{
TC_LOG_ERROR("module.playerbot", "[{}] BehaviorManager created with null bot pointer!", m_managerName);
TC_LOG_DEBUG("module.playerbot", "[{}] BehaviorManager created with null bot pointer!", m_managerName);
m_enabled.store(false, ::std::memory_order_release);
return;
}
@@ -48,7 +48,7 @@ namespace Playerbot
// ACCESS_VIOLATION in string operations. Safe logging only after IsInWorld().
if (!m_ai)
{
TC_LOG_ERROR("module.playerbot", "[{}] BehaviorManager created with null AI pointer",
TC_LOG_DEBUG("module.playerbot", "[{}] BehaviorManager created with null AI pointer",
m_managerName);
m_enabled.store(false, ::std::memory_order_release);
return;
@@ -94,7 +94,7 @@ namespace Playerbot
if (shouldLog)
{
TC_LOG_ERROR("module.playerbot", " [{}] Update() ENTRY: enabled={}, busy={}, bot={}, botInWorld={}",
TC_LOG_DEBUG("module.playerbot", " [{}] Update() ENTRY: enabled={}, busy={}, bot={}, botInWorld={}",
m_managerName,
m_enabled.load(::std::memory_order_acquire),
m_isBusy.load(::std::memory_order_acquire),
@@ -106,7 +106,7 @@ namespace Playerbot
if (!m_enabled.load(::std::memory_order_acquire))
{
if (shouldLog)
TC_LOG_ERROR("module.playerbot", " [{}] DISABLED - returning early", m_managerName);
TC_LOG_DEBUG("module.playerbot", " [{}] DISABLED - returning early", m_managerName);
return;
}
@@ -114,7 +114,7 @@ namespace Playerbot
if (m_isBusy.load(::std::memory_order_acquire))
{
if (shouldLog)
TC_LOG_ERROR("module.playerbot", "⏳ [{}] BUSY - returning early", m_managerName);
TC_LOG_DEBUG("module.playerbot", "? [{}] BUSY - returning early", m_managerName);
return;
}
// Validate pointers are still valid
@@ -130,7 +130,7 @@ namespace Playerbot
}
if (shouldLog)
TC_LOG_ERROR("module.playerbot", " [{}] ValidatePointers() passed", m_managerName);
TC_LOG_DEBUG("module.playerbot", " [{}] ValidatePointers() passed", m_managerName);
// Handle initialization on first update
if (!m_initialized.load(::std::memory_order_acquire))
@@ -201,14 +201,14 @@ namespace Playerbot
}
catch (const ::std::exception& e)
{
TC_LOG_ERROR("module.playerbot", "[{}] Exception in OnUpdate for bot {}: {}",
TC_LOG_DEBUG("module.playerbot", "[{}] Exception in OnUpdate for bot {}: {}",
m_managerName, m_bot->GetName(), e.what());
// Disable manager after exception to prevent spam
m_enabled.store(false, ::std::memory_order_release);
}
catch (...)
{
TC_LOG_ERROR("module.playerbot", "[{}] Unknown exception in OnUpdate for bot {}",
TC_LOG_DEBUG("module.playerbot", "[{}] Unknown exception in OnUpdate for bot {}",
m_managerName, m_bot->GetName());
// Disable manager after exception to prevent spam
m_enabled.store(false, ::std::memory_order_release);
@@ -300,7 +300,7 @@ namespace Playerbot
// Check AI pointer validity
if (!m_ai)
{
TC_LOG_ERROR("module.playerbot", " [{}] ValidatePointers FAILED: AI pointer is null for bot {}", m_managerName, m_bot->GetName());
TC_LOG_DEBUG("module.playerbot", " [{}] ValidatePointers FAILED: AI pointer is null for bot {}", m_managerName, m_bot->GetName());
return false;
}
@@ -326,7 +326,7 @@ namespace Playerbot
if (shouldLog)
{
TC_LOG_ERROR("module.playerbot", " [{}] ValidatePointers PASSED: Bot {} is valid and in world", m_managerName, m_bot->GetName());
TC_LOG_DEBUG("module.playerbot", " [{}] ValidatePointers PASSED: Bot {} is valid and in world", m_managerName, m_bot->GetName());
}
return true;
@@ -374,7 +374,7 @@ namespace Playerbot
// ValidatePointers() will pass. For now, just do basic pointer checks.
if (!m_bot || !m_ai)
{
TC_LOG_ERROR("module.playerbot", "[{}] Initialize() failed: null bot or AI pointer", m_managerName);
TC_LOG_DEBUG("module.playerbot", "[{}] Initialize() failed: null bot or AI pointer", m_managerName);
return false;
}
@@ -407,4 +407,4 @@ namespace Playerbot
OnEventInternal(event);
}
} // namespace Playerbot
} // namespace Playerbot
+49 -49
View File
@@ -398,7 +398,7 @@ void BotAI::UpdateAI(uint32 diff)
botPtr == 0xDDDDDDDD || botPtr == 0xFEEEFEEE ||
botPtr == 0xCDCDCDCD || botPtr == 0xCCCCCCCC)
{
TC_LOG_ERROR("module.playerbot.ai", "CRITICAL: BotAI::UpdateAI called with invalid _bot pointer 0x{:X} - aborting to prevent crash!", botPtr);
TC_LOG_DEBUG("module.playerbot.ai", "CRITICAL: BotAI::UpdateAI called with invalid _bot pointer 0x{:X} - aborting to prevent crash!", botPtr);
return;
}
}
@@ -411,7 +411,7 @@ void BotAI::UpdateAI(uint32 diff)
// BOT-SPECIFIC LOGIN SPELL CLEANUP: Clear events on first update to prevent LOGINEFFECT crash
// ========================================================================
// Issue: LOGINEFFECT (Spell 836) is cast during SendInitialPacketsAfterAddToMap() at Player.cpp:24742
// and queued in EventProcessor. It fires during first Player::Update() → EventProcessor::Update()
// and queued in EventProcessor. It fires during first Player::Update() ? EventProcessor::Update()
// which causes Spell.cpp:603 assertion failure: m_spellModTakingSpell != this
// Root Cause: Bot doesn't send CMSG_CAST_SPELL ACK packets like real players, leaving stale spell references
// Solution: Clear ALL pending events HERE on first update, BEFORE EventProcessor::Update() can fire them
@@ -434,7 +434,7 @@ void BotAI::UpdateAI(uint32 diff)
if (!loggedFirstBot || (now - lastUpdateLog > 10000))
{
TC_LOG_INFO("module.playerbot", "✅ UpdateAI active: Bot {} (ID: {}), InWorld={}, InCombat={}, InGroup={}, Strategies={}",
TC_LOG_INFO("module.playerbot", "? UpdateAI active: Bot {} (ID: {}), InWorld={}, InCombat={}, InGroup={}, Strategies={}",
_bot->GetName(),
_bot->GetGUID().GetCounter(),
_bot->IsInWorld(),
@@ -456,7 +456,7 @@ void BotAI::UpdateAI(uint32 diff)
// ========================================================================
// LIFECYCLE STATE TRANSITION - Two-Phase AddToWorld Pattern
// ========================================================================
// On first successful UpdateAI, transition from READY → ACTIVE state.
// On first successful UpdateAI, transition from READY ? ACTIVE state.
// This indicates the bot is now fully operational and all managers are safe to use.
// The lifecycle manager processes any queued deferred events at this point.
if (!_lifecycleActivated && _lifecycleManager)
@@ -564,7 +564,7 @@ void BotAI::UpdateAI(uint32 diff)
{
_currentBudgetTier = AIBudgetTier::FULL;
}
// In a group — always FULL (human player may be watching)
// In a group ? always FULL (human player may be watching)
else if (_bot->GetGroup())
{
_currentBudgetTier = AIBudgetTier::FULL;
@@ -618,7 +618,7 @@ void BotAI::UpdateAI(uint32 diff)
}
}
// Apply cheat effects (health/mana regen) — throttled: 500ms in combat, 2s out of combat
// Apply cheat effects (health/mana regen) ? throttled: 500ms in combat, 2s out of combat
if (_cheatEffectTimer <= diff)
{
if (sBotCheatMask->HasAnyCheats(_bot->GetGUID()))
@@ -693,7 +693,7 @@ void BotAI::UpdateAI(uint32 diff)
uint32 nowMs = GameTime::GetGameTimeMS();
if (!lastBGLog.count(botId) || (nowMs - lastBGLog[botId] > 10000))
{
TC_LOG_INFO("module.playerbot.bg", "🎮 BG AI ACTIVE: Bot {} in {} (Instance: {}, Status: IN_PROGRESS)",
TC_LOG_INFO("module.playerbot.bg", "?? BG AI ACTIVE: Bot {} in {} (Instance: {}, Status: IN_PROGRESS)",
_bot->GetName(),
bg->GetName(),
bg->GetInstanceID());
@@ -724,7 +724,7 @@ void BotAI::UpdateAI(uint32 diff)
// Group-related strategies (follow, group_combat) are activated in OnGroupJoined()
if (!_bot->GetGroup() && !_soloStrategiesActivated)
{
TC_LOG_INFO("module.playerbot.ai", "🎯 ACTIVATING SOLO STRATEGIES: Bot {} (not in group, first UpdateAI)", _bot->GetName());
TC_LOG_INFO("module.playerbot.ai", "?? ACTIVATING SOLO STRATEGIES: Bot {} (not in group, first UpdateAI)", _bot->GetName());
// Activate all solo-relevant strategies in priority order:
ActivateStrategy("rest");
@@ -746,7 +746,7 @@ void BotAI::UpdateAI(uint32 diff)
_soloStrategiesActivated = true;
TC_LOG_INFO("module.playerbot.ai", "✅ SOLO BOT ACTIVATION COMPLETE: Bot {} - {} strategies active", _bot->GetName(), _activeStrategies.size());
TC_LOG_INFO("module.playerbot.ai", "? SOLO BOT ACTIVATION COMPLETE: Bot {} - {} strategies active", _bot->GetName(), _activeStrategies.size());
}
// PHASE 0 - Quick Win #3: Periodic group check REMOVED
@@ -797,7 +797,7 @@ void BotAI::UpdateAI(uint32 diff)
catch (...)
{
// Catch any exceptions during member access (e.g., destroyed objects)
TC_LOG_ERROR("playerbot", "Exception while accessing group member for bot {}", _bot->GetName());
TC_LOG_DEBUG("playerbot", "Exception while accessing group member for bot {}", _bot->GetName());
continue;}
}
_objectCache.SetGroupLeader(leader);
@@ -819,28 +819,28 @@ TC_LOG_ERROR("playerbot", "Exception while accessing group member for bot {}", _
// Update internal values and caches (REDUCED+)
UpdateValues(diff);
// Phase 2 Week 3: Update Hybrid AI (Utility AI + Behavior Trees) — FULL only
// Phase 2 Week 3: Update Hybrid AI (Utility AI + Behavior Trees) ? FULL only
if (_currentBudgetTier == AIBudgetTier::FULL)
UpdateHybridAI(diff);
// Update all active strategies (including follow, idle, social) — REDUCED+
// Update all active strategies (including follow, idle, social) ? REDUCED+
// CRITICAL: Must run every frame for smooth following/movement
UpdateStrategies(diff);
// Process all triggers — FULL only
// Process all triggers ? FULL only
if (_currentBudgetTier == AIBudgetTier::FULL)
ProcessTriggers();
// Execute queued and triggered actions — FULL only
// Execute queued and triggered actions ? FULL only
if (_currentBudgetTier == AIBudgetTier::FULL)
UpdateActions(diff);
// Update movement based on strategy decisions — REDUCED+
// Update movement based on strategy decisions ? REDUCED+
// CRITICAL: Must run every frame for smooth movement
UpdateMovement(diff);
// ========================================================================
// DUNGEON AUTONOMY SYSTEM - Autonomous dungeon navigation — FULL only
// DUNGEON AUTONOMY SYSTEM - Autonomous dungeon navigation ? FULL only
// ========================================================================
// Note: Bots in dungeons always have tier FULL (forced in reassessment),
// but guard explicitly for safety.
@@ -859,7 +859,7 @@ TC_LOG_ERROR("playerbot", "Exception while accessing group member for bot {}", _
}
// ========================================================================
// PHASE 2: STATE MANAGEMENT — FULL only
// PHASE 2: STATE MANAGEMENT ? FULL only
// ========================================================================
// REDUCED bots rely on instant combat escalation (per-tick check above)
// to switch to FULL before needing full combat state processing.
@@ -867,7 +867,7 @@ TC_LOG_ERROR("playerbot", "Exception while accessing group member for bot {}", _
UpdateCombatState(diff);
// ========================================================================
// PHASE 3: CLASS-SPECIFIC UPDATES - Combat and Non-Combat — FULL only
// PHASE 3: CLASS-SPECIFIC UPDATES - Combat and Non-Combat ? FULL only
// ========================================================================
if (_currentBudgetTier == AIBudgetTier::FULL)
{
@@ -914,7 +914,7 @@ bg_update_complete:
_gameSystems->Update(diff);
}
// Phase 3: Update Tactical Group Coordinator (throttled to 500ms) — FULL only
// Phase 3: Update Tactical Group Coordinator (throttled to 500ms) ? FULL only
if (_currentBudgetTier == AIBudgetTier::FULL)
{
if (auto tacticalCoordinator = GetTacticalCoordinator())
@@ -930,7 +930,7 @@ bg_update_complete:
// Legacy BotEventSystem removed - events now flow through per-bot EventDispatcher
// ========================================================================
// PHASE 7: SOLO BEHAVIORS - Only when bot is in solo play mode — FULL only
// PHASE 7: SOLO BEHAVIORS - Only when bot is in solo play mode ? FULL only
// ========================================================================
if (_currentBudgetTier == AIBudgetTier::FULL && !IsInCombat() && !IsFollowing())
{
@@ -954,7 +954,7 @@ bg_update_complete:
{
Events::BotEvent evt(StateMachine::EventType::GROUP_JOINED, _bot->GetGUID());
eventDispatcher->Dispatch(std::move(evt));
TC_LOG_INFO("playerbot", "📢 GROUP_JOINED event dispatched for bot {} (reboot detection)", _bot->GetName());
TC_LOG_INFO("playerbot", "?? GROUP_JOINED event dispatched for bot {} (reboot detection)", _bot->GetName());
}
OnGroupJoined(_bot->GetGroup());
@@ -969,7 +969,7 @@ bg_update_complete:
{
Events::BotEvent evt(StateMachine::EventType::GROUP_LEFT, _bot->GetGUID());
eventDispatcher->Dispatch(std::move(evt));
TC_LOG_INFO("playerbot", "📢 GROUP_LEFT event dispatched for bot {}", _bot->GetName());
TC_LOG_INFO("playerbot", "?? GROUP_LEFT event dispatched for bot {}", _bot->GetName());
}
OnGroupLeft();
@@ -1105,7 +1105,7 @@ void BotAI::UpdateCombatState(uint32 diff)
uint32 now = GameTime::GetGameTimeMS();
if (now - lastCombatStateLog > 2000)
{
TC_LOG_ERROR("module.playerbot", "🔍 UpdateCombatState: Bot {} - wasInCombat={}, isInCombat={}, AIState={}, HasVictim={}",_bot ? _bot->GetName() : "null",
TC_LOG_DEBUG("module.playerbot", "?? UpdateCombatState: Bot {} - wasInCombat={}, isInCombat={}, AIState={}, HasVictim={}",_bot ? _bot->GetName() : "null",
wasInCombat, isInCombat,
static_cast<uint32>(_aiState),
(_bot && _bot->GetVictim()) ? "YES" : "NO");
@@ -1116,7 +1116,7 @@ void BotAI::UpdateCombatState(uint32 diff)
if (!wasInCombat && isInCombat)
{
// Entering combat
TC_LOG_ERROR("module.playerbot", "⚔️ ENTERING COMBAT: Bot {}", _bot->GetName());
TC_LOG_DEBUG("module.playerbot", "?? ENTERING COMBAT: Bot {}", _bot->GetName());
SetAIState(BotAIState::COMBAT);
// Find initial target
@@ -1124,7 +1124,7 @@ void BotAI::UpdateCombatState(uint32 diff)
::Unit* target = _objectCache.GetTarget();
if (target)
{
TC_LOG_ERROR("module.playerbot", "🎯 Target from cache: {}", target->GetName());
TC_LOG_DEBUG("module.playerbot", "?? Target from cache: {}", target->GetName());
}
// CRITICAL FIX: Try GetVictim() as fallback if cache has no target
@@ -1134,7 +1134,7 @@ void BotAI::UpdateCombatState(uint32 diff)
target = _bot->GetVictim();
if (target)
{
TC_LOG_ERROR("module.playerbot", "🎯 Target from GetVictim(): {}", target->GetName());
TC_LOG_DEBUG("module.playerbot", "?? Target from GetVictim(): {}", target->GetName());
}
}
@@ -1168,31 +1168,31 @@ void BotAI::UpdateCombatState(uint32 diff)
if (attacker && attacker->IsAlive() && _bot->IsValidAttackTarget(attacker))
{
target = attacker;
TC_LOG_ERROR("module.playerbot", "🎯 Target from getAttackers(): {}", target->GetName());
TC_LOG_DEBUG("module.playerbot", "?? Target from getAttackers(): {}", target->GetName());
break;
}
}
}
catch (...)
{
TC_LOG_ERROR("module.playerbot", "⚠️ Exception while iterating attackers for bot {}", _bot->GetName());
TC_LOG_DEBUG("module.playerbot", "?? Exception while iterating attackers for bot {}", _bot->GetName());
}
}
if (target)
{
TC_LOG_ERROR("module.playerbot", "✅ Calling OnCombatStart() with target {}", target->GetName());
TC_LOG_DEBUG("module.playerbot", "? Calling OnCombatStart() with target {}", target->GetName());
OnCombatStart(target);
}
else
{
TC_LOG_ERROR("module.playerbot", "❌ COMBAT START FAILED: No valid target found!");
TC_LOG_DEBUG("module.playerbot", "? COMBAT START FAILED: No valid target found!");
}
}
else if (wasInCombat && !isInCombat)
{
// Leaving combat
TC_LOG_ERROR("module.playerbot", "🏳️ LEAVING COMBAT: Bot {}", _bot->GetName());OnCombatEnd();
TC_LOG_DEBUG("module.playerbot", "??? LEAVING COMBAT: Bot {}", _bot->GetName());OnCombatEnd();
// Determine new state
if (_bot->GetGroup() && GetStrategy("follow"))
@@ -1362,7 +1362,7 @@ void BotAI::UpdateSoloBehaviors(uint32 diff)
_bot->GetName(), snapshot.entry, snapshot.level);
// Combat will initiate naturally:
// - Bot has target set → ClassAI will cast spells/attack
// - Bot has target set ? ClassAI will cast spells/attack
// - CombatMovementStrategy will handle positioning
// - Threat will be established when damage lands
// NO NEED for explicit Attack() or SetInCombatWith() calls from worker thread
@@ -1445,7 +1445,7 @@ void BotAI::OnCombatEnd()
::MovementGeneratorType currentType = mm->GetCurrentMovementGeneratorType(MOTION_SLOT_ACTIVE);
if (currentType != FOLLOW_MOTION_TYPE && currentType != IDLE_MOTION_TYPE)
{
TC_LOG_ERROR("playerbot", "🧹 OnCombatEnd: Clearing {} motion type for bot {} to allow follow",
TC_LOG_DEBUG("playerbot", "?? OnCombatEnd: Clearing {} motion type for bot {} to allow follow",
static_cast<uint32>(currentType), _bot->GetName());
mm->Clear();
}
@@ -1491,12 +1491,12 @@ void BotAI::OnDeath()
// Initiate death recovery process
if (auto* deathRecoveryManager = GetDeathRecoveryManager())
{
TC_LOG_ERROR("playerbots.ai", "Bot {} died - calling DeathRecoveryManager::OnDeath()", _bot->GetName());
TC_LOG_DEBUG("playerbots.ai", "Bot {} died - calling DeathRecoveryManager::OnDeath()", _bot->GetName());
deathRecoveryManager->OnDeath();
}
else
{
TC_LOG_ERROR("playerbots.ai", "Bot {} died but GetDeathRecoveryManager() returned nullptr! _gameSystems={}",
TC_LOG_DEBUG("playerbots.ai", "Bot {} died but GetDeathRecoveryManager() returned nullptr! _gameSystems={}",
_bot->GetName(), _gameSystems ? "valid" : "null");
}
@@ -1546,12 +1546,12 @@ void BotAI::OnGroupJoined(Group* group)
if (!group && _bot)
group = _bot->GetGroup();
TC_LOG_INFO("module.playerbot.ai", "🚨 OnGroupJoined called for bot {}, provided group={}, bot's group={}",
TC_LOG_INFO("module.playerbot.ai", "?? OnGroupJoined called for bot {}, provided group={}, bot's group={}",
_bot ? _bot->GetName() : "NULL", (void*)group, _bot ? (void*)_bot->GetGroup() : nullptr);
if (!group)
{
TC_LOG_INFO("module.playerbot.ai", "❌ OnGroupJoined: No group available for bot {}",
TC_LOG_INFO("module.playerbot.ai", "? OnGroupJoined: No group available for bot {}",
_bot ? _bot->GetName() : "NULL");
return;
}
@@ -1581,7 +1581,7 @@ void BotAI::OnGroupJoined(Group* group)
// Check if follow strategy existsif (_strategies.find("follow") == _strategies.end())
{
TC_LOG_ERROR("playerbot", "CRITICAL: Follow strategy not found for bot {} - creating emergency fallback",_bot->GetName());
TC_LOG_DEBUG("playerbot", "CRITICAL: Follow strategy not found for bot {} - creating emergency fallback",_bot->GetName());
// Create it immediately while we hold the lock
auto followBehavior = std::make_unique<LeaderFollowBehavior>();
@@ -1591,7 +1591,7 @@ void BotAI::OnGroupJoined(Group* group)
// Check if group combat strategy exists
if (_strategies.find("group_combat") == _strategies.end())
{
TC_LOG_ERROR("playerbot", "CRITICAL: GroupCombat strategy not found for bot {} - creating emergency fallback",_bot->GetName());
TC_LOG_DEBUG("playerbot", "CRITICAL: GroupCombat strategy not found for bot {} - creating emergency fallback",_bot->GetName());
// Create it immediately while we hold the lock
auto groupCombat = std::make_unique<GroupCombatStrategy>();
@@ -1606,7 +1606,7 @@ void BotAI::OnGroupJoined(Group* group)
{
bool wasActive = it->second->IsActive(this);
TC_LOG_ERROR("playerbot", "🔍 OnGroupJoined: Bot {} follow strategy - alreadyInList={}, wasActive={}",_bot->GetName(), alreadyInList, wasActive);
TC_LOG_DEBUG("playerbot", "?? OnGroupJoined: Bot {} follow strategy - alreadyInList={}, wasActive={}",_bot->GetName(), alreadyInList, wasActive);
if (!alreadyInList)
_activeStrategies.push_back("follow");
@@ -1617,7 +1617,7 @@ void BotAI::OnGroupJoined(Group* group)
// This handles server restart where bot loads with group but follow not initialized
strategiesToActivate.push_back(it->second.get());
TC_LOG_ERROR("playerbot", "✅ OnGroupJoined: Bot {} queued follow strategy for OnActivate callback", _bot->GetName());
TC_LOG_DEBUG("playerbot", "? OnGroupJoined: Bot {} queued follow strategy for OnActivate callback", _bot->GetName());
}
}
@@ -1642,11 +1642,11 @@ void BotAI::OnGroupJoined(Group* group)
bool combatActive = std::find(_activeStrategies.begin(), _activeStrategies.end(), "group_combat") != _activeStrategies.end();
if (followActive && combatActive)
{TC_LOG_INFO("playerbot", "✅ Successfully activated follow and group_combat strategies for bot {}", _bot->GetName());
{TC_LOG_INFO("playerbot", "? Successfully activated follow and group_combat strategies for bot {}", _bot->GetName());
}
else
{
TC_LOG_ERROR("playerbot", "❌ Strategy activation FAILED for bot {} - follow={}, combat={}",_bot->GetName(), followActive, combatActive);
TC_LOG_DEBUG("playerbot", "? Strategy activation FAILED for bot {} - follow={}, combat={}",_bot->GetName(), followActive, combatActive);
}
} // RELEASE LOCK - all operations completed
@@ -1723,7 +1723,7 @@ void BotAI::OnGroupLeft()
// Phase 3: TacticalCoordinator cleanup
// NOTE: TacticalCoordinator is now owned by GroupCoordinator (in GameSystemsManager),
// so we don't need to manually reset it here. GroupCoordinator handles its lifecycle.
TC_LOG_INFO("playerbot.coordination", "🔴 Bot {} left group, TacticalCoordinator cleanup handled by GroupCoordinator",
TC_LOG_INFO("playerbot.coordination", "?? Bot {} left group, TacticalCoordinator cleanup handled by GroupCoordinator",
_bot->GetName());
// Activate all solo strategies when leaving a group
@@ -1734,7 +1734,7 @@ void BotAI::OnGroupLeft()
ActivateStrategy("grind"); // Priority: GRIND (40) - fallback when quests unavailable
ActivateStrategy("loot"); // Priority: MOVEMENT (45) - corpse looting
ActivateStrategy("solo"); // Priority: SOLO (10) - fallback coordinator
TC_LOG_INFO("module.playerbot.ai", "🎯 SOLO BOT REACTIVATION: Bot {} reactivated solo strategies after leaving group", _bot->GetName());
TC_LOG_INFO("module.playerbot.ai", "?? SOLO BOT REACTIVATION: Bot {} reactivated solo strategies after leaving group", _bot->GetName());
// Set state to solo if not in combatif (!IsInCombat())
SetAIState(BotAIState::SOLO);_wasInGroup = false;
@@ -1905,7 +1905,7 @@ void BotAI::ActivateStrategy(std::string const& name)
// This handles both: new activations and re-activation of strategies that were improperly added
needsOnActivate = !alreadyInList || !wasActive;
TC_LOG_ERROR("module.playerbot.ai", "🔥 ACTIVATED STRATEGY: '{}' for bot {}, alreadyInList={}, wasActive={}, needsOnActivate={}",name, _bot->GetName(), alreadyInList, wasActive, needsOnActivate);
TC_LOG_DEBUG("module.playerbot.ai", "?? ACTIVATED STRATEGY: '{}' for bot {}, alreadyInList={}, wasActive={}, needsOnActivate={}",name, _bot->GetName(), alreadyInList, wasActive, needsOnActivate);
// Get strategy pointer for callback
strategy = it->second.get();
@@ -1914,7 +1914,7 @@ void BotAI::ActivateStrategy(std::string const& name)
// Call OnActivate hook WITHOUT holding lock if needed
if (strategy && needsOnActivate)
{
TC_LOG_ERROR("module.playerbot.ai", "🎬 Calling OnActivate() for strategy '{}' on bot {}", name, _bot->GetName());
TC_LOG_DEBUG("module.playerbot.ai", "?? Calling OnActivate() for strategy '{}' on bot {}", name, _bot->GetName());
strategy->OnActivate(this);
TC_LOG_DEBUG("playerbot", "Activated strategy '{}' for bot {}", name, _bot->GetName());
}
@@ -2191,7 +2191,7 @@ void BotAI::InitializeDefaultStrategies()
// NOTE: Mutual exclusion rules are automatically configured in BehaviorPriorityManager constructor
// No need to add them here - they're already set up when _priorityManager is initialized
TC_LOG_INFO("module.playerbot.ai", "✅ Initialized follow, group_combat, solo_combat, quest, loot, rest, solo, and grind strategies for bot {}", _bot->GetName());
TC_LOG_INFO("module.playerbot.ai", "? Initialized follow, group_combat, solo_combat, quest, loot, rest, solo, and grind strategies for bot {}", _bot->GetName());
// NOTE: Do NOT activate strategies here!
// Strategy activation happens AFTER bot is fully loaded:
@@ -2406,4 +2406,4 @@ bool BotAI::IsStuck() const
return _movementController && _movementController->IsStuck();
}
} // namespace Playerbot
} // namespace Playerbot
@@ -44,7 +44,7 @@ bool BotSpawnerAdapter::Initialize()
if (!InitializeOrchestrator())
{
TC_LOG_ERROR("module.playerbot.adapter",
TC_LOG_DEBUG("module.playerbot.adapter",
"BotSpawnerAdapter: Failed to initialize orchestrator");
return false;
}
@@ -228,7 +228,7 @@ bool BotSpawnerAdapter::InitializeOrchestrator()
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("module.playerbot.adapter",
TC_LOG_DEBUG("module.playerbot.adapter",
"BotSpawnerAdapter: Exception during orchestrator initialization: {}", ex.what());
return false;
}
@@ -463,7 +463,7 @@ bool LegacyBotSpawnerAdapter::Initialize()
_legacySpawner = BotSpawner::instance();
if (!_legacySpawner)
{
TC_LOG_ERROR("module.playerbot.adapter",
TC_LOG_DEBUG("module.playerbot.adapter",
"LegacyBotSpawnerAdapter: Failed to get BotSpawner singleton instance");
return false;
}
@@ -471,7 +471,7 @@ bool LegacyBotSpawnerAdapter::Initialize()
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("module.playerbot.adapter",
TC_LOG_DEBUG("module.playerbot.adapter",
"LegacyBotSpawnerAdapter: Exception during initialization: {}", ex.what());
return false;
}
@@ -743,4 +743,4 @@ bool BotSpawnerFactory::ShouldUseLegacySpawner()
return sPlayerbotConfig->GetBool("Playerbot.ForceLegacyMode", false);
}
} // namespace Playerbot
} // namespace Playerbot
@@ -153,7 +153,7 @@ CommandResult BotChatCommandHandler::ProcessChatMessage(CommandContext const& co
if (!context.sender || !context.bot || !context.botSession)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Invalid context (null pointers)");
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Invalid context (null pointers)");
return CommandResult::INTERNAL_ERROR;
}
@@ -230,7 +230,7 @@ void BotChatCommandHandler::SendResponse(CommandContext const& context, CommandR
{
if (!context.sender || !context.bot)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Cannot send response - invalid context");
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Cannot send response - invalid context");
return;
}
@@ -357,7 +357,7 @@ CommandResult BotChatCommandHandler::ExecuteCommand(CommandContext const& contex
{
if (!command.handler)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Command '{}' has no handler",
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Command '{}' has no handler",
command.name);
return CommandResult::INTERNAL_ERROR;
}
@@ -377,7 +377,7 @@ CommandResult BotChatCommandHandler::ExecuteCommand(CommandContext const& contex
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Exception executing command '{}': {}",
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Exception executing command '{}': {}",
command.name, ex.what());
CommandResponse response;
@@ -438,7 +438,7 @@ CommandResult BotChatCommandHandler::ProcessNaturalLanguageCommand(CommandContex
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Exception in async NLP processing: {}",
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Exception in async NLP processing: {}",
ex.what());
resp.SetText("Error processing natural language command.");
return CommandResult::EXECUTION_FAILED;
@@ -463,7 +463,7 @@ CommandResult BotChatCommandHandler::ProcessNaturalLanguageCommand(CommandContex
}
else
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Failed to enqueue NLP command");
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Failed to enqueue NLP command");
CommandResponse failResponse;
failResponse.SetText("Failed to queue command for processing.");
@@ -501,7 +501,7 @@ CommandResult BotChatCommandHandler::ProcessNaturalLanguageCommand(CommandContex
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Exception in NLP processing: {}",
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Exception in NLP processing: {}",
ex.what());
CommandResponse response;
@@ -521,13 +521,13 @@ bool BotChatCommandHandler::RegisterCommand(ChatCommand const& command)
if (command.name.empty())
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Cannot register command - empty name");
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Cannot register command - empty name");
return false;
}
if (!command.handler)
{
TC_LOG_ERROR("playerbot.chat", "BotChatCommandHandler: Cannot register command '{}' - no handler",
TC_LOG_DEBUG("playerbot.chat", "BotChatCommandHandler: Cannot register command '{}' - no handler",
command.name);
return false;
}
@@ -1497,7 +1497,7 @@ void AsyncCommandQueue::ProcessCommand(AsyncCommandEntry& entry)
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.chat", "AsyncCommandQueue: Exception executing command {}: {}",
TC_LOG_DEBUG("playerbot.chat", "AsyncCommandQueue: Exception executing command {}: {}",
entry.commandId, ex.what());
response.SetText("Internal error: " + ::std::string(ex.what()));
result = CommandResult::EXECUTION_FAILED;
@@ -661,7 +661,7 @@ namespace Playerbot
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.config", "Failed to parse value for key '%s' on line %u: %s",
TC_LOG_DEBUG("playerbot.config", "Failed to parse value for key '%s' on line %u: %s",
key.c_str(), lineNumber, ex.what());
}
}
@@ -720,7 +720,7 @@ namespace Playerbot
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("playerbot.config", "Exception in config callback for key '%s': %s",
TC_LOG_DEBUG("playerbot.config", "Exception in config callback for key '%s': %s",
key.c_str(), ex.what());
}
}
@@ -95,7 +95,7 @@ void BotOperationTracker::RecordSuccess(BotOperationCategory category, std::stri
metrics.recentSuccess++;
metrics.lastSuccess = std::chrono::system_clock::now();
TC_LOG_TRACE("module.playerbot.diagnostics", "✓ {} success: {} (bot: {})",
TC_LOG_TRACE("module.playerbot.diagnostics", "? {} success: {} (bot: {})",
CategoryToString(category), operation,
botGuid.IsEmpty() ? "N/A" : std::to_string(botGuid.GetCounter()));
}
@@ -190,7 +190,7 @@ void BotOperationTracker::RecordPartial(BotOperationCategory category, std::stri
if (successCount > 0)
metrics.lastSuccess = std::chrono::system_clock::now();
TC_LOG_DEBUG("module.playerbot.diagnostics", "⚠ {} partial: {} ({}/{} success, bot: {})",
TC_LOG_DEBUG("module.playerbot.diagnostics", "? {} partial: {} ({}/{} success, bot: {})",
CategoryToString(category), operation, successCount, successCount + failCount,
botGuid.IsEmpty() ? "N/A" : std::to_string(botGuid.GetCounter()));
}
@@ -220,7 +220,7 @@ void BotOperationTracker::RecordRecovery(uint64 errorId)
_recentErrors[it->second].recovered = true;
_recentErrors[it->second].result = BotOperationResult::SUCCESS;
TC_LOG_DEBUG("module.playerbot.diagnostics", "✓ Error {} recovered after {} retries",
TC_LOG_DEBUG("module.playerbot.diagnostics", "? Error {} recovered after {} retries",
errorId, _recentErrors[it->second].retryCount);
}
}
@@ -416,7 +416,7 @@ void BotOperationTracker::PrintStatus() const
{
TC_LOG_WARN("module.playerbot.diagnostics", "Active Alerts:");
for (auto const& alert : report.activeAlerts)
TC_LOG_WARN("module.playerbot.diagnostics", " ⚠ {}", alert);
TC_LOG_WARN("module.playerbot.diagnostics", " ? {}", alert);
}
}
@@ -512,7 +512,7 @@ void BotOperationTracker::CheckAlerts()
// Alert just triggered
_alertActive[i] = true;
TC_LOG_WARN("module.playerbot.diagnostics",
"⚠ ALERT: {} success rate dropped to {:.1f}% (threshold: {:.1f}%)",
"? ALERT: {} success rate dropped to {:.1f}% (threshold: {:.1f}%)",
CategoryToString(static_cast<BotOperationCategory>(i)),
recentSuccessRate * 100.0f,
_alertThresholds[i] * 100.0f);
@@ -522,7 +522,7 @@ void BotOperationTracker::CheckAlerts()
// Alert recovered
_alertActive[i] = false;
TC_LOG_INFO("module.playerbot.diagnostics",
"✓ RESOLVED: {} success rate recovered to {:.1f}%",
"? RESOLVED: {} success rate recovered to {:.1f}%",
CategoryToString(static_cast<BotOperationCategory>(i)),
recentSuccessRate * 100.0f);
}
@@ -677,8 +677,8 @@ void BotOperationTracker::LogError(BotOperationError const& error)
std::string contentInfo = error.contentId > 0 ?
Trinity::StringFormat(" [content: {}]", error.contentId) : "";
TC_LOG_ERROR("module.playerbot.diagnostics",
"✗ {} ERROR [{}] {}: {} | Bot: {}{}{} | Account: {}",
TC_LOG_DEBUG("module.playerbot.diagnostics",
"? {} ERROR [{}] {}: {} | Bot: {}{}{} | Account: {}",
CategoryToString(error.category),
error.errorCode,
ErrorCodeToString(error.category, error.errorCode),
@@ -290,11 +290,11 @@ void BotNpcLocationService::BuildAreaTriggerCache()
_areaTriggerQuestCache[questId] = areaTriggerID;
questMappings++;
TC_LOG_DEBUG("module.playerbot.services", " Cached quest {} → areatrigger {}", questId, areaTriggerID);
TC_LOG_DEBUG("module.playerbot.services", " Cached quest {} ? areatrigger {}", questId, areaTriggerID);
} while (questResult->NextRow());
}
TC_LOG_INFO("module.playerbot.services", " Cached {} quest→areatrigger mappings", questMappings);
TC_LOG_INFO("module.playerbot.services", " Cached {} quest?areatrigger mappings", questMappings);
// Cache 2: areatrigger table - maps areaTriggerID to position data
// For classic WoW triggers not in DB2 sAreaTriggerStore
@@ -366,14 +366,14 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
// ========================================================================
case QUEST_OBJECTIVE_TALKTO:
{
TC_LOG_ERROR("module.playerbot.services", "🗣️ FindQuestObjectiveLocation: TALKTO objective for NPC entry {}",
TC_LOG_DEBUG("module.playerbot.services", "??? FindQuestObjectiveLocation: TALKTO objective for NPC entry {}",
objective.ObjectID);
// Try 1: Live creature in spatial grid (best quality)
result = TryFindLiveCreature(bot, objective.ObjectID, 500.0f);
if (result.isValid)
{
TC_LOG_ERROR("module.playerbot.services", "✅ TALKTO: Found live NPC {} at ({:.1f}, {:.1f}, {:.1f})",
TC_LOG_DEBUG("module.playerbot.services", "? TALKTO: Found live NPC {} at ({:.1f}, {:.1f}, {:.1f})",
objective.ObjectID, result.position.GetPositionX(),
result.position.GetPositionY(), result.position.GetPositionZ());
return result;
@@ -383,13 +383,13 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
result = FindNearestCreatureSpawn(bot, objective.ObjectID, 500.0f);
if (result.isValid)
{
TC_LOG_ERROR("module.playerbot.services", "✅ TALKTO: Found NPC spawn {} at ({:.1f}, {:.1f}, {:.1f})",
TC_LOG_DEBUG("module.playerbot.services", "? TALKTO: Found NPC spawn {} at ({:.1f}, {:.1f}, {:.1f})",
objective.ObjectID, result.position.GetPositionX(),
result.position.GetPositionY(), result.position.GetPositionZ());
return result;
}
TC_LOG_WARN("module.playerbot.services", "⚠️ TALKTO: NPC {} not found via live search or spawn data",
TC_LOG_WARN("module.playerbot.services", "?? TALKTO: NPC {} not found via live search or spawn data",
objective.ObjectID);
break;
}
@@ -428,14 +428,14 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
if (objective.ObjectID > 0)
{
areaTriggerID = static_cast<uint32>(objective.ObjectID);
TC_LOG_DEBUG("module.playerbot.services", "🗺️ AREATRIGGER: Using ObjectID {} from quest_objectives for quest {}",
TC_LOG_DEBUG("module.playerbot.services", "??? AREATRIGGER: Using ObjectID {} from quest_objectives for quest {}",
areaTriggerID, questId);
}
else
{
// Try 2: FALLBACK - Check cached areatrigger_involvedrelation data (THREAD-SAFE!)
// Some classic quests have ObjectID=-1 but the area trigger is linked via this table
TC_LOG_DEBUG("module.playerbot.services", "⚠️ AREATRIGGER: Quest {} has invalid ObjectID={}, checking cached areatrigger_involvedrelation...",
TC_LOG_DEBUG("module.playerbot.services", "?? AREATRIGGER: Quest {} has invalid ObjectID={}, checking cached areatrigger_involvedrelation...",
questId, objective.ObjectID);
// Use CACHED data instead of runtime query (fixes thread safety crash!)
@@ -443,12 +443,12 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
if (atQuestIt != _areaTriggerQuestCache.end())
{
areaTriggerID = atQuestIt->second;
TC_LOG_DEBUG("module.playerbot.services", "✅ AREATRIGGER: Found area trigger {} via cached areatrigger_involvedrelation for quest {}",
TC_LOG_DEBUG("module.playerbot.services", "? AREATRIGGER: Found area trigger {} via cached areatrigger_involvedrelation for quest {}",
areaTriggerID, questId);
}
else
{
TC_LOG_WARN("module.playerbot.services", "❌ AREATRIGGER: No area trigger found in cached areatrigger_involvedrelation for quest {}",
TC_LOG_WARN("module.playerbot.services", "? AREATRIGGER: No area trigger found in cached areatrigger_involvedrelation for quest {}",
questId);
}
}
@@ -469,7 +469,7 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
result.qualityScore = 100; // Area trigger has exact position - highest quality
result.sourceName = "AreaTrigger";
TC_LOG_ERROR("module.playerbot.services", "✅ EXPLORATION QUEST: Found AreaTrigger {} at ({:.1f}, {:.1f}, {:.1f}) - Map {} - Radius {:.1f}",
TC_LOG_DEBUG("module.playerbot.services", "? EXPLORATION QUEST: Found AreaTrigger {} at ({:.1f}, {:.1f}, {:.1f}) - Map {} - Radius {:.1f}",
areaTriggerID, atEntry->Pos.X, atEntry->Pos.Y, atEntry->Pos.Z,
atEntry->ContinentID, atEntry->Radius);
@@ -477,13 +477,13 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
}
else
{
TC_LOG_WARN("module.playerbot.services", "⚠️ AreaTrigger {} is on different map {} (bot is on map {})",
TC_LOG_WARN("module.playerbot.services", "?? AreaTrigger {} is on different map {} (bot is on map {})",
areaTriggerID, atEntry->ContinentID, bot->GetMapId());
}
}
else
{
TC_LOG_WARN("module.playerbot.services", "⚠️ AreaTrigger {} not found in sAreaTriggerStore (may be classic WoW trigger)",
TC_LOG_WARN("module.playerbot.services", "?? AreaTrigger {} not found in sAreaTriggerStore (may be classic WoW trigger)",
areaTriggerID);
// Try 3: CLASSIC WoW FALLBACK - Use cached world.areatrigger position (THREAD-SAFE!)
@@ -506,20 +506,20 @@ NpcLocationResult BotNpcLocationService::FindQuestObjectiveLocation(Player* bot,
result.qualityScore = 100;
result.sourceName = "AreaTrigger-DB";
TC_LOG_ERROR("module.playerbot.services", "✅ EXPLORATION QUEST: Found classic AreaTrigger {} in DB at ({:.1f}, {:.1f}, {:.1f}) - Map {}",
TC_LOG_DEBUG("module.playerbot.services", "? EXPLORATION QUEST: Found classic AreaTrigger {} in DB at ({:.1f}, {:.1f}, {:.1f}) - Map {}",
areaTriggerID, posX, posY, posZ, mapId);
return result;
}
else
{
TC_LOG_WARN("module.playerbot.services", "⚠️ Classic AreaTrigger {} is on map {} but bot is on map {}",
TC_LOG_WARN("module.playerbot.services", "?? Classic AreaTrigger {} is on map {} but bot is on map {}",
areaTriggerID, mapId, bot->GetMapId());
}
}
else
{
TC_LOG_WARN("module.playerbot.services", "⚠️ AreaTrigger {} not in DB - will try Quest POI fallback",
TC_LOG_WARN("module.playerbot.services", "?? AreaTrigger {} not in DB - will try Quest POI fallback",
areaTriggerID);
}
}
@@ -362,7 +362,7 @@ namespace Playerbot
_errorCount++;
_errorsByCategory[category]++;
TC_LOG_ERROR("playerbot", "BotMonitor: Error in %s: %s", category.c_str(), message.c_str());
TC_LOG_DEBUG("playerbot", "BotMonitor: Error in %s: %s", category.c_str(), message.c_str());
}
void BotMonitor::RecordWarning(::std::string const& category, ::std::string const& message)
@@ -778,7 +778,7 @@ namespace Playerbot
{
TC_LOG_ERROR("playerbot", "BotMonitor: Alert callback exception: %s", ex.what());
TC_LOG_DEBUG("playerbot", "BotMonitor: Alert callback exception: %s", ex.what());
}
}
+46 -46
View File
@@ -94,7 +94,7 @@ bool PlayerbotModule::Initialize()
if (!Playerbot::GuidedSetupHelper::CheckAndRunSetup())
{
_lastError = "Configuration setup failed - see logs for details";
TC_LOG_ERROR("module.playerbot", "Playerbot Module: {}", _lastError);
TC_LOG_DEBUG("module.playerbot", "Playerbot Module: {}", _lastError);
return false;
}
@@ -102,7 +102,7 @@ bool PlayerbotModule::Initialize()
if (!sPlayerbotConfig->Initialize())
{
_lastError = "Failed to load playerbot configuration";
TC_LOG_ERROR("module.playerbot", "Playerbot Module: {}", _lastError);
TC_LOG_DEBUG("module.playerbot", "Playerbot Module: {}", _lastError);
return false;
}
@@ -119,7 +119,7 @@ bool PlayerbotModule::Initialize()
if (!ValidateConfig())
{
_lastError = "Configuration validation failed";
TC_LOG_ERROR("module.playerbot", "Playerbot Module: {}", _lastError);
TC_LOG_DEBUG("module.playerbot", "Playerbot Module: {}", _lastError);
return false;
}
@@ -136,12 +136,12 @@ bool PlayerbotModule::Initialize()
if (!InitializeDatabase())
{
// _lastError is already set by InitializeDatabase() with detailed information
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " >>> SERVER STARTUP ABORTED <<<");
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " Playerbot module failed to initialize due to database connection failure.");
TC_LOG_ERROR("module.playerbot", " Error: {}", _lastError);
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " >>> SERVER STARTUP ABORTED <<<");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " Playerbot module failed to initialize due to database connection failure.");
TC_LOG_DEBUG("module.playerbot", " Error: {}", _lastError);
TC_LOG_DEBUG("module.playerbot", "");
return false;
}
@@ -149,7 +149,7 @@ bool PlayerbotModule::Initialize()
if (!sPlayerbotCharDB->Initialize())
{
_lastError = "Failed to initialize Character Database Interface";
TC_LOG_ERROR("module.playerbot", "Playerbot Module: {}", _lastError);
TC_LOG_DEBUG("module.playerbot", "Playerbot Module: {}", _lastError);
return false;
}
@@ -184,8 +184,8 @@ bool PlayerbotModule::Initialize()
// NOTE: Do NOT register with ModuleUpdateManager here!
// PlayerbotModuleAdapter already registers with ModuleManager, which calls OnModuleUpdate
// → OnWorldUpdate. Registering here would cause DOUBLE updates per tick, leading to
// FreezeDetector crashes (60s timeout exceeded due to 35s × 2 = 70s max wait).
// ? OnWorldUpdate. Registering here would cause DOUBLE updates per tick, leading to
// FreezeDetector crashes (60s timeout exceeded due to 35s × 2 = 70s max wait).
// See: World.cpp lines 2327 (ModuleManager::CallOnUpdate) and 2334 (sModuleUpdateManager->Update)
_initialized = true;
@@ -283,14 +283,14 @@ void PlayerbotModule::OnWorldUpdate(uint32 diff)
}
catch (std::exception const& ex)
{
TC_LOG_ERROR("module.playerbot", "CRITICAL EXCEPTION in PlayerbotModule::OnWorldUpdate: {}", ex.what());
TC_LOG_ERROR("module.playerbot", "Disabling playerbot to prevent further crashes");
TC_LOG_DEBUG("module.playerbot", "CRITICAL EXCEPTION in PlayerbotModule::OnWorldUpdate: {}", ex.what());
TC_LOG_DEBUG("module.playerbot", "Disabling playerbot to prevent further crashes");
_enabled = false;
}
catch (...)
{
TC_LOG_ERROR("module.playerbot", "CRITICAL UNKNOWN EXCEPTION in PlayerbotModule::OnWorldUpdate");
TC_LOG_ERROR("module.playerbot", "Disabling playerbot to prevent further crashes");
TC_LOG_DEBUG("module.playerbot", "CRITICAL UNKNOWN EXCEPTION in PlayerbotModule::OnWorldUpdate");
TC_LOG_DEBUG("module.playerbot", "Disabling playerbot to prevent further crashes");
_enabled = false;
}
}
@@ -365,36 +365,36 @@ bool PlayerbotModule::InitializeDatabase()
// ============================================================================
// CRITICAL DATABASE CONNECTION FAILURE - BLOCK SERVER STARTUP
// ============================================================================
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", "================================================================================");
TC_LOG_ERROR("module.playerbot", " PLAYERBOT DATABASE CONNECTION FAILED - SERVER STARTUP BLOCKED");
TC_LOG_ERROR("module.playerbot", "================================================================================");
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " Playerbot is ENABLED but cannot connect to its database.");
TC_LOG_ERROR("module.playerbot", " The server cannot start safely without a working database connection.");
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " Current Configuration:");
TC_LOG_ERROR("module.playerbot", " Host: {}", host);
TC_LOG_ERROR("module.playerbot", " Port: {}", port);
TC_LOG_ERROR("module.playerbot", " User: {}", user);
TC_LOG_ERROR("module.playerbot", " Database: {}", database);
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " Possible Causes:");
TC_LOG_ERROR("module.playerbot", " 1. MySQL server is not running");
TC_LOG_ERROR("module.playerbot", " 2. Wrong hostname or port in configuration");
TC_LOG_ERROR("module.playerbot", " 3. Invalid username or password");
TC_LOG_ERROR("module.playerbot", " 4. Database '{}' does not exist", database);
TC_LOG_ERROR("module.playerbot", " 5. User '{}' has no access to database '{}'", user, database);
TC_LOG_ERROR("module.playerbot", " 6. Firewall blocking connection to port {}", port);
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", " Solutions:");
TC_LOG_ERROR("module.playerbot", " - Check worldserver.conf for Playerbot.Database.* settings");
TC_LOG_ERROR("module.playerbot", " - Verify MySQL server is running: mysql -u {} -p -h {} -P {}", user, host, port);
TC_LOG_ERROR("module.playerbot", " - Create database if missing: CREATE DATABASE {};", database);
TC_LOG_ERROR("module.playerbot", " - Or disable Playerbot: set Playerbot.Enable = 0");
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_ERROR("module.playerbot", "================================================================================");
TC_LOG_ERROR("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", "================================================================================");
TC_LOG_DEBUG("module.playerbot", " PLAYERBOT DATABASE CONNECTION FAILED - SERVER STARTUP BLOCKED");
TC_LOG_DEBUG("module.playerbot", "================================================================================");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " Playerbot is ENABLED but cannot connect to its database.");
TC_LOG_DEBUG("module.playerbot", " The server cannot start safely without a working database connection.");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " Current Configuration:");
TC_LOG_DEBUG("module.playerbot", " Host: {}", host);
TC_LOG_DEBUG("module.playerbot", " Port: {}", port);
TC_LOG_DEBUG("module.playerbot", " User: {}", user);
TC_LOG_DEBUG("module.playerbot", " Database: {}", database);
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " Possible Causes:");
TC_LOG_DEBUG("module.playerbot", " 1. MySQL server is not running");
TC_LOG_DEBUG("module.playerbot", " 2. Wrong hostname or port in configuration");
TC_LOG_DEBUG("module.playerbot", " 3. Invalid username or password");
TC_LOG_DEBUG("module.playerbot", " 4. Database '{}' does not exist", database);
TC_LOG_DEBUG("module.playerbot", " 5. User '{}' has no access to database '{}'", user, database);
TC_LOG_DEBUG("module.playerbot", " 6. Firewall blocking connection to port {}", port);
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", " Solutions:");
TC_LOG_DEBUG("module.playerbot", " - Check worldserver.conf for Playerbot.Database.* settings");
TC_LOG_DEBUG("module.playerbot", " - Verify MySQL server is running: mysql -u {} -p -h {} -P {}", user, host, port);
TC_LOG_DEBUG("module.playerbot", " - Create database if missing: CREATE DATABASE {};", database);
TC_LOG_DEBUG("module.playerbot", " - Or disable Playerbot: set Playerbot.Enable = 0");
TC_LOG_DEBUG("module.playerbot", "");
TC_LOG_DEBUG("module.playerbot", "================================================================================");
TC_LOG_DEBUG("module.playerbot", "");
_lastError = Trinity::StringFormat(
"CRITICAL: Playerbot database connection failed! "
@@ -63,7 +63,7 @@ void PlayerbotModuleAdapter::OnModuleStartup()
// which runs earlier during server startup
if (!Playerbot::sBotSpawner->Initialize())
{
TC_LOG_ERROR("module.playerbot", "PlayerbotModuleAdapter: Failed to initialize Bot Spawner");
TC_LOG_DEBUG("module.playerbot", "PlayerbotModuleAdapter: Failed to initialize Bot Spawner");
return;
}
@@ -74,7 +74,7 @@ void PlayerbotModuleAdapter::OnModuleStartup()
}
catch (::std::exception const& e)
{
TC_LOG_ERROR("module.playerbot", "PlayerbotModuleAdapter: Startup failed: {}", e.what());
TC_LOG_DEBUG("module.playerbot", "PlayerbotModuleAdapter: Startup failed: {}", e.what());
}
}
@@ -94,7 +94,7 @@ void PlayerbotModuleAdapter::OnModuleUpdate(uint32 diff)
}
catch (::std::exception const& e)
{
TC_LOG_ERROR("module.playerbot", "PlayerbotModuleAdapter: Update failed: {}", e.what());
TC_LOG_DEBUG("module.playerbot", "PlayerbotModuleAdapter: Update failed: {}", e.what());
}
}
@@ -116,8 +116,8 @@ void PlayerbotModuleAdapter::OnModuleShutdown()
}
catch (::std::exception const& e)
{
TC_LOG_ERROR("module.playerbot", "PlayerbotModuleAdapter: Shutdown failed: {}", e.what());
TC_LOG_DEBUG("module.playerbot", "PlayerbotModuleAdapter: Shutdown failed: {}", e.what());
}
}
} // namespace Playerbot
} // namespace Playerbot
@@ -86,7 +86,7 @@ void BotHealthCheck::PerformHealthChecks(uint32 currentTime)
AddHealthIssue(HealthStatus::UNHEALTHY, "ErrorRate",
"Excessive error rate detected", currentTime);
TC_LOG_ERROR("module.playerbot.health",
TC_LOG_DEBUG("module.playerbot.health",
"ERROR RATE EXCESSIVE: {:.2f} errors/sec (threshold: {:.2f})",
GetSystemErrorRate(), _errorRateThreshold);
}
@@ -126,7 +126,7 @@ void BotHealthCheck::CheckForStalledBots(uint32 currentTime)
AddHealthIssue(HealthStatus::UNHEALTHY, "BotStall",
"Bot " + guid.ToString() + " is stalled", currentTime);
TC_LOG_ERROR("module.playerbot.health", "Bot {} detected as STALLED", guid.ToString());
TC_LOG_DEBUG("module.playerbot.health", "Bot {} detected as STALLED", guid.ToString());
// Trigger auto-recovery if enabled
if (_autoRecoveryEnabled.load())
@@ -140,7 +140,7 @@ void BotHealthCheck::CheckForStalledBots(uint32 currentTime)
// Log summary if many bots are stalled
if (stalledBots.size() > 10)
{
TC_LOG_ERROR("module.playerbot.health",
TC_LOG_DEBUG("module.playerbot.health",
"CRITICAL: {} bots are stalled! System may be overloaded.",
stalledBots.size());
@@ -338,7 +338,7 @@ void BotHealthCheck::TriggerSystemRecovery()
if (currentTime - _lastRecoveryTime < RECOVERY_COOLDOWN_MS)
return;
TC_LOG_ERROR("module.playerbot.health", "Attempting system-wide recovery...");
TC_LOG_DEBUG("module.playerbot.health", "Attempting system-wide recovery...");
// System recovery actions:
// 1. Clear all stalled bot flags
@@ -148,7 +148,7 @@ void BotPacketRelay::RelayToGroupMembers(BotSession* botSession, WorldPacket con
if (!botSession || !packet)
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::RelayToGroupMembers() called with null botSession or packet");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::RelayToGroupMembers() called with null botSession or packet");
return;
}
@@ -229,7 +229,7 @@ void BotPacketRelay::RelayToPlayer(BotSession* botSession, WorldPacket const* pa
if (!botSession || !packet || !targetPlayer)
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::RelayToPlayer() called with null parameter");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::RelayToPlayer() called with null parameter");
return;
}
@@ -237,7 +237,7 @@ void BotPacketRelay::RelayToPlayer(BotSession* botSession, WorldPacket const* pa
if (!_initialized.load())
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::RelayToPlayer() called but system not initialized");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::RelayToPlayer() called but system not initialized");
return;
}
@@ -275,7 +275,7 @@ void BotPacketRelay::BroadcastToGroup(BotSession* botSession, WorldPacket const*
if (!botSession || !packet)
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::BroadcastToGroup() called with null botSession or packet");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::BroadcastToGroup() called with null botSession or packet");
return;
}
@@ -283,7 +283,7 @@ void BotPacketRelay::BroadcastToGroup(BotSession* botSession, WorldPacket const*
if (!_initialized.load())
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::BroadcastToGroup() called but system not initialized");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::BroadcastToGroup() called but system not initialized");
return;
}
@@ -473,7 +473,7 @@ void BotPacketRelay::InitializeForGroup(Player* bot, Group* group)
{
if (!bot || !group)
{
TC_LOG_ERROR("playerbot", "BotPacketRelay::InitializeForGroup() called with null bot or group");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::InitializeForGroup() called with null bot or group");
return;
}
@@ -491,7 +491,7 @@ void BotPacketRelay::CleanupForGroup(Player* bot, Group* group)
{
if (!bot || !group)
{
TC_LOG_ERROR("playerbot", "BotPacketRelay::CleanupForGroup() called with null bot or group");
TC_LOG_DEBUG("playerbot", "BotPacketRelay::CleanupForGroup() called with null bot or group");
return;
}
@@ -619,14 +619,14 @@ bool BotPacketRelay::SendPacketToPlayer(Player* player, WorldPacket const* packe
catch (::std::exception const& ex)
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::SendPacketToPlayer() - Exception sending packet to player {}: {}",
TC_LOG_DEBUG("playerbot", "BotPacketRelay::SendPacketToPlayer() - Exception sending packet to player {}: {}",
player->GetName(), ex.what());
return false;
}
catch (...)
{
_statistics.totalRelayErrors++;
TC_LOG_ERROR("playerbot", "BotPacketRelay::SendPacketToPlayer() - Unknown exception sending packet to player {}",
TC_LOG_DEBUG("playerbot", "BotPacketRelay::SendPacketToPlayer() - Unknown exception sending packet to player {}",
player->GetName());
return false;
}
@@ -749,7 +749,7 @@ void BotPacketRelay::ProcessDeferredPackets()
// Validate bot session is still valid
if (!deferred.botSession || !deferred.packet)
{
TC_LOG_ERROR("playerbot", "BotPacketRelay::ProcessDeferredPackets() - Invalid deferred packet (botSession={}, packet={})",
TC_LOG_DEBUG("playerbot", "BotPacketRelay::ProcessDeferredPackets() - Invalid deferred packet (botSession={}, packet={})",
deferred.botSession != nullptr, deferred.packet != nullptr);
failedCount++;
_deferredPackets.pop();
@@ -298,10 +298,10 @@ bool BotPriorityManager::ShouldUpdateThisTick(ObjectGuid botGuid, uint32 current
//
// Example: 91 LOW priority bots with 50-tick interval
// - Before: All 91 bots update at tick 0, 50, 100 (SPIKE: 91ms per spike)
// - After: ~2 bots update per tick (91 ÷ 50 = 1.82) (SMOOTH: ~2ms per tick)
// - After: ~2 bots update per tick (91 ÷ 50 = 1.82) (SMOOTH: ~2ms per tick)
//
// Benefits:
// - Eliminates 900ms spikes at interval boundaries (851ms → 110ms)
// - Eliminates 900ms spikes at interval boundaries (851ms ? 110ms)
// - Maintains same average update frequency
// - Deterministic (same bot always updates at same offset)
// - Zero memory overhead (uses existing GUID)
@@ -490,7 +490,7 @@ void BotPriorityManager::DetectStalledBots(uint32 currentTime, uint32 stallThres
if (!metrics.isStalled)
{
metrics.isStalled = true;
TC_LOG_ERROR("module.playerbot.health", "Bot {} detected as STALLED (no update for {}ms)",
TC_LOG_DEBUG("module.playerbot.health", "Bot {} detected as STALLED (no update for {}ms)",
guid.ToString(), timeSinceUpdate);
}
}
@@ -47,7 +47,7 @@ void BotSessionFactory::Shutdown()
auto const& stats = GetStats();
TC_LOG_INFO("module.playerbot.session.factory",
"Final Factory Statistics - Created: {}, Success Rate: {:.1f}%, Avg Time: {}μs",
"Final Factory Statistics - Created: {}, Success Rate: {:.1f}%, Avg Time: {}?s",
stats.sessionsCreated.load(), stats.GetSuccessRate(), stats.avgCreationTimeUs.load());
// Clear templates
@@ -180,7 +180,7 @@ bool BotSessionFactory::ConfigureSession(::std::shared_ptr<BotSession> session,
}
catch (::std::exception const& ex)
{
TC_LOG_ERROR("module.playerbot.session.factory",
TC_LOG_DEBUG("module.playerbot.session.factory",
"Failed to configure session: {}", ex.what());
return false;
}
@@ -441,7 +441,7 @@ void BotSessionFactory::ResetStats()
void BotSessionFactory::HandleCreationError(::std::string const& error, ObjectGuid characterGuid)
{
TC_LOG_ERROR("module.playerbot.session.factory",
TC_LOG_DEBUG("module.playerbot.session.factory",
"Session creation error for character {}: {}", characterGuid.ToString(), error);
_stats.creationFailures.fetch_add(1);
@@ -454,4 +454,4 @@ void BotSessionFactory::HandleCreationError(::std::string const& error, ObjectGu
return nullptr; // Simplified for now
}
} // namespace Playerbot
} // namespace Playerbot
@@ -84,14 +84,14 @@ namespace {
if (elapsed > thresholdMs)
{
++stuckCount;
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"STUCK TASK DETECTED: Bot {} (GUID: {}) has been executing for {}ms!",
task.botName, guid.ToString(), elapsed);
}
}
// Always log summary for diagnostics
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"LogStuckTasks: {} tasks tracked, {} stuck (>{} ms)",
totalTracked, stuckCount, thresholdMs);
}
@@ -140,19 +140,19 @@ bool BotWorldSessionMgr::Initialize()
// Initialize enterprise components
if (!sBotPriorityMgr->Initialize())
{
TC_LOG_ERROR("module.playerbot.session", "Failed to initialize BotPriorityManager");
TC_LOG_DEBUG("module.playerbot.session", "Failed to initialize BotPriorityManager");
return false;
}
if (!sBotPerformanceMon->Initialize())
{
TC_LOG_ERROR("module.playerbot.session", "Failed to initialize BotPerformanceMonitor");
TC_LOG_DEBUG("module.playerbot.session", "Failed to initialize BotPerformanceMonitor");
return false;
}
if (!sBotHealthCheck->Initialize())
{
TC_LOG_ERROR("module.playerbot.session", "Failed to initialize BotHealthCheck");
TC_LOG_DEBUG("module.playerbot.session", "Failed to initialize BotHealthCheck");
return false;
}
@@ -194,7 +194,7 @@ void BotWorldSessionMgr::Shutdown()
{
if (!session)
{
TC_LOG_ERROR("playerbot.nullcheck", "Null pointer: session in Shutdown");
TC_LOG_DEBUG("playerbot.nullcheck", "Null pointer: session in Shutdown");
continue; // Skip null sessions
}
if (session && session->GetPlayer())
@@ -202,11 +202,11 @@ void BotWorldSessionMgr::Shutdown()
// MEMORY SAFETY: Protect against use-after-free when accessing Player name
Player* player = session->GetPlayer();
try {
TC_LOG_INFO("module.playerbot.session", "🛑 Logging out bot: {}", player->GetName());
TC_LOG_INFO("module.playerbot.session", "?? Logging out bot: {}", player->GetName());
}
catch (...)
{
TC_LOG_INFO("module.playerbot.session", "🛑 Logging out bot (name unavailable - use-after-free protection)");
TC_LOG_INFO("module.playerbot.session", "?? Logging out bot (name unavailable - use-after-free protection)");
}
// PRE-CLEANUP: Clean up bot state before logout to prevent stuck auras
@@ -242,7 +242,7 @@ void BotWorldSessionMgr::Shutdown()
}
catch (...)
{
TC_LOG_ERROR("module.playerbot.session", "🛑 Exception during bot pre-cleanup");
TC_LOG_DEBUG("module.playerbot.session", "?? Exception during bot pre-cleanup");
}
session->LogoutPlayer(true);
@@ -267,7 +267,7 @@ bool BotWorldSessionMgr::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccoun
if (!_enabled.load() || !_initialized.load())
{
TC_LOG_ERROR("module.playerbot.session", "?? BotWorldSessionMgr not enabled or initialized");
TC_LOG_DEBUG("module.playerbot.session", "?? BotWorldSessionMgr not enabled or initialized");
return false;
}
@@ -309,7 +309,7 @@ bool BotWorldSessionMgr::AddPlayerBot(ObjectGuid playerGuid, uint32 masterAccoun
if (!accountId)
{
TC_LOG_ERROR("module.playerbot.session", "?? Could not find account for character {} in playerbot database", playerGuid.ToString());
TC_LOG_DEBUG("module.playerbot.session", "?? Could not find account for character {} in playerbot database", playerGuid.ToString());
return false;
}
@@ -558,7 +558,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// Synchronize character cache
if (!SynchronizeCharacterCache(playerGuid))
{
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"?? Failed to synchronize character cache for {}", playerGuid.ToString());
continue;
}
@@ -570,7 +570,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
::std::shared_ptr<BotSession> botSession = BotSession::Create(accountId);
if (!botSession)
{
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"?? Failed to create BotSession for {}", playerGuid.ToString());
_botsLoading.erase(playerGuid);
continue;
@@ -582,7 +582,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// Initiate async login (1 bot 66 queries)
if (!botSession->LoginCharacter(playerGuid))
{
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"?? Failed to initiate async login for {}", playerGuid.ToString());
_botSessions.erase(playerGuid);
_botsLoading.erase(playerGuid);
@@ -724,7 +724,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
}
else if (session->IsLoginFailed())
{
TC_LOG_ERROR("module.playerbot.session", "?? Bot login failed: {}", guid.ToString());
TC_LOG_DEBUG("module.playerbot.session", "?? Bot login failed: {}", guid.ToString());
_botsLoading.erase(guid);
sessionsToRemove.push_back(guid);
}
@@ -920,7 +920,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// Bot has been idle for 60+ seconds - schedule for logout
Player* bot = botSession->GetPlayer();
TC_LOG_INFO("module.playerbot.instance",
"📴 Instance bot {} idle timeout - scheduling logout to reduce server load",
"?? Instance bot {} idle timeout - scheduling logout to reduce server load",
bot ? bot->GetName() : guid.ToString().c_str());
disconnectedSessions.push_back(guid);
continue;
@@ -1036,13 +1036,13 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
}
catch (::std::exception const& e)
{
TC_LOG_ERROR("module.playerbot.session", "?? Exception updating bot {}: {}", guid.ToString(), e.what());
TC_LOG_DEBUG("module.playerbot.session", "?? Exception updating bot {}: {}", guid.ToString(), e.what());
sBotHealthCheck->RecordError(guid, "UpdateException");
_asyncDisconnections.push(guid); // Lock-free push
}
catch (...)
{
TC_LOG_ERROR("module.playerbot.session", "?? Unknown exception updating bot {}", guid.ToString());
TC_LOG_DEBUG("module.playerbot.session", "?? Unknown exception updating bot {}", guid.ToString());
sBotHealthCheck->RecordError(guid, "UnknownException");
_asyncDisconnections.push(guid); // Lock-free push
}
@@ -1055,7 +1055,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// CRITICAL FIX: Sessions with pending login MUST run on main thread!
// ============================================================================
// Problem: When LOGIN_IN_PROGRESS, the session has a pending SQL callback that
// will call HandleBotPlayerLogin → Map::AddPlayerToMap.
// will call HandleBotPlayerLogin ? Map::AddPlayerToMap.
// Map operations are NOT thread-safe and MUST run on the map update thread.
// Running this on a thread pool worker causes ACCESS_VIOLATION crash in
// TerrainInfo::LoadMapAndVMap when acquiring the terrain mutex.
@@ -1109,7 +1109,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// 1. Main thread submits 100 bot update tasks to ThreadPool (line 718)
// 2. Main thread IMMEDIATELY processes deferred logouts (calls LogoutPlayer)
// 3. ThreadPool workers are STILL running, accessing player data
// 4. Player removed from map while worker accesses it → Map.cpp:686 crash
// 4. Player removed from map while worker accesses it ? Map.cpp:686 crash
//
// Solution: Wait for all ThreadPool tasks to complete before processing logouts.
// ONLY wait if there are actually pending logouts to process.
@@ -1132,7 +1132,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
// CRITICAL FIX (Map.cpp:1973 crash): Must check HasPendingWork() not just GetQueuedTasks()!
// Problem: GetQueuedTasks() only checks if tasks are QUEUED. A task can be dequeued
// (queue empty) but STILL EXECUTING on a worker thread. If we only check queues,
// we can proceed while workers still access bot objects → use-after-free crash.
// we can proceed while workers still access bot objects ? use-after-free crash.
// Solution: Use HasPendingWork() which checks both queued AND in-flight (executing) tasks.
if (useThreadPool)
{
@@ -1185,7 +1185,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
size_t finalInFlight = Performance::GetThreadPool().GetInFlightTasks();
size_t activeThreads = Performance::GetThreadPool().GetActiveThreads();
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"ThreadPool wait timeout after {}ms! {} queued, {} in-flight, {} active workers. "
"PROCEEDING to prevent FreezeDetector crash - some bot updates may be incomplete!",
waitDuration.count(), finalQueued, finalInFlight, activeThreads);
@@ -1325,7 +1325,7 @@ void BotWorldSessionMgr::UpdateSessions(uint32 diff)
}
catch (...)
{
TC_LOG_ERROR("module.playerbot.session",
TC_LOG_DEBUG("module.playerbot.session",
"Exception during deferred LogoutPlayer() for bot {} - continuing cleanup",
guid.ToString());
}
@@ -1541,7 +1541,7 @@ bool BotWorldSessionMgr::SynchronizeCharacterCache(ObjectGuid playerGuid)
if (!result)
{
TC_LOG_ERROR("module.playerbot.session", "?? Character {} not found in characters table", playerGuid.ToString());
TC_LOG_DEBUG("module.playerbot.session", "?? Character {} not found in characters table", playerGuid.ToString());
return false;
}
@@ -861,6 +861,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_NEIGHBORHOOD_CHARTER, "DELETE FROM neighborhood_charters WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_NEIGHBORHOOD_CHARTER_SIGNATURE, "INSERT INTO neighborhood_charter_signatures (charterId, signerGuid, signTime) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_NEIGHBORHOOD_CHARTER_SIGNATURES, "DELETE FROM neighborhood_charter_signatures WHERE charterId = ?", CONNECTION_ASYNC);
//GARRISONS
PrepareStatement(CHAR_SEL_CHARACTER_GARRISON_MISSIONS, "SELECT dbId, guid, missionRecID, offerTime, offerDuration, startTime, travelDuration, missionDuration, missionState, successChance FROM character_garrison_missions WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHARACTER_GARRISON_MISSIONS, "INSERT INTO character_garrison_missions (dbId, guid, missionRecID, offerTime, offerDuration, startTime, travelDuration, missionDuration, missionState, successChance) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER_GARRISON_MISSIONS, "DELETE FROM character_garrison_missions WHERE guid = ?", CONNECTION_ASYNC);
// WowCommunity end
}
@@ -716,6 +716,9 @@ enum CharacterDatabaseStatements : uint32
CHAR_INS_NEIGHBORHOOD_CHARTER_SIGNATURE,
CHAR_DEL_NEIGHBORHOOD_CHARTER_SIGNATURES,
CHAR_SEL_CHARACTER_GARRISON_MISSIONS,
CHAR_INS_CHARACTER_GARRISON_MISSIONS,
CHAR_DEL_CHARACTER_GARRISON_MISSIONS,
//WowCommunity end
MAX_CHARACTERDATABASE_STATEMENTS