rollback_interface.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "rollback_interface.h"
  17. #include <sstream>
  18. #include "util/serialize.h"
  19. #include "util/string.h"
  20. #include "util/numeric.h"
  21. #include "util/basic_macros.h"
  22. #include "map.h"
  23. #include "gamedef.h"
  24. #include "nodedef.h"
  25. #include "nodemetadata.h"
  26. #include "exceptions.h"
  27. #include "log.h"
  28. #include "inventorymanager.h"
  29. #include "inventory.h"
  30. #include "mapblock.h"
  31. RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
  32. {
  33. const NodeDefManager *ndef = gamedef->ndef();
  34. MapNode n = map->getNode(p);
  35. name = ndef->get(n).name;
  36. param1 = n.param1;
  37. param2 = n.param2;
  38. NodeMetadata *metap = map->getNodeMetadata(p);
  39. if (metap) {
  40. std::ostringstream os(std::ios::binary);
  41. metap->serialize(os, 1); // FIXME: version bump??
  42. meta = os.str();
  43. }
  44. }
  45. std::string RollbackAction::toString() const
  46. {
  47. std::ostringstream os(std::ios::binary);
  48. switch (type) {
  49. case TYPE_SET_NODE:
  50. os << "set_node " << PP(p);
  51. os << ": (" << serializeJsonString(n_old.name);
  52. os << ", " << itos(n_old.param1);
  53. os << ", " << itos(n_old.param2);
  54. os << ", " << serializeJsonString(n_old.meta);
  55. os << ") -> (" << serializeJsonString(n_new.name);
  56. os << ", " << itos(n_new.param1);
  57. os << ", " << itos(n_new.param2);
  58. os << ", " << serializeJsonString(n_new.meta);
  59. os << ')';
  60. case TYPE_MODIFY_INVENTORY_STACK:
  61. os << "modify_inventory_stack (";
  62. os << serializeJsonString(inventory_location);
  63. os << ", " << serializeJsonString(inventory_list);
  64. os << ", " << inventory_index;
  65. os << ", " << (inventory_add ? "add" : "remove");
  66. os << ", " << serializeJsonString(inventory_stack.getItemString());
  67. os << ')';
  68. default:
  69. return "<unknown action>";
  70. }
  71. return os.str();
  72. }
  73. bool RollbackAction::isImportant(IGameDef *gamedef) const
  74. {
  75. if (type != TYPE_SET_NODE)
  76. return true;
  77. // If names differ, action is always important
  78. if(n_old.name != n_new.name)
  79. return true;
  80. // If metadata differs, action is always important
  81. if(n_old.meta != n_new.meta)
  82. return true;
  83. const NodeDefManager *ndef = gamedef->ndef();
  84. // Both are of the same name, so a single definition is needed
  85. const ContentFeatures &def = ndef->get(n_old.name);
  86. // If the type is flowing liquid, action is not important
  87. if (def.liquid_type == LIQUID_FLOWING)
  88. return false;
  89. // Otherwise action is important
  90. return true;
  91. }
  92. bool RollbackAction::getPosition(v3s16 *dst) const
  93. {
  94. switch (type) {
  95. case TYPE_SET_NODE:
  96. if (dst) *dst = p;
  97. return true;
  98. case TYPE_MODIFY_INVENTORY_STACK: {
  99. InventoryLocation loc;
  100. loc.deSerialize(inventory_location);
  101. if (loc.type != InventoryLocation::NODEMETA) {
  102. return false;
  103. }
  104. if (dst) *dst = loc.p;
  105. return true; }
  106. default:
  107. return false;
  108. }
  109. }
  110. bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
  111. {
  112. try {
  113. switch (type) {
  114. case TYPE_NOTHING:
  115. return true;
  116. case TYPE_SET_NODE: {
  117. const NodeDefManager *ndef = gamedef->ndef();
  118. // Make sure position is loaded from disk
  119. map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
  120. // Check current node
  121. MapNode current_node = map->getNode(p);
  122. std::string current_name = ndef->get(current_node).name;
  123. // If current node not the new node, it's bad
  124. if (current_name != n_new.name) {
  125. return false;
  126. }
  127. // Create rollback node
  128. content_t id = CONTENT_IGNORE;
  129. if (!ndef->getId(n_old.name, id)) {
  130. // The old node is not registered
  131. return false;
  132. }
  133. MapNode n(id, n_old.param1, n_old.param2);
  134. // Set rollback node
  135. try {
  136. if (!map->addNodeWithEvent(p, n)) {
  137. infostream << "RollbackAction::applyRevert(): "
  138. << "AddNodeWithEvent failed at "
  139. << PP(p) << " for " << n_old.name
  140. << std::endl;
  141. return false;
  142. }
  143. if (n_old.meta.empty()) {
  144. map->removeNodeMetadata(p);
  145. } else {
  146. NodeMetadata *meta = map->getNodeMetadata(p);
  147. if (!meta) {
  148. meta = new NodeMetadata(gamedef->idef());
  149. if (!map->setNodeMetadata(p, meta)) {
  150. delete meta;
  151. infostream << "RollbackAction::applyRevert(): "
  152. << "setNodeMetadata failed at "
  153. << PP(p) << " for " << n_old.name
  154. << std::endl;
  155. return false;
  156. }
  157. }
  158. std::istringstream is(n_old.meta, std::ios::binary);
  159. meta->deSerialize(is, 1); // FIXME: version bump??
  160. }
  161. // Inform other things that the meta data has changed
  162. MapEditEvent event;
  163. event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
  164. event.setPositionModified(p);
  165. map->dispatchEvent(event);
  166. } catch (InvalidPositionException &e) {
  167. infostream << "RollbackAction::applyRevert(): "
  168. << "InvalidPositionException: " << e.what()
  169. << std::endl;
  170. return false;
  171. }
  172. // Success
  173. return true; }
  174. case TYPE_MODIFY_INVENTORY_STACK: {
  175. InventoryLocation loc;
  176. loc.deSerialize(inventory_location);
  177. Inventory *inv = imgr->getInventory(loc);
  178. if (!inv) {
  179. infostream << "RollbackAction::applyRevert(): Could not get "
  180. "inventory at " << inventory_location << std::endl;
  181. return false;
  182. }
  183. InventoryList *list = inv->getList(inventory_list);
  184. if (!list) {
  185. infostream << "RollbackAction::applyRevert(): Could not get "
  186. "inventory list \"" << inventory_list << "\" in "
  187. << inventory_location << std::endl;
  188. return false;
  189. }
  190. if (list->getSize() <= inventory_index) {
  191. infostream << "RollbackAction::applyRevert(): List index "
  192. << inventory_index << " too large in "
  193. << "inventory list \"" << inventory_list << "\" in "
  194. << inventory_location << std::endl;
  195. return false;
  196. }
  197. // If item was added, take away item, otherwise add removed item
  198. if (inventory_add) {
  199. // Silently ignore different current item
  200. if (list->getItem(inventory_index).name !=
  201. gamedef->idef()->getAlias(inventory_stack.name))
  202. return false;
  203. list->takeItem(inventory_index, inventory_stack.count);
  204. } else {
  205. list->addItem(inventory_index, inventory_stack);
  206. }
  207. // Inventory was modified; send to clients
  208. imgr->setInventoryModified(loc);
  209. return true; }
  210. default:
  211. errorstream << "RollbackAction::applyRevert(): type not handled"
  212. << std::endl;
  213. return false;
  214. }
  215. } catch(SerializationError &e) {
  216. errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
  217. << ", SerializationError: " << e.what() << std::endl;
  218. }
  219. return false;
  220. }