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<<")"
38 RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
40 INodeDefManager *ndef = gamedef->ndef();
41 MapNode n = map->getNodeNoEx(p);
42 name = ndef->get(n).name;
45 NodeMetadata *metap = map->getNodeMetadata(p);
47 std::ostringstream os(std::ios::binary);
54 std::string RollbackAction::toString() const
56 std::ostringstream os(std::ios::binary);
59 os << "set_node " << PP(p);
60 os << ": (" << serializeJsonString(n_old.name);
61 os << ", " << itos(n_old.param1);
62 os << ", " << itos(n_old.param2);
63 os << ", " << serializeJsonString(n_old.meta);
64 os << ") -> (" << serializeJsonString(n_new.name);
65 os << ", " << itos(n_new.param1);
66 os << ", " << itos(n_new.param2);
67 os << ", " << serializeJsonString(n_new.meta);
69 case TYPE_MODIFY_INVENTORY_STACK:
70 os << "modify_inventory_stack (";
71 os << serializeJsonString(inventory_location);
72 os << ", " << serializeJsonString(inventory_list);
73 os << ", " << inventory_index;
74 os << ", " << (inventory_add ? "add" : "remove");
75 os << ", " << serializeJsonString(inventory_stack.getItemString());
78 return "<unknown action>";
84 bool RollbackAction::isImportant(IGameDef *gamedef) const
86 if (type != TYPE_SET_NODE)
88 // If names differ, action is always important
89 if(n_old.name != n_new.name)
91 // If metadata differs, action is always important
92 if(n_old.meta != n_new.meta)
94 INodeDefManager *ndef = gamedef->ndef();
95 // Both are of the same name, so a single definition is needed
96 const ContentFeatures &def = ndef->get(n_old.name);
97 // If the type is flowing liquid, action is not important
98 if (def.liquid_type == LIQUID_FLOWING)
100 // Otherwise action is important
105 bool RollbackAction::getPosition(v3s16 *dst) const
111 case TYPE_MODIFY_INVENTORY_STACK: {
112 InventoryLocation loc;
113 loc.deSerialize(inventory_location);
114 if (loc.type != InventoryLocation::NODEMETA) {
117 if (dst) *dst = loc.p;
125 bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
131 case TYPE_SET_NODE: {
132 INodeDefManager *ndef = gamedef->ndef();
133 // Make sure position is loaded from disk
134 map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
135 // Check current node
136 MapNode current_node = map->getNodeNoEx(p);
137 std::string current_name = ndef->get(current_node).name;
138 // If current node not the new node, it's bad
139 if (current_name != n_new.name) {
142 // Create rollback node
143 MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
146 if (!map->addNodeWithEvent(p, n)) {
147 infostream << "RollbackAction::applyRevert(): "
148 << "AddNodeWithEvent failed at "
149 << PP(p) << " for " << n_old.name
153 if (n_old.meta.empty()) {
154 map->removeNodeMetadata(p);
156 NodeMetadata *meta = map->getNodeMetadata(p);
158 meta = new NodeMetadata(gamedef);
159 if (!map->setNodeMetadata(p, meta)) {
161 infostream << "RollbackAction::applyRevert(): "
162 << "setNodeMetadata failed at "
163 << PP(p) << " for " << n_old.name
168 std::istringstream is(n_old.meta, std::ios::binary);
169 meta->deSerialize(is);
171 // Inform other things that the meta data has changed
172 v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
174 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
176 map->dispatchEvent(&event);
177 // Set the block to be saved
178 MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
180 block->raiseModified(MOD_STATE_WRITE_NEEDED,
181 MOD_REASON_REPORT_META_CHANGE);
183 } catch (InvalidPositionException &e) {
184 infostream << "RollbackAction::applyRevert(): "
185 << "InvalidPositionException: " << e.what()
191 case TYPE_MODIFY_INVENTORY_STACK: {
192 InventoryLocation loc;
193 loc.deSerialize(inventory_location);
194 std::string real_name = gamedef->idef()->getAlias(inventory_stack.name);
195 Inventory *inv = imgr->getInventory(loc);
197 infostream << "RollbackAction::applyRevert(): Could not get "
198 "inventory at " << inventory_location << std::endl;
201 InventoryList *list = inv->getList(inventory_list);
203 infostream << "RollbackAction::applyRevert(): Could not get "
204 "inventory list \"" << inventory_list << "\" in "
205 << inventory_location << std::endl;
208 if (list->getSize() <= inventory_index) {
209 infostream << "RollbackAction::applyRevert(): List index "
210 << inventory_index << " too large in "
211 << "inventory list \"" << inventory_list << "\" in "
212 << inventory_location << std::endl;
214 // If item was added, take away item, otherwise add removed item
216 // Silently ignore different current item
217 if (list->getItem(inventory_index).name != real_name)
219 list->takeItem(inventory_index, inventory_stack.count);
221 list->addItem(inventory_index, inventory_stack);
223 // Inventory was modified; send to clients
224 imgr->setInventoryModified(loc);
227 errorstream << "RollbackAction::applyRevert(): type not handled"
231 } catch(SerializationError &e) {
232 errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
233 << ", SerializationError: " << e.what() << std::endl;