diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 249794c8..6e9d7fae 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -449,6 +449,11 @@ AiPlayerbot.BotCheats = "taxi" # Enables/Disables password to bot account AiPlayerbot.RandomBotRandomPassword = 0 +# Diff with/without player in server. The server will tune bot activity to reach the desired server tick speed (in ms). +# AiPlayerbot.EnablePrototypePerformanceDiff = 0 +# AiPlayerbot.DiffWithPlayer = 100 +# AiPlayerbot.DiffEmpty = 200 + ################################################################################## # # # Database Stuff # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 2b824f97..6bc26946 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -282,6 +282,11 @@ bool PlayerbotAIConfig::Initialize() playerbotsXPrate = sConfigMgr->GetOption("AiPlayerbot.KillXPRate", 1); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 10); + + enablePrototypePerformanceDiff = sConfigMgr->GetOption("AiPlayerbot.EnablePrototypePerformanceDiff", false); + diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100); + diffEmpty = sConfigMgr->GetIntDefault("AiPlayerbot.DiffEmpty", 200); + randombotsWalkingRPG = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG", false); randombotsWalkingRPGInDoors = sConfigMgr->GetOption("AiPlayerbot.RandombotsWalkingRPG.InDoors", false); minEnchantingBotLevel = sConfigMgr->GetOption("AiPlayerbot.MinEnchantingBotLevel", 60); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 3fbdf45c..cbe18cd7 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -178,6 +178,10 @@ class PlayerbotAIConfig uint32 playerbotsXPrate; uint32 botActiveAlone; + uint32 enablePrototypePerformanceDiff; + uint32 diffWithPlayer; + uint32 diffEmpty; + bool freeMethodLoot; int32 lootRollLevel; std::string autoPickReward; diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 55a884ce..299a67cd 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -28,6 +28,7 @@ #include "ChannelMgr.h" #include "Unit.h" #include "World.h" +#include "UpdateTime.h" #include #include @@ -79,6 +80,106 @@ void activateCheckPlayersThread() t.detach(); } +class botPIDImpl +{ +public: + botPIDImpl(double dt, double max, double min, double Kp, double Ki, double Kd); + ~botPIDImpl(); + double calculate(double setpoint, double pv); + void adjust(double Kp, double Ki, double Kd) { _Kp = Kp; _Ki = Ki; _Kd = Kd; } + void reset() { _integral = 0; } + +private: + double _dt; + double _max; + double _min; + double _Kp; + double _Ki; + double _Kd; + double _pre_error; + double _integral; +}; + + +botPID::botPID(double dt, double max, double min, double Kp, double Ki, double Kd) +{ + pimpl = new botPIDImpl(dt, max, min, Kp, Ki, Kd); +} +void botPID::adjust(double Kp, double Ki, double Kd) +{ + pimpl->adjust(Kp, Ki, Kd); +} +void botPID::reset() +{ + pimpl->reset(); +} +double botPID::calculate(double setpoint, double pv) +{ + return pimpl->calculate(setpoint, pv); +} +botPID::~botPID() +{ + delete pimpl; +} + + +/** + * Implementation + */ +botPIDImpl::botPIDImpl(double dt, double max, double min, double Kp, double Ki, double Kd) : + _dt(dt), + _max(max), + _min(min), + _Kp(Kp), + _Ki(Ki), + _Kd(Kd), + _pre_error(0), + _integral(0) +{ +} + +double botPIDImpl::calculate(double setpoint, double pv) +{ + + // Calculate error + double error = setpoint - pv; + + // Proportional term + double Pout = _Kp * error; + + // Integral term + _integral += error * _dt; + double Iout = _Ki * _integral; + + // Derivative term + double derivative = (error - _pre_error) / _dt; + double Dout = _Kd * derivative; + + // Calculate total output + double output = Pout + Iout + Dout; + + // Restrict to max/min + if (output > _max) + { + output = _max; + _integral -= error * _dt; //Stop integral buildup at max + } + else if (output < _min) + { + output = _min; + _integral -= error * _dt; //Stop integral buildup at min + } + + // Save error to previous error + _pre_error = error; + + return output; +} + +botPIDImpl::~botPIDImpl() +{ +} + RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0), totalPmo(nullptr) { playersLevel = sPlayerbotAIConfig->randombotStartingLevel; @@ -236,6 +337,14 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) if (!sPlayerbotAIConfig->randomBotAutologin || !sPlayerbotAIConfig->enabled) return; + if (sPlayerbotAIConfig->enablePrototypePerformanceDiff) + { + LOG_INFO("playerbots", "---------------------------------------"); + LOG_INFO("playerbots", "PROTOTYPE: Playerbot performance enhancements are active. Issues and instability may occur."); + LOG_INFO("playerbots", "---------------------------------------"); + ScaleBotActivity(); + } + uint32 maxAllowedBotCount = GetEventValue(0, "bot_count"); if (!maxAllowedBotCount || (maxAllowedBotCount < sPlayerbotAIConfig->minRandomBots || maxAllowedBotCount > sPlayerbotAIConfig->maxRandomBots)) { @@ -327,6 +436,23 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) } } +void RandomPlayerbotMgr::ScaleBotActivity() +{ + float activityPercentage = getActivityPercentage(); + + //if (activityPercentage >= 100.0f || activityPercentage <= 0.0f) pid.reset(); //Stop integer buildup during max/min activity + + // % increase/decrease wanted diff , avg diff + float activityPercentageMod = pid.calculate(sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer, sWorldUpdateTime.GetAverageUpdateTime()); + + activityPercentage = activityPercentageMod + 50; + + //Cap the percentage between 0 and 100. + activityPercentage = std::max(0.0f, std::min(100.0f, activityPercentage)); + + setActivityPercentage(activityPercentage); +} + uint32 RandomPlayerbotMgr::AddRandomBots() { uint32 maxAllowedBotCount = GetEventValue(0, "bot_count"); diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index dc6a362b..a79bd269 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -27,6 +27,29 @@ class CachedEvent std::string data; }; +//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: @@ -41,6 +64,9 @@ class RandomPlayerbotMgr : public PlayerbotHolder void LogPlayerLocation(); void UpdateAIInternal(uint32 elapsed, bool minimal = false) override; + private: + void ScaleBotActivity(); + public: uint32 activeBots = 0; static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args); @@ -99,10 +125,17 @@ class RandomPlayerbotMgr : public PlayerbotHolder std::map>> getBattleMastersCache() { return BattleMastersCache; } + float getActivityMod() { return activityMod; } + float getActivityPercentage() { return activityMod * 100.0f; } + void setActivityPercentage(float percentage) { activityMod = percentage / 100.0f; } + protected: void OnBotLoginInternal(Player* const bot) override; private: + //pid values are set in constructor + botPID pid = botPID(1, 50, -50, 0, 0, 0); + float activityMod = 0.25; uint32 GetEventValue(uint32 bot, std::string const event); std::string const GetEventData(uint32 bot, std::string const event); uint32 SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn, std::string const data = ""); @@ -137,4 +170,4 @@ class RandomPlayerbotMgr : public PlayerbotHolder #define sRandomPlayerbotMgr RandomPlayerbotMgr::instance() -#endif +#endif \ No newline at end of file diff --git a/src/strategy/actions/BattleGroundJoinAction.cpp b/src/strategy/actions/BattleGroundJoinAction.cpp index 951f0dc1..e64a2567 100644 --- a/src/strategy/actions/BattleGroundJoinAction.cpp +++ b/src/strategy/actions/BattleGroundJoinAction.cpp @@ -11,6 +11,7 @@ #include "PlayerbotAI.h" #include "Playerbots.h" #include "PositionValue.h" +#include "UpdateTime.h" bool BGJoinAction::Execute(Event event) { @@ -239,6 +240,7 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun bool isArena = false; bool isRated = false; + bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer) * 1.1; ArenaType type = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId)); if (type != ARENA_TYPE_NONE) @@ -246,12 +248,33 @@ bool BGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battlegroun bool hasPlayers = (sRandomPlayerbotMgr->BgPlayers[queueTypeId][bracketId][TEAM_ALLIANCE] + sRandomPlayerbotMgr->BgPlayers[queueTypeId][bracketId][TEAM_HORDE]) > 0; bool hasBots = (sRandomPlayerbotMgr->BgBots[queueTypeId][bracketId][TEAM_ALLIANCE] + sRandomPlayerbotMgr->BgBots[queueTypeId][bracketId][TEAM_HORDE]) >= bg->GetMinPlayersPerTeam(); + if (!sPlayerbotAIConfig->randomBotAutoJoinBG && !hasPlayers) + { return false; - if (!(hasPlayers || hasBots)) + if (sPlayerbotAIConfig->enablePrototypePerformanceDiff) + { + if (!noLag) + { + return false; + } + } + } + + if (!hasPlayers && !(isArena)) + { return false; + if (sPlayerbotAIConfig->enablePrototypePerformanceDiff) + { + if (!noLag) + { + return false; + } + } + } + uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2; uint32 TeamSize = bg->GetMaxPlayersPerTeam(); @@ -584,6 +607,7 @@ bool FreeBGJoinAction::shouldJoinBg(BattlegroundQueueTypeId queueTypeId, Battleg bool isArena = false; bool isRated = false; + bool noLag = sWorldUpdateTime.GetAverageUpdateTime() < (sRandomPlayerbotMgr->GetPlayers().empty() ? sPlayerbotAIConfig->diffEmpty : sPlayerbotAIConfig->diffWithPlayer) * 1.1; ArenaType type = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId)); if (type != ARENA_TYPE_NONE) @@ -1117,4 +1141,4 @@ bool BGStrategyCheckAction::Execute(Event event) return false; } return false; -} \ No newline at end of file +}