cleanup
[waspsaliva.git] / src / rollback_interface.cpp
blobc00206e984592a53f167ced313544b672dbbbcdc
1 /*
2 Minetest
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"
21 #include <sstream>
22 #include "util/serialize.h"
23 #include "util/string.h"
24 #include "util/numeric.h"
25 #include "util/basic_macros.h"
26 #include "map.h"
27 #include "gamedef.h"
28 #include "nodedef.h"
29 #include "nodemetadata.h"
30 #include "exceptions.h"
31 #include "log.h"
32 #include "inventorymanager.h"
33 #include "inventory.h"
34 #include "mapblock.h"
37 RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
39 const NodeDefManager *ndef = gamedef->ndef();
40 MapNode n = map->getNode(p);
41 name = ndef->get(n).name;
42 param1 = n.param1;
43 param2 = n.param2;
44 NodeMetadata *metap = map->getNodeMetadata(p);
45 if (metap) {
46 std::ostringstream os(std::ios::binary);
47 metap->serialize(os, 1); // FIXME: version bump??
48 meta = os.str();
53 std::string RollbackAction::toString() const
55 std::ostringstream os(std::ios::binary);
56 switch (type) {
57 case TYPE_SET_NODE:
58 os << "set_node " << PP(p);
59 os << ": (" << serializeJsonString(n_old.name);
60 os << ", " << itos(n_old.param1);
61 os << ", " << itos(n_old.param2);
62 os << ", " << serializeJsonString(n_old.meta);
63 os << ") -> (" << serializeJsonString(n_new.name);
64 os << ", " << itos(n_new.param1);
65 os << ", " << itos(n_new.param2);
66 os << ", " << serializeJsonString(n_new.meta);
67 os << ')';
68 case TYPE_MODIFY_INVENTORY_STACK:
69 os << "modify_inventory_stack (";
70 os << serializeJsonString(inventory_location);
71 os << ", " << serializeJsonString(inventory_list);
72 os << ", " << inventory_index;
73 os << ", " << (inventory_add ? "add" : "remove");
74 os << ", " << serializeJsonString(inventory_stack.getItemString());
75 os << ')';
76 default:
77 return "<unknown action>";
79 return os.str();
83 bool RollbackAction::isImportant(IGameDef *gamedef) const
85 if (type != TYPE_SET_NODE)
86 return true;
87 // If names differ, action is always important
88 if(n_old.name != n_new.name)
89 return true;
90 // If metadata differs, action is always important
91 if(n_old.meta != n_new.meta)
92 return true;
93 const NodeDefManager *ndef = gamedef->ndef();
94 // Both are of the same name, so a single definition is needed
95 const ContentFeatures &def = ndef->get(n_old.name);
96 // If the type is flowing liquid, action is not important
97 if (def.liquid_type == LIQUID_FLOWING)
98 return false;
99 // Otherwise action is important
100 return true;
104 bool RollbackAction::getPosition(v3s16 *dst) const
106 switch (type) {
107 case TYPE_SET_NODE:
108 if (dst) *dst = p;
109 return true;
110 case TYPE_MODIFY_INVENTORY_STACK: {
111 InventoryLocation loc;
112 loc.deSerialize(inventory_location);
113 if (loc.type != InventoryLocation::NODEMETA) {
114 return false;
116 if (dst) *dst = loc.p;
117 return true; }
118 default:
119 return false;
124 bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
126 try {
127 switch (type) {
128 case TYPE_NOTHING:
129 return true;
130 case TYPE_SET_NODE: {
131 const NodeDefManager *ndef = gamedef->ndef();
132 // Make sure position is loaded from disk
133 map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
134 // Check current node
135 MapNode current_node = map->getNode(p);
136 std::string current_name = ndef->get(current_node).name;
137 // If current node not the new node, it's bad
138 if (current_name != n_new.name) {
139 return false;
141 // Create rollback node
142 content_t id = CONTENT_IGNORE;
143 if (!ndef->getId(n_old.name, id)) {
144 // The old node is not registered
145 return false;
147 MapNode n(id, n_old.param1, n_old.param2);
148 // Set rollback node
149 try {
150 if (!map->addNodeWithEvent(p, n)) {
151 infostream << "RollbackAction::applyRevert(): "
152 << "AddNodeWithEvent failed at "
153 << PP(p) << " for " << n_old.name
154 << std::endl;
155 return false;
157 if (n_old.meta.empty()) {
158 map->removeNodeMetadata(p);
159 } else {
160 NodeMetadata *meta = map->getNodeMetadata(p);
161 if (!meta) {
162 meta = new NodeMetadata(gamedef->idef());
163 if (!map->setNodeMetadata(p, meta)) {
164 delete meta;
165 infostream << "RollbackAction::applyRevert(): "
166 << "setNodeMetadata failed at "
167 << PP(p) << " for " << n_old.name
168 << std::endl;
169 return false;
172 std::istringstream is(n_old.meta, std::ios::binary);
173 meta->deSerialize(is, 1); // FIXME: version bump??
175 // Inform other things that the meta data has changed
176 MapEditEvent event;
177 event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
178 event.p = p;
179 map->dispatchEvent(event);
180 } catch (InvalidPositionException &e) {
181 infostream << "RollbackAction::applyRevert(): "
182 << "InvalidPositionException: " << e.what()
183 << std::endl;
184 return false;
186 // Success
187 return true; }
188 case TYPE_MODIFY_INVENTORY_STACK: {
189 InventoryLocation loc;
190 loc.deSerialize(inventory_location);
191 Inventory *inv = imgr->getInventory(loc);
192 if (!inv) {
193 infostream << "RollbackAction::applyRevert(): Could not get "
194 "inventory at " << inventory_location << std::endl;
195 return false;
197 InventoryList *list = inv->getList(inventory_list);
198 if (!list) {
199 infostream << "RollbackAction::applyRevert(): Could not get "
200 "inventory list \"" << inventory_list << "\" in "
201 << inventory_location << std::endl;
202 return false;
204 if (list->getSize() <= inventory_index) {
205 infostream << "RollbackAction::applyRevert(): List index "
206 << inventory_index << " too large in "
207 << "inventory list \"" << inventory_list << "\" in "
208 << inventory_location << std::endl;
209 return false;
212 // If item was added, take away item, otherwise add removed item
213 if (inventory_add) {
214 // Silently ignore different current item
215 if (list->getItem(inventory_index).name !=
216 gamedef->idef()->getAlias(inventory_stack.name))
217 return false;
218 list->takeItem(inventory_index, inventory_stack.count);
219 } else {
220 list->addItem(inventory_index, inventory_stack);
222 // Inventory was modified; send to clients
223 imgr->setInventoryModified(loc);
224 return true; }
225 default:
226 errorstream << "RollbackAction::applyRevert(): type not handled"
227 << std::endl;
228 return false;
230 } catch(SerializationError &e) {
231 errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
232 << ", SerializationError: " << e.what() << std::endl;
234 return false;