rollback_interface.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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 "map.h"
  22. #include "gamedef.h"
  23. #include "nodedef.h"
  24. #include "nodemetadata.h"
  25. #include "exceptions.h"
  26. #include "log.h"
  27. #include "inventorymanager.h"
  28. #include "inventory.h"
  29. #include "mapblock.h"
  30. #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
  31. RollbackNode::RollbackNode(Map *map, v3s16 p, IGameDef *gamedef)
  32. {
  33. INodeDefManager *ndef = gamedef->ndef();
  34. MapNode n = map->getNodeNoEx(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);
  42. meta = os.str();
  43. }
  44. }
  45. std::string RollbackAction::toString() const
  46. {
  47. switch(type){
  48. case TYPE_SET_NODE: {
  49. std::ostringstream os(std::ios::binary);
  50. os<<"[set_node";
  51. os<<" ";
  52. os<<"("<<itos(p.X)<<","<<itos(p.Y)<<","<<itos(p.Z)<<")";
  53. os<<" ";
  54. os<<serializeJsonString(n_old.name);
  55. os<<" ";
  56. os<<itos(n_old.param1);
  57. os<<" ";
  58. os<<itos(n_old.param2);
  59. os<<" ";
  60. os<<serializeJsonString(n_old.meta);
  61. os<<" ";
  62. os<<serializeJsonString(n_new.name);
  63. os<<" ";
  64. os<<itos(n_new.param1);
  65. os<<" ";
  66. os<<itos(n_new.param2);
  67. os<<" ";
  68. os<<serializeJsonString(n_new.meta);
  69. os<<"]";
  70. return os.str(); }
  71. case TYPE_MODIFY_INVENTORY_STACK: {
  72. std::ostringstream os(std::ios::binary);
  73. os<<"[modify_inventory_stack";
  74. os<<" ";
  75. os<<serializeJsonString(inventory_location);
  76. os<<" ";
  77. os<<serializeJsonString(inventory_list);
  78. os<<" ";
  79. os<<inventory_index;
  80. os<<" ";
  81. os<<(inventory_add?"add":"remove");
  82. os<<" ";
  83. os<<serializeJsonString(inventory_stack);
  84. os<<"]";
  85. return os.str(); }
  86. default:
  87. return "none";
  88. }
  89. }
  90. void RollbackAction::fromStream(std::istream &is) throw(SerializationError)
  91. {
  92. int c = is.get();
  93. if(c != '['){
  94. is.putback(c);
  95. throw SerializationError("RollbackAction: starting [ not found");
  96. }
  97. std::string id;
  98. std::getline(is, id, ' ');
  99. if(id == "set_node")
  100. {
  101. c = is.get();
  102. if(c != '('){
  103. is.putback(c);
  104. throw SerializationError("RollbackAction: starting ( not found");
  105. }
  106. // Position
  107. std::string px_raw;
  108. std::string py_raw;
  109. std::string pz_raw;
  110. std::getline(is, px_raw, ',');
  111. std::getline(is, py_raw, ',');
  112. std::getline(is, pz_raw, ')');
  113. c = is.get();
  114. if(c != ' '){
  115. is.putback(c);
  116. throw SerializationError("RollbackAction: after-p ' ' not found");
  117. }
  118. v3s16 loaded_p(stoi(px_raw), stoi(py_raw), stoi(pz_raw));
  119. // Old node
  120. std::string old_name;
  121. try{
  122. old_name = deSerializeJsonString(is);
  123. }catch(SerializationError &e){
  124. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  125. <<"old_name: "<<e.what()<<std::endl;
  126. throw e;
  127. }
  128. c = is.get();
  129. if(c != ' '){
  130. is.putback(c);
  131. throw SerializationError("RollbackAction: after-old_name ' ' not found");
  132. }
  133. std::string old_p1_raw;
  134. std::string old_p2_raw;
  135. std::getline(is, old_p1_raw, ' ');
  136. std::getline(is, old_p2_raw, ' ');
  137. int old_p1 = stoi(old_p1_raw);
  138. int old_p2 = stoi(old_p2_raw);
  139. std::string old_meta;
  140. try{
  141. old_meta = deSerializeJsonString(is);
  142. }catch(SerializationError &e){
  143. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  144. <<"old_meta: "<<e.what()<<std::endl;
  145. throw e;
  146. }
  147. c = is.get();
  148. if(c != ' '){
  149. is.putback(c);
  150. throw SerializationError("RollbackAction: after-old_meta ' ' not found");
  151. }
  152. // New node
  153. std::string new_name;
  154. try{
  155. new_name = deSerializeJsonString(is);
  156. }catch(SerializationError &e){
  157. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  158. <<"new_name: "<<e.what()<<std::endl;
  159. throw e;
  160. }
  161. c = is.get();
  162. if(c != ' '){
  163. is.putback(c);
  164. throw SerializationError("RollbackAction: after-new_name ' ' not found");
  165. }
  166. std::string new_p1_raw;
  167. std::string new_p2_raw;
  168. std::getline(is, new_p1_raw, ' ');
  169. std::getline(is, new_p2_raw, ' ');
  170. int new_p1 = stoi(new_p1_raw);
  171. int new_p2 = stoi(new_p2_raw);
  172. std::string new_meta;
  173. try{
  174. new_meta = deSerializeJsonString(is);
  175. }catch(SerializationError &e){
  176. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  177. <<"new_meta: "<<e.what()<<std::endl;
  178. throw e;
  179. }
  180. c = is.get();
  181. if(c != ']'){
  182. is.putback(c);
  183. throw SerializationError("RollbackAction: after-new_meta ] not found");
  184. }
  185. // Set values
  186. type = TYPE_SET_NODE;
  187. p = loaded_p;
  188. n_old.name = old_name;
  189. n_old.param1 = old_p1;
  190. n_old.param2 = old_p2;
  191. n_old.meta = old_meta;
  192. n_new.name = new_name;
  193. n_new.param1 = new_p1;
  194. n_new.param2 = new_p2;
  195. n_new.meta = new_meta;
  196. }
  197. else if(id == "modify_inventory_stack")
  198. {
  199. // Location
  200. std::string location;
  201. try{
  202. location = deSerializeJsonString(is);
  203. }catch(SerializationError &e){
  204. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  205. <<"location: "<<e.what()<<std::endl;
  206. throw e;
  207. }
  208. c = is.get();
  209. if(c != ' '){
  210. is.putback(c);
  211. throw SerializationError("RollbackAction: after-loc ' ' not found");
  212. }
  213. // List
  214. std::string listname;
  215. try{
  216. listname = deSerializeJsonString(is);
  217. }catch(SerializationError &e){
  218. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  219. <<"listname: "<<e.what()<<std::endl;
  220. throw e;
  221. }
  222. c = is.get();
  223. if(c != ' '){
  224. is.putback(c);
  225. throw SerializationError("RollbackAction: after-list ' ' not found");
  226. }
  227. // Index
  228. std::string index_raw;
  229. std::getline(is, index_raw, ' ');
  230. // add/remove
  231. std::string addremove;
  232. std::getline(is, addremove, ' ');
  233. if(addremove != "add" && addremove != "remove"){
  234. throw SerializationError("RollbackAction: addremove is not add or remove");
  235. }
  236. // Itemstring
  237. std::string stack;
  238. try{
  239. stack = deSerializeJsonString(is);
  240. }catch(SerializationError &e){
  241. errorstream<<"Serialization error in RollbackAction::fromStream(): "
  242. <<"stack: "<<e.what()<<std::endl;
  243. throw e;
  244. }
  245. // Set values
  246. type = TYPE_MODIFY_INVENTORY_STACK;
  247. inventory_location = location;
  248. inventory_list = listname;
  249. inventory_index = stoi(index_raw);
  250. inventory_add = (addremove == "add");
  251. inventory_stack = stack;
  252. }
  253. else
  254. {
  255. throw SerializationError("RollbackAction: Unknown id");
  256. }
  257. }
  258. bool RollbackAction::isImportant(IGameDef *gamedef) const
  259. {
  260. switch(type){
  261. case TYPE_SET_NODE: {
  262. // If names differ, action is always important
  263. if(n_old.name != n_new.name)
  264. return true;
  265. // If metadata differs, action is always important
  266. if(n_old.meta != n_new.meta)
  267. return true;
  268. INodeDefManager *ndef = gamedef->ndef();
  269. // Both are of the same name, so a single definition is needed
  270. const ContentFeatures &def = ndef->get(n_old.name);
  271. // If the type is flowing liquid, action is not important
  272. if(def.liquid_type == LIQUID_FLOWING)
  273. return false;
  274. // Otherwise action is important
  275. return true; }
  276. default:
  277. return true;
  278. }
  279. }
  280. bool RollbackAction::getPosition(v3s16 *dst) const
  281. {
  282. switch(type){
  283. case RollbackAction::TYPE_SET_NODE:
  284. if(dst) *dst = p;
  285. return true;
  286. case RollbackAction::TYPE_MODIFY_INVENTORY_STACK: {
  287. InventoryLocation loc;
  288. loc.deSerialize(inventory_location);
  289. if(loc.type != InventoryLocation::NODEMETA)
  290. return false;
  291. if(dst) *dst = loc.p;
  292. return true; }
  293. default:
  294. return false;
  295. }
  296. }
  297. bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
  298. {
  299. try{
  300. switch(type){
  301. case TYPE_NOTHING:
  302. return true;
  303. case TYPE_SET_NODE: {
  304. INodeDefManager *ndef = gamedef->ndef();
  305. // Make sure position is loaded from disk
  306. map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
  307. // Check current node
  308. MapNode current_node = map->getNodeNoEx(p);
  309. std::string current_name = ndef->get(current_node).name;
  310. // If current node not the new node, it's bad
  311. if(current_name != n_new.name)
  312. return false;
  313. /*// If current node not the new node and not ignore, it's bad
  314. if(current_name != n_new.name && current_name != "ignore")
  315. return false;*/
  316. // Create rollback node
  317. MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
  318. // Set rollback node
  319. try{
  320. if(!map->addNodeWithEvent(p, n)){
  321. infostream<<"RollbackAction::applyRevert(): "
  322. <<"AddNodeWithEvent failed at "
  323. <<PP(p)<<" for "<<n_old.name<<std::endl;
  324. return false;
  325. }
  326. NodeMetadata *meta = map->getNodeMetadata(p);
  327. if(n_old.meta != ""){
  328. if(!meta){
  329. meta = new NodeMetadata(gamedef);
  330. if(!map->setNodeMetadata(p, meta)){
  331. delete meta;
  332. infostream<<"RollbackAction::applyRevert(): "
  333. <<"setNodeMetadata failed at "
  334. <<PP(p)<<" for "<<n_old.name<<std::endl;
  335. return false;
  336. }
  337. }
  338. std::istringstream is(n_old.meta, std::ios::binary);
  339. meta->deSerialize(is);
  340. } else {
  341. map->removeNodeMetadata(p);
  342. }
  343. // NOTE: This same code is in scriptapi.cpp
  344. // Inform other things that the metadata has changed
  345. v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
  346. MapEditEvent event;
  347. event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
  348. event.p = blockpos;
  349. map->dispatchEvent(&event);
  350. // Set the block to be saved
  351. MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
  352. if(block)
  353. block->raiseModified(MOD_STATE_WRITE_NEEDED,
  354. "NodeMetaRef::reportMetadataChange");
  355. }catch(InvalidPositionException &e){
  356. infostream<<"RollbackAction::applyRevert(): "
  357. <<"InvalidPositionException: "<<e.what()<<std::endl;
  358. return false;
  359. }
  360. // Success
  361. return true; }
  362. case TYPE_MODIFY_INVENTORY_STACK: {
  363. InventoryLocation loc;
  364. loc.deSerialize(inventory_location);
  365. ItemStack stack;
  366. stack.deSerialize(inventory_stack, gamedef->idef());
  367. Inventory *inv = imgr->getInventory(loc);
  368. if(!inv){
  369. infostream<<"RollbackAction::applyRevert(): Could not get "
  370. "inventory at "<<inventory_location<<std::endl;
  371. return false;
  372. }
  373. InventoryList *list = inv->getList(inventory_list);
  374. if(!list){
  375. infostream<<"RollbackAction::applyRevert(): Could not get "
  376. "inventory list \""<<inventory_list<<"\" in "
  377. <<inventory_location<<std::endl;
  378. return false;
  379. }
  380. if(list->getSize() <= inventory_index){
  381. infostream<<"RollbackAction::applyRevert(): List index "
  382. <<inventory_index<<" too large in "
  383. <<"inventory list \""<<inventory_list<<"\" in "
  384. <<inventory_location<<std::endl;
  385. }
  386. // If item was added, take away item, otherwise add removed item
  387. if(inventory_add){
  388. // Silently ignore different current item
  389. if(list->getItem(inventory_index).name != stack.name)
  390. return false;
  391. list->takeItem(inventory_index, stack.count);
  392. } else {
  393. list->addItem(inventory_index, stack);
  394. }
  395. // Inventory was modified; send to clients
  396. imgr->setInventoryModified(loc);
  397. return true; }
  398. default:
  399. errorstream<<"RollbackAction::applyRevert(): type not handled"
  400. <<std::endl;
  401. return false;
  402. }
  403. }catch(SerializationError &e){
  404. errorstream<<"RollbackAction::applyRevert(): n_old.name="<<n_old.name
  405. <<", SerializationError: "<<e.what()<<std::endl;
  406. }
  407. return false;
  408. }