Files
mod-playerbots/src/Bot/RandomPlayerbotMgr.h
bashermens 13fff46fa0 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>
2026-01-30 21:49:37 +01:00

280 lines
10 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
* and/or modify it under version 3 of the License, or (at your option), any later version.
*/
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
#define _PLAYERBOT_RANDOMPLAYERBOTMGR_H
#include "NewRpgInfo.h"
#include "ObjectGuid.h"
#include "PlayerbotMgr.h"
#include "GameTime.h"
#include "PlayerbotCommandServer.h"
struct BattlegroundInfo
{
std::vector<uint32> bgInstances;
std::vector<uint32> ratedArenaInstances;
std::vector<uint32> skirmishArenaInstances;
uint32 bgInstanceCount = 0;
uint32 ratedArenaInstanceCount = 0;
uint32 skirmishArenaInstanceCount = 0;
uint32 minLevel = 0;
uint32 maxLevel = 0;
uint32 activeRatedArenaQueue = 0; // 0 = Inactive, 1 = Active
uint32 activeSkirmishArenaQueue = 0; // 0 = Inactive, 1 = Active
uint32 activeBgQueue = 0; // 0 = Inactive, 1 = Active
// Bots (Arena)
uint32 ratedArenaBotCount = 0;
uint32 skirmishArenaBotCount = 0;
// Bots (Battleground)
uint32 bgHordeBotCount = 0;
uint32 bgAllianceBotCount = 0;
// Players (Arena)
uint32 ratedArenaPlayerCount = 0;
uint32 skirmishArenaPlayerCount = 0;
// Players (Battleground)
uint32 bgHordePlayerCount = 0;
uint32 bgAlliancePlayerCount = 0;
};
class ChatHandler;
class PerfMonitorOperation;
class WorldLocation;
struct CachedEvent
{
uint32 value = 0;
uint32 lastChangeTime = 0;
uint32 validIn = 0;
std::string data;
bool IsEmpty() const { return !lastChangeTime; }
};
struct BotEventCache
{
bool loaded = false;
std::unordered_map<std::string, CachedEvent> events;
};
// https://gist.github.com/bradley219/5373998
class botPIDImpl;
class botPID
{
public:
// Kp - proportional gain
// Ki - Integral gain
// Kd - derivative gain
// dt - loop interval time
// max - maximum value of manipulated variable
// min - minimum value of manipulated variable
botPID(double dt, double max, double min, double Kp, double Ki, double Kd);
void adjust(double Kp, double Ki, double Kd);
void reset();
double calculate(double setpoint, double pv);
~botPID();
private:
botPIDImpl* pimpl;
};
class RandomPlayerbotMgr : public PlayerbotHolder
{
public:
static RandomPlayerbotMgr& instance()
{
static RandomPlayerbotMgr instance;
return instance;
}
void LogPlayerLocation();
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
uint32 activeBots = 0;
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
bool IsRandomBot(Player* bot);
bool IsRandomBot(ObjectGuid::LowType bot);
bool IsAddclassBot(Player* bot);
bool IsAddclassBot(ObjectGuid::LowType bot);
void Randomize(Player* bot);
void Clear(Player* bot);
void RandomizeFirst(Player* bot);
void RandomizeMin(Player* bot);
void IncreaseLevel(Player* bot);
void ScheduleTeleport(uint32 bot, uint32 time = 0);
void ScheduleChangeStrategy(uint32 bot, uint32 time = 0);
void HandleCommand(uint32 type, std::string const text, Player* fromPlayer, std::string channelName = "");
std::string const HandleRemoteCommand(std::string const request);
void OnPlayerLogout(Player* player);
void OnPlayerLogin(Player* player);
void OnPlayerLoginError(uint32 bot);
Player* GetRandomPlayer();
std::vector<Player*> GetPlayers() { return players; };
PlayerBotMap GetAllBots() { return playerBots; };
void PrintStats();
double GetBuyMultiplier(Player* bot);
double GetSellMultiplier(Player* bot);
void AddTradeDiscount(Player* bot, Player* master, int32 value);
void SetTradeDiscount(Player* bot, Player* master, uint32 value);
uint32 GetTradeDiscount(Player* bot, Player* master);
void Refresh(Player* bot);
void RandomTeleportForLevel(Player* bot);
void RandomTeleportGrindForLevel(Player* bot);
void RandomTeleportForRpg(Player* bot);
uint32 GetMaxAllowedBotCount();
bool ProcessBot(Player* player);
void Revive(Player* player);
void ChangeStrategy(Player* player);
void ChangeStrategyOnce(Player* player);
uint32 GetValue(Player* bot, std::string const& type);
uint32 GetValue(uint32 bot, std::string const& type);
std::string GetData(uint32 bot, std::string const& type);
void SetValue(uint32 bot, std::string const& type, uint32 value, std::string const& data = "");
void SetValue(Player* bot, std::string const& type, uint32 value, std::string const& data = "");
void Remove(Player* bot);
ObjectGuid GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
CreatureData const* GetCreatureDataByEntry(uint32 entry);
void LoadBattleMastersCache();
std::map<uint32, std::map<uint32, BattlegroundInfo>> BattlegroundData;
std::map<uint32, std::map<uint32, std::map<TeamId, uint32>>> VisualBots;
std::map<uint32, std::map<uint32, std::map<uint32, uint32>>> Supporters;
std::map<TeamId, std::vector<uint32>> LfgDungeons;
void CheckBgQueue();
void CheckLfgQueue();
void CheckPlayers();
void LogBattlegroundInfo();
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> getBattleMastersCache()
{
return BattleMastersCache;
}
float getActivityMod() { return activityMod; }
float getActivityPercentage() { return activityMod * 100.0f; }
void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; }
static uint8 GetTeamClassIdx(bool isAlliance, uint8 claz) { return isAlliance * 20 + claz; }
void PrepareAddclassCache();
void PrepareZone2LevelBracket();
void PrepareTeleportCache();
void Init();
std::map<uint8, std::unordered_set<ObjectGuid>> addclassCache;
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> allianceStarterPerLevelCache;
std::map<uint8, std::vector<WorldLocation>> hordeStarterPerLevelCache;
struct LevelBracket {
uint32 low;
uint32 high;
bool InsideBracket(uint32 val) { return val >= low && val <= high; }
};
std::map<uint32, LevelBracket> zone2LevelBracket;
struct BankerLocation {
WorldLocation loc;
uint32 entry;
};
std::map<uint8, std::vector<BankerLocation>> bankerLocsPerLevelCache;
// Account type management
void AssignAccountTypes();
bool IsAccountType(uint32 accountId, uint8 accountType);
protected:
void OnBotLoginInternal(Player* const bot) override;
private:
RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0)
{
this->playersLevel = sPlayerbotAIConfig.randombotStartingLevel;
if (sPlayerbotAIConfig.enabled || sPlayerbotAIConfig.randomBotAutologin)
{
PlayerbotCommandServer::instance().Start();
}
BattlegroundData.clear(); // Clear here and here only.
// Cleanup on server start: orphaned pet data that's often left behind by bot pets that no longer exist in the DB
CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)");
CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)");
CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)");
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
{
this->BattlegroundData[queueType][bracket] = BattlegroundInfo();
}
}
this->BgCheckTimer = 0;
this->LfgCheckTimer = 0;
this->PlayersCheckTimer = 0;
}
~RandomPlayerbotMgr() = default;
RandomPlayerbotMgr(const RandomPlayerbotMgr&) = delete;
RandomPlayerbotMgr& operator=(const RandomPlayerbotMgr&) = delete;
RandomPlayerbotMgr(RandomPlayerbotMgr&&) = delete;
RandomPlayerbotMgr& operator=(RandomPlayerbotMgr&&) = delete;
// pid values are set in constructor
botPID pid = botPID(1, 50, -50, 0, 0, 0);
float activityMod = 0.25;
bool _isBotInitializing = true;
bool _isBotLogging = true;
NewRpgStatistic rpgStasticTotal;
CachedEvent* FindEvent(uint32 bot, std::string const& event);
uint32 GetEventValue(uint32 bot, std::string const& event);
std::string GetEventData(uint32 bot, std::string const& event);
uint32 SetEventValue(uint32 bot, std::string const& event, uint32 value, uint32 validIn,
std::string const& data = "");
void GetBots();
std::vector<uint32> GetBgBots(uint32 bracket);
time_t BgCheckTimer;
time_t LfgCheckTimer;
time_t PlayersCheckTimer;
time_t RealPlayerLastTimeSeen = 0;
time_t DelayLoginBotsTimer;
time_t printStatsTimer;
uint32 AddRandomBots();
bool ProcessBot(uint32 bot);
void ScheduleRandomize(uint32 bot, uint32 time);
void RandomTeleport(Player* bot);
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
std::vector<Player*> players;
uint32 processTicks;
// std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> BattleMastersCache;
std::unordered_map<uint32, BotEventCache> eventCache;
std::list<uint32> currentBots;
uint32 bgBotsCount;
uint32 playersLevel;
// Account lists
std::vector<uint32> rndBotTypeAccounts; // Accounts marked as RNDbot (type 1)
std::vector<uint32> addClassTypeAccounts; // Accounts marked as AddClass (type 2)
//void ScaleBotActivity(); // Deprecated function
static inline uint32 NowSeconds() { return static_cast<uint32>(GameTime::GetGameTime().count()); }
};
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()
#endif