inventorymanager.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810
  1. /*
  2. Minetest
  3. Copyright (C) 2010-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 "inventorymanager.h"
  17. #include "log.h"
  18. #include "environment.h"
  19. #include "scripting_game.h"
  20. #include "serverobject.h"
  21. #include "main.h" // for g_settings
  22. #include "settings.h"
  23. #include "craftdef.h"
  24. #include "rollback_interface.h"
  25. #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
  26. #define PLAYER_TO_SA(p) p->getEnv()->getScriptIface()
  27. /*
  28. InventoryLocation
  29. */
  30. std::string InventoryLocation::dump() const
  31. {
  32. std::ostringstream os(std::ios::binary);
  33. serialize(os);
  34. return os.str();
  35. }
  36. void InventoryLocation::serialize(std::ostream &os) const
  37. {
  38. switch(type){
  39. case InventoryLocation::UNDEFINED:
  40. os<<"undefined";
  41. break;
  42. case InventoryLocation::CURRENT_PLAYER:
  43. os<<"current_player";
  44. break;
  45. case InventoryLocation::PLAYER:
  46. os<<"player:"<<name;
  47. break;
  48. case InventoryLocation::NODEMETA:
  49. os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
  50. break;
  51. case InventoryLocation::DETACHED:
  52. os<<"detached:"<<name;
  53. break;
  54. default:
  55. assert(0);
  56. }
  57. }
  58. void InventoryLocation::deSerialize(std::istream &is)
  59. {
  60. std::string tname;
  61. std::getline(is, tname, ':');
  62. if(tname == "undefined")
  63. {
  64. type = InventoryLocation::UNDEFINED;
  65. }
  66. else if(tname == "current_player")
  67. {
  68. type = InventoryLocation::CURRENT_PLAYER;
  69. }
  70. else if(tname == "player")
  71. {
  72. type = InventoryLocation::PLAYER;
  73. std::getline(is, name, '\n');
  74. }
  75. else if(tname == "nodemeta")
  76. {
  77. type = InventoryLocation::NODEMETA;
  78. std::string pos;
  79. std::getline(is, pos, '\n');
  80. Strfnd fn(pos);
  81. p.X = stoi(fn.next(","));
  82. p.Y = stoi(fn.next(","));
  83. p.Z = stoi(fn.next(","));
  84. }
  85. else if(tname == "detached")
  86. {
  87. type = InventoryLocation::DETACHED;
  88. std::getline(is, name, '\n');
  89. }
  90. else
  91. {
  92. infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
  93. throw SerializationError("Unknown InventoryLocation type");
  94. }
  95. }
  96. void InventoryLocation::deSerialize(std::string s)
  97. {
  98. std::istringstream is(s, std::ios::binary);
  99. deSerialize(is);
  100. }
  101. /*
  102. InventoryAction
  103. */
  104. InventoryAction * InventoryAction::deSerialize(std::istream &is)
  105. {
  106. std::string type;
  107. std::getline(is, type, ' ');
  108. InventoryAction *a = NULL;
  109. if(type == "Move")
  110. {
  111. a = new IMoveAction(is);
  112. }
  113. else if(type == "Drop")
  114. {
  115. a = new IDropAction(is);
  116. }
  117. else if(type == "Craft")
  118. {
  119. a = new ICraftAction(is);
  120. }
  121. return a;
  122. }
  123. /*
  124. IMoveAction
  125. */
  126. IMoveAction::IMoveAction(std::istream &is)
  127. {
  128. std::string ts;
  129. std::getline(is, ts, ' ');
  130. count = stoi(ts);
  131. std::getline(is, ts, ' ');
  132. from_inv.deSerialize(ts);
  133. std::getline(is, from_list, ' ');
  134. std::getline(is, ts, ' ');
  135. from_i = stoi(ts);
  136. std::getline(is, ts, ' ');
  137. to_inv.deSerialize(ts);
  138. std::getline(is, to_list, ' ');
  139. std::getline(is, ts, ' ');
  140. to_i = stoi(ts);
  141. }
  142. void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
  143. {
  144. Inventory *inv_from = mgr->getInventory(from_inv);
  145. Inventory *inv_to = mgr->getInventory(to_inv);
  146. if(!inv_from){
  147. infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
  148. <<"from_inv=\""<<from_inv.dump()<<"\""
  149. <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
  150. return;
  151. }
  152. if(!inv_to){
  153. infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
  154. <<"from_inv=\""<<from_inv.dump()<<"\""
  155. <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
  156. return;
  157. }
  158. InventoryList *list_from = inv_from->getList(from_list);
  159. InventoryList *list_to = inv_to->getList(to_list);
  160. /*
  161. If a list doesn't exist or the source item doesn't exist
  162. */
  163. if(!list_from){
  164. infostream<<"IMoveAction::apply(): FAIL: source list not found: "
  165. <<"from_inv=\""<<from_inv.dump()<<"\""
  166. <<", from_list=\""<<from_list<<"\""<<std::endl;
  167. return;
  168. }
  169. if(!list_to){
  170. infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
  171. <<"to_inv=\""<<to_inv.dump()<<"\""
  172. <<", to_list=\""<<to_list<<"\""<<std::endl;
  173. return;
  174. }
  175. /*
  176. Do not handle rollback if both inventories are that of the same player
  177. */
  178. bool ignore_rollback = (
  179. from_inv.type == InventoryLocation::PLAYER &&
  180. to_inv.type == InventoryLocation::PLAYER &&
  181. from_inv.name == to_inv.name);
  182. /*
  183. Collect information of endpoints
  184. */
  185. int try_take_count = count;
  186. if(try_take_count == 0)
  187. try_take_count = list_from->getItem(from_i).count;
  188. int src_can_take_count = 0xffff;
  189. int dst_can_put_count = 0xffff;
  190. /* Query detached inventories */
  191. // Move occurs in the same detached inventory
  192. if(from_inv.type == InventoryLocation::DETACHED &&
  193. to_inv.type == InventoryLocation::DETACHED &&
  194. from_inv.name == to_inv.name)
  195. {
  196. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove(
  197. from_inv.name, from_list, from_i,
  198. to_list, to_i, try_take_count, player);
  199. dst_can_put_count = src_can_take_count;
  200. }
  201. else
  202. {
  203. // Destination is detached
  204. if(to_inv.type == InventoryLocation::DETACHED)
  205. {
  206. ItemStack src_item = list_from->getItem(from_i);
  207. src_item.count = try_take_count;
  208. dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut(
  209. to_inv.name, to_list, to_i, src_item, player);
  210. }
  211. // Source is detached
  212. if(from_inv.type == InventoryLocation::DETACHED)
  213. {
  214. ItemStack src_item = list_from->getItem(from_i);
  215. src_item.count = try_take_count;
  216. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
  217. from_inv.name, from_list, from_i, src_item, player);
  218. }
  219. }
  220. /* Query node metadata inventories */
  221. // Both endpoints are nodemeta
  222. // Move occurs in the same nodemeta inventory
  223. if(from_inv.type == InventoryLocation::NODEMETA &&
  224. to_inv.type == InventoryLocation::NODEMETA &&
  225. from_inv.p == to_inv.p)
  226. {
  227. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove(
  228. from_inv.p, from_list, from_i,
  229. to_list, to_i, try_take_count, player);
  230. dst_can_put_count = src_can_take_count;
  231. }
  232. else
  233. {
  234. // Destination is nodemeta
  235. if(to_inv.type == InventoryLocation::NODEMETA)
  236. {
  237. ItemStack src_item = list_from->getItem(from_i);
  238. src_item.count = try_take_count;
  239. dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut(
  240. to_inv.p, to_list, to_i, src_item, player);
  241. }
  242. // Source is nodemeta
  243. if(from_inv.type == InventoryLocation::NODEMETA)
  244. {
  245. ItemStack src_item = list_from->getItem(from_i);
  246. src_item.count = try_take_count;
  247. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
  248. from_inv.p, from_list, from_i, src_item, player);
  249. }
  250. }
  251. int old_count = count;
  252. /* Modify count according to collected data */
  253. count = try_take_count;
  254. if(src_can_take_count != -1 && count > src_can_take_count)
  255. count = src_can_take_count;
  256. if(dst_can_put_count != -1 && count > dst_can_put_count)
  257. count = dst_can_put_count;
  258. /* Limit according to source item count */
  259. if(count > list_from->getItem(from_i).count)
  260. count = list_from->getItem(from_i).count;
  261. /* If no items will be moved, don't go further */
  262. if(count == 0)
  263. {
  264. infostream<<"IMoveAction::apply(): move was completely disallowed:"
  265. <<" count="<<old_count
  266. <<" from inv=\""<<from_inv.dump()<<"\""
  267. <<" list=\""<<from_list<<"\""
  268. <<" i="<<from_i
  269. <<" to inv=\""<<to_inv.dump()<<"\""
  270. <<" list=\""<<to_list<<"\""
  271. <<" i="<<to_i
  272. <<std::endl;
  273. return;
  274. }
  275. ItemStack src_item = list_from->getItem(from_i);
  276. src_item.count = count;
  277. ItemStack from_stack_was = list_from->getItem(from_i);
  278. ItemStack to_stack_was = list_to->getItem(to_i);
  279. /*
  280. Perform actual move
  281. If something is wrong (source item is empty, destination is the
  282. same as source), nothing happens
  283. */
  284. list_from->moveItem(from_i, list_to, to_i, count);
  285. // If source is infinite, reset it's stack
  286. if(src_can_take_count == -1){
  287. // If destination stack is of different type and there are leftover
  288. // items, attempt to put the leftover items to a different place in the
  289. // destination inventory.
  290. // The client-side GUI will try to guess if this happens.
  291. if(from_stack_was.name != to_stack_was.name){
  292. for(u32 i=0; i<list_to->getSize(); i++){
  293. if(list_to->getItem(i).empty()){
  294. list_to->changeItem(i, to_stack_was);
  295. break;
  296. }
  297. }
  298. }
  299. list_from->deleteItem(from_i);
  300. list_from->addItem(from_i, from_stack_was);
  301. }
  302. // If destination is infinite, reset it's stack and take count from source
  303. if(dst_can_put_count == -1){
  304. list_to->deleteItem(to_i);
  305. list_to->addItem(to_i, to_stack_was);
  306. list_from->deleteItem(from_i);
  307. list_from->addItem(from_i, from_stack_was);
  308. list_from->takeItem(from_i, count);
  309. }
  310. infostream<<"IMoveAction::apply(): moved"
  311. <<" count="<<count
  312. <<" from inv=\""<<from_inv.dump()<<"\""
  313. <<" list=\""<<from_list<<"\""
  314. <<" i="<<from_i
  315. <<" to inv=\""<<to_inv.dump()<<"\""
  316. <<" list=\""<<to_list<<"\""
  317. <<" i="<<to_i
  318. <<std::endl;
  319. /*
  320. Record rollback information
  321. */
  322. if(!ignore_rollback && gamedef->rollback())
  323. {
  324. IRollbackReportSink *rollback = gamedef->rollback();
  325. // If source is not infinite, record item take
  326. if(src_can_take_count != -1){
  327. RollbackAction action;
  328. std::string loc;
  329. {
  330. std::ostringstream os(std::ios::binary);
  331. from_inv.serialize(os);
  332. loc = os.str();
  333. }
  334. action.setModifyInventoryStack(loc, from_list, from_i, false,
  335. src_item.getItemString());
  336. rollback->reportAction(action);
  337. }
  338. // If destination is not infinite, record item put
  339. if(dst_can_put_count != -1){
  340. RollbackAction action;
  341. std::string loc;
  342. {
  343. std::ostringstream os(std::ios::binary);
  344. to_inv.serialize(os);
  345. loc = os.str();
  346. }
  347. action.setModifyInventoryStack(loc, to_list, to_i, true,
  348. src_item.getItemString());
  349. rollback->reportAction(action);
  350. }
  351. }
  352. /*
  353. Report move to endpoints
  354. */
  355. /* Detached inventories */
  356. // Both endpoints are same detached
  357. if(from_inv.type == InventoryLocation::DETACHED &&
  358. to_inv.type == InventoryLocation::DETACHED &&
  359. from_inv.name == to_inv.name)
  360. {
  361. PLAYER_TO_SA(player)->detached_inventory_OnMove(
  362. from_inv.name, from_list, from_i,
  363. to_list, to_i, count, player);
  364. }
  365. else
  366. {
  367. // Destination is detached
  368. if(to_inv.type == InventoryLocation::DETACHED)
  369. {
  370. PLAYER_TO_SA(player)->detached_inventory_OnPut(
  371. to_inv.name, to_list, to_i, src_item, player);
  372. }
  373. // Source is detached
  374. if(from_inv.type == InventoryLocation::DETACHED)
  375. {
  376. PLAYER_TO_SA(player)->detached_inventory_OnTake(
  377. from_inv.name, from_list, from_i, src_item, player);
  378. }
  379. }
  380. /* Node metadata inventories */
  381. // Both endpoints are same nodemeta
  382. if(from_inv.type == InventoryLocation::NODEMETA &&
  383. to_inv.type == InventoryLocation::NODEMETA &&
  384. from_inv.p == to_inv.p)
  385. {
  386. PLAYER_TO_SA(player)->nodemeta_inventory_OnMove(
  387. from_inv.p, from_list, from_i,
  388. to_list, to_i, count, player);
  389. }
  390. else{
  391. // Destination is nodemeta
  392. if(to_inv.type == InventoryLocation::NODEMETA)
  393. {
  394. PLAYER_TO_SA(player)->nodemeta_inventory_OnPut(
  395. to_inv.p, to_list, to_i, src_item, player);
  396. }
  397. // Source is nodemeta
  398. else if(from_inv.type == InventoryLocation::NODEMETA)
  399. {
  400. PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
  401. from_inv.p, from_list, from_i, src_item, player);
  402. }
  403. }
  404. mgr->setInventoryModified(from_inv);
  405. if(inv_from != inv_to)
  406. mgr->setInventoryModified(to_inv);
  407. }
  408. void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  409. {
  410. // Optional InventoryAction operation that is run on the client
  411. // to make lag less apparent.
  412. Inventory *inv_from = mgr->getInventory(from_inv);
  413. Inventory *inv_to = mgr->getInventory(to_inv);
  414. if(!inv_from || !inv_to)
  415. return;
  416. InventoryLocation current_player;
  417. current_player.setCurrentPlayer();
  418. Inventory *inv_player = mgr->getInventory(current_player);
  419. if(inv_from != inv_player || inv_to != inv_player)
  420. return;
  421. InventoryList *list_from = inv_from->getList(from_list);
  422. InventoryList *list_to = inv_to->getList(to_list);
  423. if(!list_from || !list_to)
  424. return;
  425. list_from->moveItem(from_i, list_to, to_i, count);
  426. mgr->setInventoryModified(from_inv);
  427. if(inv_from != inv_to)
  428. mgr->setInventoryModified(to_inv);
  429. }
  430. /*
  431. IDropAction
  432. */
  433. IDropAction::IDropAction(std::istream &is)
  434. {
  435. std::string ts;
  436. std::getline(is, ts, ' ');
  437. count = stoi(ts);
  438. std::getline(is, ts, ' ');
  439. from_inv.deSerialize(ts);
  440. std::getline(is, from_list, ' ');
  441. std::getline(is, ts, ' ');
  442. from_i = stoi(ts);
  443. }
  444. void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
  445. {
  446. Inventory *inv_from = mgr->getInventory(from_inv);
  447. if(!inv_from){
  448. infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
  449. <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
  450. return;
  451. }
  452. InventoryList *list_from = inv_from->getList(from_list);
  453. /*
  454. If a list doesn't exist or the source item doesn't exist
  455. */
  456. if(!list_from){
  457. infostream<<"IDropAction::apply(): FAIL: source list not found: "
  458. <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
  459. return;
  460. }
  461. if(list_from->getItem(from_i).empty())
  462. {
  463. infostream<<"IDropAction::apply(): FAIL: source item not found: "
  464. <<"from_inv=\""<<from_inv.dump()<<"\""
  465. <<", from_list=\""<<from_list<<"\""
  466. <<" from_i="<<from_i<<std::endl;
  467. return;
  468. }
  469. /*
  470. Do not handle rollback if inventory is player's
  471. */
  472. bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER);
  473. /*
  474. Collect information of endpoints
  475. */
  476. int take_count = list_from->getItem(from_i).count;
  477. if(count != 0 && count < take_count)
  478. take_count = count;
  479. int src_can_take_count = take_count;
  480. // Source is detached
  481. if(from_inv.type == InventoryLocation::DETACHED)
  482. {
  483. ItemStack src_item = list_from->getItem(from_i);
  484. src_item.count = take_count;
  485. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
  486. from_inv.name, from_list, from_i, src_item, player);
  487. }
  488. // Source is nodemeta
  489. if(from_inv.type == InventoryLocation::NODEMETA)
  490. {
  491. ItemStack src_item = list_from->getItem(from_i);
  492. src_item.count = take_count;
  493. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
  494. from_inv.p, from_list, from_i, src_item, player);
  495. }
  496. if(src_can_take_count != -1 && src_can_take_count < take_count)
  497. take_count = src_can_take_count;
  498. int actually_dropped_count = 0;
  499. ItemStack src_item = list_from->getItem(from_i);
  500. // Drop the item
  501. ItemStack item1 = list_from->getItem(from_i);
  502. item1.count = take_count;
  503. if(PLAYER_TO_SA(player)->item_OnDrop(item1, player,
  504. player->getBasePosition() + v3f(0,1,0)))
  505. {
  506. actually_dropped_count = take_count - item1.count;
  507. if(actually_dropped_count == 0){
  508. infostream<<"Actually dropped no items"<<std::endl;
  509. return;
  510. }
  511. // If source isn't infinite
  512. if(src_can_take_count != -1){
  513. // Take item from source list
  514. ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count);
  515. if(item2.count != actually_dropped_count)
  516. errorstream<<"Could not take dropped count of items"<<std::endl;
  517. mgr->setInventoryModified(from_inv);
  518. }
  519. }
  520. infostream<<"IDropAction::apply(): dropped "
  521. <<" from inv=\""<<from_inv.dump()<<"\""
  522. <<" list=\""<<from_list<<"\""
  523. <<" i="<<from_i
  524. <<std::endl;
  525. src_item.count = actually_dropped_count;
  526. /*
  527. Report drop to endpoints
  528. */
  529. // Source is detached
  530. if(from_inv.type == InventoryLocation::DETACHED)
  531. {
  532. PLAYER_TO_SA(player)->detached_inventory_OnTake(
  533. from_inv.name, from_list, from_i, src_item, player);
  534. }
  535. // Source is nodemeta
  536. if(from_inv.type == InventoryLocation::NODEMETA)
  537. {
  538. PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
  539. from_inv.p, from_list, from_i, src_item, player);
  540. }
  541. /*
  542. Record rollback information
  543. */
  544. if(!ignore_src_rollback && gamedef->rollback())
  545. {
  546. IRollbackReportSink *rollback = gamedef->rollback();
  547. // If source is not infinite, record item take
  548. if(src_can_take_count != -1){
  549. RollbackAction action;
  550. std::string loc;
  551. {
  552. std::ostringstream os(std::ios::binary);
  553. from_inv.serialize(os);
  554. loc = os.str();
  555. }
  556. action.setModifyInventoryStack(loc, from_list, from_i,
  557. false, src_item.getItemString());
  558. rollback->reportAction(action);
  559. }
  560. }
  561. }
  562. void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  563. {
  564. // Optional InventoryAction operation that is run on the client
  565. // to make lag less apparent.
  566. Inventory *inv_from = mgr->getInventory(from_inv);
  567. if(!inv_from)
  568. return;
  569. InventoryLocation current_player;
  570. current_player.setCurrentPlayer();
  571. Inventory *inv_player = mgr->getInventory(current_player);
  572. if(inv_from != inv_player)
  573. return;
  574. InventoryList *list_from = inv_from->getList(from_list);
  575. if(!list_from)
  576. return;
  577. if(count == 0)
  578. list_from->changeItem(from_i, ItemStack());
  579. else
  580. list_from->takeItem(from_i, count);
  581. mgr->setInventoryModified(from_inv);
  582. }
  583. /*
  584. ICraftAction
  585. */
  586. ICraftAction::ICraftAction(std::istream &is)
  587. {
  588. std::string ts;
  589. std::getline(is, ts, ' ');
  590. count = stoi(ts);
  591. std::getline(is, ts, ' ');
  592. craft_inv.deSerialize(ts);
  593. }
  594. void ICraftAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
  595. {
  596. Inventory *inv_craft = mgr->getInventory(craft_inv);
  597. if(!inv_craft){
  598. infostream<<"ICraftAction::apply(): FAIL: inventory not found: "
  599. <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
  600. return;
  601. }
  602. InventoryList *list_craft = inv_craft->getList("craft");
  603. InventoryList *list_craftresult = inv_craft->getList("craftresult");
  604. /*
  605. If a list doesn't exist or the source item doesn't exist
  606. */
  607. if(!list_craft){
  608. infostream<<"ICraftAction::apply(): FAIL: craft list not found: "
  609. <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
  610. return;
  611. }
  612. if(!list_craftresult){
  613. infostream<<"ICraftAction::apply(): FAIL: craftresult list not found: "
  614. <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
  615. return;
  616. }
  617. if(list_craftresult->getSize() < 1){
  618. infostream<<"ICraftAction::apply(): FAIL: craftresult list too short: "
  619. <<"craft_inv=\""<<craft_inv.dump()<<"\""<<std::endl;
  620. return;
  621. }
  622. ItemStack crafted;
  623. ItemStack craftresultitem;
  624. int count_remaining = count;
  625. bool found = getCraftingResult(inv_craft, crafted, false, gamedef);
  626. PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
  627. found = !crafted.empty();
  628. while(found && list_craftresult->itemFits(0, crafted))
  629. {
  630. InventoryList saved_craft_list = *list_craft;
  631. // Decrement input and add crafting output
  632. getCraftingResult(inv_craft, crafted, true, gamedef);
  633. PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv);
  634. list_craftresult->addItem(0, crafted);
  635. mgr->setInventoryModified(craft_inv);
  636. actionstream<<player->getDescription()
  637. <<" crafts "
  638. <<crafted.getItemString()
  639. <<std::endl;
  640. // Decrement counter
  641. if(count_remaining == 1)
  642. break;
  643. else if(count_remaining > 1)
  644. count_remaining--;
  645. // Get next crafting result
  646. found = getCraftingResult(inv_craft, crafted, false, gamedef);
  647. PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
  648. found = !crafted.empty();
  649. }
  650. infostream<<"ICraftAction::apply(): crafted "
  651. <<" craft_inv=\""<<craft_inv.dump()<<"\""
  652. <<std::endl;
  653. }
  654. void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  655. {
  656. // Optional InventoryAction operation that is run on the client
  657. // to make lag less apparent.
  658. }
  659. // Crafting helper
  660. bool getCraftingResult(Inventory *inv, ItemStack& result,
  661. bool decrementInput, IGameDef *gamedef)
  662. {
  663. DSTACK(__FUNCTION_NAME);
  664. result.clear();
  665. // Get the InventoryList in which we will operate
  666. InventoryList *clist = inv->getList("craft");
  667. if(!clist)
  668. return false;
  669. // Mangle crafting grid to an another format
  670. CraftInput ci;
  671. ci.method = CRAFT_METHOD_NORMAL;
  672. ci.width = clist->getWidth() ? clist->getWidth() : 3;
  673. for(u16 i=0; i<clist->getSize(); i++)
  674. ci.items.push_back(clist->getItem(i));
  675. // Find out what is crafted and add it to result item slot
  676. CraftOutput co;
  677. bool found = gamedef->getCraftDefManager()->getCraftResult(
  678. ci, co, decrementInput, gamedef);
  679. if(found)
  680. result.deSerialize(co.item, gamedef->getItemDefManager());
  681. if(found && decrementInput)
  682. {
  683. // CraftInput has been changed, apply changes in clist
  684. for(u16 i=0; i<clist->getSize(); i++)
  685. {
  686. clist->changeItem(i, ci.items[i]);
  687. }
  688. }
  689. return found;
  690. }