diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql index b5886e1199a..37b2b8d6ee4 100644 --- a/sql/base/auth_database.sql +++ b/sql/base/auth_database.sql @@ -2176,6 +2176,7 @@ INSERT INTO `rbac_permissions` VALUES (877,'Command: lookup quest id'), (878,'Command: debug questreset'), (879,'Command: debug poolstatus'), +(880,'Command: pdump copy'), (881,'Command: reload vehicle_template'), (882,'Command: reload spell_script_names'); /*!40000 ALTER TABLE `rbac_permissions` ENABLE KEYS */; @@ -2487,6 +2488,7 @@ INSERT INTO `updates` VALUES ('2020_08_14_00_auth.sql','DFB9B07A7846FC0E124EE4CC099E49FE5742FB66','ARCHIVED','2020-08-14 21:41:24',0), ('2020_08_26_00_auth.sql','D5EF787DECB41D898379588F101A0453B46F04D9','ARCHIVED','2020-08-26 21:00:34',0), ('2020_09_06_00_auth.sql','DC4B5D4C65EB138D5609F137799C3289B9CC2493','ARCHIVED','2020-09-06 00:00:00',0), +('2020_09_17_00_auth.sql','BBC0A8B2BBED38A57A83999909EB066753A893C5','ARCHIVED','2020-09-17 00:00:00',0), ('2020_09_25_00_auth.sql','3CCA78EF89223724BA6784A4F3783DED30416637','ARCHIVED','2020-09-25 19:52:40',0), ('2020_10_20_00_auth.sql','1835C5EFD5816DEF914F27E867C8C8D5E08B3F68','ARCHIVED','2020-10-20 21:36:49',0), ('2020_12_06_00_auth.sql','FA254400D3D7D53E9C350EABFEABFF4EC3AD40DA','ARCHIVED','2020-12-06 20:25:10',0), diff --git a/sql/updates/auth/master/2020_09_17_00_auth.sql b/sql/updates/auth/master/2020_09_17_00_auth.sql new file mode 100644 index 00000000000..9cc01ed837e --- /dev/null +++ b/sql/updates/auth/master/2020_09_17_00_auth.sql @@ -0,0 +1,3 @@ +-- +DELETE FROM `rbac_permissions` WHERE `id`= 880; +INSERT INTO `rbac_permissions` (`id`,`name`) VALUES (880, 'Command: pdump copy'); diff --git a/sql/updates/world/master/2022_02_26_02_world_2020_09_17_00_world.sql b/sql/updates/world/master/2022_02_26_02_world_2020_09_17_00_world.sql new file mode 100644 index 00000000000..96180f587f1 --- /dev/null +++ b/sql/updates/world/master/2022_02_26_02_world_2020_09_17_00_world.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `command` WHERE `name`='pdump copy'; +INSERT INTO `command` (`name`,`permission`,`help`) VALUES +('pdump copy',880,'Syntax: .pdump copy $playerNameOrGUID $account [$newname] [$newguid] +Copy character with name/guid $playerNameOrGUID into character list of $account with $newname, with first free or $newguid guid.'); diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp index b37cb909b0d..a818c38dc58 100644 --- a/src/server/game/Tools/PlayerDump.cpp +++ b/src/server/game/Tools/PlayerDump.cpp @@ -25,6 +25,7 @@ #include "Player.h" #include "World.h" #include +#include #include // static data @@ -809,7 +810,7 @@ bool PlayerDumpWriter::GetDump(ObjectGuid::LowType guid, std::string& dump) return true; } -DumpReturn PlayerDumpWriter::WriteDump(std::string const& file, ObjectGuid::LowType guid) +DumpReturn PlayerDumpWriter::WriteDumpToFile(std::string const& file, ObjectGuid::LowType guid) { if (sWorld->getBoolConfig(CONFIG_PDUMP_NO_PATHS)) if (strchr(file.c_str(), '\\') || strchr(file.c_str(), '/')) @@ -835,6 +836,14 @@ DumpReturn PlayerDumpWriter::WriteDump(std::string const& file, ObjectGuid::LowT return ret; } +DumpReturn PlayerDumpWriter::WriteDumpToString(std::string& dump, ObjectGuid::LowType guid) +{ + DumpReturn ret = DUMP_SUCCESS; + if (!GetDump(guid, dump)) + ret = DUMP_CHARACTER_DELETED; + return ret; +} + // Reading - High-level functions inline void FixNULLfields(std::string& line) { @@ -847,16 +856,12 @@ inline void FixNULLfields(std::string& line) } } -DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid) +DumpReturn PlayerDumpReader::LoadDump(std::istream& input, uint32 account, std::string name, ObjectGuid::LowType guid) { uint32 charcount = AccountMgr::GetCharactersCount(account); if (charcount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM)) return DUMP_TOO_MANY_CHARS; - FileHandle fin = GetFileHandle(file.c_str(), "r"); - if (!fin) - return DUMP_FILE_OPEN_ERROR; - std::string newguid, chraccount; // make sure the same guid doesn't already exist and is safe to use @@ -905,8 +910,7 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s std::map equipmentSetIds; uint64 equipmentSetGuidOffset = sObjectMgr->_equipmentSetGuid; - static size_t const BUFFER_SIZE = 32000; - char buf[BUFFER_SIZE] = { }; + std::string line; uint8 gender = GENDER_NONE; uint8 race = RACE_NONE; @@ -917,17 +921,8 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s size_t lineNumber = 0; CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - while (!feof(fin.get())) + while (std::getline(input, line)) { - if (!fgets(buf, BUFFER_SIZE, fin.get())) - { - if (feof(fin.get())) - break; - return DUMP_FILE_BROKEN; - } - - std::string line; - line.assign(buf); ++lineNumber; // skip empty strings @@ -1047,6 +1042,9 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s trans->Append(line.c_str()); } + if (input.fail() && !input.eof()) + return DUMP_FILE_BROKEN; + CharacterDatabase.CommitTransaction(trans); // in case of name conflict player has to rename at login anyway @@ -1064,3 +1062,17 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s return DUMP_SUCCESS; } + +DumpReturn PlayerDumpReader::LoadDumpFromString(std::string const& dump, uint32 account, std::string name, ObjectGuid::LowType guid) +{ + std::istringstream input(dump); + return LoadDump(input, account, name, guid); +} + +DumpReturn PlayerDumpReader::LoadDumpFromFile(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid) +{ + std::ifstream input(file); + if (!input) + return DUMP_FILE_OPEN_ERROR; + return LoadDump(input, account, name, guid); +} diff --git a/src/server/game/Tools/PlayerDump.h b/src/server/game/Tools/PlayerDump.h index 91c6741dc29..7858acff731 100644 --- a/src/server/game/Tools/PlayerDump.h +++ b/src/server/game/Tools/PlayerDump.h @@ -20,6 +20,7 @@ #include "ObjectGuid.h" #include +#include #include enum DumpTableType @@ -90,7 +91,8 @@ class TC_GAME_API PlayerDumpWriter : public PlayerDump PlayerDumpWriter() { } bool GetDump(ObjectGuid::LowType guid, std::string& dump); - DumpReturn WriteDump(std::string const& file, ObjectGuid::LowType guid); + DumpReturn WriteDumpToFile(std::string const& file, ObjectGuid::LowType guid); + DumpReturn WriteDumpToString(std::string& dump, ObjectGuid::LowType guid); private: bool AppendTable(StringTransaction& trans, ObjectGuid::LowType guid, TableStruct const& tableStruct, DumpTable const& dumpTable); @@ -108,7 +110,11 @@ class TC_GAME_API PlayerDumpReader : public PlayerDump public: PlayerDumpReader() { } - DumpReturn LoadDump(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid); + DumpReturn LoadDumpFromFile(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid); + DumpReturn LoadDumpFromString(std::string const& dump, uint32 account, std::string name, ObjectGuid::LowType guid); + + private: + DumpReturn LoadDump(std::istream& input, uint32 account, std::string name, ObjectGuid::LowType guid); }; #endif diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index a604764c667..4a6d872886f 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -48,6 +48,7 @@ public: { static std::vector pdumpCommandTable = { + { "copy", rbac::RBAC_PERM_COMMAND_PDUMP_COPY, true, &HandlePDumpCopyCommand, "" }, { "load", rbac::RBAC_PERM_COMMAND_PDUMP_LOAD, true, &HandlePDumpLoadCommand, "" }, { "write", rbac::RBAC_PERM_COMMAND_PDUMP_WRITE, true, &HandlePDumpWriteCommand, "" }, }; @@ -776,9 +777,84 @@ public: return true; } + static bool HandlePDumpCopyCommand(ChatHandler* handler, PlayerIdentifier player, AccountIdentifier account, Optional characterName, Optional characterGUID) + { + std::string name; + if (!ValidatePDumpTarget(handler, name, characterName, characterGUID)) + return false; + + std::string dump; + switch (PlayerDumpWriter().WriteDumpToString(dump, player.GetGUID().GetCounter())) + { + case DUMP_SUCCESS: + break; + case DUMP_CHARACTER_DELETED: + handler->PSendSysMessage(LANG_COMMAND_EXPORT_DELETED_CHAR); + handler->SetSentErrorMessage(true); + return false; + case DUMP_FILE_OPEN_ERROR: // this error code should not happen + default: + handler->PSendSysMessage(LANG_COMMAND_EXPORT_FAILED); + handler->SetSentErrorMessage(true); + return false; + } + + switch (PlayerDumpReader().LoadDumpFromString(dump, account, name, characterGUID.value_or(0))) + { + case DUMP_SUCCESS: + break; + case DUMP_TOO_MANY_CHARS: + handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, account.GetName().c_str(), account.GetID()); + handler->SetSentErrorMessage(true); + return false; + case DUMP_FILE_OPEN_ERROR: // this error code should not happen + case DUMP_FILE_BROKEN: // this error code should not happen + default: + handler->PSendSysMessage(LANG_COMMAND_IMPORT_FAILED); + handler->SetSentErrorMessage(true); + return false; + } + + // ToDo: use a new trinity_string for this commands + handler->PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS); + + return true; + } + static bool HandlePDumpLoadCommand(ChatHandler* handler, std::string fileName, AccountIdentifier account, Optional characterName, Optional characterGUID) { std::string name; + if (!ValidatePDumpTarget(handler, name, characterName, characterGUID)) + return false; + + switch (PlayerDumpReader().LoadDumpFromFile(fileName, account, name, characterGUID.value_or(0))) + { + case DUMP_SUCCESS: + handler->PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS); + break; + case DUMP_FILE_OPEN_ERROR: + handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileName.c_str()); + handler->SetSentErrorMessage(true); + return false; + case DUMP_FILE_BROKEN: + handler->PSendSysMessage(LANG_DUMP_BROKEN, fileName.c_str()); + handler->SetSentErrorMessage(true); + return false; + case DUMP_TOO_MANY_CHARS: + handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, account.GetName().c_str(), account.GetID()); + handler->SetSentErrorMessage(true); + return false; + default: + handler->PSendSysMessage(LANG_COMMAND_IMPORT_FAILED); + handler->SetSentErrorMessage(true); + return false; + } + + return true; + } + + static bool ValidatePDumpTarget(ChatHandler* handler, std::string& name, Optional characterName, Optional characterGUID) + { if (characterName) { name.assign(*characterName); @@ -808,35 +884,12 @@ public: } } - switch (PlayerDumpReader().LoadDump(fileName, account, name, characterGUID.value_or(0))) - { - case DUMP_SUCCESS: - handler->PSendSysMessage(LANG_COMMAND_IMPORT_SUCCESS); - break; - case DUMP_FILE_OPEN_ERROR: - handler->PSendSysMessage(LANG_FILE_OPEN_FAIL, fileName.c_str()); - handler->SetSentErrorMessage(true); - return false; - case DUMP_FILE_BROKEN: - handler->PSendSysMessage(LANG_DUMP_BROKEN, fileName.c_str()); - handler->SetSentErrorMessage(true); - return false; - case DUMP_TOO_MANY_CHARS: - handler->PSendSysMessage(LANG_ACCOUNT_CHARACTER_LIST_FULL, account.GetName().c_str(), account.GetID()); - handler->SetSentErrorMessage(true); - return false; - default: - handler->PSendSysMessage(LANG_COMMAND_IMPORT_FAILED); - handler->SetSentErrorMessage(true); - return false; - } - return true; } static bool HandlePDumpWriteCommand(ChatHandler* handler, std::string fileName, PlayerIdentifier player) { - switch (PlayerDumpWriter().WriteDump(fileName, player.GetGUID().GetCounter())) + switch (PlayerDumpWriter().WriteDumpToFile(fileName, player.GetGUID().GetCounter())) { case DUMP_SUCCESS: handler->PSendSysMessage(LANG_COMMAND_EXPORT_SUCCESS);