+
+ 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 `actionIndex` ON `action`(`x`,`y`,`z`,`timestamp`,`actor`);\n",
+ NULL, NULL, NULL));
+ verbosestream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
+
+ return true;
+}
+
+
+bool RollbackManager::initDatabase()
+{
+ verbosestream << "RollbackManager: Database connection setup" << std::endl;
+
+ bool needs_create = !fs::PathExists(database_path);
+ SQLOK(sqlite3_open_v2(database_path.c_str(), &db,
+ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL));
+
+ if (needs_create) {
+ createTables();
+ }
+
+ 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, &stmt_insert, NULL));
+
+ 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, &stmt_replace, NULL));
+
+ 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, &stmt_select, NULL));
+
+ 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));
+
+ 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));
+
+ SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `actor`",
+ -1, &stmt_knownActor_select, NULL));
+
+ SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `actor` (`name`) VALUES (?)",
+ -1, &stmt_knownActor_insert, NULL));
+
+ SQLOK(sqlite3_prepare_v2(db, "SELECT `id`, `name` FROM `node`",
+ -1, &stmt_knownNode_select, NULL));
+
+ SQLOK(sqlite3_prepare_v2(db, "INSERT INTO `node` (`name`) VALUES (?)",
+ -1, &stmt_knownNode_insert, NULL));
+
+ verbosestream << "SQL prepared statements setup correctly" << std::endl;
+
+ while (sqlite3_step(stmt_knownActor_select) == SQLITE_ROW) {
+ registerNewActor(
+ sqlite3_column_int(stmt_knownActor_select, 0),
+ reinterpret_cast<const char *>(sqlite3_column_text(stmt_knownActor_select, 1))
+ );
+ }
+ SQLOK(sqlite3_reset(stmt_knownActor_select));
+
+ while (sqlite3_step(stmt_knownNode_select) == SQLITE_ROW) {
+ registerNewNode(
+ sqlite3_column_int(stmt_knownNode_select, 0),
+ reinterpret_cast<const char *>(sqlite3_column_text(stmt_knownNode_select, 1))
+ );
+ }
+ SQLOK(sqlite3_reset(stmt_knownNode_select));
+
+ return needs_create;
+}
+
+
+bool RollbackManager::registerRow(const ActionRow & row)
+{
+ sqlite3_stmt * stmt_do = (row.id) ? stmt_replace : stmt_insert;
+
+ bool nodeMeta = false;
+
+ 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) {
+ const std::string & loc = row.location;
+ nodeMeta = (loc.substr(0, 9) == "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::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())));