* Implement the ability to delete characters without them being removed from
* the DB, so they can be unerased * Original patch by DasBlub * Ported to Trinty by Az@zel --HG-- branch : trunk
This commit is contained in:
@@ -61,6 +61,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid)
|
||||
if (!result)
|
||||
return AOR_NAME_NOT_EXIST; // account doesn't exist
|
||||
|
||||
// existed characters list
|
||||
result = CharacterDatabase.PQuery("SELECT guid FROM characters WHERE account='%d'",accid);
|
||||
if (result)
|
||||
{
|
||||
@@ -214,6 +215,19 @@ bool AccountMgr::CheckPassword(uint32 accid, std::string passwd)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 AccountMgr::GetCharactersCount(uint32 acc_id)
|
||||
{
|
||||
uint32 charcount = 0;
|
||||
// check character count
|
||||
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", acc_id);
|
||||
if (result)
|
||||
{
|
||||
Field *fields=result->Fetch();
|
||||
charcount = fields[0].GetUInt32();
|
||||
}
|
||||
return charcount;
|
||||
}
|
||||
|
||||
bool AccountMgr::normalizeString(std::string& utf8str)
|
||||
{
|
||||
wchar_t wstr_buf[MAX_ACCOUNT_STR+1];
|
||||
|
||||
@@ -53,6 +53,7 @@ class AccountMgr
|
||||
uint32 GetSecurity(uint32 acc_id);
|
||||
uint32 GetSecurity(uint32 acc_id, int32 realm_id);
|
||||
bool GetName(uint32 acc_id, std::string &name);
|
||||
uint32 GetCharactersCount(uint32 acc_id);
|
||||
std::string CalculateShaPassHash(std::string& name, std::string& password);
|
||||
|
||||
static bool normalizeString(std::string& utf8str);
|
||||
|
||||
@@ -119,10 +119,20 @@ ChatCommand * ChatHandler::getCommandTable()
|
||||
{ NULL, 0, false, NULL, "", NULL }
|
||||
};
|
||||
|
||||
static ChatCommand characterDeletedCommandTable[] =
|
||||
{
|
||||
{ "delete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeletedDeleteCommand, "", NULL },
|
||||
{ "list", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterDeletedListCommand, "", NULL },
|
||||
{ "restore", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterDeletedRestoreCommand, "", NULL },
|
||||
{ "old", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeletedOldCommand, "", NULL },
|
||||
{ NULL, 0, false, NULL, "", NULL }
|
||||
};
|
||||
|
||||
static ChatCommand characterCommandTable[] =
|
||||
{
|
||||
{ "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL },
|
||||
{ "delete", SEC_CONSOLE, true, &ChatHandler::HandleCharacterDeleteCommand, "", NULL },
|
||||
{ "deleted", SEC_GAMEMASTER, true, NULL, "", characterDeletedCommandTable},
|
||||
{ "erase", SEC_CONSOLE, true, &ChatHandler::HandleCharacterEraseCommand, "", NULL },
|
||||
{ "level", SEC_ADMINISTRATOR, true, &ChatHandler::HandleCharacterLevelCommand, "", NULL },
|
||||
{ "rename", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterRenameCommand, "", NULL },
|
||||
{ "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL },
|
||||
@@ -667,7 +677,7 @@ ChatCommand * ChatHandler::getCommandTable()
|
||||
{ "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable },
|
||||
{ "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable },
|
||||
{ "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable },
|
||||
{ "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable},
|
||||
{ "character", SEC_GAMEMASTER, true, NULL, "", characterCommandTable},
|
||||
{ "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable },
|
||||
{ "gobject", SEC_GAMEMASTER, false, NULL, "", gobjectCommandTable },
|
||||
{ "honor", SEC_GAMEMASTER, false, NULL, "", honorCommandTable },
|
||||
|
||||
@@ -133,7 +133,11 @@ class ChatHandler
|
||||
bool HandleCastTargetCommand(const char *args);
|
||||
|
||||
bool HandleCharacterCustomizeCommand(const char * args);
|
||||
bool HandleCharacterDeleteCommand(const char* args);
|
||||
bool HandleCharacterDeletedDeleteCommand(const char* args);
|
||||
bool HandleCharacterDeletedListCommand(const char* args);
|
||||
bool HandleCharacterDeletedRestoreCommand(const char* args);
|
||||
bool HandleCharacterDeletedOldCommand(const char* args);
|
||||
bool HandleCharacterEraseCommand(const char* args);
|
||||
bool HandleCharacterLevelCommand(const char* args);
|
||||
bool HandleCharacterRenameCommand(const char * args);
|
||||
bool HandleCharacterReputationCommand(const char* args);
|
||||
@@ -620,6 +624,22 @@ class ChatHandler
|
||||
void HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel);
|
||||
void HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id);
|
||||
|
||||
// Stores informations about a deleted character
|
||||
struct DeletedInfo
|
||||
{
|
||||
uint32 lowguid; ///< the low GUID from the character
|
||||
std::string name; ///< the character name
|
||||
uint32 accountId; ///< the account id
|
||||
std::string accountName; ///< the account name
|
||||
time_t deleteDate; ///< the date at which the character has been deleted
|
||||
};
|
||||
|
||||
typedef std::list<DeletedInfo> DeletedInfoList;
|
||||
bool GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString = "");
|
||||
std::string GenerateDeletedCharacterGUIDsWhereStr(DeletedInfoList::const_iterator& itr, DeletedInfoList::const_iterator const& itr_end);
|
||||
void HandleCharacterDeletedListHelper(DeletedInfoList const& foundList);
|
||||
void HandleCharacterDeletedRestoreHelper(DeletedInfo const& delInfo);
|
||||
|
||||
void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
|
||||
private:
|
||||
WorldSession * m_session; // != NULL for chat command call and NULL for CLI command
|
||||
|
||||
@@ -6522,7 +6522,7 @@ bool ChatHandler::HandlePDumpWriteCommand(const char *args)
|
||||
|
||||
uint32 guid;
|
||||
// character name can't start from number
|
||||
if (isNumeric(p2[0]))
|
||||
if (isNumeric(p2))
|
||||
guid = atoi(p2);
|
||||
else
|
||||
{
|
||||
|
||||
@@ -4343,22 +4343,41 @@ TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell
|
||||
return TRAINER_SPELL_GREEN;
|
||||
}
|
||||
|
||||
void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars)
|
||||
/**
|
||||
* Deletes a character from the database
|
||||
*
|
||||
* The way, how the characters will be deleted is decided based on the config option.
|
||||
*
|
||||
* @see Player::DeleteOldCharacters
|
||||
*
|
||||
* @param playerguid the low-GUID from the player which should be deleted
|
||||
* @param accountId the account id from the player
|
||||
* @param updateRealmChars when this flag is set, the amount of characters on that realm will be updated in the realmlist
|
||||
* @param deleteFinally if this flag is set, the config option will be ignored and the character will be permanently removed from the database
|
||||
*/
|
||||
void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars, bool deleteFinally)
|
||||
{
|
||||
// for not existed account avoid update realm
|
||||
if (accountId == 0)
|
||||
updateRealmChars = false;
|
||||
|
||||
uint32 charDelete_method = sWorld.getConfig(CONFIG_CHARDELETE_METHOD);
|
||||
uint32 charDelete_minLvl = sWorld.getConfig(CONFIG_CHARDELETE_MIN_LEVEL);
|
||||
|
||||
// if we want to finally delete the character or the character does not meet the level requirement,
|
||||
// we set it to mode CHAR_DELETE_REMOVE
|
||||
if (deleteFinally || Player::GetLevelFromDB(playerguid) < charDelete_minLvl)
|
||||
charDelete_method = CHAR_DELETE_REMOVE;
|
||||
|
||||
uint32 guid = GUID_LOPART(playerguid);
|
||||
|
||||
// convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry)
|
||||
// bones will be deleted by corpse/bones deleting thread shortly
|
||||
sObjectAccessor.ConvertCorpseForPlayer(playerguid);
|
||||
|
||||
// remove from guild
|
||||
uint32 guildId = GetGuildIdFromDB(playerguid);
|
||||
if (guildId != 0)
|
||||
{
|
||||
Guild* guild = objmgr.GetGuildById(guildId);
|
||||
if (guild)
|
||||
if (uint32 guildId = GetGuildIdFromDB(playerguid))
|
||||
if (Guild* guild = objmgr.GetGuildById(guildId))
|
||||
guild->DelMember(guid);
|
||||
}
|
||||
|
||||
// remove from arena teams
|
||||
LeaveAllArenaTeams(playerguid);
|
||||
@@ -4372,139 +4391,194 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
|
||||
RemoveFromGroup(group, playerguid);
|
||||
}
|
||||
|
||||
// remove signs from petitions (also remove petitions if owner);
|
||||
// Remove signs from petitions (also remove petitions if owner);
|
||||
RemovePetitionsAndSigns(playerguid, 10);
|
||||
|
||||
// return back all mails with COD and Item 0 1 2 3 4 5 6 7
|
||||
QueryResult_AutoPtr resultMail = CharacterDatabase.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,body,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
|
||||
if (resultMail)
|
||||
switch (charDelete_method)
|
||||
{
|
||||
do
|
||||
// Completely remove from the database
|
||||
case CHAR_DELETE_REMOVE:
|
||||
{
|
||||
Field *fields = resultMail->Fetch();
|
||||
|
||||
uint32 mail_id = fields[0].GetUInt32();
|
||||
uint16 mailType = fields[1].GetUInt16();
|
||||
uint16 mailTemplateId= fields[2].GetUInt16();
|
||||
uint32 sender = fields[3].GetUInt32();
|
||||
std::string subject = fields[4].GetCppString();
|
||||
std::string body = fields[5].GetCppString();
|
||||
uint32 money = fields[6].GetUInt32();
|
||||
bool has_items = fields[7].GetBool();
|
||||
|
||||
//we can return mail now
|
||||
//so firstly delete the old one
|
||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
|
||||
|
||||
// mail not from player
|
||||
if (mailType != MAIL_NORMAL)
|
||||
// Return back all mails with COD and Item 0 1 2 3 4 5 6 7
|
||||
QueryResult_AutoPtr resultMail = CharacterDatabase.PQuery("SELECT id,messageType,mailTemplateId,sender,subject,body,money,has_items FROM mail WHERE receiver='%u' AND has_items<>0 AND cod<>0", guid);
|
||||
if (resultMail)
|
||||
{
|
||||
if (has_items)
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
MailDraft draft(subject, body);
|
||||
if (mailTemplateId)
|
||||
draft = MailDraft(mailTemplateId,false); // itesm already included
|
||||
|
||||
if (has_items)
|
||||
{
|
||||
// data needs to be at first place for Item::LoadFromDB
|
||||
QueryResult_AutoPtr resultItems = CharacterDatabase.PQuery("SELECT data,text,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id);
|
||||
if (resultItems)
|
||||
do
|
||||
{
|
||||
do
|
||||
Field *fields = resultMail->Fetch();
|
||||
|
||||
uint32 mail_id = fields[0].GetUInt32();
|
||||
uint16 mailType = fields[1].GetUInt16();
|
||||
uint16 mailTemplateId= fields[2].GetUInt16();
|
||||
uint32 sender = fields[3].GetUInt32();
|
||||
std::string subject = fields[4].GetCppString();
|
||||
std::string body = fields[5].GetCppString();
|
||||
uint32 money = fields[6].GetUInt32();
|
||||
bool has_items = fields[7].GetBool();
|
||||
|
||||
// We can return mail now
|
||||
// So firstly delete the old one
|
||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mail_id);
|
||||
|
||||
// Mail is not from player
|
||||
if (mailType != MAIL_NORMAL)
|
||||
{
|
||||
Field *fields2 = resultItems->Fetch();
|
||||
|
||||
uint32 item_guidlow = fields2[2].GetUInt32();
|
||||
uint32 item_template = fields2[3].GetUInt32();
|
||||
|
||||
ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template);
|
||||
if (!itemProto)
|
||||
{
|
||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
|
||||
continue;
|
||||
}
|
||||
|
||||
Item *pItem = NewItemOrBag(itemProto);
|
||||
if (!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),resultItems))
|
||||
{
|
||||
pItem->FSetState(ITEM_REMOVED);
|
||||
pItem->SaveToDB(); // it also deletes item object !
|
||||
continue;
|
||||
}
|
||||
|
||||
draft.AddItem(pItem);
|
||||
if (has_items)
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
|
||||
continue;
|
||||
}
|
||||
while (resultItems->NextRow());
|
||||
|
||||
MailDraft draft(subject, body);
|
||||
if (mailTemplateId)
|
||||
draft = MailDraft(mailTemplateId,false); // items are already included
|
||||
|
||||
if (has_items)
|
||||
{
|
||||
// Data needs to be at first place for Item::LoadFromDB
|
||||
QueryResult_AutoPtr resultItems = CharacterDatabase.PQuery("SELECT data,text,item_guid,item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail_id);
|
||||
if (resultItems)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields2 = resultItems->Fetch();
|
||||
|
||||
uint32 item_guidlow = fields2[2].GetUInt32();
|
||||
uint32 item_template = fields2[3].GetUInt32();
|
||||
|
||||
ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_template);
|
||||
if (!itemProto)
|
||||
{
|
||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guidlow);
|
||||
continue;
|
||||
}
|
||||
|
||||
Item *pItem = NewItemOrBag(itemProto);
|
||||
if (!pItem->LoadFromDB(item_guidlow, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER),resultItems))
|
||||
{
|
||||
pItem->FSetState(ITEM_REMOVED);
|
||||
pItem->SaveToDB(); // it also deletes item object!
|
||||
continue;
|
||||
}
|
||||
|
||||
draft.AddItem(pItem);
|
||||
}
|
||||
while (resultItems->NextRow());
|
||||
}
|
||||
}
|
||||
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
|
||||
|
||||
uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
|
||||
|
||||
draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender);
|
||||
}
|
||||
while (resultMail->NextRow());
|
||||
}
|
||||
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mail_id);
|
||||
// Unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
|
||||
// Get guids of character's pets, will deleted in transaction
|
||||
QueryResult_AutoPtr resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
|
||||
|
||||
uint32 pl_account = objmgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
|
||||
// NOW we can finally clear other DB data related to character
|
||||
CharacterDatabase.BeginTransaction();
|
||||
if (resultPets)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields3 = resultPets->Fetch();
|
||||
uint32 petguidlow = fields3[0].GetUInt32();
|
||||
Pet::DeleteFromDB(petguidlow);
|
||||
} while (resultPets->NextRow());
|
||||
}
|
||||
|
||||
draft.AddMoney(money).SendReturnToSender(pl_account, guid, sender);
|
||||
CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM group_instance WHERE leaderGuid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM group_member WHERE leaderGuid = '%u' OR memberGuid = '%u'",guid,guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM groups WHERE leaderGuid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM gm_tickets WHERE playerGuid = '%u'", guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u' " // NOTE: These achievements have flags & 256 in DBC.
|
||||
"AND achievement NOT BETWEEN '456' AND '467' " // Realm First Level 80
|
||||
"AND achievement NOT BETWEEN '1400' AND '1427' " // Realm First Raid Achievements
|
||||
"AND achievement NOT IN(1463, 3117, 3259) ", guid); // Realm First Northen Vanguard + Raid Achievements
|
||||
CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'",guid, guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);
|
||||
|
||||
CharacterDatabase.CommitTransaction();
|
||||
break;
|
||||
}
|
||||
while (resultMail->NextRow());
|
||||
// The character gets unlinked from the account, the name gets freed up and appears as deleted ingame
|
||||
case CHAR_DELETE_UNLINK:
|
||||
CharacterDatabase.PExecute("UPDATE characters SET deleteInfos_Name=name, deleteInfos_Account=account, deleteDate='" UI64FMTD "', name='', account=0 WHERE guid=%u", uint64(time(NULL)), guid);
|
||||
break;
|
||||
default:
|
||||
sLog.outError("Player::DeleteFromDB: Unsupported delete method: %u.", charDelete_method);
|
||||
}
|
||||
|
||||
// unsummon and delete for pets in world is not required: player deleted from CLI or character list with not loaded pet.
|
||||
// Get guids of character's pets, will deleted in transaction
|
||||
QueryResult_AutoPtr resultPets = CharacterDatabase.PQuery("SELECT id FROM character_pet WHERE owner = '%u'",guid);
|
||||
if (updateRealmChars)
|
||||
sWorld.UpdateRealmCharCount(accountId);
|
||||
}
|
||||
|
||||
// NOW we can finally clear other DB data related to character
|
||||
CharacterDatabase.BeginTransaction();
|
||||
if (resultPets)
|
||||
/**
|
||||
* Characters which were kept back in the database after being deleted and are now too old (see config option "CharDelete.KeepDays"), will be completely deleted.
|
||||
*
|
||||
* @see Player::DeleteFromDB
|
||||
*/
|
||||
void Player::DeleteOldCharacters()
|
||||
{
|
||||
uint32 keepDays = sWorld.getConfig(CONFIG_CHARDELETE_KEEP_DAYS);
|
||||
if (!keepDays)
|
||||
return;
|
||||
|
||||
Player::DeleteOldCharacters(keepDays);
|
||||
}
|
||||
|
||||
/**
|
||||
* Characters which were kept back in the database after being deleted and are older than the specified amount of days, will be completely deleted.
|
||||
*
|
||||
* @see Player::DeleteFromDB
|
||||
*
|
||||
* @param keepDays overrite the config option by another amount of days
|
||||
*/
|
||||
void Player::DeleteOldCharacters(uint32 keepDays)
|
||||
{
|
||||
sLog.outString("Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays);
|
||||
|
||||
QueryResult_AutoPtr resultChars = CharacterDatabase.PQuery("SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < '" UI64FMTD "'", uint64(time(NULL) - time_t(keepDays * DAY)));
|
||||
if (resultChars)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field *fields3 = resultPets->Fetch();
|
||||
uint32 petguidlow = fields3[0].GetUInt32();
|
||||
Pet::DeleteFromDB(petguidlow);
|
||||
} while (resultPets->NextRow());
|
||||
sLog.outString("Player::DeleteOldChars: Found %u character(s) to delete",resultChars->GetRowCount());
|
||||
do
|
||||
{
|
||||
Field *charFields = resultChars->Fetch();
|
||||
Player::DeleteFromDB(charFields[0].GetUInt64(), charFields[1].GetUInt32(), true, true);
|
||||
} while(resultChars->NextRow());
|
||||
}
|
||||
|
||||
CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_action WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_aura WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_gifts WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_inventory WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_reputation WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_spell WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_spell_cooldown WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM gm_tickets WHERE playerGuid = '%u'", guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE owner_guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_social WHERE guid = '%u' OR friend='%u'",guid,guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE receiver = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE receiver = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_pet_declinedname WHERE owner = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE guid = '%u' " // NOTE: These achievements have flags & 256 in DBC.
|
||||
"AND achievement NOT BETWEEN '456' AND '467' " // Realm First Level 80
|
||||
"AND achievement NOT BETWEEN '1400' AND '1427' " // Realm First Raid Achievements
|
||||
"AND achievement NOT IN(1463, 3117, 3259) ", guid); // Realm First Northen Vanguard + Raid Achievements
|
||||
CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_equipmentsets WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid1 = '%u' OR PlayerGuid2 = '%u'",guid, guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_queststatus_daily WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u'",guid);
|
||||
CharacterDatabase.PExecute("DELETE FROM character_skills WHERE guid = '%u'",guid);
|
||||
|
||||
CharacterDatabase.CommitTransaction();
|
||||
|
||||
//LoginDatabase.PExecute("UPDATE realmcharacters SET numchars = numchars - 1 WHERE acctid = %d AND realmid = %d", accountId, realmID);
|
||||
if (updateRealmChars) sWorld.UpdateRealmCharCount(accountId);
|
||||
}
|
||||
|
||||
void Player::SetMovement(PlayerMovementType pType)
|
||||
|
||||
@@ -839,6 +839,13 @@ struct AccessRequirement
|
||||
std::string heroicQuestFailedText;
|
||||
};
|
||||
|
||||
enum CharDeleteMethod
|
||||
{
|
||||
CHAR_DELETE_REMOVE = 0, // Completely remove from the database
|
||||
CHAR_DELETE_UNLINK = 1 // The character gets unlinked from the account,
|
||||
// the name gets freed up and appears as deleted ingame
|
||||
};
|
||||
|
||||
class PlayerTaxi
|
||||
{
|
||||
public:
|
||||
@@ -1418,6 +1425,10 @@ class Player : public Unit, public GridObject<Player>
|
||||
static void Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair);
|
||||
static void SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint32 zone,uint64 guid);
|
||||
|
||||
static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true, bool deleteFinally = false);
|
||||
static void DeleteOldCharacters();
|
||||
static void DeleteOldCharacters(uint32 keepDays);
|
||||
|
||||
bool m_mailsLoaded;
|
||||
bool m_mailsUpdated;
|
||||
|
||||
@@ -1836,8 +1847,6 @@ class Player : public Unit, public GridObject<Player>
|
||||
void SendTeleportPacket(Position &oldPos);
|
||||
void SendTeleportAckPacket();
|
||||
|
||||
static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true);
|
||||
|
||||
Corpse *GetCorpse() const;
|
||||
void SpawnCorpseBones();
|
||||
void CreateCorpse();
|
||||
|
||||
@@ -748,7 +748,18 @@ enum TrinityStrings
|
||||
LANG_ACCOUNT_LIST_LINE = 1013,
|
||||
LANG_ACCOUNT_LIST_EMPTY = 1014,
|
||||
LANG_ACCOUNT_LIST_BAR_HEADER = 1015,
|
||||
// Room for more level 4 1016-1099 not used
|
||||
LANG_CHARACTER_DELETED_LIST_HEADER = 1016,
|
||||
LANG_CHARACTER_DELETED_LIST_LINE_CONSOLE = 1017,
|
||||
LANG_CHARACTER_DELETED_LIST_BAR = 1018,
|
||||
LANG_CHARACTER_DELETED_LIST_EMPTY = 1019,
|
||||
LANG_CHARACTER_DELETED_RESTORE = 1020,
|
||||
LANG_CHARACTER_DELETED_DELETE = 1021,
|
||||
LANG_CHARACTER_DELETED_ERR_RENAME = 1022,
|
||||
LANG_CHARACTER_DELETED_SKIP_ACCOUNT = 1023,
|
||||
LANG_CHARACTER_DELETED_SKIP_FULL = 1024,
|
||||
LANG_CHARACTER_DELETED_SKIP_NAME = 1025,
|
||||
LANG_CHARACTER_DELETED_LIST_LINE_CHAT = 1026,
|
||||
// Room for more level 4 1027-1099 not used
|
||||
|
||||
// Level 3 (continue)
|
||||
LANG_ACCOUNT_SETADDON = 1100,
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "SQLStorage.h"
|
||||
#include "UpdateFields.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "AccountMgr.h"
|
||||
|
||||
#define DUMP_TABLE_COUNT 26
|
||||
struct DumpTable
|
||||
@@ -359,19 +360,9 @@ DumpReturn PlayerDumpWriter::WriteDump(const std::string& file, uint32 guid)
|
||||
|
||||
DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, std::string name, uint32 guid)
|
||||
{
|
||||
// check character count
|
||||
{
|
||||
QueryResult_AutoPtr result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account);
|
||||
uint8 charcount = 0;
|
||||
if (result)
|
||||
{
|
||||
Field *fields=result->Fetch();
|
||||
charcount = fields[0].GetUInt8();
|
||||
|
||||
if (charcount >= 10)
|
||||
return DUMP_TOO_MANY_CHARS;
|
||||
}
|
||||
}
|
||||
uint32 charcount = accmgr.GetCharactersCount(account);
|
||||
if (charcount >= 10)
|
||||
return DUMP_TOO_MANY_CHARS;
|
||||
|
||||
FILE *fin = fopen(file.c_str(), "r");
|
||||
if (!fin)
|
||||
|
||||
@@ -1172,6 +1172,11 @@ void World::LoadConfigSettings(bool reload)
|
||||
m_visibility_notify_periodInInstances = sConfig.GetIntDefault("Visibility.Notify.Period.InInstances", DEFAULT_VISIBILITY_NOTIFY_PERIOD);
|
||||
m_visibility_notify_periodInBGArenas = sConfig.GetIntDefault("Visibility.Notify.Period.InBGArenas", DEFAULT_VISIBILITY_NOTIFY_PERIOD);
|
||||
|
||||
///- Load the CharDelete related config options
|
||||
m_configs[CONFIG_CHARDELETE_METHOD] = sConfig.GetIntDefault("CharDelete.Method", 0);
|
||||
m_configs[CONFIG_CHARDELETE_MIN_LEVEL] = sConfig.GetIntDefault("CharDelete.MinLevel", 0);
|
||||
m_configs[CONFIG_CHARDELETE_KEEP_DAYS] = sConfig.GetIntDefault("CharDelete.KeepDays", 30);
|
||||
|
||||
///- Read the "Data" directory from the config file
|
||||
std::string dataPath = sConfig.GetStringDefault("DataDir","./");
|
||||
if (dataPath.at(dataPath.length()-1) != '/' && dataPath.at(dataPath.length()-1) != '\\')
|
||||
@@ -1639,6 +1644,7 @@ void World::SetInitialWorldSettings()
|
||||
m_timers[WUPDATE_CLEANDB].SetInterval(m_configs[CONFIG_LOGDB_CLEARINTERVAL]*MINUTE*IN_MILLISECONDS);
|
||||
// clean logs table every 14 days by default
|
||||
m_timers[WUPDATE_AUTOBROADCAST].SetInterval(abtimer);
|
||||
m_timers[WUPDATE_DELETECHARS].SetInterval(DAY*IN_MILLISECONDS); // check for chars to delete every day
|
||||
|
||||
//to set mailtimer to return mails every day between 4 and 5 am
|
||||
//mailtimer is increased when updating auctions
|
||||
@@ -1660,6 +1666,9 @@ void World::SetInitialWorldSettings()
|
||||
uint32 nextGameEvent = gameeventmgr.Initialize();
|
||||
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
|
||||
|
||||
// Delete all characters which have been deleted X days before
|
||||
Player::DeleteOldCharacters();
|
||||
|
||||
sLog.outString("Starting Arena Season...");
|
||||
gameeventmgr.StartArenaSeason();
|
||||
|
||||
@@ -1958,6 +1967,13 @@ void World::Update(uint32 diff)
|
||||
sOutdoorPvPMgr.Update(diff);
|
||||
RecordTimeDiff("UpdateOutdoorPvPMgr");
|
||||
|
||||
///- Delete all characters which have been deleted X days before
|
||||
if (m_timers[WUPDATE_DELETECHARS].Passed())
|
||||
{
|
||||
m_timers[WUPDATE_DELETECHARS].Reset();
|
||||
Player::DeleteOldCharacters();
|
||||
}
|
||||
|
||||
sLFGMgr.Update(diff);
|
||||
RecordTimeDiff("UpdateLFGMgr");
|
||||
|
||||
|
||||
@@ -84,7 +84,8 @@ enum WorldTimers
|
||||
WUPDATE_CLEANDB = 7,
|
||||
WUPDATE_AUTOBROADCAST = 8,
|
||||
WUPDATE_MAILBOXQUEUE = 9,
|
||||
WUPDATE_COUNT = 10
|
||||
WUPDATE_DELETECHARS = 10,
|
||||
WUPDATE_COUNT = 11
|
||||
};
|
||||
|
||||
/// Configuration elements
|
||||
@@ -281,6 +282,9 @@ enum WorldConfigs
|
||||
CONFIG_BG_XP_FOR_KILL,
|
||||
CONFIG_RANDOM_BG_RESET_HOUR,
|
||||
CONFIG_VMAP_INDOOR_CHECK,
|
||||
CONFIG_CHARDELETE_KEEP_DAYS,
|
||||
CONFIG_CHARDELETE_METHOD,
|
||||
CONFIG_CHARDELETE_MIN_LEVEL,
|
||||
CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user