Add performance tuning

Add a diff with and without player in server. The server will tune bot activity to reach the desired server tick speed (in ms), as what CMaNGOS' playerbots are using.

ce4bd79d2d is used as a reference.
This commit is contained in:
Brian Oost
2024-05-27 23:30:06 +02:00
parent fb839823af
commit 3879b5116e
6 changed files with 200 additions and 3 deletions

View File

@@ -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 #

View File

@@ -282,6 +282,11 @@ bool PlayerbotAIConfig::Initialize()
playerbotsXPrate = sConfigMgr->GetOption<int32>("AiPlayerbot.KillXPRate", 1);
botActiveAlone = sConfigMgr->GetOption<int32>("AiPlayerbot.BotActiveAlone", 10);
enablePrototypePerformanceDiff = sConfigMgr->GetOption<bool>("AiPlayerbot.EnablePrototypePerformanceDiff", false);
diffWithPlayer = sConfigMgr->GetOption("AiPlayerbot.DiffWithPlayer", 100);
diffEmpty = sConfigMgr->GetIntDefault("AiPlayerbot.DiffEmpty", 200);
randombotsWalkingRPG = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG", false);
randombotsWalkingRPGInDoors = sConfigMgr->GetOption<bool>("AiPlayerbot.RandombotsWalkingRPG.InDoors", false);
minEnchantingBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.MinEnchantingBotLevel", 60);

View File

@@ -178,6 +178,10 @@ class PlayerbotAIConfig
uint32 playerbotsXPrate;
uint32 botActiveAlone;
uint32 enablePrototypePerformanceDiff;
uint32 diffWithPlayer;
uint32 diffEmpty;
bool freeMethodLoot;
int32 lootRollLevel;
std::string autoPickReward;

View File

@@ -28,6 +28,7 @@
#include "ChannelMgr.h"
#include "Unit.h"
#include "World.h"
#include "UpdateTime.h"
#include <algorithm>
#include <cstdlib>
@@ -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");

View File

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

View File

@@ -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,11 +248,32 @@ 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)