rewrite random bot accounts deletion

This commit is contained in:
Yunfan Li 2023-08-09 23:10:58 +08:00
parent 3aef47aa85
commit 8c9ab0e428
2 changed files with 74 additions and 167 deletions

View File

@ -5,10 +5,13 @@
#include "RandomPlayerbotFactory.h" #include "RandomPlayerbotFactory.h"
#include "ArenaTeamMgr.h" #include "ArenaTeamMgr.h"
#include "AccountMgr.h" #include "AccountMgr.h"
#include "DatabaseEnv.h"
#include "GuildMgr.h" #include "GuildMgr.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
#include "ScriptMgr.h"
#include "SocialMgr.h" #include "SocialMgr.h"
#include <unistd.h>
std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces; std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces;
@ -129,21 +132,33 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
} }
} }
std::string name; std::string name;
if (names.empty()) // int tries = 10;
name = CreateRandomBotName(gender); // while (tries--) {
else // std::string cur_name;
{ // if (names.empty())
if (names[gender].empty()) name = CreateRandomBotName(gender);
return nullptr; // else
// {
// if (names[gender].empty())
// return nullptr;
uint32 i = urand(0, names[gender].size() - 1); // uint32 i = urand(0, names[gender].size() - 1);
name = names[gender][i]; // cur_name = names[gender][i];
std::swap(names[gender][i], names[gender].back()); // std::swap(names[gender][i], names[gender].back());
names[gender].pop_back(); // names[gender].pop_back();
} // }
// if (ObjectMgr::CheckPlayerName(name) != CHAR_NAME_SUCCESS)
// {
// name.clear();
// continue;
// }
// }
if (name.empty()) if (name.empty()) {
LOG_ERROR("playerbots", "Unable to get random bot name!");
return nullptr; return nullptr;
}
CharacterDatabase.DirectExecute("UPDATE playerbots_names SET in_use=1 WHERE name='{}'", name);
std::vector<uint8> skinColors, facialHairTypes; std::vector<uint8> skinColors, facialHairTypes;
std::vector<std::pair<uint8,uint8>> faces, hairs; std::vector<std::pair<uint8,uint8>> faces, hairs;
@ -192,10 +207,12 @@ Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls
player->setCinematic(2); player->setCinematic(2);
player->SetAtLoginFlag(AT_LOGIN_NONE); player->SetAtLoginFlag(AT_LOGIN_NONE);
if (player->getClass() == CLASS_DEATH_KNIGHT) // player->SaveToDB(true, false);
{ // if (player->getClass() == CLASS_DEATH_KNIGHT)
player->learnSpell(50977, false); // {
} // player->learnSpell(50977, false);
// }
// player->RewardQuest(const Quest *quest, uint32 reward, Object *questGiver)
LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId, name.c_str(), race, cls); LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId, name.c_str(), race, cls);
return player; return player;
@ -206,26 +223,18 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(uint8 gender)
std::string botName = ""; std::string botName = "";
int tries = 10; int tries = 10;
while(--tries) { while(--tries) {
QueryResult result = CharacterDatabase.Query("SELECT n.name FROM playerbots_names n " QueryResult result = CharacterDatabase.Query("SELECT name FROM playerbots_names "
"LEFT OUTER JOIN characters e ON e.name = n.name " "WHERE in_use = 0 AND gender = {} ORDER BY RAND() LIMIT 1", gender);
"WHERE e.guid IS NULL AND n.in_use=0 ORDER BY RAND() LIMIT 1");
if (!result) if (!result)
{ {
continue; break;
} }
Field* fields = result->Fetch(); Field* fields = result->Fetch();
std::string ret = fields[0].Get<std::string>(); std::string ret = fields[0].Get<std::string>();
// check name limitations if (ObjectMgr::CheckPlayerName(ret) == CHAR_NAME_SUCCESS)
if (ObjectMgr::CheckPlayerName(ret) != CHAR_NAME_SUCCESS ||
(sObjectMgr->IsReservedName(ret) || sObjectMgr->IsProfanityName(ret)))
{ {
continue; return ret;
} }
if (ret.size()) {
CharacterDatabase.DirectExecute("UPDATE playerbots_names SET in_use=1 WHERE name='{}'", ret);
}
return ret;
} }
LOG_ERROR("playerbots", "No more names left for random bots. Simply random."); LOG_ERROR("playerbots", "No more names left for random bots. Simply random.");
tries = 10; tries = 10;
@ -242,31 +251,14 @@ std::string const RandomPlayerbotFactory::CreateRandomBotName(uint8 gender)
return std::move(botName); return std::move(botName);
} }
LOG_ERROR("playerbots", "Random name generation failed."); LOG_ERROR("playerbots", "Random name generation failed.");
botName.clear();
return std::move(botName); return std::move(botName);
} }
void RandomPlayerbotFactory::CreateRandomBots() void RandomPlayerbotFactory::CreateRandomBots()
{ {
// check if scheduled for delete /* multi-thread here is meaningless? since the async db operations */
bool delAccs = false; if (sPlayerbotAIConfig->deleteRandomBotAccounts)
bool delFriends = false;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_VALUE);
stmt->SetData(0, "bot_delete");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
delAccs = true;
Field* fields = result->Fetch();
uint32 deleteType = fields[0].Get<uint32>();
if (deleteType > 1)
delFriends = true;
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots WHERE event = 'bot_delete'");
}
if (sPlayerbotAIConfig->deleteRandomBotAccounts || delAccs)
{ {
std::vector<uint32> botAccounts; std::vector<uint32> botAccounts;
std::vector<uint32> botFriends; std::vector<uint32> botFriends;
@ -281,28 +273,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
botAccounts.push_back(accountId); botAccounts.push_back(accountId);
} }
// if (!delFriends) LOG_INFO("playerbots", "Deleting all random bot characters, {} accounts collected...", botAccounts.size());
// LOG_INFO("playerbots", "Deleting random bot characters without friends/guild...");
// else
LOG_INFO("playerbots", "Deleting all random bot characters...");
// load list of friends
// if (!delFriends)
// {
// QueryResult result = CharacterDatabase.Query("SELECT friend FROM character_social WHERE flags={}", SOCIAL_FLAG_FRIEND);
// if (result)
// {
// do
// {
// Field* fields = result->Fetch();
// uint32 guidlow = fields[0].Get<uint32>();
// botFriends.push_back(guidlow);
// } while (result->NextRow());
// }
// }
// std::vector<std::future<void>> dels;
QueryResult results = LoginDatabase.Query("SELECT id FROM account WHERE username LIKE '{}%%'", sPlayerbotAIConfig->randomBotAccountPrefix.c_str()); QueryResult results = LoginDatabase.Query("SELECT id FROM account WHERE username LIKE '{}%%'", sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
if (results) if (results)
{ {
@ -310,55 +281,15 @@ void RandomPlayerbotFactory::CreateRandomBots()
{ {
Field* fields = results->Fetch(); Field* fields = results->Fetch();
uint32 accId = fields[0].Get<uint32>(); uint32 accId = fields[0].Get<uint32>();
// if (!delFriends)
// {
// // existing characters list
// CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
// stmt->SetData(0, accId);
// if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
// {
// do
// {
// Field* fields = result->Fetch();
// uint32 guidlow = fields[0].Get<uint32>();
// ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(guidlow);
// // if bot is someone's friend - don't delete it
// if ((find(botFriends.begin(), botFriends.end(), guidlow) != botFriends.end()) && !delFriends)
// continue;
// // if bot is in someone's guild - don't delete it
// uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
// if (guildId && !delFriends)
// {
// Guild* guild = sGuildMgr->GetGuildById(guildId);
// uint32 accountId = sRandomPlayerbotMgr->GetAccountId(guild->GetLeaderGUID());
// if (find(botAccounts.begin(), botAccounts.end(), accountId) == botAccounts.end())
// continue;
// }
// Player::DeleteFromDB(guidlow, accId, false, true); // no need to update realm characters
// } while (result->NextRow());
// }
// }
// else
AccountMgr::DeleteAccount(accId); AccountMgr::DeleteAccount(accId);
// dels.push_back(std::async([accId]
// {
// AccountMgr::DeleteAccount(accId);
// }));
} while (results->NextRow()); } while (results->NextRow());
} }
/* for (uint32 i = 0; i < dels.size(); i++)
{
dels[i].wait();
}*/
PlayerbotsDatabase.Execute(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_RANDOM_BOTS)); PlayerbotsDatabase.Execute(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_RANDOM_BOTS));
CharacterDatabase.DirectExecute("UPDATE playerbots_names SET in_use=0"); CharacterDatabase.Execute("UPDATE playerbots_names SET in_use=0 WHERE in_use=1");
/* TODO(yunfan): we need to sleep here to wait for async account deleted, or the newly account won't be created correctly
the better way is turning the async db operation to sync db operation */
std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount);
LOG_INFO("playerbots", "Random bot characters deleted"); LOG_INFO("playerbots", "Random bot characters deleted");
} }
@ -367,6 +298,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
LOG_INFO("playerbots", "Creating random bot accounts..."); LOG_INFO("playerbots", "Creating random bot accounts...");
std::vector<std::future<void>> account_creations; std::vector<std::future<void>> account_creations;
bool account_creation = false;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber) for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{ {
std::ostringstream out; std::ostringstream out;
@ -380,7 +312,7 @@ void RandomPlayerbotFactory::CreateRandomBots()
{ {
continue; continue;
} }
account_creation = true;
std::string password = ""; std::string password = "";
if (sPlayerbotAIConfig->randomBotRandomPassword) if (sPlayerbotAIConfig->randomBotRandomPassword)
{ {
@ -392,44 +324,24 @@ void RandomPlayerbotFactory::CreateRandomBots()
else else
password = accountName; password = accountName;
account_creations.push_back(std::async([accountName, password] AccountMgr::CreateAccount(accountName, password);
{
AccountMgr::CreateAccount(accountName, password);
}));
LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str()); LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str());
} }
for (uint32 i = 0; i < account_creations.size(); i++) if (account_creation) {
{ /* wait for async accounts create to make character create correctly, same as account delete */
account_creations[i].wait(); std::this_thread::sleep_for(10ms * sPlayerbotAIConfig->randomBotAccountCount);
} }
//LoginDatabase.Execute("UPDATE account SET expansion = {} WHERE username LIKE ''{}'%%'", EXPANSION_WRATH_OF_THE_LICH_KING, sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
LOG_INFO("server.loading", "Loading available names...");
std::unordered_map<uint8,std::vector<std::string>> names;
// QueryResult result = CharacterDatabase.Query("SELECT n.gender, n.name FROM playerbots_names n LEFT OUTER JOIN characters e ON e.name = n.name WHERE e.guid IS NULL");
// if (!result)
// {
// LOG_ERROR("server.loading", "No more names left for random bots");
// return;
// }
// do
// {
// Field* fields = result->Fetch();
// uint8 gender = fields[0].Get<uint8>();
// std::string const bname = fields[1].Get<std::string>();
// names[gender].push_back(bname);
// } while (result->NextRow());
LOG_INFO("playerbots", "Creating random bot characters..."); LOG_INFO("playerbots", "Creating random bot characters...");
uint32 totalRandomBotChars = 0; uint32 totalRandomBotChars = 0;
uint32 totalCharCount = sPlayerbotAIConfig->randomBotAccountCount * 10; uint32 totalCharCount = sPlayerbotAIConfig->randomBotAccountCount * 10;
std::unordered_map<uint8,std::vector<std::string>> names;
std::vector<std::pair<Player*, uint32>> playerBots; std::vector<std::pair<Player*, uint32>> playerBots;
std::vector<WorldSession*> sessionBots; std::vector<WorldSession*> sessionBots;
bool bot_creation = false;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber) for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{ {
std::ostringstream out; std::ostringstream out;
@ -453,7 +365,8 @@ void RandomPlayerbotFactory::CreateRandomBots()
totalRandomBotChars += count; totalRandomBotChars += count;
continue; continue;
} }
bot_creation = true;
LOG_INFO("playerbots", "Creating random bot characters for account: [{}/{}]", accountNumber + 1, sPlayerbotAIConfig->randomBotAccountCount);
RandomPlayerbotFactory factory(accountId); RandomPlayerbotFactory factory(accountId);
WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true); WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true);
@ -474,35 +387,29 @@ void RandomPlayerbotFactory::CreateRandomBots()
} }
if (cls != 10) if (cls != 10)
if (Player* playerBot = factory.CreateRandomBot(session, cls, names)) if (Player* playerBot = factory.CreateRandomBot(session, cls, names)) {
playerBots.emplace_back(playerBot, accountId); playerBot->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(playerBot->GetGUID(), accountId, playerBot->GetName(),
playerBot->getGender(), playerBot->getRace(), playerBot->getClass(), playerBot->getLevel());
playerBot->CleanupsBeforeDelete();
delete playerBot;
}
} }
totalRandomBotChars += AccountMgr::GetCharactersCount(accountId);
} }
std::vector<std::future<void>> bot_creations; if (bot_creation) {
for (auto const& itr : playerBots) LOG_INFO("playerbots", "Wait for {} characters load into database...", totalCharCount);
{ /* wait for characters load into database, or characters will fail to loggin */
Player* player = itr.first; std::this_thread::sleep_for(15ms * totalCharCount);
uint32 accountId = itr.second;
bot_creations.push_back(std::async([player, accountId]
{
player->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(player->GetGUID(), accountId, player->GetName(), player->getGender(), player->getRace(), player->getClass(), player->getLevel());
player->CleanupsBeforeDelete();
delete player;
}));
}
for (uint32 i = 0; i < bot_creations.size(); i++)
{
bot_creations[i].wait();
} }
for (WorldSession* session : sessionBots) for (WorldSession* session : sessionBots)
delete session; delete session;
for (uint32 accountId : sPlayerbotAIConfig->randomBotAccounts) {
totalRandomBotChars += AccountMgr::GetCharactersCount(accountId);
}
LOG_INFO("server.loading", "{} random bot accounts with {} characters available", sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars); LOG_INFO("server.loading", "{} random bot accounts with {} characters available", sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
} }

View File

@ -799,11 +799,11 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
// do not randomize or teleport immediately after server start (prevent lagging) // do not randomize or teleport immediately after server start (prevent lagging)
if (!GetEventValue(bot, "randomize")) { if (!GetEventValue(bot, "randomize")) {
randomTime = urand(sPlayerbotAIConfig->randomBotUpdateInterval, sPlayerbotAIConfig->randomBotUpdateInterval * 10); randomTime = urand(sPlayerbotAIConfig->randomBotUpdateInterval * 5, sPlayerbotAIConfig->randomBotUpdateInterval * 20);
ScheduleRandomize(bot, randomTime); ScheduleRandomize(bot, randomTime);
} }
if (!GetEventValue(bot, "teleport")) { if (!GetEventValue(bot, "teleport")) {
randomTime = urand(sPlayerbotAIConfig->randomBotUpdateInterval, sPlayerbotAIConfig->randomBotUpdateInterval * 10); randomTime = urand(sPlayerbotAIConfig->randomBotUpdateInterval * 5, sPlayerbotAIConfig->randomBotUpdateInterval * 20);
ScheduleTeleport(bot, randomTime); ScheduleTeleport(bot, randomTime);
} }
return true; return true;