root/trunk/src/game/PlayerDump.cpp @ 268

Revision 263, 20.7 kB (checked in by yumileroy, 17 years ago)

Some missing changes. This should fix the bug that loading char causes crash.
Please do not commit to the other tip (I do not know how to delete it).

Original author: megamage
Date: 2008-11-20 17:40:13-06:00

Line 
1/*
2 * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/>
3 *
4 * Copyright (C) 2008 Trinity <http://www.trinitycore.org/>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21#include "Common.h"
22#include "PlayerDump.h"
23#include "Database/DatabaseEnv.h"
24#include "Database/SQLStorage.h"
25#include "UpdateFields.h"
26#include "ObjectMgr.h"
27
28// Character Dump tables
29#define DUMP_TABLE_COUNT 19
30
31struct DumpTable
32{
33    char const* name;
34    DumpTableType type;
35};
36
37static DumpTable dumpTables[DUMP_TABLE_COUNT] =
38{
39    { "characters",               DTT_CHARACTER  },
40    { "character_queststatus",    DTT_CHAR_TABLE },
41    { "character_reputation",     DTT_CHAR_TABLE },
42    { "character_spell",          DTT_CHAR_TABLE },
43    { "character_spell_cooldown", DTT_CHAR_TABLE },
44    { "character_action",         DTT_CHAR_TABLE },
45    { "character_aura",           DTT_CHAR_TABLE },
46    { "character_homebind",       DTT_CHAR_TABLE },
47    { "character_ticket",         DTT_CHAR_TABLE },
48    { "character_inventory",      DTT_INVENTORY  },
49    { "mail",                     DTT_MAIL       },
50    { "mail_items",               DTT_MAIL_ITEM  },
51    { "item_instance",            DTT_ITEM       },
52    { "character_gifts",          DTT_ITEM_GIFT  },
53    { "item_text",                DTT_ITEM_TEXT  },
54    { "character_pet",            DTT_PET        },
55    { "pet_aura",                 DTT_PET_TABLE  },
56    { "pet_spell",                DTT_PET_TABLE  },
57    { "pet_spell_cooldown",       DTT_PET_TABLE  },
58};
59
60// Low level functions
61static bool findtoknth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e)
62{
63    int i; s = e = 0;
64    std::string::size_type size = str.size();
65    for(i = 1; s < size && i < n; s++) if(str[s] == ' ') ++i;
66    if (i < n)
67        return false;
68
69    e = str.find(' ', s);
70
71    return e != std::string::npos;
72}
73
74std::string gettoknth(std::string &str, int n)
75{
76    std::string::size_type s = 0, e = 0;
77    if(!findtoknth(str, n, s, e))
78        return "";
79
80    return str.substr(s, e-s);
81}
82
83bool findnth(std::string &str, int n, std::string::size_type &s, std::string::size_type &e)
84{
85    s = str.find("VALUES ('")+9;
86    if (s == std::string::npos) return false;
87
88    do
89    {
90        e = str.find("'",s);
91        if (e == std::string::npos) return false;
92    } while(str[e-1] == '\\');
93
94    for(int i = 1; i < n; i++)
95    {
96        do
97        {
98            s = e+4;
99            e = str.find("'",s);
100            if (e == std::string::npos) return false;
101        } while (str[e-1] == '\\');
102    }
103    return true;
104}
105
106std::string gettablename(std::string &str)
107{
108    std::string::size_type s = 13;
109    std::string::size_type e = str.find(_TABLE_SIM_, s);
110    if (e == std::string::npos)
111        return "";
112
113    return str.substr(s, e-s);
114}
115
116bool changenth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false)
117{
118    std::string::size_type s, e;
119    if(!findnth(str,n,s,e))
120        return false;
121
122    if(nonzero && str.substr(s,e-s) == "0")
123        return true;                                        // not an error
124    if(!insert)
125        str.replace(s,e-s, with);
126    else
127        str.insert(s, with);
128
129    return true;
130}
131
132std::string getnth(std::string &str, int n)
133{
134    std::string::size_type s, e;
135    if(!findnth(str,n,s,e))
136        return "";
137
138    return str.substr(s, e-s);
139}
140
141bool changetoknth(std::string &str, int n, const char *with, bool insert = false, bool nonzero = false)
142{
143    std::string::size_type s = 0, e = 0;
144    if(!findtoknth(str, n, s, e))
145        return false;
146    if(nonzero && str.substr(s,e-s) == "0")
147        return true;                                        // not an error
148    if(!insert)
149        str.replace(s, e-s, with);
150    else
151        str.insert(s, with);
152
153    return true;
154}
155
156uint32 registerNewGuid(uint32 oldGuid, std::map<uint32, uint32> &guidMap, uint32 hiGuid)
157{
158    std::map<uint32, uint32>::iterator itr = guidMap.find(oldGuid);
159    if(itr != guidMap.end())
160        return itr->second;
161
162    uint32 newguid = hiGuid + guidMap.size();
163    guidMap[oldGuid] = newguid;
164    return newguid;
165}
166
167bool changeGuid(std::string &str, int n, std::map<uint32, uint32> &guidMap, uint32 hiGuid, bool nonzero = false)
168{
169    char chritem[20];
170    uint32 oldGuid = atoi(getnth(str, n).c_str());
171    if (nonzero && oldGuid == 0)
172        return true;                                        // not an error
173
174    uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid);
175    snprintf(chritem, 20, "%d", newGuid);
176
177    return changenth(str, n, chritem, false, nonzero);
178}
179
180bool changetokGuid(std::string &str, int n, std::map<uint32, uint32> &guidMap, uint32 hiGuid, bool nonzero = false)
181{
182    char chritem[20];
183    uint32 oldGuid = atoi(gettoknth(str, n).c_str());
184    if (nonzero && oldGuid == 0)
185        return true;                                        // not an error
186
187    uint32 newGuid = registerNewGuid(oldGuid, guidMap, hiGuid);
188    snprintf(chritem, 20, "%d", newGuid);
189
190    return changetoknth(str, n, chritem, false, nonzero);
191}
192
193std::string CreateDumpString(char const* tableName, QueryResult *result)
194{
195    if(!tableName || !result) return "";
196    std::ostringstream ss;
197    ss << "INSERT INTO "<< _TABLE_SIM_ << tableName << _TABLE_SIM_ << " VALUES (";
198    Field *fields = result->Fetch();
199    for(uint32 i = 0; i < result->GetFieldCount(); i++)
200    {
201        if (i == 0) ss << "'";
202        else ss << ", '";
203
204        std::string s = fields[i].GetCppString();
205        CharacterDatabase.escape_string(s);
206        ss << s;
207
208        ss << "'";
209    }
210    ss << ");";
211    return ss.str();
212}
213
214std::string PlayerDumpWriter::GenerateWhereStr(char const* field, uint32 guid)
215{
216    std::ostringstream wherestr;
217    wherestr << field << " = '" << guid << "'";
218    return wherestr.str();
219}
220
221std::string PlayerDumpWriter::GenerateWhereStr(char const* field, GUIDs const& guids, GUIDs::const_iterator& itr)
222{
223    std::ostringstream wherestr;
224    wherestr << field << " IN ('";
225    for(; itr != guids.end(); ++itr)
226    {
227        wherestr << *itr;
228
229        if(wherestr.str().size() > MAX_QUERY_LEN - 50)      // near to max query
230        {
231            ++itr;
232            break;
233        }
234
235        GUIDs::const_iterator itr2 = itr;
236        if(++itr2 != guids.end())
237            wherestr << "','";
238    }
239    wherestr << "')";
240    return wherestr.str();
241}
242
243void StoreGUID(QueryResult *result,uint32 field,std::set<uint32>& guids)
244{
245    Field* fields = result->Fetch();
246    uint32 guid = fields[field].GetUInt32();
247    if(guid)
248        guids.insert(guid);
249}
250
251void StoreGUID(QueryResult *result,uint32 data,uint32 field, std::set<uint32>& guids)
252{
253    Field* fields = result->Fetch();
254    std::string dataStr = fields[data].GetCppString();
255    uint32 guid = atoi(gettoknth(dataStr, field).c_str());
256    if(guid)
257        guids.insert(guid);
258}
259
260// Writing - High-level functions
261void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type)
262{
263    GUIDs const* guids = NULL;
264    char const* fieldname = NULL;
265
266    switch ( type )
267    {
268        case DTT_ITEM:      fieldname = "guid";      guids = &items; break;
269        case DTT_ITEM_GIFT: fieldname = "item_guid"; guids = &items; break;
270        case DTT_PET:       fieldname = "owner";                     break;
271        case DTT_PET_TABLE: fieldname = "guid";      guids = &pets;  break;
272        case DTT_MAIL:      fieldname = "receiver";                  break;
273        case DTT_MAIL_ITEM: fieldname = "mail_id";   guids = &mails; break;
274        case DTT_ITEM_TEXT: fieldname = "id";        guids = &texts; break;
275        default:            fieldname = "guid";                      break;
276    }
277
278    // for guid set stop if set is empty
279    if(guids && guids->empty())
280        return;                                             // nothing to do
281
282    // setup for guids case start position
283    GUIDs::const_iterator guids_itr;
284    if(guids)
285        guids_itr = guids->begin();
286
287    do
288    {
289        std::string wherestr;
290
291        if(guids)                                           // set case, get next guids string
292            wherestr = GenerateWhereStr(fieldname,*guids,guids_itr);
293        else                                                // not set case, get single guid string
294            wherestr = GenerateWhereStr(fieldname,guid);
295
296        QueryResult *result = CharacterDatabase.PQuery("SELECT * FROM %s WHERE %s", tableFrom, wherestr.c_str());
297        if(!result)
298            return;
299
300        do
301        {
302            // collect guids
303            switch ( type )
304            {
305            case DTT_INVENTORY:
306                StoreGUID(result,3,items); break;           // item guid collection
307            case DTT_ITEM:
308                StoreGUID(result,0,ITEM_FIELD_ITEM_TEXT_ID,texts); break;
309                // item text id collection
310            case DTT_PET:
311                StoreGUID(result,0,pets);  break;           // pet guid collection
312            case DTT_MAIL:
313                StoreGUID(result,0,mails);                  // mail id collection
314                StoreGUID(result,6,texts); break;           // item text id collection
315            case DTT_MAIL_ITEM:
316                StoreGUID(result,1,items); break;           // item guid collection
317            default:                       break;
318            }
319
320            dump += CreateDumpString(tableTo, result);
321            dump += "\n";
322        }
323        while (result->NextRow());
324
325        delete result;
326    }
327    while(guids && guids_itr != guids->end());              // not set case iterate single time, set case iterate for all guids
328}
329
330std::string PlayerDumpWriter::GetDump(uint32 guid)
331{
332    std::string dump;
333    for(int i = 0; i < DUMP_TABLE_COUNT; i++)
334        DumpTable(dump, guid, dumpTables[i].name, dumpTables[i].name, dumpTables[i].type);
335
336    // TODO: Add instance/group..
337    // TODO: Add a dump level option to skip some non-important tables
338
339    return dump;
340}
341
342DumpReturn PlayerDumpWriter::WriteDump(std::string file, uint32 guid)
343{
344    FILE *fout = fopen(file.c_str(), "w");
345    if (!fout)
346        return DUMP_FILE_OPEN_ERROR;
347
348    std::string dump = GetDump(guid);
349
350    fprintf(fout,"%s\n",dump.c_str());
351    fclose(fout);
352    return DUMP_SUCCESS;
353}
354
355// Reading - High-level functions
356#define ROLLBACK(DR) {CharacterDatabase.RollbackTransaction(); fclose(fin); return (DR);}
357
358DumpReturn PlayerDumpReader::LoadDump(std::string file, uint32 account, std::string name, uint32 guid)
359{
360    // check character count
361    {
362        QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", account);
363        uint8 charcount = 0;
364        if ( result )
365        {
366            Field *fields=result->Fetch();
367            charcount = fields[0].GetUInt8();
368            delete result;
369
370            if (charcount >= 10)
371                return DUMP_TOO_MANY_CHARS;
372        }
373    }
374
375    FILE *fin = fopen(file.c_str(), "r");
376    if(!fin)
377        return DUMP_FILE_OPEN_ERROR;
378
379    QueryResult * result = NULL;
380    char newguid[20], chraccount[20], newpetid[20], currpetid[20], lastpetid[20];
381
382    // make sure the same guid doesn't already exist and is safe to use
383    bool incHighest = true;
384    if(guid != 0 && guid < objmgr.m_hiCharGuid)
385    {
386        result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE guid = '%d'", guid);
387        if (result)
388        {
389            guid = objmgr.m_hiCharGuid;                     // use first free if exists
390            delete result;
391        }
392        else incHighest = false;
393    }
394    else
395        guid = objmgr.m_hiCharGuid;
396
397    // normalize the name if specified and check if it exists
398    if(!normalizePlayerName(name))
399        name = "";
400
401    if(ObjectMgr::IsValidName(name,true))
402    {
403        CharacterDatabase.escape_string(name);              // for safe, we use name only for sql quearies anyway
404        result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str());
405        if (result)
406        {
407            name = "";                                      // use the one from the dump
408            delete result;
409        }
410    }
411    else name = "";
412
413    // name encoded or empty
414
415    snprintf(newguid, 20, "%d", guid);
416    snprintf(chraccount, 20, "%d", account);
417    snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber());
418    snprintf(lastpetid, 20, "%s", "");
419
420    std::map<uint32,uint32> items;
421    std::map<uint32,uint32> mails;
422    char buf[32000] = "";
423
424    typedef std::map<uint32, uint32> PetIds;                // old->new petid relation
425    typedef PetIds::value_type PetIdsPair;
426    PetIds petids;
427
428    CharacterDatabase.BeginTransaction();
429    while(!feof(fin))
430    {
431        if(!fgets(buf, 32000, fin))
432        {
433            if(feof(fin)) break;
434            ROLLBACK(DUMP_FILE_BROKEN);
435        }
436
437        std::string line; line.assign(buf);
438
439        // skip empty strings
440        if(line.find_first_not_of(" \t\n\r\7")==std::string::npos)
441            continue;
442
443        // determine table name and load type
444        std::string tn = gettablename(line);
445        if(tn.empty())
446        {
447            sLog.outError("LoadPlayerDump: Can't extract table name from line: '%s'!", line.c_str());
448            ROLLBACK(DUMP_FILE_BROKEN);
449        }
450
451        DumpTableType type;
452        uint8 i;
453        for(i = 0; i < DUMP_TABLE_COUNT; i++)
454        {
455            if (tn == dumpTables[i].name)
456            {
457                type = dumpTables[i].type;
458                break;
459            }
460        }
461
462        if (i == DUMP_TABLE_COUNT)
463        {
464            sLog.outError("LoadPlayerDump: Unknown table: '%s'!", tn.c_str());
465            ROLLBACK(DUMP_FILE_BROKEN);
466        }
467
468        // change the data to server values
469        switch(type)
470        {
471            case DTT_CHAR_TABLE:
472                if(!changenth(line, 1, newguid))
473                    ROLLBACK(DUMP_FILE_BROKEN);
474                break;
475
476            case DTT_CHARACTER:                             // character t.
477            {
478                if(!changenth(line, 1, newguid))
479                    ROLLBACK(DUMP_FILE_BROKEN);
480
481                // guid, data field:guid, items
482                if(!changenth(line, 2, chraccount))
483                    ROLLBACK(DUMP_FILE_BROKEN);
484                std::string vals = getnth(line, 3);
485                if(!changetoknth(vals, OBJECT_FIELD_GUID+1, newguid))
486                    ROLLBACK(DUMP_FILE_BROKEN);
487                for(uint16 field = PLAYER_FIELD_INV_SLOT_HEAD; field < PLAYER_FARSIGHT; field++)
488                    if(!changetokGuid(vals, field+1, items, objmgr.m_hiItemGuid, true))
489                        ROLLBACK(DUMP_FILE_BROKEN);
490                if(!changenth(line, 3, vals.c_str()))
491                    ROLLBACK(DUMP_FILE_BROKEN);
492                if (name == "")
493                {
494                    // check if the original name already exists
495                    name = getnth(line, 4);
496                    CharacterDatabase.escape_string(name);
497
498                    result = CharacterDatabase.PQuery("SELECT * FROM characters WHERE name = '%s'", name.c_str());
499                    if (result)
500                    {
501                        delete result;
502                                                            // rename on login: `at_login` field 30 in raw field list
503                        if(!changenth(line, 30, "1"))
504                            ROLLBACK(DUMP_FILE_BROKEN);
505                    }
506                }
507                else if(!changenth(line, 4, name.c_str()))
508                    ROLLBACK(DUMP_FILE_BROKEN);
509
510                break;
511            }
512            case DTT_INVENTORY:                             // character_inventory t.
513            {
514                if(!changenth(line, 1, newguid))
515                    ROLLBACK(DUMP_FILE_BROKEN);
516
517                // bag, item
518                if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid, true))
519                    ROLLBACK(DUMP_FILE_BROKEN);
520                if(!changeGuid(line, 4, items, objmgr.m_hiItemGuid))
521                    ROLLBACK(DUMP_FILE_BROKEN);
522                break;
523            }
524            case DTT_ITEM:                                  // item_instance t.
525            {
526                // item, owner, data field:item, owner guid
527                if(!changeGuid(line, 1, items, objmgr.m_hiItemGuid))
528                    ROLLBACK(DUMP_FILE_BROKEN);
529                if(!changenth(line, 2, newguid))
530                    ROLLBACK(DUMP_FILE_BROKEN);
531                std::string vals = getnth(line,3);
532                if(!changetokGuid(vals, OBJECT_FIELD_GUID+1, items, objmgr.m_hiItemGuid))
533                    ROLLBACK(DUMP_FILE_BROKEN);
534                if(!changetoknth(vals, ITEM_FIELD_OWNER+1, newguid))
535                    ROLLBACK(DUMP_FILE_BROKEN);
536                if(!changenth(line, 3, vals.c_str()))
537                    ROLLBACK(DUMP_FILE_BROKEN);
538                break;
539            }
540            case DTT_ITEM_GIFT:                             // character_gift
541            {
542                // guid,item_guid,
543                if(!changenth(line, 1, newguid))
544                    ROLLBACK(DUMP_FILE_BROKEN);
545                if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid))
546                    ROLLBACK(DUMP_FILE_BROKEN);
547                break;
548            }
549            case DTT_PET:                                   // character_pet t
550            {
551                //store a map of old pet id to new inserted pet id for use by type 5 tables
552                snprintf(currpetid, 20, "%s", getnth(line, 1).c_str());
553                if(strlen(lastpetid)==0) snprintf(lastpetid, 20, "%s", currpetid);
554                if(strcmp(lastpetid,currpetid)!=0)
555                {
556                    snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber());
557                    snprintf(lastpetid, 20, "%s", currpetid);
558                }
559
560                std::map<uint32, uint32> :: const_iterator petids_iter = petids.find(atoi(currpetid));
561
562                if(petids_iter == petids.end())
563                {
564                    petids.insert(PetIdsPair(atoi(currpetid), atoi(newpetid)));
565                }
566
567                // item, entry, owner, ...
568                if(!changenth(line, 1, newpetid))
569                    ROLLBACK(DUMP_FILE_BROKEN);
570                if(!changenth(line, 3, newguid))
571                    ROLLBACK(DUMP_FILE_BROKEN);
572
573                break;
574            }
575            case DTT_PET_TABLE:                             // pet_aura, pet_spell, pet_spell_cooldown t
576            {
577                snprintf(currpetid, 20, "%s", getnth(line, 1).c_str());
578
579                // lookup currpetid and match to new inserted pet id
580                std::map<uint32, uint32> :: const_iterator petids_iter = petids.find(atoi(currpetid));
581                if(petids_iter == petids.end())             // couldn't find new inserted id
582                    ROLLBACK(DUMP_FILE_BROKEN);
583
584                snprintf(newpetid, 20, "%d", petids_iter->second);
585
586                if(!changenth(line, 1, newpetid))
587                    ROLLBACK(DUMP_FILE_BROKEN);
588
589                break;
590            }
591            case DTT_MAIL:                                  // mail
592            {
593                // id,messageType,stationery,sender,receiver
594                if(!changeGuid(line, 1, mails, objmgr.m_mailid))
595                    ROLLBACK(DUMP_FILE_BROKEN);
596                if(!changenth(line, 5, newguid))
597                    ROLLBACK(DUMP_FILE_BROKEN);
598                break;
599            }
600            case DTT_MAIL_ITEM:                             // mail_items
601            {
602                // mail_id,item_guid,item_template,receiver
603                if(!changeGuid(line, 1, mails, objmgr.m_mailid))
604                    ROLLBACK(DUMP_FILE_BROKEN);
605                if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid))
606                    ROLLBACK(DUMP_FILE_BROKEN);
607                if(!changenth(line, 4, newguid))
608                    ROLLBACK(DUMP_FILE_BROKEN);
609                break;
610            }
611            default:
612                sLog.outError("Unknown dump table type: %u",type);
613                break;
614        }
615
616        if(!CharacterDatabase.Execute(line.c_str()))
617            ROLLBACK(DUMP_FILE_BROKEN);
618    }
619
620    CharacterDatabase.CommitTransaction();
621
622    objmgr.m_hiItemGuid += items.size();
623    objmgr.m_mailid     += mails.size();
624
625    if(incHighest)
626        ++objmgr.m_hiCharGuid;
627
628    fclose(fin);
629
630    return DUMP_SUCCESS;
631}
Note: See TracBrowser for help on using the browser.