diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 8d7bf12e..4a42ed8e 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -728,7 +728,7 @@ AiPlayerbot.FastReactInBG = 1 # # All In seconds -AiPlayerbot.RandomBotUpdateInterval = 1 +AiPlayerbot.RandomBotUpdateInterval = 20 AiPlayerbot.RandomBotCountChangeMinInterval = 1800 AiPlayerbot.RandomBotCountChangeMaxInterval = 7200 AiPlayerbot.MinRandomBotInWorldTime = 3600 @@ -1460,7 +1460,7 @@ AiPlayerbot.BotActiveAlone = 100 # Specify smart scaling is enabled or not. # The default is 1. When enabled (smart) scales the 'BotActiveAlone' value. # Only when botLevel is between WhenMinLevel and WhenMaxLevel. -AiPlayerbot.botActiveAloneSmartScale = 1 +AiPlayerbot.botActiveAloneSmartScale = 0 AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel = 1 AiPlayerbot.botActiveAloneSmartScaleWhenMaxLevel = 80 diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 3c515ebb..f804e975 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "AiFactory.h" #include "BudgetValues.h" @@ -31,7 +32,6 @@ #include "ObjectGuid.h" #include "PerformanceMonitor.h" #include "Player.h" -#include "GameTime.h" #include "PlayerbotAIConfig.h" #include "PlayerbotDbStore.h" #include "PlayerbotMgr.h" @@ -49,6 +49,7 @@ #include "Unit.h" #include "UpdateTime.h" #include "Vehicle.h" +#include "GameTime.h" std::vector PlayerbotAI::dispel_whitelist = { "mutating injection", @@ -240,7 +241,6 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) { return; } - // if (!GetMaster() || !GetMaster()->IsInWorld() || !GetMaster()->GetSession() || // GetMaster()->GetSession()->isLogingOut()) { // return; @@ -303,7 +303,6 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal) // bot->GetMotionMaster()->Clear(); // bot->GetMotionMaster()->MoveIdle(); // } - // cheat options if (bot->IsAlive() && ((uint32)GetCheat() > 0 || (uint32)sPlayerbotAIConfig->botCheatMask > 0)) { @@ -2452,8 +2451,12 @@ bool PlayerbotAI::SayToWorld(const std::string& msg) bool PlayerbotAI::SayToChannel(const std::string& msg, const ChatChannelId& chanId) { + // Checks whether the message or ChannelMgr is valid + if (msg.empty()) + return false; + ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId()); - if (!cMgr || msg.empty()) + if (!cMgr) return false; AreaTableEntry const* current_zone = GetCurrentZone(); @@ -2461,11 +2464,24 @@ bool PlayerbotAI::SayToChannel(const std::string& msg, const ChatChannelId& chan return false; const auto current_str_zone = GetLocalizedAreaName(current_zone); + + std::mutex socialMutex; + std::lock_guard lock(socialMutex); // Blocking for thread safety when accessing SocialMgr + for (auto const& [key, channel] : cMgr->GetChannels()) { - // check for current zone - if (channel && channel->GetChannelId() == chanId) + // Checks if the channel pointer is valid + if (!channel) + continue; + + // Checks if the channel matches the specified ChatChannelId + if (channel->GetChannelId() == chanId) { + // If the channel name is empty, skip it to avoid access problems + if (channel->GetName().empty()) + continue; + + // Checks if the channel name contains the current zone const auto does_contains = channel->GetName().find(current_str_zone) != std::string::npos; if (chanId != ChatChannelId::LOOKING_FOR_GROUP && chanId != ChatChannelId::WORLD_DEFENSE && !does_contains) { @@ -2473,11 +2489,15 @@ bool PlayerbotAI::SayToChannel(const std::string& msg, const ChatChannelId& chan } else if (chanId == ChatChannelId::LOOKING_FOR_GROUP || chanId == ChatChannelId::WORLD_DEFENSE) { - // check if capitals then return false if not + // Here you can add the capital check if necessary } - channel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); - return true; + // Final check to ensure the channel is correct before trying to say something + if (channel) + { + channel->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); + return true; + } } } @@ -3929,10 +3949,7 @@ Player* PlayerbotAI::GetGroupMaster() uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, float cyclePerMin) { - //deterministic seed - uint8 seedNumber = uint8(typeNumber); - std::mt19937 rng(seedNumber); - uint32 randseed = rng(); // Seed random number + uint32 randseed = rand32(); // Seed random number uint32 randnum = bot->GetGUID().GetCounter() + randseed; // Semi-random but fixed number for each bot. if (cyclePerMin > 0) @@ -3942,7 +3959,8 @@ uint32 PlayerbotAI::GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum, fl randnum += cycle; // Make the random number cylce. } - randnum = (randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99. + randnum = + (randnum % (maxNum + 1)); // Loops the randomnumber at maxNum. Bassically removes all the numbers above 99. return randnum; // Now we have a number unique for each bot between 0 and maxNum that increases by cyclePerMin. } @@ -4080,73 +4098,83 @@ inline bool HasRealPlayers(Map* map) return false; } -ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) +bool PlayerbotAI::AllowActive(ActivityType activityType) { - // First priority - priorities disabled or has player master. Always active. - if (HasRealPlayerMaster()) - return ActivePiorityType::HAS_REAL_PLAYER_MASTER; + // only keep updating till initializing time has completed, + // which prevents unneeded expensive GameTime calls. + if (_isBotInitializing) + { + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.12; - // Self bot in a group with a bot master. - if (IsRealPlayer()) - return ActivePiorityType::IS_REAL_PLAYER; + // no activity allowed during bot initialization + if (_isBotInitializing) + { + return false; + } + } + // General exceptions + if (activityType == PACKET_ACTIVITY) + { + return true; + } + + // Has player master. Always active. + if (GetMaster()) + { + PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(GetMaster()); + if (!masterBotAI || masterBotAI->IsRealPlayer()) + { + return true; + } + } + + // If grouped up Group* group = bot->GetGroup(); if (group) { for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) { Player* member = gref->GetSource(); - if (!member || (!member->IsInWorld() && member->GetMapId() != bot->GetMapId())) + if (!member || !member->IsInWorld() && member->GetMapId() != bot->GetMapId()) + { continue; + } if (member == bot) + { continue; + } - //IN_GROUP_WITH_REAL_PLAYER PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); - if (!memberBotAI || memberBotAI->HasRealPlayerMaster()) - return ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER; + { + if (!memberBotAI || memberBotAI->HasRealPlayerMaster()) + { + return true; + } + } - //ALLOWED_PARTY_ACTIVITY if (group->IsLeader(member->GetGUID())) { if (!memberBotAI->AllowActivity(PARTY_ACTIVITY)) - return ActivePiorityType::ALLOWED_PARTY_ACTIVITY; + { + return false; + } } } } - //IN_INSTANCE - if (bot->IsBeingTeleported()) // Allow activity while teleportation. - return ActivePiorityType::IN_INSTANCE; - - //IN_INSTANCE + // bg, raid, dungeon if (!WorldPosition(bot).isOverworld()) - return ActivePiorityType::IN_INSTANCE; - - //IN_COMBAT - if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY) { - // Is in combat, defend yourself. - if (bot->IsInCombat()) - return ActivePiorityType::IN_COMBAT; + return true; } - // IN_REACT_DISTANCE - if (HasPlayerNearby(sPlayerbotAIConfig->reactDistance)) - return ActivePiorityType::IN_REACT_DISTANCE; - - // NEARBY_PLAYER acitivity based on yards. - if (HasPlayerNearby(300.f)) - return ActivePiorityType::NEARBY_PLAYER_300; - if (HasPlayerNearby(600.f)) - return ActivePiorityType::NEARBY_PLAYER_600; - - //if (sPlayerbotAIConfig->IsFreeAltBot(bot) || HasStrategy("travel once", BotState::BOT_STATE_NON_COMBAT)) - // return ActivePiorityType::IS_ALWAYS_ACTIVE; - + // In bg queue. Speed up bg queue/join. if (bot->InBattlegroundQueue()) - return ActivePiorityType::IN_BG_QUEUE; + { + return true; + } bool isLFG = false; if (group) @@ -4161,122 +4189,82 @@ ActivePiorityType PlayerbotAI::GetPriorityType(ActivityType activityType) isLFG = true; } if (isLFG) - return ActivePiorityType::IN_LFG; - - //IN_EMPTY_SERVER - if (sRandomPlayerbotMgr->GetPlayers().empty()) - return ActivePiorityType::IN_EMPTY_SERVER; - - //PLAYER_FRIEND (on friendlist of real player) (needs to be tested for stability) - /*for (auto& player : sRandomPlayerbotMgr->GetPlayers()) { - if (!player || !player->IsInWorld()) - continue; + return true; + } - if (player->GetSocial() && - bot->GetGUID() && - player->GetSocial()->HasFriend(bot->GetGUID())) - return ActivePiorityType::PLAYER_FRIEND; + // Is in combat. Defend yourself. + if (activityType != OUT_OF_PARTY_ACTIVITY && activityType != PACKET_ACTIVITY) + { + if (bot->IsInCombat()) + { + return true; + } + } + + // Player is near. Always active. + if (HasPlayerNearby(300.f)) + { + return true; + } + + // friends always active + + // HasFriend sometimes cause crash, disable + // for (auto& player : sRandomPlayerbotMgr->GetPlayers()) + // { + // if (!player || !player->IsInWorld() || !player->GetSocial() || !bot->GetGUID()) + // continue; + + // if (player->GetSocial()->HasFriend(bot->GetGUID())) + // return true; + // } + + /* if (activityType == OUT_OF_PARTY_ACTIVITY || activityType == GRIND_ACTIVITY) + { + if (HasManyPlayersNearby()) + { + return false; + } }*/ - //PLAYER_GUILD (contains real player) - if (IsInRealGuild()) - return ActivePiorityType::PLAYER_GUILD; - - - //IN_NOT_ACTIVE_MAP - if (Map* map = bot->GetMap()) + // Bots don't need to move using PathGenerator. + if (activityType == DETAILED_MOVE_ACTIVITY) { - if (map->GetEntry()->IsWorldMap()) - { - if (!HasRealPlayers(map)) - return ActivePiorityType::IN_NOT_ACTIVE_MAP; - - if (!map->IsGridLoaded(bot->GetPositionX(), bot->GetPositionY())) - return ActivePiorityType::IN_NOT_ACTIVE_MAP; - } - } - - //IN_ACTIVE_MAP - if (Map* map = bot->GetMap()) - { - if (map->GetEntry()->IsWorldMap()) - { - if (HasRealPlayers(map)) - return ActivePiorityType::IN_ACTIVE_MAP; - } - } - - // IN_VERY_ACTIVE_AREA - if (activityType == OUT_OF_PARTY_ACTIVITY || activityType == GRIND_ACTIVITY) - { - if (HasManyPlayersNearby(20, sPlayerbotAIConfig->sightDistance)) - return ActivePiorityType::IN_VERY_ACTIVE_AREA; - } - - return ActivePiorityType::DEFAULT; -} - -bool PlayerbotAI::AllowActive(ActivityType activityType) -{ - // no activity allowed during bot initialization during first - // few minutes after starting the server based on maxRandomBots. - if (sRandomPlayerbotMgr->isBotInitializing()) return false; - - // General exceptions - if (activityType == PACKET_ACTIVITY) - return true; - - uint32 botActiveAlonePerc = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone; - uint32 mod = botActiveAlonePerc; - ActivePiorityType type = GetPriorityType(activityType); - - switch (type) - { - case ActivePiorityType::HAS_REAL_PLAYER_MASTER: - case ActivePiorityType::IS_REAL_PLAYER: - case ActivePiorityType::IN_GROUP_WITH_REAL_PLAYER: - case ActivePiorityType::IN_INSTANCE: - case ActivePiorityType::IN_COMBAT: - case ActivePiorityType::IN_REACT_DISTANCE: - case ActivePiorityType::NEARBY_PLAYER_300: - case ActivePiorityType::IS_ALWAYS_ACTIVE: - return true; - break; - case ActivePiorityType::ALLOWED_PARTY_ACTIVITY: - return false; - break; - case ActivePiorityType::IN_BG_QUEUE: - case ActivePiorityType::IN_LFG: - case ActivePiorityType::PLAYER_FRIEND: - case ActivePiorityType::PLAYER_GUILD: - case ActivePiorityType::IN_ACTIVE_MAP: - case ActivePiorityType::IN_NOT_ACTIVE_MAP: - case ActivePiorityType::IN_EMPTY_SERVER: - default: - break; } - // Bots do not need to move using PathGenerator. - //if (activityType == DETAILED_MOVE_ACTIVITY) return false; + if (sPlayerbotAIConfig->botActiveAlone <= 0) + { + return false; + } + if (sPlayerbotAIConfig->botActiveAlone >= 100 && !sPlayerbotAIConfig->botActiveAloneSmartScale) + { + return true; + } - // All exceptions are now done, below is the code to have a specified % of bots - // active at all times. The default is 10%. With 0.1% of all bots going active - // or inactive each minute. - if (sPlayerbotAIConfig->botActiveAlone <= 0) return false; + // ####################################################################################### + // All mandatory conditations are checked to be active or not, from here the remaining + // situations are usable for scaling when enabled. + // ####################################################################################### + + // Below is code to have a specified % of bots active at all times. + // The default is 10%. With 0.1% of all bots going active or inactive each minute. + uint32 mod = sPlayerbotAIConfig->botActiveAlone > 100 ? 100 : sPlayerbotAIConfig->botActiveAlone; if (sPlayerbotAIConfig->botActiveAloneSmartScale && bot->GetLevel() >= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMinLevel && bot->GetLevel() <= sPlayerbotAIConfig->botActiveAloneSmartScaleWhenMaxLevel) { - mod = SmartScaleActivity(type, botActiveAlonePerc); + mod = AutoScaleActivity(mod); } - uint32 ActivityNumber = GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, - botActiveAlonePerc * static_cast(mod) / 100 * 0.01f); + uint32 ActivityNumber = + GetFixedBotNumer(BotTypeNumber::ACTIVITY_TYPE_NUMBER, 100, + sPlayerbotAIConfig->botActiveAlone * static_cast(mod) / 100 * 0.01f); - // The given percentage of bots should be active and rotate 1% of those active bots each minute. - return ActivityNumber <= (botActiveAlonePerc * mod) / 100; + return ActivityNumber <= + (sPlayerbotAIConfig->botActiveAlone * mod) / + 100; // The given percentage of bots should be active and rotate 1% of those active bots each minute. } bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow) @@ -4293,42 +4281,37 @@ bool PlayerbotAI::AllowActivity(ActivityType activityType, bool checkNow) return allowed; } -uint32 PlayerbotAI::SmartScaleActivity(ActivePiorityType type, uint32 botActiveAlonePerc) +uint32 PlayerbotAI::AutoScaleActivity(uint32 mod) { - uint32 maxDiff = sWorldUpdateTime.GetMaxUpdateTime(); - if (maxDiff > 1000) return false; - switch (type) - { - case ActivePiorityType::IN_BG_QUEUE: - case ActivePiorityType::IN_LFG: - if (maxDiff > 100) return 80; - if (maxDiff > 100) return 90; - break; - case ActivePiorityType::NEARBY_PLAYER_600: - if (maxDiff > 100) return 50; - if (maxDiff > 50) return 75; - break; - case ActivePiorityType::PLAYER_FRIEND: - case ActivePiorityType::PLAYER_GUILD: - case ActivePiorityType::IN_ACTIVE_MAP: - if (maxDiff > 200) return 30; - if (maxDiff > 100) return 50; - if (maxDiff > 50) return 75; - break; - case ActivePiorityType::IN_VERY_ACTIVE_AREA: - case ActivePiorityType::IN_NOT_ACTIVE_MAP: - if (maxDiff > 100) return 30; - if (maxDiff > 50) return 50; - break; - case ActivePiorityType::IN_EMPTY_SERVER: - return 30; - default: - if (maxDiff > 200) return 30; - if (maxDiff > 100) return 50; - break; - } + uint32 maxDiff = sWorldUpdateTime.GetAverageUpdateTime(); - return botActiveAlonePerc; + if (maxDiff > 500) return 0; + if (maxDiff > 250) + { + if (Map* map = bot->GetMap()) + { + if (map->GetEntry()->IsWorldMap()) + { + if (!HasRealPlayers(map)) + { + return 0; + } + + if (!map->IsGridLoaded(bot->GetPositionX(), bot->GetPositionY())) + { + return 0; + } + } + } + + return (mod * 1) / 10; + } + if (maxDiff > 200) return (mod * 3) / 10; + if (maxDiff > 150) return (mod * 5) / 10; + if (maxDiff > 100) return (mod * 6) / 10; + if (maxDiff > 75) return (mod * 9) / 10; + + return mod; } bool PlayerbotAI::IsOpposing(Player* player) { return IsOpposing(player->getRace(), bot->getRace()); } @@ -5438,29 +5421,15 @@ bool PlayerbotAI::CanMove() if (IsInVehicle() && !IsInVehicle(true)) return false; - if (bot->isFrozen() || - bot->IsPolymorphed() || - (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) || - bot->IsBeingTeleported() || - bot->isInRoots() || - bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) || - bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || - bot->IsCharmed() || - bot->HasAuraType(SPELL_AURA_MOD_STUN) || - bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || - bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) - + if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)) || + bot->IsBeingTeleported() || bot->isInRoots() || bot->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION) || + bot->HasAuraType(SPELL_AURA_MOD_CONFUSE) || bot->IsCharmed() || bot->HasAuraType(SPELL_AURA_MOD_STUN) || + bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || bot->HasUnitState(UNIT_STATE_LOST_CONTROL)) return false; return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE; } -bool PlayerbotAI::IsTaxiFlying() -{ - return bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && - bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING); -} - bool PlayerbotAI::IsInRealGuild() { if (!bot->GetGuildId()) diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index f7cf77ca..5477685e 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -241,30 +241,6 @@ enum class GuilderType : uint8 VERY_LARGE = 250 }; -enum class ActivePiorityType : uint8 -{ - IS_REAL_PLAYER = 0, - HAS_REAL_PLAYER_MASTER = 1, - IN_GROUP_WITH_REAL_PLAYER = 2, - IN_INSTANCE = 3, - IS_ALWAYS_ACTIVE = 4, - IN_COMBAT = 5, - IN_BG_QUEUE = 6, - IN_LFG = 7, - IN_REACT_DISTANCE = 8, - NEARBY_PLAYER_300 = 9, - NEARBY_PLAYER_600 = 10, - NEARBY_PLAYER_900 = 11, - PLAYER_FRIEND = 12, - PLAYER_GUILD = 13, - IN_VERY_ACTIVE_AREA = 14, - IN_ACTIVE_MAP = 15, - IN_NOT_ACTIVE_MAP = 16, - IN_EMPTY_SERVER = 17, - ALLOWED_PARTY_ACTIVITY = 18, - DEFAULT -}; - enum ActivityType { GRIND_ACTIVITY = 1, @@ -274,8 +250,8 @@ enum ActivityType PACKET_ACTIVITY = 5, DETAILED_MOVE_ACTIVITY = 6, PARTY_ACTIVITY = 7, - REACT_ACTIVITY = 8, - ALL_ACTIVITY = 9, + ALL_ACTIVITY = 8, + MAX_ACTIVITY_TYPE }; @@ -549,10 +525,9 @@ public: bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance); bool HasPlayerNearby(float range = sPlayerbotAIConfig->reactDistance); bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig->sightDistance); - ActivePiorityType GetPriorityType(ActivityType activityType); bool AllowActive(ActivityType activityType); bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false); - uint32 SmartScaleActivity(ActivePiorityType type, uint32 botActiveAlonePerc); + uint32 AutoScaleActivity(uint32 mod); // Check if player is safe to use. bool IsSafe(Player* player); @@ -579,7 +554,6 @@ public: void ResetJumpDestination() { jumpDestination = Position(); } bool CanMove(); - bool IsTaxiFlying(); bool IsInRealGuild(); static std::vector dispel_whitelist; bool EqualLowercaseName(std::string s1, std::string s2); @@ -606,6 +580,7 @@ private: void HandleCommands(); void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL); + bool _isBotInitializing = true; protected: Player* bot; diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 7bf16079..a56408d5 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -156,7 +156,7 @@ bool PlayerbotAIConfig::Initialize() randomBotAutologin = sConfigMgr->GetOption("AiPlayerbot.RandomBotAutologin", true); minRandomBots = sConfigMgr->GetOption("AiPlayerbot.MinRandomBots", 50); maxRandomBots = sConfigMgr->GetOption("AiPlayerbot.MaxRandomBots", 200); - randomBotUpdateInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotUpdateInterval", 1); + randomBotUpdateInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotUpdateInterval", 20); randomBotCountChangeMinInterval = sConfigMgr->GetOption("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE); randomBotCountChangeMaxInterval = @@ -465,7 +465,7 @@ bool PlayerbotAIConfig::Initialize() playerbotsXPrate = sConfigMgr->GetOption("AiPlayerbot.KillXPRate", 1); disableDeathKnightLogin = sConfigMgr->GetOption("AiPlayerbot.DisableDeathKnightLogin", 0); botActiveAlone = sConfigMgr->GetOption("AiPlayerbot.BotActiveAlone", 100); - botActiveAloneSmartScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScale", 1); + botActiveAloneSmartScale = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScale", 0); botActiveAloneSmartScaleWhenMinLevel = sConfigMgr->GetOption("AiPlayerbot.botActiveAloneSmartScaleWhenMinLevel", 1); botActiveAloneSmartScaleWhenMaxLevel = diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index eeacb85a..94b6b806 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -26,6 +26,7 @@ #include "RandomPlayerbotMgr.h" #include "ScriptMgr.h" #include "cs_playerbots.h" +#include "cmath" class PlayerbotsDatabaseScript : public DatabaseScript { @@ -96,6 +97,16 @@ public: { sPlayerbotsMgr->AddPlayerbotData(player, false); sRandomPlayerbotMgr->OnPlayerLogin(player); + + if (sPlayerbotAIConfig->enabled || sPlayerbotAIConfig->randomBotAutologin) + { + std::string roundedTime = + std::to_string(std::ceil((sPlayerbotAIConfig->maxRandomBots * 0.13 / 60) * 10) / 10.0); + roundedTime = roundedTime.substr(0, roundedTime.find('.') + 2); + + ChatHandler(player->GetSession()).SendSysMessage( + "Playerbots: bot initialization at server startup will require '" + roundedTime + "' minutes."); + } } } diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index ca9049ec..e2fa72f9 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -40,7 +40,6 @@ #include "Unit.h" #include "UpdateTime.h" #include "World.h" -#include "GameTime.h" void PrintStatsThread() { sRandomPlayerbotMgr->PrintStats(); } @@ -166,7 +165,6 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) } BattlegroundData.clear(); - BgCheckTimer = 0; LfgCheckTimer = 0; PlayersCheckTimer = 0; @@ -293,6 +291,15 @@ 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)) @@ -310,17 +317,16 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/) uint32 onlineBotFocus = 75; if (onlineBotCount < (uint32)(sPlayerbotAIConfig->minRandomBots * 90 / 100)) - { onlineBotFocus = 25; + + // only keep updating till initializing time has completed, + // which prevents unneeded expensive GameTime calls. + if (_isBotInitializing) + { + _isBotInitializing = GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.13; } - setBotInitializing( - //onlineBotCount < maxAllowedBotCount && <-- these fields are incorrect when using bot amount min/max are not equal. - GameTime::GetUptime().count() < sPlayerbotAIConfig->maxRandomBots * 0.12); - - // when server is balancing bots then boost (decrease value of) the nextCheckDelay till - // onlineBotCount reached the AllowedBotCount. - uint32 updateIntervalTurboBoost = isBotInitializing() ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; + uint32 updateIntervalTurboBoost = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; SetNextCheckDelay(updateIntervalTurboBoost * (onlineBotFocus + 25) * 10); PerformanceMonitorOperation* pmo = sPerformanceMonitor->start( @@ -404,6 +410,26 @@ 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"); @@ -993,7 +1019,6 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) if (isLogginIn) return false; - uint32 randomTime; if (!player) { @@ -1001,21 +1026,21 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) randomTime = urand(1, 2); SetEventValue(bot, "login", 1, randomTime); - uint32 updateIntervalTurboBoost = isBotInitializing() ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; - randomTime = urand(std::max(5, static_cast(updateIntervalTurboBoost * 0.5)), - std::max(12, static_cast(updateIntervalTurboBoost * 2))); + uint32 randomBotUpdateInterval = _isBotInitializing ? 1 : sPlayerbotAIConfig->randomBotUpdateInterval; + randomTime = urand(std::max(5, static_cast(randomBotUpdateInterval * 0.5)), + std::max(12, static_cast(randomBotUpdateInterval * 2))); SetEventValue(bot, "update", 1, randomTime); // do not randomize or teleport immediately after server start (prevent lagging) if (!GetEventValue(bot, "randomize")) { - randomTime = urand(3, std::max(4, static_cast(updateIntervalTurboBoost * 0.4))); + randomTime = urand(3, std::max(4, static_cast(randomBotUpdateInterval * 0.4))); ScheduleRandomize(bot, randomTime); } if (!GetEventValue(bot, "teleport")) { - randomTime = urand(std::max(7, static_cast(updateIntervalTurboBoost * 0.7)), - std::max(14, static_cast(updateIntervalTurboBoost * 1.4))); + randomTime = urand(std::max(7, static_cast(randomBotUpdateInterval * 0.7)), + std::max(14, static_cast(randomBotUpdateInterval * 1.4))); ScheduleTeleport(bot, randomTime); } @@ -1085,9 +1110,6 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot) bool RandomPlayerbotMgr::ProcessBot(Player* player) { - if (!player || !player->IsInWorld() || player->IsBeingTeleported() || player->GetSession()->isLogingOut()) - return false; - uint32 bot = player->GetGUID().GetCounter(); if (player->InBattleground()) @@ -1242,7 +1264,9 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector& if (botAI) { // ignore when in when taxi with boat/zeppelin and has players nearby - if (botAI->IsTaxiFlying() && botAI->HasPlayerNearby()) + if (bot->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && + bot->HasUnitState(UNIT_STATE_IGNORE_PATHFINDING) && + botAI->HasPlayerNearby()) return; } diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index 10405413..f7ebf9aa 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -103,7 +103,8 @@ public: void LogPlayerLocation(); void UpdateAIInternal(uint32 elapsed, bool minimal = false) override; -//private: +private: + //void ScaleBotActivity(); public: uint32 activeBots = 0; @@ -163,11 +164,11 @@ public: 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; } - bool isBotInitializing() const { return _isBotInitializing; } - void setBotInitializing(bool completed) { _isBotInitializing = completed; } - void PrepareAddclassCache(); std::map> addclassCache; protected: @@ -176,6 +177,7 @@ protected: private: // pid values are set in constructor botPID pid = botPID(1, 50, -50, 0, 0, 0); + float activityMod = 0.25; bool _isBotInitializing = true; uint32 GetEventValue(uint32 bot, std::string const event); std::string const GetEventData(uint32 bot, std::string const event); diff --git a/src/strategy/actions/LfgActions.cpp b/src/strategy/actions/LfgActions.cpp index 9a07f527..3e36086d 100644 --- a/src/strategy/actions/LfgActions.cpp +++ b/src/strategy/actions/LfgActions.cpp @@ -188,7 +188,7 @@ bool LfgAcceptAction::Execute(Event event) LOG_INFO("playerbots", "Bot {} {}:{} <{}> is in combat and refuses LFG proposal {}", bot->GetGUID().ToString().c_str(), bot->GetTeamId() == TEAM_ALLIANCE ? "A" : "H", bot->GetLevel(), bot->GetName().c_str(), id); - sLFGMgr->UpdateProposal(id, bot->GetGUID(), false); + sLFGMgr->UpdateProposal(id, bot->GetGUID(), true); return true; } diff --git a/src/strategy/actions/QuestAction.cpp b/src/strategy/actions/QuestAction.cpp index 139033f1..13ed7bba 100644 --- a/src/strategy/actions/QuestAction.cpp +++ b/src/strategy/actions/QuestAction.cpp @@ -15,9 +15,13 @@ bool QuestAction::Execute(Event event) { ObjectGuid guid = event.getObject(); - Player* master = GetMaster(); + // Checks if the bot and botAI are valid + if (!bot || !botAI) + return false; + + // Sets guid based on bot or master target if (!guid) { if (!master) @@ -36,19 +40,27 @@ bool QuestAction::Execute(Event event) } bool result = false; + + // Check the nearest NPCs GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); - for (const auto npc : npcs) + for (const auto& npc : npcs) { Unit* unit = botAI->GetUnit(npc); if (unit && bot->GetDistance(unit) <= INTERACTION_DISTANCE) + { result |= ProcessQuests(unit); + } } + + // Checks the nearest game objects std::list gos = AI_VALUE(std::list, "nearest game objects"); - for (const auto go : gos) + for (const auto& go : gos) { GameObject* gameobj = botAI->GetGameObject(go); if (gameobj && bot->GetDistance(gameobj) <= INTERACTION_DISTANCE) + { result |= ProcessQuests(gameobj); + } } return result; diff --git a/src/strategy/values/ItemUsageValue.cpp b/src/strategy/values/ItemUsageValue.cpp index 459340c3..c82a3e25 100644 --- a/src/strategy/values/ItemUsageValue.cpp +++ b/src/strategy/values/ItemUsageValue.cpp @@ -207,6 +207,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) uint8 possibleSlots = 1; uint8 dstSlot = botAI->FindEquipSlot(itemProto, NULL_SLOT, true); + if (dstSlot == EQUIPMENT_SLOT_FINGER1 || dstSlot == EQUIPMENT_SLOT_TRINKET1) { possibleSlots = 2;