Rollback fixes and get_node_actions
authorShadowNinja <shadowninja@minetest.net>
Tue, 12 Nov 2013 21:13:00 +0000 (16:13 -0500)
committerShadowNinja <shadowninja@minetest.net>
Wed, 27 Nov 2013 23:13:06 +0000 (18:13 -0500)
builtin/chatcommands.lua
builtin/deprecated.lua
doc/lua_api.txt
src/rollback.cpp
src/rollback.h
src/rollback_interface.h
src/script/lua_api/l_rollback.cpp
src/script/lua_api/l_rollback.h

index 831d3a7b41e7d139787c9710500aec9d75e8033e..b9661a8889c2a76a8b6005d9269d3c9bb2cbabcc 100644 (file)
@@ -515,34 +515,45 @@ minetest.register_on_punchnode(function(pos, node, puncher)
 end)
 
 minetest.register_chatcommand("rollback_check", {
-       params = "[<range>] [<seconds>]",
+       params = "[<range>] [<seconds>] [limit]",
        description = "check who has last touched a node or near it, "..
-                       "max. <seconds> ago (default range=0, seconds=86400=24h)",
+                       "max. <seconds> ago (default range=0, seconds=86400=24h, limit=5)",
        privs = {rollback=true},
        func = function(name, param)
-               local range, seconds = string.match(param, "(%d+) *(%d*)")
+               local range, seconds, limit =
+                       param:match("(%d+) *(%d*) *(%d*)")
                range = tonumber(range) or 0
                seconds = tonumber(seconds) or 86400
-               minetest.chat_send_player(name, "Punch a node (limits set: range="..
-                               dump(range).." seconds="..dump(seconds).."s)")
+               limit = tonumber(limit) or 5
+               if limit > 100 then
+                       minetest.chat_send_player(name, "That limit is too high!")
+                       return
+               end
+               minetest.chat_send_player(name, "Punch a node (range="..
+                               range..", seconds="..seconds.."s, limit="..limit..")")
+
                minetest.rollback_punch_callbacks[name] = function(pos, node, puncher)
                        local name = puncher:get_player_name()
-                       minetest.chat_send_player(name, "Checking...")
-                       local actor, act_p, act_seconds =
-                                       minetest.rollback_get_last_node_actor(pos, range, seconds)
-                       if actor == "" then
+                       minetest.chat_send_player(name, "Checking "..minetest.pos_to_string(pos).."...")
+                       local actions = minetest.rollback_get_node_actions(pos, range, seconds, limit)
+                       local num_actions = #actions
+                       if num_actions == 0 then
                                minetest.chat_send_player(name, "Nobody has touched the "..
-                                               "specified location in "..dump(seconds).." seconds")
+                                               "specified location in "..seconds.." seconds")
                                return
                        end
-                       local nodedesc = "this node"
-                       if act_p.x ~= pos.x or act_p.y ~= pos.y or act_p.z ~= pos.z then
-                               nodedesc = minetest.pos_to_string(act_p)
+                       local time = os.time()
+                       for i = num_actions, 1, -1 do
+                               local action = actions[i]
+                               minetest.chat_send_player(name,
+                                       ("%s %s %s -> %s %d seconds ago.")
+                                               :format(
+                                                       minetest.pos_to_string(action.pos),
+                                                       action.actor,
+                                                       action.oldnode.name,
+                                                       action.newnode.name,
+                                                       time - action.time))
                        end
-                       local nodename = minetest.get_node(act_p).name
-                       minetest.chat_send_player(name, "Last actor on "..nodedesc..
-                                       " was "..actor..", "..dump(act_seconds)..
-                                       "s ago (node is now "..nodename..")")
                end
        end,
 })
