fix(lifecycle): Fix incomplete type error and deprecated APIs in clean build

- Add BotAI.h include to 9 files that use unique_ptr<BotAI> via BotSession.h
  Files fixed: BotCharacterCreator, BotResourcePool, BotSpawnOrchestrator,
  BotSpawner, GracefulExitHandler, DynamicQuestSystem, ObjectiveTracker,
  LootDistribution

- Fix CorpseCrashMitigation.cpp deprecated TrinityCore APIs:
  * Replace SetDeathState() with setDeathState() (correct casing)
  * Replace SetFlag/RemoveFlag/GetByteValue with internal tracking set
  * Replace deprecated PLAYER_FIELD_BYTES2 with _pendingPrevention set
  * Fix dynamic_cast<BotAI*> by using BotSession->GetAI() pattern
  * Fix OrderedSharedMutex usage (direct lock/unlock instead of std::lock)

- Add _pendingPrevention unordered_set to CorpseCrashMitigation.h

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Signed-off-by: luis <[email protected]>
This commit is contained in:
agatho
2026-02-04 21:08:34 -03:00
committed by luis
co-authored by Claude Opus 4.5
parent ce470d10ce
commit d5d61361ba
10 changed files with 98 additions and 30 deletions
@@ -23,6 +23,7 @@
#include "World.h"
#include "WorldSession.h"
#include "../Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Data/WoW120CharacterCreation.h"
#include "CharacterPackets.h"
#include <random>
@@ -4,6 +4,7 @@
#include "BotResourcePool.h"
#include "BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Config/PlayerbotConfig.h"
#include "Logging/Log.h"
#include "Player.h"
@@ -16,6 +16,7 @@
#include "BotSpawnEventBus.h"
#include "BotSpawner.h"
#include "Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Logging/Log.h"
#include "Player.h"
@@ -42,6 +42,7 @@
#include "CharacterDatabase.h"
#include "BotWorldSessionMgr.h"
#include "Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "BotResourcePool.h"
#include "BotAccountMgr.h"
#include "Config/PlayerbotConfig.h"
@@ -14,6 +14,7 @@
#include "WorldSession.h"
#include "Log.h"
#include "../AI/BotAI.h"
#include "../Session/BotSession.h"
#include "DeathRecoveryManager.h"
namespace Playerbot
@@ -47,9 +48,12 @@ void CorpseCrashMitigation::OnBotDeath(Player* bot)
// Cache death location BEFORE death for corpse-less resurrection
CacheDeathLocation(bot);
// Mark bot for corpse prevention using custom player flag
// Safe because it's in the unused flag range (high byte)
bot->SetFlag(PLAYER_FIELD_BYTES2, 0x80000000);
// Mark bot for corpse prevention in our internal tracking
{
_mutex.lock();
_pendingPrevention.insert(bot->GetGUID());
_mutex.unlock();
}
}
}
@@ -61,8 +65,13 @@ void CorpseCrashMitigation::OnCorpseCreated(Player* bot, Corpse* corpse)
if (!bot->GetSession()->IsBot())
return;
// Check if this bot has corpse prevention flag
bool preventionAttempted = (bot->GetByteValue(PLAYER_FIELD_BYTES2, 3) & 0x80);
// Check if this bot has corpse prevention flag in our internal tracking
bool preventionAttempted = false;
{
_mutex.lock_shared();
preventionAttempted = (_pendingPrevention.count(bot->GetGUID()) > 0);
_mutex.unlock_shared();
}
if (preventionAttempted)
{
@@ -91,8 +100,12 @@ void CorpseCrashMitigation::OnCorpseCreated(Player* bot, Corpse* corpse)
TrackCorpseSafely(bot, corpse);
}
// Clear prevention flag
bot->RemoveFlag(PLAYER_FIELD_BYTES2, 0x80000000);
// Clear prevention flag from our internal tracking
{
_mutex.lock();
_pendingPrevention.erase(bot->GetGUID());
_mutex.unlock();
}
// Decrement active prevention counter
--_activePrevention;
@@ -115,7 +128,7 @@ void CorpseCrashMitigation::OnBotResurrection(Player* bot)
ObjectGuid botGuid = bot->GetGUID();
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
// Clean up death location cache
_deathLocations.erase(botGuid);
@@ -131,6 +144,8 @@ void CorpseCrashMitigation::OnBotResurrection(Player* bot)
TC_LOG_DEBUG("playerbot.corpse", "Bot {} resurrected - cleaned up corpse tracking",
bot->GetName());
}
_mutex.unlock();
}
// ============================================================================
@@ -146,7 +161,7 @@ bool CorpseCrashMitigation::TryPreventCorpse(Player* bot)
// CRITICAL: Set death state to ALIVE immediately to prevent corpse creation
// This MUST happen before TrinityCore's death handling creates the corpse
bot->SetDeathState(ALIVE);
bot->setDeathState(ALIVE);
// Set bot as ghost for visual effect (but alive mechanically)
bot->SetPlayerFlag(PLAYER_FLAGS_GHOST);
@@ -158,12 +173,18 @@ bool CorpseCrashMitigation::TryPreventCorpse(Player* bot)
bot->SetHealth(1);
// Get BotAI to handle the fake death recovery
if (BotAI* ai = dynamic_cast<BotAI*>(bot->GetAI()))
if (WorldSession* session = bot->GetSession())
{
if (DeathRecoveryManager* drm = ai->GetDeathRecoveryManager())
if (BotSession* botSession = dynamic_cast<BotSession*>(session))
{
// Initialize death recovery with cached location (no corpse needed)
drm->OnDeath();
if (BotAI* ai = botSession->GetAI())
{
if (DeathRecoveryManager* drm = ai->GetDeathRecoveryManager())
{
// Initialize death recovery with cached location (no corpse needed)
drm->OnDeath();
}
}
}
}
@@ -207,28 +228,36 @@ void CorpseCrashMitigation::CacheDeathLocation(Player* bot)
location.z = bot->GetPositionZ();
location.deathTime = ::std::chrono::steady_clock::now();
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
_deathLocations[botGuid] = location;
_mutex.unlock();
TC_LOG_DEBUG("playerbot.corpse", "Cached death location for bot {} at ({:.2f}, {:.2f}, {:.2f}) map {}",
bot->GetName(), location.x, location.y, location.z, location.mapId);
// Also notify DeathRecoveryManager if available
if (BotAI* ai = dynamic_cast<BotAI*>(bot->GetAI()))
if (WorldSession* session = bot->GetSession())
{
if (DeathRecoveryManager* drm = ai->GetDeathRecoveryManager())
if (BotSession* botSession = dynamic_cast<BotSession*>(session))
{
// DeathRecoveryManager will cache location in OnDeath()
// This is a pre-cache to ensure it happens BEFORE corpse creation
TC_LOG_TRACE("playerbot.corpse", "Pre-cached death location in unified mitigation system");
if (BotAI* ai = botSession->GetAI())
{
if (DeathRecoveryManager* drm = ai->GetDeathRecoveryManager())
{
// DeathRecoveryManager will cache location in OnDeath()
// This is a pre-cache to ensure it happens BEFORE corpse creation
TC_LOG_TRACE("playerbot.corpse", "Pre-cached death location in unified mitigation system");
}
}
}
}
}
void CorpseCrashMitigation::UncacheDeathLocation(ObjectGuid botGuid)
{
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
_deathLocations.erase(botGuid);
_mutex.unlock();
}
// ============================================================================
@@ -255,17 +284,18 @@ void CorpseCrashMitigation::TrackCorpseSafely(Player* bot, Corpse* corpse)
tracker->safeToDelete = false; // NOT safe until Map update completes
tracker->referenceCount = 1;
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
_trackedCorpses[corpseGuid] = ::std::move(tracker);
_ownerToCorpse[ownerGuid] = corpseGuid;
_mutex.unlock();
TC_LOG_DEBUG("playerbot.corpse", "Tracking corpse {} for bot {} at ({:.2f}, {:.2f}, {:.2f}) (strategy 2 fallback)",
corpseGuid.ToString(), bot->GetName(), tracker->x, tracker->y, tracker->z);
corpseGuid.ToString(), bot->GetName(), corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ());
}
void CorpseCrashMitigation::UntrackCorpse(ObjectGuid corpseGuid)
{
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
auto it = _trackedCorpses.find(corpseGuid);
if (it != _trackedCorpses.end())
@@ -276,11 +306,13 @@ void CorpseCrashMitigation::UntrackCorpse(ObjectGuid corpseGuid)
TC_LOG_DEBUG("playerbot.corpse", "Untracked corpse {}", corpseGuid.ToString());
}
_mutex.unlock();
}
bool CorpseCrashMitigation::IsCorpseSafeToDelete(ObjectGuid corpseGuid) const
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto it = _trackedCorpses.find(corpseGuid);
if (it != _trackedCorpses.end())
@@ -291,6 +323,8 @@ bool CorpseCrashMitigation::IsCorpseSafeToDelete(ObjectGuid corpseGuid) const
bool safe = it->second->safeToDelete.load() &&
(it->second->referenceCount.load() == 0);
_mutex.unlock_shared();
if (!safe)
{
const_cast<CorpseCrashMitigation*>(this)->_safetyDelayedDeletions++;
@@ -303,13 +337,15 @@ bool CorpseCrashMitigation::IsCorpseSafeToDelete(ObjectGuid corpseGuid) const
return safe;
}
_mutex.unlock_shared();
// Unknown corpse = not a bot corpse = safe to delete normally
return true;
}
void CorpseCrashMitigation::MarkCorpseSafeForDeletion(ObjectGuid corpseGuid)
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto it = _trackedCorpses.find(corpseGuid);
if (it != _trackedCorpses.end())
@@ -317,11 +353,13 @@ void CorpseCrashMitigation::MarkCorpseSafeForDeletion(ObjectGuid corpseGuid)
it->second->safeToDelete = true;
TC_LOG_DEBUG("playerbot.corpse", "Corpse {} marked safe for deletion", corpseGuid.ToString());
}
_mutex.unlock_shared();
}
void CorpseCrashMitigation::AddCorpseReference(ObjectGuid corpseGuid)
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto it = _trackedCorpses.find(corpseGuid);
if (it != _trackedCorpses.end())
@@ -330,11 +368,13 @@ void CorpseCrashMitigation::AddCorpseReference(ObjectGuid corpseGuid)
TC_LOG_TRACE("playerbot.corpse", "Corpse {} reference++ (count={})",
corpseGuid.ToString(), it->second->referenceCount.load());
}
_mutex.unlock_shared();
}
void CorpseCrashMitigation::RemoveCorpseReference(ObjectGuid corpseGuid)
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto it = _trackedCorpses.find(corpseGuid);
if (it != _trackedCorpses.end())
@@ -350,6 +390,8 @@ void CorpseCrashMitigation::RemoveCorpseReference(ObjectGuid corpseGuid)
corpseGuid.ToString());
}
}
_mutex.unlock_shared();
}
// ============================================================================
@@ -358,32 +400,44 @@ void CorpseCrashMitigation::RemoveCorpseReference(ObjectGuid corpseGuid)
CorpseLocation const* CorpseCrashMitigation::GetDeathLocation(ObjectGuid botGuid) const
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto it = _deathLocations.find(botGuid);
if (it != _deathLocations.end())
{
_mutex.unlock_shared();
return &it->second;
}
_mutex.unlock_shared();
return nullptr;
}
bool CorpseCrashMitigation::GetCorpseLocation(ObjectGuid ownerGuid, float& x, float& y, float& z, uint32& mapId) const
{
::std::shared_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock_shared();
auto ownerIt = _ownerToCorpse.find(ownerGuid);
if (ownerIt == _ownerToCorpse.end())
{
_mutex.unlock_shared();
return false;
}
auto corpseIt = _trackedCorpses.find(ownerIt->second);
if (corpseIt == _trackedCorpses.end())
{
_mutex.unlock_shared();
return false;
}
x = corpseIt->second->x;
y = corpseIt->second->y;
z = corpseIt->second->z;
mapId = corpseIt->second->mapId;
_mutex.unlock_shared();
TC_LOG_TRACE("playerbot.corpse", "Retrieved corpse location for owner {} at ({:.2f}, {:.2f}, {:.2f})",
ownerGuid.ToString(), x, y, z);
@@ -396,7 +450,7 @@ bool CorpseCrashMitigation::GetCorpseLocation(ObjectGuid ownerGuid, float& x, fl
void CorpseCrashMitigation::CleanupExpiredCorpses()
{
::std::unique_lock<::std::shared_mutex> lock(_mutex);
_mutex.lock();
auto now = ::std::chrono::steady_clock::now();
@@ -438,6 +492,8 @@ void CorpseCrashMitigation::CleanupExpiredCorpses()
++it;
}
}
_mutex.unlock();
}
} // namespace Playerbot
@@ -25,6 +25,7 @@
#include "ObjectGuid.h"
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <shared_mutex>
#include <atomic>
#include <chrono>
@@ -289,6 +290,9 @@ private:
// Strategy 1: Death location cache (for corpse-less resurrection)
::std::unordered_map<ObjectGuid, CorpseLocation> _deathLocations;
// Strategy 1: Pending prevention tracking (internal flag replacement)
::std::unordered_set<ObjectGuid> _pendingPrevention;
// Strategy 2: Corpse tracking (fallback if prevention fails)
::std::unordered_map<ObjectGuid, ::std::unique_ptr<CorpseTracker>> _trackedCorpses;
::std::unordered_map<ObjectGuid, ObjectGuid> _ownerToCorpse; // owner -> corpse mapping
@@ -21,6 +21,7 @@
#include "WorldSession.h"
#include "Session/BotSession.h"
#include "Session/BotWorldSessionMgr.h"
#include "../../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include <sstream>
namespace Playerbot
@@ -23,6 +23,7 @@
#include "MapManager.h"
#include "ObjectAccessor.h"
#include "Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Spatial/SpatialGridQueryHelpers.h" // PHASE 2G: For snapshot-based player validation
#include <algorithm>
#include <random>
@@ -23,6 +23,7 @@
#include "Map.h"
#include "Group.h"
#include "Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Core/Services/BotNpcLocationService.h"
#include "Core/Threading/SafeGridOperations.h" // SEH-protected grid operations
#include <algorithm>
@@ -21,6 +21,7 @@
#include "WorldSession.h"
#include "ObjectAccessor.h"
#include "Session/BotSession.h"
#include "../AI/BotAI.h" // P1 FIX: Required for unique_ptr<BotAI> in BotSession.h
#include "Spatial/SpatialGridQueryHelpers.h"
#include "../Group/GroupMemberResolver.h"
#include "../Core/Diagnostics/GroupMemberDiagnostics.h"