SQLite rollback
[oweals/minetest.git] / src / rollback.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "rollback.h"
21 #include <fstream>
22 #include <list>
23 #include <sstream>
24 #include "log.h"
25 #include "mapnode.h"
26 #include "gamedef.h"
27 #include "nodedef.h"
28 #include "util/serialize.h"
29 #include "util/string.h"
30 #include "util/numeric.h"
31 #include "inventorymanager.h" // deserializing InventoryLocations
32 #include "sqlite3.h"
33 #include "filesys.h"
34
35 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
36
37 #define POINTS_PER_NODE (16.0)
38
39 std::string dbp;
40 sqlite3* dbh;
41 sqlite3_stmt* dbs_insert;
42 sqlite3_stmt* dbs_replace;
43 sqlite3_stmt* dbs_select;
44 sqlite3_stmt* dbs_select_range;
45 sqlite3_stmt* dbs_select_withActor;
46 sqlite3_stmt* dbs_knownActor_select;
47 sqlite3_stmt* dbs_knownActor_insert;
48 sqlite3_stmt* dbs_knownNode_select;
49 sqlite3_stmt* dbs_knownNode_insert;
50
51 struct Stack
52 {
53         int node;
54         int quantity;
55 };
56 struct ActionRow
57 {
58         int                     id;
59         int                     actor;
60         int                     timestamp;
61         int                     type;
62         std::string     location, list;
63         int                     index, add;
64         Stack           stack;
65         int                     nodeMeta;
66         int                     x, y, z;
67         int                     oldNode;
68         int                     oldParam1, oldParam2;
69         std::string     oldMeta;
70         int                     newNode;
71         int                     newParam1, newParam2;
72         std::string     newMeta;
73         int                     guessed;
74 };
75 struct Entity
76 {
77         int                     id;
78         std::string     name;
79 };
80 typedef std::vector<Entity> Entities;
81
82 Entities KnownActors;
83 Entities KnownNodes;
84
85 void registerNewActor (int id, std::string name)
86 {
87         Entity newActor;
88
89         newActor.id   = id;
90         newActor.name = name;
91
92         KnownActors.push_back(newActor);
93
94         //std::cout << "New actor registered: " << id << " | " << name << std::endl;
95 }
96 void registerNewNode (int id, std::string name)
97 {
98         Entity newNode;
99
100         newNode.id   = id;
101         newNode.name = name;
102
103         KnownNodes.push_back(newNode);
104
105         //std::cout << "New node registered: " << id << " | " << name << std::endl;
106 }
107 int getActorId (std::string name)
108 {
109         Entities::const_iterator iter;
110
111         for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
112                 if (iter->name == name)
113                         return iter->id;
114
115         sqlite3_reset     (dbs_knownActor_insert);
116         sqlite3_bind_text (dbs_knownActor_insert, 1, name.c_str(), -1, NULL);
117         sqlite3_step      (dbs_knownActor_insert);
118
119         int id = sqlite3_last_insert_rowid(dbh);
120
121         //std::cout << "Actor ID insert returns " << insert << std::endl;
122
123         registerNewActor(id, name);
124
125         return id;
126 }
127 int getNodeId (std::string name)
128 {
129         Entities::const_iterator iter;
130
131         for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter)
132                 if (iter->name == name)
133                         return iter->id;
134
135         sqlite3_reset     (dbs_knownNode_insert);
136         sqlite3_bind_text (dbs_knownNode_insert, 1, name.c_str(), -1, NULL);
137         sqlite3_step      (dbs_knownNode_insert);
138
139         int id = sqlite3_last_insert_rowid(dbh);
140
141         registerNewNode(id, name);
142
143         return id;
144 }
145 const char * getActorName (int id)
146 {
147         Entities::const_iterator iter;
148
149         //std::cout << "getActorName of id " << id << std::endl;
150
151         for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
152                 if (iter->id == id)
153                         return iter->name.c_str();
154
155         return "";
156 }
157 const char * getNodeName (int id)
158 {
159         Entities::const_iterator iter;
160
161         //std::cout << "getNodeName of id " << id << std::endl;
162
163         for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter)
164                 if (iter->id == id)
165                         return iter->name.c_str();
166
167         return "";
168 }
169 Stack getStackFromString (std::string text)
170 {
171         Stack stack;
172
173         size_t off = text.find_last_of(" ");
174
175         stack.node     = getNodeId(text.substr(0, off));
176         stack.quantity = atoi(text.substr(off + 1).c_str());
177
178         return stack;
179 }
180 std::string getStringFromStack (Stack stack)
181 {
182         std::string text;
183
184         text.append(getNodeName(stack.node));
185         text.append(" ");
186         text.append(itos(stack.quantity));
187
188         return text;
189 }
190 bool SQL_createDatabase (void)
191 {
192         infostream << "CreateDB:" << dbp << std::endl;
193
194         int dbs = sqlite3_exec(
195                 dbh
196         ,       "CREATE TABLE IF NOT EXISTS `actor` ("
197                 "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
198                 "`name` TEXT NOT NULL);"
199                 "CREATE TABLE IF NOT EXISTS `node` ("
200                 "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
201                 "`name` TEXT NOT NULL);"
202                 "CREATE TABLE IF NOT EXISTS `action` ("
203                 "`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
204                 "`actor` INTEGER NOT NULL,"
205                 "`timestamp` INTEGER NOT NULL,"
206                 "`type` INTEGER NOT NULL,"
207                 "`list` TEXT,"
208                 "`index` INTEGER,"
209                 "`add` INTEGER,"
210                 "`stackNode` INTEGER,"
211                 "`stackQuantity` INTEGER,"
212                 "`nodeMeta` INTEGER,"
213                 "`x` INT,"
214                 "`y` INT,"
215                 "`z` INT,"
216                 "`oldNode` INTEGER,"
217                 "`oldParam1` INTEGER,"
218                 "`oldParam2` INTEGER,"
219                 "`oldMeta` TEXT,"
220                 "`newNode` INTEGER,"
221                 "`newParam1` INTEGER,"
222                 "`newParam2` INTEGER,"
223                 "`newMeta` TEXT,"
224                 "`guessedActor` INTEGER,"
225                 "FOREIGN KEY (`actor`)   REFERENCES `actor`(`id`),"
226                 "FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`),"
227                 "FOREIGN KEY (`newNode`) REFERENCES `node`(`id`));"
228                 "CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);"
229                 "CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);"
230         ,       NULL, NULL, NULL
231         );
232         if (dbs == SQLITE_ABORT)
233                 throw FileNotGoodException("Could not create sqlite3 database structure");
234         else
235         if (dbs != 0)
236                 throw FileNotGoodException("SQL Rollback: Exec statement to create table structure returned a non-zero value");
237         else
238                 infostream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
239
240         return true;
241 }
242 void SQL_databaseCheck (void)
243 {
244         if (dbh) return;
245
246         infostream << "Database connection setup" << std::endl;
247
248         bool needsCreate = !fs::PathExists(dbp);
249         int  dbo = sqlite3_open_v2(dbp.c_str(), &dbh, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
250
251         if (dbo != SQLITE_OK)
252         {
253                 infostream
254                 <<      "SQLROLLBACK: SQLite3 database failed to open: "
255                 <<      sqlite3_errmsg(dbh)
256                 <<      std::endl;
257                 throw FileNotGoodException("Cannot open database file");
258         }
259
260         if (needsCreate) SQL_createDatabase();
261
262         int dbr;
263
264         dbr = sqlite3_prepare_v2(
265                 dbh
266         ,       "INSERT INTO `action`"
267                 "       (       `actor`, `timestamp`, `type`,"
268                 "               `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,"
269                 "               `x`, `y`, `z`,"
270                 "               `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,"
271                 "               `newNode`, `newParam1`, `newParam2`, `newMeta`,"
272                 "               `guessedActor`"
273                 "       )"
274                 "VALUES"
275                 "       (       ?, ?, ?,"
276                 "               ?, ?, ?, ?, ?, ?,"
277                 "               ?, ?, ?,"
278                 "               ?, ?, ?, ?,"
279                 "               ?, ?, ?, ?,"
280                 "               ?"
281                 "       );"
282         ,       -1, &dbs_insert, NULL
283         );
284
285         if (dbr != SQLITE_OK)
286                 throw FileNotGoodException(sqlite3_errmsg(dbh));
287
288         dbr = sqlite3_prepare_v2(
289                 dbh
290         ,       "REPLACE INTO `action`"
291                 "       (       `actor`, `timestamp`, `type`,"
292                 "               `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,"
293                 "               `x`, `y`, `z`,"
294                 "               `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,"
295                 "               `newNode`, `newParam1`, `newParam2`, `newMeta`,"
296                 "               `guessedActor`, `id`"
297                 "       )"
298                 "VALUES"
299                 "       (       ?, ?, ?,"
300                 "               ?, ?, ?, ?, ?, ?,"
301                 "               ?, ?, ?,"
302                 "               ?, ?, ?, ?,"
303                 "               ?, ?, ?, ?,"
304                 "               ?, ?"
305                 "       );"
306         ,       -1, &dbs_replace, NULL
307         );
308
309         if (dbr != SQLITE_OK)
310                 throw FileNotGoodException(sqlite3_errmsg(dbh));
311
312         dbr = sqlite3_prepare_v2(dbh
313         ,       "SELECT "
314                 "               `actor`, `timestamp`, `type`"
315                 "       ,       `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
316                 "       ,       `x`, `y`, `z`"
317                 "       ,       `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
318                 "       ,       `newNode`, `newParam1`, `newParam2`, `newMeta`"
319                 "       ,       `guessedActor`"
320                 " FROM  `action`"
321                 " WHERE `timestamp` >= ?"
322                 " ORDER BY `timestamp` DESC, `id` DESC"
323         ,       -1, &dbs_select, NULL
324         );
325         if (dbr != SQLITE_OK)
326                 throw FileNotGoodException(itos(dbr).c_str());
327
328         dbr = sqlite3_prepare_v2(dbh
329         ,       "SELECT "
330                 "               `actor`, `timestamp`, `type`"
331                 "       ,       `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
332                 "       ,       `x`, `y`, `z`"
333                 "       ,       `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
334                 "       ,       `newNode`, `newParam1`, `newParam2`, `newMeta`"
335                 "       ,       `guessedActor`"
336                 " FROM  `action`"
337                 " WHERE `timestamp` >= ?"
338                 " AND   `x` IS NOT NULL"
339                 " AND   `y` IS NOT NULL"
340                 " AND   `z` IS NOT NULL"
341                 " AND   (ABS(`x`) - ABS(?)) <= ?"
342                 " AND   (ABS(`y`) - ABS(?)) <= ?"
343                 " AND   (ABS(`z`) - ABS(?)) <= ?"
344                 " ORDER BY `timestamp` DESC, `id` DESC"
345                 " LIMIT 0,5"
346         ,       -1, &dbs_select_range, NULL
347         );
348         if (dbr != SQLITE_OK)
349                 throw FileNotGoodException(itos(dbr).c_str());
350
351         dbr = sqlite3_prepare_v2(dbh
352         ,       "SELECT "
353                 "               `actor`, `timestamp`, `type`"
354                 "       ,       `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
355                 "       ,       `x`, `y`, `z`"
356                 "       ,       `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
357                 "       ,       `newNode`, `newParam1`, `newParam2`, `newMeta`"
358                 "       ,       `guessedActor`"
359                 " FROM  `action`"
360                 " WHERE `timestamp` >= ?"
361                 " AND   `actor` = ?"
362                 " ORDER BY `timestamp` DESC, `id` DESC"
363         ,       -1, &dbs_select_withActor, NULL
364         );
365         if (dbr != SQLITE_OK)
366                 throw FileNotGoodException(itos(dbr).c_str());
367
368         dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `actor`", -1, &dbs_knownActor_select, NULL);
369         if (dbr != SQLITE_OK)
370                 throw FileNotGoodException(itos(dbr).c_str());
371
372         dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `actor` (`name`) VALUES (?)", -1, &dbs_knownActor_insert, NULL);
373         if (dbr != SQLITE_OK)
374                 throw FileNotGoodException(itos(dbr).c_str());
375
376         dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `node`", -1, &dbs_knownNode_select, NULL);
377         if (dbr != SQLITE_OK)
378                 throw FileNotGoodException(itos(dbr).c_str());
379
380         dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `node` (`name`) VALUES (?)", -1, &dbs_knownNode_insert, NULL);
381         if (dbr != SQLITE_OK)
382                 throw FileNotGoodException(itos(dbr).c_str());
383
384         infostream << "SQL prepared statements setup correctly" << std::endl;
385
386         int select;
387
388         sqlite3_reset(dbs_knownActor_select);
389         while (SQLITE_ROW == (select = sqlite3_step(dbs_knownActor_select)))
390                 registerNewActor(
391                                 sqlite3_column_int  (dbs_knownActor_select, 0),
392                                 reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownActor_select, 1))
393                 );
394
395         sqlite3_reset(dbs_knownNode_select);
396         while (SQLITE_ROW == (select = sqlite3_step(dbs_knownNode_select)))
397                 registerNewNode(
398                                 sqlite3_column_int  (dbs_knownNode_select, 0),
399                                 reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownNode_select, 1))
400                 );
401
402         return;
403 }
404 bool SQL_registerRow (ActionRow row)
405 {
406         SQL_databaseCheck();
407
408         sqlite3_stmt * dbs_do = (row.id)? dbs_replace: dbs_insert;
409
410         /*
411         std::cout
412                 << (row.id? "Replacing": "Inserting") 
413                 << " ActionRow" << std::endl;
414         */
415         sqlite3_reset(dbs_do);
416
417         int bind [20 + (((bool) row.id)? 1: 0)], ii = 0;
418         bool nodeMeta = false;
419
420         bind[ii++] = sqlite3_bind_int (dbs_do, 1, row.actor);
421         bind[ii++] = sqlite3_bind_int (dbs_do, 2, row.timestamp);
422         bind[ii++] = sqlite3_bind_int (dbs_do, 3, row.type);
423
424         if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
425         {
426                 std::string loc         = row.location;
427                 std::string locType     = loc.substr(0, loc.find(":"));
428                 nodeMeta = (locType == "nodemeta");
429
430                 bind[ii++] = sqlite3_bind_text (dbs_do, 4, row.list.c_str(), row.list.size(), NULL);
431                 bind[ii++] = sqlite3_bind_int  (dbs_do, 5, row.index);
432                 bind[ii++] = sqlite3_bind_int  (dbs_do, 6, row.add);
433                 bind[ii++] = sqlite3_bind_int  (dbs_do, 7, row.stack.node);
434                 bind[ii++] = sqlite3_bind_int  (dbs_do, 8, row.stack.quantity);
435                 bind[ii++] = sqlite3_bind_int  (dbs_do, 9, (int) nodeMeta);
436
437                 if (nodeMeta)
438                 {
439                         std::string x, y, z;
440                         int     l, r;
441                         l = loc.find(':') + 1;
442                         r = loc.find(',');
443                         x = loc.substr(l, r - l);
444                         l = r + 1;
445                         r = loc.find(',', l);
446                         y = loc.substr(l, r - l);
447                         z = loc.substr(r +1);
448                         bind[ii++] = sqlite3_bind_int (dbs_do, 10, atoi(x.c_str()));
449                         bind[ii++] = sqlite3_bind_int (dbs_do, 11, atoi(y.c_str()));
450                         bind[ii++] = sqlite3_bind_int (dbs_do, 12, atoi(z.c_str()));
451                 }
452         }
453                 else
454         {
455                 bind[ii++] = sqlite3_bind_null (dbs_do, 4);
456                 bind[ii++] = sqlite3_bind_null (dbs_do, 5);
457                 bind[ii++] = sqlite3_bind_null (dbs_do, 6);
458                 bind[ii++] = sqlite3_bind_null (dbs_do, 7);
459                 bind[ii++] = sqlite3_bind_null (dbs_do, 8);
460                 bind[ii++] = sqlite3_bind_null (dbs_do, 9);
461         }
462
463         if (row.type == RollbackAction::TYPE_SET_NODE)
464         {
465                 bind[ii++] = sqlite3_bind_int  (dbs_do, 10, row.x);
466                 bind[ii++] = sqlite3_bind_int  (dbs_do, 11, row.y);
467                 bind[ii++] = sqlite3_bind_int  (dbs_do, 12, row.z);
468                 bind[ii++] = sqlite3_bind_int  (dbs_do, 13, row.oldNode);
469                 bind[ii++] = sqlite3_bind_int  (dbs_do, 14, row.oldParam1);
470                 bind[ii++] = sqlite3_bind_int  (dbs_do, 15, row.oldParam2);
471                 bind[ii++] = sqlite3_bind_text (dbs_do, 16, row.oldMeta.c_str(), row.oldMeta.size(), NULL);
472                 bind[ii++] = sqlite3_bind_int  (dbs_do, 17, row.newNode);
473                 bind[ii++] = sqlite3_bind_int  (dbs_do, 18, row.newParam1);
474                 bind[ii++] = sqlite3_bind_int  (dbs_do, 19, row.newParam2);
475                 bind[ii++] = sqlite3_bind_text (dbs_do, 20, row.newMeta.c_str(), row.newMeta.size(), NULL);
476                 bind[ii++] = sqlite3_bind_int  (dbs_do, 21, row.guessed? 1: 0);
477         }
478                 else
479         {
480                 if (!nodeMeta)
481                 {
482                         bind[ii++] = sqlite3_bind_null (dbs_do, 10);
483                         bind[ii++] = sqlite3_bind_null (dbs_do, 11);
484                         bind[ii++] = sqlite3_bind_null (dbs_do, 12);
485                 }
486                 bind[ii++] = sqlite3_bind_null (dbs_do, 13);
487                 bind[ii++] = sqlite3_bind_null (dbs_do, 14);
488                 bind[ii++] = sqlite3_bind_null (dbs_do, 15);
489                 bind[ii++] = sqlite3_bind_null (dbs_do, 16);
490                 bind[ii++] = sqlite3_bind_null (dbs_do, 17);
491                 bind[ii++] = sqlite3_bind_null (dbs_do, 18);
492                 bind[ii++] = sqlite3_bind_null (dbs_do, 19);
493                 bind[ii++] = sqlite3_bind_null (dbs_do, 20);
494                 bind[ii++] = sqlite3_bind_null (dbs_do, 21);
495         }
496
497         if (row.id)
498                 bind[ii++] = sqlite3_bind_int (dbs_do, 22, row.id);
499
500         for (ii = 0; ii < 20; ++ii)
501         if  (bind[ii] != SQLITE_OK)
502                 infostream
503                         << "WARNING: failed to bind param " << ii + 1
504                         << " when inserting an entry in table setnode" << std::endl;
505
506         /*
507         std::cout << "========DB-WRITTEN==========" << std::endl;
508         std::cout << "id:        " << row.id << std::endl;
509         std::cout << "actor:     " << row.actor << std::endl;
510         std::cout << "time:      " << row.timestamp << std::endl;
511         std::cout << "type:      " << row.type << std::endl;
512         if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
513         {
514                 std::cout << "Location:   " << row.location << std::endl;
515                 std::cout << "List:       " << row.list << std::endl;
516                 std::cout << "Index:      " << row.index << std::endl;
517                 std::cout << "Add:        " << row.add << std::endl;
518                 std::cout << "Stack:      " << row.stack << std::endl;
519         }
520         if (row.type == RollbackAction::TYPE_SET_NODE)
521         {
522                 std::cout << "x:         " << row.x << std::endl;
523                 std::cout << "y:         " << row.y << std::endl;
524                 std::cout << "z:         " << row.z << std::endl;
525                 std::cout << "oldNode:   " << row.oldNode << std::endl;
526                 std::cout << "oldParam1: " << row.oldParam1 << std::endl;
527                 std::cout << "oldParam2: " << row.oldParam2 << std::endl;
528                 std::cout << "oldMeta:   " << row.oldMeta << std::endl;
529                 std::cout << "newNode:   " << row.newNode << std::endl;
530                 std::cout << "newParam1: " << row.newParam1 << std::endl;
531                 std::cout << "newParam2: " << row.newParam2 << std::endl;
532                 std::cout << "newMeta:   " << row.newMeta << std::endl;
533                 std::cout << "DESERIALIZE" << row.newMeta.c_str() << std::endl;
534                 std::cout << "guessed:   " << row.guessed << std::endl;
535         }
536         */
537
538         int written = sqlite3_step(dbs_do);
539
540         return written == SQLITE_DONE;
541
542         //if  (written != SQLITE_DONE)
543         //       std::cout << "WARNING: rollback action not written: " << sqlite3_errmsg(dbh) << std::endl;
544         //else std::cout << "Action correctly inserted via SQL" << std::endl;
545 }
546 std::list<ActionRow> actionRowsFromSelect (sqlite3_stmt* stmt)
547 {
548         std::list<ActionRow> rows;
549         const unsigned char * text;
550         size_t size;
551
552         while (SQLITE_ROW == sqlite3_step(stmt))
553         {
554                 ActionRow row;
555
556                 row.actor               = sqlite3_column_int (stmt, 0);
557                 row.timestamp   = sqlite3_column_int (stmt, 1); 
558                 row.type                = sqlite3_column_int (stmt, 2);
559
560                 if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
561                 {
562                         text                            = sqlite3_column_text (stmt, 3);
563                         size                            = sqlite3_column_bytes(stmt, 3);
564                         row.list                        = std::string(reinterpret_cast<const char*>(text), size);
565                         row.index                       = sqlite3_column_int  (stmt, 4);
566                         row.add                         = sqlite3_column_int  (stmt, 5);
567                         row.stack.node          = sqlite3_column_int  (stmt, 6);
568                         row.stack.quantity      = sqlite3_column_int  (stmt, 7);
569                         row.nodeMeta            = sqlite3_column_int  (stmt, 8);
570                 }
571
572                 if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta)
573                 {
574                         row.x = sqlite3_column_int (stmt,  9);
575                         row.y = sqlite3_column_int (stmt, 10);
576                         row.z = sqlite3_column_int (stmt, 11);
577                 }
578                 
579                 if (row.type == RollbackAction::TYPE_SET_NODE)
580                 {
581                         row.oldNode             = sqlite3_column_int  (stmt, 12);
582                         row.oldParam1   = sqlite3_column_int  (stmt, 13);
583                         row.oldParam2   = sqlite3_column_int  (stmt, 14);
584                         text                    = sqlite3_column_text (stmt, 15);
585                         size                    = sqlite3_column_bytes(stmt, 15);
586                         row.oldMeta             = std::string(reinterpret_cast<const char*>(text), size);
587                         row.newNode             = sqlite3_column_int  (stmt, 16);
588                         row.newParam1   = sqlite3_column_int  (stmt, 17);
589                         row.newParam2   = sqlite3_column_int  (stmt, 18);
590                         text                    = sqlite3_column_text (stmt, 19);
591                         size                    = sqlite3_column_bytes(stmt, 19);
592                         row.newMeta             = std::string(reinterpret_cast<const char*>(text), size);
593                         row.guessed             = sqlite3_column_int  (stmt, 20);
594                 }
595
596                 row.location = row.nodeMeta? "nodemeta:": getActorName(row.actor);
597
598                 if (row.nodeMeta)
599                 {
600                         row.location.append(itos(row.x));
601                         row.location.append(",");
602                         row.location.append(itos(row.y));
603                         row.location.append(",");
604                         row.location.append(itos(row.z));
605                 }
606
607                 /*
608                 std::cout << "=======SELECTED==========" << "\n";
609                 std::cout << "Actor: " << row.actor << "\n";
610                 std::cout << "Timestamp: " << row.timestamp << "\n";
611
612                 if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
613                 {
614                         std::cout << "list: " << row.list << "\n";
615                         std::cout << "index: " << row.index << "\n";
616                         std::cout << "add: " << row.add << "\n";
617                         std::cout << "stackNode: " << row.stack.node << "\n";
618                         std::cout << "stackQuantity: " << row.stack.quantity << "\n";
619                         if (row.nodeMeta)
620                         {
621                                 std::cout << "X: " << row.x << "\n";
622                                 std::cout << "Y: " << row.y << "\n";
623                                 std::cout << "Z: " << row.z << "\n";
624                         }
625                         std::cout << "Location: " << row.location << "\n";
626                 }
627                         else
628                 {
629                         std::cout << "X: " << row.x << "\n";
630                         std::cout << "Y: " << row.y << "\n";
631                         std::cout << "Z: " << row.z << "\n";
632                         std::cout << "oldNode: " << row.oldNode << "\n";
633                         std::cout << "oldParam1: " << row.oldParam1 << "\n";
634                         std::cout << "oldParam2: " << row.oldParam2 << "\n";
635                         std::cout << "oldMeta: " << row.oldMeta << "\n";
636                         std::cout << "newNode: " << row.newNode << "\n";
637                         std::cout << "newParam1: " << row.newParam1 << "\n";
638                         std::cout << "newParam2: " << row.newParam2 << "\n";
639                         std::cout << "newMeta: " << row.newMeta << "\n";
640                         std::cout << "guessed: " << row.guessed << "\n";
641                 }
642                 */
643
644                 rows.push_back(row);
645         }
646
647         return rows;
648 }
649 ActionRow actionRowFromRollbackAction (RollbackAction action)
650 {
651         ActionRow row;
652
653         row.id                  = 0;
654         row.actor               = getActorId(action.actor);
655         row.timestamp   = action.unix_time;
656         row.type                = action.type;
657
658         if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
659         {
660                 row.location = action.inventory_location;
661                 row.list     = action.inventory_list;
662                 row.index    = action.inventory_index;
663                 row.add      = action.inventory_add;
664                 row.stack    = getStackFromString(action.inventory_stack);
665         }
666                 else
667         {
668                 row.x         = action.p.X;
669                 row.y         = action.p.Y;
670                 row.z         = action.p.Z;
671                 row.oldNode   = getNodeId(action.n_old.name);
672                 row.oldParam1 = action.n_old.param1;
673                 row.oldParam2 = action.n_old.param2;
674                 row.oldMeta   = action.n_old.meta;
675                 row.newNode   = getNodeId(action.n_new.name);
676                 row.newParam1 = action.n_new.param1;
677                 row.newParam2 = action.n_new.param2;
678                 row.newMeta   = action.n_new.meta;
679                 row.guessed   = action.actor_is_guess;
680         }
681
682         return row;
683 }
684 std::list<RollbackAction> rollbackActionsFromActionRows (std::list<ActionRow> rows)
685 {
686         std::list<RollbackAction> actions;
687         std::list<ActionRow>::const_iterator it;
688
689         for (it = rows.begin(); it != rows.end(); ++it)
690         {
691                 RollbackAction action;
692                 action.actor            = (it->actor)? getActorName(it->actor): "";
693                 action.unix_time        = it->timestamp;
694                 action.type                     = static_cast<RollbackAction::Type>(it->type);
695
696         switch (action.type)
697                 {
698                         case RollbackAction::TYPE_MODIFY_INVENTORY_STACK:
699
700                                 action.inventory_location   = it->location.c_str();
701                                 action.inventory_list       = it->list;
702                                 action.inventory_index      = it->index;
703                                 action.inventory_add        = it->add;
704                                 action.inventory_stack      = getStringFromStack(it->stack);
705                                 break;
706                                 
707                         case RollbackAction::TYPE_SET_NODE:
708
709                                 action.p                        = v3s16(it->x, it->y, it->z);
710                                 action.n_old.name       = getNodeName(it->oldNode);
711                                 action.n_old.param1 = it->oldParam1;
712                                 action.n_old.param2 = it->oldParam2;
713                                 action.n_old.meta       = it->oldMeta;
714                                 action.n_new.name       = getNodeName(it->newNode);
715                                 action.n_new.param1 = it->newParam1;
716                                 action.n_new.param2 = it->newParam2;
717                                 action.n_new.meta       = it->newMeta;
718                                 break;
719
720                         default:
721
722                                 throw("W.T.F.");
723                                 break;
724                 }
725
726                 actions.push_back(action);
727         }
728
729         return actions;
730 }
731 std::list<ActionRow> SQL_getRowsSince (int firstTime, std::string actor = "")
732 {
733         sqlite3_stmt * dbs_stmt = (!actor.length())? dbs_select: dbs_select_withActor;
734         sqlite3_reset     (dbs_stmt);
735         sqlite3_bind_int  (dbs_stmt, 1, firstTime);
736
737         if (actor.length())
738                 sqlite3_bind_int (dbs_stmt, 2, getActorId(actor));
739
740         return actionRowsFromSelect(dbs_stmt);
741 }
742 std::list<ActionRow> SQL_getRowsSince_range (int firstTime, v3s16 p, int range)
743 {
744         sqlite3_stmt * stmt = dbs_select_range;
745         sqlite3_reset(stmt);
746
747         sqlite3_bind_int(stmt, 1, firstTime);
748         sqlite3_bind_int(stmt, 2, (int) p.X);
749         sqlite3_bind_int(stmt, 3, range);
750         sqlite3_bind_int(stmt, 4, (int) p.Y);
751         sqlite3_bind_int(stmt, 5, range);
752         sqlite3_bind_int(stmt, 6, (int) p.Z);
753         sqlite3_bind_int(stmt, 7, range);
754
755         return actionRowsFromSelect(stmt);
756 }
757 std::list<RollbackAction> SQL_getActionsSince_range (int firstTime, v3s16 p, int range)
758 {
759         std::list<ActionRow> rows = SQL_getRowsSince_range(firstTime, p, range);
760
761         return rollbackActionsFromActionRows(rows);
762 }
763 std::list<RollbackAction> SQL_getActionsSince (int firstTime, std::string actor = "")
764 {
765         std::list<ActionRow> rows = SQL_getRowsSince(firstTime, actor);
766         return rollbackActionsFromActionRows(rows);
767 }
768 void TXT_migrate (std::string filepath)
769 {
770         std::cout << "Migrating from rollback.txt to rollback.sqlite" << std::endl;
771         SQL_databaseCheck();
772
773         std::ifstream fh (filepath.c_str(), std::ios::in | std::ios::ate);
774         if (!fh.good()) throw("DIE");
775
776         int filesize = fh.tellg();
777
778         if (filesize > 10)
779         {
780                 fh.seekg(0);
781
782                 std::string bit;
783                 int i   = 0;
784                 int id  = 1;
785                 int t   = 0;
786                 do
787                 {
788                         ActionRow row;
789
790                         row.id = id;
791
792                         // Get the timestamp
793                         std::getline(fh, bit, ' '); bit = trim(bit); if (!atoi(trim(bit).c_str())) { std::getline(fh, bit); continue; }
794                         row.timestamp = atoi(bit.c_str());
795
796                         // Get the actor
797                         row.actor = getActorId(trim(deSerializeJsonString(fh)));
798
799                         // Get the action type
800                         std::getline(fh, bit, '[');
801                         std::getline(fh, bit, ' ');
802
803                         if (bit == "modify_inventory_stack")
804                                 row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK;
805
806                         if (bit == "set_node")
807                                 row.type = RollbackAction::TYPE_SET_NODE;
808
809                         if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
810                         {
811                                                                                         row.location = trim(deSerializeJsonString(fh));
812                                 std::getline(fh, bit, ' ');     row.list     = trim(deSerializeJsonString(fh));
813                                 std::getline(fh, bit, ' ');
814                                 std::getline(fh, bit, ' ');
815                                                                                         row.index    = atoi(trim(bit).c_str());
816                                 std::getline(fh, bit, ' '); row.add      = (int) ( trim(bit) == "add" );
817                                                                                         row.stack    = getStackFromString(trim(deSerializeJsonString(fh)));
818                                 std::getline(fh, bit);
819                                 
820                         }
821                         else
822                                 if (row.type == RollbackAction::TYPE_SET_NODE)
823                         {
824                                 std::getline(fh, bit, '(');
825                                 std::getline(fh, bit, ','); row.x       = atoi(trim(bit).c_str());
826                                 std::getline(fh, bit, ','); row.y       = atoi(trim(bit).c_str());
827                                 std::getline(fh, bit, ')'); row.z       = atoi(trim(bit).c_str());
828                                 std::getline(fh, bit, ' ');     row.oldNode = getNodeId(trim(deSerializeJsonString(fh)));
829                                 std::getline(fh, bit, ' ');
830                                 std::getline(fh, bit, ' '); row.oldParam1 = atoi(trim(bit).c_str());
831                                 std::getline(fh, bit, ' '); row.oldParam2 = atoi(trim(bit).c_str());
832                                                                                         row.oldMeta   = trim(deSerializeJsonString(fh));
833                                 std::getline(fh, bit, ' ');     row.newNode   = getNodeId(trim(deSerializeJsonString(fh)));
834                                 std::getline(fh, bit, ' ');
835                                 std::getline(fh, bit, ' '); row.newParam1 = atoi(trim(bit).c_str());
836                                 std::getline(fh, bit, ' '); row.newParam2 = atoi(trim(bit).c_str());
837                                                                                         row.newMeta   = trim(deSerializeJsonString(fh));
838                                 std::getline(fh, bit, ' ');
839                                 std::getline(fh, bit, ' ');
840                                 std::getline(fh, bit);          row.guessed = (int) ( trim(bit) == "actor_is_guess" );
841                         }
842
843                         /*
844                         std::cout << "==========READ===========" << std::endl;
845                         std::cout << "time:      " << row.timestamp << std::endl;
846                         std::cout << "actor:     " << row.actor << std::endl;
847                         std::cout << "type:      " << row.type << std::endl;
848                         if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
849                         {
850                                 std::cout << "Location:   " << row.location << std::endl;
851                                 std::cout << "List:       " << row.list << std::endl;
852                                 std::cout << "Index:      " << row.index << std::endl;
853                                 std::cout << "Add:        " << row.add << std::endl;
854                                 std::cout << "Stack:      " << row.stack << std::endl;
855                         }
856                         if (row.type == RollbackAction::TYPE_SET_NODE)
857                         {
858                                 std::cout << "x:         " << row.x << std::endl;
859                                 std::cout << "y:         " << row.y << std::endl;
860                                 std::cout << "z:         " << row.z << std::endl;
861                                 std::cout << "oldNode:   " << row.oldNode << std::endl;
862                                 std::cout << "oldParam1: " << row.oldParam1 << std::endl;
863                                 std::cout << "oldParam2: " << row.oldParam2 << std::endl;
864                                 std::cout << "oldMeta:   " << row.oldMeta << std::endl;
865                                 std::cout << "newNode:   " << row.newNode << std::endl;
866                                 std::cout << "newParam1: " << row.newParam1 << std::endl;
867                                 std::cout << "newParam2: " << row.newParam2 << std::endl;
868                                 std::cout << "newMeta:   " << row.newMeta << std::endl;
869                                 std::cout << "guessed:   " << row.guessed << std::endl;
870                         }
871                         */
872
873                         if (i == 0)
874                         {
875                                 t = time(0);
876                                 sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
877                         }
878
879                         SQL_registerRow(row); ++i;
880
881                         if (time(0) - t)
882                         {
883                                 sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL);
884                                 t = time(0) - t;
885                                 std::cout
886                                         << "  Done: " << (int)(((float) fh.tellg() / (float) filesize) * 100) << "%"
887                                         << "\tSpeed: " << i / t << " actions inserted per second   "
888                                         << "\r";
889                                 std::cout.flush();
890                                 i = 0;
891                         }
892
893                         ++id;
894                 }
895                 while (!fh.eof() && fh.good());
896         }
897
898         std::cout
899                 << "  Done: 100%" << std::endl
900                 << "  Now you can delete the old rollback.txt file." << std::endl;
901 }
902 // Get nearness factor for subject's action for this action
903 // Return value: 0 = impossible, >0 = factor
904 static float getSuspectNearness(bool is_guess, v3s16 suspect_p, int suspect_t,
905                 v3s16 action_p, int action_t)
906 {
907         // Suspect cannot cause things in the past
908         if(action_t < suspect_t)
909                 return 0; // 0 = cannot be
910         // Start from 100
911         int f = 100;
912         // Distance (1 node = -x points)
913         f -= POINTS_PER_NODE * intToFloat(suspect_p, 1).getDistanceFrom(intToFloat(action_p, 1));
914         // Time (1 second = -x points)
915         f -= 1 * (action_t - suspect_t);
916         // If is a guess, halve the points
917         if(is_guess)
918                 f *= 0.5;
919         // Limit to 0
920         if(f < 0)
921                 f = 0;
922         return f;
923 }
924 class RollbackManager: public IRollbackManager
925 {
926 public:
927         // IRollbackManager interface
928         void reportAction(const RollbackAction &action_)
929         {
930                 // Ignore if not important
931                 if  (!action_.isImportant(m_gamedef))
932                         return;
933
934                 RollbackAction action = action_;
935                 action.unix_time = time(0);
936
937                 // Figure out actor
938                 action.actor = m_current_actor;
939                 action.actor_is_guess = m_current_actor_is_guess;
940
941                 if  (action.actor.empty()) // If actor is not known, find out suspect or cancel
942                 {
943                         v3s16 p;
944                         if  (!action.getPosition(&p))
945                                 return;
946
947                         action.actor = getSuspect(p, 83, 1);
948                         if  (action.actor.empty())
949                                 return;
950
951                         action.actor_is_guess = true;
952                 }
953
954                 infostream
955                         <<      "RollbackManager::reportAction():"
956                         <<      " time=" << action.unix_time
957                         <<      " actor=\"" << action.actor << "\""
958                         <<      (action.actor_is_guess? " (guess)": "")
959                         <<      " action=" << action.toString()
960                         <<      std::endl;
961                 addAction(action);
962         }
963         std::string getActor()
964         {
965                 return m_current_actor;
966         }
967         bool isActorGuess()
968         {
969                 return m_current_actor_is_guess;
970         }
971         void setActor(const std::string &actor, bool is_guess)
972         {
973                 m_current_actor = actor;
974                 m_current_actor_is_guess = is_guess;
975         }
976         std::string getSuspect(v3s16 p, float nearness_shortcut, float min_nearness)
977         {
978                 if(m_current_actor != "")
979                         return m_current_actor;
980                 int cur_time = time(0);
981                 int first_time = cur_time - (100-min_nearness);
982                 RollbackAction likely_suspect;
983                 float likely_suspect_nearness = 0;
984                 for(std::list<RollbackAction>::const_reverse_iterator
985                                 i = m_action_latest_buffer.rbegin();
986                                 i != m_action_latest_buffer.rend(); i++)
987                 {
988                         if(i->unix_time < first_time)
989                                 break;
990                         if(i->actor == "")
991                                 continue;
992                         // Find position of suspect or continue
993                         v3s16 suspect_p;
994                         if(!i->getPosition(&suspect_p))
995                                 continue;
996                         float f = getSuspectNearness(i->actor_is_guess, suspect_p,
997                                         i->unix_time, p, cur_time);
998                         if(f >= min_nearness && f > likely_suspect_nearness){
999                                 likely_suspect_nearness = f;
1000                                 likely_suspect = *i;
1001                                 if(likely_suspect_nearness >= nearness_shortcut)
1002                                         break;
1003                         }
1004                 }
1005                 // No likely suspect was found
1006                 if(likely_suspect_nearness == 0)
1007                         return "";
1008                 // Likely suspect was found
1009                 return likely_suspect.actor;
1010         }
1011         void flush()
1012         {
1013                 infostream << "RollbackManager::flush()" << std::endl;
1014
1015                 sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
1016
1017                 std::list<RollbackAction>::const_iterator iter;
1018
1019                 for     (iter  = m_action_todisk_buffer.begin();
1020                          iter != m_action_todisk_buffer.end();
1021                          iter++)
1022                 {
1023                         if (iter->actor == "")
1024                                 continue;
1025
1026                         SQL_registerRow(actionRowFromRollbackAction(*iter));
1027                 }
1028
1029                 sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL);
1030                 m_action_todisk_buffer.clear();
1031         }
1032         RollbackManager(const std::string &filepath, IGameDef *gamedef):
1033                 m_filepath(filepath),
1034                 m_gamedef(gamedef),
1035                 m_current_actor_is_guess(false)
1036         {
1037                 infostream
1038                         << "RollbackManager::RollbackManager(" << filepath << ")"
1039                         << std::endl;
1040                 
1041                 // Operate correctly in case of still being given rollback.txt as filepath
1042                 std::string directory   =  filepath.substr(0, filepath.rfind(DIR_DELIM) + 1);
1043                 std::string filenameOld =  filepath.substr(1+ filepath.rfind(DIR_DELIM));
1044                 std::string filenameNew = (filenameOld == "rollback.txt")? "rollback.sqlite": filenameOld;
1045                 std::string filenameTXT =  directory + "rollback.txt";
1046                 std::string migratingFlag = filepath;
1047                 migratingFlag.append(".migrating");
1048
1049                 infostream << "Directory: " << directory   << std::endl;
1050                 infostream << "CheckFor:  " << filenameTXT << std::endl;
1051                 infostream << "FileOld:   " << filenameOld << std::endl;
1052                 infostream << "FileNew:   " << filenameNew << std::endl;
1053                         
1054                 dbp = directory + filenameNew;
1055
1056                 if ((fs::PathExists(filenameTXT) &&  fs::PathExists(migratingFlag))
1057                 ||      (fs::PathExists(filenameTXT) && !fs::PathExists(dbp)))
1058                 {
1059                         std::ofstream of(migratingFlag.c_str());
1060                         TXT_migrate(filenameTXT);
1061                         fs::DeleteSingleFileOrEmptyDirectory(migratingFlag);
1062                 }
1063
1064                 SQL_databaseCheck();
1065         }
1066         ~RollbackManager()
1067         {
1068                 infostream << "RollbackManager::~RollbackManager()" << std::endl;
1069                 flush();
1070         }
1071         void addAction(const RollbackAction &action)
1072         {
1073                 m_action_todisk_buffer.push_back(action);
1074                 m_action_latest_buffer.push_back(action);
1075
1076                 // Flush to disk sometimes
1077                 if(m_action_todisk_buffer.size() >= 500)
1078                         flush();
1079         }
1080         std::list<RollbackAction> getEntriesSince(int first_time)
1081         {
1082                 infostream
1083                         << "RollbackManager::getEntriesSince(" << first_time << ")"
1084                         << std::endl;
1085
1086                 flush();
1087                 
1088                 std::list<RollbackAction> result = SQL_getActionsSince(first_time);
1089
1090                 return result;
1091         }
1092         std::string getLastNodeActor(v3s16 p, int range, int seconds, v3s16 *act_p, int *act_seconds)
1093         {
1094                 int cur_time = time(0);
1095                 int first_time = cur_time - seconds;
1096
1097                 std::list<RollbackAction> action_buffer = SQL_getActionsSince_range(first_time, p, range);
1098                 std::list<RollbackAction>::const_reverse_iterator iter;
1099
1100                 for (iter  = action_buffer.rbegin();
1101                          iter != action_buffer.rend();
1102                          iter++)
1103                 {
1104                         v3s16 action_p;
1105
1106                         action_p.X = iter->p.X;
1107                         action_p.Y = iter->p.Y;
1108                         action_p.Z = iter->p.Z;
1109                         
1110                         if (act_p)
1111                                 *act_p = action_p;
1112
1113                         if (act_seconds)
1114                                 *act_seconds = cur_time - iter->unix_time;
1115
1116                         return iter->actor;
1117                 }
1118
1119                 return "";
1120         }
1121         std::list<RollbackAction> getRevertActions(const std::string &actor_filter, int seconds)
1122         {
1123                 infostream
1124                         << "RollbackManager::getRevertActions(" << actor_filter
1125                         << ", " << seconds << ")"
1126                         << std::endl;
1127
1128                 // Figure out time
1129                 int cur_time = time(0);
1130                 int first_time = cur_time - seconds;
1131                 
1132                 flush();
1133                 
1134                 std::list<RollbackAction> result = SQL_getActionsSince(first_time, actor_filter);
1135
1136                 return result;
1137         }
1138 private:
1139         std::string m_filepath;
1140         IGameDef *m_gamedef;
1141         std::string m_current_actor;
1142         bool m_current_actor_is_guess;
1143         std::list<RollbackAction> m_action_todisk_buffer;
1144         std::list<RollbackAction> m_action_latest_buffer;
1145 };
1146
1147 IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef)
1148 {
1149         return new RollbackManager(filepath, gamedef);
1150 }