Improper singletons migration to clean Meyer's singletons (cherry-pick) (#2082)

# Pull Request

- Applies the clean and corrected singletons, Meyer pattern. (cherry
picked from @SmashingQuasar )

Testing by just playing the game in various ways. Been tested by myself
@Celandriel and @SmashingQuasar
---

## Complexity & Impact

- Does this change add new decision branches?
    - [x] No
    - [ ] Yes (**explain below**)

- Does this change increase per-bot or per-tick processing?
    - [x] No
    - [ ] Yes (**describe and justify impact**)

- Could this logic scale poorly under load?
    - [x] No
    - [ ] Yes (**explain why**)

---

## Defaults & Configuration

- Does this change modify default bot behavior?
    - [x] No
    - [ ] Yes (**explain why**)

---

## AI Assistance

- Was AI assistance (e.g. ChatGPT or similar tools) used while working
on this change?
    - [x] No
    - [ ] Yes (**explain below**)
---

## Final Checklist

- [x] Stability is not compromised
- [x] Performance impact is understood, tested, and acceptable
- [x] Added logic complexity is justified and explained
- [x] Documentation updated if needed

---

## Notes for Reviewers

Anything that significantly improves realism at the cost of stability or
performance should be carefully discussed
before merging.

---------

Co-authored-by: Nicolas Lebacq <nicolas.cordier@outlook.com>
Co-authored-by: Keleborn <22352763+Celandriel@users.noreply.github.com>
This commit is contained in:
bashermens
2026-01-30 21:49:37 +01:00
committed by GitHub
parent a92886032c
commit 13fff46fa0
233 changed files with 2460 additions and 2354 deletions

View File