@@ -554,7 +565,7 @@ minetest.register_chatcommand("rollback", {
        func = function(name, param)
                local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)")
                if not target_name then
-                       local player_name = nil;
+                       local player_name = nil
                        player_name, seconds = string.match(param, "([^ ]+) *(%d*)")
                        if not player_name then
                                minetest.chat_send_player(name, "Invalid parameters. See /help rollback and /help rollback_check")
@@ -564,13 +575,13 @@ minetest.register_chatcommand("rollback", {
                end
                seconds = tonumber(seconds) or 60
                minetest.chat_send_player(name, "Reverting actions of "..
-                               dump(target_name).." since "..dump(seconds).." seconds.")
+                               target_name.." since "..seconds.." seconds.")
                local success, log = minetest.rollback_revert_actions_by(
                                target_name, seconds)
-               if #log > 10 then
+               if #log > 100 then
                        minetest.chat_send_player(name, "(log is too long to show)")
                else
-                       for _,line in ipairs(log) do
+                       for _, line in pairs(log) do
                                minetest.chat_send_player(name, line)
                        end
                end
index 333f64cdc3c7d879d2d1a581fa699452b5d6ce63..d8b578d4834c4e6f21e6330c74837f7038741d81 100644 (file)
@@ -46,3 +46,8 @@ setmetatable(minetest.env, {
                return rawget(table, key)
        end
 })
+
+function minetest.rollback_get_last_node_actor(pos, range, seconds)
+       return minetest.rollback_get_node_actions(pos, range, seconds, 1)[1]
+end
+
index d0090a89cc4097f0840cf8849a7d02b718528eb6..cd9e27b7a1e2112e5dacec037c5bc25707cb0f5a 100644 (file)
@@ -1402,8 +1402,8 @@ minetest.handle_node_drops(pos, drops, digger)
 ^ Can be overridden to get different functionality (eg. dropping items on
   ground)
 
-Rollbacks:
-minetest.rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
+Rollback:
+minetest.rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...}
 ^ Find who has done something to a node, or near a node
 ^ actor: "player:<name>", also "liquid".
 minetest.rollback_revert_actions_by(actor, seconds) -> bool, log messages
index b95cc7bf3fce02b00db434fd3967a2b617c45500..4dc462af618f7b0ac455454c935a5188a4f22527 100644 (file)
@@ -48,41 +48,40 @@ sqlite3_stmt* dbs_knownActor_insert;
 sqlite3_stmt* dbs_knownNode_select;
 sqlite3_stmt* dbs_knownNode_insert;
 
-struct Stack
-{
+struct Stack {
        int node;
        int quantity;
 };
-struct ActionRow
-{
-       int                     id;
-       int                     actor;
-       int                     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;
+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;
 };
-struct Entity
-{
-       int                     id;
-       std::string     name;
+
+struct Entity {
+       int         id;
+       std::string name;
 };
+
 typedef std::vector<Entity> Entities;
 
 Entities KnownActors;
 Entities KnownNodes;
 
-void registerNewActor (int id, std::string name)
+void registerNewActor(int id, std::string name)
 {
        Entity newActor;
 
@@ -93,7 +92,8 @@ void registerNewActor (int id, std::string name)
 
        //std::cout << "New actor registered: " << id << " | " << name << std::endl;
 }
-void registerNewNode (int id, std::string name)
+
+void registerNewNode(int id, std::string name)
 {
        Entity newNode;
 
@@ -104,17 +104,19 @@ void registerNewNode (int id, std::string name)
 
        //std::cout << "New node registered: " << id << " | " << name << std::endl;
 }
-int getActorId (std::string name)
+
+int getActorId(std::string name)
 {
        Entities::const_iterator iter;
 
        for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
-               if (iter->name == name)
+               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);
+       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);
 
@@ -124,17 +126,19 @@ int getActorId (std::string name)
 
        return id;
 }
-int getNodeId (std::string name)
+
+int getNodeId(std::string name)
 {
        Entities::const_iterator iter;
 
        for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter)
-               if (iter->name == name)
+               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);
+       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);
 
@@ -142,31 +146,36 @@ int getNodeId (std::string name)
 
        return id;
 }
-const char * getActorName (int id)
+
+const char * getActorName(int id)
 {
        Entities::const_iterator iter;
 
        //std::cout << "getActorName of id " << id << std::endl;
 
        for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
-               if (iter->id == id)
+               if (iter->id == id) {
                        return iter->name.c_str();
+               }
 
        return "";
 }
-const char * getNodeName (int id)
+
+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)
+               if (iter->id == id) {
                        return iter->name.c_str();
+               }
 
        return "";
 }
-Stack getStackFromString (std::string text)
+
+Stack getStackFromString(std::string text)
 {
        Stack stack;
 
@@ -177,7 +186,8 @@ Stack getStackFromString (std::string text)
 
        return stack;
 }
-std::string getStringFromStack (Stack stack)
+
+std::string getStringFromStack(Stack stack)
 {
        std::string text;
 
@@ -187,199 +197,204 @@ std::string getStringFromStack (Stack stack)
 
        return text;
 }
-bool SQL_createDatabase (void)
+
+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);"
+       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` INTEGER 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`));"
+                       "`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)
+               "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)
+       } else if (dbs != 0) {
                throw FileNotGoodException("SQL Rollback: Exec statement to create table structure returned a non-zero value");
-       else
+       } else {
                infostream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
+       }
 
        return true;
 }
-void SQL_databaseCheck (void)
+void SQL_databaseCheck(void)
 {
-       if (dbh) return;
+       if (dbh) {
+               return;
+       }
 
        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);
+       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;
+       if (dbo != SQLITE_OK) {
+               infostream << "SQLROLLBACK: SQLite3 database failed to open: "
+                          << sqlite3_errmsg(dbh) << std::endl;
                throw FileNotGoodException("Cannot open database file");
        }
 
-       if (needsCreate) SQL_createDatabase();
+       if (needsCreate) {
+               SQL_createDatabase();
+       }
 
        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"
