inventorymanager.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901
  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 "settings.h"
  22. #include "craftdef.h"
  23. #include "rollback_interface.h"
  24. #include "strfnd.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. FATAL_ERROR("Unhandled inventory location type");
  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. a = new IMoveAction(is, false);
  111. } else if (type == "MoveSomewhere") {
  112. a = new IMoveAction(is, true);
  113. } else if (type == "Drop") {
  114. a = new IDropAction(is);
  115. } else if(type == "Craft") {
  116. a = new ICraftAction(is);
  117. }
  118. return a;
  119. }
  120. /*
  121. IMoveAction
  122. */
  123. IMoveAction::IMoveAction(std::istream &is, bool somewhere)
  124. {
  125. std::string ts;
  126. move_somewhere = somewhere;
  127. caused_by_move_somewhere = false;
  128. move_count = 0;
  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. if (!somewhere) {
  140. std::getline(is, ts, ' ');
  141. to_i = stoi(ts);
  142. }
  143. }
  144. void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
  145. {
  146. Inventory *inv_from = mgr->getInventory(from_inv);
  147. Inventory *inv_to = mgr->getInventory(to_inv);
  148. if(!inv_from){
  149. infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
  150. <<"from_inv=\""<<from_inv.dump()<<"\""
  151. <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
  152. return;
  153. }
  154. if(!inv_to){
  155. infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
  156. <<"from_inv=\""<<from_inv.dump()<<"\""
  157. <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
  158. return;
  159. }
  160. InventoryList *list_from = inv_from->getList(from_list);
  161. InventoryList *list_to = inv_to->getList(to_list);
  162. /*
  163. If a list doesn't exist or the source item doesn't exist
  164. */
  165. if(!list_from){
  166. infostream<<"IMoveAction::apply(): FAIL: source list not found: "
  167. <<"from_inv=\""<<from_inv.dump()<<"\""
  168. <<", from_list=\""<<from_list<<"\""<<std::endl;
  169. return;
  170. }
  171. if(!list_to){
  172. infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
  173. <<"to_inv=\""<<to_inv.dump()<<"\""
  174. <<", to_list=\""<<to_list<<"\""<<std::endl;
  175. return;
  176. }
  177. if (move_somewhere) {
  178. s16 old_to_i = to_i;
  179. u16 old_count = count;
  180. caused_by_move_somewhere = true;
  181. move_somewhere = false;
  182. infostream << "IMoveAction::apply(): moving item somewhere"
  183. << " msom=" << move_somewhere
  184. << " count=" << count
  185. << " from inv=\"" << from_inv.dump() << "\""
  186. << " list=\"" << from_list << "\""
  187. << " i=" << from_i
  188. << " to inv=\"" << to_inv.dump() << "\""
  189. << " list=\"" << to_list << "\""
  190. << std::endl;
  191. // Try to add the item to destination list
  192. s16 dest_size = list_to->getSize();
  193. // First try all the non-empty slots
  194. for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
  195. if (!list_to->getItem(dest_i).empty()) {
  196. to_i = dest_i;
  197. apply(mgr, player, gamedef);
  198. count -= move_count;
  199. }
  200. }
  201. // Then try all the empty ones
  202. for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) {
  203. if (list_to->getItem(dest_i).empty()) {
  204. to_i = dest_i;
  205. apply(mgr, player, gamedef);
  206. count -= move_count;
  207. }
  208. }
  209. to_i = old_to_i;
  210. count = old_count;
  211. caused_by_move_somewhere = false;
  212. move_somewhere = true;
  213. return;
  214. }
  215. /*
  216. Do not handle rollback if both inventories are that of the same player
  217. */
  218. bool ignore_rollback = (
  219. from_inv.type == InventoryLocation::PLAYER &&
  220. to_inv.type == InventoryLocation::PLAYER &&
  221. from_inv.name == to_inv.name);
  222. /*
  223. Collect information of endpoints
  224. */
  225. int try_take_count = count;
  226. if(try_take_count == 0)
  227. try_take_count = list_from->getItem(from_i).count;
  228. int src_can_take_count = 0xffff;
  229. int dst_can_put_count = 0xffff;
  230. /* Query detached inventories */
  231. // Move occurs in the same detached inventory
  232. if(from_inv.type == InventoryLocation::DETACHED &&
  233. to_inv.type == InventoryLocation::DETACHED &&
  234. from_inv.name == to_inv.name)
  235. {
  236. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove(
  237. from_inv.name, from_list, from_i,
  238. to_list, to_i, try_take_count, player);
  239. dst_can_put_count = src_can_take_count;
  240. }
  241. else
  242. {
  243. // Destination is detached
  244. if(to_inv.type == InventoryLocation::DETACHED)
  245. {
  246. ItemStack src_item = list_from->getItem(from_i);
  247. src_item.count = try_take_count;
  248. dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut(
  249. to_inv.name, to_list, to_i, src_item, player);
  250. }
  251. // Source is detached
  252. if(from_inv.type == InventoryLocation::DETACHED)
  253. {
  254. ItemStack src_item = list_from->getItem(from_i);
  255. src_item.count = try_take_count;
  256. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
  257. from_inv.name, from_list, from_i, src_item, player);
  258. }
  259. }
  260. /* Query node metadata inventories */
  261. // Both endpoints are nodemeta
  262. // Move occurs in the same nodemeta inventory
  263. if(from_inv.type == InventoryLocation::NODEMETA &&
  264. to_inv.type == InventoryLocation::NODEMETA &&
  265. from_inv.p == to_inv.p)
  266. {
  267. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove(
  268. from_inv.p, from_list, from_i,
  269. to_list, to_i, try_take_count, player);
  270. dst_can_put_count = src_can_take_count;
  271. }
  272. else
  273. {
  274. // Destination is nodemeta
  275. if(to_inv.type == InventoryLocation::NODEMETA)
  276. {
  277. ItemStack src_item = list_from->getItem(from_i);
  278. src_item.count = try_take_count;
  279. dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut(
  280. to_inv.p, to_list, to_i, src_item, player);
  281. }
  282. // Source is nodemeta
  283. if(from_inv.type == InventoryLocation::NODEMETA)
  284. {
  285. ItemStack src_item = list_from->getItem(from_i);
  286. src_item.count = try_take_count;
  287. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
  288. from_inv.p, from_list, from_i, src_item, player);
  289. }
  290. }
  291. int old_count = count;
  292. /* Modify count according to collected data */
  293. count = try_take_count;
  294. if(src_can_take_count != -1 && count > src_can_take_count)
  295. count = src_can_take_count;
  296. if(dst_can_put_count != -1 && count > dst_can_put_count)
  297. count = dst_can_put_count;
  298. /* Limit according to source item count */
  299. if(count > list_from->getItem(from_i).count)
  300. count = list_from->getItem(from_i).count;
  301. /* If no items will be moved, don't go further */
  302. if(count == 0)
  303. {
  304. infostream<<"IMoveAction::apply(): move was completely disallowed:"
  305. <<" count="<<old_count
  306. <<" from inv=\""<<from_inv.dump()<<"\""
  307. <<" list=\""<<from_list<<"\""
  308. <<" i="<<from_i
  309. <<" to inv=\""<<to_inv.dump()<<"\""
  310. <<" list=\""<<to_list<<"\""
  311. <<" i="<<to_i
  312. <<std::endl;
  313. return;
  314. }
  315. ItemStack src_item = list_from->getItem(from_i);
  316. src_item.count = count;
  317. ItemStack from_stack_was = list_from->getItem(from_i);
  318. ItemStack to_stack_was = list_to->getItem(to_i);
  319. /*
  320. Perform actual move
  321. If something is wrong (source item is empty, destination is the
  322. same as source), nothing happens
  323. */
  324. move_count = list_from->moveItem(from_i,
  325. list_to, to_i, count, !caused_by_move_somewhere);
  326. // If source is infinite, reset it's stack
  327. if(src_can_take_count == -1){
  328. // If destination stack is of different type and there are leftover
  329. // items, attempt to put the leftover items to a different place in the
  330. // destination inventory.
  331. // The client-side GUI will try to guess if this happens.
  332. if(from_stack_was.name != to_stack_was.name){
  333. for(u32 i=0; i<list_to->getSize(); i++){
  334. if(list_to->getItem(i).empty()){
  335. list_to->changeItem(i, to_stack_was);
  336. break;
  337. }
  338. }
  339. }
  340. list_from->deleteItem(from_i);
  341. list_from->addItem(from_i, from_stack_was);
  342. }
  343. // If destination is infinite, reset it's stack and take count from source
  344. if(dst_can_put_count == -1){
  345. list_to->deleteItem(to_i);
  346. list_to->addItem(to_i, to_stack_was);
  347. list_from->deleteItem(from_i);
  348. list_from->addItem(from_i, from_stack_was);
  349. list_from->takeItem(from_i, count);
  350. }
  351. infostream << "IMoveAction::apply(): moved"
  352. << " msom=" << move_somewhere
  353. << " caused=" << caused_by_move_somewhere
  354. << " count=" << count
  355. << " from inv=\"" << from_inv.dump() << "\""
  356. << " list=\"" << from_list << "\""
  357. << " i=" << from_i
  358. << " to inv=\"" << to_inv.dump() << "\""
  359. << " list=\"" << to_list << "\""
  360. << " i=" << to_i
  361. << std::endl;
  362. /*
  363. Record rollback information
  364. */
  365. if(!ignore_rollback && gamedef->rollback())
  366. {
  367. IRollbackManager *rollback = gamedef->rollback();
  368. // If source is not infinite, record item take
  369. if(src_can_take_count != -1){
  370. RollbackAction action;
  371. std::string loc;
  372. {
  373. std::ostringstream os(std::ios::binary);
  374. from_inv.serialize(os);
  375. loc = os.str();
  376. }
  377. action.setModifyInventoryStack(loc, from_list, from_i, false,
  378. src_item);
  379. rollback->reportAction(action);
  380. }
  381. // If destination is not infinite, record item put
  382. if(dst_can_put_count != -1){
  383. RollbackAction action;
  384. std::string loc;
  385. {
  386. std::ostringstream os(std::ios::binary);
  387. to_inv.serialize(os);
  388. loc = os.str();
  389. }
  390. action.setModifyInventoryStack(loc, to_list, to_i, true,
  391. src_item);
  392. rollback->reportAction(action);
  393. }
  394. }
  395. /*
  396. Report move to endpoints
  397. */
  398. /* Detached inventories */
  399. // Both endpoints are same detached
  400. if(from_inv.type == InventoryLocation::DETACHED &&
  401. to_inv.type == InventoryLocation::DETACHED &&
  402. from_inv.name == to_inv.name)
  403. {
  404. PLAYER_TO_SA(player)->detached_inventory_OnMove(
  405. from_inv.name, from_list, from_i,
  406. to_list, to_i, count, player);
  407. }
  408. else
  409. {
  410. // Destination is detached
  411. if(to_inv.type == InventoryLocation::DETACHED)
  412. {
  413. PLAYER_TO_SA(player)->detached_inventory_OnPut(
  414. to_inv.name, to_list, to_i, src_item, player);
  415. }
  416. // Source is detached
  417. if(from_inv.type == InventoryLocation::DETACHED)
  418. {
  419. PLAYER_TO_SA(player)->detached_inventory_OnTake(
  420. from_inv.name, from_list, from_i, src_item, player);
  421. }
  422. }
  423. /* Node metadata inventories */
  424. // Both endpoints are same nodemeta
  425. if(from_inv.type == InventoryLocation::NODEMETA &&
  426. to_inv.type == InventoryLocation::NODEMETA &&
  427. from_inv.p == to_inv.p)
  428. {
  429. PLAYER_TO_SA(player)->nodemeta_inventory_OnMove(
  430. from_inv.p, from_list, from_i,
  431. to_list, to_i, count, player);
  432. }
  433. else{
  434. // Destination is nodemeta
  435. if(to_inv.type == InventoryLocation::NODEMETA)
  436. {
  437. PLAYER_TO_SA(player)->nodemeta_inventory_OnPut(
  438. to_inv.p, to_list, to_i, src_item, player);
  439. }
  440. // Source is nodemeta
  441. else if(from_inv.type == InventoryLocation::NODEMETA)
  442. {
  443. PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
  444. from_inv.p, from_list, from_i, src_item, player);
  445. }
  446. }
  447. mgr->setInventoryModified(from_inv, false);
  448. if(inv_from != inv_to)
  449. mgr->setInventoryModified(to_inv, false);
  450. }
  451. void IMoveAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  452. {
  453. // Optional InventoryAction operation that is run on the client
  454. // to make lag less apparent.
  455. Inventory *inv_from = mgr->getInventory(from_inv);
  456. Inventory *inv_to = mgr->getInventory(to_inv);
  457. if(!inv_from || !inv_to)
  458. return;
  459. InventoryLocation current_player;
  460. current_player.setCurrentPlayer();
  461. Inventory *inv_player = mgr->getInventory(current_player);
  462. if(inv_from != inv_player || inv_to != inv_player)
  463. return;
  464. InventoryList *list_from = inv_from->getList(from_list);
  465. InventoryList *list_to = inv_to->getList(to_list);
  466. if(!list_from || !list_to)
  467. return;
  468. if (!move_somewhere)
  469. list_from->moveItem(from_i, list_to, to_i, count);
  470. else
  471. list_from->moveItemSomewhere(from_i, list_to, count);
  472. mgr->setInventoryModified(from_inv);
  473. if(inv_from != inv_to)
  474. mgr->setInventoryModified(to_inv);
  475. }
  476. /*
  477. IDropAction
  478. */
  479. IDropAction::IDropAction(std::istream &is)
  480. {
  481. std::string ts;
  482. std::getline(is, ts, ' ');
  483. count = stoi(ts);
  484. std::getline(is, ts, ' ');
  485. from_inv.deSerialize(ts);
  486. std::getline(is, from_list, ' ');
  487. std::getline(is, ts, ' ');
  488. from_i = stoi(ts);
  489. }
  490. void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef)
  491. {
  492. Inventory *inv_from = mgr->getInventory(from_inv);
  493. if(!inv_from){
  494. infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
  495. <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
  496. return;
  497. }
  498. InventoryList *list_from = inv_from->getList(from_list);
  499. /*
  500. If a list doesn't exist or the source item doesn't exist
  501. */
  502. if(!list_from){
  503. infostream<<"IDropAction::apply(): FAIL: source list not found: "
  504. <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
  505. return;
  506. }
  507. if(list_from->getItem(from_i).empty())
  508. {
  509. infostream<<"IDropAction::apply(): FAIL: source item not found: "
  510. <<"from_inv=\""<<from_inv.dump()<<"\""
  511. <<", from_list=\""<<from_list<<"\""
  512. <<" from_i="<<from_i<<std::endl;
  513. return;
  514. }
  515. /*
  516. Do not handle rollback if inventory is player's
  517. */
  518. bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER);
  519. /*
  520. Collect information of endpoints
  521. */
  522. int take_count = list_from->getItem(from_i).count;
  523. if(count != 0 && count < take_count)
  524. take_count = count;
  525. int src_can_take_count = take_count;
  526. // Source is detached
  527. if(from_inv.type == InventoryLocation::DETACHED)
  528. {
  529. ItemStack src_item = list_from->getItem(from_i);
  530. src_item.count = take_count;
  531. src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake(
  532. from_inv.name, from_list, from_i, src_item, player);
  533. }
  534. // Source is nodemeta
  535. if(from_inv.type == InventoryLocation::NODEMETA)
  536. {
  537. ItemStack src_item = list_from->getItem(from_i);
  538. src_item.count = take_count;
  539. src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake(
  540. from_inv.p, from_list, from_i, src_item, player);
  541. }
  542. if(src_can_take_count != -1 && src_can_take_count < take_count)
  543. take_count = src_can_take_count;
  544. int actually_dropped_count = 0;
  545. ItemStack src_item = list_from->getItem(from_i);
  546. // Drop the item
  547. ItemStack item1 = list_from->getItem(from_i);
  548. item1.count = take_count;
  549. if(PLAYER_TO_SA(player)->item_OnDrop(item1, player,
  550. player->getBasePosition() + v3f(0,1,0)))
  551. {
  552. actually_dropped_count = take_count - item1.count;
  553. if(actually_dropped_count == 0){
  554. infostream<<"Actually dropped no items"<<std::endl;
  555. return;
  556. }
  557. // If source isn't infinite
  558. if(src_can_take_count != -1){
  559. // Take item from source list
  560. ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count);
  561. if(item2.count != actually_dropped_count)
  562. errorstream<<"Could not take dropped count of items"<<std::endl;
  563. mgr->setInventoryModified(from_inv, false);
  564. }
  565. }
  566. infostream<<"IDropAction::apply(): dropped "
  567. <<" from inv=\""<<from_inv.dump()<<"\""
  568. <<" list=\""<<from_list<<"\""
  569. <<" i="<<from_i
  570. <<std::endl;
  571. src_item.count = actually_dropped_count;
  572. /*
  573. Report drop to endpoints
  574. */
  575. // Source is detached
  576. if(from_inv.type == InventoryLocation::DETACHED)
  577. {
  578. PLAYER_TO_SA(player)->detached_inventory_OnTake(
  579. from_inv.name, from_list, from_i, src_item, player);
  580. }
  581. // Source is nodemeta
  582. if(from_inv.type == InventoryLocation::NODEMETA)
  583. {
  584. PLAYER_TO_SA(player)->nodemeta_inventory_OnTake(
  585. from_inv.p, from_list, from_i, src_item, player);
  586. }
  587. /*
  588. Record rollback information
  589. */
  590. if(!ignore_src_rollback && gamedef->rollback())
  591. {
  592. IRollbackManager *rollback = gamedef->rollback();
  593. // If source is not infinite, record item take
  594. if(src_can_take_count != -1){
  595. RollbackAction action;
  596. std::string loc;
  597. {
  598. std::ostringstream os(std::ios::binary);
  599. from_inv.serialize(os);
  600. loc = os.str();
  601. }
  602. action.setModifyInventoryStack(loc, from_list, from_i,
  603. false, src_item);
  604. rollback->reportAction(action);
  605. }
  606. }
  607. }
  608. void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  609. {
  610. // Optional InventoryAction operation that is run on the client
  611. // to make lag less apparent.
  612. Inventory *inv_from = mgr->getInventory(from_inv);
  613. if(!inv_from)
  614. return;
  615. InventoryLocation current_player;
  616. current_player.setCurrentPlayer();
  617. Inventory *inv_player = mgr->getInventory(current_player);
  618. if(inv_from != inv_player)
  619. return;
  620. InventoryList *list_from = inv_from->getList(from_list);
  621. if(!list_from)
  622. return;
  623. if(count == 0)
  624. list_from->changeItem(from_i, ItemStack());
  625. else
  626. list_from->takeItem(from_i, count);
  627. mgr->setInventoryModified(from_inv);
  628. }
  629. /*
  630. ICraftAction
  631. */
  632. ICraftAction::ICraftAction(std::istream &is)
  633. {
  634. std::string ts;
  635. std::getline(is, ts, ' ');
  636. count = stoi(ts);
  637. std::getline(is, ts, ' ');
  638. craft_inv.deSerialize(ts);
  639. }
  640. void ICraftAction::apply(InventoryManager *mgr,
  641. ServerActiveObject *player, IGameDef *gamedef)
  642. {
  643. Inventory *inv_craft = mgr->getInventory(craft_inv);
  644. if (!inv_craft) {
  645. infostream << "ICraftAction::apply(): FAIL: inventory not found: "
  646. << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
  647. return;
  648. }
  649. InventoryList *list_craft = inv_craft->getList("craft");
  650. InventoryList *list_craftresult = inv_craft->getList("craftresult");
  651. InventoryList *list_main = inv_craft->getList("main");
  652. /*
  653. If a list doesn't exist or the source item doesn't exist
  654. */
  655. if (!list_craft) {
  656. infostream << "ICraftAction::apply(): FAIL: craft list not found: "
  657. << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
  658. return;
  659. }
  660. if (!list_craftresult) {
  661. infostream << "ICraftAction::apply(): FAIL: craftresult list not found: "
  662. << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
  663. return;
  664. }
  665. if (list_craftresult->getSize() < 1) {
  666. infostream << "ICraftAction::apply(): FAIL: craftresult list too short: "
  667. << "craft_inv=\"" << craft_inv.dump() << "\"" << std::endl;
  668. return;
  669. }
  670. ItemStack crafted;
  671. ItemStack craftresultitem;
  672. int count_remaining = count;
  673. std::vector<ItemStack> output_replacements;
  674. getCraftingResult(inv_craft, crafted, output_replacements, false, gamedef);
  675. PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
  676. bool found = !crafted.empty();
  677. while (found && list_craftresult->itemFits(0, crafted)) {
  678. InventoryList saved_craft_list = *list_craft;
  679. std::vector<ItemStack> temp;
  680. // Decrement input and add crafting output
  681. getCraftingResult(inv_craft, crafted, temp, true, gamedef);
  682. PLAYER_TO_SA(player)->item_OnCraft(crafted, player, &saved_craft_list, craft_inv);
  683. list_craftresult->addItem(0, crafted);
  684. mgr->setInventoryModified(craft_inv);
  685. // Add the new replacements to the list
  686. IItemDefManager *itemdef = gamedef->getItemDefManager();
  687. for (std::vector<ItemStack>::iterator it = temp.begin();
  688. it != temp.end(); it++) {
  689. for (std::vector<ItemStack>::iterator jt = output_replacements.begin();
  690. jt != output_replacements.end(); jt++) {
  691. if (it->name == jt->name) {
  692. *it = jt->addItem(*it, itemdef);
  693. if (it->empty())
  694. continue;
  695. }
  696. }
  697. output_replacements.push_back(*it);
  698. }
  699. actionstream << player->getDescription()
  700. << " crafts "
  701. << crafted.getItemString()
  702. << std::endl;
  703. // Decrement counter
  704. if (count_remaining == 1)
  705. break;
  706. else if (count_remaining > 1)
  707. count_remaining--;
  708. // Get next crafting result
  709. found = getCraftingResult(inv_craft, crafted, temp, false, gamedef);
  710. PLAYER_TO_SA(player)->item_CraftPredict(crafted, player, list_craft, craft_inv);
  711. found = !crafted.empty();
  712. }
  713. // Put the replacements in the inventory or drop them on the floor, if
  714. // the invenotry is full
  715. for (std::vector<ItemStack>::iterator it = output_replacements.begin();
  716. it != output_replacements.end(); it++) {
  717. if (list_main)
  718. *it = list_main->addItem(*it);
  719. if (it->empty())
  720. continue;
  721. u16 count = it->count;
  722. do {
  723. PLAYER_TO_SA(player)->item_OnDrop(*it, player,
  724. player->getBasePosition() + v3f(0,1,0));
  725. if (count >= it->count) {
  726. errorstream << "Couldn't drop replacement stack " <<
  727. it->getItemString() << " because drop loop didn't "
  728. "decrease count." << std::endl;
  729. break;
  730. }
  731. } while (!it->empty());
  732. }
  733. infostream<<"ICraftAction::apply(): crafted "
  734. <<" craft_inv=\""<<craft_inv.dump()<<"\""
  735. <<std::endl;
  736. }
  737. void ICraftAction::clientApply(InventoryManager *mgr, IGameDef *gamedef)
  738. {
  739. // Optional InventoryAction operation that is run on the client
  740. // to make lag less apparent.
  741. }
  742. // Crafting helper
  743. bool getCraftingResult(Inventory *inv, ItemStack& result,
  744. std::vector<ItemStack> &output_replacements,
  745. bool decrementInput, IGameDef *gamedef)
  746. {
  747. DSTACK(__FUNCTION_NAME);
  748. result.clear();
  749. // Get the InventoryList in which we will operate
  750. InventoryList *clist = inv->getList("craft");
  751. if(!clist)
  752. return false;
  753. // Mangle crafting grid to an another format
  754. CraftInput ci;
  755. ci.method = CRAFT_METHOD_NORMAL;
  756. ci.width = clist->getWidth() ? clist->getWidth() : 3;
  757. for(u16 i=0; i<clist->getSize(); i++)
  758. ci.items.push_back(clist->getItem(i));
  759. // Find out what is crafted and add it to result item slot
  760. CraftOutput co;
  761. bool found = gamedef->getCraftDefManager()->getCraftResult(
  762. ci, co, output_replacements, decrementInput, gamedef);
  763. if(found)
  764. result.deSerialize(co.item, gamedef->getItemDefManager());
  765. if(found && decrementInput)
  766. {
  767. // CraftInput has been changed, apply changes in clist
  768. for(u16 i=0; i<clist->getSize(); i++)
  769. {
  770. clist->changeItem(i, ci.items[i]);
  771. }
  772. }
  773. return found;
  774. }