move bot logs to debug part 1 and add db for garr missions
This commit is contained in:
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user