Line data Source code
1 : /*
2 : Minetest
3 : Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 :
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.
9 :
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.
14 :
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.
18 : */
19 :
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 "map.h"
26 : #include "gamedef.h"
27 : #include "nodedef.h"
28 : #include "nodemetadata.h"
29 : #include "exceptions.h"
30 : #include "log.h"
31 : #include "inventorymanager.h"
32 : #include "inventory.h"
33 : #include "mapblock.h"
34 :
35 : #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
36 :
37 :
38 55 : RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
39 : {
40 55 : INodeDefManager *ndef = gamedef->ndef();
41 55 : MapNode n = map->getNodeNoEx(p);
42 55 : name = ndef->get(n).name;
43 55 : param1 = n.param1;
44 55 : param2 = n.param2;
45 55 : NodeMetadata *metap = map->getNodeMetadata(p);
46 55 : if (metap) {
47 46 : std::ostringstream os(std::ios::binary);
48 23 : metap->serialize(os);
49 23 : meta = os.str();
50 : }
51 55 : }
52 :
53 :
54 0 : std::string RollbackAction::toString() const
55 : {
56 0 : std::ostringstream os(std::ios::binary);
57 0 : switch (type) {
58 : case TYPE_SET_NODE:
59 0 : os << "set_node " << PP(p);
60 0 : os << ": (" << serializeJsonString(n_old.name);
61 0 : os << ", " << itos(n_old.param1);
62 0 : os << ", " << itos(n_old.param2);
63 0 : os << ", " << serializeJsonString(n_old.meta);
64 0 : os << ") -> (" << serializeJsonString(n_new.name);
65 0 : os << ", " << itos(n_new.param1);
66 0 : os << ", " << itos(n_new.param2);
67 0 : os << ", " << serializeJsonString(n_new.meta);
68 0 : os << ')';
69 : case TYPE_MODIFY_INVENTORY_STACK:
70 0 : os << "modify_inventory_stack (";
71 0 : os << serializeJsonString(inventory_location);
72 0 : os << ", " << serializeJsonString(inventory_list);
73 0 : os << ", " << inventory_index;
74 0 : os << ", " << (inventory_add ? "add" : "remove");
75 0 : os << ", " << serializeJsonString(inventory_stack.getItemString());
76 0 : os << ')';
77 : default:
78 0 : return "<unknown action>";
79 : }
80 : return os.str();
81 : }
82 :
83 :
84 0 : bool RollbackAction::isImportant(IGameDef *gamedef) const
85 : {
86 0 : if (type != TYPE_SET_NODE)
87 0 : return true;
88 : // If names differ, action is always important
89 0 : if(n_old.name != n_new.name)
90 0 : return true;
91 : // If metadata differs, action is always important
92 0 : if(n_old.meta != n_new.meta)
93 0 : return true;
94 0 : INodeDefManager *ndef = gamedef->ndef();
95 : // Both are of the same name, so a single definition is needed
96 0 : const ContentFeatures &def = ndef->get(n_old.name);
97 : // If the type is flowing liquid, action is not important
98 0 : if (def.liquid_type == LIQUID_FLOWING)
99 0 : return false;
100 : // Otherwise action is important
101 0 : return true;
102 : }
103 :
104 :
105 0 : bool RollbackAction::getPosition(v3s16 *dst) const
106 : {
107 0 : switch (type) {
108 : case TYPE_SET_NODE:
109 0 : if (dst) *dst = p;
110 0 : return true;
111 : case TYPE_MODIFY_INVENTORY_STACK: {
112 0 : InventoryLocation loc;
113 0 : loc.deSerialize(inventory_location);
114 0 : if (loc.type != InventoryLocation::NODEMETA) {
115 0 : return false;
116 : }
117 0 : if (dst) *dst = loc.p;
118 0 : return true; }
119 : default:
120 0 : return false;
121 : }
122 : }
123 :
124 :
125 0 : bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
126 : {
127 : try {
128 0 : switch (type) {
129 : case TYPE_NOTHING:
130 0 : return true;
131 : case TYPE_SET_NODE: {
132 0 : INodeDefManager *ndef = gamedef->ndef();
133 : // Make sure position is loaded from disk
134 0 : map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
135 : // Check current node
136 0 : MapNode current_node = map->getNodeNoEx(p);
137 0 : std::string current_name = ndef->get(current_node).name;
138 : // If current node not the new node, it's bad
139 0 : if (current_name != n_new.name) {
140 0 : return false;
141 : }
142 : // Create rollback node
143 0 : MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
144 : // Set rollback node
145 : try {
146 0 : if (!map->addNodeWithEvent(p, n)) {
147 0 : infostream << "RollbackAction::applyRevert(): "
148 0 : << "AddNodeWithEvent failed at "
149 0 : << PP(p) << " for " << n_old.name
150 0 : << std::endl;
151 0 : return false;
152 : }
153 0 : if (n_old.meta.empty()) {
154 0 : map->removeNodeMetadata(p);
155 : } else {
156 0 : NodeMetadata *meta = map->getNodeMetadata(p);
157 0 : if (!meta) {
158 0 : meta = new NodeMetadata(gamedef);
159 0 : if (!map->setNodeMetadata(p, meta)) {
160 0 : delete meta;
161 0 : infostream << "RollbackAction::applyRevert(): "
162 0 : << "setNodeMetadata failed at "
163 0 : << PP(p) << " for " << n_old.name
164 0 : << std::endl;
165 0 : return false;
166 : }
167 : }
168 0 : std::istringstream is(n_old.meta, std::ios::binary);
169 0 : meta->deSerialize(is);
170 : }
171 : // Inform other things that the meta data has changed
172 0 : v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
173 0 : MapEditEvent event;
174 0 : event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
175 0 : event.p = blockpos;
176 0 : map->dispatchEvent(&event);
177 : // Set the block to be saved
178 0 : MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
179 0 : if (block) {
180 : block->raiseModified(MOD_STATE_WRITE_NEEDED,
181 0 : MOD_REASON_REPORT_META_CHANGE);
182 : }
183 0 : } catch (InvalidPositionException &e) {
184 0 : infostream << "RollbackAction::applyRevert(): "
185 0 : << "InvalidPositionException: " << e.what()
186 0 : << std::endl;
187 0 : return false;
188 : }
189 : // Success
190 0 : return true; }
191 : case TYPE_MODIFY_INVENTORY_STACK: {
192 0 : InventoryLocation loc;
193 0 : loc.deSerialize(inventory_location);
194 0 : std::string real_name = gamedef->idef()->getAlias(inventory_stack.name);
195 0 : Inventory *inv = imgr->getInventory(loc);
196 0 : if (!inv) {
197 : infostream << "RollbackAction::applyRevert(): Could not get "
198 0 : "inventory at " << inventory_location << std::endl;
199 0 : return false;
200 : }
201 0 : InventoryList *list = inv->getList(inventory_list);
202 0 : if (!list) {
203 : infostream << "RollbackAction::applyRevert(): Could not get "
204 0 : "inventory list \"" << inventory_list << "\" in "
205 0 : << inventory_location << std::endl;
206 0 : return false;
207 : }
208 0 : if (list->getSize() <= inventory_index) {
209 0 : infostream << "RollbackAction::applyRevert(): List index "
210 0 : << inventory_index << " too large in "
211 0 : << "inventory list \"" << inventory_list << "\" in "
212 0 : << inventory_location << std::endl;
213 : }
214 : // If item was added, take away item, otherwise add removed item
215 0 : if (inventory_add) {
216 : // Silently ignore different current item
217 0 : if (list->getItem(inventory_index).name != real_name)
218 0 : return false;
219 0 : list->takeItem(inventory_index, inventory_stack.count);
220 : } else {
221 0 : list->addItem(inventory_index, inventory_stack);
222 : }
223 : // Inventory was modified; send to clients
224 0 : imgr->setInventoryModified(loc);
225 0 : return true; }
226 : default:
227 0 : errorstream << "RollbackAction::applyRevert(): type not handled"
228 0 : << std::endl;
229 0 : return false;
230 : }
231 0 : } catch(SerializationError &e) {
232 0 : errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
233 0 : << ", SerializationError: " << e.what() << std::endl;
234 : }
235 0 : return false;
236 3 : }
237 :
|