mirror of
https://github.com/liyunfan1223/mod-playerbots.git
synced 2026-06-20 15:39:25 +02:00
# Pull Request Feature - Enable multi node flying for bots - Bots currently only do node to node flying. This PR makes it so they can connect multiple noted. -- This is enabled by sending a vector containing the node sequence instead of a single destination node -- To minimize the run-time cost of searching for available nodes and connection, a cache of all possible connections is prepared at start up using a BFS search algorithm. Refactor - Move all world destination logic (cities, banks, inns) to existing Travel manager - Eliminate flightmastercache and integrate to new manager - replace SQLs calls with in-memory data search by core - Add in new map that stores creature areas by template. Clean up - Move other rpg files to related folder. (Next steps) The selection for where bots fly to should be smarter than it is. Instead of trying to determine where a bot can go, it should first decide where it should go, and then identify the correct way to get there. --- ## Feature Evaluation Please answer the following: - Describe the **minimum logic** required to achieve the intended behavior? - Describe the **cheapest implementation** that produces an acceptable result? - Describe the **runtime cost** when this logic executes across many bots? --- ## How to Test the Changes - Step-by-step instructions to test the change - Any required setup (e.g. multiple players, bots, specific configuration) - Expected behavior and how to verify it ## 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**) The call itself is fairly infrequent, and although now there are a greater number of paths available for the bots, I dont think it would be significant. ## Defaults & Configuration Does this change modify default bot behavior? - - [ ] No - - [x] Yes (**explain why**) If this introduces more advanced or AI-heavy logic: - - [x] Lightweight mode remains the default - - [ ] More complex behavior is optional and thereby configurable --- ## AI Assistance Was AI assistance (e.g. ChatGPT or similar tools) used while working on this change? - - [ ] No - - [x] Yes (**explain below**) Gemini first suggested the use of a BFS algorithm. This was rewritten by me to actually work as intended. Verification by additional logging not present in final code. Claude code converted the SQL filtering to the atrocious if statements found in PrepareDestinationCache, but after verifying them it works. If there are better ways to do this Im open to it. --- ## 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.
263 lines
9.4 KiB
C++
263 lines
9.4 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 Init();
|
|
std::map<uint8, std::unordered_set<ObjectGuid>> addclassCache;
|
|
|
|
// 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
|