@@ -76,33 +76,33 @@ public:
{
if (!strcmp(args, "reset"))
{
sPerfMonitor->Reset();
sPerfMonitor.Reset();
return true;
}
if (!strcmp(args, "tick"))
{
sPerfMonitor->PrintStats(true, false);
sPerfMonitor.PrintStats(true, false);
return true;
}
if (!strcmp(args, "stack"))
{
sPerfMonitor->PrintStats(false, true);
sPerfMonitor.PrintStats(false, true);
return true;
}
if (!strcmp(args, "toggle"))
{
sPlayerbotAIConfig->perfMonEnabled = !sPlayerbotAIConfig->perfMonEnabled;
if (sPlayerbotAIConfig->perfMonEnabled)
sPlayerbotAIConfig.perfMonEnabled = !sPlayerbotAIConfig.perfMonEnabled;
if (sPlayerbotAIConfig.perfMonEnabled)
LOG_INFO("playerbots", "Performance monitor enabled");
else
LOG_INFO("playerbots", "Performance monitor disabled");
return true;
}
sPerfMonitor->PrintStats();
sPerfMonitor.PrintStats();
return true;
}
@@ -122,7 +122,7 @@ public:
Player* player = handler->GetSession()->GetPlayer();
std::string key = args;
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (mgr)
{
mgr->HandleSetSecurityKeyCommand(player, key);
@@ -151,7 +151,7 @@ public:
Player* player = handler->GetSession()->GetPlayer();
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (mgr)
{
mgr->HandleLinkAccountCommand(player, accountName, key);
@@ -168,7 +168,7 @@ public:
{
Player* player = handler->GetSession()->GetPlayer();
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (mgr)
{
mgr->HandleViewLinkedAccountsCommand(player);
@@ -195,7 +195,7 @@ public:
Player* player = handler->GetSession()->GetPlayer();
PlayerbotMgr* mgr = sPlayerbotsMgr->GetPlayerbotMgr(player);
PlayerbotMgr* mgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (mgr)
{
mgr->HandleUnlinkAccountCommand(player, accountName);

View File

@@ -22,7 +22,6 @@
#include "DatabaseEnv.h"
#include "DatabaseLoader.h"
#include "GuildTaskMgr.h"
#include "Metric.h"
#include "PlayerScript.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotGuildMgr.h"
@@ -98,24 +97,24 @@ public:
{
if (!player->GetSession()->IsBot())
{
sPlayerbotsMgr->AddPlayerbotData(player, false);
sRandomPlayerbotMgr->OnPlayerLogin(player);
PlayerbotsMgr::instance().AddPlayerbotData(player, false);
sRandomPlayerbotMgr.OnPlayerLogin(player);
// Before modifying the following messages, please make sure it does not violate the AGPLv3.0 license
// especially if you are distributing a repack or hosting a public server
// e.g. you can replace the URL with your own repository,
// but it should be publicly accessible and include all modifications you've made
if (sPlayerbotAIConfig->enabled)
if (sPlayerbotAIConfig.enabled)
{
ChatHandler(player->GetSession()).SendSysMessage(
"|cff00ff00This server runs with |cff00ccffmod-playerbots|r "
"|cffcccccchttps://github.com/mod-playerbots/mod-playerbots|r");
}
if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin)
if (sPlayerbotAIConfig.enabled || sPlayerbotAIConfig.randomBotAutologin)
{
std::string roundedTime =
std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.11 / 60) * 10) / 10.0);
std::to_string(std::ceil((sPlayerbotAIConfig.maxRandomBots * 0.11 / 60) * 10) / 10.0);
roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2);
ChatHandler(player->GetSession()).SendSysMessage(
@@ -125,54 +124,11 @@ public:
}
}
bool OnPlayerBeforeTeleport(Player* /*player*/, uint32 /*mapid*/, float /*x*/, float /*y*/, float /*z*/, float /*orientation*/, uint32 /*options*/, Unit* /*target*/) override
{
/* for now commmented out until proven its actually required
* havent seen any proof CleanVisibilityReferences() is needed
// If the player is not safe to touch, do nothing
if (!player)
return true;
// If same map or not in world do nothing
if (!player->IsInWorld() || player->GetMapId() == mapid)
return true;
// If real player do nothing
PlayerbotAI* ai = GET_PLAYERBOT_AI(player);
if (!ai || ai->IsRealPlayer())
return true;
// Cross-map bot teleport: defer visibility reference cleanup.
// CleanVisibilityReferences() erases this bot's GUID from other objects' visibility containers.
// This is intentionally done via the event queue (instead of directly here) because erasing
// from other players' visibility maps inside the teleport call stack can hit unsafe re-entrancy
// or iterator invalidation while visibility updates are in progress
ObjectGuid guid = player->GetGUID();
player->m_Events.AddEventAtOffset(
[guid, mapid]()
{
// do nothing, if the player is not safe to touch
Player* p = ObjectAccessor::FindPlayer(guid);
if (!p || !p->IsInWorld() || p->IsDuringRemoveFromWorld())
return;
// do nothing if we are already on the target map
if (p->GetMapId() == mapid)
return;
p->GetObjectVisibilityContainer().CleanVisibilityReferences();
},
Milliseconds(0));
*/
return true;
}
void OnPlayerAfterUpdate(Player* player, uint32 diff) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player);
if (botAI != nullptr)
{
botAI->UpdateAI(diff);
}
@@ -185,19 +141,26 @@ public:
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
{
if (type == CHAT_MSG_WHISPER)
if (type != CHAT_MSG_WHISPER)
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
{
botAI->HandleCommand(type, msg, player);
// hotfix; otherwise the server will crash when whispering logout
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
// TODO: find the root cause and solve it. (does not happen in party chat)
if (msg == "logout")
return false;
}
return true;
}
PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(receiver);
if (botAI == nullptr)
{
return true;
}
botAI->HandleCommand(type, msg, player);
// hotfix; otherwise the server will crash when whispering logout
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
// TODO: find the root cause and solve it. (does not happen in party chat)
if (msg == "logout")
return false;
return true;
}
@@ -205,56 +168,77 @@ public:
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
if (Player* member = itr->GetSource())
Player* const member = itr->GetSource();
if (member == nullptr)
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member))
{
botAI->HandleCommand(type, msg, player);
}
continue;
}
PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(member);
if (botAI == nullptr)
{
continue;
}
botAI->HandleCommand(type, msg, player);
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
{
if (type == CHAT_MSG_GUILD)
if (type != CHAT_MSG_GUILD)
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin();
it != playerbotMgr->GetPlayerBotsEnd(); ++it)
{
if (Player* const bot = it->second)
{
if (bot->GetGuildId() == player->GetGuildId())
{
GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player);
}
}
}
}
return true;
}
PlayerbotMgr* playerbotMgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (playerbotMgr == nullptr)
{
return true;
}
for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin(); it != playerbotMgr->GetPlayerBotsEnd(); ++it)
{
Player* const bot = it->second;
if (bot == nullptr)
{
continue;
}
if (bot->GetGuildId() != player->GetGuildId())
{
continue;
}
PlayerbotsMgr::instance().GetPlayerbotAI(bot)->HandleCommand(type, msg, player);
}
return true;
}
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
PlayerbotMgr* const playerbotMgr = PlayerbotsMgr::instance().GetPlayerbotMgr(player);
if (playerbotMgr != nullptr && channel->GetFlags() & 0x18)
{
if (channel->GetFlags() & 0x18)
{
playerbotMgr->HandleCommand(type, msg);
}
playerbotMgr->HandleCommand(type, msg);
}
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
sRandomPlayerbotMgr.HandleCommand(type, msg, player);
return true;
}
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
{
if ((sRandomPlayerbotMgr->IsRandomBot(player) || sRandomPlayerbotMgr->IsAddclassBot(player)) &&
if ((sRandomPlayerbotMgr.IsRandomBot(player) || sRandomPlayerbotMgr.IsAddclassBot(player)) &&
(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL)))
{
return false;
@@ -266,11 +250,11 @@ public:
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
{
// early return
if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player)
if (sPlayerbotAIConfig.randomBotXPRate == 1.0 || !player)
return;
// no XP multiplier, when player is no bot.
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player))
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr.IsRandomBot(player))
return;
// no XP multiplier, when bot is in a group with a real player.
@@ -292,7 +276,7 @@ public:
}
// otherwise apply bot XP multiplier.
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig.randomBotXPRate));
}
};
@@ -303,7 +287,9 @@ public:
void OnDestructPlayer(Player* player) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player);
if (botAI != nullptr)
{
delete botAI;
}
@@ -360,20 +346,20 @@ public:
LOG_INFO("server.loading", " ");
LOG_INFO("server.loading", "Load Playerbots Config...");
sPlayerbotAIConfig->Initialize();
sPlayerbotAIConfig.Initialize();
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
sPlayerbotSpellRepository->Initialize();
PlayerbotSpellRepository::Instance().Initialize();
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
}
void OnUpdate(uint32 diff) override
{
sPlayerbotWorldProcessor->Update(diff);
sRandomPlayerbotMgr->UpdateAI(diff); // World thread only
PlayerbotWorldThreadProcessor::instance().Update(diff);
sRandomPlayerbotMgr.UpdateAI(diff); // World thread only
}
};
@@ -385,10 +371,12 @@ public:
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
{
bool nonBotFound = false;
for (ObjectGuid const& guid : guidsList.guids)
{
Player* player = ObjectAccessor::FindPlayer(guid);
if (guid.IsGroup() || (player && !GET_PLAYERBOT_AI(player)))
if (guid.IsGroup() || (player && !PlayerbotsMgr::instance().GetPlayerbotAI(player)))
{
nonBotFound = true;
break;
@@ -401,32 +389,48 @@ public:
void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override
{
if (player)
sGuildTaskMgr->CheckKillTask(player, victim);
GuildTaskMgr::instance().CheckKillTask(player, victim);
}
void OnPlayerbotCheckPetitionAccount(Player* player, bool& found) override
{
if (found && GET_PLAYERBOT_AI(player))
if (!found)
{
return;
}
if (PlayerbotsMgr::instance().GetPlayerbotAI(player) != nullptr)
{
found = false;
}
}
bool OnPlayerbotCheckUpdatesToSend(Player* player) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
return botAI->IsRealPlayer();
PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player);
return true;
if (botAI == nullptr)
{
return true;
}
return botAI->IsRealPlayer();
}
void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet) override
{
if (!player)
if (player == nullptr)
{
return;
}
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player);
if (botAI != nullptr)
{
botAI->HandleBotOutgoingPacket(*packet);
}
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
playerbotMgr->HandleMasterOutgoingPacket(*packet);
@@ -435,7 +439,7 @@ public:
void OnPlayerbotUpdate(uint32 diff) override
{
sRandomPlayerbotMgr->UpdateSessions(); // Per-bot updates only
sRandomPlayerbotMgr.UpdateSessions(); // Per-bot updates only
}
void OnPlayerbotUpdateSessions(Player* player) override
@@ -449,20 +453,21 @@ public:
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || botAI->IsRealPlayer())
PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player);
if (botAI == nullptr || botAI->IsRealPlayer())
{
playerbotMgr->LogoutAllBots();
}
}
sRandomPlayerbotMgr->OnPlayerLogout(player);
sRandomPlayerbotMgr.OnPlayerLogout(player);
}
void OnPlayerbotLogoutBots() override
{
LOG_INFO("playerbots", "Logging out all bots...");
sRandomPlayerbotMgr->LogoutAllBots();
sRandomPlayerbotMgr.LogoutAllBots();
}
};

