aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorariel- <ariel-@users.noreply.github.com>2017-05-26 12:06:38 -0300
committerShauren <shauren.trinity@gmail.com>2020-07-21 20:03:06 +0200
commit4b8c4f6064e2220a8449bbc187ca4ed644f7c899 (patch)
treef78cadf82bbef2558b003f637b612da621cdc47c
parentb20ede9061d088cbcb5095d75406bb26638c20e7 (diff)
Core/PlayerDump: general revamping
- Changed insert queries to include column names, check columns on dump load - Modify and search columns by name instead of storing magic offsets - Fully forward and backward compatible with previous dumps - Added better logs, C++11-ize code (cherry picked from commit 9bdbd696552f2bfe99790f5b3794c8cc3d806403) (cherry picked from commit 545f1a8385ee7e83472d1046aee5f10e65eeac49) (cherry picked from commit 4bf8802da17148f27a8da2857eaffb58b4727269) (cherry picked from commit 44381a78736f4e1b852e24b7a5766fa97a24881a) (cherry picked from commit f50575ca77fd683ee265998c436a86d544df213a)
-rw-r--r--src/server/game/Tools/PlayerDump.cpp1139
-rw-r--r--src/server/game/Tools/PlayerDump.h27
-rw-r--r--src/server/game/World/World.cpp7
3 files changed, 735 insertions, 438 deletions
diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp
index 16554652224..a5883b91c37 100644
--- a/src/server/game/Tools/PlayerDump.cpp
+++ b/src/server/game/Tools/PlayerDump.cpp
@@ -24,26 +24,80 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
+#include <boost/algorithm/string/find.hpp>
#include <sstream>
+// static data
+enum GuidType : uint8
+{
+ // 32 bit long guids
+ GUID_TYPE_ACCOUNT,
+ GUID_TYPE_MAIL,
+
+ // 64 bit long guids
+ GUID_TYPE_CHAR,
+ GUID_TYPE_EQUIPMENT_SET,
+ GUID_TYPE_ITEM,
+ GUID_TYPE_PET,
+
+ // special types
+ GUID_TYPE_NULL // set to null
+};
+
+// for RAII
+struct FileCloser
+{
+ void operator()(FILE* f) const
+ {
+ if (f)
+ fclose(f);
+ }
+};
+typedef std::unique_ptr<FILE, FileCloser> FileHandle;
+
+inline FileHandle GetFileHandle(char const* path, char const* mode)
+{
+ return FileHandle(fopen(path, mode), FileCloser());
+}
+
+struct BaseTable
+{
+ char const* TableName;
+ char const* PrimaryKey;
+ char const* PlayerGuid;
+
+ GuidType StoredType;
+};
+
+BaseTable const BaseTables[] =
+{
+ { "character_pet", "id", "owner", GUID_TYPE_PET },
+ { "mail", "id", "receiver", GUID_TYPE_MAIL },
+ { "item_instance", "guid", "owner_guid", GUID_TYPE_ITEM },
+
+ { "character_equipmentsets", "setguid", "guid", GUID_TYPE_EQUIPMENT_SET },
+ { "character_transmog_outfits", "setguid", "guid", GUID_TYPE_EQUIPMENT_SET }
+};
+
struct DumpTable
{
- char const* name;
- DumpTableType type;
+ char const* Name;
+ DumpTableType Type;
};
-DumpTable const dumpTables[] =
+DumpTable const DumpTables[] =
{
{ "characters", DTT_CHARACTER },
{ "character_account_data", DTT_CHAR_TABLE },
{ "character_achievement", DTT_CHAR_TABLE },
{ "character_achievement_progress", DTT_CHAR_TABLE },
{ "character_action", DTT_CHAR_TABLE },
- // { "character_aura", DTT_CHAR_TABLE }, /// @todo: reguid casterguid (full guid)
- // { "character_aura_effect", DTT_CHAR_TABLE }, /// @todo: reguid casterguid (full guid)
+ { "character_aura", DTT_CHAR_TABLE },
+ { "character_aura_effect", DTT_CHAR_TABLE },
{ "character_cuf_profiles", DTT_CHAR_TABLE },
{ "character_currency", DTT_CURRENCY },
{ "character_declinedname", DTT_CHAR_TABLE },
+ { "character_favorite_auctions", DTT_CHAR_TABLE },
{ "character_fishingsteps", DTT_CHAR_TABLE },
{ "character_garrison", DTT_CHAR_TABLE },
{ "character_garrison_blueprints", DTT_CHAR_TABLE },
@@ -55,15 +109,16 @@ DumpTable const dumpTables[] =
{ "character_inventory", DTT_INVENTORY },
{ "character_pet", DTT_PET },
{ "character_pet_declinedname", DTT_PET },
+ { "character_pvp_talent", DTT_CHAR_TABLE },
{ "character_queststatus", DTT_CHAR_TABLE },
{ "character_queststatus_daily", DTT_CHAR_TABLE },
{ "character_queststatus_monthly", DTT_CHAR_TABLE },
{ "character_queststatus_objectives", DTT_CHAR_TABLE },
{ "character_queststatus_objectives_criteria", DTT_CHAR_TABLE },
{ "character_queststatus_objectives_criteria_progress", DTT_CHAR_TABLE },
- { "character_queststatus_weekly", DTT_CHAR_TABLE },
- { "character_queststatus_seasonal", DTT_CHAR_TABLE },
{ "character_queststatus_rewarded", DTT_CHAR_TABLE },
+ { "character_queststatus_seasonal", DTT_CHAR_TABLE },
+ { "character_queststatus_weekly", DTT_CHAR_TABLE },
{ "character_reputation", DTT_CHAR_TABLE },
{ "character_skills", DTT_CHAR_TABLE },
{ "character_spell", DTT_CHAR_TABLE },
@@ -74,8 +129,8 @@ DumpTable const dumpTables[] =
/// @todo: character_void_storage
{ "mail", DTT_MAIL },
{ "mail_items", DTT_MAIL_ITEM }, // must be after mail
- // { "pet_aura", DTT_PET_TABLE }, // must be after character_pet /// @todo: reguid casterguid (full guid)
- // { "pet_aura_effect", DTT_PET_TABLE }, // must be after character_pet /// @todo: reguid casterguid (full guid)
+ { "pet_aura", DTT_PET_TABLE }, // must be after character_pet
+ { "pet_aura_effect", DTT_PET_TABLE }, // must be after character_pet
{ "pet_spell", DTT_PET_TABLE }, // must be after character_pet
{ "pet_spell_charges", DTT_PET_TABLE }, // must be after character_pet
{ "pet_spell_cooldown", DTT_PET_TABLE }, // must be after character_pet
@@ -84,99 +139,369 @@ DumpTable const dumpTables[] =
{ "character_gifts", DTT_ITEM_GIFT }, // must be after item_instance
{ "item_instance_artifact", DTT_ITEM_TABLE }, // must be after item_instance
{ "item_instance_artifact_powers", DTT_ITEM_TABLE }, // must be after item_instance
+ { "item_instance_azerite", DTT_ITEM_TABLE }, // must be after item_instance
+ { "item_instance_azerite_empowered", DTT_ITEM_TABLE }, // must be after item_instance
+ { "item_instance_azerite_milestone_power", DTT_ITEM_TABLE }, // must be after item_instance
+ { "item_instance_azerite_unlocked_essence", DTT_ITEM_TABLE }, // must be after item_instance
{ "item_instance_gems", DTT_ITEM_TABLE }, // must be after item_instance
{ "item_instance_modifiers", DTT_ITEM_TABLE }, // must be after item_instance
{ "item_instance_transmog", DTT_ITEM_TABLE }, // must be after item_instance
};
-size_t constexpr dumpTablesCount = std::extent<decltype(dumpTables)>::value;
-namespace
+uint32 const DUMP_TABLE_COUNT = std::extent<decltype(DumpTables)>::value;
+
+// helper class to dump sql queries to a printable string
+class StringTransaction
{
- std::unordered_map<std::pair<std::string, std::string>, uint32> ColumnDefinitions;
-}
+ public:
+ StringTransaction() : _buf() { }
+
+ void Append(char const* sql)
+ {
+ std::ostringstream oss;
+ oss << sql << '\n';
+ _buf += oss.str();
+ }
+
+ char const* GetBuffer() const
+ {
+ return _buf.c_str();
+ }
+
+ private:
+ std::string _buf;
+};
-void PlayerDump::InitializeColumnDefinition()
+// dynamic data, loaded at startup
+struct TableField
{
- uint32 oldMSTime = getMSTime();
+ std::string FieldName;
- for (size_t i = 0; i < dumpTablesCount; ++i)
- {
- QueryResult result = CharacterDatabase.PQuery("SELECT column_name, ordinal_position FROM information_schema.columns WHERE table_schema=SCHEMA() AND table_name = \"%s\"", dumpTables[i].name);
- WPFatal(result, "Error while initializing PlayerDump ColumnDefinitions. Table \"%s\" does not exist.", dumpTables[i].name);
+ GuidType FieldGuidType = GUID_TYPE_ACCOUNT;
+ bool IsDependentField = false;
+ bool IsBinaryField = false;
+};
- Field* fields = result->Fetch();
+struct TableStruct
+{
+ std::string TableName;
+ std::string WhereFieldName;
+ std::vector<TableField> TableFields;
- do
- {
- ColumnDefinitions[std::make_pair(std::string(dumpTables[i].name), fields[0].GetString())] = uint32(fields[1].GetUInt64());
- } while (result->NextRow());
- }
+ // for lookup
+ std::unordered_map<std::string /*fieldName*/, int32 /*index*/> FieldIndices;
+};
+
+std::vector<TableStruct> CharacterTables;
- TC_LOG_INFO("server.loading", ">> Initialized " SZFMTD " PlayerDump ColumnDefinitions in %u ms.", ColumnDefinitions.size(), GetMSTimeDiffToNow(oldMSTime));
+inline bool StringsEqualCaseInsensitive(std::string const& left, std::string const& right)
+{
+ std::string upperLeftString = left;
+ ASSERT(Utf8ToUpperOnlyLatin(upperLeftString));
+
+ std::string upperRightString = right;
+ ASSERT(Utf8ToUpperOnlyLatin(upperRightString));
+
+ return upperLeftString == upperRightString;
}
-// Low level functions
-static uint32 GetColNumber(std::string const& table, std::string const& column)
+inline auto FindColumnByName(TableStruct& tableStruct, std::string const& columnName) -> decltype(tableStruct.TableFields.begin())
{
- auto itr = ColumnDefinitions.find(std::make_pair(table, column));
- ASSERT(itr != ColumnDefinitions.end(), "ColumnDefinition (Table: \"%s\", Column: \"%s\") not found.", table.c_str(), column.c_str());
+ return std::find_if(tableStruct.TableFields.begin(), tableStruct.TableFields.end(), [columnName](TableField const& tableField) -> bool
+ {
+ return StringsEqualCaseInsensitive(tableField.FieldName, columnName);
+ });
+}
+
+inline int32 GetColumnIndexByName(TableStruct const& tableStruct, std::string const& columnName)
+{
+ auto itr = tableStruct.FieldIndices.find(columnName);
+ if (itr == tableStruct.FieldIndices.end())
+ return -1;
+
return itr->second;
}
-static bool FindTokNth(std::string const& str, uint32 n, std::string::size_type& s, std::string::size_type& e)
+inline void MarkDependentColumn(TableStruct& tableStruct, std::string const& columnName, GuidType dependentType)
{
- s = e = 0;
+ auto itr = FindColumnByName(tableStruct, columnName);
+ if (itr == tableStruct.TableFields.end())
+ {
+ TC_LOG_FATAL("server.loading", "Column `%s` declared in table `%s` marked as dependent but doesn't exist, PlayerDump will not work properly, please update table definitions",
+ columnName.c_str(), tableStruct.TableName.c_str());
+ ABORT();
+ return;
+ }
- uint32 i = 1;
- for (; s < str.size() && i < n; ++s)
- if (str[s] == ' ')
- ++i;
+ if (itr->IsDependentField)
+ {
+ TC_LOG_FATAL("server.loading", "Attempt to mark column `%s` in table `%s` as dependent column but already marked! please check your code.",
+ columnName.c_str(), tableStruct.TableName.c_str());
+ ABORT();
+ return;
+ }
- if (i < n)
- return false;
+ itr->IsDependentField = true;
+ itr->FieldGuidType = dependentType;
+}
- e = str.find(' ', s);
+inline void MarkWhereField(TableStruct& tableStruct, std::string const& whereField)
+{
+ ASSERT(tableStruct.WhereFieldName.empty());
- return e != std::string::npos;
+ auto whereFieldItr = FindColumnByName(tableStruct, whereField);
+ if (whereFieldItr == tableStruct.TableFields.end())
+ {
+ TC_LOG_FATAL("server.loading", "Column name `%s` set as 'WHERE' column for table `%s` doesn't exist. PlayerDump won't work properly",
+ whereField.c_str(), tableStruct.TableName.c_str());
+ ABORT();
+ return;
+ }
+
+ tableStruct.WhereFieldName = whereField;
}
-std::string GetTokNth(std::string const& str, uint32 n)
+inline void AssertBaseTable(BaseTable const& baseTable)
{
- std::string::size_type s = 0, e = 0;
- if (!FindTokNth(str, n, s, e))
- return "";
+ auto itr = std::find_if(CharacterTables.begin(), CharacterTables.end(), [baseTable](TableStruct const& tableStruct) -> bool
+ {
+ return StringsEqualCaseInsensitive(tableStruct.TableName, baseTable.TableName);
+ });
- return str.substr(s, e - s);
+ ASSERT(itr != CharacterTables.end());
+
+ auto columnItr = FindColumnByName(*itr, baseTable.PrimaryKey);
+ ASSERT(columnItr != itr->TableFields.end());
+
+ columnItr = FindColumnByName(*itr, baseTable.PlayerGuid);
+ ASSERT(columnItr != itr->TableFields.end());
}
-bool FindNth(std::string const& str, uint32 n, std::string::size_type& s, std::string::size_type& e)
+void PlayerDump::InitializeTables()
{
- s = str.find("VALUES ('") + 9;
- if (s == std::string::npos)
- return false;
+ uint32 oldMSTime = getMSTime();
- do
+ for (DumpTable const& dumpTable : DumpTables)
{
- e = str.find('\'', s);
- if (e == std::string::npos)
- return false;
- } while (str[e - 1] == '\\');
+ TableStruct t;
+ t.TableName = dumpTable.Name;
+
+ QueryResult result = CharacterDatabase.PQuery("DESC %s", dumpTable.Name);
+ // prepared statement is correct (checked at startup) so table must exist
+ ASSERT(result);
+
+ int32 i = 0;
+ do
+ {
+ std::string columnName = (*result)[0].GetString();
+ std::string typeName = (*result)[1].GetString();
+ t.FieldIndices.emplace(columnName, i++);
+
+ TableField f;
+ f.FieldName = columnName;
+ f.IsBinaryField = !boost::ifind_first(typeName, "binary").empty() || !boost::ifind_first(typeName, "blob").empty();
+
+ ASSERT(Utf8ToUpperOnlyLatin(columnName));
- for (uint32 i = 1; i < n; ++i)
+ t.TableFields.emplace_back(std::move(f));
+ } while (result->NextRow());
+
+ switch (dumpTable.Type)
+ {
+ case DTT_CHARACTER:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ MarkDependentColumn(t, "account", GUID_TYPE_ACCOUNT);
+
+ MarkDependentColumn(t, "deleteInfos_Account", GUID_TYPE_NULL);
+ MarkDependentColumn(t, "deleteInfos_Name", GUID_TYPE_NULL);
+ MarkDependentColumn(t, "deleteDate", GUID_TYPE_NULL);
+ break;
+ case DTT_CHAR_TABLE:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ break;
+ case DTT_CURRENCY:
+ MarkWhereField(t, "CharacterGuid");
+
+ MarkDependentColumn(t, "CharacterGuid", GUID_TYPE_CHAR);
+ break;
+ case DTT_EQSET_TABLE:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ MarkDependentColumn(t, "setguid", GUID_TYPE_EQUIPMENT_SET);
+
+ // item0 - item18
+ for (uint32 j = 0; j < EQUIPMENT_SLOT_END; ++j)
+ {
+ std::string itColumn = Trinity::StringFormat("item%u", j);
+ MarkDependentColumn(t, itColumn, GUID_TYPE_ITEM);
+ }
+ break;
+ case DTT_INVENTORY:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ MarkDependentColumn(t, "bag", GUID_TYPE_ITEM);
+ MarkDependentColumn(t, "item", GUID_TYPE_ITEM);
+ break;
+ case DTT_CHAR_TRANSMOG:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ MarkDependentColumn(t, "setguid", GUID_TYPE_EQUIPMENT_SET);
+ break;
+ case DTT_MAIL:
+ MarkWhereField(t, "receiver");
+
+ MarkDependentColumn(t, "id", GUID_TYPE_MAIL);
+ MarkDependentColumn(t, "receiver", GUID_TYPE_CHAR);
+ break;
+ case DTT_MAIL_ITEM:
+ MarkWhereField(t, "mail_id");
+
+ MarkDependentColumn(t, "mail_id", GUID_TYPE_MAIL);
+ MarkDependentColumn(t, "item_guid", GUID_TYPE_ITEM);
+ MarkDependentColumn(t, "receiver", GUID_TYPE_CHAR);
+ break;
+ case DTT_ITEM:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_ITEM);
+ MarkDependentColumn(t, "owner_guid", GUID_TYPE_CHAR);
+ break;
+ case DTT_ITEM_GIFT:
+ MarkWhereField(t, "item_guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_CHAR);
+ MarkDependentColumn(t, "item_guid", GUID_TYPE_ITEM);
+ break;
+ case DTT_ITEM_TABLE:
+ MarkWhereField(t, "itemGuid");
+
+ MarkDependentColumn(t, "itemGuid", GUID_TYPE_ITEM);
+ break;
+ case DTT_PET:
+ MarkWhereField(t, "owner");
+
+ MarkDependentColumn(t, "id", GUID_TYPE_PET);
+ MarkDependentColumn(t, "owner", GUID_TYPE_CHAR);
+ break;
+ case DTT_PET_TABLE:
+ MarkWhereField(t, "guid");
+
+ MarkDependentColumn(t, "guid", GUID_TYPE_PET);
+ break;
+ default:
+ TC_LOG_FATAL("server.loading", "Wrong dump table type %u, probably added a new table type without updating code", uint32(dumpTable.Type));
+ ABORT();
+ return;
+ }
+
+ CharacterTables.emplace_back(std::move(t));
+ }
+
+ // perform some sanity checks
+ for (TableStruct const& tableStruct : CharacterTables)
+ {
+ if (tableStruct.WhereFieldName.empty())
+ {
+ TC_LOG_FATAL("server.loading", "Table `%s` defined in player dump doesn't have a WHERE query field", tableStruct.TableName.c_str());
+ ABORT();
+ }
+ }
+
+ for (BaseTable const& baseTable : BaseTables)
+ AssertBaseTable(baseTable);
+
+ ASSERT(CharacterTables.size() == DUMP_TABLE_COUNT);
+
+ TC_LOG_INFO("server.loading", ">> Initialized tables for PlayerDump in %u ms.", GetMSTimeDiffToNow(oldMSTime));
+}
+
+// Low level functions
+inline bool FindColumn(TableStruct const& ts, std::string const& str, std::string const& column, std::string::size_type& s, std::string::size_type& e)
+{
+ int32 columnIndex = GetColumnIndexByName(ts, column);
+ if (columnIndex == -1)
+ return false;
+
+ // array indices start at 0, compensate
+ ++columnIndex;
+
+ s = str.find("VALUES (");
+ if (s == std::string::npos)
+ return false;
+ s += 8;
+ e = s;
+
+ bool isQuoted = str[s] == '\'';
+ if (isQuoted)
{
+ ++s;
+ ++e;
+ // find first unescaped quote
do
{
- s = e + 4;
- e = str.find('\'', s);
+ e = str.find('\'', e);
if (e == std::string::npos)
return false;
- } while (str[e - 1] == '\\');
+ if (str[e - 1] == '\\')
+ continue;
+ if (e + 1 < str.length() && str[e + 1] == '\'')
+ {
+ ++e;
+ continue;
+ }
+ break;
+ } while (true);
+ }
+ else
+ e = str.find_first_of(",)", e);
+
+ for (int32 i = 1; i < columnIndex; ++i)
+ {
+ // if previous value was quoted, move old e to comma
+ if (isQuoted)
+ ++e;
+
+ // move past ", "
+ s = e + 2;
+ e = s;
+ isQuoted = str[s] == '\'';
+ if (isQuoted)
+ {
+ ++s;
+ ++e;
+ // find first unescaped quote
+ do
+ {
+ e = str.find('\'', e);
+ if (e == std::string::npos)
+ return false;
+ if (str[e - 1] == '\\')
+ continue;
+ if (e + 1 < str.length() && str[e + 1] == '\'')
+ {
+ ++e;
+ continue;
+ }
+ break;
+ } while (str[e - 1] == '\\');
+ }
+ else
+ e = str.find_first_of(",)", e);
}
+
return true;
}
-std::string GetTableName(std::string const& str)
+inline std::string GetTableName(std::string const& str)
{
+ // length of "INSERT INTO `"
static std::string::size_type const s = 13;
std::string::size_type e = str.find('`', s);
if (e == std::string::npos)
@@ -185,249 +510,316 @@ std::string GetTableName(std::string const& str)
return str.substr(s, e - s);
}
-bool ChangeNth(std::string& str, uint32 n, char const* with, bool insert = false, bool allowZero = false)
+inline bool ValidateFields(TableStruct const& ts, std::string const& str, size_t lineNumber)
{
- std::string::size_type s, e;
- if (!FindNth(str, n, s, e))
+ std::string::size_type s = str.find("` VALUES (");
+ if (s != std::string::npos) // old dump format (no column names)
+ return true;
+
+ // new format has insert with columns, need validation else we risk executing an invalid query
+ s = str.find("` (`");
+ if (s == std::string::npos)
+ {
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line " UI64FMTD ") dump format not recognized.", lineNumber);
return false;
+ }
+ s += 4;
- if (allowZero && str.substr(s, e - s) == "0")
- return true; // not an error
+ std::string::size_type valPos = str.find("VALUES ('");
+ std::string::size_type e = str.find('`', s);
+ if (e == std::string::npos || valPos == std::string::npos)
+ {
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line " UI64FMTD ") unexpected end of line", lineNumber);
+ return false;
+ }
- if (!insert)
- str.replace(s, e - s, with);
- else
- str.insert(s, with);
+ do
+ {
+ std::string column = str.substr(s, e - s);
+ int32 columnIndex = GetColumnIndexByName(ts, column);
+ if (columnIndex == -1)
+ {
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line " UI64FMTD ") unknown column name `%s` for table `%s`, aborting due to incompatible DB structure.", lineNumber, column.c_str(), ts.TableName.c_str());
+ return false;
+ }
+
+ // length of "`, `"
+ s = e + 4;
+ e = str.find('`', s);
+ } while (e < valPos);
return true;
}
-std::string GetNth(std::string& str, uint32 n)
+inline bool ChangeColumn(TableStruct const& ts, std::string& str, std::string const& column, std::string const& with, bool allowZero = false)
{
std::string::size_type s, e;
- if (!FindNth(str, n, s, e))
- return "";
-
- return str.substr(s, e-s);
-}
-
-bool ChangeTokNth(std::string& str, uint32 n, char const* with, bool insert = false, bool allowZero = false)
-{
- std::string::size_type s = 0, e = 0;
- if (!FindTokNth(str, n, s, e))
+ if (!FindColumn(ts, str, column, s, e))
return false;
if (allowZero && str.substr(s, e - s) == "0")
return true; // not an error
- if (!insert)
- str.replace(s, e-s, with);
- else
- str.insert(s, with);
-
+ str.replace(s, e - s, with);
return true;
}
-ObjectGuid::LowType RegisterNewGuid(ObjectGuid::LowType oldGuid, PlayerDump::DumpGuidMap& guidMap, ObjectGuid::LowType guidOffset)
+inline std::string GetColumn(TableStruct const& ts, std::string& str, std::string const& column)
{
- PlayerDumpWriter::DumpGuidMap::const_iterator itr = guidMap.find(oldGuid);
+ std::string::size_type s, e;
+ if (!FindColumn(ts, str, column, s, e))
+ return "";
+
+ return str.substr(s, e - s);
+}
+
+template <typename T, template<class, class, class...> class MapType, class... Rest>
+inline T RegisterNewGuid(T oldGuid, MapType<T, T, Rest...>& guidMap, T guidOffset)
+{
+ auto itr = guidMap.find(oldGuid);
if (itr != guidMap.end())
return itr->second;
- ObjectGuid::LowType newguid = guidOffset + guidMap.size();
- guidMap[oldGuid] = newguid;
+ T newguid = guidOffset + T(guidMap.size());
+ guidMap.emplace(oldGuid, newguid);
return newguid;
}
-bool ChangeGuid(std::string& str, uint32 n, PlayerDump::DumpGuidMap& guidMap, ObjectGuid::LowType guidOffset, bool allowZero = false)
+template <typename T, template<class, class, class...> class MapType, class... Rest>
+inline bool ChangeGuid(TableStruct const& ts, std::string& str, std::string const& column, MapType<T, T, Rest...>& guidMap, T guidOffset, bool allowZero = false)
{
- ObjectGuid::LowType oldGuid = strtoull(GetNth(str, n).c_str(), nullptr, 10);
+ T oldGuid(atoull(GetColumn(ts, str, column).c_str()));
if (allowZero && !oldGuid)
return true; // not an error
- char chritem[21];
- ObjectGuid::LowType newGuid = RegisterNewGuid(oldGuid, guidMap, guidOffset);
- snprintf(chritem, 21, UI64FMTD, newGuid);
+ std::string chritem;
+ T newGuid = RegisterNewGuid(oldGuid, guidMap, guidOffset);
+ chritem = std::to_string(newGuid);
- return ChangeNth(str, n, chritem, false, allowZero);
+ return ChangeColumn(ts, str, column, chritem, allowZero);
}
-std::string CreateDumpString(char const* tableName, QueryResult result)
+inline void AppendTableDump(StringTransaction& trans, TableStruct const& tableStruct, QueryResult result)
{
- if (!tableName || !result) return "";
- std::ostringstream ss;
- ss << "INSERT INTO `" << tableName << "` VALUES (";
- Field* fields = result->Fetch();
- for (uint32 i = 0; i < result->GetFieldCount(); ++i)
+ if (!result)
+ return;
+
+ do
{
- if (i == 0) ss << '\'';
- else ss << ", '";
+ std::ostringstream ss;
+ ss << "INSERT INTO `" << tableStruct.TableName << "` (";
+ for (auto itr = tableStruct.TableFields.begin(); itr != tableStruct.TableFields.end();)
+ {
+ ss << '`' << itr->FieldName << '`';
+ ++itr;
- std::string s = fields[i].GetString();
- CharacterDatabase.EscapeString(s);
- ss << s;
+ if (itr != tableStruct.TableFields.end())
+ ss << ", ";
+ }
+ ss << ") VALUES (";
- ss << '\'';
- }
- ss << ");";
- return ss.str();
+ uint32 const fieldSize = uint32(tableStruct.TableFields.size());
+ Field* fields = result->Fetch();
+
+ for (uint32 i = 0; i < fieldSize;)
+ {
+ if (fields[i].IsNull())
+ ss << "'NULL'";
+ else
+ {
+ if (!tableStruct.TableFields[i].IsBinaryField)
+ {
+ std::string s(fields[i].GetString());
+ CharacterDatabase.EscapeString(s);
+ ss << '\'' << s << '\'';
+ }
+ else
+ {
+ std::vector<uint8> b(fields[i].GetBinary());
+ ss << "0x" << ByteArrayToHexStr(b.data(), b.size());
+ }
+ }
+
+ ++i;
+ if (i != fieldSize)
+ ss << ", ";
+ }
+ ss << ");";
+
+ trans.Append(ss.str().c_str());
+ } while (result->NextRow());
}
-std::string PlayerDumpWriter::GenerateWhereStr(char const* field, ObjectGuid::LowType guid)
+inline std::string GenerateWhereStr(std::string const& field, ObjectGuid::LowType guid)
{
- std::ostringstream wherestr;
- wherestr << field << " = '" << guid << '\'';
- return wherestr.str();
+ std::ostringstream whereStr;
+ whereStr << field << " = '" << guid << '\'';
+ return whereStr.str();
}
-std::string PlayerDumpWriter::GenerateWhereStr(char const* field, DumpGuidSet const& guids, DumpGuidSet::const_iterator& itr)
+template <typename T, template<class, class...> class SetType, class... Rest>
+inline std::string GenerateWhereStr(std::string const& field, SetType<T, Rest...> const& guidSet)
{
- std::ostringstream wherestr;
- wherestr << field << " IN ('";
- for (; itr != guids.end();)
+ std::ostringstream whereStr;
+ whereStr << field << " IN ('";
+ for (auto itr = guidSet.begin(); itr != guidSet.end();)
{
- wherestr << *itr;
+ whereStr << *itr;
++itr;
- if (wherestr.str().size() > MAX_QUERY_LEN - 50) // near to max query
+ if (whereStr.str().size() > MAX_QUERY_LEN - 50) // near to max query
break;
- if (itr != guids.end())
- wherestr << "', '";
+ if (itr != guidSet.end())
+ whereStr << "','";
}
- wherestr << "')";
- return wherestr.str();
-}
-
-void StoreGUID(QueryResult result, uint32 field, PlayerDump::DumpGuidSet &guids)
-{
- Field* fields = result->Fetch();
- ObjectGuid::LowType guid = fields[field].GetUInt64();
- if (guid)
- guids.insert(guid);
-}
-
-void StoreGUID(QueryResult result, uint32 data, uint32 field, PlayerDump::DumpGuidSet& guids)
-{
- Field* fields = result->Fetch();
- std::string dataStr = fields[data].GetString();
- ObjectGuid::LowType guid = strtoull(GetTokNth(dataStr, field).c_str(), nullptr, 10);
- if (guid)
- guids.insert(guid);
+ whereStr << "')";
+ return whereStr.str();
}
// Writing - High-level functions
-bool PlayerDumpWriter::DumpTable(std::string& dump, ObjectGuid::LowType guid, char const* tableFrom, char const* tableTo, DumpTableType type)
+void PlayerDumpWriter::PopulateGuids(ObjectGuid::LowType guid)
{
- DumpGuidSet const* guids = nullptr;
- char const* fieldname = nullptr;
-
- switch (type)
+ for (BaseTable const& baseTable : BaseTables)
{
- case DTT_CURRENCY: fieldname = "CharacterGuid"; break;
- case DTT_ITEM: fieldname = "guid"; guids = &items; break;
- case DTT_ITEM_GIFT: fieldname = "item_guid"; guids = &items; break;
- case DTT_ITEM_TABLE: fieldname = "itemGuid"; guids = &items; break;
- case DTT_PET: fieldname = "owner"; break;
- case DTT_PET_TABLE: fieldname = "guid"; guids = &pets; break;
- case DTT_MAIL: fieldname = "receiver"; break;
- case DTT_MAIL_ITEM: fieldname = "mail_id"; guids = &mails; break;
- default: fieldname = "guid"; break;
- }
-
- // for guid set stop if set is empty
- if (guids && guids->empty())
- return true; // nothing to do
-
- // setup for guids case start position
- DumpGuidSet::const_iterator guidsItr;
- if (guids)
- guidsItr = guids->begin();
-
- do
- {
- std::string wherestr;
-
- if (guids) // set case, get next guids string
- wherestr = GenerateWhereStr(fieldname, *guids, guidsItr);
- else // not set case, get single guid string
- wherestr = GenerateWhereStr(fieldname, guid);
+ switch (baseTable.StoredType)
+ {
+ case GUID_TYPE_ITEM:
+ case GUID_TYPE_MAIL:
+ case GUID_TYPE_PET:
+ case GUID_TYPE_EQUIPMENT_SET:
+ break;
+ default:
+ return;
+ }
- QueryResult result = CharacterDatabase.PQuery("SELECT * FROM %s WHERE %s", tableFrom, wherestr.c_str());
+ std::string whereStr = GenerateWhereStr(baseTable.PlayerGuid, guid);
+ QueryResult result = CharacterDatabase.PQuery("SELECT %s FROM %s WHERE %s", baseTable.PrimaryKey, baseTable.TableName, whereStr.c_str());
if (!result)
- return true;
+ continue;
do
{
- // collect guids
- switch (type)
+ switch (baseTable.StoredType)
{
- case DTT_INVENTORY:
- StoreGUID(result, GetColNumber(tableFrom, "item") - 1, items); // item guid collection (character_inventory.item)
- break;
- case DTT_PET:
- StoreGUID(result, GetColNumber(tableFrom, "id") - 1, pets); // pet petnumber collection (character_pet.id)
+ case GUID_TYPE_ITEM:
+ if (ObjectGuid::LowType guid = (*result)[0].GetUInt64())
+ _items.insert(guid);
break;
- case DTT_MAIL:
- StoreGUID(result, GetColNumber(tableFrom, "id") - 1, mails); // mail id collection (mail.id)
+ case GUID_TYPE_MAIL:
+ if (uint32 guid = (*result)[0].GetUInt32())
+ _mails.insert(guid);
break;
- case DTT_MAIL_ITEM:
- StoreGUID(result, GetColNumber(tableFrom, "item_guid") - 1, items); // item guid collection (mail_items.item_guid)
+ case GUID_TYPE_PET:
+ if (ObjectGuid::LowType guid = (*result)[0].GetUInt64())
+ _pets.insert(guid);
break;
- case DTT_CHARACTER:
- {
- if (result->GetFieldCount() <= GetColNumber(tableFrom, "deleteInfos_Account") - 1) // avoid crashes on next check
- {
- TC_LOG_FATAL("misc", "PlayerDumpWriter::DumpTable - Trying to access non-existing or wrong positioned field (`deleteInfos_Account`) in `characters` table.");
- return false;
- }
-
- if (result->Fetch()[GetColNumber(tableFrom, "deleteInfos_Account") - 1].GetUInt32()) // characters.deleteInfos_Account - if filled error
- return false;
+ case GUID_TYPE_EQUIPMENT_SET:
+ if (uint64 eqSetId = (*result)[0].GetUInt64())
+ _itemSets.insert(eqSetId);
break;
- }
default:
break;
}
+ } while (result->NextRow());
+ }
+}
- dump += CreateDumpString(tableTo, result);
- dump += "\n";
- }
- while (result->NextRow());
+bool PlayerDumpWriter::AppendTable(StringTransaction& trans, ObjectGuid::LowType guid, TableStruct const& tableStruct, DumpTable const& dumpTable)
+{
+ std::string whereStr;
+ switch (dumpTable.Type)
+ {
+ case DTT_ITEM:
+ case DTT_ITEM_GIFT:
+ case DTT_ITEM_TABLE:
+ if (_items.empty())
+ return true;
+
+ whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _items);
+ break;
+ case DTT_PET_TABLE:
+ if (_pets.empty())
+ return true;
+
+ whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _pets);
+ break;
+ case DTT_MAIL_ITEM:
+ if (_mails.empty())
+ return true;
+
+ whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _mails);
+ break;
+ case DTT_EQSET_TABLE:
+ case DTT_CHAR_TRANSMOG:
+ if (_itemSets.empty())
+ return true;
+
+ whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _itemSets);
+ break;
+ default:
+ // not set case, get single guid string
+ whereStr = GenerateWhereStr(tableStruct.WhereFieldName, guid);
+ break;
+ }
+
+ QueryResult result = CharacterDatabase.PQuery("SELECT * FROM %s WHERE %s", dumpTable.Name, whereStr.c_str());
+ switch (dumpTable.Type)
+ {
+ case DTT_CHARACTER:
+ if (result)
+ {
+ // characters.deleteInfos_Account - if filled error
+ int32 index = GetColumnIndexByName(tableStruct, "deleteInfos_Account");
+ ASSERT(index != -1); // checked at startup
+
+ if ((*result)[index].GetUInt32())
+ return false;
+ }
+ break;
+ default:
+ break;
}
- while (guids && guidsItr != guids->end()); // not set case iterate single time, set case iterate for all guids
+
+ AppendTableDump(trans, tableStruct, result);
return true;
}
-bool PlayerDumpWriter::GetDump(ObjectGuid::LowType guid, std::string &dump)
+bool PlayerDumpWriter::GetDump(ObjectGuid::LowType guid, std::string& dump)
{
dump = "IMPORTANT NOTE: THIS DUMPFILE IS MADE FOR USE WITH THE 'PDUMP' COMMAND ONLY - EITHER THROUGH INGAME CHAT OR ON CONSOLE!\n";
dump += "IMPORTANT NOTE: DO NOT apply it directly - it will irreversibly DAMAGE and CORRUPT your database! You have been warned!\n\n";
- for (size_t i = 0; i < dumpTablesCount; ++i)
- if (!DumpTable(dump, guid, dumpTables[i].name, dumpTables[i].name, dumpTables[i].type))
+ StringTransaction trans;
+
+ // collect guids
+ PopulateGuids(guid);
+ for (uint32 i = 0; i < DUMP_TABLE_COUNT; ++i)
+ if (!AppendTable(trans, guid, CharacterTables[i], DumpTables[i]))
return false;
+ dump += trans.GetBuffer();
+
/// @todo Add instance/group..
/// @todo Add a dump level option to skip some non-important tables
return true;
}
-DumpReturn PlayerDumpWriter::WriteDump(const std::string& file, ObjectGuid::LowType guid)
+DumpReturn PlayerDumpWriter::WriteDump(std::string const& file, ObjectGuid::LowType guid)
{
if (sWorld->getBoolConfig(CONFIG_PDUMP_NO_PATHS))
if (strchr(file.c_str(), '\\') || strchr(file.c_str(), '/'))
return DUMP_FILE_OPEN_ERROR;
if (sWorld->getBoolConfig(CONFIG_PDUMP_NO_OVERWRITE))
- if (FILE* f = fopen(file.c_str(), "r"))
- {
- fclose(f);
+ {
+ // check if file exists already
+ if (GetFileHandle(file.c_str(), "r"))
return DUMP_FILE_OPEN_ERROR;
- }
+ }
- FILE* fout = fopen(file.c_str(), "w");
+ FileHandle fout = GetFileHandle(file.c_str(), "w");
if (!fout)
return DUMP_FILE_OPEN_ERROR;
@@ -436,22 +828,19 @@ DumpReturn PlayerDumpWriter::WriteDump(const std::string& file, ObjectGuid::LowT
if (!GetDump(guid, dump))
ret = DUMP_CHARACTER_DELETED;
- fprintf(fout, "%s\n", dump.c_str());
- fclose(fout);
+ fprintf(fout.get(), "%s", dump.c_str());
return ret;
}
// Reading - High-level functions
-#define ROLLBACK(DR) {fclose(fin); return (DR);}
-
-void fixNULLfields(std::string& line)
+inline void FixNULLfields(std::string& line)
{
- static std::string const nullString("'NULL'");
- size_t pos = line.find(nullString);
+ static std::string const NullString("'NULL'");
+ size_t pos = line.find(NullString);
while (pos != std::string::npos)
{
- line.replace(pos, nullString.length(), "NULL");
- pos = line.find(nullString);
+ line.replace(pos, NullString.length(), "NULL");
+ pos = line.find(NullString);
}
}
@@ -461,11 +850,11 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
if (charcount >= sWorld->getIntConfig(CONFIG_CHARACTERS_PER_REALM))
return DUMP_TOO_MANY_CHARS;
- FILE* fin = fopen(file.c_str(), "r");
+ FileHandle fin = GetFileHandle(file.c_str(), "r");
if (!fin)
return DUMP_FILE_OPEN_ERROR;
- char newguid[20], chraccount[20];
+ std::string newguid, chraccount;
// make sure the same guid doesn't already exist and is safe to use
bool incHighest = true;
@@ -473,9 +862,8 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_GUID);
stmt->setUInt64(0, guid);
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
- if (result)
+ if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
guid = sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed(); // use first free if exists
else
incHighest = false;
@@ -491,262 +879,167 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
stmt->setString(0, name);
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
- if (result)
+ if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
name.clear(); // use the one from the dump
}
else
name.clear();
// name encoded or empty
+ newguid = std::to_string(guid);
+ chraccount = std::to_string(account);
+
+ std::map<ObjectGuid::LowType, ObjectGuid::LowType> items;
+ ObjectGuid::LowType itemLowGuidOffset = sObjectMgr->GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed();
- snprintf(newguid, 20, UI64FMTD, guid);
- snprintf(chraccount, 20, "%u", account);
+ std::map<uint32, uint32> mails;
+ uint32 mailLowGuidOffset = sObjectMgr->_mailId;
- DumpGuidMap items;
- DumpGuidMap mails;
- char buf[32000] = "";
+ std::map<uint32, uint32> petIds;
+ uint32 petLowGuidOffset = sObjectMgr->_hiPetNumber;
- typedef std::map<uint32 /*old*/, uint32 /*new*/> PetIds;
- PetIds petIds;
+ std::map<uint64, uint64> equipmentSetIds;
+ uint64 equipmentSetGuidOffset = sObjectMgr->_equipmentSetGuid;
+
+ static size_t const BUFFER_SIZE = 32000;
+ char buf[BUFFER_SIZE] = { };
uint8 gender = GENDER_NONE;
uint8 race = RACE_NONE;
- uint8 playerClass = 0;
+ uint8 playerClass = CLASS_NONE;
uint8 level = 1;
- ObjectGuid::LowType itemLowGuidOffset = sObjectMgr->GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed();
+ // for logs
+ size_t lineNumber = 0;
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
- while (!feof(fin))
+ while (!feof(fin.get()))
{
- if (!fgets(buf, 32000, fin))
+ if (!fgets(buf, BUFFER_SIZE, fin.get()))
{
- if (feof(fin))
+ if (feof(fin.get()))
break;
- ROLLBACK(DUMP_FILE_BROKEN);
+ return DUMP_FILE_BROKEN;
}
- std::string line; line.assign(buf);
+ std::string line;
+ line.assign(buf);
+ ++lineNumber;
// skip empty strings
size_t nw_pos = line.find_first_not_of(" \t\n\r\7");
if (nw_pos == std::string::npos)
continue;
- // skip logfile-side dump start notice, the important notes and dump end notices
- if ((line.substr(nw_pos, 16) == "== START DUMP ==") ||
- (line.substr(nw_pos, 15) == "IMPORTANT NOTE:") ||
- (line.substr(nw_pos, 14) == "== END DUMP =="))
+ // skip the important notes
+ static std::string const SkippedLine = "IMPORTANT NOTE:";
+ if (line.substr(nw_pos, SkippedLine.size()) == SkippedLine)
continue;
- // add required_ check
- /*
- if (line.substr(nw_pos, 41) == "UPDATE character_db_version SET required_")
- {
- if (!CharacterDatabase.Execute(line.c_str()))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- continue;
- }
- */
-
// determine table name and load type
std::string tn = GetTableName(line);
if (tn.empty())
{
- TC_LOG_ERROR("misc", "LoadPlayerDump: Can't extract table name from line: '%s'!", line.c_str());
- ROLLBACK(DUMP_FILE_BROKEN);
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line " UI64FMTD ") Can't extract table name!", lineNumber);
+ return DUMP_FILE_BROKEN;
}
- DumpTableType type = DumpTableType(0);
- size_t i;
- for (i = 0; i < dumpTablesCount; ++i)
+ DumpTableType type = DTT_CHARACTER;
+ uint32 i;
+ for (i = 0; i < DUMP_TABLE_COUNT; ++i)
{
- if (tn == dumpTables[i].name)
+ if (tn == DumpTables[i].Name)
{
- type = dumpTables[i].type;
+ type = DumpTables[i].Type;
break;
}
}
- if (i == dumpTablesCount)
+ if (i == DUMP_TABLE_COUNT)
{
- TC_LOG_ERROR("misc", "LoadPlayerDump: Unknown table: '%s'!", tn.c_str());
- ROLLBACK(DUMP_FILE_BROKEN);
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line " UI64FMTD ") Unknown table: `%s`!", lineNumber, tn.c_str());
+ return DUMP_FILE_BROKEN;
}
- // change the data to server values
- switch (type)
- {
- case DTT_CHARACTER:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
+ TableStruct const& ts = CharacterTables[i];
+ if (!ValidateFields(ts, line, lineNumber))
+ return DUMP_FILE_BROKEN;
- if (!ChangeNth(line, GetColNumber(tn, "account"), chraccount))
- ROLLBACK(DUMP_FILE_BROKEN);
+ // per field guid offsetting
+ for (TableField const& field : ts.TableFields)
+ {
+ if (!field.IsDependentField)
+ continue;
- race = uint8(atoul(GetNth(line, GetColNumber(tn, "race")).c_str()));
- playerClass = uint8(atoul(GetNth(line, GetColNumber(tn, "class")).c_str()));
- gender = uint8(atoul(GetNth(line, GetColNumber(tn, "gender")).c_str()));
- level = uint8(atoul(GetNth(line, GetColNumber(tn, "level")).c_str()));
- if (name.empty())
+ switch (field.FieldGuidType)
+ {
+ case GUID_TYPE_ACCOUNT:
+ if (!ChangeColumn(ts, line, field.FieldName, chraccount))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_CHAR:
+ if (!ChangeColumn(ts, line, field.FieldName, newguid))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_PET:
+ if (!ChangeGuid(ts, line, field.FieldName, petIds, petLowGuidOffset))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_MAIL:
+ if (!ChangeGuid(ts, line, field.FieldName, mails, mailLowGuidOffset))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_ITEM:
+ if (!ChangeGuid(ts, line, field.FieldName, items, itemLowGuidOffset, true))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_EQUIPMENT_SET:
+ if (!ChangeGuid(ts, line, field.FieldName, equipmentSetIds, equipmentSetGuidOffset))
+ return DUMP_FILE_BROKEN;
+ break;
+ case GUID_TYPE_NULL:
{
- // check if the original name already exists
- name = GetNth(line, GetColNumber(tn, "name"));
-
- CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
- stmt->setString(0, name);
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
-
- if (result)
- if (!ChangeNth(line, GetColNumber(tn, "at_login"), "1")) // characters.at_login set to "rename on login"
- ROLLBACK(DUMP_FILE_BROKEN);
+ static std::string const NullString("NULL");
+ if (!ChangeColumn(ts, line, field.FieldName, NullString))
+ return DUMP_FILE_BROKEN;
+ break;
}
- else if (!ChangeNth(line, GetColNumber(tn, "name"), name.c_str()))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- const char null[5] = "NULL";
- if (!ChangeNth(line, GetColNumber(tn, "deleteInfos_Account"), null))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "deleteInfos_Name"), null))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "deleteDate"), null))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_CHAR_TABLE:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_CURRENCY:
- {
- if (!ChangeNth(line, GetColNumber(tn, "CharacterGuid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_EQSET_TABLE:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- char newSetGuid[24];
- snprintf(newSetGuid, 24, UI64FMTD, sObjectMgr->GenerateEquipmentSetGuid());
- if (!ChangeNth(line, GetColNumber(tn, "setguid"), newSetGuid))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
- if (!ChangeGuid(line, GetColNumber(tn, "item" + std::to_string(slot)), items, itemLowGuidOffset, true))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_INVENTORY:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- if (!ChangeGuid(line, GetColNumber(tn, "bag"), items, itemLowGuidOffset, true))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeGuid(line, GetColNumber(tn, "item"), items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_CHAR_TRANSMOG:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
-
- char newSetGuid[24];
- snprintf(newSetGuid, 24, UI64FMTD, sObjectMgr->GenerateEquipmentSetGuid());
- if (!ChangeNth(line, GetColNumber(tn, "setguid"), newSetGuid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_MAIL:
- {
- if (!ChangeGuid(line, GetColNumber(tn, "id"), mails, sObjectMgr->_mailId))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "receiver"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_MAIL_ITEM:
- {
- if (!ChangeGuid(line, GetColNumber(tn, "mail_id"), mails, sObjectMgr->_mailId))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeGuid(line, GetColNumber(tn, "item_guid"), items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "receiver"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_ITEM:
- {
- if (!ChangeGuid(line, GetColNumber(tn, "guid"), items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "owner_guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_ITEM_GIFT:
- {
- if (!ChangeNth(line, GetColNumber(tn, "guid"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeGuid(line, GetColNumber(tn, "item_guid"), items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_ITEM_TABLE:
- {
- if (!ChangeGuid(line, GetColNumber(tn, "itemGuid"), items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
}
- case DTT_PET:
- {
- // store a map of old pet id to new inserted pet id for use by DTT_PET_TABLE tables
- std::string petIdStr = GetNth(line, GetColNumber(tn, "id"));
-
- uint32 currentPetId = atoul(petIdStr.c_str());
-
- PetIds::const_iterator petIdsItr = petIds.find(currentPetId);
- if (petIdsItr != petIds.end()) // duplicate pets
- ROLLBACK(DUMP_FILE_BROKEN);
-
- uint32 newPetId = sObjectMgr->GeneratePetNumber();
- petIds[currentPetId] = newPetId;
-
- if (!ChangeNth(line, GetColNumber(tn, "id"), std::to_string(newPetId).c_str()))
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, GetColNumber(tn, "owner"), newguid))
- ROLLBACK(DUMP_FILE_BROKEN);
+ }
- break;
- }
- case DTT_PET_TABLE: // pet_aura, pet_spell, pet_spell_cooldown
+ // extra modifications for other tables
+ switch (type)
+ {
+ case DTT_CHARACTER:
{
- std::string petIdStr = GetNth(line, GetColNumber(tn, "id"));
-
- // lookup currpetid and match to new inserted pet id
- PetIds::const_iterator petIdsItr = petIds.find(atoul(petIdStr.c_str()));
- if (petIdsItr == petIds.end()) // couldn't find new inserted id
- ROLLBACK(DUMP_FILE_BROKEN);
+ race = uint8(atoul(GetColumn(ts, line, "race").c_str()));
+ playerClass = uint8(atoul(GetColumn(ts, line, "class").c_str()));
+ gender = uint8(atoul(GetColumn(ts, line, "gender").c_str()));
+ level = uint8(atoul(GetColumn(ts, line, "level").c_str()));
+ if (name.empty())
+ {
+ // generate a temporary name
+ std::string guidPart = Trinity::StringFormat("%X", guid);
+ std::size_t maxCharsFromOriginalName = MAX_PLAYER_NAME - guidPart.length();
- if (!ChangeNth(line, GetColNumber(tn, "guid"), std::to_string(petIdsItr->second).c_str()))
- ROLLBACK(DUMP_FILE_BROKEN);
+ name = GetColumn(ts, line, "name").substr(0, maxCharsFromOriginalName) + guidPart;
+ // characters.at_login set to "rename on login"
+ if (!ChangeColumn(ts, line, "name", name))
+ return DUMP_FILE_BROKEN;
+ if (!ChangeColumn(ts, line, "at_login", "1"))
+ return DUMP_FILE_BROKEN;
+ }
+ else if (!ChangeColumn(ts, line, "name", name.c_str())) // characters.name
+ return DUMP_FILE_BROKEN;
break;
}
default:
- TC_LOG_ERROR("misc", "Unknown dump table type: %u", type);
break;
}
- fixNULLfields(line);
+ FixNULLfields(line);
trans->Append(line.c_str());
}
@@ -757,12 +1050,12 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
sCharacterCache->AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(guid), account, name, gender, race, playerClass, level, false);
sObjectMgr->GetGenerator<HighGuid::Item>().Set(sObjectMgr->GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed() + items.size());
- sObjectMgr->_mailId += mails.size();
+ sObjectMgr->_mailId += mails.size();
+ sObjectMgr->_hiPetNumber += petIds.size();
+ sObjectMgr->_equipmentSetGuid += equipmentSetIds.size();
if (incHighest)
sObjectMgr->GetGenerator<HighGuid::Player>().Generate();
- fclose(fin);
-
return DUMP_SUCCESS;
}
diff --git a/src/server/game/Tools/PlayerDump.h b/src/server/game/Tools/PlayerDump.h
index 4b473663bee..995cca51668 100644
--- a/src/server/game/Tools/PlayerDump.h
+++ b/src/server/game/Tools/PlayerDump.h
@@ -30,7 +30,7 @@ enum DumpTableType
DTT_CHAR_TABLE, // // character_achievement, character_achievement_progress,
// character_action, character_aura, character_homebind,
// character_queststatus, character_queststatus_rewarded, character_reputation,
- // character_spell, character_spell_cooldown, character_ticket, character_talent.
+ // character_spell, character_spell_cooldown, character_ticket, character_talent,
// character_cuf_profiles
DTT_CURRENCY, // // character_currency
@@ -52,7 +52,10 @@ enum DumpTableType
DTT_ITEM_GIFT, // <- item guids // character_gifts
- DTT_ITEM_TABLE, // <- item guids // item_instance_artifact, item_instance_artifact_powers, item_instance_gems, item_instance_modifiers, item_instance_transmog
+ DTT_ITEM_TABLE, // <- item guids // item_instance_artifact, item_instance_artifact_powers, item_instance_azerite
+ // item_instance_azerite_empowered, item_instance_azerite_milestone_power,
+ // item_instance_azerite_unlocked_essence, item_instance_gems, item_instance_modifiers,
+ // item_instance_transmog
DTT_PET, // -> pet guids collection // character_pet
DTT_PET_TABLE // <- pet guids // pet_aura, pet_spell, pet_spell_cooldown
@@ -63,16 +66,18 @@ enum DumpReturn
DUMP_SUCCESS,
DUMP_FILE_OPEN_ERROR,
DUMP_TOO_MANY_CHARS,
- DUMP_UNEXPECTED_END,
DUMP_FILE_BROKEN,
DUMP_CHARACTER_DELETED
};
+struct DumpTable;
+struct TableStruct;
+class StringTransaction;
+
class TC_GAME_API PlayerDump
{
public:
- typedef std::set<ObjectGuid::LowType> DumpGuidSet;
- typedef std::map<ObjectGuid::LowType, ObjectGuid::LowType> DumpGuidMap;
+ static void InitializeTables();
static void InitializeColumnDefinition();
@@ -89,14 +94,14 @@ class TC_GAME_API PlayerDumpWriter : public PlayerDump
DumpReturn WriteDump(std::string const& file, ObjectGuid::LowType guid);
private:
+ bool AppendTable(StringTransaction& trans, ObjectGuid::LowType guid, TableStruct const& tableStruct, DumpTable const& dumpTable);
+ void PopulateGuids(ObjectGuid::LowType guid);
- bool DumpTable(std::string& dump, ObjectGuid::LowType guid, char const* tableFrom, char const* tableTo, DumpTableType type);
- std::string GenerateWhereStr(char const* field, DumpGuidSet const& guids, DumpGuidSet::const_iterator& itr);
- std::string GenerateWhereStr(char const* field, ObjectGuid::LowType guid);
+ std::set<ObjectGuid::LowType> _pets;
+ std::set<ObjectGuid::LowType> _mails;
+ std::set<ObjectGuid::LowType> _items;
- DumpGuidSet pets;
- DumpGuidSet mails;
- DumpGuidSet items;
+ std::set<uint64> _itemSets;
};
class TC_GAME_API PlayerDumpReader : public PlayerDump
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 8d5f8ca66ac..cce33cd7795 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1548,10 +1548,6 @@ void World::SetInitialWorldSettings()
exit(1);
}
- ///- Initialize PlayerDump
- TC_LOG_INFO("server.loading", "Initialize PlayerDump...");
- PlayerDump::InitializeColumnDefinition();
-
///- Initialize pool manager
sPoolMgr->Initialize();
@@ -1625,6 +1621,9 @@ void World::SetInitialWorldSettings()
///- Initialize static helper structures
AIRegistry::Initialize();
+ TC_LOG_INFO("server.loading", "Initializing PlayerDump tables...");
+ PlayerDump::InitializeTables();
+
TC_LOG_INFO("server.loading", "Loading SpellInfo store...");
sSpellMgr->LoadSpellInfoStore();