* 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:
Brian
2010-07-21 12:13:23 -06:00
parent 5995a8ec1d
commit 4320b10901
18 changed files with 677 additions and 146 deletions
+3
View File
@@ -392,6 +392,9 @@ CREATE TABLE `characters` (
`ammoId` int(10) UNSIGNED NOT NULL default '0',
`knownTitles` longtext,
`actionBars` tinyint(3) UNSIGNED NOT NULL default '0',
`deleteInfos_Account` int(11) UNSIGNED default NULL,
`deleteInfos_Name` varchar(12) default NULL,
`deleteDate` bigint(20) default NULL,
PRIMARY KEY (`guid`),
KEY `idx_account` (`account`),
KEY `idx_online` (`online`),
+16 -1
View File
@@ -350,7 +350,11 @@ INSERT INTO `command` VALUES
('cast self',3,'Syntax: .cast self #spellid [triggered]\r\nCast #spellid by target at target itself. If ''trigered'' or part provided then spell casted with triggered flag.'),
('cast target',3,'Syntax: .cast target #spellid [triggered]\r\n Selected target will cast #spellid to his victim. If ''trigered'' or part provided then spell casted with triggered flag.'),
('character customize',2,'Syntax: .character customize [$name]\r\n\r\nMark selected in game or by $name in command character for customize at next login.'),
('character delete',4,'Syntax: .character delete $name\r\n\r\nDelete character $name.'),
('character erase',4,'Syntax: .character erase $name\r\n\r\nDelete character $name. Character finally deleted in case any deleting options.'),
('character deleted delete', 4, 'Syntax: .character deleted delete #guid|$name\r\n\r\nCompletely deletes the selected characters.\r\nIf $name is supplied, only characters with that string in their name will be deleted, if #guid is supplied, only the character with that GUID will be deleted.'),
('character deleted list', 3, 'Syntax: .character deleted list [#guid|$name]\r\n\r\nShows a list with all deleted characters.\r\nIf $name is supplied, only characters with that string in their name will be selected, if #guid is supplied, only the character with that GUID will be selected.'),
('character deleted old', 4, 'Syntax: .character deleted old [#keepDays]\r\n\r\nCompletely deletes all characters with deleted time longer #keepDays. If #keepDays not provided the used value from mangosd.conf option \'CharDelete.KeepDays\'. If referenced config option disabled (use 0 value) then command can\'t be used without #keepDays.'),
('character deleted restore', 3, 'Syntax: .character deleted restore #guid|$name [$newname] [#new account]\r\n\r\nRestores deleted characters.\r\nIf $name is supplied, only characters with that string in their name will be restored, if $guid is supplied, only the character with that GUID will be restored.\r\nIf $newname is set, the character will be restored with that name instead of the original one. If #newaccount is set, the character will be restored to specific account character list. This works only with one character!');
('character level',3,'Syntax: .character level [$playername] [#level]\r\n\r\nSet the level of character with $playername (or the selected if not name provided) by #numberoflevels Or +1 if no #numberoflevels provided). If #numberoflevels is omitted, the level will be increase by 1. If #numberoflevels is 0, the same level will be restarted. If no character is selected and name not provided, increase your level. Command can be used for offline character. All stats and dependent values recalculated. At level decrease talents can be reset if need. Also at level decrease equipped items with greater level requirement can be lost.'),
('character rename',2,'Syntax: .character rename [$name]\r\n\r\nMark selected in game or by $name in command character for rename at next login.'),
('character reputation',2,'Syntax: .character reputation [$player_name]\r\n\r\nShow reputation information for selected player or player find by $player_name.'),
@@ -15252,6 +15256,17 @@ INSERT INTO `trinity_string` (`entry`,`content_default`,`content_loc1`,`content_
(1013, '-[%16s][%12s][%15s][%5d][%9d][%3d][%4d]', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1014, 'No online players.', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1015, '============================== Characters Online =============================', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1016, '| GUID | Name | Account | Delete Date |', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1017, '| %10u | %20s | %15s (%10u) | %19s |', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1018, '==========================================================================================', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1019, 'No characters found.', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1020, 'Restoring the following characters:', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1021, 'Deleting the following characters:', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1022, 'ERROR: You can only assign a new name if you have only selected a single character!', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1023, 'Character \'%s\' (GUID: %u Account %u) can\'t be restored: account not exist!', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1024, 'Character \'%s\' (GUID: %u Account %u) can\'t be restored: account character list full!', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1025, 'Character \'%s\' (GUID: %u Account %u) can\'t be restored: new name already used!', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1026, 'GUID: %u Name: %s Account: %s (%u) Date: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
(1100, 'Account %s (Id: %u) have up to %u expansion allowed now.', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1101, 'Message of the day changed to:\r\n%s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
(1102, 'Message sent to %s: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
@@ -0,0 +1,5 @@
ALTER TABLE `characters`
ADD COLUMN `deleteInfos_Account` int(11) UNSIGNED default NULL AFTER actionBars,
ADD COLUMN `deleteInfos_Name` varchar(12) default NULL AFTER deleteInfos_Account,
ADD COLUMN `deleteDate` bigint(20) default NULL AFTER deleteInfos_Name;
+8
View File
@@ -0,0 +1,8 @@
DELETE FROM command WHERE name IN ('character delete', 'character deleted list', 'character deleted restore', 'character deleted delete', 'character deleted old', 'character erase');
INSERT INTO command (name, security, help) VALUES
('character erase',4,'Syntax: .character erase $name\r\n\r\nDelete character $name. Character finally deleted in case any deleting options.'),
('character deleted delete', 4, 'Syntax: .character deleted delete #guid|$name\r\n\r\nCompletely deletes the selected characters.\r\nIf $name is supplied, only characters with that string in their name will be deleted, if #guid is supplied, only the character with that GUID will be deleted.'),
('character deleted list', 3, 'Syntax: .character deleted list [#guid|$name]\r\n\r\nShows a list with all deleted characters.\r\nIf $name is supplied, only characters with that string in their name will be selected, if #guid is supplied, only the character with that GUID will be selected.'),
('character deleted old', 4, 'Syntax: .character deleted old [#keepDays]\r\n\r\nCompletely deletes all characters with deleted time longer #keepDays. If #keepDays not provided the used value from mangosd.conf option \'CharDelete.KeepDays\'. If referenced config option disabled (use 0 value) then command can\'t be used without #keepDays.'),
('character deleted restore', 3, 'Syntax: .character deleted restore #guid|$name [$newname] [#new account]\r\n\r\nRestores deleted characters.\r\nIf $name is supplied, only characters with that string in their name will be restored, if $guid is supplied, only the character with that GUID will be restored.\r\nIf $newname is set, the character will be restored with that name instead of the original one. If #newaccount is set, the character will be restored to specific account character list. This works only with one character!');
+14
View File
@@ -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];
+1
View File
@@ -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);
+12 -2
View File
@@ -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 },
+21 -1
View File
@@ -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
+1 -1
View File
@@ -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
{
+196 -122
View File
@@ -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)
+11 -2
View File
@@ -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();
+12 -1
View File
@@ -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,
+4 -13
View File
@@ -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)
+16
View File
@@ -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");
+5 -1
View File
@@ -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
};
+9
View File
@@ -182,6 +182,15 @@ inline bool isNumeric(char c)
return (c >= '0' && c <='9');
}
inline bool isNumeric(char const* str)
{
for (char const* c = str; *c; ++c)
if (!isNumeric(*c))
return false;
return true;
}
inline bool isNumericOrSpace(wchar_t wchar)
{
return isNumeric(wchar) || wchar == L' ';
@@ -163,8 +163,322 @@ bool ChatHandler::HandleAccountDeleteCommand(const char* args)
return true;
}
bool ChatHandler::HandleCharacterDeleteCommand(const char* args)
/**
* Collects all GUIDs (and related info) from deleted characters which are still in the database.
*
* @param foundList a reference to an std::list which will be filled with info data
* @param searchString the search string which either contains a player GUID or a part fo the character-name
* @return returns false if there was a problem while selecting the characters (e.g. player name not normalizeable)
*/
bool ChatHandler::GetDeletedCharacterInfoList(DeletedInfoList& foundList, std::string searchString)
{
QueryResult_AutoPtr resultChar;
if (!searchString.empty())
{
// search by GUID
if (isNumeric(searchString.c_str()))
resultChar = CharacterDatabase.PQuery("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = %u", uint64(atoi(searchString.c_str())));
// search by name
else
{
if(!normalizePlayerName(searchString))
return false;
resultChar = CharacterDatabase.PQuery("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name " _LIKE_ " " _CONCAT3_("'%%'", "'%s'", "'%%'"), searchString.c_str());
}
}
else
resultChar = CharacterDatabase.Query("SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL");
if (resultChar)
{
do
{
Field* fields = resultChar->Fetch();
DeletedInfo info;
info.lowguid = fields[0].GetUInt32();
info.name = fields[1].GetCppString();
info.accountId = fields[2].GetUInt32();
// account name will be empty for not existed account
accmgr.GetName(info.accountId, info.accountName);
info.deleteDate = time_t(fields[3].GetUInt64());
foundList.push_back(info);
} while (resultChar->NextRow());
}
return true;
}
/**
* Generate WHERE guids list by deleted info in way preventing return too long where list for existed query string length limit.
*
* @param itr a reference to an deleted info list iterator, it updated in function for possible next function call if list to long
* @param itr_end a reference to an deleted info list iterator end()
* @return returns generated where list string in form: 'guid IN (gui1, guid2, ...)'
*/
std::string ChatHandler::GenerateDeletedCharacterGUIDsWhereStr(DeletedInfoList::const_iterator& itr, DeletedInfoList::const_iterator const& itr_end)
{
std::ostringstream wherestr;
wherestr << "guid IN ('";
for(; itr != itr_end; ++itr)
{
wherestr << itr->lowguid;
if (wherestr.str().size() > MAX_QUERY_LEN - 50) // near to max query
{
++itr;
break;
}
DeletedInfoList::const_iterator itr2 = itr;
if(++itr2 != itr_end)
wherestr << "','";
}
wherestr << "')";
return wherestr.str();
}
/**
* Shows all deleted characters which matches the given search string, expected non empty list
*
* @see ChatHandler::HandleCharacterDeletedListCommand
* @see ChatHandler::HandleCharacterDeletedRestoreCommand
* @see ChatHandler::HandleCharacterDeletedDeleteCommand
* @see ChatHandler::DeletedInfoList
*
* @param foundList contains a list with all found deleted characters
*/
void ChatHandler::HandleCharacterDeletedListHelper(DeletedInfoList const& foundList)
{
if (!m_session)
{
SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR);
SendSysMessage(LANG_CHARACTER_DELETED_LIST_HEADER);
SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR);
}
for (DeletedInfoList::const_iterator itr = foundList.begin(); itr != foundList.end(); ++itr)
{
std::string dateStr = TimeToTimestampStr(itr->deleteDate);
if (!m_session)
PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CONSOLE,
itr->lowguid, itr->name.c_str(), itr->accountName.empty() ? "<Not existed>" : itr->accountName.c_str(),
itr->accountId, dateStr.c_str());
else
PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CHAT,
itr->lowguid, itr->name.c_str(), itr->accountName.empty() ? "<Not existed>" : itr->accountName.c_str(),
itr->accountId, dateStr.c_str());
}
if (!m_session)
SendSysMessage(LANG_CHARACTER_DELETED_LIST_BAR);
}
/**
* Handles the '.character deleted list' command, which shows all deleted characters which matches the given search string
*
* @see ChatHandler::HandleCharacterDeletedListHelper
* @see ChatHandler::HandleCharacterDeletedRestoreCommand
* @see ChatHandler::HandleCharacterDeletedDeleteCommand
* @see ChatHandler::DeletedInfoList
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
bool ChatHandler::HandleCharacterDeletedListCommand(const char* args)
{
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, args))
return false;
// if no characters have been found, output a warning
if (foundList.empty())
{
SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
return false;
}
HandleCharacterDeletedListHelper(foundList);
return true;
}
/**
* Restore a previously deleted character
*
* @see ChatHandler::HandleCharacterDeletedListHelper
* @see ChatHandler::HandleCharacterDeletedRestoreCommand
* @see ChatHandler::HandleCharacterDeletedDeleteCommand
* @see ChatHandler::DeletedInfoList
*
* @param delInfo the informations about the character which will be restored
*/
void ChatHandler::HandleCharacterDeletedRestoreHelper(DeletedInfo const& delInfo)
{
if (delInfo.accountName.empty()) // account not exist
{
PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_ACCOUNT, delInfo.name.c_str(), delInfo.lowguid, delInfo.accountId);
return;
}
// check character count
uint32 charcount = accmgr.GetCharactersCount(delInfo.accountId);
if (charcount >= 10)
{
PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_FULL, delInfo.name.c_str(), delInfo.lowguid, delInfo.accountId);
return;
}
if (objmgr.GetPlayerGUIDByName(delInfo.name))
{
PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_NAME, delInfo.name.c_str(), delInfo.lowguid, delInfo.accountId);
return;
}
CharacterDatabase.PExecute("UPDATE characters SET name='%s', account='%u', deleteDate=NULL, deleteInfos_Name=NULL, deleteInfos_Account=NULL WHERE deleteDate IS NOT NULL AND guid = %u",
delInfo.name.c_str(), delInfo.accountId, delInfo.lowguid);
}
/**
* Handles the '.character deleted restore' command, which restores all deleted characters which matches the given search string
*
* The command automatically calls '.character deleted list' command with the search string to show all restored characters.
*
* @see ChatHandler::HandleCharacterDeletedRestoreHelper
* @see ChatHandler::HandleCharacterDeletedListCommand
* @see ChatHandler::HandleCharacterDeletedDeleteCommand
*
* @param args the search string which either contains a player GUID or a part of the character-name
*/
bool ChatHandler::HandleCharacterDeletedRestoreCommand(const char* args)
{
// It is required to submit at least one argument
if (!*args)
return false;
std::string searchString;
std::string newCharName;
uint32 newAccount = 0;
// GCC by some strange reason fail build code without temporary variable
std::istringstream params(args);
params >> searchString >> newCharName >> newAccount;
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, searchString))
return false;
if (foundList.empty())
{
SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
return false;
}
SendSysMessage(LANG_CHARACTER_DELETED_RESTORE);
HandleCharacterDeletedListHelper(foundList);
if (newCharName.empty())
{
// Drop not existed account cases
for (DeletedInfoList::iterator itr = foundList.begin(); itr != foundList.end(); ++itr)
HandleCharacterDeletedRestoreHelper(*itr);
}
else if (foundList.size() == 1 && normalizePlayerName(newCharName))
{
DeletedInfo delInfo = foundList.front();
// update name
delInfo.name = newCharName;
// if new account provided update deleted info
if (newAccount && newAccount != delInfo.accountId)
{
delInfo.accountId = newAccount;
accmgr.GetName(newAccount, delInfo.accountName);
}
HandleCharacterDeletedRestoreHelper(delInfo);
}
else
SendSysMessage(LANG_CHARACTER_DELETED_ERR_RENAME);
return true;
}
/**
* Handles the '.character deleted delete' command, which completely deletes all deleted characters which matches the given search string
*
* @see Player::GetDeletedCharacterGUIDs
* @see Player::DeleteFromDB
* @see ChatHandler::HandleCharacterDeletedListCommand
* @see ChatHandler::HandleCharacterDeletedRestoreCommand
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
bool ChatHandler::HandleCharacterDeletedDeleteCommand(const char* args)
{
// It is required to submit at least one argument
if (!*args)
return false;
DeletedInfoList foundList;
if (!GetDeletedCharacterInfoList(foundList, args))
return false;
if (foundList.empty())
{
SendSysMessage(LANG_CHARACTER_DELETED_LIST_EMPTY);
return false;
}
SendSysMessage(LANG_CHARACTER_DELETED_DELETE);
HandleCharacterDeletedListHelper(foundList);
// Call the appropriate function to delete them (current account for deleted characters is 0)
for(DeletedInfoList::const_iterator itr = foundList.begin(); itr != foundList.end(); ++itr)
Player::DeleteFromDB(itr->lowguid, 0, false, true);
return true;
}
/**
* Handles the '.character deleted old' command, which completely deletes all deleted characters deleted with some days ago
*
* @see Player::DeleteOldCharacters
* @see Player::DeleteFromDB
* @see ChatHandler::HandleCharacterDeletedDeleteCommand
* @see ChatHandler::HandleCharacterDeletedListCommand
* @see ChatHandler::HandleCharacterDeletedRestoreCommand
*
* @param args the search string which either contains a player GUID or a part fo the character-name
*/
bool ChatHandler::HandleCharacterDeletedOldCommand(const char* args)
{
int32 keepDays = sWorld.getConfig(CONFIG_CHARDELETE_KEEP_DAYS);
char* px = strtok((char*)args, " ");
if (px)
{
if (!isNumeric(px))
return false;
keepDays = atoi(px);
if (keepDays < 0)
return false;
}
// config option value 0 -> disabled and can't be used
else if (keepDays <= 0)
return false;
Player::DeleteOldCharacters((uint32)keepDays);
return true;
}
bool ChatHandler::HandleCharacterEraseCommand(const char* args){
if(!*args)
return false;
@@ -202,7 +516,7 @@ bool ChatHandler::HandleCharacterDeleteCommand(const char* args)
std::string account_name;
accmgr.GetName (account_id,account_name);
Player::DeleteFromDB(character_guid, account_id, true);
Player::DeleteFromDB(character_guid, account_id, true, true);
PSendSysMessage(LANG_CHARACTER_DELETED,character_name.c_str(),GUID_LOPART(character_guid),account_name.c_str(), account_id);
return true;
}
@@ -2087,6 +2087,33 @@ Ra.Port = 3443
Ra.MinLevel = 3
Ra.Secure = 1
###################################################################################################################
# CharDelete.Method
# Character deletion behavior
# Default: 0 - Completely remove the character from the database
# 1 - Unlinking, the character gets unlinked from the account,
# the name gets freed up and appears as deleted ingame
#
# CharDelete.MinLevel
# Character gets deleted by CharDelete.Method=0 when the character
# hasn't the specified level yet.
# Default: 0 - For all characters the specified mode will be used
# 1+ - Only for players which have reached the specified level
# will be deleted by the specified mode.
# the rest will be deleted by CharDelete.Method=0
#
# CharDelete.KeepDays
# Define the amount of days for which the characters are kept in the database before
# they will be removed
# Default: 30
# 0 - Don't delete any characters, they stay in the database forever.
#
###################################################################################################################
CharDelete.Method = 0
CharDelete.MinLevel = 0
CharDelete.KeepDays = 30
###############################################################################
# CUSTOM SERVER OPTIONS
#