summaryrefslogtreecommitdiff
path: root/src/rollback.cpp
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2012-07-26 22:06:45 +0300
committerPerttu Ahola <celeron55@gmail.com>2012-07-27 02:27:18 +0300
commit0190f9b077dcb2b8cb41c622dd91ffc1e04dacac (patch)
tree0baa7a5d2094afdbb193e1850836c209c9395c70 /src/rollback.cpp
parent0c91a0d59db70f3f502004d4c37fecd4e10c9401 (diff)
downloadminetest-0190f9b077dcb2b8cb41c622dd91ffc1e04dacac.tar.gz
minetest-0190f9b077dcb2b8cb41c622dd91ffc1e04dacac.tar.bz2
minetest-0190f9b077dcb2b8cb41c622dd91ffc1e04dacac.zip
Experimental-ish rollback functionality
Diffstat (limited to 'src/rollback.cpp')
-rw-r--r--src/rollback.cpp293
1 files changed, 293 insertions, 0 deletions
diff --git a/src/rollback.cpp b/src/rollback.cpp
new file mode 100644
index 000000000..3fe791050
--- /dev/null
+++ b/src/rollback.cpp
@@ -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);
+}
+
+