-               "       (       ?, ?, ?,"
-               "               ?, ?, ?, ?, ?, ?,"
-               "               ?, ?, ?,"
-               "               ?, ?, ?, ?,"
-               "               ?, ?, ?, ?,"
-               "               ?"
-               "       );"
-       ,       -1, &dbs_insert, NULL
-       );
-
-       if (dbr != SQLITE_OK)
+       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 ("
+               "       ?, ?, ?,"
+               "       ?, ?, ?, ?, ?, ?,"
+               "       ?, ?, ?,"
+               "       ?, ?, ?, ?,"
+               "       ?, ?, ?, ?,"
+               "       ?"
+               ");",
+               -1, &dbs_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"
-               "       (       ?, ?, ?,"
-               "               ?, ?, ?, ?, ?, ?,"
-               "               ?, ?, ?,"
-               "               ?, ?, ?, ?,"
-               "               ?, ?, ?, ?,"
-               "               ?, ?"
-               "       );"
-       ,       -1, &dbs_replace, NULL
-       );
-
-       if (dbr != SQLITE_OK)
+       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 ("
+               "       ?, ?, ?,"
+               "       ?, ?, ?, ?, ?, ?,"
+               "       ?, ?, ?,"
+               "       ?, ?, ?, ?,"
+               "       ?, ?, ?, ?,"
+               "       ?, ?"
+               ");",
+               -1, &dbs_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`"
+       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` >= ?"
-               " ORDER BY `timestamp` DESC, `id` DESC"
-       ,       -1, &dbs_select, NULL
-       );
-       if (dbr != SQLITE_OK)
+               " ORDER BY `timestamp` DESC, `id` DESC",
+               -1, &dbs_select, NULL);
+       if (dbr != SQLITE_OK) {
                throw FileNotGoodException(itos(dbr).c_str());
+       }
 
-       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`"
+       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`) - ABS(?)) <= ?"
-               " AND   (ABS(`y`) - ABS(?)) <= ?"
-               " AND   (ABS(`z`) - ABS(?)) <= ?"
+               " AND   ABS(`x` - ?) <= ?"
+               " AND   ABS(`y` - ?) <= ?"
+               " AND   ABS(`z` - ?) <= ?"
                " ORDER BY `timestamp` DESC, `id` DESC"
-               " LIMIT 0,5"
-       ,       -1, &dbs_select_range, NULL
-       );
-       if (dbr != SQLITE_OK)
+               " LIMIT 0,?",
+               -1, &dbs_select_range, NULL);
+       if (dbr != SQLITE_OK) {
                throw FileNotGoodException(itos(dbr).c_str());
+       }
 
