Merge pull request #1426 from NoxMax/delete-all-the-orphans

Fix: DeleteRandomBotAccounts sometimes leaves orphans. Also fix accumulating orphan pet data in DB
This commit is contained in:
kadeshar
2025-07-06 09:51:31 +02:00
committed by GitHub
2 changed files with 46 additions and 20 deletions

View File

@@ -486,6 +486,13 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')", CharacterDatabase.Execute("DELETE FROM characters WHERE account IN (SELECT id FROM " + loginDBName + ".account WHERE username LIKE '{}%%')",
sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
// Wait for the characters to be deleted before proceeding to dependent deletes
while (CharacterDatabase.QueueSize())
{
std::this_thread::sleep_for(1s);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Clean up orphaned entries in playerbots_guild_tasks // Clean up orphaned entries in playerbots_guild_tasks
PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)"); PlayerbotsDatabase.Execute("DELETE FROM playerbots_guild_tasks WHERE owner NOT IN (SELECT guid FROM " + characterDBName + ".characters)");
@@ -500,11 +507,13 @@ void RandomPlayerbotFactory::CreateRandomBots()
CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_achievement WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_achievement_progress WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_action WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_arena_stats WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_aura WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_entry_point WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_glyphs WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_homebind WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_inventory WHERE guid NOT IN (SELECT guid FROM characters)");
CharacterDatabase.Execute("DELETE FROM item_instance WHERE owner_guid NOT IN (SELECT guid FROM characters) AND owner_guid > 0");
// Clean up pet data // Clean up pet data
CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)"); CharacterDatabase.Execute("DELETE FROM character_pet WHERE owner NOT IN (SELECT guid FROM characters)");
@@ -559,11 +568,23 @@ void RandomPlayerbotFactory::CreateRandomBots()
uint32 timer = getMSTime(); uint32 timer = getMSTime();
// After ALL deletions, make sure data is commited to DB
LoginDatabase.Execute("COMMIT");
CharacterDatabase.Execute("COMMIT");
PlayerbotsDatabase.Execute("COMMIT");
// Wait for all pending database operations to complete // Wait for all pending database operations to complete
while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize()) while (LoginDatabase.QueueSize() || CharacterDatabase.QueueSize() || PlayerbotsDatabase.QueueSize())
{ {
std::this_thread::sleep_for(1s); std::this_thread::sleep_for(1s);
} }
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Extra 100ms fixed delay for safety.
// Flush tables to ensure all data in memory are written to disk
LoginDatabase.Execute("FLUSH TABLES");
CharacterDatabase.Execute("FLUSH TABLES");
PlayerbotsDatabase.Execute("FLUSH TABLES");
LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer)); LOG_INFO("playerbots", ">> Random bot accounts and data deleted in {} ms", GetMSTimeDiffToNow(timer));
LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server..."); LOG_INFO("playerbots", "Please reset the AiPlayerbot.DeleteRandomBotAccounts to 0 and restart the server...");
World::StopNow(SHUTDOWN_EXIT_CODE); World::StopNow(SHUTDOWN_EXIT_CODE);

View File

