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"
25 #include "util/basic_macros.h"
29 #include "nodemetadata.h"
30 #include "exceptions.h"
32 #include "inventorymanager.h"
33 #include "inventory.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
;
44 NodeMetadata
*metap
= map
->getNodeMetadata(p
);
46 std::ostringstream
os(std::ios::binary
);
47 metap
->serialize(os
, 1); // FIXME: version bump??
53 std::string
RollbackAction::toString() const
55 std::ostringstream
os(std::ios::binary
);
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
);
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());
77 return "<unknown action>";
83 bool RollbackAction::isImportant(IGameDef
*gamedef
) const
85 if (type
!= TYPE_SET_NODE
)
87 // If names differ, action is always important
88 if(n_old
.name
!= n_new
.name
)
90 // If metadata differs, action is always important
91 if(n_old
.meta
!= n_new
.meta
)
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
)
99 // Otherwise action is important
104 bool RollbackAction::getPosition(v3s16
*dst
) const
110 case TYPE_MODIFY_INVENTORY_STACK
: {
111 InventoryLocation loc
;
112 loc
.deSerialize(inventory_location
);
113 if (loc
.type
!= InventoryLocation::NODEMETA
) {
116 if (dst
) *dst
= loc
.p
;
124 bool RollbackAction::applyRevert(Map
*map
, InventoryManager
*imgr
, IGameDef
*gamedef
) const
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
) {
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
147 MapNode
n(id
, n_old
.param1
, n_old
.param2
);
150 if (!map
->addNodeWithEvent(p
, n
)) {
151 infostream
<< "RollbackAction::applyRevert(): "
152 << "AddNodeWithEvent failed at "
153 << PP(p
) << " for " << n_old
.name
157 if (n_old
.meta
.empty()) {
158 map
->removeNodeMetadata(p
);
160 NodeMetadata
*meta
= map
->getNodeMetadata(p
);
162 meta
= new NodeMetadata(gamedef
->idef());
163 if (!map
->setNodeMetadata(p
, meta
)) {
165 infostream
<< "RollbackAction::applyRevert(): "
166 << "setNodeMetadata failed at "
167 << PP(p
) << " for " << n_old
.name
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
177 event
.type
= MEET_BLOCK_NODE_METADATA_CHANGED
;
179 map
->dispatchEvent(event
);
180 } catch (InvalidPositionException
&e
) {
181 infostream
<< "RollbackAction::applyRevert(): "
182 << "InvalidPositionException: " << e
.what()
188 case TYPE_MODIFY_INVENTORY_STACK
: {
189 InventoryLocation loc
;
190 loc
.deSerialize(inventory_location
);
191 Inventory
*inv
= imgr
->getInventory(loc
);
193 infostream
<< "RollbackAction::applyRevert(): Could not get "
194 "inventory at " << inventory_location
<< std::endl
;
197 InventoryList
*list
= inv
->getList(inventory_list
);
199 infostream
<< "RollbackAction::applyRevert(): Could not get "
200 "inventory list \"" << inventory_list
<< "\" in "
201 << inventory_location
<< std::endl
;
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
;
212 // If item was added, take away item, otherwise add removed item
214 // Silently ignore different current item
215 if (list
->getItem(inventory_index
).name
!=
216 gamedef
->idef()->getAlias(inventory_stack
.name
))
218 list
->takeItem(inventory_index
, inventory_stack
.count
);
220 list
->addItem(inventory_index
, inventory_stack
);
222 // Inventory was modified; send to clients
223 imgr
->setInventoryModified(loc
);
226 errorstream
<< "RollbackAction::applyRevert(): type not handled"
230 } catch(SerializationError
&e
) {
231 errorstream
<< "RollbackAction::applyRevert(): n_old.name=" << n_old
.name
232 << ", SerializationError: " << e
.what() << std::endl
;