From ad13013ea1b1ea640ee51f76a55427b4d6bcb506 Mon Sep 17 00:00:00 2001 From: Dustin Hendrickson Date: Fri, 28 Feb 2025 12:50:17 -0800 Subject: [PATCH 1/2] Adding new config varaible to control if the module should run --- conf/mod_player_bot_level_brackets.conf.dist | 18 +- src/mod-player-bot-level-brackets.cpp | 258 +++++++++++++------ 2 files changed, 191 insertions(+), 85 deletions(-) diff --git a/conf/mod_player_bot_level_brackets.conf.dist b/conf/mod_player_bot_level_brackets.conf.dist index 9f0c9e4..a99bd76 100644 --- a/conf/mod_player_bot_level_brackets.conf.dist +++ b/conf/mod_player_bot_level_brackets.conf.dist @@ -4,11 +4,23 @@ # mod-player-bot-level-brackets configuration ############################################## # -# BotLevelBrackets.DebugMode -# Description: Enables debug logging for the Bot Level Brackets module. +# BotLevelBrackets.Enabled +# Description: Enables the module. +# Default: 1 (enabled) +# Valid values: 0 (off) / 1 (on) +BotLevelBrackets.Enabled = 1 + +# BotLevelBrackets.FullDebugMode +# Description: Enables full debug logging for the Bot Level Brackets module. # Default: 0 (disabled) # Valid values: 0 (off) / 1 (on) -BotLevelBrackets.DebugMode = 0 +BotLevelBrackets.FullDebugMode = 0 + +# BotLevelBrackets.LiteDebugMode +# Description: Enables lite debug logging for the Bot Level Brackets module. +# Default: 0 (disabled) +# Valid values: 0 (off) / 1 (on) +BotLevelBrackets.LiteDebugMode = 0 # BotLevelBrackets.CheckFrequency # Description: The frequency (in seconds) at which the bot level distribution check is performed. diff --git a/src/mod-player-bot-level-brackets.cpp b/src/mod-player-bot-level-brackets.cpp index b1a159a..65d169b 100644 --- a/src/mod-player-bot-level-brackets.cpp +++ b/src/mod-player-bot-level-brackets.cpp @@ -37,6 +37,8 @@ static const uint8 NUM_RANGES = 9; static uint8 g_RandomBotMinLevel = 1; static uint8 g_RandomBotMaxLevel = 80; +// New configuration option to enable/disable the mod. Default is true. +static bool g_BotLevelBracketsEnabled = true; // Separate arrays for Alliance and Horde. static LevelRangeConfig g_BaseLevelRanges[NUM_RANGES]; @@ -45,7 +47,8 @@ static LevelRangeConfig g_HordeLevelRanges[NUM_RANGES]; static uint32 g_BotDistCheckFrequency = 300; // in seconds static uint32 g_BotDistFlaggedCheckFrequency = 15; // in seconds -static bool g_BotDistDebugMode = false; +static bool g_BotDistFullDebugMode = false; +static bool g_BotDistLiteDebugMode = false; static bool g_UseDynamicDistribution = false; // Real player weight to boost bracket contributions. @@ -54,7 +57,10 @@ static float g_RealPlayerWeight = 1.0f; // Loads the configuration from the config file. static void LoadBotLevelBracketsConfig() { - g_BotDistDebugMode = sConfigMgr->GetOption("BotLevelBrackets.DebugMode", false); + g_BotLevelBracketsEnabled = sConfigMgr->GetOption("BotLevelBrackets.Enabled", true); + + g_BotDistFullDebugMode = sConfigMgr->GetOption("BotLevelBrackets.FullDebugMode", false); + g_BotDistLiteDebugMode = sConfigMgr->GetOption("BotLevelBrackets.LiteDebugMode", false); g_BotDistCheckFrequency = sConfigMgr->GetOption("BotLevelBrackets.CheckFrequency", 300); g_BotDistFlaggedCheckFrequency = sConfigMgr->GetOption("BotLevelBrackets.CheckFlaggedFrequency", 15); g_UseDynamicDistribution = sConfigMgr->GetOption("BotLevelBrackets.UseDynamicDistribution", false); @@ -145,7 +151,7 @@ static void AdjustBotToRange(Player* bot, int targetRangeIndex, const LevelRange uint8 upperBound = factionRanges[targetRangeIndex].upper; if (upperBound < 55) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) { std::string playerFaction = IsAlliancePlayerBot(bot) ? "Alliance" : "Horde"; LOG_INFO("server.loading", @@ -167,7 +173,7 @@ static void AdjustBotToRange(Player* bot, int targetRangeIndex, const LevelRange newFactory.Randomize(false); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) { PlayerbotAI* botAI = sPlayerbotsMgr->GetPlayerbotAI(bot); std::string playerClassName = botAI ? botAI->GetChatHelper()->FormatClass(bot->getClass()) : "Unknown"; @@ -214,20 +220,23 @@ static bool IsHordePlayerBot(Player* bot) static void LogAllBotLevels() { - std::map botLevelCount; - for (auto const& itr : ObjectAccessor::GetPlayers()) + if (g_BotDistFullDebugMode) { - Player* player = itr.second; - if (!player || !player->IsInWorld()) - continue; - if (!IsPlayerBot(player)) - continue; - uint8 level = player->GetLevel(); - botLevelCount[level]++; - } - for (const auto& entry : botLevelCount) - { - LOG_INFO("server.loading", "[BotLevelBrackets] Level {}: {} bots", entry.first, entry.second); + std::map botLevelCount; + for (auto const& itr : ObjectAccessor::GetPlayers()) + { + Player* player = itr.second; + if (!player || !player->IsInWorld()) + continue; + if (!IsPlayerBot(player)) + continue; + uint8 level = player->GetLevel(); + botLevelCount[level]++; + } + for (const auto& entry : botLevelCount) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Level {}: {} bots", entry.first, entry.second); + } } } @@ -266,8 +275,11 @@ static void ClampAndBalanceBrackets() if(totalAlliance != 100 && totalAlliance > 0) { - LOG_INFO("server.loading", "[BotLevelBrackets] Alliance: Sum of percentages is {} (expected 100). Auto adjusting.", totalAlliance); - + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Alliance: Sum of percentages is {} (expected 100). Auto adjusting.", totalAlliance); + } + int missing = 100 - totalAlliance; while(missing > 0) { @@ -284,9 +296,11 @@ static void ClampAndBalanceBrackets() } if(totalHorde != 100 && totalHorde > 0) { - - LOG_INFO("server.loading", "[BotLevelBrackets] Horde: Sum of percentages is {} (expected 100). Auto adjusting.", totalHorde); - + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Horde: Sum of percentages is {} (expected 100). Auto adjusting.", totalHorde); + } + int missing = 100 - totalHorde; while(missing > 0) { @@ -310,32 +324,50 @@ static bool IsBotSafeForLevelReset(Player* bot) { if (!bot) { - LOG_INFO("server.loading", "[BotLevelBrackets] Null bot pointer provided."); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Null bot pointer provided."); + } return false; } if (!bot->IsInWorld()) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is not in world.", bot->GetName(), bot->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is not in world.", bot->GetName(), bot->GetLevel()); + } return false; } if (!bot->IsAlive()) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is not alive.", bot->GetName(), bot->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is not alive.", bot->GetName(), bot->GetLevel()); + } return false; } if (bot->IsInCombat()) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in combat.", bot->GetName(), bot->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in combat.", bot->GetName(), bot->GetLevel()); + } return false; } if (bot->InBattleground() || bot->InArena() || bot->inRandomLfgDungeon() || bot->InBattlegroundQueue()) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in battleground, arena, random dungeon, or battleground queue.", bot->GetName(), bot->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in battleground, arena, random dungeon, or battleground queue.", bot->GetName(), bot->GetLevel()); + } return false; } if (bot->IsInFlight()) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in flight.", bot->GetName(), bot->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) is in flight.", bot->GetName(), bot->GetLevel()); + } return false; } if (Group* group = bot->GetGroup()) @@ -345,7 +377,10 @@ static bool IsBotSafeForLevelReset(Player* bot) Player* member = ref->GetSource(); if (member && !IsPlayerBot(member)) { - LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) has non-bot group member {} (Level {}).", bot->GetName(), bot->GetLevel(), member->GetName(), member->GetLevel()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot {} (Level {}) has non-bot group member {} (Level {}).", bot->GetName(), bot->GetLevel(), member->GetName(), member->GetLevel()); + } return false; } } @@ -366,7 +401,10 @@ static std::vector g_PendingLevelResets; static void ProcessPendingLevelResets() { - LOG_INFO("server.loading", "[BotLevelBrackets] Processing {} pending resets...", g_PendingLevelResets.size()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Processing {} pending resets...", g_PendingLevelResets.size()); + } if (g_PendingLevelResets.empty()) return; @@ -379,8 +417,10 @@ static void ProcessPendingLevelResets() if (bot && bot->IsInWorld() && IsBotSafeForLevelReset(bot)) { AdjustBotToRange(bot, targetRange, it->factionRanges); - LOG_INFO("server.loading", "[BotLevelBrackets] Bot '{}' successfully reset to level range {}-{}.", - bot->GetName(), it->factionRanges[targetRange].lower, it->factionRanges[targetRange].upper); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Bot '{}' successfully reset to level range {}-{}.", bot->GetName(), it->factionRanges[targetRange].lower, it->factionRanges[targetRange].upper); + } it = g_PendingLevelResets.erase(it); } else @@ -460,7 +500,12 @@ public: void OnStartup() override { LoadBotLevelBracketsConfig(); - if (g_BotDistDebugMode) + if (!g_BotLevelBracketsEnabled) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Module disabled via configuration."); + return; + } + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Module loaded. Check frequency: {} seconds, Check flagged frequency: {}.", g_BotDistCheckFrequency, g_BotDistFlaggedCheckFrequency); for (uint8 i = 0; i < NUM_RANGES; ++i) @@ -478,13 +523,19 @@ public: void OnUpdate(uint32 diff) override { + if (!g_BotLevelBracketsEnabled) + return; + m_timer += diff; m_flaggedTimer += diff; // Process pending level resets. if (m_flaggedTimer >= g_BotDistFlaggedCheckFrequency * 1000) { - LOG_INFO("server.loading", "[BotLevelBrackets] Pending Level Resets Triggering."); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Pending Level Resets Triggering."); + } ProcessPendingLevelResets(); m_flaggedTimer = 0; } @@ -553,7 +604,7 @@ public: for (int i = 0; i < NUM_RANGES; ++i) { g_AllianceLevelRanges[i].desiredPercent = static_cast(round((allianceWeights[i] / allianceTotalWeight) * 100)); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Dynamic Distribution - Alliance Range {}: {}-{}, Real Players: {} (weight: {:.2f}), New Desired: {}%", i + 1, g_AllianceLevelRanges[i].lower, g_AllianceLevelRanges[i].upper, allianceRealCounts[i], allianceWeights[i], g_AllianceLevelRanges[i].desiredPercent); @@ -566,7 +617,7 @@ public: if (sumAlliance < 100 && allianceTotalWeight > 0) { uint8 missing = 100 - sumAlliance; - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Alliance normalization: current sum = {}, missing = {}", sumAlliance, missing); } @@ -582,7 +633,7 @@ public: } } } - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Alliance normalized percentages:"); for (int i = 0; i < NUM_RANGES; ++i) @@ -599,7 +650,7 @@ public: for (int i = 0; i < NUM_RANGES; ++i) { g_HordeLevelRanges[i].desiredPercent = static_cast(round((hordeWeights[i] / hordeTotalWeight) * 100)); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Dynamic Distribution - Horde Range {}: {}-{}, Real Players: {} (weight: {:.2f}), New Desired: {}%", i + 1, g_HordeLevelRanges[i].lower, g_HordeLevelRanges[i].upper, hordeRealCounts[i], hordeWeights[i], g_HordeLevelRanges[i].desiredPercent); @@ -612,7 +663,7 @@ public: if (sumHorde < 100 && hordeTotalWeight > 0) { uint8 missing = 100 - sumHorde; - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Horde normalization: current sum = {}, missing = {}", sumHorde, missing); } @@ -628,7 +679,7 @@ public: } } } - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Horde normalized percentages:"); for (int i = 0; i < NUM_RANGES; ++i) @@ -655,7 +706,7 @@ public: // Iterate only over player bots. auto const& allPlayers = ObjectAccessor::GetPlayers(); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Starting processing of {} players.", allPlayers.size()); for (auto const& itr : allPlayers) @@ -663,19 +714,19 @@ public: Player* player = itr.second; if (!player) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Skipping null player."); continue; } if (!player->IsInWorld()) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Skipping player '{}' as they are not in world.", player->GetName()); continue; } if (!IsPlayerBot(player) || !IsPlayerRandomBot(player)) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Skipping player '{}' as they are not a random bot.", player->GetName()); continue; } @@ -688,11 +739,11 @@ public: { allianceActualCounts[rangeIndex]++; allianceBotsByRange[rangeIndex].push_back(player); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' with level {} added to range {}.", player->GetName(), player->GetLevel(), rangeIndex + 1); } - else if (g_BotDistDebugMode) + else if (g_BotDistFullDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' with level {} does not fall into any defined range.", player->GetName(), player->GetLevel()); @@ -706,11 +757,11 @@ public: { hordeActualCounts[rangeIndex]++; hordeBotsByRange[rangeIndex].push_back(player); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' with level {} added to range {}.", player->GetName(), player->GetLevel(), rangeIndex + 1); } - else if (g_BotDistDebugMode) + else if (g_BotDistFullDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' with level {} does not fall into any defined range.", player->GetName(), player->GetLevel()); @@ -718,7 +769,7 @@ public: } } - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); LOG_INFO("server.loading", "[BotLevelBrackets] Total Alliance Bots: {}.", totalAllianceBots); @@ -729,23 +780,29 @@ public: // Process Alliance bots. if (totalAllianceBots > 0) { - LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + } int allianceDesiredCounts[NUM_RANGES] = {0}; for (int i = 0; i < NUM_RANGES; ++i) { allianceDesiredCounts[i] = static_cast(round((g_AllianceLevelRanges[i].desiredPercent / 100.0) * totalAllianceBots)); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Alliance Range {} ({}-{}): Desired = {}, Actual = {}.", i + 1, g_AllianceLevelRanges[i].lower, g_AllianceLevelRanges[i].upper, allianceDesiredCounts[i], allianceActualCounts[i]); } } - LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + } // Adjust overpopulated ranges. for (int i = 0; i < NUM_RANGES; ++i) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] >>> Processing Alliance bots in range {}.", i + 1); std::vector safeBots; @@ -755,7 +812,7 @@ public: if (IsBotSafeForLevelReset(bot)) { safeBots.push_back(bot); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) { //LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' is safe for level reset in range {}.", bot->GetName(), i + 1); } @@ -763,7 +820,7 @@ public: else { flaggedBots.push_back(bot); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' is NOT safe for level reset in range {}.", bot->GetName(), i + 1); } @@ -772,8 +829,10 @@ public: { Player* bot = safeBots.back(); safeBots.pop_back(); - LOG_INFO("server.loading", "[BotLevelBrackets] Alliance safe bot '{}' from range {} will be moved.", - bot->GetName(), i + 1); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Alliance safe bot '{}' from range {} will be moved.", bot->GetName(), i + 1); + } int targetRange = -1; if (bot->getClass() == CLASS_DEATH_KNIGHT) { @@ -799,11 +858,16 @@ public: } if (targetRange == -1) { - LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for alliance safe bot '{}'.", bot->GetName()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for alliance safe bot '{}'.", bot->GetName()); + } break; } - LOG_INFO("server.loading", "[BotLevelBrackets] !!!! Adjusting alliance bot '{}' from range {} to range {} ({}-{}).", - bot->GetName(), i + 1, targetRange + 1, g_AllianceLevelRanges[targetRange].lower, g_AllianceLevelRanges[targetRange].upper); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] !!!! Adjusting alliance bot '{}' from range {} to range {} ({}-{}).", bot->GetName(), i + 1, targetRange + 1, g_AllianceLevelRanges[targetRange].lower, g_AllianceLevelRanges[targetRange].upper); + } AdjustBotToRange(bot, targetRange, g_AllianceLevelRanges); allianceActualCounts[i]--; allianceActualCounts[targetRange]++; @@ -812,8 +876,11 @@ public: { Player* bot = flaggedBots.back(); flaggedBots.pop_back(); - LOG_INFO("server.loading", "[BotLevelBrackets] Alliance flagged bot '{}' from range {} will be processed for pending reset.", - bot->GetName(), i + 1); + + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Alliance flagged bot '{}' from range {} will be processed for pending reset.", bot->GetName(), i + 1); + } int targetRange = -1; if (bot->getClass() == CLASS_DEATH_KNIGHT) { @@ -839,7 +906,10 @@ public: } if (targetRange == -1) { - LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for flagged alliance bot '{}' for pending reset.", bot->GetName()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for flagged alliance bot '{}' for pending reset.", bot->GetName()); + } break; } bool alreadyFlagged = false; @@ -854,8 +924,10 @@ public: if (!alreadyFlagged) { g_PendingLevelResets.push_back({bot, targetRange, g_AllianceLevelRanges}); - LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' flagged for pending level reset to range {}-{}.", - bot->GetName(), g_AllianceLevelRanges[targetRange].lower, g_AllianceLevelRanges[targetRange].upper); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Alliance bot '{}' flagged for pending level reset to range {}-{}.", bot->GetName(), g_AllianceLevelRanges[targetRange].lower, g_AllianceLevelRanges[targetRange].upper); + } } } } @@ -864,22 +936,28 @@ public: // Process Horde bots. if (totalHordeBots > 0) { - LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + } int hordeDesiredCounts[NUM_RANGES] = {0}; for (int i = 0; i < NUM_RANGES; ++i) { hordeDesiredCounts[i] = static_cast(round((g_HordeLevelRanges[i].desiredPercent / 100.0) * totalHordeBots)); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] Horde Range {} ({}-{}): Desired = {}, Actual = {}.", i + 1, g_HordeLevelRanges[i].lower, g_HordeLevelRanges[i].upper, hordeDesiredCounts[i], hordeActualCounts[i]); } } - LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] ========================================="); + } for (int i = 0; i < NUM_RANGES; ++i) { - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Processing Horde bots in range {}.", i + 1); std::vector safeBots; @@ -889,7 +967,7 @@ public: if (IsBotSafeForLevelReset(bot)) { safeBots.push_back(bot); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) { //LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' is safe for level reset in range {}.", bot->GetName(), i + 1); } @@ -897,7 +975,7 @@ public: else { flaggedBots.push_back(bot); - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode) LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' is NOT safe for level reset in range {}.", bot->GetName(), i + 1); } @@ -906,8 +984,12 @@ public: { Player* bot = safeBots.back(); safeBots.pop_back(); - LOG_INFO("server.loading", "[BotLevelBrackets] Horde safe bot '{}' from range {} will be moved.", - bot->GetName(), i + 1); + + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Horde safe bot '{}' from range {} will be moved.", bot->GetName(), i + 1); + } + int targetRange = -1; if (bot->getClass() == CLASS_DEATH_KNIGHT) { @@ -933,11 +1015,16 @@ public: } if (targetRange == -1) { - LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for safe horde bot '{}'.", bot->GetName()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for safe horde bot '{}'.", bot->GetName()); + } break; } - LOG_INFO("server.loading", "[BotLevelBrackets] !!!! Adjusting horde bot '{}' from range {} to range {} ({}-{}).", - bot->GetName(), i + 1, targetRange + 1, g_HordeLevelRanges[targetRange].lower, g_HordeLevelRanges[targetRange].upper); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] !!!! Adjusting horde bot '{}' from range {} to range {} ({}-{}).", bot->GetName(), i + 1, targetRange + 1, g_HordeLevelRanges[targetRange].lower, g_HordeLevelRanges[targetRange].upper); + } AdjustBotToRange(bot, targetRange, g_HordeLevelRanges); hordeActualCounts[i]--; hordeActualCounts[targetRange]++; @@ -946,8 +1033,10 @@ public: { Player* bot = flaggedBots.back(); flaggedBots.pop_back(); - LOG_INFO("server.loading", "[BotLevelBrackets] Horde flagged bot '{}' from range {} will be processed for pending reset.", - bot->GetName(), i + 1); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Horde flagged bot '{}' from range {} will be processed for pending reset.", bot->GetName(), i + 1); + } int targetRange = -1; if (bot->getClass() == CLASS_DEATH_KNIGHT) { @@ -973,7 +1062,10 @@ public: } if (targetRange == -1) { - LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for flagged horde bot '{}' for pending reset.", bot->GetName()); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] No valid target range found for flagged horde bot '{}' for pending reset.", bot->GetName()); + } break; } bool alreadyFlagged = false; @@ -988,15 +1080,17 @@ public: if (!alreadyFlagged) { g_PendingLevelResets.push_back({bot, targetRange, g_HordeLevelRanges}); - LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' flagged for pending level reset to range {}-{}.", - bot->GetName(), g_HordeLevelRanges[targetRange].lower, g_HordeLevelRanges[targetRange].upper); + if (g_BotDistFullDebugMode) + { + LOG_INFO("server.loading", "[BotLevelBrackets] Horde bot '{}' flagged for pending level reset to range {}-{}.", bot->GetName(), g_HordeLevelRanges[targetRange].lower, g_HordeLevelRanges[targetRange].upper); + } } } } } - if (g_BotDistDebugMode) + if (g_BotDistFullDebugMode || g_BotDistLiteDebugMode) { LOG_INFO("server.loading", "[BotLevelBrackets] ========================================= COMPLETE"); LOG_INFO("server.loading", "[BotLevelBrackets] Distribution adjustment complete. Alliance bots: {}, Horde bots: {}.", From 958d848d55448447dc571662e004a7f8a7543d2d Mon Sep 17 00:00:00 2001 From: Dustin Hendrickson Date: Sat, 1 Mar 2025 11:15:17 -0800 Subject: [PATCH 2/2] Adding new conf to limit if Guild Bots with real players should be balanced or not, Readme cleanup --- README.md | 93 ++++++++++---------- conf/mod_player_bot_level_brackets.conf.dist | 6 ++ src/mod-player-bot-level-brackets.cpp | 40 ++++++++- 3 files changed, 92 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index bdb9f7a..f8b490a 100644 --- a/README.md +++ b/README.md @@ -8,35 +8,39 @@ Overview -------- -The Bot Level Brackets module for AzerothCore ensures an even spread of player bots across configurable level ranges (brackets). It periodically monitors bot levels and automatically adjusts them by transferring bots from overpopulated brackets to those with a deficit. During adjustments, bots will be run through the normal Playerbots Randomize function, clearing and restoring them based on their new level. Bots that are not immediately safe for level reset (for example, those in combat or engaged in other activities) are flagged for pending adjustment and processed later when they become safe. Additionally, Death Knight bots are safeguarded to never be assigned a level below 55. +The Bot Level Brackets module for AzerothCore ensures an even spread of player bots across configurable level ranges (brackets). It periodically monitors bot levels and automatically adjusts them by transferring bots from overpopulated brackets to those with a deficit. During adjustments, bots are run through the normal Playerbots Randomize function, clearing and restoring them based on their new level. Bots that are not immediately safe for level reset (for example, those in combat or engaged in other activities) are flagged for pending adjustment and processed later when they become safe. Additionally, Death Knight bots are safeguarded to never be assigned a level below 55. Features -------- - **Configurable Faction-Specific Level Brackets:** Define nine distinct level brackets for Alliance and Horde bots with configurable lower and upper bounds: - - 1-9 , 10-19 , 20-29 , 30-39 , 40-49 , 50-59 , 60-69 , 70-79 , 80 + - 1-9, 10-19, 20-29, 30-39, 40-49, 50-59, 60-69, 70-79, 80 - **Desired Percentage Distribution:** - Target percentages can be set for the number of bots within each level bracket. The sum of percentages for each faction must equal 100. If it does not, the system will try to balance out the remaining percentages itself. + Target percentages can be set for the number of bots within each level bracket. The sum of percentages for each faction must equal 100. If it does not, the system will balance out the remaining percentages automatically. - **Dynamic Bot Adjustment:** Bots in overpopulated brackets are automatically adjusted to a random level within a bracket with a deficit. Adjustments include resetting XP, removing equipped items, trade skills, learned spells, quests, and active auras, and dismissing pets. - **Death Knight Level Safeguard:** - Bots of the Death Knight class are enforced a minimum level of 55, ensuring they are only assigned to higher brackets. + Death Knight bots are enforced a minimum level of 55, ensuring they are only assigned to higher brackets. - **Support for Random Bots:** - The module applies exclusively to bots managed by RandomPlayerbotMgr. + The module applies exclusively to bots managed by the RandomPlayerbotMgr. - **Dynamic Distribution Toggle:** Enable or disable the dynamic recalculation of bot distribution percentages based on the number of non-bot players in each level bracket via the `BotLevelBrackets.UseDynamicDistribution` option. - + - **Dynamic Real Player Weighting with Inverse Scaling:** When dynamic distribution is enabled, the module uses a configurable weight multiplier (set via `BotLevelBrackets.RealPlayerWeight`) to boost each real player's contribution to the desired distribution. This weight is further scaled inversely by the total number of real players online, ensuring that when few players are active, each player's impact on the bot distribution is significantly increased. **Note:** The `RealPlayerWeight` option only takes effect when `BotLevelBrackets.UseDynamicDistribution` is enabled. +- **Guild Bot Exclusion:** + When enabled via the new configuration option `BotLevelBrackets.IgnoreGuildBotsWithRealPlayers` (default enabled), bots that are in a guild with at least one real (non-bot) player online are excluded from bot bracket calculations. These bots are not counted in the totals, nor are they subject to level changes or flagged for pending reset. + > **NOTE:** At this time Guild Bot Exclusion only works with **online** real players. I'm investigating how to accomplish the same thing even if the real player is offline. + - **Debug Mode:** - An optional debug mode provides detailed logging for monitoring bot adjustments and troubleshooting module operations. + Optional debug modes (full and lite) provide detailed logging for monitoring bot adjustments and troubleshooting module operations. ### Minimum and Maximum Bot Level Support @@ -50,13 +54,6 @@ This module now supports setting a minimum and maximum level for random bots via > **Warning:** If you configure the maximum bot level to a value below 55, ensure that Death Knight bots are disabled. The module enforces a minimum level of 55 for Death Knight bots; therefore, setting the maximum level under 55 would conflict with this safeguard and could lead to unintended behavior and Death Knight bots not moving brackets. -### Dynamic Real Player Weighting and Scaling - -When dynamic distribution is enabled (`BotLevelBrackets.UseDynamicDistribution`), the module recalculates the desired bot percentages for each level bracket based on the number of non-bot players present. - -The total weight across all brackets is computed, and each bracket’s desired percentage is derived from its share of that total. This ensures that while more players in a bracket increase its weight, each additional player contributes progressively less. - - Installation ------------ 1. **Clone the Module** @@ -89,56 +86,60 @@ Customize the module’s behavior by editing the `mod_player_bot_level_brackets. ### Global Settings -Setting | Description | Default | Valid Values -------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|---------|-------------------- -BotLevelBrackets.DebugMode | Enables detailed debug logging for module operations. | 0 | 0 (off) / 1 (on) -BotLevelBrackets.CheckFrequency | Frequency (in seconds) for performing the bot bracket distribution check. | 300 | Positive Integer -BotLevelBrackets.CheckFlaggedFrequency | Frequency (in seconds) at which the bot level reset is performed for flagged bots that failed safety checks initially. | 15 | Positive Integer -BotLevelBrackets.RealPlayerWeight | Multiplier applied to each real player's contribution in their level bracket. This value is further scaled inversely by the total number of real players online. **Only active if dynamic distribution is enabled.** | 1.0 | Floating point number -BotLevelBrackets.UseDynamicDistribution | Enables dynamic recalculation of bot distribution percentages based on the number of non-bot players in each bracket. | 0 | 0 (off) / 1 (on) +Setting | Description | Default | Valid Values +---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|---------|-------------------- +BotLevelBrackets.Enabled | Enables the module. | 1 | 0 (off) / 1 (on) +BotLevelBrackets.FullDebugMode | Enables full debug logging for the Bot Level Brackets module. | 0 | 0 (off) / 1 (on) +BotLevelBrackets.LiteDebugMode | Enables lite debug logging for the Bot Level Brackets module. | 0 | 0 (off) / 1 (on) +BotLevelBrackets.CheckFrequency | Frequency (in seconds) at which the bot level distribution check is performed. | 300 | Positive Integer +BotLevelBrackets.CheckFlaggedFrequency | Frequency (in seconds) at which the bot level reset is performed for flagged bots that initially failed safety checks. | 15 | Positive Integer +BotLevelBrackets.UseDynamicDistribution | Enables dynamic recalculation of bot distribution percentages based on the number of non-bot players present in each bracket. | 0 | 0 (off) / 1 (on) +BotLevelBrackets.RealPlayerWeight | Multiplier applied to each real player's contribution in their level bracket. **Active only if dynamic distribution is enabled.** | 1.0 | Floating point number +**BotLevelBrackets.IgnoreGuildBotsWithRealPlayers** | When enabled, bots in a guild with at least one real (non-bot) player online are excluded from bot bracket calculations and will not be level changed or flagged. | 1 | 0 (disabled) / 1 (enabled) ### Alliance Level Brackets Configuration *The percentages below must sum to 100.* -Setting | Description | Default | Valid Values --------------------------------------------|---------------------------------------------------------------|---------|-------------------- -BotLevelBrackets.Alliance.Range1Pct | Desired percentage of Alliance bots within level range 1-9. | 12 | 0-100 -BotLevelBrackets.Alliance.Range2Pct | Desired percentage of Alliance bots within level range 10-19. | 11 | 0-100 -BotLevelBrackets.Alliance.Range3Pct | Desired percentage of Alliance bots within level range 20-29. | 11 | 0-100 -BotLevelBrackets.Alliance.Range4Pct | Desired percentage of Alliance bots within level range 30-39. | 11 | 0-100 -BotLevelBrackets.Alliance.Range5Pct | Desired percentage of Alliance bots within level range 40-49. | 11 | 0-100 -BotLevelBrackets.Alliance.Range6Pct | Desired percentage of Alliance bots within level range 50-59. | 11 | 0-100 -BotLevelBrackets.Alliance.Range7Pct | Desired percentage of Alliance bots within level range 60-69. | 11 | 0-100 -BotLevelBrackets.Alliance.Range8Pct | Desired percentage of Alliance bots within level range 70-79. | 11 | 0-100 -BotLevelBrackets.Alliance.Range9Pct | Desired percentage of Alliance bots at level 80. | 11 | 0-100 +Setting | Description | Default | Valid Values +--------------------------------------------|---------------------------------------------------------------|---------|-------------------- +BotLevelBrackets.Alliance.Range1Pct | Desired percentage of Alliance bots within level range 1-9. | 12 | 0-100 +BotLevelBrackets.Alliance.Range2Pct | Desired percentage of Alliance bots within level range 10-19. | 11 | 0-100 +BotLevelBrackets.Alliance.Range3Pct | Desired percentage of Alliance bots within level range 20-29. | 11 | 0-100 +BotLevelBrackets.Alliance.Range4Pct | Desired percentage of Alliance bots within level range 30-39. | 11 | 0-100 +BotLevelBrackets.Alliance.Range5Pct | Desired percentage of Alliance bots within level range 40-49. | 11 | 0-100 +BotLevelBrackets.Alliance.Range6Pct | Desired percentage of Alliance bots within level range 50-59. | 11 | 0-100 +BotLevelBrackets.Alliance.Range7Pct | Desired percentage of Alliance bots within level range 60-69. | 11 | 0-100 +BotLevelBrackets.Alliance.Range8Pct | Desired percentage of Alliance bots within level range 70-79. | 11 | 0-100 +BotLevelBrackets.Alliance.Range9Pct | Desired percentage of Alliance bots at level 80. | 11 | 0-100 ### Horde Level Brackets Configuration *The percentages below must sum to 100.* Setting | Description | Default | Valid Values -------------------------------------------|---------------------------------------------------------------|---------|-------------------- -BotLevelBrackets.Horde.Range1Pct | Desired percentage of Horde bots within level range 1-9. | 12 | 0-100 -BotLevelBrackets.Horde.Range2Pct | Desired percentage of Horde bots within level range 10-19. | 11 | 0-100 -BotLevelBrackets.Horde.Range3Pct | Desired percentage of Horde bots within level range 20-29. | 11 | 0-100 -BotLevelBrackets.Horde.Range4Pct | Desired percentage of Horde bots within level range 30-39. | 11 | 0-100 -BotLevelBrackets.Horde.Range5Pct | Desired percentage of Horde bots within level range 40-49. | 11 | 0-100 -BotLevelBrackets.Horde.Range6Pct | Desired percentage of Horde bots within level range 50-59. | 11 | 0-100 -BotLevelBrackets.Horde.Range7Pct | Desired percentage of Horde bots within level range 60-69. | 11 | 0-100 -BotLevelBrackets.Horde.Range8Pct | Desired percentage of Horde bots within level range 70-79. | 11 | 0-100 -BotLevelBrackets.Horde.Range9Pct | Desired percentage of Horde bots at level 80. | 11 | 0-100 +BotLevelBrackets.Horde.Range1Pct | Desired percentage of Horde bots within level range 1-9. | 12 | 0-100 +BotLevelBrackets.Horde.Range2Pct | Desired percentage of Horde bots within level range 10-19. | 11 | 0-100 +BotLevelBrackets.Horde.Range3Pct | Desired percentage of Horde bots within level range 20-29. | 11 | 0-100 +BotLevelBrackets.Horde.Range4Pct | Desired percentage of Horde bots within level range 30-39. | 11 | 0-100 +BotLevelBrackets.Horde.Range5Pct | Desired percentage of Horde bots within level range 40-49. | 11 | 0-100 +BotLevelBrackets.Horde.Range6Pct | Desired percentage of Horde bots within level range 50-59. | 11 | 0-100 +BotLevelBrackets.Horde.Range7Pct | Desired percentage of Horde bots within level range 60-69. | 11 | 0-100 +BotLevelBrackets.Horde.Range8Pct | Desired percentage of Horde bots within level range 70-79. | 11 | 0-100 +BotLevelBrackets.Horde.Range9Pct | Desired percentage of Horde bots at level 80. | 11 | 0-100 Debugging --------- To enable detailed debug logging, update the configuration file: - BotLevelBrackets.DebugMode = 1 - -This setting outputs logs detailing bot level adjustments, percentages and distribution to the server console. + BotLevelBrackets.FullDebugMode = 1 + BotLevelBrackets.LiteDebugMode = 1 +Choose one of these debug modes to output logs detailing bot level adjustments, percentages, and distribution to the server console. Troubleshooting ---------- -> **Bots are not randomizing their levels within the ranges brackets.** Make sure in your `playerbots.conf` that `AiPlayerbot.DisableRandomLevels` = false. Otherwise bots will be all reset to the set level in your playerbots config. +--------------- +> **Bots are not randomizing their levels within the range brackets.** +> Ensure that in your `playerbots.conf` the option `AiPlayerbot.DisableRandomLevels` is set to false. Otherwise, bots will be reset to the fixed level specified in your Playerbots configuration. License ------- diff --git a/conf/mod_player_bot_level_brackets.conf.dist b/conf/mod_player_bot_level_brackets.conf.dist index a99bd76..9d2d7e2 100644 --- a/conf/mod_player_bot_level_brackets.conf.dist +++ b/conf/mod_player_bot_level_brackets.conf.dist @@ -32,6 +32,12 @@ BotLevelBrackets.CheckFrequency = 300 # Default: 15 BotLevelBrackets.CheckFlaggedFrequency = 15 +# BotLevelBrackets.IgnoreGuildBotsWithRealPlayers +# Description: When enabled, bots that are in a guild with at least one real (non-bot) player online are excluded from bot bracket calculations and will not be level changed or flagged. +# Default: 1 (enabled) +# Valid values: 0 (disabled) / 1 (enabled) +BotLevelBrackets.IgnoreGuildBotsWithRealPlayers = 1 + # BotLevelBrackets.UseDynamicDistribution # Description: Enables dynamic recalculation of bot distribution percentages based on the number of non-bot players present in each level bracket. # Default: 0 (disabled) diff --git a/src/mod-player-bot-level-brackets.cpp b/src/mod-player-bot-level-brackets.cpp index 65d169b..cedb73d 100644 --- a/src/mod-player-bot-level-brackets.cpp +++ b/src/mod-player-bot-level-brackets.cpp @@ -39,6 +39,8 @@ static uint8 g_RandomBotMaxLevel = 80; // New configuration option to enable/disable the mod. Default is true. static bool g_BotLevelBracketsEnabled = true; +// New configuration option to ignore bots in guilds with a real player online. Default is true. +static bool g_BotLevelBracketsIgnoreGuildBotsWithRealPlayers = true; // Separate arrays for Alliance and Horde. static LevelRangeConfig g_BaseLevelRanges[NUM_RANGES]; @@ -54,10 +56,14 @@ static bool g_UseDynamicDistribution = false; // Real player weight to boost bracket contributions. static float g_RealPlayerWeight = 1.0f; +// ----------------------------------------------------------------------------- // Loads the configuration from the config file. +// ----------------------------------------------------------------------------- static void LoadBotLevelBracketsConfig() { g_BotLevelBracketsEnabled = sConfigMgr->GetOption("BotLevelBrackets.Enabled", true); + // Load the new option to ignore guild bots with a real player online. + g_BotLevelBracketsIgnoreGuildBotsWithRealPlayers = sConfigMgr->GetOption("BotLevelBrackets.IgnoreGuildBotsWithRealPlayers", true); g_BotDistFullDebugMode = sConfigMgr->GetOption("BotLevelBrackets.FullDebugMode", false); g_BotDistLiteDebugMode = sConfigMgr->GetOption("BotLevelBrackets.LiteDebugMode", false); @@ -240,6 +246,30 @@ static void LogAllBotLevels() } } +// ----------------------------------------------------------------------------- +// HELPER FUNCTION: Check if a bot is in a guild with at least one real player online. +// ----------------------------------------------------------------------------- +static bool BotInGuildWithRealPlayer(Player* bot) +{ + if (!bot) + return false; + // If the bot is not in a guild, nothing to do. + uint32 guildId = bot->GetGuildId(); + if (guildId == 0) + return false; + + // Iterate over all players and check for an online, non-bot member in the same guild. + for (auto const& itr : ObjectAccessor::GetPlayers()) + { + Player* member = itr.second; + if (!member || !member->IsInWorld()) + continue; + if (!IsPlayerBot(member) && member->GetGuildId() == guildId) + return true; + } + return false; +} + static void ClampAndBalanceBrackets() { // First, adjust Alliance brackets. @@ -413,7 +443,12 @@ static void ProcessPendingLevelResets() { Player* bot = it->bot; int targetRange = it->targetRange; - + // If the bot is in a guild with a real player online and the option is enabled, remove it from pending resets. + if (g_BotLevelBracketsIgnoreGuildBotsWithRealPlayers && BotInGuildWithRealPlayer(bot)) + { + it = g_PendingLevelResets.erase(it); + continue; + } if (bot && bot->IsInWorld() && IsBotSafeForLevelReset(bot)) { AdjustBotToRange(bot, targetRange, it->factionRanges); @@ -730,6 +765,9 @@ public: LOG_INFO("server.loading", "[BotLevelBrackets] Skipping player '{}' as they are not a random bot.", player->GetName()); continue; } + // If the bot is in a guild with a real player online and the option is enabled, skip it. + if (g_BotLevelBracketsIgnoreGuildBotsWithRealPlayers && BotInGuildWithRealPlayer(player)) + continue; if (IsAlliancePlayerBot(player)) {