View File

@@ -27,8 +27,8 @@ int strcmpi(char const* s1, char const* s2);
#define CAST_ANGLE_IN_FRONT (2.f * static_cast<float>(M_PI) / 3.f)
#define EMOTE_ANGLE_IN_FRONT (2.f * static_cast<float>(M_PI) / 6.f)
#define GET_PLAYERBOT_AI(object) sPlayerbotsMgr->GetPlayerbotAI(object)
#define GET_PLAYERBOT_MGR(object) sPlayerbotsMgr->GetPlayerbotMgr(object)
#define GET_PLAYERBOT_AI(object) sPlayerbotsMgr.GetPlayerbotAI(object)
#define GET_PLAYERBOT_MGR(object) sPlayerbotsMgr.GetPlayerbotMgr(object)
#define AI_VALUE(type, name) context->GetValue<type>(name)->Get()
#define AI_VALUE2(type, name, param) context->GetValue<type>(name, param)->Get()
@@ -43,10 +43,10 @@ int strcmpi(char const* s1, char const* s2);
#define RESET_AI_VALUE(type, name) context->GetValue<type>(name)->Reset()
#define RESET_AI_VALUE2(type, name, param) context->GetValue<type>(name, param)->Reset()
#define PAI_VALUE(type, name) sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name)->Get()
#define PAI_VALUE(type, name) sPlayerbotsMgr.GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name)->Get()
#define PAI_VALUE2(type, name, param) \
sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name, param)->Get()
#define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue<type>(name)->Get()
#define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue<type>(name, param)->Get()
sPlayerbotsMgr.GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name, param)->Get()
#define GAI_VALUE(type, name) sSharedValueContext.getGlobalValue<type>(name)->Get()
#define GAI_VALUE2(type, name, param) sSharedValueContext.getGlobalValue<type>(name, param)->Get()
#endif