@@ -181,6 +181,11 @@ RandomPlayerbotMgr::RandomPlayerbotMgr() : PlayerbotHolder(), processTicks(0)
BattlegroundData.clear(); // Clear here and here only. BattlegroundData.clear(); // Clear here and here only.
// Cleanup on server start: orphaned pet data that's often left behind by bot pets that no longer exist in the DB
CharacterDatabase.Execute("DELETE FROM pet_aura WHERE guid NOT IN (SELECT id FROM character_pet)");
CharacterDatabase.Execute("DELETE FROM pet_spell WHERE guid NOT IN (SELECT id FROM character_pet)");
CharacterDatabase.Execute("DELETE FROM pet_spell_cooldown WHERE guid NOT IN (SELECT id FROM character_pet)");
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{ {
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType) for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
@@ -369,7 +374,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay; DelayLoginBotsTimer = time(nullptr) + sPlayerbotAIConfig->disabledWithoutRealPlayerLoginDelay;
} }
} }
else else
{ {
if (DelayLoginBotsTimer) if (DelayLoginBotsTimer)
{ {
@@ -515,7 +520,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
{ {
maxAllowedBotCount -= currentBots.size(); maxAllowedBotCount -= currentBots.size();
maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount); maxAllowedBotCount = std::min(sPlayerbotAIConfig->randomBotsPerInterval, maxAllowedBotCount);
uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio; uint32 totalRatio = sPlayerbotAIConfig->randomBotAllianceRatio + sPlayerbotAIConfig->randomBotHordeRatio;
uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio; uint32 allowedAllianceCount = maxAllowedBotCount * (sPlayerbotAIConfig->randomBotAllianceRatio) / totalRatio;
@@ -536,7 +541,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
{ {
// minus addclass bots account // minus addclass bots account
int32 baseAccount = RandomPlayerbotFactory::CalculateTotalAccountCount() - sPlayerbotAIConfig->addClassAccountPoolSize; int32 baseAccount = RandomPlayerbotFactory::CalculateTotalAccountCount() - sPlayerbotAIConfig->addClassAccountPoolSize;
if (baseAccount <= 0 || baseAccount > sPlayerbotAIConfig->randomBotAccounts.size()) if (baseAccount <= 0 || baseAccount > sPlayerbotAIConfig->randomBotAccounts.size())
{ {
LOG_ERROR("playerbots", "Account calculation error with PeriodicOnlineOffline"); LOG_ERROR("playerbots", "Account calculation error with PeriodicOnlineOffline");
@@ -551,7 +556,7 @@ uint32 RandomPlayerbotMgr::AddRandomBots()
PreparedQueryResult result = CharacterDatabase.Query(stmt); PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result) if (!result)
continue; continue;
std::vector<GuidClassRaceInfo> allGuidInfos; std::vector<GuidClassRaceInfo> allGuidInfos;
do { do {
@@ -719,7 +724,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
LOG_DEBUG("playerbots", "Checking BG Queue..."); LOG_DEBUG("playerbots", "Checking BG Queue...");
// Initialize Battleground Data (do not clear here) // Initialize Battleground Data (do not clear here)
for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket) for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{ {
for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType) for (int queueType = BATTLEGROUND_QUEUE_AV; queueType < MAX_BATTLEGROUND_QUEUE_TYPES; ++queueType)
@@ -793,11 +798,11 @@ void RandomPlayerbotMgr::CheckBgQueue()
{ {
std::vector<uint32>* instanceIds = nullptr; std::vector<uint32>* instanceIds = nullptr;
uint32 instanceId = player->GetBattleground()->GetInstanceID(); uint32 instanceId = player->GetBattleground()->GetInstanceID();
instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances; instanceIds = &BattlegroundData[queueTypeId][bracketId].bgInstances;
if (instanceIds && std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end()) if (instanceIds && std::find(instanceIds->begin(), instanceIds->end(), instanceId) == instanceIds->end())
instanceIds->push_back(instanceId); instanceIds->push_back(instanceId);
BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size(); BattlegroundData[queueTypeId][bracketId].bgInstanceCount = instanceIds->size();
} }
} }
@@ -1478,7 +1483,7 @@ void RandomPlayerbotMgr::RandomTeleport(Player* bot, std::vector<WorldLocation>&
z = 0.05f + ground; z = 0.05f + ground;
PlayerInfo const* pInfo = sObjectMgr->GetPlayerInfo(bot->getRace(true), bot->getClass()); PlayerInfo const* pInfo = sObjectMgr->GetPlayerInfo(bot->getRace(true), bot->getClass());
float dis = loc.GetExactDist(pInfo->positionX, pInfo->positionY, pInfo->positionZ); float dis = loc.GetExactDist(pInfo->positionX, pInfo->positionY, pInfo->positionZ);
// yunfan: distance check for low level // yunfan: distance check for low level
if (bot->GetLevel() <= 4 && (loc.GetMapId() != pInfo->mapId || dis > 500.0f)) if (bot->GetLevel() <= 4 && (loc.GetMapId() != pInfo->mapId || dis > 500.0f))
{ {
@@ -1606,8 +1611,8 @@ void RandomPlayerbotMgr::PrepareZone2LevelBracket()
zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest zone2LevelBracket[2817] = {77, 80}; // Crystalsong Forest
zone2LevelBracket[3537] = {68, 75}; // Borean Tundra zone2LevelBracket[3537] = {68, 75}; // Borean Tundra
zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin zone2LevelBracket[3711] = {75, 80}; // Sholazar Basin
zone2LevelBracket[4197] = {79, 80}; // Wintergrasp zone2LevelBracket[4197] = {79, 80}; // Wintergrasp
// Override with values from config // Override with values from config
for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) { for (auto const& [zoneId, bracketPair] : sPlayerbotAIConfig->zoneBrackets) {
zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second}; zone2LevelBracket[zoneId] = {bracketPair.first, bracketPair.second};
@@ -1777,7 +1782,7 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
} }
LOG_INFO("playerbots", ">> {} innkeepers locations for level collected.", collected_locs); LOG_INFO("playerbots", ">> {} innkeepers locations for level collected.", collected_locs);
} }
results = WorldDatabase.Query( results = WorldDatabase.Query(
"SELECT " "SELECT "
"map, " "map, "
@@ -1878,12 +1883,12 @@ void RandomPlayerbotMgr::Init()
{ {
if (sPlayerbotAIConfig->addClassCommand) if (sPlayerbotAIConfig->addClassCommand)
sRandomPlayerbotMgr->PrepareAddclassCache(); sRandomPlayerbotMgr->PrepareAddclassCache();
if (sPlayerbotAIConfig->enabled) if (sPlayerbotAIConfig->enabled)
{ {
sRandomPlayerbotMgr->PrepareTeleportCache(); sRandomPlayerbotMgr->PrepareTeleportCache();
} }
if (sPlayerbotAIConfig->randomBotJoinBG) if (sPlayerbotAIConfig->randomBotJoinBG)
sRandomPlayerbotMgr->LoadBattleMastersCache(); sRandomPlayerbotMgr->LoadBattleMastersCache();
@@ -2026,7 +2031,7 @@ void RandomPlayerbotMgr::RandomizeFirst(Player* bot)
if (sPlayerbotAIConfig->syncLevelWithPlayers) if (sPlayerbotAIConfig->syncLevelWithPlayers)
maxLevel = std::max(sPlayerbotAIConfig->randomBotMinLevel, maxLevel = std::max(sPlayerbotAIConfig->randomBotMinLevel,
std::min(playersLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))); std::min(playersLevel, sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)));
uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel; uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel;
if (bot->getClass() == CLASS_DEATH_KNIGHT) if (bot->getClass() == CLASS_DEATH_KNIGHT)
{ {
@@ -2623,7 +2628,7 @@ void RandomPlayerbotMgr::OnBotLoginInternal(Player* const bot)
{ {
LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(), LOG_INFO("playerbots", "{}/{} Bot {} logged in", playerBots.size(), sRandomPlayerbotMgr->GetMaxAllowedBotCount(),
bot->GetName().c_str()); bot->GetName().c_str());
if (playerBots.size() == sRandomPlayerbotMgr->GetMaxAllowedBotCount()) if (playerBots.size() == sRandomPlayerbotMgr->GetMaxAllowedBotCount())
{ {
_isBotLogging = false; _isBotLogging = false;
@@ -2867,7 +2872,7 @@ void RandomPlayerbotMgr::PrintStats()
++tank; ++tank;
else else
++dps; ++dps;
zoneCount[bot->GetZoneId()]++; zoneCount[bot->GetZoneId()]++;
if (sPlayerbotAIConfig->enableNewRpgStrategy) if (sPlayerbotAIConfig->enableNewRpgStrategy)
@@ -2878,7 +2883,7 @@ void RandomPlayerbotMgr::PrintStats()
} }
} }
LOG_INFO("playerbots", "Bots level:"); LOG_INFO("playerbots", "Bots level:");
// uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); // uint32 maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL);
uint32_t currentAlliance = 0, currentHorde = 0; uint32_t currentAlliance = 0, currentHorde = 0;
@@ -2940,7 +2945,7 @@ void RandomPlayerbotMgr::PrintStats()
LOG_INFO("playerbots", " In BG: {}", inBg); LOG_INFO("playerbots", " In BG: {}", inBg);
LOG_INFO("playerbots", " In Rest: {}", rest); LOG_INFO("playerbots", " In Rest: {}", rest);
LOG_INFO("playerbots", " Dead: {}", dead); LOG_INFO("playerbots", " Dead: {}", dead);
// LOG_INFO("playerbots", "Bots zone:"); // LOG_INFO("playerbots", "Bots zone:");
// for (auto &[zond_id, counter] : zoneCount) // for (auto &[zond_id, counter] : zoneCount)
// { // {
@@ -2948,7 +2953,7 @@ void RandomPlayerbotMgr::PrintStats()
// std::string name = PlayerbotAI::GetLocalizedAreaName(entry); // std::string name = PlayerbotAI::GetLocalizedAreaName(entry);
// LOG_INFO("playerbots", " {}: {}", name, counter); // LOG_INFO("playerbots", " {}: {}", name, counter);
// } // }
if (sPlayerbotAIConfig->enableNewRpgStrategy) if (sPlayerbotAIConfig->enableNewRpgStrategy)
{ {
LOG_INFO("playerbots", "Bots rpg status:"); LOG_INFO("playerbots", "Bots rpg status:");