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

@@ -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();
}
};