TrinityCore
Loading...
Searching...
No Matches
PlayerDump.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "PlayerDump.h"
19#include "AccountMgr.h"
20#include "CharacterCache.h"
21#include "Common.h"
22#include "DatabaseEnv.h"
23#include "Log.h"
24#include "ObjectMgr.h"
25#include "Player.h"
26#include "StringConvert.h"
27#include "World.h"
28#include <fstream>
29#include <sstream>
30
31// static data
33{
34 // 32 bit long guids
40
41 // 64 bit long guids
43
44 // special types
45 GUID_TYPE_NULL // set to null
46};
47
48// for RAII
50{
51 void operator()(FILE* f) const
52 {
53 if (f)
54 fclose(f);
55 }
56};
57typedef std::unique_ptr<FILE, FileCloser> FileHandle;
58
59inline FileHandle GetFileHandle(char const* path, char const* mode)
60{
61 return FileHandle(fopen(path, mode), FileCloser());
62}
63
65{
66 char const* TableName;
67 char const* PrimaryKey;
68 char const* PlayerGuid;
69
71};
72
74{
75 { "character_pet", "id", "owner", GUID_TYPE_PET },
76 { "mail", "id", "receiver", GUID_TYPE_MAIL },
77 { "item_instance", "guid", "owner_guid", GUID_TYPE_ITEM },
78
79 { "character_equipmentsets", "setguid", "guid", GUID_TYPE_EQUIPMENT_SET }
80};
81
83{
84 char const* Name;
86};
87
89{
90 { "characters", DTT_CHARACTER },
91 { "character_account_data", DTT_CHAR_TABLE },
92 { "character_achievement", DTT_CHAR_TABLE },
93 { "character_achievement_progress", DTT_CHAR_TABLE },
94 { "character_action", DTT_CHAR_TABLE },
95 { "character_aura", DTT_CHAR_TABLE },
96 { "character_declinedname", DTT_CHAR_TABLE },
97 { "character_equipmentsets", DTT_EQSET_TABLE },
98 { "character_fishingsteps", DTT_CHAR_TABLE },
99 { "character_glyphs", DTT_CHAR_TABLE },
100 { "character_homebind", DTT_CHAR_TABLE },
101 { "character_inventory", DTT_INVENTORY },
102 { "character_pet", DTT_PET },
103 { "character_pet_declinedname", DTT_PET },
104 { "character_queststatus", DTT_CHAR_TABLE },
105 { "character_queststatus_daily", DTT_CHAR_TABLE },
106 { "character_queststatus_weekly", DTT_CHAR_TABLE },
107 { "character_queststatus_monthly", DTT_CHAR_TABLE },
108 { "character_queststatus_seasonal", DTT_CHAR_TABLE },
109 { "character_queststatus_rewarded", DTT_CHAR_TABLE },
110 { "character_reputation", DTT_CHAR_TABLE },
111 { "character_skills", DTT_CHAR_TABLE },
112 { "character_spell", DTT_CHAR_TABLE },
113 { "character_spell_cooldown", DTT_CHAR_TABLE },
114 { "character_talent", DTT_CHAR_TABLE },
115 { "mail", DTT_MAIL },
116 { "mail_items", DTT_MAIL_ITEM }, // must be after mail
117 { "pet_aura", DTT_PET_TABLE }, // must be after character_pet
118 { "pet_spell", DTT_PET_TABLE }, // must be after character_pet
119 { "pet_spell_cooldown", DTT_PET_TABLE }, // must be after character_pet
120 { "item_instance", DTT_ITEM }, // must be after character_inventory and mail_items
121 { "character_gifts", DTT_ITEM_GIFT } // must be after item_instance
122};
123
124uint32 const DUMP_TABLE_COUNT = std::extent<decltype(DumpTables)>::value;
125
126// helper class to dump sql queries to a printable string
128{
129 public:
131
132 void Append(char const* sql)
133 {
134 std::ostringstream oss;
135 oss << sql << '\n';
136 _buf += oss.str();
137 }
138
139 char const* GetBuffer() const
140 {
141 return _buf.c_str();
142 }
143
144 private:
145 std::string _buf;
146};
147
148// dynamic data, loaded at startup
156
158{
159 std::string TableName;
160 std::string WhereFieldName;
161 std::vector<TableField> TableFields;
162
163 // for lookup
164 std::unordered_map<std::string /*fieldName*/, int32 /*index*/> FieldIndices;
165};
166
167std::vector<TableStruct> CharacterTables;
168
169inline bool StringsEqualCaseInsensitive(std::string const& left, std::string const& right)
170{
171 std::string upperLeftString = left;
172 bool leftResult = Utf8ToUpperOnlyLatin(upperLeftString);
173 ASSERT(leftResult);
174
175 std::string upperRightString = right;
176 bool rightResult = Utf8ToUpperOnlyLatin(upperRightString);
177 ASSERT(rightResult);
178
179 return upperLeftString == upperRightString;
180}
181
182inline auto FindColumnByName(TableStruct& tableStruct, std::string const& columnName) -> decltype(tableStruct.TableFields.begin())
183{
184 return std::find_if(tableStruct.TableFields.begin(), tableStruct.TableFields.end(), [columnName](TableField const& tableField) -> bool
185 {
186 return StringsEqualCaseInsensitive(tableField.FieldName, columnName);
187 });
188}
189
190inline int32 GetColumnIndexByName(TableStruct const& tableStruct, std::string const& columnName)
191{
192 auto itr = tableStruct.FieldIndices.find(columnName);
193 if (itr == tableStruct.FieldIndices.end())
194 return -1;
195
196 return itr->second;
197}
198
199inline void MarkDependentColumn(TableStruct& tableStruct, std::string const& columnName, GuidType dependentType)
200{
201 auto itr = FindColumnByName(tableStruct, columnName);
202 if (itr == tableStruct.TableFields.end())
203 {
204 TC_LOG_FATAL("server.loading", "Column `{}` declared in table `{}` marked as dependent but doesn't exist, PlayerDump will not work properly, please update table definitions",
205 columnName, tableStruct.TableName);
206 ABORT();
207 return;
208 }
209
210 if (itr->IsDependentField)
211 {
212 TC_LOG_FATAL("server.loading", "Attempt to mark column `{}` in table `{}` as dependent column but already marked! please check your code.",
213 columnName, tableStruct.TableName);
214 ABORT();
215 return;
216 }
217
218 itr->IsDependentField = true;
219 itr->FieldGuidType = dependentType;
220}
221
222inline void MarkWhereField(TableStruct& tableStruct, std::string const& whereField)
223{
224 ASSERT(tableStruct.WhereFieldName.empty());
225
226 auto whereFieldItr = FindColumnByName(tableStruct, whereField);
227 if (whereFieldItr == tableStruct.TableFields.end())
228 {
229 TC_LOG_FATAL("server.loading", "Column name `{}` set as 'WHERE' column for table `{}` doesn't exist. PlayerDump won't work properly",
230 whereField, tableStruct.TableName);
231 ABORT();
232 return;
233 }
234
235 tableStruct.WhereFieldName = whereField;
236}
237
238inline void AssertBaseTable(BaseTable const& baseTable)
239{
240 auto itr = std::find_if(CharacterTables.begin(), CharacterTables.end(), [baseTable](TableStruct const& tableStruct) -> bool
241 {
242 return StringsEqualCaseInsensitive(tableStruct.TableName, baseTable.TableName);
243 });
244
245 ASSERT(itr != CharacterTables.end());
246
247 auto columnItr = FindColumnByName(*itr, baseTable.PrimaryKey);
248 ASSERT(columnItr != itr->TableFields.end());
249
250 columnItr = FindColumnByName(*itr, baseTable.PlayerGuid);
251 ASSERT(columnItr != itr->TableFields.end());
252}
253
255{
256 uint32 oldMSTime = getMSTime();
257
258 for (DumpTable const& dumpTable : DumpTables)
259 {
260 TableStruct t;
261 t.TableName = dumpTable.Name;
262
263 QueryResult result = CharacterDatabase.PQuery("DESC {}", dumpTable.Name);
264 // prepared statement is correct (checked at startup) so table must exist
265 ASSERT(result);
266
267 int32 i = 0;
268 do
269 {
270 std::string columnName = (*result)[0].GetString();
271 t.FieldIndices.emplace(columnName, i++);
272
273 TableField f;
274 f.FieldName = columnName;
275
276 bool toUpperResult = Utf8ToUpperOnlyLatin(columnName);
277 ASSERT(toUpperResult);
278
279 t.TableFields.emplace_back(std::move(f));
280 } while (result->NextRow());
281
282 switch (dumpTable.Type)
283 {
284 case DTT_CHARACTER:
285 MarkWhereField(t, "guid");
286
289
290 MarkDependentColumn(t, "deleteInfos_Account", GUID_TYPE_NULL);
291 MarkDependentColumn(t, "deleteInfos_Name", GUID_TYPE_NULL);
292 MarkDependentColumn(t, "deleteDate", GUID_TYPE_NULL);
293 break;
294 case DTT_CHAR_TABLE:
295 MarkWhereField(t, "guid");
296
298 break;
299 case DTT_EQSET_TABLE:
300 MarkWhereField(t, "guid");
301
304
305 // item0 - item18
306 for (uint32 j = 0; j < EQUIPMENT_SLOT_END; ++j)
307 {
308 std::string itColumn = Trinity::StringFormat("item{}", j);
310 }
311 break;
312 case DTT_INVENTORY:
313 MarkWhereField(t, "guid");
314
318 break;
319 case DTT_MAIL:
320 MarkWhereField(t, "receiver");
321
323 MarkDependentColumn(t, "receiver", GUID_TYPE_CHAR);
324 break;
325 case DTT_MAIL_ITEM:
326 MarkWhereField(t, "mail_id");
327
328 MarkDependentColumn(t, "mail_id", GUID_TYPE_MAIL);
329 MarkDependentColumn(t, "item_guid", GUID_TYPE_ITEM);
330 MarkDependentColumn(t, "receiver", GUID_TYPE_CHAR);
331 break;
332 case DTT_ITEM:
333 MarkWhereField(t, "guid");
334
336 MarkDependentColumn(t, "owner_guid", GUID_TYPE_CHAR);
337 break;
338 case DTT_ITEM_GIFT:
339 MarkWhereField(t, "item_guid");
340
342 MarkDependentColumn(t, "item_guid", GUID_TYPE_ITEM);
343 break;
344 case DTT_PET:
345 MarkWhereField(t, "owner");
346
349 break;
350 case DTT_PET_TABLE:
351 MarkWhereField(t, "guid");
352
354 break;
355 default:
356 TC_LOG_FATAL("server.loading", "Wrong dump table type {}, probably added a new table type without updating code", uint32(dumpTable.Type));
357 ABORT();
358 return;
359 }
360
361 CharacterTables.emplace_back(std::move(t));
362 }
363
364 // perform some sanity checks
365 for (TableStruct const& tableStruct : CharacterTables)
366 {
367 if (tableStruct.WhereFieldName.empty())
368 {
369 TC_LOG_FATAL("server.loading", "Table `{}` defined in player dump doesn't have a WHERE query field", tableStruct.TableName);
370 ABORT();
371 }
372 }
373
374 for (BaseTable const& baseTable : BaseTables)
375 AssertBaseTable(baseTable);
376
378
379 TC_LOG_INFO("server.loading", ">> Initialized tables for PlayerDump in {} ms.", GetMSTimeDiffToNow(oldMSTime));
380}
381
382// Low level functions
383inline bool FindColumn(TableStruct const& ts, std::string const& str, std::string const& column, std::string::size_type& s, std::string::size_type& e)
384{
385 int32 columnIndex = GetColumnIndexByName(ts, column);
386 if (columnIndex == -1)
387 return false;
388
389 // array indices start at 0, compensate
390 ++columnIndex;
391
392 s = str.find("VALUES ('");
393 if (s == std::string::npos)
394 return false;
395 s += 9;
396
397 do
398 {
399 e = str.find('\'', s);
400 if (e == std::string::npos)
401 return false;
402 } while (str[e - 1] == '\\');
403
404 for (int32 i = 1; i < columnIndex; ++i)
405 {
406 do
407 {
408 // length of "', '"
409 s = e + 4;
410 e = str.find('\'', s);
411 if (e == std::string::npos)
412 return false;
413 } while (str[e - 1] == '\\');
414 }
415 return true;
416}
417
418inline std::string GetTableName(std::string const& str)
419{
420 // length of "INSERT INTO `"
421 static std::string::size_type const s = 13;
422 std::string::size_type e = str.find('`', s);
423 if (e == std::string::npos)
424 return "";
425
426 return str.substr(s, e - s);
427}
428
429inline bool ValidateFields(TableStruct const& ts, std::string const& str, size_t lineNumber)
430{
431 std::string::size_type s = str.find("` VALUES (");
432 if (s != std::string::npos) // old dump format (no column names)
433 return true;
434
435 // new format has insert with columns, need validation else we risk executing an invalid query
436 s = str.find("` (`");
437 if (s == std::string::npos)
438 {
439 TC_LOG_ERROR("misc", "LoadPlayerDump: (line {}) dump format not recognized.", lineNumber);
440 return false;
441 }
442 s += 4;
443
444 std::string::size_type valPos = str.find("VALUES ('");
445 std::string::size_type e = str.find('`', s);
446 if (e == std::string::npos || valPos == std::string::npos)
447 {
448 TC_LOG_ERROR("misc", "LoadPlayerDump: (line {}) unexpected end of line", lineNumber);
449 return false;
450 }
451
452 do
453 {
454 std::string column = str.substr(s, e - s);
455 int32 columnIndex = GetColumnIndexByName(ts, column);
456 if (columnIndex == -1)
457 {
458 TC_LOG_ERROR("misc", "LoadPlayerDump: (line {}) unknown column name `{}` for table `{}`, aborting due to incompatible DB structure.", lineNumber, column, ts.TableName);
459 return false;
460 }
461
462 // length of "`, `"
463 s = e + 4;
464 e = str.find('`', s);
465 } while (e < valPos);
466
467 return true;
468}
469
470inline bool ChangeColumn(TableStruct const& ts, std::string& str, std::string const& column, std::string const& with, bool allowZero = false)
471{
472 std::string::size_type s, e;
473 if (!FindColumn(ts, str, column, s, e))
474 return false;
475
476 if (allowZero && str.substr(s, e - s) == "0")
477 return true; // not an error
478
479 str.replace(s, e - s, with);
480 return true;
481}
482
483inline std::string GetColumn(TableStruct const& ts, std::string& str, std::string const& column)
484{
485 std::string::size_type s, e;
486 if (!FindColumn(ts, str, column, s, e))
487 return "";
488
489 return str.substr(s, e - s);
490}
491
492template <typename T, template<class, class, class...> class MapType, class... Rest>
493inline T RegisterNewGuid(T oldGuid, MapType<T, T, Rest...>& guidMap, T guidOffset)
494{
495 auto itr = guidMap.find(oldGuid);
496 if (itr != guidMap.end())
497 return itr->second;
498
499 T newguid = guidOffset + T(guidMap.size());
500 guidMap.emplace(oldGuid, newguid);
501 return newguid;
502}
503
504template <typename T, template<class, class, class...> class MapType, class... Rest>
505inline bool ChangeGuid(TableStruct const& ts, std::string& str, std::string const& column, MapType<T, T, Rest...>& guidMap, T guidOffset, bool allowZero = false)
506{
507 T oldGuid = Trinity::StringTo<T>(GetColumn(ts, str, column)).template value_or<T>(0);
508 if (allowZero && !oldGuid)
509 return true; // not an error
510
511 std::string chritem;
512 T newGuid = RegisterNewGuid(oldGuid, guidMap, guidOffset);
513 chritem = std::to_string(newGuid);
514
515 return ChangeColumn(ts, str, column, chritem, allowZero);
516}
517
518inline void AppendTableDump(StringTransaction& trans, TableStruct const& tableStruct, QueryResult result)
519{
520 if (!result)
521 return;
522
523 do
524 {
525 std::ostringstream ss;
526 ss << "INSERT INTO `" << tableStruct.TableName << "` (";
527 for (auto itr = tableStruct.TableFields.begin(); itr != tableStruct.TableFields.end();)
528 {
529 ss << '`' << itr->FieldName << '`';
530 ++itr;
531
532 if (itr != tableStruct.TableFields.end())
533 ss << ", ";
534 }
535 ss << ") VALUES (";
536
537 uint32 const fieldSize = uint32(tableStruct.TableFields.size());
538 Field* fields = result->Fetch();
539
540 for (uint32 i = 0; i < fieldSize;)
541 {
542 char const* cString = fields[i].GetCString();
543 ++i;
544
545 // null pointer -> we have null
546 if (!cString)
547 ss << "'NULL'";
548 else
549 {
550 std::string s(cString);
551 CharacterDatabase.EscapeString(s);
552 ss << '\'' << s << '\'';
553 }
554
555 if (i != fieldSize)
556 ss << ", ";
557 }
558 ss << ");";
559
560 trans.Append(ss.str().c_str());
561 } while (result->NextRow());
562}
563
564inline std::string GenerateWhereStr(std::string const& field, ObjectGuid::LowType guid)
565{
566 std::ostringstream whereStr;
567 whereStr << field << " = '" << guid << '\'';
568 return whereStr.str();
569}
570
571template <typename T, template<class, class...> class SetType, class... Rest>
572inline std::string GenerateWhereStr(std::string const& field, SetType<T, Rest...> const& guidSet)
573{
574 std::ostringstream whereStr;
575 whereStr << field << " IN ('";
576 for (auto itr = guidSet.begin(); itr != guidSet.end();)
577 {
578 whereStr << *itr;
579 ++itr;
580
581 if (whereStr.str().size() > MAX_QUERY_LEN - 50) // near to max query
582 break;
583
584 if (itr != guidSet.end())
585 whereStr << "','";
586 }
587 whereStr << "')";
588 return whereStr.str();
589}
590
591// Writing - High-level functions
593{
594 for (BaseTable const& baseTable : BaseTables)
595 {
596 switch (baseTable.StoredType)
597 {
598 case GUID_TYPE_ITEM:
599 case GUID_TYPE_MAIL:
600 case GUID_TYPE_PET:
602 break;
603 default:
604 return;
605 }
606
607 std::string whereStr = GenerateWhereStr(baseTable.PlayerGuid, guid);
608 QueryResult result = CharacterDatabase.PQuery("SELECT {} FROM {} WHERE {}", baseTable.PrimaryKey, baseTable.TableName, whereStr);
609 if (!result)
610 continue;
611
612 do
613 {
614 switch (baseTable.StoredType)
615 {
616 case GUID_TYPE_ITEM:
617 if (ObjectGuid::LowType itemLowGuid = (*result)[0].GetUInt32())
618 _items.insert(itemLowGuid);
619 break;
620 case GUID_TYPE_MAIL:
621 if (ObjectGuid::LowType mailLowGuid = (*result)[0].GetUInt32())
622 _mails.insert(mailLowGuid);
623 break;
624 case GUID_TYPE_PET:
625 if (ObjectGuid::LowType petLowGuid = (*result)[0].GetUInt32())
626 _pets.insert(petLowGuid);
627 break;
629 if (uint64 eqSetId = (*result)[0].GetUInt64())
630 _itemSets.insert(eqSetId);
631 break;
632 default:
633 break;
634 }
635 } while (result->NextRow());
636 }
637}
638
639bool PlayerDumpWriter::AppendTable(StringTransaction& trans, ObjectGuid::LowType guid, TableStruct const& tableStruct, DumpTable const& dumpTable)
640{
641 std::string whereStr;
642 switch (dumpTable.Type)
643 {
644 case DTT_ITEM:
645 case DTT_ITEM_GIFT:
646 if (_items.empty())
647 return true;
648
649 whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _items);
650 break;
651 case DTT_PET_TABLE:
652 if (_pets.empty())
653 return true;
654
655 whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _pets);
656 break;
657 case DTT_MAIL_ITEM:
658 if (_mails.empty())
659 return true;
660
661 whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _mails);
662 break;
663 case DTT_EQSET_TABLE:
664 if (_itemSets.empty())
665 return true;
666
667 whereStr = GenerateWhereStr(tableStruct.WhereFieldName, _itemSets);
668 break;
669 default:
670 // not set case, get single guid string
671 whereStr = GenerateWhereStr(tableStruct.WhereFieldName, guid);
672 break;
673 }
674
675 QueryResult result = CharacterDatabase.PQuery("SELECT * FROM {} WHERE {}", dumpTable.Name, whereStr);
676 switch (dumpTable.Type)
677 {
678 case DTT_CHARACTER:
679 if (result)
680 {
681 // characters.deleteInfos_Account - if filled error
682 int32 index = GetColumnIndexByName(tableStruct, "deleteInfos_Account");
683 ASSERT(index != -1); // checked at startup
684
685 if ((*result)[index].GetUInt32())
686 return false;
687 }
688 break;
689 default:
690 break;
691 }
692
693 AppendTableDump(trans, tableStruct, result);
694 return true;
695}
696
698{
699 dump = "IMPORTANT NOTE: THIS DUMPFILE IS MADE FOR USE WITH THE 'PDUMP' COMMAND ONLY - EITHER THROUGH INGAME CHAT OR ON CONSOLE!\n";
700 dump += "IMPORTANT NOTE: DO NOT apply it directly - it will irreversibly DAMAGE and CORRUPT your database! You have been warned!\n\n";
701
702 StringTransaction trans;
703
704 // collect guids
705 PopulateGuids(guid);
706 for (uint32 i = 0; i < DUMP_TABLE_COUNT; ++i)
707 if (!AppendTable(trans, guid, CharacterTables[i], DumpTables[i]))
708 return false;
709
710 dump += trans.GetBuffer();
711
714
715 return true;
716}
717
719{
720 if (sWorld->getBoolConfig(CONFIG_PDUMP_NO_PATHS))
721 if (strchr(file.c_str(), '\\') || strchr(file.c_str(), '/'))
723
724 if (sWorld->getBoolConfig(CONFIG_PDUMP_NO_OVERWRITE))
725 {
726 // check if file exists already
727 if (GetFileHandle(file.c_str(), "r"))
729 }
730
731 FileHandle fout = GetFileHandle(file.c_str(), "w");
732 if (!fout)
734
736 std::string dump;
737 if (!GetDump(guid, dump))
739
740 fprintf(fout.get(), "%s", dump.c_str());
741 return ret;
742}
743
745{
747 if (!GetDump(guid, dump))
749 return ret;
750}
751
752// Reading - High-level functions
753inline void FixNULLfields(std::string& line)
754{
755 static std::string const NullString("'NULL'");
756 size_t pos = line.find(NullString);
757 while (pos != std::string::npos)
758 {
759 line.replace(pos, NullString.length(), "NULL");
760 pos = line.find(NullString);
761 }
762}
763
764DumpReturn PlayerDumpReader::LoadDump(std::istream& input, uint32 account, std::string name, ObjectGuid::LowType guid)
765{
766 uint32 charcount = AccountMgr::GetCharactersCount(account);
767 if (charcount >= 10)
768 return DUMP_TOO_MANY_CHARS;
769
770 std::string newguid, chraccount;
771
772 // make sure the same guid doesn't already exist and is safe to use
773 bool incHighest = true;
774 if (guid && guid < sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed())
775 {
777 stmt->setUInt32(0, guid);
778
779 if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
780 guid = sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed(); // use first free if exists
781 else
782 incHighest = false;
783 }
784 else
785 guid = sObjectMgr->GetGenerator<HighGuid::Player>().GetNextAfterMaxUsed();
786
787 // normalize the name if specified and check if it exists
788 if (!normalizePlayerName(name))
789 name.clear();
790
791 if (ObjectMgr::CheckPlayerName(name, sWorld->GetDefaultDbcLocale(), true) == CHAR_NAME_SUCCESS)
792 {
794 stmt->setString(0, name);
795
796 if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
797 name.clear(); // use the one from the dump
798 }
799 else
800 name.clear();
801
802 // name encoded or empty
803 newguid = std::to_string(guid);
804 chraccount = std::to_string(account);
805
806 std::map<ObjectGuid::LowType, ObjectGuid::LowType> items;
807 ObjectGuid::LowType itemLowGuidOffset = sObjectMgr->GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed();
808
809 std::map<ObjectGuid::LowType, ObjectGuid::LowType> mails;
810 ObjectGuid::LowType mailLowGuidOffset = sObjectMgr->_mailId;
811
812 std::map<ObjectGuid::LowType, ObjectGuid::LowType> petIds;
813 ObjectGuid::LowType petLowGuidOffset = sObjectMgr->_hiPetNumber;
814
815 std::map<uint64, uint64> equipmentSetIds;
816 uint64 equipmentSetGuidOffset = sObjectMgr->_equipmentSetGuid;
817
818 std::string line;
819
820 uint8 gender = GENDER_NONE;
821 uint8 race = RACE_NONE;
822 uint8 playerClass = CLASS_NONE;
823 uint8 level = 1;
824
825 // for logs
826 size_t lineNumber = 0;
827
828 CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
829 while (std::getline(input, line))
830 {
831 ++lineNumber;
832
833 // skip empty strings
834 size_t nw_pos = line.find_first_not_of(" \t\n\r\7");
835 if (nw_pos == std::string::npos)
836 continue;
837
838 // skip the important notes
839 static std::string const SkippedLine = "IMPORTANT NOTE:";
840 if (line.substr(nw_pos, SkippedLine.size()) == SkippedLine)
841 continue;
842
843 // determine table name and load type
844 std::string tn = GetTableName(line);
845 if (tn.empty())
846 {
847 TC_LOG_ERROR("misc", "LoadPlayerDump: (line {}) Can't extract table name!", lineNumber);
848 return DUMP_FILE_BROKEN;
849 }
850
852 uint32 i;
853 for (i = 0; i < DUMP_TABLE_COUNT; ++i)
854 {
855 if (tn == DumpTables[i].Name)
856 {
857 type = DumpTables[i].Type;
858 break;
859 }
860 }
861
862 if (i == DUMP_TABLE_COUNT)
863 {
864 TC_LOG_ERROR("misc", "LoadPlayerDump: (line {}) Unknown table: `{}`!", lineNumber, tn);
865 return DUMP_FILE_BROKEN;
866 }
867
868 TableStruct const& ts = CharacterTables[i];
869 if (!ValidateFields(ts, line, lineNumber))
870 return DUMP_FILE_BROKEN;
871
872 // per field guid offsetting
873 for (TableField const& field : ts.TableFields)
874 {
875 if (!field.IsDependentField)
876 continue;
877
878 switch (field.FieldGuidType)
879 {
881 if (!ChangeColumn(ts, line, field.FieldName, chraccount))
882 return DUMP_FILE_BROKEN;
883 break;
884 case GUID_TYPE_CHAR:
885 if (!ChangeColumn(ts, line, field.FieldName, newguid))
886 return DUMP_FILE_BROKEN;
887 break;
888 case GUID_TYPE_PET:
889 if (!ChangeGuid(ts, line, field.FieldName, petIds, petLowGuidOffset))
890 return DUMP_FILE_BROKEN;
891 break;
892 case GUID_TYPE_MAIL:
893 if (!ChangeGuid(ts, line, field.FieldName, mails, mailLowGuidOffset))
894 return DUMP_FILE_BROKEN;
895 break;
896 case GUID_TYPE_ITEM:
897 if (!ChangeGuid(ts, line, field.FieldName, items, itemLowGuidOffset, true))
898 return DUMP_FILE_BROKEN;
899 break;
901 if (!ChangeGuid(ts, line, field.FieldName, equipmentSetIds, equipmentSetGuidOffset))
902 return DUMP_FILE_BROKEN;
903 break;
904 case GUID_TYPE_NULL:
905 {
906 static std::string const NullString("NULL");
907 if (!ChangeColumn(ts, line, field.FieldName, NullString))
908 return DUMP_FILE_BROKEN;
909 break;
910 }
911 }
912 }
913
914 // extra modifications for other tables
915 switch (type)
916 {
917 case DTT_CHARACTER:
918 {
919 race = Trinity::StringTo<uint8>(GetColumn(ts, line, "race")).value_or<uint8>(0);
920 playerClass = Trinity::StringTo<uint8>(GetColumn(ts, line, "class")).value_or<uint8>(0);
921 gender = Trinity::StringTo<uint8>(GetColumn(ts, line, "gender")).value_or<uint8>(0);
922 level = Trinity::StringTo<uint8>(GetColumn(ts, line, "level")).value_or<uint8>(0);
923 if (name.empty())
924 {
925 // generate a temporary name
926 std::string guidPart = Trinity::StringFormat("{:X}", guid);
927 std::size_t maxCharsFromOriginalName = MAX_PLAYER_NAME - guidPart.length();
928
929 name = GetColumn(ts, line, "name").substr(0, maxCharsFromOriginalName) + guidPart;
930
931 // characters.at_login set to "rename on login"
932 if (!ChangeColumn(ts, line, "name", name))
933 return DUMP_FILE_BROKEN;
934 if (!ChangeColumn(ts, line, "at_login", "1"))
935 return DUMP_FILE_BROKEN;
936 }
937 else if (!ChangeColumn(ts, line, "name", name)) // characters.name
938 return DUMP_FILE_BROKEN;
939 break;
940 }
941 default:
942 break;
943 }
944
945 FixNULLfields(line);
946
947 trans->Append(line.c_str());
948 }
949
950 if (input.fail() && !input.eof())
951 return DUMP_FILE_BROKEN;
952
953 CharacterDatabase.CommitTransaction(trans);
954
955 // in case of name conflict player has to rename at login anyway
956 sCharacterCache->AddCharacterCacheEntry(ObjectGuid::Create<HighGuid::Player>(guid), account, name, gender, race, playerClass, level);
957
958 sObjectMgr->GetGenerator<HighGuid::Item>().Set(sObjectMgr->GetGenerator<HighGuid::Item>().GetNextAfterMaxUsed() + items.size());
959 sObjectMgr->_mailId += mails.size();
960 sObjectMgr->_hiPetNumber += petIds.size();
961 sObjectMgr->_equipmentSetGuid += equipmentSetIds.size();
962
963 if (incHighest)
964 sObjectMgr->GetGenerator<HighGuid::Player>().Generate();
965
966 sWorld->UpdateRealmCharCount(account);
967
968 return DUMP_SUCCESS;
969}
970
971DumpReturn PlayerDumpReader::LoadDumpFromString(std::string const& dump, uint32 account, std::string name, ObjectGuid::LowType guid)
972{
973 std::istringstream input(dump);
974 return LoadDump(input, account, name, guid);
975}
976
977DumpReturn PlayerDumpReader::LoadDumpFromFile(std::string const& file, uint32 account, std::string name, ObjectGuid::LowType guid)
978{
979 std::ifstream input(file);
980 if (!input)
982 return LoadDump(input, account, name, guid);
983}
#define sCharacterCache
@ CHAR_SEL_CHECK_NAME
@ CHAR_SEL_CHECK_GUID
#define MAX_QUERY_LEN
Definition Common.h:79
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint64_t uint64
Definition Define.h:132
uint32_t uint32
Definition Define.h:133
#define ABORT
Definition Errors.h:74
#define ASSERT
Definition Errors.h:68
#define TC_LOG_ERROR(filterType__,...)
Definition Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition Log.h:159
#define TC_LOG_FATAL(filterType__,...)
Definition Log.h:168
bool normalizePlayerName(std::string &name)
#define MAX_PLAYER_NAME
Definition ObjectMgr.h:876
#define sObjectMgr
Definition ObjectMgr.h:1721
void MarkDependentColumn(TableStruct &tableStruct, std::string const &columnName, GuidType dependentType)
std::string GetColumn(TableStruct const &ts, std::string &str, std::string const &column)
void AppendTableDump(StringTransaction &trans, TableStruct const &tableStruct, QueryResult result)
void AssertBaseTable(BaseTable const &baseTable)
bool ChangeGuid(TableStruct const &ts, std::string &str, std::string const &column, MapType< T, T, Rest... > &guidMap, T guidOffset, bool allowZero=false)
bool ChangeColumn(TableStruct const &ts, std::string &str, std::string const &column, std::string const &with, bool allowZero=false)
FileHandle GetFileHandle(char const *path, char const *mode)
uint32 const DUMP_TABLE_COUNT
GuidType
@ GUID_TYPE_NULL
@ GUID_TYPE_MAIL
@ GUID_TYPE_PET
@ GUID_TYPE_EQUIPMENT_SET
@ GUID_TYPE_CHAR
@ GUID_TYPE_ITEM
@ GUID_TYPE_ACCOUNT
bool StringsEqualCaseInsensitive(std::string const &left, std::string const &right)
DumpTable const DumpTables[]
int32 GetColumnIndexByName(TableStruct const &tableStruct, std::string const &columnName)
T RegisterNewGuid(T oldGuid, MapType< T, T, Rest... > &guidMap, T guidOffset)
BaseTable const BaseTables[]
void MarkWhereField(TableStruct &tableStruct, std::string const &whereField)
void FixNULLfields(std::string &line)
std::unique_ptr< FILE, FileCloser > FileHandle
std::vector< TableStruct > CharacterTables
auto FindColumnByName(TableStruct &tableStruct, std::string const &columnName) -> decltype(tableStruct.TableFields.begin())
bool FindColumn(TableStruct const &ts, std::string const &str, std::string const &column, std::string::size_type &s, std::string::size_type &e)
std::string GetTableName(std::string const &str)
std::string GenerateWhereStr(std::string const &field, ObjectGuid::LowType guid)
bool ValidateFields(TableStruct const &ts, std::string const &str, size_t lineNumber)
DumpReturn
Definition PlayerDump.h:56
@ DUMP_FILE_OPEN_ERROR
Definition PlayerDump.h:58
@ DUMP_CHARACTER_DELETED
Definition PlayerDump.h:61
@ DUMP_SUCCESS
Definition PlayerDump.h:57
@ DUMP_TOO_MANY_CHARS
Definition PlayerDump.h:59
@ DUMP_FILE_BROKEN
Definition PlayerDump.h:60
DumpTableType
Definition PlayerDump.h:28
@ DTT_EQSET_TABLE
Definition PlayerDump.h:36
@ DTT_INVENTORY
Definition PlayerDump.h:38
@ DTT_CHAR_TABLE
Definition PlayerDump.h:31
@ DTT_PET_TABLE
Definition PlayerDump.h:52
@ DTT_MAIL
Definition PlayerDump.h:40
@ DTT_MAIL_ITEM
Definition PlayerDump.h:43
@ DTT_PET
Definition PlayerDump.h:51
@ DTT_ITEM
Definition PlayerDump.h:46
@ DTT_ITEM_GIFT
Definition PlayerDump.h:49
@ DTT_CHARACTER
Definition PlayerDump.h:29
@ EQUIPMENT_SLOT_END
Definition Player.h:571
@ GENDER_NONE
@ CHAR_NAME_SUCCESS
@ CLASS_NONE
@ RACE_NONE
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition Timer.h:57
uint32 getMSTime()
Definition Timer.h:33
bool Utf8ToUpperOnlyLatin(std::string &utf8String)
Definition Util.cpp:610
static uint32 GetCharactersCount(uint32 accountId)
Class used to access individual fields of database query result.
Definition Field.h:92
char const * GetCString() const
Definition Field.cpp:117
uint32 LowType
Definition ObjectGuid.h:142
static ResponseCodes CheckPlayerName(std::string_view name, LocaleConstant locale, bool create=false)
DumpReturn LoadDump(std::istream &input, uint32 account, std::string name, ObjectGuid::LowType guid)
DumpReturn LoadDumpFromString(std::string const &dump, uint32 account, std::string name, ObjectGuid::LowType guid)
DumpReturn LoadDumpFromFile(std::string const &file, uint32 account, std::string name, ObjectGuid::LowType guid)
bool GetDump(ObjectGuid::LowType guid, std::string &dump)
std::set< ObjectGuid::LowType > _mails
Definition PlayerDump.h:91
std::set< ObjectGuid::LowType > _pets
Definition PlayerDump.h:90
std::set< uint64 > _itemSets
Definition PlayerDump.h:94
DumpReturn WriteDumpToString(std::string &dump, ObjectGuid::LowType guid)
std::set< ObjectGuid::LowType > _items
Definition PlayerDump.h:92
bool AppendTable(StringTransaction &trans, ObjectGuid::LowType guid, TableStruct const &tableStruct, DumpTable const &dumpTable)
DumpReturn WriteDumpToFile(std::string const &file, ObjectGuid::LowType guid)
void PopulateGuids(ObjectGuid::LowType guid)
static void InitializeTables()
void setUInt32(uint8 index, uint32 value)
void setString(uint8 index, std::string const &value)
void Append(char const *sql)
char const * GetBuffer() const
#define sWorld
Definition World.h:900
@ CONFIG_PDUMP_NO_OVERWRITE
Definition World.h:151
@ CONFIG_PDUMP_NO_PATHS
Definition World.h:150
std::unordered_map< std::string, Player * > MapType
std::string StringFormat(FormatString< Args... > fmt, Args &&... args)
Default TC string format function.
char const * TableName
char const * PlayerGuid
char const * PrimaryKey
GuidType StoredType
char const * Name
DumpTableType Type
void operator()(FILE *f) const
bool IsDependentField
std::string FieldName
GuidType FieldGuidType
std::unordered_map< std::string, int32 > FieldIndices
std::string TableName
std::vector< TableField > TableFields
std::string WhereFieldName