-       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`"
+       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)
+               " ORDER BY `timestamp` DESC, `id` DESC",
+               -1, &dbs_select_withActor, NULL);
+       if (dbr != SQLITE_OK) {
                throw FileNotGoodException(itos(dbr).c_str());
+       }
 
-       dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `actor`", -1, &dbs_knownActor_select, NULL);
-       if (dbr != SQLITE_OK)
+       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());
+       }
 
-       dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `actor` (`name`) VALUES (?)", -1, &dbs_knownActor_insert, NULL);
-       if (dbr != SQLITE_OK)
+       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());
+       }
 
-       dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `node`", -1, &dbs_knownNode_select, NULL);
-       if (dbr != SQLITE_OK)
+       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());
+       }
 
-       dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `node` (`name`) VALUES (?)", -1, &dbs_knownNode_insert, NULL);
-       if (dbr != SQLITE_OK)
+       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());
+       }
 
        infostream << "SQL prepared statements setup correctly" << std::endl;
 
@@ -388,54 +403,52 @@ void SQL_databaseCheck (void)
        sqlite3_reset(dbs_knownActor_select);
        while (SQLITE_ROW == (select = sqlite3_step(dbs_knownActor_select)))
                registerNewActor(
-                               sqlite3_column_int  (dbs_knownActor_select, 0),
-                               reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownActor_select, 1))
+                       sqlite3_column_int(dbs_knownActor_select, 0),
+                       reinterpret_cast<const char *>(sqlite3_column_text(dbs_knownActor_select, 1))
                );
 
        sqlite3_reset(dbs_knownNode_select);
        while (SQLITE_ROW == (select = sqlite3_step(dbs_knownNode_select)))
                registerNewNode(
-                               sqlite3_column_int  (dbs_knownNode_select, 0),
-                               reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownNode_select, 1))
+                       sqlite3_column_int(dbs_knownNode_select, 0),
+                       reinterpret_cast<const char *>(sqlite3_column_text(dbs_knownNode_select, 1))
                );
 
        return;
 }
-bool SQL_registerRow (ActionRow row)
+bool SQL_registerRow(ActionRow row)
 {
        SQL_databaseCheck();
 
-       sqlite3_stmt * dbs_do = (row.id)? dbs_replace: dbs_insert;
+       sqlite3_stmt * dbs_do = (row.id) ? dbs_replace : dbs_insert;
 
        /*
        std::cout
-               << (row.id? "Replacing": "Inserting") 
+               << (row.id? "Replacing": "Inserting")
                << " ActionRow" << std::endl;
        */
        sqlite3_reset(dbs_do);
 
-       int bind [20 + (((bool) row.id)? 1: 0)], ii = 0;
+       int bind [20 + (((bool) row.id) ? 1 : 0)], ii = 0;
        bool nodeMeta = false;
 
-       bind[ii++] = sqlite3_bind_int (dbs_do, 1, row.actor);
-       bind[ii++] = sqlite3_bind_int (dbs_do, 2, row.timestamp);
-       bind[ii++] = sqlite3_bind_int (dbs_do, 3, row.type);
+       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);
 
-       if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
-       {
+       if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) {
                std::string loc         = row.location;
                std::string locType     = loc.substr(0, loc.find(":"));
                nodeMeta = (locType == "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);
+               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);
 
-               if (nodeMeta)
-               {
+               if (nodeMeta) {
                        std::string x, y, z;
                        int     l, r;
                        l = loc.find(':') + 1;
@@ -444,64 +457,59 @@ bool SQL_registerRow (ActionRow row)
                        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()));
+                       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()));
                }
-       }
-               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);
+       } 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);
        }
 
-       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);
-       }
-               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);
+       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);
+       } 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);
                }
-               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);
+               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);
        }
 
-       if (row.id)
-               bind[ii++] = sqlite3_bind_int (dbs_do, 22, row.id);
+       if (row.id) {
+               bind[ii++] = sqlite3_bind_int(dbs_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;
+               if (bind[ii] != SQLITE_OK)
+                       infostream
+                                       << "WARNING: failed to bind param " << ii + 1
+                                       << " when inserting an entry in table setnode" << std::endl;
 
        /*
        std::cout << "========DB-WRITTEN==========" << std::endl;
@@ -543,60 +551,55 @@ bool SQL_registerRow (ActionRow row)
        //       std::cout << "WARNING: rollback action not written: " << sqlite3_errmsg(dbh) << std::endl;
        //else std::cout << "Action correctly inserted via SQL" << std::endl;
 }
-std::list<ActionRow> actionRowsFromSelect (sqlite3_stmt* stmt)
+std::list<ActionRow> actionRowsFromSelect(sqlite3_stmt* stmt)
 {
        std::list<ActionRow> rows;
        const unsigned char * text;
        size_t size;
 
-       while (SQLITE_ROW == sqlite3_step(stmt))
-       {
+       while (SQLITE_ROW == sqlite3_step(stmt)) {
                ActionRow row;
 
-               row.actor               = sqlite3_column_int (stmt, 0);
-               row.timestamp   = sqlite3_column_int (stmt, 1); 
-               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<const char*>(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);
+               row.actor     = sqlite3_column_int(stmt, 0);
+               row.timestamp = sqlite3_column_int64(stmt, 1);
+               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<const char*>(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);
                }
 
-               if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta)
-               {
-                       row.x = sqlite3_column_int (stmt,  9);
-                       row.y = sqlite3_column_int (stmt, 10);
-                       row.z = sqlite3_column_int (stmt, 11);
+               if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta) {
+                       row.x = sqlite3_column_int(stmt,  9);
+                       row.y = sqlite3_column_int(stmt, 10);
+                       row.z = sqlite3_column_int(stmt, 11);
                }
-               
-               if (row.type == RollbackAction::TYPE_SET_NODE)
-               {
-                       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);
-                       row.oldMeta             = std::string(reinterpret_cast<const char*>(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);
-                       row.newMeta             = std::string(reinterpret_cast<const char*>(text), size);
-                       row.guessed             = sqlite3_column_int  (stmt, 20);
+
+               if (row.type == RollbackAction::TYPE_SET_NODE) {
+                       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);
+                       row.oldMeta   = std::string(reinterpret_cast<const char*>(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);
+                       row.newMeta   = std::string(reinterpret_cast<const char*>(text), size);
+                       row.guessed   = sqlite3_column_int(stmt, 20);
                }
 
-               row.location = row.nodeMeta? "nodemeta:": getActorName(row.actor);
+               row.location = row.nodeMeta ? "nodemeta:" : getActorName(row.actor);
 
-               if (row.nodeMeta)
-               {
+               if (row.nodeMeta) {
                        row.location.append(itos(row.x));
                        row.location.append(",");
                        row.location.append(itos(row.y));
@@ -646,25 +649,22 @@ std::list<ActionRow> actionRowsFromSelect (sqlite3_stmt* stmt)
 
        return rows;
 }
-ActionRow actionRowFromRollbackAction (RollbackAction action)
+ActionRow actionRowFromRollbackAction(RollbackAction action)
 {
        ActionRow row;
 
-       row.id                  = 0;
-       row.actor               = getActorId(action.actor);
-       row.timestamp   = action.unix_time;
-       row.type                = action.type;
+       row.id        = 0;
+       row.actor     = getActorId(action.actor);
+       row.timestamp = action.unix_time;
+       row.type      = action.type;
 
-       if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
-       {
+       if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) {
                row.location = action.inventory_location;
                row.list     = action.inventory_list;
                row.index    = action.inventory_index;
                row.add      = action.inventory_add;
                row.stack    = getStackFromString(action.inventory_stack);
-       }
-               else
-       {
+       } else {
                row.x         = action.p.X;
                row.y         = action.p.Y;
                row.z         = action.p.Z;
@@ -681,46 +681,44 @@ ActionRow actionRowFromRollbackAction (RollbackAction action)
 
        return row;
 }
-std::list<RollbackAction> rollbackActionsFromActionRows (std::list<ActionRow> rows)
+std::list<RollbackAction> rollbackActionsFromActionRows(std::list<ActionRow> rows)
 {
        std::list<RollbackAction> actions;
        std::list<ActionRow>::const_iterator it;
 
-       for (it = rows.begin(); it != rows.end(); ++it)
-       {
+       for (it = rows.begin(); it != rows.end(); ++it) {
                RollbackAction action;
-               action.actor            = (it->actor)? getActorName(it->actor): "";
-               action.unix_time        = it->timestamp;
-               action.type                     = static_cast<RollbackAction::Type>(it->type);
-
-        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);
-                               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;
-                               action.n_old.param2 = it->oldParam2;
-                               action.n_old.meta       = it->oldMeta;
-                               action.n_new.name       = getNodeName(it->newNode);
-                               action.n_new.param1 = it->newParam1;
-                               action.n_new.param2 = it->newParam2;
-                               action.n_new.meta       = it->newMeta;
-                               break;
-
-                       default:
-
-                               throw("W.T.F.");
-                               break;
+               action.actor     = (it->actor) ? getActorName(it->actor) : "";
+               action.unix_time = it->timestamp;
+               action.type      = static_cast<RollbackAction::Type>(it->type);
+
+               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);
+                       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;
+                       action.n_old.param2 = it->oldParam2;
+                       action.n_old.meta   = it->oldMeta;
+                       action.n_new.name   = getNodeName(it->newNode);
+                       action.n_new.param1 = it->newParam1;
+                       action.n_new.param2 = it->newParam2;
+                       action.n_new.meta   = it->newMeta;
+                       break;
+
+               default:
+
+                       throw ("W.T.F.");
+                       break;
                }
 
                actions.push_back(action);
@@ -728,69 +726,83 @@ std::list<RollbackAction> rollbackActionsFromActionRows (std::list<ActionRow> ro
 
        return actions;
 }
-std::list<ActionRow> SQL_getRowsSince (int firstTime, std::string actor = "")
+
+std::list<ActionRow> 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_int  (dbs_stmt, 1, firstTime);
+       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));
+       if (actor.length()) {
+               sqlite3_bind_int(dbs_stmt, 2, getActorId(actor));
+       }
 
        return actionRowsFromSelect(dbs_stmt);
 }
-std::list<ActionRow> SQL_getRowsSince_range (int firstTime, v3s16 p, int range)
+
+std::list<ActionRow> SQL_getRowsSince_range(time_t firstTime, v3s16 p, int range,
+                int limit)
 {
-       sqlite3_stmt * stmt = dbs_select_range;
+       sqlite3_stmt *stmt = dbs_select_range;
        sqlite3_reset(stmt);
 
-       sqlite3_bind_int(stmt, 1, firstTime);
+       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);
 
        return actionRowsFromSelect(stmt);
 }
-std::list<RollbackAction> SQL_getActionsSince_range (int firstTime, v3s16 p, int range)
+
+std::list<RollbackAction> SQL_getActionsSince_range(time_t firstTime, v3s16 p, int range,
+                int limit)
 {
-       std::list<ActionRow> rows = SQL_getRowsSince_range(firstTime, p, range);
+       std::list<ActionRow> rows = SQL_getRowsSince_range(firstTime, p, range, limit);
 
        return rollbackActionsFromActionRows(rows);
 }
-std::list<RollbackAction> SQL_getActionsSince (int firstTime, std::string actor = "")
+
+std::list<RollbackAction> SQL_getActionsSince(time_t firstTime, std::string actor = "")
 {
        std::list<ActionRow> rows = SQL_getRowsSince(firstTime, actor);
        return rollbackActionsFromActionRows(rows);
 }
-void TXT_migrate (std::string filepath)
+
+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);
-       if (!fh.good()) throw("DIE");
+       std::ifstream fh(filepath.c_str(), std::ios::in | std::ios::ate);
+       if (!fh.good()) {
+               throw ("DIE");
+       }
 
-       int filesize = fh.tellg();
+       std::streampos filesize = fh.tellg();
 
-       if (filesize > 10)
-       {
+       if (filesize > 10) {
                fh.seekg(0);
 
                std::string bit;
                int i   = 0;
                int id  = 1;
-               int t   = 0;
-               do
-               {
+               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; }
+                       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
@@ -800,44 +812,53 @@ void TXT_migrate (std::string filepath)
                        std::getline(fh, bit, '[');
                        std::getline(fh, bit, ' ');
 
-                       if (bit == "modify_inventory_stack")
+                       if (bit == "modify_inventory_stack") {
                                row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK;
+                       }
 
-                       if (bit == "set_node")
+                       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));
+                       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)));
+                               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)
-                       {
+                       } 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, ',');
+                               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, ' ');
-                               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)));
+                               row.oldParam2 = atoi(trim(bit).c_str());
+                               row.oldMeta   = 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));
+                               row.newNode   = getNodeId(trim(deSerializeJsonString(fh)));
                                std::getline(fh, bit, ' ');
                                std::getline(fh, bit, ' ');
-                               std::getline(fh, bit);          row.guessed = (int) ( trim(bit) == "actor_is_guess" );
+                               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");
                        }
 
                        /*
@@ -870,43 +891,43 @@ void TXT_migrate (std::string filepath)
                        }
                        */
 
-                       if (i == 0)
-                       {
+                       if (i == 0) {
                                t = time(0);
                                sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
                        }
 
-                       SQL_registerRow(row); ++i;
+                       SQL_registerRow(row);
+                       ++i;
 
-                       if (time(0) - t)
-                       {
+                       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) << "%"
-                                       << "\tSpeed: " << i / t << " actions inserted per second   "
-                                       << "\r";
-                               std::cout.flush();
+                                               << " Done: " << (int)(((float) fh.tellg() / (float) filesize) * 100) << "%"
+                                               << " Speed: " << i / t << "/second     \r" << std::flush;
                                i = 0;
                        }
 
                        ++id;
-               }
-               while (!fh.eof() && fh.good());
+               } while (!fh.eof() && fh.good());
+       } else {
+               errorstream << "Empty rollback log" << std::endl;
        }
 
        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, int suspect_t,
-               v3s16 action_p, int action_t)
+static float 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)
-               return 0; // 0 = cannot be
+       if (action_t < suspect_t) {
+               return 0;        // 0 = cannot be
+       }
        // Start from 100
        int f = 100;
        // Distance (1 node = -x points)
@@ -914,22 +935,24 @@ static float getSuspectNearness(bool is_guess, v3s16 suspect_p, int suspect_t,
        // Time (1 second = -x points)
        f -= 1 * (action_t - suspect_t);
        // If is a guess, halve the points
-       if(is_guess)
+       if (is_guess) {
                f *= 0.5;
+       }
        // Limit to 0
-       if(f < 0)
+       if (f < 0) {
                f = 0;
+       }
        return f;
 }
 class RollbackManager: public IRollbackManager
 {
 public:
        // IRollbackManager interface
-       void reportAction(const RollbackAction &action_)
-       {
+       void reportAction(const RollbackAction &action_) {
                // Ignore if not important
-               if  (!action_.isImportant(m_gamedef))
+               if (!action_.isImportant(m_gamedef)) {
                        return;
+               }
 
                RollbackAction action = action_;
                action.unix_time = time(0);
@@ -938,90 +961,96 @@ public:
                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
-               {
+               if (action.actor.empty()) { // If actor is not known, find out suspect or cancel
                        v3s16 p;
-                       if  (!action.getPosition(&p))
+                       if (!action.getPosition(&p)) {
                                return;
+                       }
 
                        action.actor = getSuspect(p, 83, 1);
-                       if  (action.actor.empty())
+                       if (action.actor.empty()) {
                                return;
+                       }
 
                        action.actor_is_guess = true;
                }
 
                infostream
-                       <<      "RollbackManager::reportAction():"
-                       <<      " time=" << action.unix_time
-                       <<      " actor=\"" << action.actor << "\""
-                       <<      (action.actor_is_guess? " (guess)": "")
-                       <<      " action=" << action.toString()
-                       <<      std::endl;
+                               << "RollbackManager::reportAction():"
+                               << " time=" << action.unix_time
+                               << " actor=\"" << action.actor << "\""
+                               << (action.actor_is_guess ? " (guess)" : "")
+                               << " action=" << action.toString()
+                               << std::endl;
                addAction(action);
        }
-       std::string getActor()
-       {
+
+       std::string getActor() {
                return m_current_actor;
        }
-       bool isActorGuess()
-       {
+
+       bool isActorGuess() {
                return m_current_actor_is_guess;
        }
-       void setActor(const std::string &actor, bool 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 != "")
+
+       std::string getSuspect(v3s16 p, float nearness_shortcut, float min_nearness) {
+               if (m_current_actor != "") {
                        return m_current_actor;
+               }
                int cur_time = time(0);
-               int first_time = cur_time - (100-min_nearness);
+               time_t first_time = cur_time - (100 - min_nearness);
                RollbackAction likely_suspect;
                float likely_suspect_nearness = 0;
-               for(std::list<RollbackAction>::const_reverse_iterator
-                               i = m_action_latest_buffer.rbegin();
-                               i != m_action_latest_buffer.rend(); i++)
-               {
-                       if(i->unix_time < first_time)
+               for (std::list<RollbackAction>::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 == "")
+                       }
+                       if (i->actor == "") {
                                continue;
+                       }
                        // Find position of suspect or continue
                        v3s16 suspect_p;
-                       if(!i->getPosition(&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){
+                                                    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)
+                               if (likely_suspect_nearness >= nearness_shortcut) {
                                        break;
+                               }
                        }
                }
                // No likely suspect was found
-               if(likely_suspect_nearness == 0)
+               if (likely_suspect_nearness == 0) {
                        return "";
+               }
                // Likely suspect was found
                return likely_suspect.actor;
        }
-       void flush()
-       {
+
+       void flush() {
                infostream << "RollbackManager::flush()" << std::endl;
 
                sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
 
                std::list<RollbackAction>::const_iterator iter;
 
-               for     (iter  = m_action_todisk_buffer.begin();
-                        iter != m_action_todisk_buffer.end();
-                        iter++)
-               {
-                       if (iter->actor == "")
+               for (iter  = m_action_todisk_buffer.begin();
+                    iter != m_action_todisk_buffer.end();
+                    iter++) {
+                       if (iter->actor == "") {
                                continue;
+                       }
 
                        SQL_registerRow(actionRowFromRollbackAction(*iter));
                }
@@ -1029,33 +1058,32 @@ public:
                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)
-       {
+               m_current_actor_is_guess(false) {
                infostream
-                       << "RollbackManager::RollbackManager(" << filepath << ")"
-                       << std::endl;
-               
+                               << "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(1+ filepath.rfind(DIR_DELIM));
-               std::string filenameNew = (filenameOld == "rollback.txt")? "rollback.sqlite": filenameOld;
+               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;
-               migratingFlag.append(".migrating");
+               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)))
-               {
+               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);
@@ -1063,74 +1091,54 @@ public:
 
                SQL_databaseCheck();
        }
-       ~RollbackManager()
-       {
+
+       ~RollbackManager() {
                infostream << "RollbackManager::~RollbackManager()" << std::endl;
                flush();
        }
-       void addAction(const RollbackAction &action)
-       {
+
+       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)
+               if (m_action_todisk_buffer.size() >= 500) {
                        flush();
+               }
        }
-       std::list<RollbackAction> getEntriesSince(int first_time)
-       {
+
+       std::list<RollbackAction> getEntriesSince(time_t first_time) {
                infostream
-                       << "RollbackManager::getEntriesSince(" << first_time << ")"
-                       << std::endl;
+                               << "RollbackManager::getEntriesSince(" << first_time << ")"
+                               << std::endl;
 
                flush();
-               
+
                std::list<RollbackAction> result = SQL_getActionsSince(first_time);
 
                return result;
        }
-       std::string getLastNodeActor(v3s16 p, int range, int seconds, v3s16 *act_p, int *act_seconds)
-       {
-               int cur_time = time(0);
-               int first_time = cur_time - seconds;
-
-               std::list<RollbackAction> action_buffer = SQL_getActionsSince_range(first_time, p, range);
-               std::list<RollbackAction>::const_reverse_iterator iter;
-
-               for (iter  = action_buffer.rbegin();
-                        iter != action_buffer.rend();
-                        iter++)
-               {
-                       v3s16 action_p;
 
-                       action_p.X = iter->p.X;
-                       action_p.Y = iter->p.Y;
-                       action_p.Z = iter->p.Z;
-                       
-                       if (act_p)
-                               *act_p = action_p;
+       std::list<RollbackAction> getNodeActors(v3s16 pos, int range, time_t seconds, int limit) {
+               time_t cur_time = time(0);
+               time_t first_time = cur_time - seconds;
 
-                       if (act_seconds)
-                               *act_seconds = cur_time - iter->unix_time;
-
-                       return iter->actor;
-               }
-
-               return "";
+               return SQL_getActionsSince_range(first_time, pos, range, limit);
        }
-       std::list<RollbackAction> getRevertActions(const std::string &actor_filter, int seconds)
-       {
+
+       std::list<RollbackAction> getRevertActions(const std::string &actor_filter,
+                       time_t seconds) {
                infostream
-                       << "RollbackManager::getRevertActions(" << actor_filter
-                       << ", " << seconds << ")"
-                       << std::endl;
+                               << "RollbackManager::getRevertActions(" << actor_filter
+                               << ", " << seconds << ")"
+                               << std::endl;
 
                // Figure out time
-               int cur_time = time(0);
-               int first_time = cur_time - seconds;
-               
+               time_t cur_time = time(0);
+               time_t first_time = cur_time - seconds;
+
                flush();
-               
+
                std::list<RollbackAction> result = SQL_getActionsSince(first_time, actor_filter);
 
                return result;
@@ -1148,3 +1156,4 @@ IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *g
 {
        return new RollbackManager(filepath, gamedef);
 }
+
index 46a76f5830e5233f418c922d52681d1ca78555a1..eea7c59f2ec5287ef2df3037f621ebb14bad44d6 100644 (file)
@@ -36,17 +36,17 @@ public:
        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 ~IRollbackManager() {}
        virtual void flush() = 0;
-       // Get last actor that did something to position p, but not further than
+       // Get all actors that did something to position p, but not further than
        // <seconds> in history
-       virtual std::string getLastNodeActor(v3s16 p, int range, int seconds,
-                       v3s16 *act_p, int *act_seconds) = 0;
+       virtual std::list<RollbackAction> getNodeActors(v3s16 pos, int range,
+                       time_t seconds, int limit) = 0;
        // Get actions to revert <seconds> of history made by <actor>
        virtual std::list<RollbackAction> getRevertActions(const std::string &actor,
-                       int seconds) = 0;
+                       time_t seconds) = 0;
 };
 
 IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef);
index b2e8f3428fc13b4cca612d023799848a20a12c2d..f16f82b4028f910cf76e82eaf0d6ed80ccf961b8 100644 (file)
@@ -63,7 +63,7 @@ struct RollbackAction
                TYPE_MODIFY_INVENTORY_STACK,
        } type;
 
-       int unix_time;
+       time_t unix_time;
        std::string actor;
        bool actor_is_guess;
 
index d5abe176e4e551c6bcf20cc04acb2bfdd10d3fea..e7185b00b5bd48bd514e97a9a050d1d7da5bd3bd 100644 (file)
@@ -24,21 +24,54 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "rollback.h"
 
 
-// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
-int ModApiRollback::l_rollback_get_last_node_actor(lua_State *L)
+void push_RollbackNode(lua_State *L, RollbackNode &node)
 {
-       v3s16 p = read_v3s16(L, 1);
+       lua_createtable(L, 0, 3);
+       lua_pushstring(L, node.name.c_str());
+       lua_setfield(L, -2, "name");
+       lua_pushnumber(L, node.param1);
+       lua_setfield(L, -2, "param1");
+       lua_pushnumber(L, node.param2);
+       lua_setfield(L, -2, "param2");
+}
+
+// rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...}
+int ModApiRollback::l_rollback_get_node_actions(lua_State *L)
+{
+       v3s16 pos = read_v3s16(L, 1);
        int range = luaL_checknumber(L, 2);
-       int seconds = luaL_checknumber(L, 3);
+       time_t seconds = (time_t) luaL_checknumber(L, 3);
+       int limit = luaL_checknumber(L, 4);
        Server *server = getServer(L);
        IRollbackManager *rollback = server->getRollbackManager();
-       v3s16 act_p;
-       int act_seconds = 0;
-       std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds);
-       lua_pushstring(L, actor.c_str());
-       push_v3s16(L, act_p);
-       lua_pushnumber(L, act_seconds);
-       return 3;
+
+       std::list<RollbackAction> actions = rollback->getNodeActors(pos, range, seconds, limit);
+       std::list<RollbackAction>::iterator iter = actions.begin();
+
+       lua_createtable(L, actions.size(), 0);
+       for (unsigned int i = 1; iter != actions.end(); ++iter, ++i) {
+               lua_pushnumber(L, i); // Push index
+               lua_createtable(L, 0, 5); // Make a table with enough space pre-allocated
+
+               lua_pushstring(L, iter->actor.c_str());
+               lua_setfield(L, -2, "actor");
+
+               push_v3s16(L, iter->p);
+               lua_setfield(L, -2, "pos");
+
+               lua_pushnumber(L, iter->unix_time);
+               lua_setfield(L, -2, "time");
+
+               push_RollbackNode(L, iter->n_old);
+               lua_setfield(L, -2, "oldnode");
+
+               push_RollbackNode(L, iter->n_new);
+               lua_setfield(L, -2, "newnode");
+
+               lua_settable(L, -3); // Add action table to main table
+       }
+
+       return 1;
 }
 
 // rollback_revert_actions_by(actor, seconds) -> bool, log messages
@@ -53,28 +86,19 @@ int ModApiRollback::l_rollback_revert_actions_by(lua_State *L)
        bool success = server->rollbackRevertActions(actions, &log);
        // Push boolean result
        lua_pushboolean(L, success);
-       // Get the table insert function and push the log table
-       lua_getglobal(L, "table");
-       lua_getfield(L, -1, "insert");
-       int table_insert = lua_gettop(L);
-       lua_newtable(L);
-       int table = lua_gettop(L);
-       for(std::list<std::string>::const_iterator i = log.begin();
-                       i != log.end(); i++)
-       {
-               lua_pushvalue(L, table_insert);
-               lua_pushvalue(L, table);
-               lua_pushstring(L, i->c_str());
-               if(lua_pcall(L, 2, 0, 0))
-                       script_error(L);
+       lua_createtable(L, log.size(), 0);
+       unsigned long i = 0;
+       for(std::list<std::string>::const_iterator iter = log.begin();
+                       iter != log.end(); ++i, ++iter) {
+               lua_pushnumber(L, i);
+               lua_pushstring(L, iter->c_str());
+               lua_settable(L, -3);
        }
-       lua_remove(L, -2); // Remove table
-       lua_remove(L, -2); // Remove insert
        return 2;
 }
 
 void ModApiRollback::Initialize(lua_State *L, int top)
 {
-       API_FCT(rollback_get_last_node_actor);
+       API_FCT(rollback_get_node_actions);
        API_FCT(rollback_revert_actions_by);
 }
index 86992a47ead84c3cd7f9e0cb87242b48e9101342..3b01c0d8b82caa861fd6ba1bf9513dd9324de4fb 100644 (file)
@@ -22,10 +22,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "lua_api/l_base.h"
 
-class ModApiRollback : public ModApiBase {
+class ModApiRollback : public ModApiBase
+{
 private:
-       // rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds
-       static int l_rollback_get_last_node_actor(lua_State *L);
+       // rollback_get_node_actions(pos, range, seconds) -> {{actor, pos, time, oldnode, newnode}, ...}
+       static int l_rollback_get_node_actions(lua_State *L);
 
        // rollback_revert_actions_by(actor, seconds) -> bool, log messages
        static int l_rollback_revert_actions_by(lua_State *L);