3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "rollback_interface.h"
22 #include "util/serialize.h"
23 #include "util/string.h"
24 #include "util/numeric.h"
28 #include "nodemetadata.h"
29 #include "exceptions.h"
31 #include "inventorymanager.h"
32 #include "inventory.h"
35 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
37 RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
39 INodeDefManager *ndef = gamedef->ndef();
40 MapNode n = map->getNodeNoEx(p);
41 name = ndef->get(n).name;
44 NodeMetadata *metap = map->getNodeMetadata(p);
46 std::ostringstream os(std::ios::binary);
52 std::string RollbackAction::toString() const
56 std::ostringstream os(std::ios::binary);
59 os<<"("<<itos(p.X)<<","<<itos(p.Y)<<","<<itos(p.Z)<<")";
61 os<<serializeJsonString(n_old.name);
63 os<<itos(n_old.param1);
65 os<<itos(n_old.param2);
67 os<<serializeJsonString(n_old.meta);
69 os<<serializeJsonString(n_new.name);
71 os<<itos(n_new.param1);
73 os<<itos(n_new.param2);
75 os<<serializeJsonString(n_new.meta);
78 case TYPE_MODIFY_INVENTORY_STACK: {
79 std::ostringstream os(std::ios::binary);
80 os<<"[modify_inventory_stack";
82 os<<serializeJsonString(inventory_location);
84 os<<serializeJsonString(inventory_list);
88 os<<(inventory_add?"add":"remove");
90 os<<serializeJsonString(inventory_stack);
98 void RollbackAction::fromStream(std::istream &is) throw(SerializationError)
103 throw SerializationError("RollbackAction: starting [ not found");
107 std::getline(is, id, ' ');
114 throw SerializationError("RollbackAction: starting ( not found");
120 std::getline(is, px_raw, ',');
121 std::getline(is, py_raw, ',');
122 std::getline(is, pz_raw, ')');
126 throw SerializationError("RollbackAction: after-p ' ' not found");
128 v3s16 loaded_p(stoi(px_raw), stoi(py_raw), stoi(pz_raw));
130 std::string old_name;
132 old_name = deSerializeJsonString(is);
133 }catch(SerializationError &e){
134 errorstream<<"Serialization error in RollbackAction::fromStream(): "
135 <<"old_name: "<<e.what()<<std::endl;
141 throw SerializationError("RollbackAction: after-old_name ' ' not found");
143 std::string old_p1_raw;
144 std::string old_p2_raw;
145 std::getline(is, old_p1_raw, ' ');
146 std::getline(is, old_p2_raw, ' ');
147 int old_p1 = stoi(old_p1_raw);
148 int old_p2 = stoi(old_p2_raw);
149 std::string old_meta;
151 old_meta = deSerializeJsonString(is);
152 }catch(SerializationError &e){
153 errorstream<<"Serialization error in RollbackAction::fromStream(): "
154 <<"old_meta: "<<e.what()<<std::endl;
160 throw SerializationError("RollbackAction: after-old_meta ' ' not found");
163 std::string new_name;
165 new_name = deSerializeJsonString(is);
166 }catch(SerializationError &e){
167 errorstream<<"Serialization error in RollbackAction::fromStream(): "
168 <<"new_name: "<<e.what()<<std::endl;
174 throw SerializationError("RollbackAction: after-new_name ' ' not found");
176 std::string new_p1_raw;
177 std::string new_p2_raw;
178 std::getline(is, new_p1_raw, ' ');
179 std::getline(is, new_p2_raw, ' ');
180 int new_p1 = stoi(new_p1_raw);
181 int new_p2 = stoi(new_p2_raw);
182 std::string new_meta;
184 new_meta = deSerializeJsonString(is);
185 }catch(SerializationError &e){
186 errorstream<<"Serialization error in RollbackAction::fromStream(): "
187 <<"new_meta: "<<e.what()<<std::endl;
193 throw SerializationError("RollbackAction: after-new_meta ] not found");
196 type = TYPE_SET_NODE;
198 n_old.name = old_name;
199 n_old.param1 = old_p1;
200 n_old.param2 = old_p2;
201 n_old.meta = old_meta;
202 n_new.name = new_name;
203 n_new.param1 = new_p1;
204 n_new.param2 = new_p2;
205 n_new.meta = new_meta;
207 else if(id == "modify_inventory_stack")
210 std::string location;
212 location = deSerializeJsonString(is);
213 }catch(SerializationError &e){
214 errorstream<<"Serialization error in RollbackAction::fromStream(): "
215 <<"location: "<<e.what()<<std::endl;
221 throw SerializationError("RollbackAction: after-loc ' ' not found");
224 std::string listname;
226 listname = deSerializeJsonString(is);
227 }catch(SerializationError &e){
228 errorstream<<"Serialization error in RollbackAction::fromStream(): "
229 <<"listname: "<<e.what()<<std::endl;
235 throw SerializationError("RollbackAction: after-list ' ' not found");
238 std::string index_raw;
239 std::getline(is, index_raw, ' ');
241 std::string addremove;
242 std::getline(is, addremove, ' ');
243 if(addremove != "add" && addremove != "remove"){
244 throw SerializationError("RollbackAction: addremove is not add or remove");
249 stack = deSerializeJsonString(is);
250 }catch(SerializationError &e){
251 errorstream<<"Serialization error in RollbackAction::fromStream(): "
252 <<"stack: "<<e.what()<<std::endl;
256 type = TYPE_MODIFY_INVENTORY_STACK;
257 inventory_location = location;
258 inventory_list = listname;
259 inventory_index = stoi(index_raw);
260 inventory_add = (addremove == "add");
261 inventory_stack = stack;
265 throw SerializationError("RollbackAction: Unknown id");
269 bool RollbackAction::isImportant(IGameDef *gamedef) const
272 case TYPE_SET_NODE: {
273 // If names differ, action is always important
274 if(n_old.name != n_new.name)
276 // If metadata differs, action is always important
277 if(n_old.meta != n_new.meta)
279 INodeDefManager *ndef = gamedef->ndef();
280 // Both are of the same name, so a single definition is needed
281 const ContentFeatures &def = ndef->get(n_old.name);
282 // If the type is flowing liquid, action is not important
283 if(def.liquid_type == LIQUID_FLOWING)
285 // Otherwise action is important
292 bool RollbackAction::getPosition(v3s16 *dst) const
295 case RollbackAction::TYPE_SET_NODE:
298 case RollbackAction::TYPE_MODIFY_INVENTORY_STACK: {
299 InventoryLocation loc;
300 loc.deSerialize(inventory_location);
301 if(loc.type != InventoryLocation::NODEMETA)
303 if(dst) *dst = loc.p;
310 bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
316 case TYPE_SET_NODE: {
317 INodeDefManager *ndef = gamedef->ndef();
318 // Make sure position is loaded from disk
319 map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
320 // Check current node
321 MapNode current_node = map->getNodeNoEx(p);
322 std::string current_name = ndef->get(current_node).name;
323 // If current node not the new node, it's bad
324 if(current_name != n_new.name)
326 /*// If current node not the new node and not ignore, it's bad
327 if(current_name != n_new.name && current_name != "ignore")
329 // Create rollback node
330 MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
333 if(!map->addNodeWithEvent(p, n)){
334 infostream<<"RollbackAction::applyRevert(): "
335 <<"AddNodeWithEvent failed at "
336 <<PP(p)<<" for "<<n_old.name<<std::endl;
339 NodeMetadata *meta = map->getNodeMetadata(p);
340 if(n_old.meta != ""){
342 meta = new NodeMetadata(gamedef);
343 map->setNodeMetadata(p, meta);
345 std::istringstream is(n_old.meta, std::ios::binary);
346 meta->deSerialize(is);
348 map->removeNodeMetadata(p);
350 // NOTE: This same code is in scriptapi.cpp
351 // Inform other things that the metadata has changed
352 v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
354 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
356 map->dispatchEvent(&event);
357 // Set the block to be saved
358 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
360 block->raiseModified(MOD_STATE_WRITE_NEEDED,
361 "NodeMetaRef::reportMetadataChange");
362 }catch(InvalidPositionException &e){
363 infostream<<"RollbackAction::applyRevert(): "
364 <<"InvalidPositionException: "<<e.what()<<std::endl;
369 case TYPE_MODIFY_INVENTORY_STACK: {
370 InventoryLocation loc;
371 loc.deSerialize(inventory_location);
373 stack.deSerialize(inventory_stack, gamedef->idef());
374 Inventory *inv = imgr->getInventory(loc);
376 infostream<<"RollbackAction::applyRevert(): Could not get "
377 "inventory at "<<inventory_location<<std::endl;
380 InventoryList *list = inv->getList(inventory_list);
382 infostream<<"RollbackAction::applyRevert(): Could not get "
383 "inventory list \""<<inventory_list<<"\" in "
384 <<inventory_location<<std::endl;
387 if(list->getSize() <= inventory_index){
388 infostream<<"RollbackAction::applyRevert(): List index "
389 <<inventory_index<<" too large in "
390 <<"inventory list \""<<inventory_list<<"\" in "
391 <<inventory_location<<std::endl;
393 // If item was added, take away item, otherwise add removed item
395 // Silently ignore different current item
396 if(list->getItem(inventory_index).name != stack.name)
398 list->takeItem(inventory_index, stack.count);
400 list->addItem(inventory_index, stack);
402 // Inventory was modified; send to clients
403 imgr->setInventoryModified(loc);
406 errorstream<<"RollbackAction::applyRevert(): type not handled"
410 }catch(SerializationError &e){
411 errorstream<<"RollbackAction::applyRevert(): n_old.name="<<n_old.name
412 <<", SerializationError: "<<e.what()<<std::endl;