diff --git a/builtin/chatcommands.lua b/builtin/chatcommands.lua
index 831d3a7b41e7d139787c9710500aec9d75e8033e..b9661a8889c2a76a8b6005d9269d3c9bb2cbabcc 100644
--- a/builtin/chatcommands.lua
+++ b/builtin/chatcommands.lua
@@ -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
diff --git a/builtin/deprecated.lua b/builtin/deprecated.lua
index 333f64cdc3c7d879d2d1a581fa699452b5d6ce63..d8b578d4834c4e6f21e6330c74837f7038741d81 100644
--- a/builtin/deprecated.lua
+++ b/builtin/deprecated.lua
@@ -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
+
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index d0090a89cc4097f0840cf8849a7d02b718528eb6..cd9e27b7a1e2112e5dacec037c5bc25707cb0f5a 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -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
diff --git a/src/rollback.cpp b/src/rollback.cpp
index b95cc7bf3fce02b00db434fd3967a2b617c45500..4dc462af618f7b0ac455454c935a5188a4f22527 100644
--- a/src/rollback.cpp
+++ b/src/rollback.cpp
@@ -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 @@ class RollbackManager: public IRollbackManager
 		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 @@ class RollbackManager: public IRollbackManager
 		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 @@ class RollbackManager: public IRollbackManager
 
 		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);
 }
+
diff --git a/src/rollback.h b/src/rollback.h
index 46a76f5830e5233f418c922d52681d1ca78555a1..eea7c59f2ec5287ef2df3037f621ebb14bad44d6 100644
--- a/src/rollback.h
+++ b/src/rollback.h
@@ -36,17 +36,17 @@ class IRollbackManager: public IRollbackReportSink
 	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);
diff --git a/src/rollback_interface.h b/src/rollback_interface.h
index b2e8f3428fc13b4cca612d023799848a20a12c2d..f16f82b4028f910cf76e82eaf0d6ed80ccf961b8 100644
--- a/src/rollback_interface.h
+++ b/src/rollback_interface.h
@@ -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;
 
diff --git a/src/script/lua_api/l_rollback.cpp b/src/script/lua_api/l_rollback.cpp
index d5abe176e4e551c6bcf20cc04acb2bfdd10d3fea..e7185b00b5bd48bd514e97a9a050d1d7da5bd3bd 100644
--- a/src/script/lua_api/l_rollback.cpp
+++ b/src/script/lua_api/l_rollback.cpp
@@ -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);
 }
diff --git a/src/script/lua_api/l_rollback.h b/src/script/lua_api/l_rollback.h
index 86992a47ead84c3cd7f9e0cb87242b48e9101342..3b01c0d8b82caa861fd6ba1bf9513dd9324de4fb 100644
--- a/src/script/lua_api/l_rollback.h
+++ b/src/script/lua_api/l_rollback.h
@@ -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);