Line data Source code
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 POINTS_PER_NODE (16.0)
36 :
37 : #define SQLRES(f, good) \
38 : if ((f) != (good)) {\
39 : throw FileNotGoodException(std::string("RollbackManager: " \
40 : "SQLite3 error (" __FILE__ ":" TOSTRING(__LINE__) \
41 : "): ") + sqlite3_errmsg(db)); \
42 : }
43 : #define SQLOK(f) SQLRES(f, SQLITE_OK)
44 :
45 :
46 0 : class ItemStackRow : public ItemStack {
47 : public:
48 0 : ItemStackRow & operator = (const ItemStack & other)
49 : {
50 0 : *static_cast<ItemStack *>(this) = other;
51 0 : return *this;
52 : }
53 :
54 : int id;
55 : };
56 :
57 0 : struct ActionRow {
58 : int id;
59 : int actor;
60 : time_t timestamp;
61 : int type;
62 : std::string location, list;
63 : int index, add;
64 : ItemStackRow 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 :
76 :
77 0 : struct Entity {
78 : int id;
79 : std::string name;
80 : };
81 :
82 :
83 :
84 0 : RollbackManager::RollbackManager(const std::string & world_path,
85 : IGameDef * gamedef_) :
86 : gamedef(gamedef_),
87 0 : current_actor_is_guess(false)
88 : {
89 0 : verbosestream << "RollbackManager::RollbackManager(" << world_path
90 0 : << ")" << std::endl;
91 :
92 0 : std::string txt_filename = world_path + DIR_DELIM "rollback.txt";
93 0 : std::string migrating_flag = txt_filename + ".migrating";
94 0 : database_path = world_path + DIR_DELIM "rollback.sqlite";
95 :
96 0 : initDatabase();
97 :
98 0 : if (fs::PathExists(txt_filename) && (fs::PathExists(migrating_flag) ||
99 0 : !fs::PathExists(database_path))) {
100 0 : std::ofstream of(migrating_flag.c_str());
101 0 : of.close();
102 0 : migrate(txt_filename);
103 0 : fs::DeleteSingleFileOrEmptyDirectory(migrating_flag);
104 : }
105 0 : }
106 :
107 :
108 0 : RollbackManager::~RollbackManager()
109 : {
110 0 : SQLOK(sqlite3_finalize(stmt_insert));
111 0 : SQLOK(sqlite3_finalize(stmt_replace));
112 0 : SQLOK(sqlite3_finalize(stmt_select));
113 0 : SQLOK(sqlite3_finalize(stmt_select_range));
114 0 : SQLOK(sqlite3_finalize(stmt_select_withActor));
115 0 : SQLOK(sqlite3_finalize(stmt_knownActor_select));
116 0 : SQLOK(sqlite3_finalize(stmt_knownActor_insert));
117 0 : SQLOK(sqlite3_finalize(stmt_knownNode_select));
118 0 : SQLOK(sqlite3_finalize(stmt_knownNode_insert));
119 :
120 0 : SQLOK(sqlite3_close(db));
121 0 : }
122 :
123 :
124 0 : void RollbackManager::registerNewActor(const int id, const std::string &name)
125 : {
126 0 : Entity actor = {id, name};
127 0 : knownActors.push_back(actor);
128 0 : }
129 :
130 :
131 0 : void RollbackManager::registerNewNode(const int id, const std::string &name)
132 : {
133 0 : Entity node = {id, name};
134 0 : knownNodes.push_back(node);
135 0 : }
136 :
137 :
138 0 : int RollbackManager::getActorId(const std::string &name)
139 : {
140 0 : for (std::vector<Entity>::const_iterator iter = knownActors.begin();
141 0 : iter != knownActors.end(); ++iter) {
142 0 : if (iter->name == name) {
143 0 : return iter->id;
144 : }
145 : }
146 :
147 0 : SQLOK(sqlite3_bind_text(stmt_knownActor_insert, 1, name.c_str(), name.size(), NULL));
148 0 : SQLRES(sqlite3_step(stmt_knownActor_insert), SQLITE_DONE);
149 0 : SQLOK(sqlite3_reset(stmt_knownActor_insert));
150 :
151 0 : int id = sqlite3_last_insert_rowid(db);
152 0 : registerNewActor(id, name);
153 :
154 0 : return id;
155 : }
156 :
157 :
158 0 : int RollbackManager::getNodeId(const std::string &name)
159 : {
160 0 : for (std::vector<Entity>::const_iterator iter = knownNodes.begin();
161 0 : iter != knownNodes.end(); ++iter) {
162 0 : if (iter->name == name) {
163 0 : return iter->id;
164 : }
165 : }
166 :
167 0 : SQLOK(sqlite3_bind_text(stmt_knownNode_insert, 1, name.c_str(), name.size(), NULL));
168 0 : SQLRES(sqlite3_step(stmt_knownNode_insert), SQLITE_DONE);
169 0 : SQLOK(sqlite3_reset(stmt_knownNode_insert));
170 :
171 0 : int id = sqlite3_last_insert_rowid(db);
172 0 : registerNewNode(id, name);
173 :
174 0 : return id;
175 : }
176 :
177 :
178 0 : const char * RollbackManager::getActorName(const int id)
179 : {
180 0 : for (std::vector<Entity>::const_iterator iter = knownActors.begin();
181 0 : iter != knownActors.end(); ++iter) {
182 0 : if (iter->id == id) {
183 0 : return iter->name.c_str();
184 : }
185 : }
186 :
187 0 : return "";
188 : }
189 :
190 :
191 0 : const char * RollbackManager::getNodeName(const int id)
192 : {
193 0 : for (std::vector<Entity>::const_iterator iter = knownNodes.begin();
194 0 : iter != knownNodes.end(); ++iter) {
195 0 : if (iter->id == id) {
196 0 : return iter->name.c_str();
197 : }
198 : }
199 :
200 0 : return "";
201 : }
202 :
203 :
204 0 : bool RollbackManager::createTables()
205 : {
206 0 : SQLOK(sqlite3_exec(db,
207 : "CREATE TABLE IF NOT EXISTS `actor` (\n"
208 : " `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
209 : " `name` TEXT NOT NULL\n"
210 : ");\n"
211 : "CREATE TABLE IF NOT EXISTS `node` (\n"
212 : " `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n"
213 : " `name` TEXT NOT NULL\n"
214 : ");\n"
215 : "CREATE TABLE IF NOT EXISTS `action` (\n"
216 : " `id` INTEGER PRIMARY KEY AUTOINCREMENT,\n"
217 : " `actor` INTEGER NOT NULL,\n"
218 : " `timestamp` TIMESTAMP NOT NULL,\n"
219 : " `type` INTEGER NOT NULL,\n"
220 : " `list` TEXT,\n"
221 : " `index` INTEGER,\n"
222 : " `add` INTEGER,\n"
223 : " `stackNode` INTEGER,\n"
224 : " `stackQuantity` INTEGER,\n"
225 : " `nodeMeta` INTEGER,\n"
226 : " `x` INT,\n"
227 : " `y` INT,\n"
228 : " `z` INT,\n"
229 : " `oldNode` INTEGER,\n"
230 : " `oldParam1` INTEGER,\n"
231 : " `oldParam2` INTEGER,\n"
232 : " `oldMeta` TEXT,\n"
233 : " `newNode` INTEGER,\n"
234 : " `newParam1` INTEGER,\n"
235 : " `newParam2` INTEGER,\n"
236 : " `newMeta` TEXT,\n"
237 : " `guessedActor` INTEGER,\n"
238 : " FOREIGN KEY (`actor`) REFERENCES `actor`(`id`),\n"
239 : " FOREIGN KEY (`stackNode`) REFERENCES `node`(`id`),\n"
240 : " FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`),\n"
241 : " FOREIGN KEY (`newNode`) REFERENCES `node`(`id`)\n"
242 : ");\n"
243 : "CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);\n"
244 : "CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);\n",
245 : NULL, NULL, NULL));
246 0 : verbosestream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
247 :
248 0 : return true;
249 : }
250 :
251 :
252 0 : void RollbackManager::initDatabase()
253 : {
254 0 : verbosestream << "RollbackManager: Database connection setup" << std::endl;
255 :
256 0 : bool needsCreate = !fs::PathExists(database_path);
257 0 : SQLOK(sqlite3_open_v2(database_path.c_str(), &db,
258 : SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL));
259 :
260 0 : if (needsCreate) {
261 0 : createTables();
262 : }
263 :
264 0 : SQLOK(sqlite3_prepare_v2(db,
265 : "INSERT INTO `action` (\n"
266 : " `actor`, `timestamp`, `type`,\n"
267 : " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,\n"
268 : " `x`, `y`, `z`,\n"
269 : " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n"
270 : " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n"
271 : " `guessedActor`\n"
272 : ") VALUES (\n"
273 : " ?, ?, ?,\n"
274 : " ?, ?, ?, ?, ?, ?,\n"
275 : " ?, ?, ?,\n"
276 : " ?, ?, ?, ?,\n"
277 : " ?, ?, ?, ?,\n"
278 : " ?"
279 : ");",
280 : -1, &stmt_insert, NULL));
281 :
282 0 : SQLOK(sqlite3_prepare_v2(db,
283 : "REPLACE INTO `action` (\n"
284 : " `actor`, `timestamp`, `type`,\n"
285 : " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,\n"
286 : " `x`, `y`, `z`,\n"
287 : " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n"
288 : " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n"
289 : " `guessedActor`, `id`\n"
290 : ") VALUES (\n"
291 : " ?, ?, ?,\n"
292 : " ?, ?, ?, ?, ?, ?,\n"
293 : " ?, ?, ?,\n"
294 : " ?, ?, ?, ?,\n"
295 : " ?, ?, ?, ?,\n"
296 : " ?, ?\n"
297 : ");",
298 : -1, &stmt_replace, NULL));
299 :
300 0 : SQLOK(sqlite3_prepare_v2(db,
301 : "SELECT\n"
302 : " `actor`, `timestamp`, `type`,\n"
303 : " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n"
304 : " `x`, `y`, `z`,\n"
305 : " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n"
306 : " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n"
307 : " `guessedActor`\n"
308 : " FROM `action`\n"
309 : " WHERE `timestamp` >= ?\n"
310 : " ORDER BY `timestamp` DESC, `id` DESC",
311 : -1, &stmt_select, NULL));
312 :
313 0 : SQLOK(sqlite3_prepare_v2(db,
314 : "SELECT\n"
315 : " `actor`, `timestamp`, `type`,\n"
316 : " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n"
317 : " `x`, `y`, `z`,\n"
318 : " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n"
319 : " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n"
320 : " `guessedActor`\n"
321 : "FROM `action`\n"
322 : "WHERE `timestamp` >= ?\n"
323 : " AND `x` IS NOT NULL\n"
324 : " AND `y` IS NOT NULL\n"
325 : " AND `z` IS NOT NULL\n"
326 : " AND `x` BETWEEN ? AND ?\n"
327 : " AND `y` BETWEEN ? AND ?\n"
328 : " AND `z` BETWEEN ? AND ?\n"
329 : "ORDER BY `timestamp` DESC, `id` DESC\n"
330 : "LIMIT 0,?",
331 : -1, &stmt_select_range, NULL));
332 :
333 0 : SQLOK(sqlite3_prepare_v2(db,
334 : "SELECT\n"
335 : " `actor`, `timestamp`, `type`,\n"
336 : " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n"
337 : " `x`, `y`, `z`,\n"
338 : " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n"
339 : " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n"
340 : " `guessedActor`\n"
341 : "FROM `action`\n"
342 : "WHERE `timestamp` >= ?\n"
343 : " AND `actor` = ?\n"
344 : "ORDER BY `timestamp` DESC, `id` DESC\n",
345 : -1, &stmt_select_withActor, NULL));
346 :
347 0 : SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `actor`",
348 : -1, &stmt_knownActor_select, NULL));
349 :
350 0 : SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `actor` (`name`) VALUES (?)",
351 : -1, &stmt_knownActor_insert, NULL));
352 :
353 0 : SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `node`",
354 : -1, &stmt_knownNode_select, NULL));
355 :
356 0 : SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `node` (`name`) VALUES (?)",
357 : -1, &stmt_knownNode_insert, NULL));
358 :
359 0 : verbosestream << "SQL prepared statements setup correctly" << std::endl;
360 :
361 0 : while (sqlite3_step(stmt_knownActor_select) == SQLITE_ROW) {
362 0 : registerNewActor(
363 : sqlite3_column_int(stmt_knownActor_select, 0),
364 0 : reinterpret_cast<const char *>(sqlite3_column_text(stmt_knownActor_select, 1))
365 0 : );
366 : }
367 0 : SQLOK(sqlite3_reset(stmt_knownActor_select));
368 :
369 0 : while (sqlite3_step(stmt_knownNode_select) == SQLITE_ROW) {
370 0 : registerNewNode(
371 : sqlite3_column_int(stmt_knownNode_select, 0),
372 0 : reinterpret_cast<const char *>(sqlite3_column_text(stmt_knownNode_select, 1))
373 0 : );
374 : }
375 0 : SQLOK(sqlite3_reset(stmt_knownNode_select));
376 0 : }
377 :
378 :
379 0 : bool RollbackManager::registerRow(const ActionRow & row)
380 : {
381 0 : sqlite3_stmt * stmt_do = (row.id) ? stmt_replace : stmt_insert;
382 :
383 0 : bool nodeMeta = false;
384 :
385 0 : SQLOK(sqlite3_bind_int (stmt_do, 1, row.actor));
386 0 : SQLOK(sqlite3_bind_int64(stmt_do, 2, row.timestamp));
387 0 : SQLOK(sqlite3_bind_int (stmt_do, 3, row.type));
388 :
389 0 : if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) {
390 0 : const std::string & loc = row.location;
391 0 : nodeMeta = (loc.substr(0, 9) == "nodemeta:");
392 :
393 0 : SQLOK(sqlite3_bind_text(stmt_do, 4, row.list.c_str(), row.list.size(), NULL));
394 0 : SQLOK(sqlite3_bind_int (stmt_do, 5, row.index));
395 0 : SQLOK(sqlite3_bind_int (stmt_do, 6, row.add));
396 0 : SQLOK(sqlite3_bind_int (stmt_do, 7, row.stack.id));
397 0 : SQLOK(sqlite3_bind_int (stmt_do, 8, row.stack.count));
398 0 : SQLOK(sqlite3_bind_int (stmt_do, 9, (int) nodeMeta));
399 :
400 0 : if (nodeMeta) {
401 : std::string::size_type p1, p2;
402 0 : p1 = loc.find(':') + 1;
403 0 : p2 = loc.find(',');
404 0 : std::string x = loc.substr(p1, p2 - p1);
405 0 : p1 = p2 + 1;
406 0 : p2 = loc.find(',', p1);
407 0 : std::string y = loc.substr(p1, p2 - p1);
408 0 : std::string z = loc.substr(p2 + 1);
409 0 : SQLOK(sqlite3_bind_int(stmt_do, 10, atoi(x.c_str())));
410 0 : SQLOK(sqlite3_bind_int(stmt_do, 11, atoi(y.c_str())));
411 0 : SQLOK(sqlite3_bind_int(stmt_do, 12, atoi(z.c_str())));
412 : }
413 : } else {
414 0 : SQLOK(sqlite3_bind_null(stmt_do, 4));
415 0 : SQLOK(sqlite3_bind_null(stmt_do, 5));
416 0 : SQLOK(sqlite3_bind_null(stmt_do, 6));
417 0 : SQLOK(sqlite3_bind_null(stmt_do, 7));
418 0 : SQLOK(sqlite3_bind_null(stmt_do, 8));
419 0 : SQLOK(sqlite3_bind_null(stmt_do, 9));
420 : }
421 :
422 0 : if (row.type == RollbackAction::TYPE_SET_NODE) {
423 0 : SQLOK(sqlite3_bind_int (stmt_do, 10, row.x));
424 0 : SQLOK(sqlite3_bind_int (stmt_do, 11, row.y));
425 0 : SQLOK(sqlite3_bind_int (stmt_do, 12, row.z));
426 0 : SQLOK(sqlite3_bind_int (stmt_do, 13, row.oldNode));
427 0 : SQLOK(sqlite3_bind_int (stmt_do, 14, row.oldParam1));
428 0 : SQLOK(sqlite3_bind_int (stmt_do, 15, row.oldParam2));
429 0 : SQLOK(sqlite3_bind_text(stmt_do, 16, row.oldMeta.c_str(), row.oldMeta.size(), NULL));
430 0 : SQLOK(sqlite3_bind_int (stmt_do, 17, row.newNode));
431 0 : SQLOK(sqlite3_bind_int (stmt_do, 18, row.newParam1));
432 0 : SQLOK(sqlite3_bind_int (stmt_do, 19, row.newParam2));
433 0 : SQLOK(sqlite3_bind_text(stmt_do, 20, row.newMeta.c_str(), row.newMeta.size(), NULL));
434 0 : SQLOK(sqlite3_bind_int (stmt_do, 21, row.guessed ? 1 : 0));
435 : } else {
436 0 : if (!nodeMeta) {
437 0 : SQLOK(sqlite3_bind_null(stmt_do, 10));
438 0 : SQLOK(sqlite3_bind_null(stmt_do, 11));
439 0 : SQLOK(sqlite3_bind_null(stmt_do, 12));
440 : }
441 0 : SQLOK(sqlite3_bind_null(stmt_do, 13));
442 0 : SQLOK(sqlite3_bind_null(stmt_do, 14));
443 0 : SQLOK(sqlite3_bind_null(stmt_do, 15));
444 0 : SQLOK(sqlite3_bind_null(stmt_do, 16));
445 0 : SQLOK(sqlite3_bind_null(stmt_do, 17));
446 0 : SQLOK(sqlite3_bind_null(stmt_do, 18));
447 0 : SQLOK(sqlite3_bind_null(stmt_do, 19));
448 0 : SQLOK(sqlite3_bind_null(stmt_do, 20));
449 0 : SQLOK(sqlite3_bind_null(stmt_do, 21));
450 : }
451 :
452 0 : if (row.id) {
453 0 : SQLOK(sqlite3_bind_int(stmt_do, 22, row.id));
454 : }
455 :
456 0 : int written = sqlite3_step(stmt_do);
457 :
458 0 : SQLOK(sqlite3_reset(stmt_do));
459 :
460 0 : return written == SQLITE_DONE;
461 : }
462 :
463 :
464 0 : const std::list<ActionRow> RollbackManager::actionRowsFromSelect(sqlite3_stmt* stmt)
465 : {
466 0 : std::list<ActionRow> rows;
467 : const unsigned char * text;
468 : size_t size;
469 :
470 0 : while (sqlite3_step(stmt) == SQLITE_ROW) {
471 0 : ActionRow row;
472 :
473 0 : row.actor = sqlite3_column_int (stmt, 0);
474 0 : row.timestamp = sqlite3_column_int64(stmt, 1);
475 0 : row.type = sqlite3_column_int (stmt, 2);
476 :
477 0 : if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) {
478 0 : text = sqlite3_column_text (stmt, 3);
479 0 : size = sqlite3_column_bytes(stmt, 3);
480 0 : row.list = std::string(reinterpret_cast<const char*>(text), size);
481 0 : row.index = sqlite3_column_int(stmt, 4);
482 0 : row.add = sqlite3_column_int(stmt, 5);
483 0 : row.stack.id = sqlite3_column_int(stmt, 6);
484 0 : row.stack.count = sqlite3_column_int(stmt, 7);
485 0 : row.nodeMeta = sqlite3_column_int(stmt, 8);
486 : }
487 :
488 0 : if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta) {
489 0 : row.x = sqlite3_column_int(stmt, 9);
490 0 : row.y = sqlite3_column_int(stmt, 10);
491 0 : row.z = sqlite3_column_int(stmt, 11);
492 : }
493 :
494 0 : if (row.type == RollbackAction::TYPE_SET_NODE) {
495 0 : row.oldNode = sqlite3_column_int(stmt, 12);
496 0 : row.oldParam1 = sqlite3_column_int(stmt, 13);
497 0 : row.oldParam2 = sqlite3_column_int(stmt, 14);
498 0 : text = sqlite3_column_text (stmt, 15);
499 0 : size = sqlite3_column_bytes(stmt, 15);
500 0 : row.oldMeta = std::string(reinterpret_cast<const char*>(text), size);
501 0 : row.newNode = sqlite3_column_int(stmt, 16);
502 0 : row.newParam1 = sqlite3_column_int(stmt, 17);
503 0 : row.newParam2 = sqlite3_column_int(stmt, 18);
504 0 : text = sqlite3_column_text(stmt, 19);
505 0 : size = sqlite3_column_bytes(stmt, 19);
506 0 : row.newMeta = std::string(reinterpret_cast<const char*>(text), size);
507 0 : row.guessed = sqlite3_column_int(stmt, 20);
508 : }
509 :
510 0 : if (row.nodeMeta) {
511 0 : row.location = "nodemeta:";
512 0 : row.location += itos(row.x);
513 0 : row.location += ',';
514 0 : row.location += itos(row.y);
515 0 : row.location += ',';
516 0 : row.location += itos(row.z);
517 : } else {
518 0 : row.location = getActorName(row.actor);
519 : }
520 :
521 0 : rows.push_back(row);
522 : }
523 :
524 0 : SQLOK(sqlite3_reset(stmt));
525 :
526 0 : return rows;
527 : }
528 :
529 :
530 0 : ActionRow RollbackManager::actionRowFromRollbackAction(const RollbackAction & action)
531 : {
532 0 : ActionRow row;
533 :
534 0 : row.id = 0;
535 0 : row.actor = getActorId(action.actor);
536 0 : row.timestamp = action.unix_time;
537 0 : row.type = action.type;
538 :
539 0 : if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) {
540 0 : row.location = action.inventory_location;
541 0 : row.list = action.inventory_list;
542 0 : row.index = action.inventory_index;
543 0 : row.add = action.inventory_add;
544 0 : row.stack = action.inventory_stack;
545 0 : row.stack.id = getNodeId(row.stack.name);
546 : } else {
547 0 : row.x = action.p.X;
548 0 : row.y = action.p.Y;
549 0 : row.z = action.p.Z;
550 0 : row.oldNode = getNodeId(action.n_old.name);
551 0 : row.oldParam1 = action.n_old.param1;
552 0 : row.oldParam2 = action.n_old.param2;
553 0 : row.oldMeta = action.n_old.meta;
554 0 : row.newNode = getNodeId(action.n_new.name);
555 0 : row.newParam1 = action.n_new.param1;
556 0 : row.newParam2 = action.n_new.param2;
557 0 : row.newMeta = action.n_new.meta;
558 0 : row.guessed = action.actor_is_guess;
559 : }
560 :
561 0 : return row;
562 : }
563 :
564 :
565 0 : const std::list<RollbackAction> RollbackManager::rollbackActionsFromActionRows(
566 : const std::list<ActionRow> & rows)
567 : {
568 0 : std::list<RollbackAction> actions;
569 :
570 0 : for (std::list<ActionRow>::const_iterator it = rows.begin();
571 0 : it != rows.end(); ++it) {
572 0 : RollbackAction action;
573 0 : action.actor = (it->actor) ? getActorName(it->actor) : "";
574 0 : action.unix_time = it->timestamp;
575 0 : action.type = static_cast<RollbackAction::Type>(it->type);
576 :
577 0 : switch (action.type) {
578 : case RollbackAction::TYPE_MODIFY_INVENTORY_STACK:
579 0 : action.inventory_location = it->location.c_str();
580 0 : action.inventory_list = it->list;
581 0 : action.inventory_index = it->index;
582 0 : action.inventory_add = it->add;
583 0 : action.inventory_stack = it->stack;
584 0 : if (action.inventory_stack.name.empty()) {
585 0 : action.inventory_stack.name = getNodeName(it->stack.id);
586 : }
587 0 : break;
588 :
589 : case RollbackAction::TYPE_SET_NODE:
590 0 : action.p = v3s16(it->x, it->y, it->z);
591 0 : action.n_old.name = getNodeName(it->oldNode);
592 0 : action.n_old.param1 = it->oldParam1;
593 0 : action.n_old.param2 = it->oldParam2;
594 0 : action.n_old.meta = it->oldMeta;
595 0 : action.n_new.name = getNodeName(it->newNode);
596 0 : action.n_new.param1 = it->newParam1;
597 0 : action.n_new.param2 = it->newParam2;
598 0 : action.n_new.meta = it->newMeta;
599 0 : break;
600 :
601 : default:
602 0 : throw ("W.T.F.");
603 : break;
604 : }
605 :
606 0 : actions.push_back(action);
607 : }
608 :
609 0 : return actions;
610 : }
611 :
612 :
613 0 : const std::list<ActionRow> RollbackManager::getRowsSince(time_t firstTime, const std::string & actor)
614 : {
615 0 : sqlite3_stmt *stmt_stmt = actor.empty() ? stmt_select : stmt_select_withActor;
616 0 : sqlite3_bind_int64(stmt_stmt, 1, firstTime);
617 :
618 0 : if (!actor.empty()) {
619 0 : sqlite3_bind_int(stmt_stmt, 2, getActorId(actor));
620 : }
621 :
622 0 : const std::list<ActionRow> & rows = actionRowsFromSelect(stmt_stmt);
623 0 : sqlite3_reset(stmt_stmt);
624 :
625 0 : return rows;
626 : }
627 :
628 :
629 0 : const std::list<ActionRow> RollbackManager::getRowsSince_range(
630 : time_t start_time, v3s16 p, int range, int limit)
631 : {
632 :
633 0 : sqlite3_bind_int64(stmt_select_range, 1, start_time);
634 0 : sqlite3_bind_int (stmt_select_range, 2, static_cast<int>(p.X - range));
635 0 : sqlite3_bind_int (stmt_select_range, 3, static_cast<int>(p.X + range));
636 0 : sqlite3_bind_int (stmt_select_range, 4, static_cast<int>(p.Y - range));
637 0 : sqlite3_bind_int (stmt_select_range, 5, static_cast<int>(p.Y + range));
638 0 : sqlite3_bind_int (stmt_select_range, 6, static_cast<int>(p.Z - range));
639 0 : sqlite3_bind_int (stmt_select_range, 7, static_cast<int>(p.Z + range));
640 0 : sqlite3_bind_int (stmt_select_range, 8, limit);
641 :
642 0 : const std::list<ActionRow> & rows = actionRowsFromSelect(stmt_select_range);
643 0 : sqlite3_reset(stmt_select_range);
644 :
645 0 : return rows;
646 : }
647 :
648 :
649 0 : const std::list<RollbackAction> RollbackManager::getActionsSince_range(
650 : time_t start_time, v3s16 p, int range, int limit)
651 : {
652 0 : return rollbackActionsFromActionRows(getRowsSince_range(start_time, p, range, limit));
653 : }
654 :
655 :
656 0 : const std::list<RollbackAction> RollbackManager::getActionsSince(
657 : time_t start_time, const std::string & actor)
658 : {
659 0 : return rollbackActionsFromActionRows(getRowsSince(start_time, actor));
660 : }
661 :
662 :
663 0 : void RollbackManager::migrate(const std::string & file_path)
664 : {
665 0 : std::cout << "Migrating from rollback.txt to rollback.sqlite." << std::endl;
666 :
667 0 : std::ifstream fh(file_path.c_str(), std::ios::in | std::ios::ate);
668 0 : if (!fh.good()) {
669 0 : throw FileNotGoodException("Unable to open rollback.txt");
670 : }
671 :
672 0 : std::streampos file_size = fh.tellg();
673 :
674 0 : if (file_size > 10) {
675 0 : errorstream << "Empty rollback log." << std::endl;
676 0 : return;
677 : }
678 :
679 0 : fh.seekg(0);
680 :
681 0 : std::string bit;
682 0 : int i = 0;
683 0 : int id = 1;
684 0 : time_t start = time(0);
685 0 : time_t t = start;
686 0 : sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
687 0 : do {
688 0 : ActionRow row;
689 :
690 0 : row.id = id;
691 :
692 : // Get the timestamp
693 0 : std::getline(fh, bit, ' ');
694 0 : bit = trim(bit);
695 0 : if (!atoi(bit.c_str())) {
696 0 : std::getline(fh, bit);
697 0 : continue;
698 : }
699 0 : row.timestamp = atoi(bit.c_str());
700 :
701 : // Get the actor
702 0 : row.actor = getActorId(deSerializeJsonString(fh));
703 :
704 : // Get the action type
705 0 : std::getline(fh, bit, '[');
706 0 : std::getline(fh, bit, ' ');
707 :
708 0 : if (bit == "modify_inventory_stack") {
709 0 : row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK;
710 0 : row.location = trim(deSerializeJsonString(fh));
711 0 : std::getline(fh, bit, ' ');
712 0 : row.list = trim(deSerializeJsonString(fh));
713 0 : std::getline(fh, bit, ' ');
714 0 : std::getline(fh, bit, ' ');
715 0 : row.index = atoi(trim(bit).c_str());
716 0 : std::getline(fh, bit, ' ');
717 0 : row.add = (int)(trim(bit) == "add");
718 0 : row.stack.deSerialize(deSerializeJsonString(fh));
719 0 : row.stack.id = getNodeId(row.stack.name);
720 0 : std::getline(fh, bit);
721 0 : } else if (bit == "set_node") {
722 0 : row.type = RollbackAction::TYPE_SET_NODE;
723 0 : std::getline(fh, bit, '(');
724 0 : std::getline(fh, bit, ',');
725 0 : row.x = atoi(trim(bit).c_str());
726 0 : std::getline(fh, bit, ',');
727 0 : row.y = atoi(trim(bit).c_str());
728 0 : std::getline(fh, bit, ')');
729 0 : row.z = atoi(trim(bit).c_str());
730 0 : std::getline(fh, bit, ' ');
731 0 : row.oldNode = getNodeId(trim(deSerializeJsonString(fh)));
732 0 : std::getline(fh, bit, ' ');
733 0 : std::getline(fh, bit, ' ');
734 0 : row.oldParam1 = atoi(trim(bit).c_str());
735 0 : std::getline(fh, bit, ' ');
736 0 : row.oldParam2 = atoi(trim(bit).c_str());
737 0 : row.oldMeta = trim(deSerializeJsonString(fh));
738 0 : std::getline(fh, bit, ' ');
739 0 : row.newNode = getNodeId(trim(deSerializeJsonString(fh)));
740 0 : std::getline(fh, bit, ' ');
741 0 : std::getline(fh, bit, ' ');
742 0 : row.newParam1 = atoi(trim(bit).c_str());
743 0 : std::getline(fh, bit, ' ');
744 0 : row.newParam2 = atoi(trim(bit).c_str());
745 0 : row.newMeta = trim(deSerializeJsonString(fh));
746 0 : std::getline(fh, bit, ' ');
747 0 : std::getline(fh, bit, ' ');
748 0 : std::getline(fh, bit);
749 0 : row.guessed = (int)(trim(bit) == "actor_is_guess");
750 : } else {
751 0 : errorstream << "Unrecognized rollback action type \""
752 0 : << bit << "\"!" << std::endl;
753 0 : continue;
754 : }
755 :
756 0 : registerRow(row);
757 0 : ++i;
758 :
759 0 : if (time(0) - t >= 1) {
760 0 : sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
761 0 : t = time(0);
762 : std::cout
763 0 : << " Done: " << static_cast<int>((static_cast<float>(fh.tellg()) / static_cast<float>(file_size)) * 100) << "%"
764 0 : << " Speed: " << i / (t - start) << "/second \r" << std::flush;
765 0 : sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
766 : }
767 : } while (fh.good());
768 :
769 : std::cout
770 0 : << " Done: 100% " << std::endl
771 0 : << "Now you can delete the old rollback.txt file." << std::endl;
772 : }
773 :
774 :
775 : // Get nearness factor for subject's action for this action
776 : // Return value: 0 = impossible, >0 = factor
777 0 : float RollbackManager::getSuspectNearness(bool is_guess, v3s16 suspect_p,
778 : time_t suspect_t, v3s16 action_p, time_t action_t)
779 : {
780 : // Suspect cannot cause things in the past
781 0 : if (action_t < suspect_t) {
782 0 : return 0; // 0 = cannot be
783 : }
784 : // Start from 100
785 0 : int f = 100;
786 : // Distance (1 node = -x points)
787 0 : f -= POINTS_PER_NODE * intToFloat(suspect_p, 1).getDistanceFrom(intToFloat(action_p, 1));
788 : // Time (1 second = -x points)
789 0 : f -= 1 * (action_t - suspect_t);
790 : // If is a guess, halve the points
791 0 : if (is_guess) {
792 0 : f *= 0.5;
793 : }
794 : // Limit to 0
795 0 : if (f < 0) {
796 0 : f = 0;
797 : }
798 0 : return f;
799 : }
800 :
801 :
802 0 : void RollbackManager::reportAction(const RollbackAction &action_)
803 : {
804 : // Ignore if not important
805 0 : if (!action_.isImportant(gamedef)) {
806 0 : return;
807 : }
808 :
809 0 : RollbackAction action = action_;
810 0 : action.unix_time = time(0);
811 :
812 : // Figure out actor
813 0 : action.actor = current_actor;
814 0 : action.actor_is_guess = current_actor_is_guess;
815 :
816 0 : if (action.actor.empty()) { // If actor is not known, find out suspect or cancel
817 0 : v3s16 p;
818 0 : if (!action.getPosition(&p)) {
819 0 : return;
820 : }
821 :
822 0 : action.actor = getSuspect(p, 83, 1);
823 0 : if (action.actor.empty()) {
824 0 : return;
825 : }
826 :
827 0 : action.actor_is_guess = true;
828 : }
829 :
830 0 : addAction(action);
831 : }
832 :
833 0 : std::string RollbackManager::getActor()
834 : {
835 0 : return current_actor;
836 : }
837 :
838 0 : bool RollbackManager::isActorGuess()
839 : {
840 0 : return current_actor_is_guess;
841 : }
842 :
843 0 : void RollbackManager::setActor(const std::string & actor, bool is_guess)
844 : {
845 0 : current_actor = actor;
846 0 : current_actor_is_guess = is_guess;
847 0 : }
848 :
849 0 : std::string RollbackManager::getSuspect(v3s16 p, float nearness_shortcut,
850 : float min_nearness)
851 : {
852 0 : if (current_actor != "") {
853 0 : return current_actor;
854 : }
855 0 : int cur_time = time(0);
856 0 : time_t first_time = cur_time - (100 - min_nearness);
857 0 : RollbackAction likely_suspect;
858 0 : float likely_suspect_nearness = 0;
859 0 : for (std::list<RollbackAction>::const_reverse_iterator
860 0 : i = action_latest_buffer.rbegin();
861 0 : i != action_latest_buffer.rend(); i++) {
862 0 : if (i->unix_time < first_time) {
863 0 : break;
864 : }
865 0 : if (i->actor == "") {
866 0 : continue;
867 : }
868 : // Find position of suspect or continue
869 0 : v3s16 suspect_p;
870 0 : if (!i->getPosition(&suspect_p)) {
871 0 : continue;
872 : }
873 0 : float f = getSuspectNearness(i->actor_is_guess, suspect_p,
874 0 : i->unix_time, p, cur_time);
875 0 : if (f >= min_nearness && f > likely_suspect_nearness) {
876 0 : likely_suspect_nearness = f;
877 0 : likely_suspect = *i;
878 0 : if (likely_suspect_nearness >= nearness_shortcut) {
879 0 : break;
880 : }
881 : }
882 : }
883 : // No likely suspect was found
884 0 : if (likely_suspect_nearness == 0) {
885 0 : return "";
886 : }
887 : // Likely suspect was found
888 0 : return likely_suspect.actor;
889 : }
890 :
891 :
892 0 : void RollbackManager::flush()
893 : {
894 0 : sqlite3_exec(db, "BEGIN", NULL, NULL, NULL);
895 :
896 0 : std::list<RollbackAction>::const_iterator iter;
897 :
898 0 : for (iter = action_todisk_buffer.begin();
899 0 : iter != action_todisk_buffer.end();
900 : iter++) {
901 0 : if (iter->actor == "") {
902 0 : continue;
903 : }
904 :
905 0 : registerRow(actionRowFromRollbackAction(*iter));
906 : }
907 :
908 0 : sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
909 0 : action_todisk_buffer.clear();
910 0 : }
911 :
912 :
913 0 : void RollbackManager::addAction(const RollbackAction & action)
914 : {
915 0 : action_todisk_buffer.push_back(action);
916 0 : action_latest_buffer.push_back(action);
917 :
918 : // Flush to disk sometimes
919 0 : if (action_todisk_buffer.size() >= 500) {
920 0 : flush();
921 : }
922 0 : }
923 :
924 0 : std::list<RollbackAction> RollbackManager::getEntriesSince(time_t first_time)
925 : {
926 0 : flush();
927 0 : return getActionsSince(first_time);
928 : }
929 :
930 0 : std::list<RollbackAction> RollbackManager::getNodeActors(v3s16 pos, int range,
931 : time_t seconds, int limit)
932 : {
933 0 : time_t cur_time = time(0);
934 0 : time_t first_time = cur_time - seconds;
935 :
936 0 : return getActionsSince_range(first_time, pos, range, limit);
937 : }
938 :
939 0 : std::list<RollbackAction> RollbackManager::getRevertActions(
940 : const std::string &actor_filter,
941 : time_t seconds)
942 : {
943 0 : time_t cur_time = time(0);
944 0 : time_t first_time = cur_time - seconds;
945 :
946 0 : flush();
947 :
948 0 : return getActionsSince(first_time, actor_filter);
949 3 : }
950 :
|