| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,293 @@ | ||
| /* | ||
| Minetest-c55 | ||
| Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com> | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Lesser General Public License as published by | ||
| the Free Software Foundation; either version 2.1 of the License, or | ||
| (at your option) any later version. | ||
| This program is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public License along | ||
| with this program; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| */ | ||
|
|
||
| #include "rollback.h" | ||
| #include <fstream> | ||
| #include <list> | ||
| #include <sstream> | ||
| #include "log.h" | ||
| #include "mapnode.h" | ||
| #include "gamedef.h" | ||
| #include "nodedef.h" | ||
| #include "util/serialize.h" | ||
| #include "util/string.h" | ||
| #include "strfnd.h" | ||
| #include "inventorymanager.h" // deserializing InventoryLocations | ||
|
|
||
| #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" | ||
|
|
||
| class RollbackManager: public IRollbackManager | ||
| { | ||
| public: | ||
| // IRollbackManager interface | ||
|
|
||
| void reportAction(const RollbackAction &action_) | ||
| { | ||
| // Ignore if not important | ||
| if(!action_.isImportant(m_gamedef)) | ||
| return; | ||
| RollbackAction action = action_; | ||
| action.unix_time = time(0); | ||
| action.actor = m_current_actor; | ||
| infostream<<"RollbackManager::reportAction():" | ||
| <<" time="<<action.unix_time | ||
| <<" actor=\""<<action.actor<<"\"" | ||
| <<" action="<<action.toString() | ||
| <<std::endl; | ||
| addAction(action); | ||
| } | ||
| std::string getActor() | ||
| { | ||
| return m_current_actor; | ||
| } | ||
| void setActor(const std::string &actor) | ||
| { | ||
| m_current_actor = actor; | ||
| } | ||
| void flush() | ||
| { | ||
| infostream<<"RollbackManager::flush()"<<std::endl; | ||
| std::ofstream of(m_filepath.c_str(), std::ios::app); | ||
| if(!of.good()){ | ||
| errorstream<<"RollbackManager::flush(): Could not open file " | ||
| <<"for appending: \""<<m_filepath<<"\""<<std::endl; | ||
| return; | ||
| } | ||
| for(std::list<RollbackAction>::const_iterator | ||
| i = m_action_todisk_buffer.begin(); | ||
| i != m_action_todisk_buffer.end(); i++) | ||
| { | ||
| // Do not save stuff that does not have an actor | ||
| if(i->actor == "") | ||
| continue; | ||
| of<<i->unix_time; | ||
| of<<" "; | ||
| of<<serializeJsonString(i->actor); | ||
| of<<" "; | ||
| std::string action_s = i->toString(); | ||
| of<<action_s<<std::endl; | ||
| } | ||
| m_action_todisk_buffer.clear(); | ||
| } | ||
|
|
||
| // Other | ||
|
|
||
| RollbackManager(const std::string &filepath, IGameDef *gamedef): | ||
| m_filepath(filepath), | ||
| m_gamedef(gamedef) | ||
| { | ||
| infostream<<"RollbackManager::RollbackManager("<<filepath<<")" | ||
| <<std::endl; | ||
| } | ||
| ~RollbackManager() | ||
| { | ||
| infostream<<"RollbackManager::~RollbackManager()"<<std::endl; | ||
| flush(); | ||
| } | ||
|
|
||
| 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() >= 100) | ||
| flush(); | ||
| } | ||
|
|
||
| bool readFile(std::list<RollbackAction> &dst) | ||
| { | ||
| // Load whole file to memory | ||
| std::ifstream f(m_filepath.c_str(), std::ios::in); | ||
| if(!f.good()){ | ||
| errorstream<<"RollbackManager::readFile(): Could not open " | ||
| <<"file for reading: \""<<m_filepath<<"\""<<std::endl; | ||
| return false; | ||
| } | ||
| for(;;){ | ||
| if(f.eof() || !f.good()) | ||
| break; | ||
| std::string line; | ||
| std::getline(f, line); | ||
| line = trim(line); | ||
| if(line == "") | ||
| continue; | ||
| std::istringstream is(line); | ||
|
|
||
| try{ | ||
| std::string action_time_raw; | ||
| std::getline(is, action_time_raw, ' '); | ||
| std::string action_actor; | ||
| try{ | ||
| action_actor = deSerializeJsonString(is); | ||
| }catch(SerializationError &e){ | ||
| errorstream<<"RollbackManager: Error deserializing actor: " | ||
| <<e.what()<<std::endl; | ||
| throw e; | ||
| } | ||
| RollbackAction action; | ||
| action.unix_time = stoi(action_time_raw); | ||
| action.actor = action_actor; | ||
| int c = is.get(); | ||
| if(c != ' '){ | ||
| is.putback(c); | ||
| throw SerializationError("readFile(): second ' ' not found"); | ||
| } | ||
| action.fromStream(is); | ||
| /*infostream<<"RollbackManager::readFile(): Action from disk: " | ||
| <<action.toString()<<std::endl;*/ | ||
| dst.push_back(action); | ||
| } | ||
| catch(SerializationError &e){ | ||
| errorstream<<"RollbackManager: Error on line: "<<line<<std::endl; | ||
| errorstream<<"RollbackManager: ^ error: "<<e.what()<<std::endl; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| std::list<RollbackAction> getEntriesSince(int first_time) | ||
| { | ||
| infostream<<"RollbackManager::getEntriesSince("<<first_time<<")"<<std::endl; | ||
| // Collect enough data to this buffer | ||
| std::list<RollbackAction> action_buffer; | ||
| // Use the latest buffer if it is long enough | ||
| if(!m_action_latest_buffer.empty() && | ||
| m_action_latest_buffer.begin()->unix_time <= first_time){ | ||
| action_buffer = m_action_latest_buffer; | ||
| } | ||
| else | ||
| { | ||
| // Save all remaining stuff | ||
| flush(); | ||
| // Load whole file to memory | ||
| bool good = readFile(action_buffer); | ||
| if(!good){ | ||
| errorstream<<"RollbackManager::getEntriesSince(): Failed to" | ||
| <<" open file; using data in memory."<<std::endl; | ||
| action_buffer = m_action_latest_buffer; | ||
| } | ||
| } | ||
| return action_buffer; | ||
| } | ||
|
|
||
| std::string getLastNodeActor(v3s16 p, int range, int seconds, | ||
| v3s16 *act_p, int *act_seconds) | ||
| { | ||
| infostream<<"RollbackManager::getLastNodeActor("<<PP(p) | ||
| <<", "<<seconds<<")"<<std::endl; | ||
| // Figure out time | ||
| int cur_time = time(0); | ||
| int first_time = cur_time - seconds; | ||
|
|
||
| std::list<RollbackAction> action_buffer = getEntriesSince(first_time); | ||
|
|
||
| std::list<RollbackAction> result; | ||
|
|
||
| for(std::list<RollbackAction>::const_reverse_iterator | ||
| i = action_buffer.rbegin(); | ||
| i != action_buffer.rend(); i++) | ||
| { | ||
| if(i->unix_time < first_time) | ||
| break; | ||
|
|
||
| // Find position of action or continue | ||
| v3s16 action_p; | ||
|
|
||
| if(i->type == RollbackAction::TYPE_SET_NODE) | ||
| { | ||
| action_p = i->p; | ||
| } | ||
| else if(i->type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK) | ||
| { | ||
| InventoryLocation loc; | ||
| loc.deSerialize(i->inventory_location); | ||
| if(loc.type != InventoryLocation::NODEMETA) | ||
| continue; | ||
| action_p = loc.p; | ||
| } | ||
| else | ||
| continue; | ||
|
|
||
| if(range == 0){ | ||
| if(action_p != p) | ||
| continue; | ||
| } else { | ||
| if(abs(action_p.X - p.X) > range || | ||
| abs(action_p.Y - p.Y) > range || | ||
| abs(action_p.Z - p.Z) > range) | ||
| continue; | ||
| } | ||
|
|
||
| if(act_p) | ||
| *act_p = action_p; | ||
| if(act_seconds) | ||
| *act_seconds = cur_time - i->unix_time; | ||
| return i->actor; | ||
| } | ||
| return ""; | ||
| } | ||
|
|
||
| std::list<RollbackAction> getRevertActions(const std::string &actor_filter, | ||
| int seconds) | ||
| { | ||
| infostream<<"RollbackManager::getRevertActions("<<actor_filter | ||
| <<", "<<seconds<<")"<<std::endl; | ||
| // Figure out time | ||
| int cur_time = time(0); | ||
| int first_time = cur_time - seconds; | ||
|
|
||
| std::list<RollbackAction> action_buffer = getEntriesSince(first_time); | ||
|
|
||
| std::list<RollbackAction> result; | ||
|
|
||
| for(std::list<RollbackAction>::const_reverse_iterator | ||
| i = action_buffer.rbegin(); | ||
| i != action_buffer.rend(); i++) | ||
| { | ||
| if(i->unix_time < first_time) | ||
| break; | ||
| if(i->actor != actor_filter) | ||
| continue; | ||
| const RollbackAction &action = *i; | ||
| /*infostream<<"RollbackManager::revertAction(): Should revert" | ||
| <<" time="<<action.unix_time | ||
| <<" actor=\""<<action.actor<<"\"" | ||
| <<" action="<<action.toString() | ||
| <<std::endl;*/ | ||
| result.push_back(action); | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| private: | ||
| std::string m_filepath; | ||
| IGameDef *m_gamedef; | ||
| std::string m_current_actor; | ||
| std::list<RollbackAction> m_action_todisk_buffer; | ||
| std::list<RollbackAction> m_action_latest_buffer; | ||
| }; | ||
|
|
||
| IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef) | ||
| { | ||
| return new RollbackManager(filepath, gamedef); | ||
| } | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| /* | ||
| Minetest-c55 | ||
| Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com> | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Lesser General Public License as published by | ||
| the Free Software Foundation; either version 2.1 of the License, or | ||
| (at your option) any later version. | ||
| This program is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public License along | ||
| with this program; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| */ | ||
|
|
||
| #ifndef ROLLBACK_HEADER | ||
| #define ROLLBACK_HEADER | ||
|
|
||
| #include <string> | ||
| #include "irr_v3d.h" | ||
| #include "rollback_interface.h" | ||
| #include <list> | ||
|
|
||
| class IGameDef; | ||
|
|
||
| class IRollbackManager: public IRollbackReportSink | ||
| { | ||
| public: | ||
| // IRollbackReportManager | ||
| virtual void reportAction(const RollbackAction &action) = 0; | ||
| virtual std::string getActor() = 0; | ||
| virtual void setActor(const std::string &actor) = 0; | ||
|
|
||
| virtual ~IRollbackManager(){} | ||
| virtual void flush() = 0; | ||
| // Get last actor 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; | ||
| // Get actions to revert <seconds> of history made by <actor> | ||
| virtual std::list<RollbackAction> getRevertActions(const std::string &actor, | ||
| int seconds) = 0; | ||
| }; | ||
|
|
||
| IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *gamedef); | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,398 @@ | ||
| /* | ||
| Minetest-c55 | ||
| Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com> | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Lesser General Public License as published by | ||
| the Free Software Foundation; either version 2.1 of the License, or | ||
| (at your option) any later version. | ||
| This program is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public License along | ||
| with this program; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| */ | ||
|
|
||
| #include "rollback_interface.h" | ||
| #include <sstream> | ||
| #include "util/serialize.h" | ||
| #include "util/string.h" | ||
| #include "util/numeric.h" | ||
| #include "map.h" | ||
| #include "gamedef.h" | ||
| #include "nodedef.h" | ||
| #include "nodemetadata.h" | ||
| #include "exceptions.h" | ||
| #include "log.h" | ||
| #include "inventorymanager.h" | ||
| #include "inventory.h" | ||
| #include "mapblock.h" | ||
|
|
||
| #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" | ||
|
|
||
| RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef) | ||
| { | ||
| INodeDefManager *ndef = gamedef->ndef(); | ||
| MapNode n = map->getNodeNoEx(p); | ||
| name = ndef->get(n).name; | ||
| param1 = n.param1; | ||
| param2 = n.param2; | ||
| NodeMetadata *metap = map->getNodeMetadata(p); | ||
| if(metap){ | ||
| std::ostringstream os(std::ios::binary); | ||
| metap->serialize(os); | ||
| meta = os.str(); | ||
| } | ||
| } | ||
|
|
||
| std::string RollbackAction::toString() const | ||
| { | ||
| switch(type){ | ||
| case TYPE_SET_NODE: { | ||
| std::ostringstream os(std::ios::binary); | ||
| os<<"[set_node"; | ||
| os<<" "; | ||
| os<<"("<<itos(p.X)<<","<<itos(p.Y)<<","<<itos(p.Z)<<")"; | ||
| os<<" "; | ||
| os<<serializeJsonString(n_old.name); | ||
| os<<" "; | ||
| os<<itos(n_old.param1); | ||
| os<<" "; | ||
| os<<itos(n_old.param2); | ||
| os<<" "; | ||
| os<<serializeJsonString(n_old.meta); | ||
| os<<" "; | ||
| os<<serializeJsonString(n_new.name); | ||
| os<<" "; | ||
| os<<itos(n_new.param1); | ||
| os<<" "; | ||
| os<<itos(n_new.param2); | ||
| 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: | ||
| return "none"; | ||
| } | ||
| } | ||
|
|
||
| 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 | ||
| { | ||
| switch(type){ | ||
| 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; | ||
| } | ||
| } | ||
|
|
||
| bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const | ||
| { | ||
| try{ | ||
| switch(type){ | ||
| case TYPE_NOTHING: | ||
| return true; | ||
| case TYPE_SET_NODE: { | ||
| INodeDefManager *ndef = gamedef->ndef(); | ||
| // Make sure position is loaded from disk | ||
| map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false); | ||
| // Check current node | ||
| MapNode current_node = map->getNodeNoEx(p); | ||
| std::string current_name = ndef->get(current_node).name; | ||
| // If current node not the new node, it's bad | ||
| if(current_name != n_new.name) | ||
| return false; | ||
| /*// If current node not the new node and not ignore, it's bad | ||
| if(current_name != n_new.name && current_name != "ignore") | ||
| return false;*/ | ||
| // Create rollback node | ||
| MapNode n(ndef, n_old.name, n_old.param1, n_old.param2); | ||
| // Set rollback node | ||
| try{ | ||
| if(!map->addNodeWithEvent(p, n)){ | ||
| infostream<<"RollbackAction::applyRevert(): " | ||
| <<"AddNodeWithEvent failed at " | ||
| <<PP(p)<<" for "<<n_old.name<<std::endl; | ||
| return false; | ||
| } | ||
| NodeMetadata *meta = map->getNodeMetadata(p); | ||
| if(n_old.meta != ""){ | ||
| if(!meta){ | ||
| meta = new NodeMetadata(gamedef); | ||
| map->setNodeMetadata(p, meta); | ||
| } | ||
| std::istringstream is(n_old.meta, std::ios::binary); | ||
| meta->deSerialize(is); | ||
| } else { | ||
| map->removeNodeMetadata(p); | ||
| } | ||
| // NOTE: This same code is in scriptapi.cpp | ||
| // Inform other things that the metadata has changed | ||
| v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE); | ||
| MapEditEvent event; | ||
| event.type = MEET_BLOCK_NODE_METADATA_CHANGED; | ||
| event.p = blockpos; | ||
| map->dispatchEvent(&event); | ||
| // Set the block to be saved | ||
| MapBlock *block = map->getBlockNoCreateNoEx(blockpos); | ||
| if(block) | ||
| block->raiseModified(MOD_STATE_WRITE_NEEDED, | ||
| "NodeMetaRef::reportMetadataChange"); | ||
| }catch(InvalidPositionException &e){ | ||
| infostream<<"RollbackAction::applyRevert(): " | ||
| <<"InvalidPositionException: "<<e.what()<<std::endl; | ||
| return false; | ||
| } | ||
| // Success | ||
| return true; } | ||
| case TYPE_MODIFY_INVENTORY_STACK: { | ||
| InventoryLocation loc; | ||
| loc.deSerialize(inventory_location); | ||
| ItemStack stack; | ||
| stack.deSerialize(inventory_stack, gamedef->idef()); | ||
| Inventory *inv = imgr->getInventory(loc); | ||
| if(!inv){ | ||
| infostream<<"RollbackAction::applyRevert(): Could not get " | ||
| "inventory at "<<inventory_location<<std::endl; | ||
| return false; | ||
| } | ||
| InventoryList *list = inv->getList(inventory_list); | ||
| if(!list){ | ||
| infostream<<"RollbackAction::applyRevert(): Could not get " | ||
| "inventory list \""<<inventory_list<<"\" in " | ||
| <<inventory_location<<std::endl; | ||
| return false; | ||
| } | ||
| if(list->getSize() <= inventory_index){ | ||
| infostream<<"RollbackAction::applyRevert(): List index " | ||
| <<inventory_index<<" too large in " | ||
| <<"inventory list \""<<inventory_list<<"\" in " | ||
| <<inventory_location<<std::endl; | ||
| } | ||
| // If item was added, take away item, otherwise add removed item | ||
| if(inventory_add){ | ||
| // Silently ignore different current item | ||
| if(list->getItem(inventory_index).name != stack.name) | ||
| return false; | ||
| list->takeItem(inventory_index, stack.count); | ||
| } else { | ||
| list->addItem(inventory_index, stack); | ||
| } | ||
| // Inventory was modified; send to clients | ||
| imgr->setInventoryModified(loc); | ||
| return true; } | ||
| default: | ||
| errorstream<<"RollbackAction::applyRevert(): type not handled" | ||
| <<std::endl; | ||
| return false; | ||
| } | ||
| }catch(SerializationError &e){ | ||
| errorstream<<"RollbackAction::applyRevert(): n_old.name="<<n_old.name | ||
| <<", SerializationError: "<<e.what()<<std::endl; | ||
| } | ||
| return false; | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| /* | ||
| Minetest-c55 | ||
| Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com> | ||
| This program is free software; you can redistribute it and/or modify | ||
| it under the terms of the GNU Lesser General Public License as published by | ||
| the Free Software Foundation; either version 2.1 of the License, or | ||
| (at your option) any later version. | ||
| This program is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| GNU Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public License along | ||
| with this program; if not, write to the Free Software Foundation, Inc., | ||
| 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||
| */ | ||
|
|
||
| #ifndef ROLLBACK_INTERFACE_HEADER | ||
| #define ROLLBACK_INTERFACE_HEADER | ||
|
|
||
| #include "irr_v3d.h" | ||
| #include <string> | ||
| #include <iostream> | ||
| #include "exceptions.h" | ||
|
|
||
| class Map; | ||
| class IGameDef; | ||
| struct MapNode; | ||
| class InventoryManager; | ||
|
|
||
| struct RollbackNode | ||
| { | ||
| std::string name; | ||
| int param1; | ||
| int param2; | ||
| std::string meta; | ||
|
|
||
| bool operator==(const RollbackNode &other) | ||
| { | ||
| return (name == other.name && param1 == other.param1 && | ||
| param2 == other.param2 && meta == other.meta); | ||
| } | ||
| bool operator!=(const RollbackNode &other) | ||
| { | ||
| return !(*this == other); | ||
| } | ||
|
|
||
| RollbackNode(): | ||
| param1(0), | ||
| param2(0) | ||
| {} | ||
|
|
||
| RollbackNode(Map *map, v3s16 p, IGameDef *gamedef); | ||
| }; | ||
|
|
||
| struct RollbackAction | ||
| { | ||
| enum Type{ | ||
| TYPE_NOTHING, | ||
| TYPE_SET_NODE, | ||
| TYPE_MODIFY_INVENTORY_STACK, | ||
| } type; | ||
|
|
||
| int unix_time; | ||
| std::string actor; | ||
|
|
||
| v3s16 p; | ||
| RollbackNode n_old; | ||
| RollbackNode n_new; | ||
|
|
||
| std::string inventory_location; | ||
| std::string inventory_list; | ||
| u32 inventory_index; | ||
| bool inventory_add; | ||
| std::string inventory_stack; | ||
|
|
||
| RollbackAction(): | ||
| type(TYPE_NOTHING) | ||
| {} | ||
|
|
||
| void setSetNode(v3s16 p_, const RollbackNode &n_old_, | ||
| const RollbackNode &n_new_) | ||
| { | ||
| type = TYPE_SET_NODE; | ||
| p = p_; | ||
| n_old = n_old_; | ||
| n_new = n_new_; | ||
| } | ||
|
|
||
| void setModifyInventoryStack(const std::string &inventory_location_, | ||
| const std::string &inventory_list_, int index_, | ||
| bool add_, const std::string &inventory_stack_) | ||
| { | ||
| type = TYPE_MODIFY_INVENTORY_STACK; | ||
| inventory_location = inventory_location_; | ||
| inventory_list = inventory_list_; | ||
| inventory_index = index_; | ||
| inventory_add = add_; | ||
| inventory_stack = inventory_stack_; | ||
| } | ||
|
|
||
| // String should not contain newlines or nulls | ||
| std::string toString() const; | ||
| void fromStream(std::istream &is) throw(SerializationError); | ||
|
|
||
| // Eg. flowing water level changes are not important | ||
| bool isImportant(IGameDef *gamedef) const; | ||
|
|
||
| bool applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const; | ||
| }; | ||
|
|
||
| class IRollbackReportSink | ||
| { | ||
| public: | ||
| virtual ~IRollbackReportSink(){} | ||
| virtual void reportAction(const RollbackAction &action) = 0; | ||
| virtual std::string getActor() = 0; | ||
| virtual void setActor(const std::string &actor) = 0; | ||
| }; | ||
|
|
||
| class RollbackScopeActor | ||
| { | ||
| public: | ||
| RollbackScopeActor(IRollbackReportSink *sink, const std::string &actor): | ||
| m_sink(sink) | ||
| { | ||
| if(m_sink){ | ||
| m_actor_was = m_sink->getActor(); | ||
| m_sink->setActor(actor); | ||
| } | ||
| } | ||
| ~RollbackScopeActor() | ||
| { | ||
| if(m_sink){ | ||
| m_sink->setActor(m_actor_was); | ||
| } | ||
| } | ||
| private: | ||
| IRollbackReportSink *m_sink; | ||
| std::string m_actor_was; | ||
| }; | ||
|
|
||
| #endif |