2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include "PlayerDump.h"
21 #include "Database/DatabaseEnv.h"
22 #include "Database/SQLStorage.h"
23 #include "UpdateFields.h"
24 #include "ObjectMgr.h"
25 #include "AccountMgr.h"
27 // Character Dump tables
34 bool isValid() const { return name
!= NULL
; }
37 static DumpTable dumpTables
[] =
39 { "characters", DTT_CHARACTER
}, // -> guid, must be first for name check
40 { "character_account_data", DTT_CHAR_TABLE
},
41 { "character_achievement", DTT_CHAR_TABLE
},
42 { "character_achievement_progress", DTT_CHAR_TABLE
},
43 { "character_action", DTT_CHAR_TABLE
},
44 { "character_aura", DTT_CHAR_TABLE
},
45 { "character_declinedname", DTT_CHAR_NAME_TABLE
},
46 { "character_equipmentsets", DTT_EQSET_TABLE
},
47 { "character_glyphs", DTT_CHAR_TABLE
},
48 { "character_homebind", DTT_CHAR_TABLE
},
49 { "character_inventory", DTT_INVENTORY
}, // -> item guids
50 { "character_queststatus", DTT_CHAR_TABLE
},
51 { "character_pet", DTT_PET
}, // -> pet number
52 { "character_pet_declinedname", DTT_PET_DECL
}, // <- pet number
53 { "character_reputation", DTT_CHAR_TABLE
},
54 { "character_skills", DTT_CHAR_TABLE
},
55 { "character_spell", DTT_CHAR_TABLE
},
56 { "character_spell_cooldown", DTT_CHAR_TABLE
},
57 { "character_talent", DTT_CHAR_TABLE
},
58 { "character_ticket", DTT_CHAR_TABLE
},
59 { "mail", DTT_MAIL
}, // -> mail guids
60 { "mail_items", DTT_MAIL_ITEM
}, // -> item guids <- mail guids
61 { "pet_aura", DTT_PET_TABLE
}, // <- pet number
62 { "pet_spell", DTT_PET_TABLE
}, // <- pet number
63 { "pet_spell_cooldown", DTT_PET_TABLE
}, // <- pet number
64 { "character_gifts", DTT_ITEM_GIFT
}, // <- item guids
65 { "item_instance", DTT_ITEM
}, // <- item guids
66 { NULL
, DTT_CHAR_TABLE
}, // end marker
69 // Low level functions
70 static bool findtoknth(std::string
&str
, int n
, std::string::size_type
&s
, std::string::size_type
&e
)
73 std::string::size_type size
= str
.size();
74 for(i
= 1; s
< size
&& i
< n
; s
++) if(str
[s
] == ' ') ++i
;
80 return e
!= std::string::npos
;
83 std::string
gettoknth(std::string
&str
, int n
)
85 std::string::size_type s
= 0, e
= 0;
86 if (!findtoknth(str
, n
, s
, e
))
89 return str
.substr(s
, e
-s
);
92 bool findnth(std::string
&str
, int n
, std::string::size_type
&s
, std::string::size_type
&e
)
94 s
= str
.find("VALUES ('")+9;
95 if (s
== std::string::npos
)
101 if (e
== std::string::npos
)
103 } while(str
[e
-1] == '\\');
105 for(int i
= 1; i
< n
; ++i
)
111 if (e
== std::string::npos
)
113 } while (str
[e
-1] == '\\');
118 std::string
gettablename(std::string
&str
)
120 std::string::size_type s
= 13;
121 std::string::size_type e
= str
.find(_TABLE_SIM_
, s
);
122 if (e
== std::string::npos
)
125 return str
.substr(s
, e
-s
);
128 bool changenth(std::string
&str
, int n
, const char *with
, bool insert
= false, bool nonzero
= false)
130 std::string::size_type s
, e
;
131 if (!findnth(str
,n
,s
,e
))
134 if (nonzero
&& str
.substr(s
,e
-s
) == "0")
135 return true; // not an error
137 str
.replace(s
,e
-s
, with
);
144 std::string
getnth(std::string
&str
, int n
)
146 std::string::size_type s
, e
;
147 if (!findnth(str
,n
,s
,e
))
150 return str
.substr(s
, e
-s
);
153 bool changetoknth(std::string
&str
, int n
, const char *with
, bool insert
= false, bool nonzero
= false)
155 std::string::size_type s
= 0, e
= 0;
156 if (!findtoknth(str
, n
, s
, e
))
158 if (nonzero
&& str
.substr(s
,e
-s
) == "0")
159 return true; // not an error
161 str
.replace(s
, e
-s
, with
);
168 uint32
registerNewGuid(uint32 oldGuid
, std::map
<uint32
, uint32
> &guidMap
, uint32 hiGuid
)
170 std::map
<uint32
, uint32
>::const_iterator itr
= guidMap
.find(oldGuid
);
171 if (itr
!= guidMap
.end())
174 uint32 newguid
= hiGuid
+ guidMap
.size();
175 guidMap
[oldGuid
] = newguid
;
179 bool changeGuid(std::string
&str
, int n
, std::map
<uint32
, uint32
> &guidMap
, uint32 hiGuid
, bool nonzero
= false)
182 uint32 oldGuid
= atoi(getnth(str
, n
).c_str());
183 if (nonzero
&& oldGuid
== 0)
184 return true; // not an error
186 uint32 newGuid
= registerNewGuid(oldGuid
, guidMap
, hiGuid
);
187 snprintf(chritem
, 20, "%d", newGuid
);
189 return changenth(str
, n
, chritem
, false, nonzero
);
192 bool changetokGuid(std::string
&str
, int n
, std::map
<uint32
, uint32
> &guidMap
, uint32 hiGuid
, bool nonzero
= false)
195 uint32 oldGuid
= atoi(gettoknth(str
, n
).c_str());
196 if (nonzero
&& oldGuid
== 0)
197 return true; // not an error
199 uint32 newGuid
= registerNewGuid(oldGuid
, guidMap
, hiGuid
);
200 snprintf(chritem
, 20, "%d", newGuid
);
202 return changetoknth(str
, n
, chritem
, false, nonzero
);
205 std::string
CreateDumpString(char const* tableName
, QueryResult
*result
)
207 if (!tableName
|| !result
)
210 std::ostringstream ss
;
211 ss
<< "INSERT INTO "<< _TABLE_SIM_
<< tableName
<< _TABLE_SIM_
<< " VALUES (";
212 Field
*fields
= result
->Fetch();
213 for(uint32 i
= 0; i
< result
->GetFieldCount(); ++i
)
218 if (fields
[i
].IsNULL())
222 std::string s
= fields
[i
].GetCppString();
223 CharacterDatabase
.escape_string(s
);
225 ss
<< "'" << s
<< "'";
232 std::string
PlayerDumpWriter::GenerateWhereStr(char const* field
, uint32 guid
)
234 std::ostringstream wherestr
;
235 wherestr
<< field
<< " = '" << guid
<< "'";
236 return wherestr
.str();
239 std::string
PlayerDumpWriter::GenerateWhereStr(char const* field
, GUIDs
const& guids
, GUIDs::const_iterator
& itr
)
241 std::ostringstream wherestr
;
242 wherestr
<< field
<< " IN ('";
243 for(; itr
!= guids
.end(); ++itr
)
247 if (wherestr
.str().size() > MAX_QUERY_LEN
- 50) // near to max query
253 GUIDs::const_iterator itr2
= itr
;
254 if (++itr2
!= guids
.end())
258 return wherestr
.str();
261 void StoreGUID(QueryResult
*result
,uint32 field
,std::set
<uint32
>& guids
)
263 Field
* fields
= result
->Fetch();
264 uint32 guid
= fields
[field
].GetUInt32();
269 void StoreGUID(QueryResult
*result
,uint32 data
,uint32 field
, std::set
<uint32
>& guids
)
271 Field
* fields
= result
->Fetch();
272 std::string dataStr
= fields
[data
].GetCppString();
273 uint32 guid
= atoi(gettoknth(dataStr
, field
).c_str());
278 // Writing - High-level functions
279 void PlayerDumpWriter::DumpTableContent(std::string
& dump
, uint32 guid
, char const*tableFrom
, char const*tableTo
, DumpTableType type
)
281 GUIDs
const* guids
= NULL
;
282 char const* fieldname
= NULL
;
286 case DTT_ITEM
: fieldname
= "guid"; guids
= &items
; break;
287 case DTT_ITEM_GIFT
: fieldname
= "item_guid"; guids
= &items
; break;
288 case DTT_PET
: fieldname
= "owner"; break;
289 case DTT_PET_TABLE
: fieldname
= "guid"; guids
= &pets
; break;
290 case DTT_PET_DECL
: fieldname
= "id"; break;
291 case DTT_MAIL
: fieldname
= "receiver"; break;
292 case DTT_MAIL_ITEM
: fieldname
= "mail_id"; guids
= &mails
; break;
293 default: fieldname
= "guid"; break;
296 // for guid set stop if set is empty
297 if (guids
&& guids
->empty())
298 return; // nothing to do
300 // setup for guids case start position
301 GUIDs::const_iterator guids_itr
;
303 guids_itr
= guids
->begin();
307 std::string wherestr
;
309 if (guids
) // set case, get next guids string
310 wherestr
= GenerateWhereStr(fieldname
,*guids
,guids_itr
);
311 else // not set case, get single guid string
312 wherestr
= GenerateWhereStr(fieldname
,guid
);
314 QueryResult
*result
= CharacterDatabase
.PQuery("SELECT * FROM %s WHERE %s", tableFrom
, wherestr
.c_str());
324 StoreGUID(result
,3,items
); break; // item guid collection (character_inventory.item)
326 StoreGUID(result
,0,pets
); break; // pet petnumber collection (character_pet.id)
328 StoreGUID(result
,0,mails
); // mail id collection (mail.id)
330 StoreGUID(result
,1,items
); break; // item guid collection (mail_items.item_guid)
334 dump
+= CreateDumpString(tableTo
, result
);
337 while (result
->NextRow());
341 while(guids
&& guids_itr
!= guids
->end()); // not set case iterate single time, set case iterate for all guids
344 std::string
PlayerDumpWriter::GetDump(uint32 guid
)
348 dump
+= "IMPORTANT NOTE: This sql queries not created for apply directly, use '.pdump load' command in console or client chat instead.\n";
349 dump
+= "IMPORTANT NOTE: NOT APPLY ITS DIRECTLY to character DB or you will DAMAGE and CORRUPT character DB\n\n";
351 // revision check guard
352 QueryNamedResult
* result
= CharacterDatabase
.QueryNamed("SELECT * FROM character_db_version LIMIT 1");
355 QueryFieldNames
const& namesMap
= result
->GetFieldNames();
357 for(QueryFieldNames::const_iterator itr
= namesMap
.begin(); itr
!= namesMap
.end(); ++itr
)
359 if (itr
->substr(0,9)=="required_")
366 if (!reqName
.empty())
368 // this will fail at wrong character DB version
369 dump
+= "UPDATE character_db_version SET "+reqName
+" = 1 WHERE FALSE;\n\n";
372 sLog
.outError("Table 'character_db_version' not have revision guard field, revision guard query not added to pdump.");
377 sLog
.outError("Character DB not have 'character_db_version' table, revision guard query not added to pdump.");
379 for(DumpTable
* itr
= &dumpTables
[0]; itr
->isValid(); ++itr
)
380 DumpTableContent(dump
, guid
, itr
->name
, itr
->name
, itr
->type
);
382 // TODO: Add instance/group..
383 // TODO: Add a dump level option to skip some non-important tables
388 DumpReturn
PlayerDumpWriter::WriteDump(const std::string
& file
, uint32 guid
)
390 FILE *fout
= fopen(file
.c_str(), "w");
392 return DUMP_FILE_OPEN_ERROR
;
394 std::string dump
= GetDump(guid
);
396 fprintf(fout
,"%s\n",dump
.c_str());
401 // Reading - High-level functions
402 #define ROLLBACK(DR) {CharacterDatabase.RollbackTransaction(); fclose(fin); return (DR);}
404 DumpReturn
PlayerDumpReader::LoadDump(const std::string
& file
, uint32 account
, std::string name
, uint32 guid
)
406 bool nameInvalidated
= false; // set when name changed or will requested changed at next login
408 // check character count
409 uint32 charcount
= sAccountMgr
.GetCharactersCount(account
);
411 return DUMP_TOO_MANY_CHARS
;
413 FILE *fin
= fopen(file
.c_str(), "r");
415 return DUMP_FILE_OPEN_ERROR
;
417 QueryResult
* result
= NULL
;
418 char newguid
[20], chraccount
[20], newpetid
[20], currpetid
[20], lastpetid
[20];
420 // make sure the same guid doesn't already exist and is safe to use
421 bool incHighest
= true;
422 if (guid
!= 0 && guid
< sObjectMgr
.m_CharGuids
.GetNextAfterMaxUsed())
424 result
= CharacterDatabase
.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid
);
427 guid
= sObjectMgr
.m_CharGuids
.GetNextAfterMaxUsed();
430 else incHighest
= false;
433 guid
= sObjectMgr
.m_CharGuids
.GetNextAfterMaxUsed();
435 // normalize the name if specified and check if it exists
436 if (!normalizePlayerName(name
))
439 if (ObjectMgr::CheckPlayerName(name
,true) == CHAR_NAME_SUCCESS
)
441 CharacterDatabase
.escape_string(name
); // for safe, we use name only for sql quearies anyway
442 result
= CharacterDatabase
.PQuery("SELECT * FROM characters WHERE name = '%s'", name
.c_str());
445 name
= ""; // use the one from the dump
452 // name encoded or empty
454 snprintf(newguid
, 20, "%d", guid
);
455 snprintf(chraccount
, 20, "%d", account
);
456 snprintf(newpetid
, 20, "%d", sObjectMgr
.GeneratePetNumber());
457 snprintf(lastpetid
, 20, "%s", "");
459 std::map
<uint32
,uint32
> items
;
460 std::map
<uint32
,uint32
> mails
;
461 std::map
<uint32
,uint32
> eqsets
;
462 char buf
[32000] = "";
464 typedef std::map
<uint32
, uint32
> PetIds
; // old->new petid relation
465 typedef PetIds::value_type PetIdsPair
;
468 CharacterDatabase
.BeginTransaction();
471 if (!fgets(buf
, 32000, fin
))
474 ROLLBACK(DUMP_FILE_BROKEN
);
477 std::string line
; line
.assign(buf
);
479 // skip empty strings
480 size_t nw_pos
= line
.find_first_not_of(" \t\n\r\7");
481 if (nw_pos
==std::string::npos
)
485 if (line
.substr(nw_pos
,15)=="IMPORTANT NOTE:")
488 // add required_ check
489 if (line
.substr(nw_pos
,41)=="UPDATE character_db_version SET required_")
491 if (!CharacterDatabase
.Execute(line
.c_str()))
492 ROLLBACK(DUMP_FILE_BROKEN
);
497 // determine table name and load type
498 std::string tn
= gettablename(line
);
501 sLog
.outError("LoadPlayerDump: Can't extract table name from line: '%s'!", line
.c_str());
502 ROLLBACK(DUMP_FILE_BROKEN
);
505 DumpTableType type
= DTT_CHARACTER
; //Fixed: Using uninitialized memory 'type'
506 DumpTable
* dTable
= &dumpTables
[0];
507 for(; dTable
->isValid(); ++dTable
)
509 if (tn
== dTable
->name
)
516 if (!dTable
->isValid())
518 sLog
.outError("LoadPlayerDump: Unknown table: '%s'!", tn
.c_str());
519 ROLLBACK(DUMP_FILE_BROKEN
);
522 bool execute_ok
= true; // false, if need skip soem query
524 // change the data to server values
528 if (!changenth(line
, 1, newguid
)) // character_*.guid update
529 ROLLBACK(DUMP_FILE_BROKEN
);
532 case DTT_CHAR_NAME_TABLE
:
533 if (nameInvalidated
) // ignore declined names if name will changed in some way
539 if (!changenth(line
, 1, newguid
)) // character_*.guid update
540 ROLLBACK(DUMP_FILE_BROKEN
);
545 if (!changenth(line
, 1, newguid
)) // characters.guid update
546 ROLLBACK(DUMP_FILE_BROKEN
);
548 if (!changenth(line
, 2, chraccount
)) // characters.account update
549 ROLLBACK(DUMP_FILE_BROKEN
);
553 // check if the original name already exists
554 name
= getnth(line
, 3); // characters.name
555 CharacterDatabase
.escape_string(name
);
557 result
= CharacterDatabase
.PQuery("SELECT * FROM characters WHERE name = '%s'", name
.c_str());
562 if (!changenth(line
, 36, "1")) // characters.at_login set to "rename on login"
563 ROLLBACK(DUMP_FILE_BROKEN
);
565 nameInvalidated
= true;
570 if (!changenth(line
, 3, name
.c_str())) // characters.name update
571 ROLLBACK(DUMP_FILE_BROKEN
);
573 nameInvalidated
= true;
580 if (!changenth(line
, 1, newguid
)) // character_inventory.guid update
581 ROLLBACK(DUMP_FILE_BROKEN
);
583 if (!changeGuid(line
, 2, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed(), true))
584 ROLLBACK(DUMP_FILE_BROKEN
); // character_inventory.bag update
585 if (!changeGuid(line
, 4, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
586 ROLLBACK(DUMP_FILE_BROKEN
); // character_inventory.item update
591 // item, owner, data field:item, owner guid
592 if (!changeGuid(line
, 1, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
593 ROLLBACK(DUMP_FILE_BROKEN
); // item_instance.guid update
594 if (!changenth(line
, 2, newguid
)) // item_instance.owner_guid update
595 ROLLBACK(DUMP_FILE_BROKEN
);
596 std::string vals
= getnth(line
,3); // item_instance.data get
597 if (!changetokGuid(vals
, OBJECT_FIELD_GUID
+1, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
598 ROLLBACK(DUMP_FILE_BROKEN
); // item_instance.data.OBJECT_FIELD_GUID update
599 if (!changetoknth(vals
, ITEM_FIELD_OWNER
+1, newguid
))
600 ROLLBACK(DUMP_FILE_BROKEN
); // item_instance.data.ITEM_FIELD_OWNER update
601 if (!changenth(line
, 3, vals
.c_str())) // item_instance.data update
602 ROLLBACK(DUMP_FILE_BROKEN
);
607 if (!changenth(line
, 1, newguid
)) // character_gifts.guid update
608 ROLLBACK(DUMP_FILE_BROKEN
);
609 if (!changeGuid(line
, 2, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
610 ROLLBACK(DUMP_FILE_BROKEN
); // character_gifts.item_guid update
615 //store a map of old pet id to new inserted pet id for use by type 5 tables
616 snprintf(currpetid
, 20, "%s", getnth(line
, 1).c_str());
617 if (strlen(lastpetid
)==0)
618 snprintf(lastpetid
, 20, "%s", currpetid
);
620 if (strcmp(lastpetid
,currpetid
)!=0)
622 snprintf(newpetid
, 20, "%d", sObjectMgr
.GeneratePetNumber());
623 snprintf(lastpetid
, 20, "%s", currpetid
);
626 std::map
<uint32
, uint32
> :: const_iterator petids_iter
= petids
.find(atoi(currpetid
));
628 if (petids_iter
== petids
.end())
630 petids
.insert(PetIdsPair(atoi(currpetid
), atoi(newpetid
)));
633 if (!changenth(line
, 1, newpetid
)) // character_pet.id update
634 ROLLBACK(DUMP_FILE_BROKEN
);
635 if (!changenth(line
, 3, newguid
)) // character_pet.owner update
636 ROLLBACK(DUMP_FILE_BROKEN
);
640 case DTT_PET_TABLE
: // pet_aura, pet_spell, pet_spell_cooldown
642 snprintf(currpetid
, 20, "%s", getnth(line
, 1).c_str());
644 // lookup currpetid and match to new inserted pet id
645 std::map
<uint32
, uint32
> :: const_iterator petids_iter
= petids
.find(atoi(currpetid
));
646 if (petids_iter
== petids
.end()) // couldn't find new inserted id
647 ROLLBACK(DUMP_FILE_BROKEN
);
649 snprintf(newpetid
, 20, "%d", petids_iter
->second
);
651 if (!changenth(line
, 1, newpetid
)) // pet_*.guid -> petid in fact
652 ROLLBACK(DUMP_FILE_BROKEN
);
656 case DTT_PET_DECL
: // character_pet_declinedname
658 snprintf(currpetid
, 20, "%s", getnth(line
, 1).c_str());
660 // lookup currpetid and match to new inserted pet id
661 std::map
<uint32
, uint32
> :: const_iterator petids_iter
= petids
.find(atoi(currpetid
));
662 if (petids_iter
== petids
.end()) // couldn't find new inserted id
663 ROLLBACK(DUMP_FILE_BROKEN
);
665 snprintf(newpetid
, 20, "%d", petids_iter
->second
);
667 if (!changenth(line
, 1, newpetid
)) // character_pet_declinedname.id
668 ROLLBACK(DUMP_FILE_BROKEN
);
670 if (!changenth(line
, 2, newguid
)) // character_pet_declinedname.owner update
671 ROLLBACK(DUMP_FILE_BROKEN
);
675 case DTT_MAIL
: // mail
677 if (!changeGuid(line
, 1, mails
, sObjectMgr
.m_MailIds
.GetNextAfterMaxUsed()))
678 ROLLBACK(DUMP_FILE_BROKEN
); // mail.id update
679 if (!changenth(line
, 6, newguid
)) // mail.receiver update
680 ROLLBACK(DUMP_FILE_BROKEN
);
683 case DTT_MAIL_ITEM
: // mail_items
685 if (!changeGuid(line
, 1, mails
, sObjectMgr
.m_MailIds
.GetNextAfterMaxUsed()))
686 ROLLBACK(DUMP_FILE_BROKEN
); // mail_items.id
687 if (!changeGuid(line
, 2, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
688 ROLLBACK(DUMP_FILE_BROKEN
); // mail_items.item_guid
689 if (!changenth(line
, 4, newguid
)) // mail_items.receiver
690 ROLLBACK(DUMP_FILE_BROKEN
);
693 case DTT_EQSET_TABLE
:
694 if (!changenth(line
, 1, newguid
)) // character_equipmentsets.guid update
695 ROLLBACK(DUMP_FILE_BROKEN
);
696 if (!changeGuid(line
, 2, eqsets
, sObjectMgr
.m_EquipmentSetIds
.GetNextAfterMaxUsed()))
697 ROLLBACK(DUMP_FILE_BROKEN
); // character_equipmentsets.setguid
698 for(int i
= 0; i
< 19; ++i
) // character_equipmentsets.item0..item18
699 if(!changeGuid(line
, 6+i
, items
, sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed()))
700 ROLLBACK(DUMP_FILE_BROKEN
);
704 sLog
.outError("Unknown dump table type: %u",type
);
708 if (execute_ok
&& !CharacterDatabase
.Execute(line
.c_str()))
709 ROLLBACK(DUMP_FILE_BROKEN
);
712 CharacterDatabase
.CommitTransaction();
714 //FIXME: current code with post-updating guids not safe for future per-map threads
715 sObjectMgr
.m_ItemGuids
.Set(sObjectMgr
.m_ItemGuids
.GetNextAfterMaxUsed() + items
.size());
716 sObjectMgr
.m_MailIds
.Set(sObjectMgr
.m_MailIds
.GetNextAfterMaxUsed() + mails
.size());
717 sObjectMgr
.m_EquipmentSetIds
.Set(sObjectMgr
.m_EquipmentSetIds
.GetNextAfterMaxUsed() + eqsets
.size());
720 sObjectMgr
.m_CharGuids
.Set(sObjectMgr
.m_CharGuids
.GetNextAfterMaxUsed()+1);