fix: Replace static m_botReleaseTimes with per-bot storage to prevent race condition

Fixes a thread safety issue where multiple bots dying in battlegrounds
simultaneously would corrupt the shared static unordered_map, causing
segmentation faults.

Changes:
- Remove: static m_botReleaseTimes map from AutoReleaseSpiritAction
- Add: bgReleaseAttemptTime member to PlayerbotAI (per-bot storage)
- Update: All references to use per-bot storage instead of static map

Why this fixes the crash:
- Each PlayerbotAI instance is accessed by only one map update thread
- No cross-thread access to shared data structures
- No mutex/locking required - thread-safe by design
- Automatic cleanup when bot is destroyed

Thread-safe solution: Per-bot state eliminates race conditions without
performance overhead.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-10-06 11:22:45 +11:00
parent d26c2a3549
commit c90b155a70
3 changed files with 7 additions and 9 deletions

View File

@@ -601,6 +601,7 @@ public:
NewRpgInfo rpgInfo; NewRpgInfo rpgInfo;
NewRpgStatistic rpgStatistic; NewRpgStatistic rpgStatistic;
std::unordered_set<uint32> lowPriorityQuest; std::unordered_set<uint32> lowPriorityQuest;
time_t bgReleaseAttemptTime = 0;
// Schedules a callback to run once after <delayMs> milliseconds. // Schedules a callback to run once after <delayMs> milliseconds.
void AddTimedEvent(std::function<void()> callback, uint32 delayMs); void AddTimedEvent(std::function<void()> callback, uint32 delayMs);

View File

@@ -192,12 +192,11 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
{ {
// The below delays release to spirit with 6 seconds. // The below delays release to spirit with 6 seconds.
// This prevents currently casted (ranged) spells to be re-directed to the died bot's ghost. // This prevents currently casted (ranged) spells to be re-directed to the died bot's ghost.
const int32_t botId = bot->GetGUID().GetRawValue();
// If the bot already is a spirit, erase release time and return true // If the bot already is a spirit, reset release time and return true
if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) if (bot->HasPlayerFlag(PLAYER_FLAGS_GHOST))
{ {
m_botReleaseTimes.erase(botId); botAI->bgReleaseAttemptTime = 0;
return true; return true;
} }
@@ -205,14 +204,13 @@ bool AutoReleaseSpiritAction::ShouldDelayBattlegroundRelease() const
const time_t now = time(nullptr); const time_t now = time(nullptr);
constexpr time_t RELEASE_DELAY = 6; constexpr time_t RELEASE_DELAY = 6;
auto& lastReleaseTime = m_botReleaseTimes[botId]; if (botAI->bgReleaseAttemptTime == 0)
if (lastReleaseTime == 0) botAI->bgReleaseAttemptTime = now;
lastReleaseTime = now;
if (now - lastReleaseTime < RELEASE_DELAY) if (now - botAI->bgReleaseAttemptTime < RELEASE_DELAY)
return false; return false;
m_botReleaseTimes.erase(botId); botAI->bgReleaseAttemptTime = 0;
return true; return true;
} }

View File

@@ -38,7 +38,6 @@ private:
bool ShouldAutoRelease() const; bool ShouldAutoRelease() const;
bool ShouldDelayBattlegroundRelease() const; bool ShouldDelayBattlegroundRelease() const;
inline static std::unordered_map<uint32_t, time_t> m_botReleaseTimes;
time_t m_bgGossipTime = 0; time_t m_bgGossipTime = 0;
}; };