From b1965ac20922e3722392114bd63a22b403dcbe98 Mon Sep 17 00:00:00 2001 From: ShadowNinja Date: Wed, 25 Jun 2014 20:28:41 -0400 Subject: [PATCH] Clean up rollback --- src/gamedef.h | 6 +- src/inventory.cpp | 21 +- src/inventory.h | 14 +- src/inventorymanager.cpp | 10 +- src/map.h | 2 +- src/rollback.cpp | 1528 ++++++++++++----------------- src/rollback.h | 91 +- src/rollback_interface.cpp | 385 ++------ src/rollback_interface.h | 57 +- src/script/lua_api/l_rollback.cpp | 2 +- src/server.cpp | 16 +- src/server.h | 11 +- 12 files changed, 888 insertions(+), 1255 deletions(-) diff --git a/src/gamedef.h b/src/gamedef.h index b7830373b..793d85b39 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -30,7 +30,7 @@ class ITextureSource; class ISoundManager; class IShaderSource; class MtEventManager; -class IRollbackReportSink; +class IRollbackManager; namespace irr { namespace scene { class IAnimatedMesh; class ISceneManager; @@ -68,7 +68,7 @@ public: // Only usable on the server, and NOT thread-safe. It is usable from the // environment thread. - virtual IRollbackReportSink* getRollbackReportSink(){return NULL;} + virtual IRollbackManager* getRollbackManager(){return NULL;} // Used on the client virtual bool checkLocalPrivilege(const std::string &priv) @@ -82,7 +82,7 @@ public: ISoundManager* sound(){return getSoundManager();} IShaderSource* shsrc(){return getShaderSource();} MtEventManager* event(){return getEventManager();} - IRollbackReportSink* rollback(){return getRollbackReportSink();} + IRollbackManager* rollback(){return getRollbackManager();} }; #endif diff --git a/src/inventory.cpp b/src/inventory.cpp index cecef01a0..3fa31965c 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -183,7 +183,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) legacy_nimap.getName(material, name); if(name == "") name = "unknown_block"; - name = itemdef->getAlias(name); + if (itemdef) + name = itemdef->getAlias(name); count = materialcount; } else if(name == "MaterialItem2") @@ -202,7 +203,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) legacy_nimap.getName(material, name); if(name == "") name = "unknown_block"; - name = itemdef->getAlias(name); + if (itemdef) + name = itemdef->getAlias(name); count = materialcount; } else if(name == "node" || name == "NodeItem" || name == "MaterialItem3" @@ -223,7 +225,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) name = fnd.next(" "); } fnd.skip_over(" "); - name = itemdef->getAlias(name); + if (itemdef) + name = itemdef->getAlias(name); count = stoi(trim(fnd.next(""))); if(count == 0) count = 1; @@ -252,7 +255,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) count = 1; // Then read wear fnd.skip_over(" "); - name = itemdef->getAlias(name); + if (itemdef) + name = itemdef->getAlias(name); wear = stoi(trim(fnd.next(""))); } else @@ -262,7 +266,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) // The real thing // Apply item aliases - name = itemdef->getAlias(name); + if (itemdef) + name = itemdef->getAlias(name); // Read the count std::string count_str; @@ -294,9 +299,9 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef) } while(false); } - if(name.empty() || count == 0) + if (name.empty() || count == 0) clear(); - else if(itemdef->get(name).type == ITEM_TOOL) + else if (itemdef && itemdef->get(name).type == ITEM_TOOL) count = 1; } @@ -308,12 +313,12 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef) std::string ItemStack::getItemString() const { - // Get item string std::ostringstream os(std::ios::binary); serialize(os); return os.str(); } + ItemStack ItemStack::addItem(const ItemStack &newitem_, IItemDefManager *itemdef) { diff --git a/src/inventory.h b/src/inventory.h index d21a5deda..e4a97e1d3 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -20,12 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #ifndef INVENTORY_HEADER #define INVENTORY_HEADER -#include -#include -#include -#include "irrlichttypes.h" #include "debug.h" #include "itemdef.h" +#include "irrlichttypes.h" +#include +#include +#include +#include struct ToolCapabilities; @@ -39,8 +40,9 @@ struct ItemStack // Serialization void serialize(std::ostream &os) const; - void deSerialize(std::istream &is, IItemDefManager *itemdef); - void deSerialize(const std::string &s, IItemDefManager *itemdef); + // Deserialization. Pass itemdef unless you don't want aliases resolved. + void deSerialize(std::istream &is, IItemDefManager *itemdef = NULL); + void deSerialize(const std::string &s, IItemDefManager *itemdef = NULL); // Returns the string used for inventory std::string getItemString() const; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 1ffcc3460..7486cc419 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -368,7 +368,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame */ if(!ignore_rollback && gamedef->rollback()) { - IRollbackReportSink *rollback = gamedef->rollback(); + IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if(src_can_take_count != -1){ @@ -380,7 +380,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, false, - src_item.getItemString()); + src_item); rollback->reportAction(action); } // If destination is not infinite, record item put @@ -393,7 +393,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame loc = os.str(); } action.setModifyInventoryStack(loc, to_list, to_i, true, - src_item.getItemString()); + src_item); rollback->reportAction(action); } } @@ -632,7 +632,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame */ if(!ignore_src_rollback && gamedef->rollback()) { - IRollbackReportSink *rollback = gamedef->rollback(); + IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if(src_can_take_count != -1){ @@ -644,7 +644,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, - false, src_item.getItemString()); + false, src_item); rollback->reportAction(action); } } diff --git a/src/map.h b/src/map.h index 4df9a5562..c650e51b6 100644 --- a/src/map.h +++ b/src/map.h @@ -41,7 +41,7 @@ class ServerMapSector; class MapBlock; class NodeMetadata; class IGameDef; -class IRollbackReportSink; +class IRollbackManager; class EmergeManager; class ServerEnvironment; struct BlockMakeData; diff --git a/src/rollback.cpp b/src/rollback.cpp index 0af7aa4f7..7267a50f6 100644 --- a/src/rollback.cpp +++ b/src/rollback.cpp @@ -32,547 +32,457 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "sqlite3.h" #include "filesys.h" -#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" - #define POINTS_PER_NODE (16.0) -static std::string dbp; -static sqlite3* dbh = NULL; -static sqlite3_stmt* dbs_insert = NULL; -static sqlite3_stmt* dbs_replace = NULL; -static sqlite3_stmt* dbs_select = NULL; -static sqlite3_stmt* dbs_select_range = NULL; -static sqlite3_stmt* dbs_select_withActor = NULL; -static sqlite3_stmt* dbs_knownActor_select = NULL; -static sqlite3_stmt* dbs_knownActor_insert = NULL; -static sqlite3_stmt* dbs_knownNode_select = NULL; -static sqlite3_stmt* dbs_knownNode_insert = NULL; +#define SQLRES(f, good) \ + if ((f) != (good)) {\ + throw FileNotGoodException(std::string("RollbackManager: " \ + "SQLite3 error (" __FILE__ ":" TOSTRING(__LINE__) \ + "): ") + sqlite3_errmsg(db)); \ + } +#define SQLOK(f) SQLRES(f, SQLITE_OK) -struct Stack { - int node; - int quantity; + +class ItemStackRow : public ItemStack { +public: + ItemStackRow & operator = (const ItemStack & other) + { + *static_cast(this) = other; + return *this; + } + + int id; }; + struct ActionRow { - int id; - int actor; - time_t timestamp; - int type; - std::string location, list; - int index, add; - Stack stack; - int nodeMeta; - int x, y, z; - int oldNode; - int oldParam1, oldParam2; - std::string oldMeta; - int newNode; - int newParam1, newParam2; - std::string newMeta; - int guessed; + int id; + int actor; + time_t timestamp; + int type; + std::string location, list; + int index, add; + ItemStackRow stack; + int nodeMeta; + int x, y, z; + int oldNode; + int oldParam1, oldParam2; + std::string oldMeta; + int newNode; + int newParam1, newParam2; + std::string newMeta; + int guessed; }; + struct Entity { int id; std::string name; }; -typedef std::vector Entities; -Entities KnownActors; -Entities KnownNodes; -void registerNewActor(int id, std::string name) +RollbackManager::RollbackManager(const std::string & world_path, + IGameDef * gamedef_) : + gamedef(gamedef_), + current_actor_is_guess(false) { - Entity newActor; + verbosestream << "RollbackManager::RollbackManager(" << world_path + << ")" << std::endl; - newActor.id = id; - newActor.name = name; + std::string txt_filename = world_path + DIR_DELIM "rollback.txt"; + std::string migrating_flag = txt_filename + ".migrating"; + database_path = world_path + DIR_DELIM "rollback.sqlite"; - KnownActors.push_back(newActor); + initDatabase(); - //std::cout << "New actor registered: " << id << " | " << name << std::endl; + if (fs::PathExists(txt_filename) && (fs::PathExists(migrating_flag) || + !fs::PathExists(database_path))) { + std::ofstream of(migrating_flag.c_str()); + of.close(); + migrate(txt_filename); + fs::DeleteSingleFileOrEmptyDirectory(migrating_flag); + } } -void registerNewNode(int id, std::string name) + +RollbackManager::~RollbackManager() { - Entity newNode; + SQLOK(sqlite3_finalize(stmt_insert)); + SQLOK(sqlite3_finalize(stmt_replace)); + SQLOK(sqlite3_finalize(stmt_select)); + SQLOK(sqlite3_finalize(stmt_select_range)); + SQLOK(sqlite3_finalize(stmt_select_withActor)); + SQLOK(sqlite3_finalize(stmt_knownActor_select)); + SQLOK(sqlite3_finalize(stmt_knownActor_insert)); + SQLOK(sqlite3_finalize(stmt_knownNode_select)); + SQLOK(sqlite3_finalize(stmt_knownNode_insert)); - newNode.id = id; - newNode.name = name; - - KnownNodes.push_back(newNode); - - //std::cout << "New node registered: " << id << " | " << name << std::endl; + SQLOK(sqlite3_close(db)); } -int getActorId(std::string name) -{ - Entities::const_iterator iter; - for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter) +void RollbackManager::registerNewActor(const int id, const std::string &name) +{ + Entity actor = {id, name}; + knownActors.push_back(actor); +} + + +void RollbackManager::registerNewNode(const int id, const std::string &name) +{ + Entity node = {id, name}; + knownNodes.push_back(node); +} + + +int RollbackManager::getActorId(const std::string &name) +{ + for (std::vector::const_iterator iter = knownActors.begin(); + iter != knownActors.end(); ++iter) { if (iter->name == name) { return iter->id; } + } - sqlite3_reset(dbs_knownActor_insert); - sqlite3_bind_text(dbs_knownActor_insert, 1, name.c_str(), -1, NULL); - sqlite3_step(dbs_knownActor_insert); - - int id = sqlite3_last_insert_rowid(dbh); - - //std::cout << "Actor ID insert returns " << insert << std::endl; + SQLOK(sqlite3_bind_text(stmt_knownActor_insert, 1, name.c_str(), name.size(), NULL)); + SQLRES(sqlite3_step(stmt_knownActor_insert), SQLITE_DONE); + SQLOK(sqlite3_reset(stmt_knownActor_insert)); + int id = sqlite3_last_insert_rowid(db); registerNewActor(id, name); return id; } -int getNodeId(std::string name) -{ - Entities::const_iterator iter; - for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter) +int RollbackManager::getNodeId(const std::string &name) +{ + for (std::vector::const_iterator iter = knownNodes.begin(); + iter != knownNodes.end(); ++iter) { if (iter->name == name) { return iter->id; } + } - sqlite3_reset(dbs_knownNode_insert); - sqlite3_bind_text(dbs_knownNode_insert, 1, name.c_str(), -1, NULL); - sqlite3_step(dbs_knownNode_insert); - - int id = sqlite3_last_insert_rowid(dbh); + SQLOK(sqlite3_bind_text(stmt_knownNode_insert, 1, name.c_str(), name.size(), NULL)); + SQLRES(sqlite3_step(stmt_knownNode_insert), SQLITE_DONE); + SQLOK(sqlite3_reset(stmt_knownNode_insert)); + int id = sqlite3_last_insert_rowid(db); registerNewNode(id, name); return id; } -const char * getActorName(int id) + +const char * RollbackManager::getActorName(const int id) { - Entities::const_iterator iter; - - //std::cout << "getActorName of id " << id << std::endl; - - for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter) + for (std::vector::const_iterator iter = knownActors.begin(); + iter != knownActors.end(); ++iter) { if (iter->id == id) { return iter->name.c_str(); } - - return ""; -} - -const char * getNodeName(int id) -{ - Entities::const_iterator iter; - - //std::cout << "getNodeName of id " << id << std::endl; - - for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter) - if (iter->id == id) { - return iter->name.c_str(); - } - - return ""; -} - -Stack getStackFromString(std::string text) -{ - Stack stack; - - size_t off = text.find_last_of(" "); - - stack.node = getNodeId(text.substr(0, off)); - stack.quantity = atoi(text.substr(off + 1).c_str()); - - return stack; -} - -std::string getStringFromStack(Stack stack) -{ - std::string text; - - text.append(getNodeName(stack.node)); - text.append(" "); - text.append(itos(stack.quantity)); - - return text; -} - -bool SQL_createDatabase(void) -{ - infostream << "CreateDB:" << dbp << std::endl; - - int dbs = sqlite3_exec(dbh, - "CREATE TABLE IF NOT EXISTS `actor` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`name` TEXT NOT NULL);" - "CREATE TABLE IF NOT EXISTS `node` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`name` TEXT NOT NULL);" - "CREATE TABLE IF NOT EXISTS `action` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT," - "`actor` INTEGER NOT NULL," - "`timestamp` TIMESTAMP NOT NULL," - "`type` INTEGER NOT NULL," - "`list` TEXT," - "`index` INTEGER," - "`add` INTEGER," - "`stackNode` INTEGER," - "`stackQuantity` INTEGER," - "`nodeMeta` INTEGER," - "`x` INT," - "`y` INT," - "`z` INT," - "`oldNode` INTEGER," - "`oldParam1` INTEGER," - "`oldParam2` INTEGER," - "`oldMeta` TEXT," - "`newNode` INTEGER," - "`newParam1` INTEGER," - "`newParam2` INTEGER," - "`newMeta` TEXT," - "`guessedActor` INTEGER," - "FOREIGN KEY (`actor`) REFERENCES `actor`(`id`)," - "FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`)," - "FOREIGN KEY (`newNode`) REFERENCES `node`(`id`));" - "CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);" - "CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);", - NULL, NULL, NULL); - if (dbs == SQLITE_ABORT) { - throw FileNotGoodException("Could not create sqlite3 database structure"); - } else if (dbs != 0) { - throw FileNotGoodException("SQL Rollback: Exec statement to create table structure returned a non-zero value"); - } else { - infostream << "SQL Rollback: SQLite3 database structure was created" << std::endl; } + return ""; +} + + +const char * RollbackManager::getNodeName(const int id) +{ + for (std::vector::const_iterator iter = knownNodes.begin(); + iter != knownNodes.end(); ++iter) { + if (iter->id == id) { + return iter->name.c_str(); + } + } + + return ""; +} + + +bool RollbackManager::createTables() +{ + SQLOK(sqlite3_exec(db, + "CREATE TABLE IF NOT EXISTS `actor` (\n" + " `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + " `name` TEXT NOT NULL\n" + ");\n" + "CREATE TABLE IF NOT EXISTS `node` (\n" + " `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,\n" + " `name` TEXT NOT NULL\n" + ");\n" + "CREATE TABLE IF NOT EXISTS `action` (\n" + " `id` INTEGER PRIMARY KEY AUTOINCREMENT,\n" + " `actor` INTEGER NOT NULL,\n" + " `timestamp` TIMESTAMP NOT NULL,\n" + " `type` INTEGER NOT NULL,\n" + " `list` TEXT,\n" + " `index` INTEGER,\n" + " `add` INTEGER,\n" + " `stackNode` INTEGER,\n" + " `stackQuantity` INTEGER,\n" + " `nodeMeta` INTEGER,\n" + " `x` INT,\n" + " `y` INT,\n" + " `z` INT,\n" + " `oldNode` INTEGER,\n" + " `oldParam1` INTEGER,\n" + " `oldParam2` INTEGER,\n" + " `oldMeta` TEXT,\n" + " `newNode` INTEGER,\n" + " `newParam1` INTEGER,\n" + " `newParam2` INTEGER,\n" + " `newMeta` TEXT,\n" + " `guessedActor` INTEGER,\n" + " FOREIGN KEY (`actor`) REFERENCES `actor`(`id`),\n" + " FOREIGN KEY (`stackNode`) REFERENCES `node`(`id`),\n" + " FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`),\n" + " FOREIGN KEY (`newNode`) REFERENCES `node`(`id`)\n" + ");\n" + "CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);\n" + "CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);\n", + NULL, NULL, NULL)); + verbosestream << "SQL Rollback: SQLite3 database structure was created" << std::endl; + return true; } -void SQL_databaseCheck(void) + + +void RollbackManager::initDatabase() { - if (dbh) { - return; - } + verbosestream << "RollbackManager: Database connection setup" << std::endl; - infostream << "Database connection setup" << std::endl; - - bool needsCreate = !fs::PathExists(dbp); - int dbo = sqlite3_open_v2(dbp.c_str(), &dbh, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, - NULL); - - if (dbo != SQLITE_OK) { - infostream << "SQLROLLBACK: SQLite3 database failed to open: " - << sqlite3_errmsg(dbh) << std::endl; - throw FileNotGoodException("Cannot open database file"); - } + bool needsCreate = !fs::PathExists(database_path); + SQLOK(sqlite3_open_v2(database_path.c_str(), &db, + SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)); if (needsCreate) { - SQL_createDatabase(); + createTables(); } - int dbr; - - dbr = sqlite3_prepare_v2(dbh, - "INSERT INTO `action` (" - " `actor`, `timestamp`, `type`," - " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`," - " `x`, `y`, `z`," - " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`," - " `newNode`, `newParam1`, `newParam2`, `newMeta`," - " `guessedActor`" - ") VALUES (" - " ?, ?, ?," - " ?, ?, ?, ?, ?, ?," - " ?, ?, ?," - " ?, ?, ?, ?," - " ?, ?, ?, ?," + SQLOK(sqlite3_prepare_v2(db, + "INSERT INTO `action` (\n" + " `actor`, `timestamp`, `type`,\n" + " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,\n" + " `x`, `y`, `z`,\n" + " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n" + " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n" + " `guessedActor`\n" + ") VALUES (\n" + " ?, ?, ?,\n" + " ?, ?, ?, ?, ?, ?,\n" + " ?, ?, ?,\n" + " ?, ?, ?, ?,\n" + " ?, ?, ?, ?,\n" " ?" ");", - -1, &dbs_insert, NULL); + -1, &stmt_insert, NULL)); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(sqlite3_errmsg(dbh)); - } - - dbr = sqlite3_prepare_v2(dbh, - "REPLACE INTO `action` (" - " `actor`, `timestamp`, `type`," - " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`," - " `x`, `y`, `z`," - " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`," - " `newNode`, `newParam1`, `newParam2`, `newMeta`," - " `guessedActor`, `id`" - ") VALUES (" - " ?, ?, ?," - " ?, ?, ?, ?, ?, ?," - " ?, ?, ?," - " ?, ?, ?, ?," - " ?, ?, ?, ?," - " ?, ?" + SQLOK(sqlite3_prepare_v2(db, + "REPLACE INTO `action` (\n" + " `actor`, `timestamp`, `type`,\n" + " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,\n" + " `x`, `y`, `z`,\n" + " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n" + " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n" + " `guessedActor`, `id`\n" + ") VALUES (\n" + " ?, ?, ?,\n" + " ?, ?, ?, ?, ?, ?,\n" + " ?, ?, ?,\n" + " ?, ?, ?, ?,\n" + " ?, ?, ?, ?,\n" + " ?, ?\n" ");", - -1, &dbs_replace, NULL); + -1, &stmt_replace, NULL)); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(sqlite3_errmsg(dbh)); - } - - dbr = sqlite3_prepare_v2(dbh, - "SELECT" - " `actor`, `timestamp`, `type`," - " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`," - " `x`, `y`, `z`," - " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`," - " `newNode`, `newParam1`, `newParam2`, `newMeta`," - " `guessedActor`" - " FROM `action`" - " WHERE `timestamp` >= ?" + SQLOK(sqlite3_prepare_v2(db, + "SELECT\n" + " `actor`, `timestamp`, `type`,\n" + " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n" + " `x`, `y`, `z`,\n" + " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n" + " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n" + " `guessedActor`\n" + " FROM `action`\n" + " WHERE `timestamp` >= ?\n" " ORDER BY `timestamp` DESC, `id` DESC", - -1, &dbs_select, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + -1, &stmt_select, NULL)); - dbr = sqlite3_prepare_v2(dbh, - "SELECT" - " `actor`, `timestamp`, `type`," - " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`," - " `x`, `y`, `z`," - " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`," - " `newNode`, `newParam1`, `newParam2`, `newMeta`," - " `guessedActor`" - " FROM `action`" - " WHERE `timestamp` >= ?" - " AND `x` IS NOT NULL" - " AND `y` IS NOT NULL" - " AND `z` IS NOT NULL" - " AND ABS(`x` - ?) <= ?" - " AND ABS(`y` - ?) <= ?" - " AND ABS(`z` - ?) <= ?" - " ORDER BY `timestamp` DESC, `id` DESC" - " LIMIT 0,?", - -1, &dbs_select_range, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, + "SELECT\n" + " `actor`, `timestamp`, `type`,\n" + " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n" + " `x`, `y`, `z`,\n" + " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n" + " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n" + " `guessedActor`\n" + "FROM `action`\n" + "WHERE `timestamp` >= ?\n" + " AND `x` IS NOT NULL\n" + " AND `y` IS NOT NULL\n" + " AND `z` IS NOT NULL\n" + " AND `x` BETWEEN ? AND ?\n" + " AND `y` BETWEEN ? AND ?\n" + " AND `z` BETWEEN ? AND ?\n" + "ORDER BY `timestamp` DESC, `id` DESC\n" + "LIMIT 0,?", + -1, &stmt_select_range, NULL)); - dbr = sqlite3_prepare_v2(dbh, - "SELECT" - " `actor`, `timestamp`, `type`," - " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`," - " `x`, `y`, `z`," - " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`," - " `newNode`, `newParam1`, `newParam2`, `newMeta`," - " `guessedActor`" - " FROM `action`" - " WHERE `timestamp` >= ?" - " AND `actor` = ?" - " ORDER BY `timestamp` DESC, `id` DESC", - -1, &dbs_select_withActor, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, + "SELECT\n" + " `actor`, `timestamp`, `type`,\n" + " `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`,\n" + " `x`, `y`, `z`,\n" + " `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,\n" + " `newNode`, `newParam1`, `newParam2`, `newMeta`,\n" + " `guessedActor`\n" + "FROM `action`\n" + "WHERE `timestamp` >= ?\n" + " AND `actor` = ?\n" + "ORDER BY `timestamp` DESC, `id` DESC\n", + -1, &stmt_select_withActor, NULL)); - dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `actor`", -1, - &dbs_knownActor_select, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `actor`", + -1, &stmt_knownActor_select, NULL)); - dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `actor` (`name`) VALUES (?)", -1, - &dbs_knownActor_insert, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `actor` (`name`) VALUES (?)", + -1, &stmt_knownActor_insert, NULL)); - dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `node`", -1, - &dbs_knownNode_select, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `node`", + -1, &stmt_knownNode_select, NULL)); - dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `node` (`name`) VALUES (?)", -1, - &dbs_knownNode_insert, NULL); - if (dbr != SQLITE_OK) { - throw FileNotGoodException(itos(dbr).c_str()); - } + SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `node` (`name`) VALUES (?)", + -1, &stmt_knownNode_insert, NULL)); - infostream << "SQL prepared statements setup correctly" << std::endl; + verbosestream << "SQL prepared statements setup correctly" << std::endl; - int select; - - sqlite3_reset(dbs_knownActor_select); - while (SQLITE_ROW == (select = sqlite3_step(dbs_knownActor_select))) + while (sqlite3_step(stmt_knownActor_select) == SQLITE_ROW) { registerNewActor( - sqlite3_column_int(dbs_knownActor_select, 0), - reinterpret_cast(sqlite3_column_text(dbs_knownActor_select, 1)) + sqlite3_column_int(stmt_knownActor_select, 0), + reinterpret_cast(sqlite3_column_text(stmt_knownActor_select, 1)) ); + } + SQLOK(sqlite3_reset(stmt_knownActor_select)); - sqlite3_reset(dbs_knownNode_select); - while (SQLITE_ROW == (select = sqlite3_step(dbs_knownNode_select))) + while (sqlite3_step(stmt_knownNode_select) == SQLITE_ROW) { registerNewNode( - sqlite3_column_int(dbs_knownNode_select, 0), - reinterpret_cast(sqlite3_column_text(dbs_knownNode_select, 1)) + sqlite3_column_int(stmt_knownNode_select, 0), + reinterpret_cast(sqlite3_column_text(stmt_knownNode_select, 1)) ); - - return; + } + SQLOK(sqlite3_reset(stmt_knownNode_select)); } -bool SQL_registerRow(ActionRow row) + + +bool RollbackManager::registerRow(const ActionRow & row) { - SQL_databaseCheck(); + sqlite3_stmt * stmt_do = (row.id) ? stmt_replace : stmt_insert; - sqlite3_stmt * dbs_do = (row.id) ? dbs_replace : dbs_insert; - - /* - std::cout - << (row.id? "Replacing": "Inserting") - << " ActionRow" << std::endl; - */ - sqlite3_reset(dbs_do); - - int bind [22], ii = 0; bool nodeMeta = false; - bind[ii++] = sqlite3_bind_int(dbs_do, 1, row.actor); - bind[ii++] = sqlite3_bind_int64(dbs_do, 2, row.timestamp); - bind[ii++] = sqlite3_bind_int(dbs_do, 3, row.type); + SQLOK(sqlite3_bind_int (stmt_do, 1, row.actor)); + SQLOK(sqlite3_bind_int64(stmt_do, 2, row.timestamp)); + SQLOK(sqlite3_bind_int (stmt_do, 3, row.type)); if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) { - std::string loc = row.location; - std::string locType = loc.substr(0, loc.find(":")); - nodeMeta = (locType == "nodemeta"); + const std::string & loc = row.location; + nodeMeta = (loc.substr(0, 9) == "nodemeta:"); - bind[ii++] = sqlite3_bind_text(dbs_do, 4, row.list.c_str(), row.list.size(), NULL); - bind[ii++] = sqlite3_bind_int(dbs_do, 5, row.index); - bind[ii++] = sqlite3_bind_int(dbs_do, 6, row.add); - bind[ii++] = sqlite3_bind_int(dbs_do, 7, row.stack.node); - bind[ii++] = sqlite3_bind_int(dbs_do, 8, row.stack.quantity); - bind[ii++] = sqlite3_bind_int(dbs_do, 9, (int) nodeMeta); + SQLOK(sqlite3_bind_text(stmt_do, 4, row.list.c_str(), row.list.size(), NULL)); + SQLOK(sqlite3_bind_int (stmt_do, 5, row.index)); + SQLOK(sqlite3_bind_int (stmt_do, 6, row.add)); + SQLOK(sqlite3_bind_int (stmt_do, 7, row.stack.id)); + SQLOK(sqlite3_bind_int (stmt_do, 8, row.stack.count)); + SQLOK(sqlite3_bind_int (stmt_do, 9, (int) nodeMeta)); if (nodeMeta) { - std::string x, y, z; - int l, r; - l = loc.find(':') + 1; - r = loc.find(','); - x = loc.substr(l, r - l); - l = r + 1; - r = loc.find(',', l); - y = loc.substr(l, r - l); - z = loc.substr(r + 1); - bind[ii++] = sqlite3_bind_int(dbs_do, 10, atoi(x.c_str())); - bind[ii++] = sqlite3_bind_int(dbs_do, 11, atoi(y.c_str())); - bind[ii++] = sqlite3_bind_int(dbs_do, 12, atoi(z.c_str())); + std::string::size_type p1, p2; + p1 = loc.find(':') + 1; + p2 = loc.find(','); + std::string x = loc.substr(p1, p2 - p1); + p1 = p2 + 1; + p2 = loc.find(',', p1); + std::string y = loc.substr(p1, p2 - p1); + std::string z = loc.substr(p2 + 1); + SQLOK(sqlite3_bind_int(stmt_do, 10, atoi(x.c_str()))); + SQLOK(sqlite3_bind_int(stmt_do, 11, atoi(y.c_str()))); + SQLOK(sqlite3_bind_int(stmt_do, 12, atoi(z.c_str()))); } } else { - bind[ii++] = sqlite3_bind_null(dbs_do, 4); - bind[ii++] = sqlite3_bind_null(dbs_do, 5); - bind[ii++] = sqlite3_bind_null(dbs_do, 6); - bind[ii++] = sqlite3_bind_null(dbs_do, 7); - bind[ii++] = sqlite3_bind_null(dbs_do, 8); - bind[ii++] = sqlite3_bind_null(dbs_do, 9); + SQLOK(sqlite3_bind_null(stmt_do, 4)); + SQLOK(sqlite3_bind_null(stmt_do, 5)); + SQLOK(sqlite3_bind_null(stmt_do, 6)); + SQLOK(sqlite3_bind_null(stmt_do, 7)); + SQLOK(sqlite3_bind_null(stmt_do, 8)); + SQLOK(sqlite3_bind_null(stmt_do, 9)); } if (row.type == RollbackAction::TYPE_SET_NODE) { - bind[ii++] = sqlite3_bind_int(dbs_do, 10, row.x); - bind[ii++] = sqlite3_bind_int(dbs_do, 11, row.y); - bind[ii++] = sqlite3_bind_int(dbs_do, 12, row.z); - bind[ii++] = sqlite3_bind_int(dbs_do, 13, row.oldNode); - bind[ii++] = sqlite3_bind_int(dbs_do, 14, row.oldParam1); - bind[ii++] = sqlite3_bind_int(dbs_do, 15, row.oldParam2); - bind[ii++] = sqlite3_bind_text(dbs_do, 16, row.oldMeta.c_str(), row.oldMeta.size(), NULL); - bind[ii++] = sqlite3_bind_int(dbs_do, 17, row.newNode); - bind[ii++] = sqlite3_bind_int(dbs_do, 18, row.newParam1); - bind[ii++] = sqlite3_bind_int(dbs_do, 19, row.newParam2); - bind[ii++] = sqlite3_bind_text(dbs_do, 20, row.newMeta.c_str(), row.newMeta.size(), NULL); - bind[ii++] = sqlite3_bind_int(dbs_do, 21, row.guessed ? 1 : 0); + SQLOK(sqlite3_bind_int (stmt_do, 10, row.x)); + SQLOK(sqlite3_bind_int (stmt_do, 11, row.y)); + SQLOK(sqlite3_bind_int (stmt_do, 12, row.z)); + SQLOK(sqlite3_bind_int (stmt_do, 13, row.oldNode)); + SQLOK(sqlite3_bind_int (stmt_do, 14, row.oldParam1)); + SQLOK(sqlite3_bind_int (stmt_do, 15, row.oldParam2)); + SQLOK(sqlite3_bind_text(stmt_do, 16, row.oldMeta.c_str(), row.oldMeta.size(), NULL)); + SQLOK(sqlite3_bind_int (stmt_do, 17, row.newNode)); + SQLOK(sqlite3_bind_int (stmt_do, 18, row.newParam1)); + SQLOK(sqlite3_bind_int (stmt_do, 19, row.newParam2)); + SQLOK(sqlite3_bind_text(stmt_do, 20, row.newMeta.c_str(), row.newMeta.size(), NULL)); + SQLOK(sqlite3_bind_int (stmt_do, 21, row.guessed ? 1 : 0)); } else { if (!nodeMeta) { - bind[ii++] = sqlite3_bind_null(dbs_do, 10); - bind[ii++] = sqlite3_bind_null(dbs_do, 11); - bind[ii++] = sqlite3_bind_null(dbs_do, 12); + SQLOK(sqlite3_bind_null(stmt_do, 10)); + SQLOK(sqlite3_bind_null(stmt_do, 11)); + SQLOK(sqlite3_bind_null(stmt_do, 12)); } - bind[ii++] = sqlite3_bind_null(dbs_do, 13); - bind[ii++] = sqlite3_bind_null(dbs_do, 14); - bind[ii++] = sqlite3_bind_null(dbs_do, 15); - bind[ii++] = sqlite3_bind_null(dbs_do, 16); - bind[ii++] = sqlite3_bind_null(dbs_do, 17); - bind[ii++] = sqlite3_bind_null(dbs_do, 18); - bind[ii++] = sqlite3_bind_null(dbs_do, 19); - bind[ii++] = sqlite3_bind_null(dbs_do, 20); - bind[ii++] = sqlite3_bind_null(dbs_do, 21); + SQLOK(sqlite3_bind_null(stmt_do, 13)); + SQLOK(sqlite3_bind_null(stmt_do, 14)); + SQLOK(sqlite3_bind_null(stmt_do, 15)); + SQLOK(sqlite3_bind_null(stmt_do, 16)); + SQLOK(sqlite3_bind_null(stmt_do, 17)); + SQLOK(sqlite3_bind_null(stmt_do, 18)); + SQLOK(sqlite3_bind_null(stmt_do, 19)); + SQLOK(sqlite3_bind_null(stmt_do, 20)); + SQLOK(sqlite3_bind_null(stmt_do, 21)); } if (row.id) { - bind[ii++] = sqlite3_bind_int(dbs_do, 22, row.id); + SQLOK(sqlite3_bind_int(stmt_do, 22, row.id)); } - for (ii = 0; ii < 20; ++ii) - if (bind[ii] != SQLITE_OK) - infostream - << "WARNING: failed to bind param " << ii + 1 - << " when inserting an entry in table setnode" << std::endl; + int written = sqlite3_step(stmt_do); - /* - std::cout << "========DB-WRITTEN==========" << std::endl; - std::cout << "id: " << row.id << std::endl; - std::cout << "actor: " << row.actor << std::endl; - std::cout << "time: " << row.timestamp << std::endl; - std::cout << "type: " << row.type << std::endl; - if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) - { - std::cout << "Location: " << row.location << std::endl; - std::cout << "List: " << row.list << std::endl; - std::cout << "Index: " << row.index << std::endl; - std::cout << "Add: " << row.add << std::endl; - std::cout << "Stack: " << row.stack << std::endl; - } - if (row.type == RollbackAction::TYPE_SET_NODE) - { - std::cout << "x: " << row.x << std::endl; - std::cout << "y: " << row.y << std::endl; - std::cout << "z: " << row.z << std::endl; - std::cout << "oldNode: " << row.oldNode << std::endl; - std::cout << "oldParam1: " << row.oldParam1 << std::endl; - std::cout << "oldParam2: " << row.oldParam2 << std::endl; - std::cout << "oldMeta: " << row.oldMeta << std::endl; - std::cout << "newNode: " << row.newNode << std::endl; - std::cout << "newParam1: " << row.newParam1 << std::endl; - std::cout << "newParam2: " << row.newParam2 << std::endl; - std::cout << "newMeta: " << row.newMeta << std::endl; - std::cout << "DESERIALIZE" << row.newMeta.c_str() << std::endl; - std::cout << "guessed: " << row.guessed << std::endl; - } - */ - - int written = sqlite3_step(dbs_do); + SQLOK(sqlite3_reset(stmt_do)); return written == SQLITE_DONE; - - //if (written != SQLITE_DONE) - // std::cout << "WARNING: rollback action not written: " << sqlite3_errmsg(dbh) << std::endl; - //else std::cout << "Action correctly inserted via SQL" << std::endl; } -std::list actionRowsFromSelect(sqlite3_stmt* stmt) + + +const std::list RollbackManager::actionRowsFromSelect(sqlite3_stmt* stmt) { std::list rows; const unsigned char * text; size_t size; - while (SQLITE_ROW == sqlite3_step(stmt)) { + while (sqlite3_step(stmt) == SQLITE_ROW) { ActionRow row; - row.actor = sqlite3_column_int(stmt, 0); + row.actor = sqlite3_column_int (stmt, 0); row.timestamp = sqlite3_column_int64(stmt, 1); - row.type = sqlite3_column_int(stmt, 2); + row.type = sqlite3_column_int (stmt, 2); if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) { - text = sqlite3_column_text(stmt, 3); - size = sqlite3_column_bytes(stmt, 3); - row.list = std::string(reinterpret_cast(text), size); - row.index = sqlite3_column_int(stmt, 4); - row.add = sqlite3_column_int(stmt, 5); - row.stack.node = sqlite3_column_int(stmt, 6); - row.stack.quantity = sqlite3_column_int(stmt, 7); - row.nodeMeta = sqlite3_column_int(stmt, 8); + text = sqlite3_column_text (stmt, 3); + size = sqlite3_column_bytes(stmt, 3); + row.list = std::string(reinterpret_cast(text), size); + row.index = sqlite3_column_int(stmt, 4); + row.add = sqlite3_column_int(stmt, 5); + row.stack.id = sqlite3_column_int(stmt, 6); + row.stack.count = sqlite3_column_int(stmt, 7); + row.nodeMeta = sqlite3_column_int(stmt, 8); } if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta) { @@ -582,74 +492,42 @@ std::list actionRowsFromSelect(sqlite3_stmt* stmt) } if (row.type == RollbackAction::TYPE_SET_NODE) { - row.oldNode = sqlite3_column_int(stmt, 12); + row.oldNode = sqlite3_column_int(stmt, 12); row.oldParam1 = sqlite3_column_int(stmt, 13); row.oldParam2 = sqlite3_column_int(stmt, 14); - text = sqlite3_column_text(stmt, 15); - size = sqlite3_column_bytes(stmt, 15); + text = sqlite3_column_text (stmt, 15); + size = sqlite3_column_bytes(stmt, 15); row.oldMeta = std::string(reinterpret_cast(text), size); row.newNode = sqlite3_column_int(stmt, 16); row.newParam1 = sqlite3_column_int(stmt, 17); row.newParam2 = sqlite3_column_int(stmt, 18); - text = sqlite3_column_text(stmt, 19); - size = sqlite3_column_bytes(stmt, 19); + text = sqlite3_column_text(stmt, 19); + size = sqlite3_column_bytes(stmt, 19); row.newMeta = std::string(reinterpret_cast(text), size); row.guessed = sqlite3_column_int(stmt, 20); } - row.location = row.nodeMeta ? "nodemeta:" : getActorName(row.actor); - if (row.nodeMeta) { - row.location.append(itos(row.x)); - row.location.append(","); - row.location.append(itos(row.y)); - row.location.append(","); - row.location.append(itos(row.z)); + row.location = "nodemeta:"; + row.location += itos(row.x); + row.location += ','; + row.location += itos(row.y); + row.location += ','; + row.location += itos(row.z); + } else { + row.location = getActorName(row.actor); } - /* - std::cout << "=======SELECTED==========" << "\n"; - std::cout << "Actor: " << row.actor << "\n"; - std::cout << "Timestamp: " << row.timestamp << "\n"; - - if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) - { - std::cout << "list: " << row.list << "\n"; - std::cout << "index: " << row.index << "\n"; - std::cout << "add: " << row.add << "\n"; - std::cout << "stackNode: " << row.stack.node << "\n"; - std::cout << "stackQuantity: " << row.stack.quantity << "\n"; - if (row.nodeMeta) - { - std::cout << "X: " << row.x << "\n"; - std::cout << "Y: " << row.y << "\n"; - std::cout << "Z: " << row.z << "\n"; - } - std::cout << "Location: " << row.location << "\n"; - } - else - { - std::cout << "X: " << row.x << "\n"; - std::cout << "Y: " << row.y << "\n"; - std::cout << "Z: " << row.z << "\n"; - std::cout << "oldNode: " << row.oldNode << "\n"; - std::cout << "oldParam1: " << row.oldParam1 << "\n"; - std::cout << "oldParam2: " << row.oldParam2 << "\n"; - std::cout << "oldMeta: " << row.oldMeta << "\n"; - std::cout << "newNode: " << row.newNode << "\n"; - std::cout << "newParam1: " << row.newParam1 << "\n"; - std::cout << "newParam2: " << row.newParam2 << "\n"; - std::cout << "newMeta: " << row.newMeta << "\n"; - std::cout << "guessed: " << row.guessed << "\n"; - } - */ - rows.push_back(row); } + SQLOK(sqlite3_reset(stmt)); + return rows; } -ActionRow actionRowFromRollbackAction(RollbackAction action) + + +ActionRow RollbackManager::actionRowFromRollbackAction(const RollbackAction & action) { ActionRow row; @@ -663,7 +541,8 @@ ActionRow actionRowFromRollbackAction(RollbackAction action) row.list = action.inventory_list; row.index = action.inventory_index; row.add = action.inventory_add; - row.stack = getStackFromString(action.inventory_stack); + row.stack = action.inventory_stack; + row.stack.id = getNodeId(row.stack.name); } else { row.x = action.p.X; row.y = action.p.Y; @@ -681,12 +560,15 @@ ActionRow actionRowFromRollbackAction(RollbackAction action) return row; } -std::list rollbackActionsFromActionRows(std::list rows) + + +const std::list RollbackManager::rollbackActionsFromActionRows( + const std::list & rows) { std::list actions; - std::list::const_iterator it; - for (it = rows.begin(); it != rows.end(); ++it) { + for (std::list::const_iterator it = rows.begin(); + it != rows.end(); ++it) { RollbackAction action; action.actor = (it->actor) ? getActorName(it->actor) : ""; action.unix_time = it->timestamp; @@ -694,16 +576,17 @@ std::list rollbackActionsFromActionRows(std::list row switch (action.type) { case RollbackAction::TYPE_MODIFY_INVENTORY_STACK: - action.inventory_location = it->location.c_str(); action.inventory_list = it->list; action.inventory_index = it->index; action.inventory_add = it->add; - action.inventory_stack = getStringFromStack(it->stack); + action.inventory_stack = it->stack; + if (action.inventory_stack.name.empty()) { + action.inventory_stack.name = getNodeName(it->stack.id); + } break; case RollbackAction::TYPE_SET_NODE: - action.p = v3s16(it->x, it->y, it->z); action.n_old.name = getNodeName(it->oldNode); action.n_old.param1 = it->oldParam1; @@ -716,7 +599,6 @@ std::list rollbackActionsFromActionRows(std::list row break; default: - throw ("W.T.F."); break; } @@ -727,202 +609,173 @@ std::list rollbackActionsFromActionRows(std::list row return actions; } -std::list SQL_getRowsSince(time_t firstTime, std::string actor = "") -{ - sqlite3_stmt *dbs_stmt = (!actor.length()) ? dbs_select : dbs_select_withActor; - sqlite3_reset(dbs_stmt); - sqlite3_bind_int64(dbs_stmt, 1, firstTime); - if (actor.length()) { - sqlite3_bind_int(dbs_stmt, 2, getActorId(actor)); +const std::list RollbackManager::getRowsSince(time_t firstTime, const std::string & actor) +{ + sqlite3_stmt *stmt_stmt = actor.empty() ? stmt_select : stmt_select_withActor; + sqlite3_bind_int64(stmt_stmt, 1, firstTime); + + if (!actor.empty()) { + sqlite3_bind_int(stmt_stmt, 2, getActorId(actor)); } - return actionRowsFromSelect(dbs_stmt); + const std::list & rows = actionRowsFromSelect(stmt_stmt); + sqlite3_reset(stmt_stmt); + + return rows; } -std::list SQL_getRowsSince_range(time_t firstTime, v3s16 p, int range, - int limit) + +const std::list RollbackManager::getRowsSince_range( + time_t start_time, v3s16 p, int range, int limit) { - sqlite3_stmt *stmt = dbs_select_range; - sqlite3_reset(stmt); - sqlite3_bind_int64(stmt, 1, firstTime); - sqlite3_bind_int(stmt, 2, (int) p.X); - sqlite3_bind_int(stmt, 3, range); - sqlite3_bind_int(stmt, 4, (int) p.Y); - sqlite3_bind_int(stmt, 5, range); - sqlite3_bind_int(stmt, 6, (int) p.Z); - sqlite3_bind_int(stmt, 7, range); - sqlite3_bind_int(stmt, 8, limit); + sqlite3_bind_int64(stmt_select_range, 1, start_time); + sqlite3_bind_int (stmt_select_range, 2, static_cast(p.X - range)); + sqlite3_bind_int (stmt_select_range, 3, static_cast(p.X + range)); + sqlite3_bind_int (stmt_select_range, 4, static_cast(p.Y - range)); + sqlite3_bind_int (stmt_select_range, 5, static_cast(p.Y + range)); + sqlite3_bind_int (stmt_select_range, 6, static_cast(p.Z - range)); + sqlite3_bind_int (stmt_select_range, 7, static_cast(p.Z + range)); + sqlite3_bind_int (stmt_select_range, 8, limit); - return actionRowsFromSelect(stmt); + const std::list & rows = actionRowsFromSelect(stmt_select_range); + sqlite3_reset(stmt_select_range); + + return rows; } -std::list SQL_getActionsSince_range(time_t firstTime, v3s16 p, int range, - int limit) -{ - std::list rows = SQL_getRowsSince_range(firstTime, p, range, limit); - return rollbackActionsFromActionRows(rows); +const std::list RollbackManager::getActionsSince_range( + time_t start_time, v3s16 p, int range, int limit) +{ + return rollbackActionsFromActionRows(getRowsSince_range(start_time, p, range, limit)); } -std::list SQL_getActionsSince(time_t firstTime, std::string actor = "") + +const std::list RollbackManager::getActionsSince( + time_t start_time, const std::string & actor) { - std::list rows = SQL_getRowsSince(firstTime, actor); - return rollbackActionsFromActionRows(rows); + return rollbackActionsFromActionRows(getRowsSince(start_time, actor)); } -void TXT_migrate(std::string filepath) -{ - std::cout << "Migrating from rollback.txt to rollback.sqlite" << std::endl; - SQL_databaseCheck(); - std::ifstream fh(filepath.c_str(), std::ios::in | std::ios::ate); +void RollbackManager::migrate(const std::string & file_path) +{ + std::cout << "Migrating from rollback.txt to rollback.sqlite." << std::endl; + + std::ifstream fh(file_path.c_str(), std::ios::in | std::ios::ate); if (!fh.good()) { - throw ("DIE"); + throw FileNotGoodException("Unable to open rollback.txt"); } - std::streampos filesize = fh.tellg(); + std::streampos file_size = fh.tellg(); - if (filesize > 10) { - fh.seekg(0); - - std::string bit; - int i = 0; - int id = 1; - time_t t = 0; - do { - ActionRow row; - - row.id = id; - - // Get the timestamp - std::getline(fh, bit, ' '); - bit = trim(bit); - if (!atoi(trim(bit).c_str())) { - std::getline(fh, bit); - continue; - } - row.timestamp = atoi(bit.c_str()); - - // Get the actor - row.actor = getActorId(trim(deSerializeJsonString(fh))); - - // Get the action type - std::getline(fh, bit, '['); - std::getline(fh, bit, ' '); - - if (bit == "modify_inventory_stack") { - row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK; - } - - if (bit == "set_node") { - row.type = RollbackAction::TYPE_SET_NODE; - } - - if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) { - row.location = trim(deSerializeJsonString(fh)); - std::getline(fh, bit, ' '); - row.list = trim(deSerializeJsonString(fh)); - std::getline(fh, bit, ' '); - std::getline(fh, bit, ' '); - row.index = atoi(trim(bit).c_str()); - std::getline(fh, bit, ' '); - row.add = (int)(trim(bit) == "add"); - row.stack = getStackFromString(trim(deSerializeJsonString(fh))); - std::getline(fh, bit); - } else if (row.type == RollbackAction::TYPE_SET_NODE) { - std::getline(fh, bit, '('); - std::getline(fh, bit, ','); - row.x = atoi(trim(bit).c_str()); - std::getline(fh, bit, ','); - row.y = atoi(trim(bit).c_str()); - std::getline(fh, bit, ')'); - row.z = atoi(trim(bit).c_str()); - std::getline(fh, bit, ' '); - row.oldNode = getNodeId(trim(deSerializeJsonString(fh))); - std::getline(fh, bit, ' '); - std::getline(fh, bit, ' '); - row.oldParam1 = atoi(trim(bit).c_str()); - std::getline(fh, bit, ' '); - row.oldParam2 = atoi(trim(bit).c_str()); - row.oldMeta = trim(deSerializeJsonString(fh)); - std::getline(fh, bit, ' '); - row.newNode = getNodeId(trim(deSerializeJsonString(fh))); - std::getline(fh, bit, ' '); - std::getline(fh, bit, ' '); - row.newParam1 = atoi(trim(bit).c_str()); - std::getline(fh, bit, ' '); - row.newParam2 = atoi(trim(bit).c_str()); - row.newMeta = trim(deSerializeJsonString(fh)); - std::getline(fh, bit, ' '); - std::getline(fh, bit, ' '); - std::getline(fh, bit); - row.guessed = (int)(trim(bit) == "actor_is_guess"); - } - - /* - std::cout << "==========READ===========" << std::endl; - std::cout << "time: " << row.timestamp << std::endl; - std::cout << "actor: " << row.actor << std::endl; - std::cout << "type: " << row.type << std::endl; - if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) - { - std::cout << "Location: " << row.location << std::endl; - std::cout << "List: " << row.list << std::endl; - std::cout << "Index: " << row.index << std::endl; - std::cout << "Add: " << row.add << std::endl; - std::cout << "Stack: " << row.stack << std::endl; - } - if (row.type == RollbackAction::TYPE_SET_NODE) - { - std::cout << "x: " << row.x << std::endl; - std::cout << "y: " << row.y << std::endl; - std::cout << "z: " << row.z << std::endl; - std::cout << "oldNode: " << row.oldNode << std::endl; - std::cout << "oldParam1: " << row.oldParam1 << std::endl; - std::cout << "oldParam2: " << row.oldParam2 << std::endl; - std::cout << "oldMeta: " << row.oldMeta << std::endl; - std::cout << "newNode: " << row.newNode << std::endl; - std::cout << "newParam1: " << row.newParam1 << std::endl; - std::cout << "newParam2: " << row.newParam2 << std::endl; - std::cout << "newMeta: " << row.newMeta << std::endl; - std::cout << "guessed: " << row.guessed << std::endl; - } - */ - - if (i == 0) { - t = time(0); - sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL); - } - - SQL_registerRow(row); - ++i; - - if (time(0) - t) { - sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL); - t = time(0) - t; - std::cout - << " Done: " << (int)(((float) fh.tellg() / (float) filesize) * 100) << "%" - << " Speed: " << i / t << "/second \r" << std::flush; - i = 0; - } - - ++id; - } while (!fh.eof() && fh.good()); - } else { - errorstream << "Empty rollback log" << std::endl; + if (file_size > 10) { + errorstream << "Empty rollback log." << std::endl; + return; } + fh.seekg(0); + + std::string bit; + int i = 0; + int id = 1; + time_t start = time(0); + time_t t = start; + sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); + do { + ActionRow row; + + row.id = id; + + // Get the timestamp + std::getline(fh, bit, ' '); + bit = trim(bit); + if (!atoi(bit.c_str())) { + std::getline(fh, bit); + continue; + } + row.timestamp = atoi(bit.c_str()); + + // Get the actor + row.actor = getActorId(deSerializeJsonString(fh)); + + // Get the action type + std::getline(fh, bit, '['); + std::getline(fh, bit, ' '); + + if (bit == "modify_inventory_stack") { + row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK; + row.location = trim(deSerializeJsonString(fh)); + std::getline(fh, bit, ' '); + row.list = trim(deSerializeJsonString(fh)); + std::getline(fh, bit, ' '); + std::getline(fh, bit, ' '); + row.index = atoi(trim(bit).c_str()); + std::getline(fh, bit, ' '); + row.add = (int)(trim(bit) == "add"); + row.stack.deSerialize(deSerializeJsonString(fh)); + row.stack.id = getNodeId(row.stack.name); + std::getline(fh, bit); + } else if (bit == "set_node") { + row.type = RollbackAction::TYPE_SET_NODE; + std::getline(fh, bit, '('); + std::getline(fh, bit, ','); + row.x = atoi(trim(bit).c_str()); + std::getline(fh, bit, ','); + row.y = atoi(trim(bit).c_str()); + std::getline(fh, bit, ')'); + row.z = atoi(trim(bit).c_str()); + std::getline(fh, bit, ' '); + row.oldNode = getNodeId(trim(deSerializeJsonString(fh))); + std::getline(fh, bit, ' '); + std::getline(fh, bit, ' '); + row.oldParam1 = atoi(trim(bit).c_str()); + std::getline(fh, bit, ' '); + row.oldParam2 = atoi(trim(bit).c_str()); + row.oldMeta = trim(deSerializeJsonString(fh)); + std::getline(fh, bit, ' '); + row.newNode = getNodeId(trim(deSerializeJsonString(fh))); + std::getline(fh, bit, ' '); + std::getline(fh, bit, ' '); + row.newParam1 = atoi(trim(bit).c_str()); + std::getline(fh, bit, ' '); + row.newParam2 = atoi(trim(bit).c_str()); + row.newMeta = trim(deSerializeJsonString(fh)); + std::getline(fh, bit, ' '); + std::getline(fh, bit, ' '); + std::getline(fh, bit); + row.guessed = (int)(trim(bit) == "actor_is_guess"); + } else { + errorstream << "Unrecognized rollback action type \"" + << bit << "\"!" << std::endl; + continue; + } + + registerRow(row); + ++i; + + if (time(0) - t >= 1) { + sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); + t = time(0); + std::cout + << " Done: " << static_cast((static_cast(fh.tellg()) / static_cast(file_size)) * 100) << "%" + << " Speed: " << i / (t - start) << "/second \r" << std::flush; + sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); + } + } while (fh.good()); + std::cout - << " Done: 100% " << std::endl - << " Now you can delete the old rollback.txt file." << std::endl; + << " Done: 100% " << std::endl + << "Now you can delete the old rollback.txt file." << std::endl; } + // Get nearness factor for subject's action for this action // Return value: 0 = impossible, >0 = factor -static float getSuspectNearness(bool is_guess, v3s16 suspect_p, time_t suspect_t, - v3s16 action_p, time_t action_t) +float RollbackManager::getSuspectNearness(bool is_guess, v3s16 suspect_p, + time_t suspect_t, v3s16 action_p, time_t action_t) { // Suspect cannot cause things in the past if (action_t < suspect_t) { @@ -944,245 +797,154 @@ static float getSuspectNearness(bool is_guess, v3s16 suspect_p, time_t suspect_t } return f; } -class RollbackManager: public IRollbackManager + + +void RollbackManager::reportAction(const RollbackAction &action_) { -public: - // IRollbackManager interface - void reportAction(const RollbackAction &action_) { - // Ignore if not important - if (!action_.isImportant(m_gamedef)) { + // Ignore if not important + if (!action_.isImportant(gamedef)) { + return; + } + + RollbackAction action = action_; + action.unix_time = time(0); + + // Figure out actor + action.actor = current_actor; + action.actor_is_guess = current_actor_is_guess; + + if (action.actor.empty()) { // If actor is not known, find out suspect or cancel + v3s16 p; + if (!action.getPosition(&p)) { return; } - RollbackAction action = action_; - action.unix_time = time(0); - - // Figure out actor - action.actor = m_current_actor; - action.actor_is_guess = m_current_actor_is_guess; - - if (action.actor.empty()) { // If actor is not known, find out suspect or cancel - v3s16 p; - if (!action.getPosition(&p)) { - return; - } - - action.actor = getSuspect(p, 83, 1); - if (action.actor.empty()) { - return; - } - - action.actor_is_guess = true; + action.actor = getSuspect(p, 83, 1); + if (action.actor.empty()) { + return; } - infostream - << "RollbackManager::reportAction():" - << " time=" << action.unix_time - << " actor=\"" << action.actor << "\"" - << (action.actor_is_guess ? " (guess)" : "") - << " action=" << action.toString() - << std::endl; - addAction(action); + action.actor_is_guess = true; } - std::string getActor() { - return m_current_actor; - } - - bool isActorGuess() { - return m_current_actor_is_guess; - } - - void setActor(const std::string &actor, bool is_guess) { - m_current_actor = actor; - m_current_actor_is_guess = is_guess; - } - - std::string getSuspect(v3s16 p, float nearness_shortcut, float min_nearness) { - if (m_current_actor != "") { - return m_current_actor; - } - int cur_time = time(0); - time_t first_time = cur_time - (100 - min_nearness); - RollbackAction likely_suspect; - float likely_suspect_nearness = 0; - for (std::list::const_reverse_iterator - i = m_action_latest_buffer.rbegin(); - i != m_action_latest_buffer.rend(); i++) { - if (i->unix_time < first_time) { - break; - } - if (i->actor == "") { - continue; - } - // Find position of suspect or continue - v3s16 suspect_p; - if (!i->getPosition(&suspect_p)) { - continue; - } - float f = getSuspectNearness(i->actor_is_guess, suspect_p, - i->unix_time, p, cur_time); - if (f >= min_nearness && f > likely_suspect_nearness) { - likely_suspect_nearness = f; - likely_suspect = *i; - if (likely_suspect_nearness >= nearness_shortcut) { - break; - } - } - } - // No likely suspect was found - if (likely_suspect_nearness == 0) { - return ""; - } - // Likely suspect was found - return likely_suspect.actor; - } - - void flush() { - infostream << "RollbackManager::flush()" << std::endl; - - sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL); - - std::list::const_iterator iter; - - for (iter = m_action_todisk_buffer.begin(); - iter != m_action_todisk_buffer.end(); - iter++) { - if (iter->actor == "") { - continue; - } - - SQL_registerRow(actionRowFromRollbackAction(*iter)); - } - - sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL); - m_action_todisk_buffer.clear(); - } - - RollbackManager(const std::string &filepath, IGameDef *gamedef): - m_filepath(filepath), - m_gamedef(gamedef), - m_current_actor_is_guess(false) { - infostream - << "RollbackManager::RollbackManager(" << filepath << ")" - << std::endl; - - // Operate correctly in case of still being given rollback.txt as filepath - std::string directory = filepath.substr(0, filepath.rfind(DIR_DELIM) + 1); - std::string filenameOld = filepath.substr(filepath.rfind(DIR_DELIM) + 1); - std::string filenameNew = (filenameOld == "rollback.txt") ? "rollback.sqlite" : - filenameOld; - std::string filenameTXT = directory + "rollback.txt"; - std::string migratingFlag = filepath + ".migrating"; - - infostream << "Directory: " << directory << std::endl; - infostream << "CheckFor: " << filenameTXT << std::endl; - infostream << "FileOld: " << filenameOld << std::endl; - infostream << "FileNew: " << filenameNew << std::endl; - - dbp = directory + filenameNew; - - if ((fs::PathExists(filenameTXT) && fs::PathExists(migratingFlag)) || - (fs::PathExists(filenameTXT) && !fs::PathExists(dbp))) { - std::ofstream of(migratingFlag.c_str()); - TXT_migrate(filenameTXT); - fs::DeleteSingleFileOrEmptyDirectory(migratingFlag); - } - - SQL_databaseCheck(); - } -#define FINALIZE_STATEMENT(statement) \ - if ( statement ) \ - rc = sqlite3_finalize(statement); \ - statement = NULL; \ - if ( rc != SQLITE_OK ) \ - errorstream << "RollbackManager::~RollbackManager():" \ - << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl; - - ~RollbackManager() { - infostream << "RollbackManager::~RollbackManager()" << std::endl; - flush(); - - int rc = SQLITE_OK; - - FINALIZE_STATEMENT(dbs_insert) - FINALIZE_STATEMENT(dbs_replace) - FINALIZE_STATEMENT(dbs_select) - FINALIZE_STATEMENT(dbs_select_range) - FINALIZE_STATEMENT(dbs_select_withActor) - FINALIZE_STATEMENT(dbs_knownActor_select) - FINALIZE_STATEMENT(dbs_knownActor_insert) - FINALIZE_STATEMENT(dbs_knownNode_select) - FINALIZE_STATEMENT(dbs_knownNode_insert) - - if(dbh) - rc = sqlite3_close(dbh); - - dbh = NULL; - - if (rc != SQLITE_OK) { - errorstream << "RollbackManager::~RollbackManager(): " - << "Failed to close database: rc=" << rc << std::endl; - } - } - - void addAction(const RollbackAction &action) { - m_action_todisk_buffer.push_back(action); - m_action_latest_buffer.push_back(action); - - // Flush to disk sometimes - if (m_action_todisk_buffer.size() >= 500) { - flush(); - } - } - - std::list getEntriesSince(time_t first_time) { - infostream - << "RollbackManager::getEntriesSince(" << first_time << ")" - << std::endl; - - flush(); - - std::list result = SQL_getActionsSince(first_time); - - return result; - } - - std::list getNodeActors(v3s16 pos, int range, time_t seconds, int limit) { - time_t cur_time = time(0); - time_t first_time = cur_time - seconds; - - return SQL_getActionsSince_range(first_time, pos, range, limit); - } - - std::list getRevertActions(const std::string &actor_filter, - time_t seconds) { - infostream - << "RollbackManager::getRevertActions(" << actor_filter - << ", " << seconds << ")" - << std::endl; - - // Figure out time - time_t cur_time = time(0); - time_t first_time = cur_time - seconds; - - flush(); - - std::list result = SQL_getActionsSince(first_time, actor_filter); - - return result; - } -private: - std::string m_filepath; - IGameDef *m_gamedef; - std::string m_current_actor; - bool m_current_actor_is_guess; - std::list m_action_todisk_buffer; - std::list m_action_latest_buffer; -}; - -IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef) -{ - return new RollbackManager(filepath, gamedef); + addAction(action); +} + +std::string RollbackManager::getActor() +{ + return current_actor; +} + +bool RollbackManager::isActorGuess() +{ + return current_actor_is_guess; +} + +void RollbackManager::setActor(const std::string & actor, bool is_guess) +{ + current_actor = actor; + current_actor_is_guess = is_guess; +} + +std::string RollbackManager::getSuspect(v3s16 p, float nearness_shortcut, + float min_nearness) +{ + if (current_actor != "") { + return current_actor; + } + int cur_time = time(0); + time_t first_time = cur_time - (100 - min_nearness); + RollbackAction likely_suspect; + float likely_suspect_nearness = 0; + for (std::list::const_reverse_iterator + i = action_latest_buffer.rbegin(); + i != action_latest_buffer.rend(); i++) { + if (i->unix_time < first_time) { + break; + } + if (i->actor == "") { + continue; + } + // Find position of suspect or continue + v3s16 suspect_p; + if (!i->getPosition(&suspect_p)) { + continue; + } + float f = getSuspectNearness(i->actor_is_guess, suspect_p, + i->unix_time, p, cur_time); + if (f >= min_nearness && f > likely_suspect_nearness) { + likely_suspect_nearness = f; + likely_suspect = *i; + if (likely_suspect_nearness >= nearness_shortcut) { + break; + } + } + } + // No likely suspect was found + if (likely_suspect_nearness == 0) { + return ""; + } + // Likely suspect was found + return likely_suspect.actor; +} + + +void RollbackManager::flush() +{ + sqlite3_exec(db, "BEGIN", NULL, NULL, NULL); + + std::list::const_iterator iter; + + for (iter = action_todisk_buffer.begin(); + iter != action_todisk_buffer.end(); + iter++) { + if (iter->actor == "") { + continue; + } + + registerRow(actionRowFromRollbackAction(*iter)); + } + + sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); + action_todisk_buffer.clear(); +} + + +void RollbackManager::addAction(const RollbackAction & action) +{ + action_todisk_buffer.push_back(action); + action_latest_buffer.push_back(action); + + // Flush to disk sometimes + if (action_todisk_buffer.size() >= 500) { + flush(); + } +} + +std::list RollbackManager::getEntriesSince(time_t first_time) +{ + flush(); + return getActionsSince(first_time); +} + +std::list RollbackManager::getNodeActors(v3s16 pos, int range, + time_t seconds, int limit) +{ + time_t cur_time = time(0); + time_t first_time = cur_time - seconds; + + return getActionsSince_range(first_time, pos, range, limit); +} + +std::list RollbackManager::getRevertActions( + const std::string &actor_filter, + time_t seconds) +{ + time_t cur_time = time(0); + time_t first_time = cur_time - seconds; + + flush(); + + return getActionsSince(first_time, actor_filter); } diff --git a/src/rollback.h b/src/rollback.h index eea7c59f2..2e6955c8d 100644 --- a/src/rollback.h +++ b/src/rollback.h @@ -24,31 +24,84 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irr_v3d.h" #include "rollback_interface.h" #include +#include +#include "sqlite3.h" class IGameDef; -class IRollbackManager: public IRollbackReportSink +class ActionRow; +class Entity; + +class RollbackManager: public IRollbackManager { public: - // IRollbackReportManager - virtual void reportAction(const RollbackAction &action) = 0; - virtual std::string getActor() = 0; - virtual bool isActorGuess() = 0; - virtual void setActor(const std::string &actor, bool is_guess) = 0; - virtual std::string getSuspect(v3s16 p, float nearness_shortcut, - float min_nearness) = 0; + RollbackManager(const std::string & world_path, IGameDef * gamedef); + ~RollbackManager(); - virtual ~IRollbackManager() {} - virtual void flush() = 0; - // Get all actors that did something to position p, but not further than - // in history - virtual std::list getNodeActors(v3s16 pos, int range, - time_t seconds, int limit) = 0; - // Get actions to revert of history made by - virtual std::list getRevertActions(const std::string &actor, - time_t seconds) = 0; + void reportAction(const RollbackAction & action_); + std::string getActor(); + bool isActorGuess(); + void setActor(const std::string & actor, bool is_guess); + std::string getSuspect(v3s16 p, float nearness_shortcut, + float min_nearness); + void flush(); + + void addAction(const RollbackAction & action); + std::list getEntriesSince(time_t first_time); + std::list getNodeActors(v3s16 pos, int range, + time_t seconds, int limit); + std::list getRevertActions( + const std::string & actor_filter, time_t seconds); + +private: + void registerNewActor(const int id, const std::string & name); + void registerNewNode(const int id, const std::string & name); + int getActorId(const std::string & name); + int getNodeId(const std::string & name); + const char * getActorName(const int id); + const char * getNodeName(const int id); + bool createTables(); + void initDatabase(); + bool registerRow(const ActionRow & row); + const std::list actionRowsFromSelect(sqlite3_stmt * stmt); + ActionRow actionRowFromRollbackAction(const RollbackAction & action); + const std::list rollbackActionsFromActionRows( + const std::list & rows); + const std::list getRowsSince(time_t firstTime, + const std::string & actor); + const std::list getRowsSince_range(time_t firstTime, v3s16 p, + int range, int limit); + const std::list getActionsSince_range(time_t firstTime, v3s16 p, + int range, int limit); + const std::list getActionsSince(time_t firstTime, + const std::string & actor = ""); + void migrate(const std::string & filepath); + static float getSuspectNearness(bool is_guess, v3s16 suspect_p, + time_t suspect_t, v3s16 action_p, time_t action_t); + + + IGameDef * gamedef; + + std::string current_actor; + bool current_actor_is_guess; + + std::list action_todisk_buffer; + std::list action_latest_buffer; + + std::string database_path; + sqlite3 * db; + sqlite3_stmt * stmt_insert; + sqlite3_stmt * stmt_replace; + sqlite3_stmt * stmt_select; + sqlite3_stmt * stmt_select_range; + sqlite3_stmt * stmt_select_withActor; + sqlite3_stmt * stmt_knownActor_select; + sqlite3_stmt * stmt_knownActor_insert; + sqlite3_stmt * stmt_knownNode_select; + sqlite3_stmt * stmt_knownNode_insert; + + std::vector knownActors; + std::vector knownNodes; }; -IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef); - #endif diff --git a/src/rollback_interface.cpp b/src/rollback_interface.cpp index 808b07fed..c35ad5781 100644 --- a/src/rollback_interface.cpp +++ b/src/rollback_interface.cpp @@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" + RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) { INodeDefManager *ndef = gamedef->ndef(); @@ -42,275 +43,89 @@ RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) param1 = n.param1; param2 = n.param2; NodeMetadata *metap = map->getNodeMetadata(p); - if(metap){ + if (metap) { std::ostringstream os(std::ios::binary); metap->serialize(os); meta = os.str(); } } + std::string RollbackAction::toString() const { - switch(type){ - case TYPE_SET_NODE: { - std::ostringstream os(std::ios::binary); - os<<"[set_node"; - os<<" "; - os<<"("< (" << serializeJsonString(n_new.name); + os << ", " << itos(n_new.param1); + os << ", " << itos(n_new.param2); + os << ", " << serializeJsonString(n_new.meta); + os << ')'; + case TYPE_MODIFY_INVENTORY_STACK: + os << "modify_inventory_stack ("; + os << serializeJsonString(inventory_location); + os << ", " << serializeJsonString(inventory_list); + os << ", " << inventory_index; + os << ", " << (inventory_add ? "add" : "remove"); + os << ", " << serializeJsonString(inventory_stack.getItemString()); + os << ')'; default: - return "none"; + return ""; } + return os.str(); } -void RollbackAction::fromStream(std::istream &is) throw(SerializationError) -{ - int c = is.get(); - if(c != '['){ - is.putback(c); - throw SerializationError("RollbackAction: starting [ not found"); - } - - std::string id; - std::getline(is, id, ' '); - - if(id == "set_node") - { - c = is.get(); - if(c != '('){ - is.putback(c); - throw SerializationError("RollbackAction: starting ( not found"); - } - // Position - std::string px_raw; - std::string py_raw; - std::string pz_raw; - std::getline(is, px_raw, ','); - std::getline(is, py_raw, ','); - std::getline(is, pz_raw, ')'); - c = is.get(); - if(c != ' '){ - is.putback(c); - throw SerializationError("RollbackAction: after-p ' ' not found"); - } - v3s16 loaded_p(stoi(px_raw), stoi(py_raw), stoi(pz_raw)); - // Old node - std::string old_name; - try{ - old_name = deSerializeJsonString(is); - }catch(SerializationError &e){ - errorstream<<"Serialization error in RollbackAction::fromStream(): " - <<"old_name: "<ndef(); - // Both are of the same name, so a single definition is needed - const ContentFeatures &def = ndef->get(n_old.name); - // If the type is flowing liquid, action is not important - if(def.liquid_type == LIQUID_FLOWING) - return false; - // Otherwise action is important - return true; } - default: + if (type != TYPE_SET_NODE) return true; - } + // If names differ, action is always important + if(n_old.name != n_new.name) + return true; + // If metadata differs, action is always important + if(n_old.meta != n_new.meta) + return true; + INodeDefManager *ndef = gamedef->ndef(); + // Both are of the same name, so a single definition is needed + const ContentFeatures &def = ndef->get(n_old.name); + // If the type is flowing liquid, action is not important + if (def.liquid_type == LIQUID_FLOWING) + return false; + // Otherwise action is important + return true; } + bool RollbackAction::getPosition(v3s16 *dst) const { - switch(type){ - case RollbackAction::TYPE_SET_NODE: - if(dst) *dst = p; + switch (type) { + case TYPE_SET_NODE: + if (dst) *dst = p; return true; - case RollbackAction::TYPE_MODIFY_INVENTORY_STACK: { + case TYPE_MODIFY_INVENTORY_STACK: { InventoryLocation loc; loc.deSerialize(inventory_location); - if(loc.type != InventoryLocation::NODEMETA) + if (loc.type != InventoryLocation::NODEMETA) { return false; - if(dst) *dst = loc.p; + } + if (dst) *dst = loc.p; return true; } default: return false; } } + bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const { - try{ - switch(type){ + try { + switch (type) { case TYPE_NOTHING: return true; case TYPE_SET_NODE: { @@ -321,40 +136,39 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam MapNode current_node = map->getNodeNoEx(p); std::string current_name = ndef->get(current_node).name; // If current node not the new node, it's bad - if(current_name != n_new.name) + if (current_name != n_new.name) { return false; - /*// If current node not the new node and not ignore, it's bad - if(current_name != n_new.name && current_name != "ignore") - return false;*/ + } // Create rollback node MapNode n(ndef, n_old.name, n_old.param1, n_old.param2); // Set rollback node - try{ - if(!map->addNodeWithEvent(p, n)){ - infostream<<"RollbackAction::applyRevert(): " - <<"AddNodeWithEvent failed at " - <addNodeWithEvent(p, n)) { + infostream << "RollbackAction::applyRevert(): " + << "AddNodeWithEvent failed at " + << PP(p) << " for " << n_old.name + << std::endl; return false; } - NodeMetadata *meta = map->getNodeMetadata(p); - if(n_old.meta != ""){ - if(!meta){ + if (n_old.meta.empty()) { + map->removeNodeMetadata(p); + } else { + NodeMetadata *meta = map->getNodeMetadata(p); + if (!meta) { meta = new NodeMetadata(gamedef); - if(!map->setNodeMetadata(p, meta)){ + if (!map->setNodeMetadata(p, meta)) { delete meta; - infostream<<"RollbackAction::applyRevert(): " - <<"setNodeMetadata failed at " - <deSerialize(is); - } else { - map->removeNodeMetadata(p); } - // NOTE: This same code is in scriptapi.cpp - // Inform other things that the metadata has changed + // Inform other things that the meta data has changed v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE); MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; @@ -362,12 +176,14 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam map->dispatchEvent(&event); // Set the block to be saved MapBlock *block = map->getBlockNoCreateNoEx(blockpos); - if(block) + if (block) { block->raiseModified(MOD_STATE_WRITE_NEEDED, - "NodeMetaRef::reportMetadataChange"); - }catch(InvalidPositionException &e){ - infostream<<"RollbackAction::applyRevert(): " - <<"InvalidPositionException: "<idef()); + std::string real_name = gamedef->idef()->getAlias(inventory_stack.name); Inventory *inv = imgr->getInventory(loc); - if(!inv){ - infostream<<"RollbackAction::applyRevert(): Could not get " - "inventory at "<getList(inventory_list); - if(!list){ - infostream<<"RollbackAction::applyRevert(): Could not get " - "inventory list \""<getSize() <= inventory_index){ - infostream<<"RollbackAction::applyRevert(): List index " - <getSize() <= inventory_index) { + infostream << "RollbackAction::applyRevert(): List index " + << inventory_index << " too large in " + << "inventory list \"" << inventory_list << "\" in " + << inventory_location << std::endl; } // If item was added, take away item, otherwise add removed item - if(inventory_add){ + if (inventory_add) { // Silently ignore different current item - if(list->getItem(inventory_index).name != stack.name) + if (list->getItem(inventory_index).name != real_name) return false; - list->takeItem(inventory_index, stack.count); + list->takeItem(inventory_index, inventory_stack.count); } else { - list->addItem(inventory_index, stack); + list->addItem(inventory_index, inventory_stack); } // Inventory was modified; send to clients imgr->setInventoryModified(loc); return true; } default: - errorstream<<"RollbackAction::applyRevert(): type not handled" - < #include +#include #include "exceptions.h" +#include "inventory.h" class Map; class IGameDef; @@ -37,15 +39,12 @@ struct RollbackNode int param2; std::string meta; - bool operator==(const RollbackNode &other) + bool operator == (const RollbackNode &other) { return (name == other.name && param1 == other.param1 && param2 == other.param2 && meta == other.meta); } - bool operator!=(const RollbackNode &other) - { - return !(*this == other); - } + bool operator != (const RollbackNode &other) { return !(*this == other); } RollbackNode(): param1(0), @@ -55,6 +54,7 @@ struct RollbackNode RollbackNode(Map *map, v3s16 p, IGameDef *gamedef); }; + struct RollbackAction { enum Type{ @@ -75,7 +75,7 @@ struct RollbackAction std::string inventory_list; u32 inventory_index; bool inventory_add; - std::string inventory_stack; + ItemStack inventory_stack; RollbackAction(): type(TYPE_NOTHING), @@ -94,7 +94,7 @@ struct RollbackAction void setModifyInventoryStack(const std::string &inventory_location_, const std::string &inventory_list_, int index_, - bool add_, const std::string &inventory_stack_) + bool add_, const ItemStack &inventory_stack_) { type = TYPE_MODIFY_INVENTORY_STACK; inventory_location = inventory_location_; @@ -106,7 +106,6 @@ struct RollbackAction // String should not contain newlines or nulls std::string toString() const; - void fromStream(std::istream &is) throw(SerializationError); // Eg. flowing water level changes are not important bool isImportant(IGameDef *gamedef) const; @@ -116,41 +115,53 @@ struct RollbackAction bool applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const; }; -class IRollbackReportSink + +class IRollbackManager { public: - virtual ~IRollbackReportSink(){} virtual void reportAction(const RollbackAction &action) = 0; virtual std::string getActor() = 0; virtual bool isActorGuess() = 0; virtual void setActor(const std::string &actor, bool is_guess) = 0; virtual std::string getSuspect(v3s16 p, float nearness_shortcut, - float min_nearness) = 0; + float min_nearness) = 0; + + virtual ~IRollbackManager() {}; + virtual void flush() = 0; + // Get all actors that did something to position p, but not further than + // in history + virtual std::list getNodeActors(v3s16 pos, int range, + time_t seconds, int limit) = 0; + // Get actions to revert of history made by + virtual std::list getRevertActions(const std::string &actor, + time_t seconds) = 0; }; + class RollbackScopeActor { public: - RollbackScopeActor(IRollbackReportSink *sink, const std::string &actor, - bool is_guess=false): - m_sink(sink) + RollbackScopeActor(IRollbackManager * rollback_, + const std::string & actor, bool is_guess = false) : + rollback(rollback_) { - if(m_sink){ - m_actor_was = m_sink->getActor(); - m_actor_was_guess = m_sink->isActorGuess(); - m_sink->setActor(actor, is_guess); + if (rollback) { + old_actor = rollback->getActor(); + old_actor_guess = rollback->isActorGuess(); + rollback->setActor(actor, is_guess); } } ~RollbackScopeActor() { - if(m_sink){ - m_sink->setActor(m_actor_was, m_actor_was_guess); + if (rollback) { + rollback->setActor(old_actor, old_actor_guess); } } + private: - IRollbackReportSink *m_sink; - std::string m_actor_was; - bool m_actor_was_guess; + IRollbackManager * rollback; + std::string old_actor; + bool old_actor_guess; }; #endif diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp index b3685c8d1..f55a72d1b 100644 --- a/src/script/lua_api/l_rollback.cpp +++ b/src/script/lua_api/l_rollback.cpp @@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "common/c_converter.h" #include "server.h" -#include "rollback.h" +#include "rollback_interface.h" void push_RollbackNode(lua_State *L, RollbackNode &node) diff --git a/src/server.cpp b/src/server.cpp index d1f3997e9..857114d0f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -181,7 +181,6 @@ Server::Server( this), m_banmanager(NULL), m_rollback(NULL), - m_rollback_sink_enabled(true), m_enable_rollback_recording(false), m_emerge(NULL), m_script(NULL), @@ -241,12 +240,11 @@ Server::Server( throw ServerError("Failed to initialize world"); // Create ban manager - std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt"; + std::string ban_path = m_path_world + DIR_DELIM "ipban.txt"; m_banmanager = new BanManager(ban_path); // Create rollback manager - std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt"; - m_rollback = createRollbackManager(rollback_path, this); + m_rollback = new RollbackManager(m_path_world, this); ModConfiguration modconf(m_path_world); m_mods = modconf.getMods(); @@ -4834,8 +4832,6 @@ bool Server::rollbackRevertActions(const std::list &actions, { infostream<<"Server::rollbackRevertActions(len="<getMap()); - // Disable rollback report sink while reverting - BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false); // Fail if no actions to handle if(actions.empty()){ @@ -4915,14 +4911,6 @@ MtEventManager* Server::getEventManager() { return m_event; } -IRollbackReportSink* Server::getRollbackReportSink() -{ - if(!m_enable_rollback_recording) - return NULL; - if(!m_rollback_sink_enabled) - return NULL; - return m_rollback; -} IWritableItemDefManager* Server::getWritableItemDefManager() { diff --git a/src/server.h b/src/server.h index 7233ffbe8..4292892c5 100644 --- a/src/server.h +++ b/src/server.h @@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mods.h" #include "inventorymanager.h" #include "subgame.h" -#include "rollback_interface.h" // Needed for rollbackRevertActions() #include "util/numeric.h" #include "util/thread.h" #include "environment.h" @@ -50,6 +49,7 @@ class Inventory; class Player; class PlayerSAO; class IRollbackManager; +class RollbackAction; class EmergeManager; class GameScripting; class ServerEnvironment; @@ -268,9 +268,6 @@ public: // Envlock and conlock should be locked when using scriptapi GameScripting *getScriptIface(){ return m_script; } - // Envlock should be locked when using the rollback manager - IRollbackManager *getRollbackManager(){ return m_rollback; } - //TODO: determine what (if anything) should be locked to access EmergeManager EmergeManager *getEmergeManager(){ return m_emerge; } @@ -289,9 +286,10 @@ public: virtual u16 allocateUnknownNodeId(const std::string &name); virtual ISoundManager* getSoundManager(); virtual MtEventManager* getEventManager(); - virtual IRollbackReportSink* getRollbackReportSink(); virtual scene::ISceneManager* getSceneManager(); - + virtual IRollbackManager *getRollbackManager() { return m_rollback; } + + IWritableItemDefManager* getWritableItemDefManager(); IWritableNodeDefManager* getWritableNodeDefManager(); IWritableCraftDefManager* getWritableCraftDefManager(); @@ -488,7 +486,6 @@ private: // Rollback manager (behind m_env_mutex) IRollbackManager *m_rollback; - bool m_rollback_sink_enabled; bool m_enable_rollback_recording; // Updated once in a while // Emerge manager