Clean up rollback

This commit is contained in:
ShadowNinja 2014-06-25 20:28:41 -04:00
parent da0f1e5497
commit b1965ac209
12 changed files with 888 additions and 1255 deletions

@ -30,7 +30,7 @@ class ITextureSource;
class ISoundManager; class ISoundManager;
class IShaderSource; class IShaderSource;
class MtEventManager; class MtEventManager;
class IRollbackReportSink; class IRollbackManager;
namespace irr { namespace scene { namespace irr { namespace scene {
class IAnimatedMesh; class IAnimatedMesh;
class ISceneManager; class ISceneManager;
@ -68,7 +68,7 @@ public:
// Only usable on the server, and NOT thread-safe. It is usable from the // Only usable on the server, and NOT thread-safe. It is usable from the
// environment thread. // environment thread.
virtual IRollbackReportSink* getRollbackReportSink(){return NULL;} virtual IRollbackManager* getRollbackManager(){return NULL;}
// Used on the client // Used on the client
virtual bool checkLocalPrivilege(const std::string &priv) virtual bool checkLocalPrivilege(const std::string &priv)
@ -82,7 +82,7 @@ public:
ISoundManager* sound(){return getSoundManager();} ISoundManager* sound(){return getSoundManager();}
IShaderSource* shsrc(){return getShaderSource();} IShaderSource* shsrc(){return getShaderSource();}
MtEventManager* event(){return getEventManager();} MtEventManager* event(){return getEventManager();}
IRollbackReportSink* rollback(){return getRollbackReportSink();} IRollbackManager* rollback(){return getRollbackManager();}
}; };
#endif #endif

@ -183,7 +183,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
legacy_nimap.getName(material, name); legacy_nimap.getName(material, name);
if(name == "") if(name == "")
name = "unknown_block"; name = "unknown_block";
name = itemdef->getAlias(name); if (itemdef)
name = itemdef->getAlias(name);
count = materialcount; count = materialcount;
} }
else if(name == "MaterialItem2") else if(name == "MaterialItem2")
@ -202,7 +203,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
legacy_nimap.getName(material, name); legacy_nimap.getName(material, name);
if(name == "") if(name == "")
name = "unknown_block"; name = "unknown_block";
name = itemdef->getAlias(name); if (itemdef)
name = itemdef->getAlias(name);
count = materialcount; count = materialcount;
} }
else if(name == "node" || name == "NodeItem" || name == "MaterialItem3" else if(name == "node" || name == "NodeItem" || name == "MaterialItem3"
@ -223,7 +225,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
name = fnd.next(" "); name = fnd.next(" ");
} }
fnd.skip_over(" "); fnd.skip_over(" ");
name = itemdef->getAlias(name); if (itemdef)
name = itemdef->getAlias(name);
count = stoi(trim(fnd.next(""))); count = stoi(trim(fnd.next("")));
if(count == 0) if(count == 0)
count = 1; count = 1;
@ -252,7 +255,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
count = 1; count = 1;
// Then read wear // Then read wear
fnd.skip_over(" "); fnd.skip_over(" ");
name = itemdef->getAlias(name); if (itemdef)
name = itemdef->getAlias(name);
wear = stoi(trim(fnd.next(""))); wear = stoi(trim(fnd.next("")));
} }
else else
@ -262,7 +266,8 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
// The real thing // The real thing
// Apply item aliases // Apply item aliases
name = itemdef->getAlias(name); if (itemdef)
name = itemdef->getAlias(name);
// Read the count // Read the count
std::string count_str; std::string count_str;
@ -294,9 +299,9 @@ void ItemStack::deSerialize(std::istream &is, IItemDefManager *itemdef)
} while(false); } while(false);
} }
if(name.empty() || count == 0) if (name.empty() || count == 0)
clear(); clear();
else if(itemdef->get(name).type == ITEM_TOOL) else if (itemdef && itemdef->get(name).type == ITEM_TOOL)
count = 1; count = 1;
} }
@ -308,12 +313,12 @@ void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef)
std::string ItemStack::getItemString() const std::string ItemStack::getItemString() const
{ {
// Get item string
std::ostringstream os(std::ios::binary); std::ostringstream os(std::ios::binary);
serialize(os); serialize(os);
return os.str(); return os.str();
} }
ItemStack ItemStack::addItem(const ItemStack &newitem_, ItemStack ItemStack::addItem(const ItemStack &newitem_,
IItemDefManager *itemdef) IItemDefManager *itemdef)
{ {

@ -20,12 +20,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#ifndef INVENTORY_HEADER #ifndef INVENTORY_HEADER
#define INVENTORY_HEADER #define INVENTORY_HEADER
#include <iostream>
#include <string>
#include <vector>
#include "irrlichttypes.h"
#include "debug.h" #include "debug.h"
#include "itemdef.h" #include "itemdef.h"
#include "irrlichttypes.h"
#include <istream>
#include <ostream>
#include <string>
#include <vector>
struct ToolCapabilities; struct ToolCapabilities;
@ -39,8 +40,9 @@ struct ItemStack
// Serialization // Serialization
void serialize(std::ostream &os) const; void serialize(std::ostream &os) const;
void deSerialize(std::istream &is, IItemDefManager *itemdef); // Deserialization. Pass itemdef unless you don't want aliases resolved.
void deSerialize(const std::string &s, IItemDefManager *itemdef); void deSerialize(std::istream &is, IItemDefManager *itemdef = NULL);
void deSerialize(const std::string &s, IItemDefManager *itemdef = NULL);
// Returns the string used for inventory // Returns the string used for inventory
std::string getItemString() const; std::string getItemString() const;

@ -368,7 +368,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
*/ */
if(!ignore_rollback && gamedef->rollback()) if(!ignore_rollback && gamedef->rollback())
{ {
IRollbackReportSink *rollback = gamedef->rollback(); IRollbackManager *rollback = gamedef->rollback();
// If source is not infinite, record item take // If source is not infinite, record item take
if(src_can_take_count != -1){ if(src_can_take_count != -1){
@ -380,7 +380,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
loc = os.str(); loc = os.str();
} }
action.setModifyInventoryStack(loc, from_list, from_i, false, action.setModifyInventoryStack(loc, from_list, from_i, false,
src_item.getItemString()); src_item);
rollback->reportAction(action); rollback->reportAction(action);
} }
// If destination is not infinite, record item put // If destination is not infinite, record item put
@ -393,7 +393,7 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
loc = os.str(); loc = os.str();
} }
action.setModifyInventoryStack(loc, to_list, to_i, true, action.setModifyInventoryStack(loc, to_list, to_i, true,
src_item.getItemString()); src_item);
rollback->reportAction(action); rollback->reportAction(action);
} }
} }
@ -632,7 +632,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
*/ */
if(!ignore_src_rollback && gamedef->rollback()) if(!ignore_src_rollback && gamedef->rollback())
{ {
IRollbackReportSink *rollback = gamedef->rollback(); IRollbackManager *rollback = gamedef->rollback();
// If source is not infinite, record item take // If source is not infinite, record item take
if(src_can_take_count != -1){ if(src_can_take_count != -1){
@ -644,7 +644,7 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame
loc = os.str(); loc = os.str();
} }
action.setModifyInventoryStack(loc, from_list, from_i, action.setModifyInventoryStack(loc, from_list, from_i,
false, src_item.getItemString()); false, src_item);
rollback->reportAction(action); rollback->reportAction(action);
} }
} }