View File

@@ -29,6 +29,7 @@ namespace
return;
PlayerbotAI* ai = GET_PLAYERBOT_AI(target);
if (!ai)
return;
@@ -41,11 +42,7 @@ namespace
}
}
if (sRandomPlayerbotMgr)
{
sRandomPlayerbotMgr->LogoutPlayerBot(target->GetGUID());
return;
}
sRandomPlayerbotMgr.LogoutPlayerBot(target->GetGUID());
}
}

View File

@@ -355,7 +355,7 @@ public:
if (!member || !newGroup->IsMember(memberGuid))
continue;
PlayerbotAI* memberBotAI = sPlayerbotsMgr->GetPlayerbotAI(member);
PlayerbotAI* memberBotAI = PlayerbotsMgr::instance().GetPlayerbotAI(member);
if (memberBotAI)
memberBotAI->Reset();
@@ -412,13 +412,13 @@ public:
if (!bot)
return false;
PlayerbotAI* botAI = sPlayerbotsMgr->GetPlayerbotAI(bot);
PlayerbotAI* botAI = PlayerbotsMgr::instance().GetPlayerbotAI(bot);
if (!botAI)
return false;
Group* group = bot->GetGroup();
if (group && !bot->InBattleground() && !bot->InBattlegroundQueue() && botAI->HasActivePlayerMaster())
sPlayerbotRepository->Save(botAI);
PlayerbotRepository::instance().Save(botAI);
return true;
}
@@ -448,7 +448,7 @@ public:
bool Execute() override
{
sRandomPlayerbotMgr->AddPlayerBot(m_botGuid, m_masterAccountId);
sRandomPlayerbotMgr.AddPlayerBot(m_botGuid, m_masterAccountId);
return true;
}
@@ -479,23 +479,33 @@ public:
bool Execute() override
{
// find and verify bot still exists
Player* bot = ObjectAccessor::FindConnectedPlayer(m_botGuid);
if (!bot)
return false;
Player* bot = ObjectAccessor::FindConnectedPlayer(this->m_botGuid);
PlayerbotHolder* holder = sRandomPlayerbotMgr;
if (m_masterAccountId)
if (!bot)
{
WorldSession* masterSession = sWorldSessionMgr->FindSession(m_masterAccountId);
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
if (masterPlayer)
holder = GET_PLAYERBOT_MGR(masterPlayer);
return false;
}
if (!holder)
return false;
if (this->m_masterAccountId)
{
WorldSession* masterSession = sWorldSessionMgr->FindSession(this->m_masterAccountId);
Player* masterPlayer = masterSession ? masterSession->GetPlayer() : nullptr;
if (masterPlayer != nullptr)
{
PlayerbotMgr* manager = PlayerbotsMgr::instance().GetPlayerbotMgr(masterPlayer);
if (manager == nullptr)
{
return false;
}
manager->OnBotLogin(bot);
}
}
sRandomPlayerbotMgr.OnBotLogin(bot);
holder->OnBotLogin(bot);
return true;
}

