LCOV - code coverage report
Current view: top level - src - rollback.cpp (source / functions) Hit Total Coverage
Test: report Lines: 1 469 0.2 %
Date: 2015-07-11 18:23:49 Functions: 2 43 4.7 %

          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             : 

Generated by: LCOV version 1.11