@ -41,7 +41,7 @@ class ServerMapSector;
class MapBlock; class MapBlock;
class NodeMetadata; class NodeMetadata;
class IGameDef; class IGameDef;
class IRollbackReportSink; class IRollbackManager;
class EmergeManager; class EmergeManager;
class ServerEnvironment; class ServerEnvironment;
struct BlockMakeData; struct BlockMakeData;

File diff suppressed because it is too large Load Diff

@ -24,31 +24,84 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h" #include "irr_v3d.h"
#include "rollback_interface.h" #include "rollback_interface.h"
#include <list> #include <list>
#include <vector>
#include "sqlite3.h"
class IGameDef; class IGameDef;
class IRollbackManager: public IRollbackReportSink class ActionRow;
class Entity;
class RollbackManager: public IRollbackManager
{ {
public: public:
// IRollbackReportManager RollbackManager(const std::string & world_path, IGameDef * gamedef);
virtual void reportAction(const RollbackAction &action) = 0; ~RollbackManager();
virtual std::string getActor() = 0;
virtual bool isActorGuess() = 0;
virtual void setActor(const std::string &actor, bool is_guess) = 0;
virtual std::string getSuspect(v3s16 p, float nearness_shortcut,
float min_nearness) = 0;
virtual ~IRollbackManager() {} void reportAction(const RollbackAction & action_);
virtual void flush() = 0; std::string getActor();
// Get all actors that did something to position p, but not further than bool isActorGuess();
// <seconds> in history void setActor(const std::string & actor, bool is_guess);
virtual std::list<RollbackAction> getNodeActors(v3s16 pos, int range, std::string getSuspect(v3s16 p, float nearness_shortcut,
time_t seconds, int limit) = 0; float min_nearness);
// Get actions to revert <seconds> of history made by <actor> void flush();
virtual std::list<RollbackAction> getRevertActions(const std::string &actor,
time_t seconds) = 0; void addAction(const RollbackAction & action);
std::list<RollbackAction> getEntriesSince(time_t first_time);
std::list<RollbackAction> getNodeActors(v3s16 pos, int range,
time_t seconds, int limit);
std::list<RollbackAction> getRevertActions(
const std::string & actor_filter, time_t seconds);
private:
void registerNewActor(const int id, const std::string & name);
void registerNewNode(const int id, const std::string & name);
int getActorId(const std::string & name);
int getNodeId(const std::string & name);
const char * getActorName(const int id);
const char * getNodeName(const int id);
bool createTables();
void initDatabase();
bool registerRow(const ActionRow & row);
const std::list<ActionRow> actionRowsFromSelect(sqlite3_stmt * stmt);
ActionRow actionRowFromRollbackAction(const RollbackAction & action);
const std::list<RollbackAction> rollbackActionsFromActionRows(
const std::list<ActionRow> & rows);
const std::list<ActionRow> getRowsSince(time_t firstTime,
const std::string & actor);
const std::list<ActionRow> getRowsSince_range(time_t firstTime, v3s16 p,
int range, int limit);
const std::list<RollbackAction> getActionsSince_range(time_t firstTime, v3s16 p,
int range, int limit);
const std::list<RollbackAction> getActionsSince(time_t firstTime,
const std::string & actor = "");
void migrate(const std::string & filepath);
static float getSuspectNearness(bool is_guess, v3s16 suspect_p,
time_t suspect_t, v3s16 action_p, time_t action_t);
IGameDef * gamedef;
std::string current_actor;
bool current_actor_is_guess;
std::list<RollbackAction> action_todisk_buffer;
std::list<RollbackAction> action_latest_buffer;
std::string database_path;
sqlite3 * db;
sqlite3_stmt * stmt_insert;
sqlite3_stmt * stmt_replace;
sqlite3_stmt * stmt_select;
sqlite3_stmt * stmt_select_range;
sqlite3_stmt * stmt_select_withActor;
sqlite3_stmt * stmt_knownActor_select;
sqlite3_stmt * stmt_knownActor_insert;
sqlite3_stmt * stmt_knownNode_select;
sqlite3_stmt * stmt_knownNode_insert;
std::vector<Entity> knownActors;
std::vector<Entity> knownNodes;
}; };
IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef);
#endif #endif

