aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Tools/PlayerDump.cpp1057
-rw-r--r--src/server/game/Tools/PlayerDump.h21
-rw-r--r--src/server/game/World/World.cpp4
3 files changed, 680 insertions, 402 deletions
diff --git a/src/server/game/Tools/PlayerDump.cpp b/src/server/game/Tools/PlayerDump.cpp
index f0a5ad8b25e..05fafaee8a4 100644
--- a/src/server/game/Tools/PlayerDump.cpp
+++ b/src/server/game/Tools/PlayerDump.cpp
@@ -26,81 +26,368 @@
#include "CharacterCache.h"
#include "World.h"
-#define DUMP_TABLE_COUNT 32
+// static data
+enum GuidType : uint8
+{
+ // 32 bit long guids
+ GUID_TYPE_ACCOUNT,
+ GUID_TYPE_CHAR,
+ GUID_TYPE_PET,
+ GUID_TYPE_MAIL,
+ GUID_TYPE_ITEM,
+
+ // 64 bit long guids
+ GUID_TYPE_EQUIPMENT_SET,
+
+ // 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 }
+};
+
struct DumpTable
{
- char const* name;
- DumpTableType type;
+ char const* Name;
+ DumpTableType Type;
};
-DumpTable const dumpTables[DUMP_TABLE_COUNT] =
-{
- { "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 },
- { "character_declinedname", DTT_CHAR_TABLE },
- { "character_equipmentsets", DTT_EQSET_TABLE},
- { "character_fishingsteps", DTT_CHAR_TABLE },
- { "character_glyphs", DTT_CHAR_TABLE },
- { "character_homebind", DTT_CHAR_TABLE },
- { "character_inventory", DTT_INVENTORY },
- { "character_pet", DTT_PET },
- { "character_pet_declinedname", DTT_PET },
- { "character_queststatus", DTT_CHAR_TABLE },
- { "character_queststatus_daily", DTT_CHAR_TABLE },
- { "character_queststatus_weekly", DTT_CHAR_TABLE },
- { "character_queststatus_monthly", DTT_CHAR_TABLE },
- { "character_queststatus_seasonal", DTT_CHAR_TABLE },
- { "character_queststatus_rewarded", DTT_CHAR_TABLE },
- { "character_reputation", DTT_CHAR_TABLE },
- { "character_skills", DTT_CHAR_TABLE },
- { "character_spell", DTT_CHAR_TABLE },
- { "character_spell_cooldown", DTT_CHAR_TABLE },
- { "character_talent", DTT_CHAR_TABLE },
- { "mail", DTT_MAIL },
- { "mail_items", DTT_MAIL_ITEM }, // must be after mail
- { "pet_aura", DTT_PET_TABLE }, // must be after character_pet
- { "pet_spell", DTT_PET_TABLE }, // must be after character_pet
- { "pet_spell_cooldown", DTT_PET_TABLE }, // must be after character_pet
- { "item_instance", DTT_ITEM }, // must be after character_inventory and mail_items
- { "character_gifts", DTT_ITEM_GIFT }, // must be after item_instance
+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 },
+ { "character_declinedname", DTT_CHAR_TABLE },
+ { "character_equipmentsets", DTT_EQSET_TABLE },
+ { "character_fishingsteps", DTT_CHAR_TABLE },
+ { "character_glyphs", DTT_CHAR_TABLE },
+ { "character_homebind", DTT_CHAR_TABLE },
+ { "character_inventory", DTT_INVENTORY },
+ { "character_pet", DTT_PET },
+ { "character_pet_declinedname", DTT_PET },
+ { "character_queststatus", DTT_CHAR_TABLE },
+ { "character_queststatus_daily", DTT_CHAR_TABLE },
+ { "character_queststatus_weekly", DTT_CHAR_TABLE },
+ { "character_queststatus_monthly", DTT_CHAR_TABLE },
+ { "character_queststatus_seasonal", DTT_CHAR_TABLE },
+ { "character_queststatus_rewarded", DTT_CHAR_TABLE },
+ { "character_reputation", DTT_CHAR_TABLE },
+ { "character_skills", DTT_CHAR_TABLE },
+ { "character_spell", DTT_CHAR_TABLE },
+ { "character_spell_cooldown", DTT_CHAR_TABLE },
+ { "character_talent", DTT_CHAR_TABLE },
+ { "mail", DTT_MAIL },
+ { "mail_items", DTT_MAIL_ITEM }, // must be after mail
+ { "pet_aura", DTT_PET_TABLE }, // must be after character_pet
+ { "pet_spell", DTT_PET_TABLE }, // must be after character_pet
+ { "pet_spell_cooldown", DTT_PET_TABLE }, // must be after character_pet
+ { "item_instance", DTT_ITEM }, // must be after character_inventory and mail_items
+ { "character_gifts", DTT_ITEM_GIFT } // must be after item_instance
};
-// Low level functions
-static bool FindTokNth(std::string const& str, uint32 n, std::string::size_type& s, std::string::size_type& e)
+uint32 const DUMP_TABLE_COUNT = sizeof(DumpTables) / sizeof(DumpTable);
+
+// helper class to dump sql queries to a printable string
+class StringTransaction
{
- s = e = 0;
+ public:
+ StringTransaction() : _buf() { }
- uint32 i = 1;
- for (; s < str.size() && i < n; ++s)
- if (str[s] == ' ')
- ++i;
+ void Append(char const* sql)
+ {
+ std::ostringstream oss;
+ oss << sql << '\n';
+ _buf += oss.str();
+ }
- if (i < n)
- return false;
+ char const* GetBuffer() const
+ {
+ return _buf.c_str();
+ }
+
+ private:
+ std::string _buf;
+};
+
+// dynamic data, loaded at startup
+struct TableField
+{
+ std::string FieldName;
+
+ GuidType FieldGuidType = GUID_TYPE_ACCOUNT;
+ bool IsDependentField = false;
+};
+
+struct TableStruct
+{
+ std::string TableName;
+ std::string WhereFieldName;
+ std::vector<TableField> TableFields;
+
+ // for lookup
+ std::unordered_map<std::string /*fieldName*/, int32 /*index*/> FieldIndices;
+};
- e = str.find(' ', s);
+std::vector<TableStruct> CharacterTables;
- return e != std::string::npos;
+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;
}
-std::string GetTokNth(std::string const& str, uint32 n)
+inline auto FindColumnByName(TableStruct& tableStruct, std::string const& columnName)
{
- std::string::size_type s = 0, e = 0;
- if (!FindTokNth(str, n, s, e))
- return "";
+ return std::find_if(tableStruct.TableFields.begin(), tableStruct.TableFields.end(), [columnName](TableField const& tableField) -> bool
+ {
+ return StringsEqualCaseInsensitive(tableField.FieldName, columnName);
+ });
+}
- return str.substr(s, e - s);
+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;
+}
+
+inline void MarkDependentColumn(TableStruct& tableStruct, std::string const& columnName, GuidType dependentType)
+{
+ 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;
+ }
+
+ 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;
+ }
+
+ itr->IsDependentField = true;
+ itr->FieldGuidType = dependentType;
}
-bool FindNth(std::string const& str, uint32 n, std::string::size_type& s, std::string::size_type& e)
+inline void MarkWhereField(TableStruct& tableStruct, std::string const& whereField)
{
- s = str.find("VALUES ('") + 9;
+ ASSERT(tableStruct.WhereFieldName.empty());
+
+ 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;
+}
+
+inline void AssertBaseTable(BaseTable const& baseTable)
+{
+ auto itr = std::find_if(CharacterTables.begin(), CharacterTables.end(), [baseTable](TableStruct const& tableStruct) -> bool
+ {
+ return StringsEqualCaseInsensitive(tableStruct.TableName, baseTable.TableName);
+ });
+
+ 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());
+}
+
+void PlayerDump::InitializeTables()
+{
+ uint32 oldMSTime = getMSTime();
+
+ for (DumpTable const& dumpTable : DumpTables)
+ {
+ 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();
+ t.FieldIndices.emplace(columnName, i++);
+
+ TableField f;
+ f.FieldName = columnName;
+
+ ASSERT(Utf8ToUpperOnlyLatin(columnName));
+
+ 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_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_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_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 += 9;
do
{
@@ -109,10 +396,11 @@ bool FindNth(std::string const& str, uint32 n, std::string::size_type& s, std::s
return false;
} while (str[e - 1] == '\\');
- for (uint32 i = 1; i < n; ++i)
+ for (int32 i = 1; i < columnIndex; ++i)
{
do
{
+ // length of "', '"
s = e + 4;
e = str.find('\'', s);
if (e == std::string::npos)
@@ -122,8 +410,9 @@ bool FindNth(std::string const& str, uint32 n, std::string::size_type& s, std::s
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(_TABLE_SIM_, s);
if (e == std::string::npos)
@@ -132,244 +421,309 @@ 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 %u) 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(_TABLE_SIM_, s);
+ if (e == std::string::npos || valPos == std::string::npos)
+ {
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line %u) 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 %u) 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(_TABLE_SIM_, 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)
+{
+ 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)
{
- PlayerDumpWriter::DumpGuidMap::const_iterator itr = guidMap.find(oldGuid);
+ 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[20];
- ObjectGuid::LowType newGuid = RegisterNewGuid(oldGuid, guidMap, guidOffset);
- snprintf(chritem, 20, "%u", 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 " << _TABLE_SIM_ << tableName << _TABLE_SIM_ << " 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 " << _TABLE_SIM_ << tableStruct.TableName << _TABLE_SIM_ << " (";
+ for (auto itr = tableStruct.TableFields.begin(); itr != tableStruct.TableFields.end();)
+ {
+ ss << _TABLE_SIM_ << itr->FieldName << _TABLE_SIM_;
+ ++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;)
+ {
+ char const* cString = fields[i].GetCString();
+ ++i;
+
+ // null pointer -> we have null
+ if (!cString)
+ ss << "'NULL'";
+ else
+ {
+ std::string s(cString);
+ CharacterDatabase.EscapeString(s);
+ ss << '\'' << s << '\'';
+ }
+
+ 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].GetUInt32();
- 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_ITEM: fieldname = "guid"; guids = &items; break;
- case DTT_ITEM_GIFT: fieldname = "item_guid"; 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, 3, items); // item guid collection (character_inventory.item)
+ case GUID_TYPE_ITEM:
+ if (ObjectGuid::LowType guid = (*result)[0].GetUInt32())
+ _items.insert(guid);
break;
- case DTT_PET:
- StoreGUID(result, 0, pets); // pet petnumber collection (character_pet.id)
+ case GUID_TYPE_MAIL:
+ if (ObjectGuid::LowType guid = (*result)[0].GetUInt32())
+ _mails.insert(guid);
break;
- case DTT_MAIL:
- StoreGUID(result, 0, mails); // mail id collection (mail.id)
+ case GUID_TYPE_PET:
+ if (ObjectGuid::LowType guid = (*result)[0].GetUInt32())
+ _pets.insert(guid);
break;
- case DTT_MAIL_ITEM:
- StoreGUID(result, 1, items); // item guid collection (mail_items.item_guid)
- break;
- case DTT_CHARACTER:
- {
- if (result->GetFieldCount() <= 73) // 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.");
-
- if (result->Fetch()[73].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:
+ 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:
+ 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 (uint8 i = 0; i < DUMP_TABLE_COUNT; ++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;
@@ -378,22 +732,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);
}
}
@@ -403,31 +754,27 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
if (charcount >= 10)
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;
if (guid && guid < sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed())
-
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_GUID);
stmt->setUInt32(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;
}
else
guid = sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed();
-
// normalize the name if specified and check if it exists
if (!normalizePlayerName(name))
name.clear();
@@ -436,241 +783,167 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
{
PreparedStatement* 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, "%u", guid);
- snprintf(chraccount, 20, "%u", account);
+ std::map<ObjectGuid::LowType, ObjectGuid::LowType> mails;
+ ObjectGuid::LowType mailLowGuidOffset = sObjectMgr->_mailId;
- DumpGuidMap items;
- DumpGuidMap mails;
- char buf[32000];
- memset(buf, 0, sizeof(buf));
+ std::map<ObjectGuid::LowType, ObjectGuid::LowType> petIds;
+ ObjectGuid::LowType 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;
SQLTransaction 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 =="))
- 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);
-
+ // skip the important notes
+ static std::string const SkippedLine = "IMPORTANT NOTE:";
+ if (line.substr(nw_pos, SkippedLine.size()) == SkippedLine)
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 %u) Can't extract table name!", lineNumber);
+ return DUMP_FILE_BROKEN;
}
- DumpTableType type = DumpTableType(0);
- uint8 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 == DUMP_TABLE_COUNT)
{
- TC_LOG_ERROR("misc", "LoadPlayerDump: Unknown table: '%s'!", tn.c_str());
- ROLLBACK(DUMP_FILE_BROKEN);
+ TC_LOG_ERROR("misc", "LoadPlayerDump: (line %u) Unknown table: `%s`!", lineNumber, tn.c_str());
+ return DUMP_FILE_BROKEN;
}
- // change the data to server values
+ TableStruct const& ts = CharacterTables[i];
+ if (!ValidateFields(ts, line, lineNumber))
+ return DUMP_FILE_BROKEN;
+
+ // per field guid offsetting
+ for (TableField const& field : ts.TableFields)
+ {
+ if (!field.IsDependentField)
+ continue;
+
+ 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:
+ {
+ static std::string const NullString("NULL");
+ if (!ChangeColumn(ts, line, field.FieldName, NullString))
+ return DUMP_FILE_BROKEN;
+ break;
+ }
+ }
+ }
+
+ // extra modifications for other tables
switch (type)
{
case DTT_CHARACTER:
{
- if (!ChangeNth(line, 1, newguid)) // characters.guid update
- ROLLBACK(DUMP_FILE_BROKEN);
-
- if (!ChangeNth(line, 2, chraccount)) // characters.account update
- ROLLBACK(DUMP_FILE_BROKEN);
-
- race = uint8(atoul(GetNth(line, 4).c_str()));
- playerClass = uint8(atoul(GetNth(line, 5).c_str()));
- gender = uint8(atoul(GetNth(line, 6).c_str()));
- level = uint8(atoul(GetNth(line, 7).c_str()));
+ 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())
{
// check if the original name already exists
- name = GetNth(line, 3);
+ name = GetColumn(ts, line, "name");
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
stmt->setString(0, name);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
+ // characters.at_login set to "rename on login"
if (result)
- if (!ChangeNth(line, 37, "1")) // characters.at_login set to "rename on login"
- ROLLBACK(DUMP_FILE_BROKEN);
+ if (!ChangeColumn(ts, line, "at_login", "1"))
+ return DUMP_FILE_BROKEN;
}
- else if (!ChangeNth(line, 3, name.c_str())) // characters.name
- ROLLBACK(DUMP_FILE_BROKEN);
-
- const char null[5] = "NULL";
- if (!ChangeNth(line, 74, null)) // characters.deleteInfos_Account
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, 75, null)) // characters.deleteInfos_Name
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, 76, null)) // characters.deleteDate
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_CHAR_TABLE:
- {
- if (!ChangeNth(line, 1, newguid)) // character_*.guid update
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_EQSET_TABLE:
- {
- if (!ChangeNth(line, 1, newguid))
- ROLLBACK(DUMP_FILE_BROKEN); // character_equipmentsets.guid
-
- char newSetGuid[24];
- snprintf(newSetGuid, 24, UI64FMTD, sObjectMgr->GenerateEquipmentSetGuid());
- if (!ChangeNth(line, 2, newSetGuid))
- ROLLBACK(DUMP_FILE_BROKEN); // character_equipmentsets.setguid
-
- for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
- if (!ChangeGuid(line, 7 + slot, items, itemLowGuidOffset, true))
- ROLLBACK(DUMP_FILE_BROKEN); // character_equipmentsets.item
- break;
- }
- case DTT_INVENTORY:
- {
- if (!ChangeNth(line, 1, newguid)) // character_inventory.guid update
- ROLLBACK(DUMP_FILE_BROKEN);
-
- if (!ChangeGuid(line, 2, items, itemLowGuidOffset, true))
- ROLLBACK(DUMP_FILE_BROKEN); // character_inventory.bag update
- if (!ChangeGuid(line, 4, items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN); // character_inventory.item update
- break;
- }
- case DTT_MAIL: // mail
- {
- if (!ChangeGuid(line, 1, mails, sObjectMgr->_mailId))
- ROLLBACK(DUMP_FILE_BROKEN); // mail.id update
- if (!ChangeNth(line, 6, newguid)) // mail.receiver update
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_MAIL_ITEM: // mail_items
- {
- if (!ChangeGuid(line, 1, mails, sObjectMgr->_mailId))
- ROLLBACK(DUMP_FILE_BROKEN); // mail_items.id
- if (!ChangeGuid(line, 2, items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN); // mail_items.item_guid
- if (!ChangeNth(line, 3, newguid)) // mail_items.receiver
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_ITEM:
- {
- // item, owner, data field:item, owner guid
- if (!ChangeGuid(line, 1, items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN); // item_instance.guid update
- if (!ChangeNth(line, 3, newguid)) // item_instance.owner_guid update
- ROLLBACK(DUMP_FILE_BROKEN);
- break;
- }
- case DTT_ITEM_GIFT:
- {
- if (!ChangeNth(line, 1, newguid)) // character_gifts.guid update
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeGuid(line, 2, items, itemLowGuidOffset))
- ROLLBACK(DUMP_FILE_BROKEN); // character_gifts.item_guid update
- 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, 1);
-
- 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, 1, std::to_string(newPetId).c_str())) // character_pet.id update
- ROLLBACK(DUMP_FILE_BROKEN);
- if (!ChangeNth(line, 3, newguid)) // character_pet.owner update
- ROLLBACK(DUMP_FILE_BROKEN);
-
- break;
- }
- case DTT_PET_TABLE: // pet_aura, pet_spell, pet_spell_cooldown
- {
- std::string petIdStr = GetNth(line, 1);
-
- // 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);
-
- if (!ChangeNth(line, 1, std::to_string(petIdsItr->second).c_str()))
- ROLLBACK(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());
}
@@ -681,14 +954,12 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
sCharacterCache->AddCharacterCacheEntry(ObjectGuid(HighGuid::Player, guid), account, name, gender, race, playerClass, level);
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 1f1d493bf61..a4b970f9dc4 100644
--- a/src/server/game/Tools/PlayerDump.h
+++ b/src/server/game/Tools/PlayerDump.h
@@ -57,16 +57,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();
protected:
PlayerDump() { }
@@ -81,13 +83,14 @@ class TC_GAME_API PlayerDumpWriter : public PlayerDump
DumpReturn WriteDump(std::string const& file, ObjectGuid::LowType guid);
private:
- 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);
+ bool AppendTable(StringTransaction& trans, ObjectGuid::LowType guid, TableStruct const& tableStruct, DumpTable const& dumpTable);
+ void PopulateGuids(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 fddb3312010..ac7f58eadfb 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -55,6 +55,7 @@
#include "OutdoorPvPMgr.h"
#include "PetitionMgr.h"
#include "Player.h"
+#include "PlayerDump.h"
#include "PoolMgr.h"
#include "QueryCallback.h"
#include "ScriptMgr.h"
@@ -1433,6 +1434,9 @@ void World::SetInitialWorldSettings()
MMAP::MMapManager* mmmgr = MMAP::MMapFactory::createOrGetMMapManager();
mmmgr->InitializeThreadUnsafe(mapIds);
+ TC_LOG_INFO("server.loading", "Initializing PlayerDump tables...");
+ PlayerDump::InitializeTables();
+
TC_LOG_INFO("server.loading", "Loading SpellInfo store...");
sSpellMgr->LoadSpellInfoStore();