diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 57f5548d..c6c0dfe1 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -80,6 +80,7 @@ AiPlayerbot.Enabled = 1 AiPlayerbot.RandomBotAutologin = 1 # Random bot account +# Please ensure that RandomBotAccountCount is greater than (MaxRandomBots / 10 + AddClassAccountPoolSize) AiPlayerbot.RandomBotAccountCount = 200 # Random bot count @@ -108,12 +109,16 @@ AiPlayerbot.DeleteRandomBotAccounts = 0 AiPlayerbot.MaxAddedBots = 40 # Maximum number of bots per class added by one account -AiPlayerbot.MaxAddedBotsPerClass = 10 +AiPlayerbot.MaxAddedBotsPerClass = 40 # Enable/Disable create bot by addclass command (0 = GM only, 1 = enable) # default: 1 (enable) AiPlayerbot.AddClassCommand = 1 +# Set the addclass command account pool size +# Addclass command uses a subset of accounts from RandomBotAccountCount +AiPlayerbot.AddClassAccountPoolSize = 50 + # Bot group invitation permission level (0 = GM only, 1 = accept based on level, 2 = always accept) # default: 1 (accept based on level) AiPlayerbot.GroupInvitationPermission = 1 diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index 46f884f8..1958f9aa 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -18,12 +18,14 @@ #include "Group.h" #include "GroupMgr.h" #include "ObjectAccessor.h" +#include "ObjectGuid.h" #include "ObjectMgr.h" #include "PlayerbotAIConfig.h" #include "PlayerbotDbStore.h" #include "PlayerbotFactory.h" #include "PlayerbotSecurity.h" #include "Playerbots.h" +#include "RandomPlayerbotMgr.h" #include "SharedDefines.h" #include "WorldSession.h" #include "ChannelMgr.h" @@ -1003,44 +1005,20 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg messages.push_back("Error: Invalid Class. Try again."); return messages; } - uint8 master_race = master->getRace(); - std::string race_limit; - switch (master_race) + uint8 teamId = master->GetTeamId(true); + std::vector &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; + for (size_t i = 0; i < guidCache.size(); i++) { - case 1: - case 3: - case 4: - case 7: - case 11: - race_limit = "1, 3, 4, 7, 11"; - break; - case 2: - case 5: - case 6: - case 8: - case 10: - race_limit = "2, 5, 6, 8, 10"; - break; - } - uint32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back(); - // find a bot fit conditions and not in any guild - QueryResult results = CharacterDatabase.Query( - "SELECT guid FROM characters " - "WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) AND " - "guid NOT IN ( SELECT guid FROM guild_member ) " - "AND account <= {} " - "ORDER BY account DESC LIMIT 1", - claz, race_limit, maxAccountId); - if (results) - { - Field* fields = results->Fetch(); - ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get()); + ObjectGuid guid = guidCache[i]; + if (botLoading.find(guid) != botLoading.end()) + continue; + if (ObjectAccessor::FindConnectedPlayer(guid)) + continue; AddPlayerBot(guid, master->GetSession()->GetAccountId()); - messages.push_back("Add class " + std::string(charname)); return messages; } - messages.push_back("Add class failed."); + messages.push_back("Add class failed, no available characters!"); return messages; } diff --git a/src/RandomPlayerbotMgr.cpp b/src/RandomPlayerbotMgr.cpp index 80f090ce..10f2bfe5 100644 --- a/src/RandomPlayerbotMgr.cpp +++ b/src/RandomPlayerbotMgr.cpp @@ -162,6 +162,8 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0) { sPlayerbotCommandServer->Start(); PrepareTeleportCache(); + if (sPlayerbotAIConfig->addClassCommand) + PrepareAddclassCache(); } BattlegroundData.clear(); @@ -1473,40 +1475,44 @@ void RandomPlayerbotMgr::PrepareTeleportCache() } while (results->NextRow()); } LOG_INFO("playerbots", "{} banker locations for level collected.", collected_locs); +} - // temporary only use locsPerLevelCache, so disable rpgLocsCacheLevel cache - - // LOG_INFO("playerbots", "Preparing RPG teleport caches for {} factions...", sFactionTemplateStore.GetNumRows()); - // results = WorldDatabase.Query("SELECT map, position_x, position_y, position_z, r.race, r.minl, r.maxl FROM - // creature c INNER JOIN playerbots_rpg_races r ON c.id1 = r.entry " - // "WHERE r.race < 15"); - // if (results) - // { - // do - // { - // Field* fields = results->Fetch(); - // uint16 mapId = fields[0].Get(); - // float x = fields[1].Get(); - // float y = fields[2].Get(); - // float z = fields[3].Get(); - // uint32 race = fields[4].Get(); - // uint32 minl = fields[5].Get(); - // uint32 maxl = fields[6].Get(); - - // for (uint32 level = 1; level < sPlayerbotAIConfig->randomBotMaxLevel + 1; level++) - // { - // if (level > maxl || level < minl) - // continue; - - // WorldLocation loc(mapId, x, y, z, 0); - // for (uint32 r = 1; r < MAX_RACES; r++) - // { - // if (race == r || race == 0) - // rpgLocsCacheLevel[r][level].push_back(loc); - // } - // } - // } while (results->NextRow()); - // } +void RandomPlayerbotMgr::PrepareAddclassCache() +{ + int32 maxAccountId = sPlayerbotAIConfig->randomBotAccounts.back(); + int32 minIdx = + sPlayerbotAIConfig->randomBotAccounts.size() - 1 >= sPlayerbotAIConfig->addClassAccountPoolSize + ? sPlayerbotAIConfig->randomBotAccounts.size() - sPlayerbotAIConfig->addClassAccountPoolSize : 0; + int32 minAccountId = sPlayerbotAIConfig->randomBotAccounts[minIdx]; + if (minAccountId < 0) + { + LOG_ERROR("playerbots", "No available account for add class!"); + } + int32 collected = 0; + for (uint8 claz = CLASS_WARRIOR; claz <= CLASS_DRUID; claz++) + { + if (claz == 10) + continue; + QueryResult results = CharacterDatabase.Query( + "SELECT guid, race FROM characters " + "WHERE account >= {} AND account <= {} AND class = '{}' AND online = 0 AND " + "guid NOT IN ( SELECT guid FROM guild_member ) " + "ORDER BY account DESC", + minAccountId, maxAccountId, claz); + if (results) + { + do + { + Field* fields = results->Fetch(); + ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get()); + uint32 race = fields[1].Get(); + bool isAlliance = race == 1 || race == 3 || race == 4 || race == 7 || race == 11; + addclassCache[GetTeamClassIdx(isAlliance, claz)].push_back(guid); + collected++; + } while (results->NextRow()); + } + } + LOG_INFO("playerbots", "{} characters collected for addclass command.", collected); } void RandomPlayerbotMgr::RandomTeleportForLevel(Player* bot) @@ -2395,7 +2401,7 @@ void RandomPlayerbotMgr::PrintStats() ++engine_combat; else ++engine_dead; - + uint8 spec = AiFactory::GetPlayerSpecTab(bot); switch (bot->getClass()) { @@ -2779,4 +2785,3 @@ ObjectGuid const RandomPlayerbotMgr::GetBattleMasterGUID(Player* bot, Battlegrou return battleMasterGUID; } - diff --git a/src/RandomPlayerbotMgr.h b/src/RandomPlayerbotMgr.h index f1e80ab6..2105477c 100644 --- a/src/RandomPlayerbotMgr.h +++ b/src/RandomPlayerbotMgr.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H #define _PLAYERBOT_RANDOMPLAYERBOTMGR_H +#include "ObjectGuid.h" #include "PlayerbotMgr.h" struct BattlegroundInfo @@ -166,7 +167,9 @@ public: 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; } + std::map> addclassCache; protected: void OnBotLoginInternal(Player* const bot) override; @@ -190,6 +193,7 @@ private: void RandomTeleport(Player* bot, std::vector& locs, bool hearth = false); uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ); void PrepareTeleportCache(); + void PrepareAddclassCache(); typedef void (RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*); std::vector players;