View File

@@ -3,27 +3,12 @@
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#include "PlayerbotWorldThreadProcessor.h"
#include "Log.h"
#include "PlayerbotAIConfig.h"
#include <algorithm>
PlayerbotWorldThreadProcessor::PlayerbotWorldThreadProcessor()
: m_enabled(true), m_maxQueueSize(10000), m_batchSize(100), m_queueWarningThreshold(80),
m_timeSinceLastUpdate(0), m_updateInterval(50) // Process at least every 50ms
{
LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized");
}
#include "PlayerbotWorldThreadProcessor.h"
PlayerbotWorldThreadProcessor::~PlayerbotWorldThreadProcessor() { ClearQueue(); }
PlayerbotWorldThreadProcessor* PlayerbotWorldThreadProcessor::instance()
{
static PlayerbotWorldThreadProcessor instance;
return &instance;
}
#include "Timer.h"
#include "Log.h"
void PlayerbotWorldThreadProcessor::Update(uint32 diff)
{

View File

@@ -6,13 +6,14 @@
#ifndef _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
#define _PLAYERBOT_WORLD_THREAD_PROCESSOR_H
#include "Common.h"
#include "PlayerbotOperation.h"
#include <memory>
#include <mutex>
#include <queue>
#include "Log.h"
#include "PlayerbotOperation.h"
/**
* @brief Processes thread-unsafe bot operations in the world thread
*
@@ -28,15 +29,17 @@
*
* Usage:
* auto op = std::make_unique<MyOperation>(botGuid, params);
* sPlayerbotWorldProcessor->QueueOperation(std::move(op));
* PlayerbotWorldThreadProcessor::instance().QueueOperation(std::move(op));
*/
class PlayerbotWorldThreadProcessor
{
public:
PlayerbotWorldThreadProcessor();
~PlayerbotWorldThreadProcessor();
static PlayerbotWorldThreadProcessor& instance()
{
static PlayerbotWorldThreadProcessor instance;
static PlayerbotWorldThreadProcessor* instance();
return instance;
}
/**
* @brief Update and process queued operations (called from world thread)
@@ -103,6 +106,21 @@ public:
bool IsEnabled() const { return m_enabled; }
private:
PlayerbotWorldThreadProcessor()
: m_enabled(true),
m_maxQueueSize(10000),
m_batchSize(100),
m_queueWarningThreshold(80),
m_timeSinceLastUpdate(0),
m_updateInterval(50) // Process at least every 50ms
{
LOG_INFO("playerbots", "PlayerbotWorldThreadProcessor initialized");
}
~PlayerbotWorldThreadProcessor()
{
this->ClearQueue();
}
/**
* @brief Process a single batch of operations
*
@@ -137,6 +155,4 @@ private:
uint32 m_updateInterval; // Minimum ms between updates
};
#define sPlayerbotWorldProcessor PlayerbotWorldThreadProcessor::instance()
#endif

View File

@@ -50,7 +50,7 @@ uint32 Queue::Size()
void Queue::RemoveExpired()
{
if (!sPlayerbotAIConfig->expireActionTime)
if (!sPlayerbotAIConfig.expireActionTime)
{
return;
}
@@ -113,7 +113,7 @@ ActionNode* Queue::extractAndDeleteBasket(ActionBasket* basket)
void Queue::collectExpiredBaskets(std::list<ActionBasket*>& expiredBaskets)
{
uint32 expiryTime = sPlayerbotAIConfig->expireActionTime;
uint32 expiryTime = sPlayerbotAIConfig.expireActionTime;
for (ActionBasket* basket : actions)
{
if (basket->isExpired(expiryTime))

View File

@@ -56,7 +56,7 @@ public:
/**
* @brief Removes and deletes expired actions from the queue
*
* Uses sPlayerbotAIConfig->expireActionTime to determine if actions have expired.
* Uses sPlayerbotAIConfig.expireActionTime to determine if actions have expired.
* Both the ActionNode and ActionBasket are deleted for expired actions.
*/
void RemoveExpired();