@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
{ {
INodeDefManager *ndef = gamedef->ndef(); INodeDefManager *ndef = gamedef->ndef();
@ -42,275 +43,89 @@ RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
param1 = n.param1; param1 = n.param1;
param2 = n.param2; param2 = n.param2;
NodeMetadata *metap = map->getNodeMetadata(p); NodeMetadata *metap = map->getNodeMetadata(p);
if(metap){ if (metap) {
std::ostringstream os(std::ios::binary); std::ostringstream os(std::ios::binary);
metap->serialize(os); metap->serialize(os);
meta = os.str(); meta = os.str();
} }
} }
std::string RollbackAction::toString() const std::string RollbackAction::toString() const
{ {
switch(type){ std::ostringstream os(std::ios::binary);
case TYPE_SET_NODE: { switch (type) {
std::ostringstream os(std::ios::binary); case TYPE_SET_NODE:
os<<"[set_node"; os << "set_node " << PP(p);
os<<" "; os << ": (" << serializeJsonString(n_old.name);
os<<"("<<itos(p.X)<<","<<itos(p.Y)<<","<<itos(p.Z)<<")"; os << ", " << itos(n_old.param1);
os<<" "; os << ", " << itos(n_old.param2);
os<<serializeJsonString(n_old.name); os << ", " << serializeJsonString(n_old.meta);
os<<" "; os << ") -> (" << serializeJsonString(n_new.name);
os<<itos(n_old.param1); os << ", " << itos(n_new.param1);
os<<" "; os << ", " << itos(n_new.param2);
os<<itos(n_old.param2); os << ", " << serializeJsonString(n_new.meta);
os<<" "; os << ')';
os<<serializeJsonString(n_old.meta); case TYPE_MODIFY_INVENTORY_STACK:
os<<" "; os << "modify_inventory_stack (";
os<<serializeJsonString(n_new.name); os << serializeJsonString(inventory_location);
os<<" "; os << ", " << serializeJsonString(inventory_list);
os<<itos(n_new.param1); os << ", " << inventory_index;
os<<" "; os << ", " << (inventory_add ? "add" : "remove");
os<<itos(n_new.param2); os << ", " << serializeJsonString(inventory_stack.getItemString());
os<<" "; os << ')';
os<<serializeJsonString(n_new.meta);
os<<"]";
return os.str(); }
case TYPE_MODIFY_INVENTORY_STACK: {
std::ostringstream os(std::ios::binary);
os<<"[modify_inventory_stack";
os<<" ";
os<<serializeJsonString(inventory_location);
os<<" ";
os<<serializeJsonString(inventory_list);
os<<" ";
os<<inventory_index;
os<<" ";
os<<(inventory_add?"add":"remove");
os<<" ";
os<<serializeJsonString(inventory_stack);
os<<"]";
return os.str(); }
default: default:
return "none"; return "<unknown action>";
} }
return os.str();
} }
void RollbackAction::fromStream(std::istream &is) throw(SerializationError)
{
int c = is.get();
if(c != '['){
is.putback(c);
throw SerializationError("RollbackAction: starting [ not found");
}
std::string id;
std::getline(is, id, ' ');
if(id == "set_node")
{
c = is.get();
if(c != '('){
is.putback(c);
throw SerializationError("RollbackAction: starting ( not found");
}
// Position
std::string px_raw;
std::string py_raw;
std::string pz_raw;
std::getline(is, px_raw, ',');
std::getline(is, py_raw, ',');
std::getline(is, pz_raw, ')');
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-p ' ' not found");
}
v3s16 loaded_p(stoi(px_raw), stoi(py_raw), stoi(pz_raw));
// Old node
std::string old_name;
try{
old_name = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"old_name: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-old_name ' ' not found");
}
std::string old_p1_raw;
std::string old_p2_raw;
std::getline(is, old_p1_raw, ' ');
std::getline(is, old_p2_raw, ' ');
int old_p1 = stoi(old_p1_raw);
int old_p2 = stoi(old_p2_raw);
std::string old_meta;
try{
old_meta = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"old_meta: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-old_meta ' ' not found");
}
// New node
std::string new_name;
try{
new_name = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"new_name: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-new_name ' ' not found");
}
std::string new_p1_raw;
std::string new_p2_raw;
std::getline(is, new_p1_raw, ' ');
std::getline(is, new_p2_raw, ' ');
int new_p1 = stoi(new_p1_raw);
int new_p2 = stoi(new_p2_raw);
std::string new_meta;
try{
new_meta = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"new_meta: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ']'){
is.putback(c);
throw SerializationError("RollbackAction: after-new_meta ] not found");
}
// Set values
type = TYPE_SET_NODE;
p = loaded_p;
n_old.name = old_name;
n_old.param1 = old_p1;
n_old.param2 = old_p2;
n_old.meta = old_meta;
n_new.name = new_name;
n_new.param1 = new_p1;
n_new.param2 = new_p2;
n_new.meta = new_meta;
}
else if(id == "modify_inventory_stack")
{
// Location
std::string location;
try{
location = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"location: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-loc ' ' not found");
}
// List
std::string listname;
try{
listname = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"listname: "<<e.what()<<std::endl;
throw e;
}
c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("RollbackAction: after-list ' ' not found");
}
// Index
std::string index_raw;
std::getline(is, index_raw, ' ');
// add/remove
std::string addremove;
std::getline(is, addremove, ' ');
if(addremove != "add" && addremove != "remove"){
throw SerializationError("RollbackAction: addremove is not add or remove");
}
// Itemstring
std::string stack;
try{
stack = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"Serialization error in RollbackAction::fromStream(): "
<<"stack: "<<e.what()<<std::endl;
throw e;
}
// Set values
type = TYPE_MODIFY_INVENTORY_STACK;
inventory_location = location;
inventory_list = listname;
inventory_index = stoi(index_raw);
inventory_add = (addremove == "add");
inventory_stack = stack;
}
else
{
throw SerializationError("RollbackAction: Unknown id");
}
}
bool RollbackAction::isImportant(IGameDef *gamedef) const bool RollbackAction::isImportant(IGameDef *gamedef) const
{ {
switch(type){ if (type != TYPE_SET_NODE)
case TYPE_SET_NODE: {
// If names differ, action is always important
if(n_old.name != n_new.name)
return true;
// If metadata differs, action is always important
if(n_old.meta != n_new.meta)
return true;
INodeDefManager *ndef = gamedef->ndef();
// Both are of the same name, so a single definition is needed
const ContentFeatures &def = ndef->get(n_old.name);
// If the type is flowing liquid, action is not important
if(def.liquid_type == LIQUID_FLOWING)
return false;
// Otherwise action is important
return true; }
default:
return true; return true;
} // If names differ, action is always important
if(n_old.name != n_new.name)
return true;
// If metadata differs, action is always important
if(n_old.meta != n_new.meta)
return true;
INodeDefManager *ndef = gamedef->ndef();
// Both are of the same name, so a single definition is needed
const ContentFeatures &def = ndef->get(n_old.name);
// If the type is flowing liquid, action is not important
if (def.liquid_type == LIQUID_FLOWING)
return false;
// Otherwise action is important
return true;
} }
bool RollbackAction::getPosition(v3s16 *dst) const bool RollbackAction::getPosition(v3s16 *dst) const
{ {
switch(type){ switch (type) {
case RollbackAction::TYPE_SET_NODE: case TYPE_SET_NODE:
if(dst) *dst = p; if (dst) *dst = p;
return true; return true;
case RollbackAction::TYPE_MODIFY_INVENTORY_STACK: { case TYPE_MODIFY_INVENTORY_STACK: {
InventoryLocation loc; InventoryLocation loc;
loc.deSerialize(inventory_location); loc.deSerialize(inventory_location);
if(loc.type != InventoryLocation::NODEMETA) if (loc.type != InventoryLocation::NODEMETA) {
return false; return false;
if(dst) *dst = loc.p; }
if (dst) *dst = loc.p;
return true; } return true; }
default: default:
return false; return false;
} }
} }
bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
{ {
try{ try {
switch(type){ switch (type) {
case TYPE_NOTHING: case TYPE_NOTHING:
return true; return true;
case TYPE_SET_NODE: { case TYPE_SET_NODE: {
@ -321,40 +136,39 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
MapNode current_node = map->getNodeNoEx(p); MapNode current_node = map->getNodeNoEx(p);
std::string current_name = ndef->get(current_node).name; std::string current_name = ndef->get(current_node).name;
// If current node not the new node, it's bad // If current node not the new node, it's bad
if(current_name != n_new.name) if (current_name != n_new.name) {
return false; return false;
/*// If current node not the new node and not ignore, it's bad }
if(current_name != n_new.name && current_name != "ignore")
return false;*/
// Create rollback node // Create rollback node
MapNode n(ndef, n_old.name, n_old.param1, n_old.param2); MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
// Set rollback node // Set rollback node
try{ try {
if(!map->addNodeWithEvent(p, n)){ if (!map->addNodeWithEvent(p, n)) {
infostream<<"RollbackAction::applyRevert(): " infostream << "RollbackAction::applyRevert(): "
<<"AddNodeWithEvent failed at " << "AddNodeWithEvent failed at "
<<PP(p)<<" for "<<n_old.name<<std::endl; << PP(p) << " for " << n_old.name
<< std::endl;
return false; return false;
} }
NodeMetadata *meta = map->getNodeMetadata(p); if (n_old.meta.empty()) {
if(n_old.meta != ""){ map->removeNodeMetadata(p);
if(!meta){ } else {
NodeMetadata *meta = map->getNodeMetadata(p);
if (!meta) {
meta = new NodeMetadata(gamedef); meta = new NodeMetadata(gamedef);
if(!map->setNodeMetadata(p, meta)){ if (!map->setNodeMetadata(p, meta)) {
delete meta; delete meta;
infostream<<"RollbackAction::applyRevert(): " infostream << "RollbackAction::applyRevert(): "
<<"setNodeMetadata failed at " << "setNodeMetadata failed at "
<<PP(p)<<" for "<<n_old.name<<std::endl; << PP(p) << " for " << n_old.name
<< std::endl;
return false; return false;
} }
} }
std::istringstream is(n_old.meta, std::ios::binary); std::istringstream is(n_old.meta, std::ios::binary);
meta->deSerialize(is); meta->deSerialize(is);
} else {
map->removeNodeMetadata(p);
} }
// NOTE: This same code is in scriptapi.cpp // Inform other things that the meta data has changed
// Inform other things that the metadata has changed
v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE); v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
MapEditEvent event; MapEditEvent event;
event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
@ -362,12 +176,14 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
map->dispatchEvent(&event); map->dispatchEvent(&event);
// Set the block to be saved // Set the block to be saved
MapBlock *block = map->getBlockNoCreateNoEx(blockpos); MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
if(block) if (block) {
block->raiseModified(MOD_STATE_WRITE_NEEDED, block->raiseModified(MOD_STATE_WRITE_NEEDED,
"NodeMetaRef::reportMetadataChange"); "NodeMetaRef::reportMetadataChange");
}catch(InvalidPositionException &e){ }
infostream<<"RollbackAction::applyRevert(): " } catch (InvalidPositionException &e) {
<<"InvalidPositionException: "<<e.what()<<std::endl; infostream << "RollbackAction::applyRevert(): "
<< "InvalidPositionException: " << e.what()
<< std::endl;
return false; return false;
} }
// Success // Success
@ -375,47 +191,46 @@ bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gam
case TYPE_MODIFY_INVENTORY_STACK: { case TYPE_MODIFY_INVENTORY_STACK: {
InventoryLocation loc; InventoryLocation loc;
loc.deSerialize(inventory_location); loc.deSerialize(inventory_location);
ItemStack stack; std::string real_name = gamedef->idef()->getAlias(inventory_stack.name);
stack.deSerialize(inventory_stack, gamedef->idef());
Inventory *inv = imgr->getInventory(loc); Inventory *inv = imgr->getInventory(loc);
if(!inv){ if (!inv) {
infostream<<"RollbackAction::applyRevert(): Could not get " infostream << "RollbackAction::applyRevert(): Could not get "
"inventory at "<<inventory_location<<std::endl; "inventory at " << inventory_location << std::endl;
return false; return false;
} }
InventoryList *list = inv->getList(inventory_list); InventoryList *list = inv->getList(inventory_list);
if(!list){ if (!list) {
infostream<<"RollbackAction::applyRevert(): Could not get " infostream << "RollbackAction::applyRevert(): Could not get "
"inventory list \""<<inventory_list<<"\" in " "inventory list \"" << inventory_list << "\" in "
<<inventory_location<<std::endl; << inventory_location << std::endl;
return false; return false;
} }
if(list->getSize() <= inventory_index){ if (list->getSize() <= inventory_index) {
infostream<<"RollbackAction::applyRevert(): List index " infostream << "RollbackAction::applyRevert(): List index "
<<inventory_index<<" too large in " << inventory_index << " too large in "
<<"inventory list \""<<inventory_list<<"\" in " << "inventory list \"" << inventory_list << "\" in "
<<inventory_location<<std::endl; << inventory_location << std::endl;
} }
// If item was added, take away item, otherwise add removed item // If item was added, take away item, otherwise add removed item
if(inventory_add){ if (inventory_add) {
// Silently ignore different current item // Silently ignore different current item
if(list->getItem(inventory_index).name != stack.name) if (list->getItem(inventory_index).name != real_name)
return false; return false;
list->takeItem(inventory_index, stack.count); list->takeItem(inventory_index, inventory_stack.count);
} else { } else {
list->addItem(inventory_index, stack); list->addItem(inventory_index, inventory_stack);
} }
// Inventory was modified; send to clients // Inventory was modified; send to clients
imgr->setInventoryModified(loc); imgr->setInventoryModified(loc);
return true; } return true; }
default: default:
errorstream<<"RollbackAction::applyRevert(): type not handled" errorstream << "RollbackAction::applyRevert(): type not handled"
<<std::endl; << std::endl;
return false; return false;
} }
}catch(SerializationError &e){ } catch(SerializationError &e) {
errorstream<<"RollbackAction::applyRevert(): n_old.name="<<n_old.name errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
<<", SerializationError: "<<e.what()<<std::endl; << ", SerializationError: " << e.what() << std::endl;
} }
return false; return false;
} }

@ -23,7 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irr_v3d.h" #include "irr_v3d.h"
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <list>
#include "exceptions.h" #include "exceptions.h"
#include "inventory.h"
class Map; class Map;
class IGameDef; class IGameDef;
@ -37,15 +39,12 @@ struct RollbackNode
int param2; int param2;
std::string meta; std::string meta;
bool operator==(const RollbackNode &other) bool operator == (const RollbackNode &other)
{ {
return (name == other.name && param1 == other.param1 && return (name == other.name && param1 == other.param1 &&
param2 == other.param2 && meta == other.meta); param2 == other.param2 && meta == other.meta);
} }
bool operator!=(const RollbackNode &other) bool operator != (const RollbackNode &other) { return !(*this == other); }
{
return !(*this == other);
}
RollbackNode(): RollbackNode():
param1(0), param1(0),
@ -55,6 +54,7 @@ struct RollbackNode
RollbackNode(Map *map, v3s16 p, IGameDef *gamedef); RollbackNode(Map *map, v3s16 p, IGameDef *gamedef);
}; };
struct RollbackAction struct RollbackAction
{ {
enum Type{ enum Type{
@ -75,7 +75,7 @@ struct RollbackAction
std::string inventory_list; std::string inventory_list;
u32 inventory_index; u32 inventory_index;
bool inventory_add; bool inventory_add;
std::string inventory_stack; ItemStack inventory_stack;
RollbackAction(): RollbackAction():
type(TYPE_NOTHING), type(TYPE_NOTHING),
@ -94,7 +94,7 @@ struct RollbackAction
void setModifyInventoryStack(const std::string &inventory_location_, void setModifyInventoryStack(const std::string &inventory_location_,
const std::string &inventory_list_, int index_, const std::string &inventory_list_, int index_,
bool add_, const std::string &inventory_stack_) bool add_, const ItemStack &inventory_stack_)
{ {
type = TYPE_MODIFY_INVENTORY_STACK; type = TYPE_MODIFY_INVENTORY_STACK;
inventory_location = inventory_location_; inventory_location = inventory_location_;
@ -106,7 +106,6 @@ struct RollbackAction
// String should not contain newlines or nulls // String should not contain newlines or nulls
std::string toString() const; std::string toString() const;
void fromStream(std::istream &is) throw(SerializationError);
// Eg. flowing water level changes are not important // Eg. flowing water level changes are not important
bool isImportant(IGameDef *gamedef) const; bool isImportant(IGameDef *gamedef) const;
@ -116,41 +115,53 @@ struct RollbackAction
bool applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const; bool applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const;
}; };
class IRollbackReportSink
class IRollbackManager
{ {
public: public:
virtual ~IRollbackReportSink(){}
virtual void reportAction(const RollbackAction &action) = 0; virtual void reportAction(const RollbackAction &action) = 0;
virtual std::string getActor() = 0; virtual std::string getActor() = 0;
virtual bool isActorGuess() = 0; virtual bool isActorGuess() = 0;
virtual void setActor(const std::string &actor, bool is_guess) = 0; virtual void setActor(const std::string &actor, bool is_guess) = 0;
virtual std::string getSuspect(v3s16 p, float nearness_shortcut, virtual std::string getSuspect(v3s16 p, float nearness_shortcut,
float min_nearness) = 0; float min_nearness) = 0;
virtual ~IRollbackManager() {};
virtual void flush() = 0;
// Get all actors that did something to position p, but not further than
// <seconds> in history
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,
time_t seconds) = 0;
}; };
class RollbackScopeActor class RollbackScopeActor
{ {
public: public:
RollbackScopeActor(IRollbackReportSink *sink, const std::string &actor, RollbackScopeActor(IRollbackManager * rollback_,
bool is_guess=false): const std::string & actor, bool is_guess = false) :
m_sink(sink) rollback(rollback_)
{ {
if(m_sink){ if (rollback) {
m_actor_was = m_sink->getActor(); old_actor = rollback->getActor();
m_actor_was_guess = m_sink->isActorGuess(); old_actor_guess = rollback->isActorGuess();
m_sink->setActor(actor, is_guess); rollback->setActor(actor, is_guess);
} }
} }
~RollbackScopeActor() ~RollbackScopeActor()
{ {
if(m_sink){ if (rollback) {
m_sink->setActor(m_actor_was, m_actor_was_guess); rollback->setActor(old_actor, old_actor_guess);
} }
} }
private: private:
IRollbackReportSink *m_sink; IRollbackManager * rollback;
std::string m_actor_was; std::string old_actor;
bool m_actor_was_guess; bool old_actor_guess;
}; };
#endif #endif

@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "lua_api/l_internal.h" #include "lua_api/l_internal.h"
#include "common/c_converter.h" #include "common/c_converter.h"
#include "server.h" #include "server.h"
#include "rollback.h" #include "rollback_interface.h"
void push_RollbackNode(lua_State *L, RollbackNode &node) void push_RollbackNode(lua_State *L, RollbackNode &node)

@ -181,7 +181,6 @@ Server::Server(
this), this),
m_banmanager(NULL), m_banmanager(NULL),
m_rollback(NULL), m_rollback(NULL),
m_rollback_sink_enabled(true),
m_enable_rollback_recording(false), m_enable_rollback_recording(false),
m_emerge(NULL), m_emerge(NULL),
m_script(NULL), m_script(NULL),
@ -241,12 +240,11 @@ Server::Server(
throw ServerError("Failed to initialize world"); throw ServerError("Failed to initialize world");
// Create ban manager // Create ban manager
std::string ban_path = m_path_world+DIR_DELIM+"ipban.txt"; std::string ban_path = m_path_world + DIR_DELIM "ipban.txt";
m_banmanager = new BanManager(ban_path); m_banmanager = new BanManager(ban_path);
// Create rollback manager // Create rollback manager
std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt"; m_rollback = new RollbackManager(m_path_world, this);
m_rollback = createRollbackManager(rollback_path, this);
ModConfiguration modconf(m_path_world); ModConfiguration modconf(m_path_world);
m_mods = modconf.getMods(); m_mods = modconf.getMods();
@ -4834,8 +4832,6 @@ bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
{ {
infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl; infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
ServerMap *map = (ServerMap*)(&m_env->getMap()); ServerMap *map = (ServerMap*)(&m_env->getMap());
// Disable rollback report sink while reverting
BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
// Fail if no actions to handle // Fail if no actions to handle
if(actions.empty()){ if(actions.empty()){
@ -4915,14 +4911,6 @@ MtEventManager* Server::getEventManager()
{ {
return m_event; return m_event;
} }
IRollbackReportSink* Server::getRollbackReportSink()
{
if(!m_enable_rollback_recording)
return NULL;
if(!m_rollback_sink_enabled)
return NULL;
return m_rollback;
}
IWritableItemDefManager* Server::getWritableItemDefManager() IWritableItemDefManager* Server::getWritableItemDefManager()
{ {

@ -29,7 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mods.h" #include "mods.h"
#include "inventorymanager.h" #include "inventorymanager.h"
#include "subgame.h" #include "subgame.h"
#include "rollback_interface.h" // Needed for rollbackRevertActions()
#include "util/numeric.h" #include "util/numeric.h"
#include "util/thread.h" #include "util/thread.h"
#include "environment.h" #include "environment.h"
@ -50,6 +49,7 @@ class Inventory;
class Player; class Player;
class PlayerSAO; class PlayerSAO;
class IRollbackManager; class IRollbackManager;
class RollbackAction;
class EmergeManager; class EmergeManager;
class GameScripting; class GameScripting;
class ServerEnvironment; class ServerEnvironment;
@ -268,9 +268,6 @@ public:
// Envlock and conlock should be locked when using scriptapi // Envlock and conlock should be locked when using scriptapi
GameScripting *getScriptIface(){ return m_script; } GameScripting *getScriptIface(){ return m_script; }
// Envlock should be locked when using the rollback manager
IRollbackManager *getRollbackManager(){ return m_rollback; }
//TODO: determine what (if anything) should be locked to access EmergeManager //TODO: determine what (if anything) should be locked to access EmergeManager
EmergeManager *getEmergeManager(){ return m_emerge; } EmergeManager *getEmergeManager(){ return m_emerge; }
@ -289,9 +286,10 @@ public:
virtual u16 allocateUnknownNodeId(const std::string &name); virtual u16 allocateUnknownNodeId(const std::string &name);
virtual ISoundManager* getSoundManager(); virtual ISoundManager* getSoundManager();
virtual MtEventManager* getEventManager(); virtual MtEventManager* getEventManager();
virtual IRollbackReportSink* getRollbackReportSink();
virtual scene::ISceneManager* getSceneManager(); virtual scene::ISceneManager* getSceneManager();
virtual IRollbackManager *getRollbackManager() { return m_rollback; }
IWritableItemDefManager* getWritableItemDefManager(); IWritableItemDefManager* getWritableItemDefManager();
IWritableNodeDefManager* getWritableNodeDefManager(); IWritableNodeDefManager* getWritableNodeDefManager();
IWritableCraftDefManager* getWritableCraftDefManager(); IWritableCraftDefManager* getWritableCraftDefManager();
@ -488,7 +486,6 @@ private:
// Rollback manager (behind m_env_mutex) // Rollback manager (behind m_env_mutex)
IRollbackManager *m_rollback; IRollbackManager *m_rollback;
bool m_rollback_sink_enabled;
bool m_enable_rollback_recording; // Updated once in a while bool m_enable_rollback_recording; // Updated once in a while
// Emerge manager // Emerge manager