craftdef.cpp 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  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 "craftdef.h"
  17. #include "irrlichttypes.h"
  18. #include "log.h"
  19. #include <sstream>
  20. #include <set>
  21. #include <algorithm>
  22. #include "gamedef.h"
  23. #include "inventory.h"
  24. #include "util/serialize.h"
  25. #include "util/string.h"
  26. #include "util/numeric.h"
  27. #include "util/strfnd.h"
  28. #include "exceptions.h"
  29. inline bool isGroupRecipeStr(const std::string &rec_name)
  30. {
  31. return str_starts_with(rec_name, std::string("group:"));
  32. }
  33. inline u64 getHashForString(const std::string &recipe_str)
  34. {
  35. /*errorstream << "Hashing craft string \"" << recipe_str << '"';*/
  36. return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
  37. }
  38. static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
  39. {
  40. switch (type) {
  41. case CRAFT_HASH_TYPE_ITEM_NAMES: {
  42. std::ostringstream os;
  43. bool is_first = true;
  44. for (const std::string &grid_name : grid_names) {
  45. if (!grid_name.empty()) {
  46. os << (is_first ? "" : "\n") << grid_name;
  47. is_first = false;
  48. }
  49. }
  50. return getHashForString(os.str());
  51. } case CRAFT_HASH_TYPE_COUNT: {
  52. u64 cnt = 0;
  53. for (const std::string &grid_name : grid_names)
  54. if (!grid_name.empty())
  55. cnt++;
  56. return cnt;
  57. } case CRAFT_HASH_TYPE_UNHASHED:
  58. return 0;
  59. }
  60. // invalid CraftHashType
  61. assert(false);
  62. return 0;
  63. }
  64. // Check if input matches recipe
  65. // Takes recipe groups into account
  66. static bool inputItemMatchesRecipe(const std::string &inp_name,
  67. const std::string &rec_name, IItemDefManager *idef)
  68. {
  69. // Exact name
  70. if (inp_name == rec_name)
  71. return true;
  72. // Group
  73. if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
  74. const struct ItemDefinition &def = idef->get(inp_name);
  75. Strfnd f(rec_name.substr(6));
  76. bool all_groups_match = true;
  77. do {
  78. std::string check_group = f.next(",");
  79. if (itemgroup_get(def.groups, check_group) == 0) {
  80. all_groups_match = false;
  81. break;
  82. }
  83. } while (!f.at_end());
  84. if (all_groups_match)
  85. return true;
  86. }
  87. // Didn't match
  88. return false;
  89. }
  90. // Deserialize an itemstring then return the name of the item
  91. static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
  92. {
  93. ItemStack item;
  94. item.deSerialize(itemstring, gamedef->idef());
  95. return item.name;
  96. }
  97. // (mapcar craftGetItemName itemstrings)
  98. static std::vector<std::string> craftGetItemNames(
  99. const std::vector<std::string> &itemstrings, IGameDef *gamedef)
  100. {
  101. std::vector<std::string> result;
  102. result.reserve(itemstrings.size());
  103. for (const auto &itemstring : itemstrings) {
  104. result.push_back(craftGetItemName(itemstring, gamedef));
  105. }
  106. return result;
  107. }
  108. // Get name of each item, and return them as a new list.
  109. static std::vector<std::string> craftGetItemNames(
  110. const std::vector<ItemStack> &items, IGameDef *gamedef)
  111. {
  112. std::vector<std::string> result;
  113. result.reserve(items.size());
  114. for (const auto &item : items) {
  115. result.push_back(item.name);
  116. }
  117. return result;
  118. }
  119. // convert a list of item names, to ItemStacks.
  120. static std::vector<ItemStack> craftGetItems(
  121. const std::vector<std::string> &items, IGameDef *gamedef)
  122. {
  123. std::vector<ItemStack> result;
  124. result.reserve(items.size());
  125. for (const auto &item : items) {
  126. result.emplace_back(std::string(item), (u16)1,
  127. (u16)0, gamedef->getItemDefManager());
  128. }
  129. return result;
  130. }
  131. // Compute bounding rectangle given a matrix of items
  132. // Returns false if every item is ""
  133. static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
  134. unsigned int &min_x, unsigned int &max_x,
  135. unsigned int &min_y, unsigned int &max_y)
  136. {
  137. bool success = false;
  138. unsigned int x = 0;
  139. unsigned int y = 0;
  140. for (const std::string &item : items) {
  141. // Is this an actual item?
  142. if (!item.empty()) {
  143. if (!success) {
  144. // This is the first nonempty item
  145. min_x = max_x = x;
  146. min_y = max_y = y;
  147. success = true;
  148. } else {
  149. if (x < min_x) min_x = x;
  150. if (x > max_x) max_x = x;
  151. if (y < min_y) min_y = y;
  152. if (y > max_y) max_y = y;
  153. }
  154. }
  155. // Step coordinate
  156. x++;
  157. if (x == width) {
  158. x = 0;
  159. y++;
  160. }
  161. }
  162. return success;
  163. }
  164. // Removes 1 from each item stack
  165. static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
  166. {
  167. for (auto &item : input.items) {
  168. if (item.count != 0)
  169. item.remove(1);
  170. }
  171. }
  172. // Removes 1 from each item stack with replacement support
  173. // Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
  174. // a water bucket will not be removed but replaced by an empty bucket.
  175. static void craftDecrementOrReplaceInput(CraftInput &input,
  176. std::vector<ItemStack> &output_replacements,
  177. const CraftReplacements &replacements,
  178. IGameDef *gamedef)
  179. {
  180. if (replacements.pairs.empty()) {
  181. craftDecrementInput(input, gamedef);
  182. return;
  183. }
  184. // Make a copy of the replacements pair list
  185. std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
  186. for (auto &item : input.items) {
  187. // Find an appropriate replacement
  188. bool found_replacement = false;
  189. for (auto j = pairs.begin(); j != pairs.end(); ++j) {
  190. if (inputItemMatchesRecipe(item.name, j->first, gamedef->idef())) {
  191. if (item.count == 1) {
  192. item.deSerialize(j->second, gamedef->idef());
  193. found_replacement = true;
  194. pairs.erase(j);
  195. break;
  196. }
  197. ItemStack rep;
  198. rep.deSerialize(j->second, gamedef->idef());
  199. item.remove(1);
  200. found_replacement = true;
  201. output_replacements.push_back(rep);
  202. break;
  203. }
  204. }
  205. // No replacement was found, simply decrement count by one
  206. if (!found_replacement && item.count > 0)
  207. item.remove(1);
  208. }
  209. }
  210. // Dump an itemstring matrix
  211. static std::string craftDumpMatrix(const std::vector<std::string> &items,
  212. unsigned int width)
  213. {
  214. std::ostringstream os(std::ios::binary);
  215. os << "{ ";
  216. unsigned int x = 0;
  217. for(std::vector<std::string>::size_type i = 0;
  218. i < items.size(); i++, x++) {
  219. if (x == width) {
  220. os << "; ";
  221. x = 0;
  222. } else if (x != 0) {
  223. os << ",";
  224. }
  225. os << '"' << items[i] << '"';
  226. }
  227. os << " }";
  228. return os.str();
  229. }
  230. // Dump an item matrix
  231. std::string craftDumpMatrix(const std::vector<ItemStack> &items,
  232. unsigned int width)
  233. {
  234. std::ostringstream os(std::ios::binary);
  235. os << "{ ";
  236. unsigned int x = 0;
  237. for (std::vector<ItemStack>::size_type i = 0;
  238. i < items.size(); i++, x++) {
  239. if (x == width) {
  240. os << "; ";
  241. x = 0;
  242. } else if (x != 0) {
  243. os << ",";
  244. }
  245. os << '"' << (items[i].getItemString()) << '"';
  246. }
  247. os << " }";
  248. return os.str();
  249. }
  250. /*
  251. CraftInput
  252. */
  253. std::string CraftInput::dump() const
  254. {
  255. std::ostringstream os(std::ios::binary);
  256. os << "(method=" << ((int)method) << ", items="
  257. << craftDumpMatrix(items, width) << ")";
  258. return os.str();
  259. }
  260. /*
  261. CraftOutput
  262. */
  263. std::string CraftOutput::dump() const
  264. {
  265. std::ostringstream os(std::ios::binary);
  266. os << "(item=\"" << item << "\", time=" << time << ")";
  267. return os.str();
  268. }
  269. /*
  270. CraftReplacements
  271. */
  272. std::string CraftReplacements::dump() const
  273. {
  274. std::ostringstream os(std::ios::binary);
  275. os<<"{";
  276. const char *sep = "";
  277. for (const auto &repl_p : pairs) {
  278. os << sep
  279. << '"' << (repl_p.first)
  280. << "\"=>\"" << (repl_p.second) << '"';
  281. sep = ",";
  282. }
  283. os << "}";
  284. return os.str();
  285. }
  286. /*
  287. CraftDefinitionShaped
  288. */
  289. std::string CraftDefinitionShaped::getName() const
  290. {
  291. return "shaped";
  292. }
  293. bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
  294. {
  295. if (input.method != CRAFT_METHOD_NORMAL)
  296. return false;
  297. // Get input item matrix
  298. std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
  299. unsigned int inp_width = input.width;
  300. if (inp_width == 0)
  301. return false;
  302. while (inp_names.size() % inp_width != 0)
  303. inp_names.emplace_back("");
  304. // Get input bounds
  305. unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0;
  306. if (!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x,
  307. inp_min_y, inp_max_y))
  308. return false; // it was empty
  309. std::vector<std::string> rec_names;
  310. if (hash_inited)
  311. rec_names = recipe_names;
  312. else
  313. rec_names = craftGetItemNames(recipe, gamedef);
  314. // Get recipe item matrix
  315. unsigned int rec_width = width;
  316. if (rec_width == 0)
  317. return false;
  318. while (rec_names.size() % rec_width != 0)
  319. rec_names.emplace_back("");
  320. // Get recipe bounds
  321. unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
  322. if (!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x,
  323. rec_min_y, rec_max_y))
  324. return false; // it was empty
  325. // Different sizes?
  326. if (inp_max_x - inp_min_x != rec_max_x - rec_min_x ||
  327. inp_max_y - inp_min_y != rec_max_y - rec_min_y)
  328. return false;
  329. // Verify that all item names in the bounding box are equal
  330. unsigned int w = inp_max_x - inp_min_x + 1;
  331. unsigned int h = inp_max_y - inp_min_y + 1;
  332. for (unsigned int y=0; y < h; y++) {
  333. unsigned int inp_y = (inp_min_y + y) * inp_width;
  334. unsigned int rec_y = (rec_min_y + y) * rec_width;
  335. for (unsigned int x=0; x < w; x++) {
  336. unsigned int inp_x = inp_min_x + x;
  337. unsigned int rec_x = rec_min_x + x;
  338. if (!inputItemMatchesRecipe(
  339. inp_names[inp_y + inp_x],
  340. rec_names[rec_y + rec_x], gamedef->idef())) {
  341. return false;
  342. }
  343. }
  344. }
  345. return true;
  346. }
  347. CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
  348. {
  349. return CraftOutput(output, 0);
  350. }
  351. CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *gamedef) const
  352. {
  353. return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
  354. }
  355. void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  356. IGameDef *gamedef) const
  357. {
  358. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  359. }
  360. CraftHashType CraftDefinitionShaped::getHashType() const
  361. {
  362. assert(hash_inited); // Pre-condition
  363. bool has_group = false;
  364. for (const auto &recipe_name : recipe_names) {
  365. if (isGroupRecipeStr(recipe_name)) {
  366. has_group = true;
  367. break;
  368. }
  369. }
  370. if (has_group)
  371. return CRAFT_HASH_TYPE_COUNT;
  372. return CRAFT_HASH_TYPE_ITEM_NAMES;
  373. }
  374. u64 CraftDefinitionShaped::getHash(CraftHashType type) const
  375. {
  376. assert(hash_inited); // Pre-condition
  377. assert((type == CRAFT_HASH_TYPE_ITEM_NAMES)
  378. || (type == CRAFT_HASH_TYPE_COUNT)); // Pre-condition
  379. std::vector<std::string> rec_names = recipe_names;
  380. std::sort(rec_names.begin(), rec_names.end());
  381. return getHashForGrid(type, rec_names);
  382. }
  383. void CraftDefinitionShaped::initHash(IGameDef *gamedef)
  384. {
  385. if (hash_inited)
  386. return;
  387. hash_inited = true;
  388. recipe_names = craftGetItemNames(recipe, gamedef);
  389. }
  390. std::string CraftDefinitionShaped::dump() const
  391. {
  392. std::ostringstream os(std::ios::binary);
  393. os << "(shaped, output=\"" << output
  394. << "\", recipe=" << craftDumpMatrix(recipe, width)
  395. << ", replacements=" << replacements.dump() << ")";
  396. return os.str();
  397. }
  398. /*
  399. CraftDefinitionShapeless
  400. */
  401. std::string CraftDefinitionShapeless::getName() const
  402. {
  403. return "shapeless";
  404. }
  405. bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
  406. {
  407. if (input.method != CRAFT_METHOD_NORMAL)
  408. return false;
  409. // Filter empty items out of input
  410. std::vector<std::string> input_filtered;
  411. for (const auto &item : input.items) {
  412. if (!item.name.empty())
  413. input_filtered.push_back(item.name);
  414. }
  415. // If there is a wrong number of items in input, no match
  416. if (input_filtered.size() != recipe.size()) {
  417. /*dstream<<"Number of input items ("<<input_filtered.size()
  418. <<") does not match recipe size ("<<recipe.size()<<") "
  419. <<"of recipe with output="<<output<<std::endl;*/
  420. return false;
  421. }
  422. std::vector<std::string> recipe_copy;
  423. if (hash_inited)
  424. recipe_copy = recipe_names;
  425. else {
  426. recipe_copy = craftGetItemNames(recipe, gamedef);
  427. std::sort(recipe_copy.begin(), recipe_copy.end());
  428. }
  429. // Try with all permutations of the recipe,
  430. // start from the lexicographically first permutation (=sorted),
  431. // recipe_names is pre-sorted
  432. do {
  433. // If all items match, the recipe matches
  434. bool all_match = true;
  435. //dstream<<"Testing recipe (output="<<output<<"):";
  436. for (size_t i=0; i<recipe.size(); i++) {
  437. //dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
  438. if (!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
  439. gamedef->idef())) {
  440. all_match = false;
  441. break;
  442. }
  443. }
  444. //dstream<<" -> match="<<all_match<<std::endl;
  445. if (all_match)
  446. return true;
  447. } while (std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
  448. return false;
  449. }
  450. CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
  451. {
  452. return CraftOutput(output, 0);
  453. }
  454. CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
  455. {
  456. return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef));
  457. }
  458. void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  459. IGameDef *gamedef) const
  460. {
  461. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  462. }
  463. CraftHashType CraftDefinitionShapeless::getHashType() const
  464. {
  465. assert(hash_inited); // Pre-condition
  466. bool has_group = false;
  467. for (const auto &recipe_name : recipe_names) {
  468. if (isGroupRecipeStr(recipe_name)) {
  469. has_group = true;
  470. break;
  471. }
  472. }
  473. if (has_group)
  474. return CRAFT_HASH_TYPE_COUNT;
  475. return CRAFT_HASH_TYPE_ITEM_NAMES;
  476. }
  477. u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
  478. {
  479. assert(hash_inited); // Pre-condition
  480. assert(type == CRAFT_HASH_TYPE_ITEM_NAMES
  481. || type == CRAFT_HASH_TYPE_COUNT); // Pre-condition
  482. return getHashForGrid(type, recipe_names);
  483. }
  484. void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
  485. {
  486. if (hash_inited)
  487. return;
  488. hash_inited = true;
  489. recipe_names = craftGetItemNames(recipe, gamedef);
  490. std::sort(recipe_names.begin(), recipe_names.end());
  491. }
  492. std::string CraftDefinitionShapeless::dump() const
  493. {
  494. std::ostringstream os(std::ios::binary);
  495. os << "(shapeless, output=\"" << output
  496. << "\", recipe=" << craftDumpMatrix(recipe, recipe.size())
  497. << ", replacements=" << replacements.dump() << ")";
  498. return os.str();
  499. }
  500. /*
  501. CraftDefinitionToolRepair
  502. */
  503. static ItemStack craftToolRepair(
  504. const ItemStack &item1,
  505. const ItemStack &item2,
  506. float additional_wear,
  507. IGameDef *gamedef)
  508. {
  509. IItemDefManager *idef = gamedef->idef();
  510. if (item1.count != 1 || item2.count != 1 || item1.name != item2.name
  511. || idef->get(item1.name).type != ITEM_TOOL
  512. || itemgroup_get(idef->get(item1.name).groups, "disable_repair") == 1) {
  513. // Failure
  514. return ItemStack();
  515. }
  516. s32 item1_uses = 65536 - (u32) item1.wear;
  517. s32 item2_uses = 65536 - (u32) item2.wear;
  518. s32 new_uses = item1_uses + item2_uses;
  519. s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
  520. if (new_wear >= 65536)
  521. return ItemStack();
  522. if (new_wear < 0)
  523. new_wear = 0;
  524. ItemStack repaired = item1;
  525. repaired.wear = new_wear;
  526. return repaired;
  527. }
  528. std::string CraftDefinitionToolRepair::getName() const
  529. {
  530. return "toolrepair";
  531. }
  532. bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
  533. {
  534. if (input.method != CRAFT_METHOD_NORMAL)
  535. return false;
  536. ItemStack item1;
  537. ItemStack item2;
  538. for (const auto &item : input.items) {
  539. if (!item.empty()) {
  540. if (item1.empty())
  541. item1 = item;
  542. else if (item2.empty())
  543. item2 = item;
  544. else
  545. return false;
  546. }
  547. }
  548. ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
  549. return !repaired.empty();
  550. }
  551. CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
  552. {
  553. ItemStack item1;
  554. ItemStack item2;
  555. for (const auto &item : input.items) {
  556. if (!item.empty()) {
  557. if (item1.empty())
  558. item1 = item;
  559. else if (item2.empty())
  560. item2 = item;
  561. }
  562. }
  563. ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
  564. return CraftOutput(repaired.getItemString(), 0);
  565. }
  566. CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const
  567. {
  568. std::vector<ItemStack> stack;
  569. stack.emplace_back();
  570. return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack);
  571. }
  572. void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  573. IGameDef *gamedef) const
  574. {
  575. craftDecrementInput(input, gamedef);
  576. }
  577. std::string CraftDefinitionToolRepair::dump() const
  578. {
  579. std::ostringstream os(std::ios::binary);
  580. os << "(toolrepair, additional_wear=" << additional_wear << ")";
  581. return os.str();
  582. }
  583. /*
  584. CraftDefinitionCooking
  585. */
  586. std::string CraftDefinitionCooking::getName() const
  587. {
  588. return "cooking";
  589. }
  590. bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
  591. {
  592. if (input.method != CRAFT_METHOD_COOKING)
  593. return false;
  594. // Filter empty items out of input
  595. std::vector<std::string> input_filtered;
  596. for (const auto &item : input.items) {
  597. const std::string &name = item.name;
  598. if (!name.empty())
  599. input_filtered.push_back(name);
  600. }
  601. // If there is a wrong number of items in input, no match
  602. if (input_filtered.size() != 1) {
  603. /*dstream<<"Number of input items ("<<input_filtered.size()
  604. <<") does not match recipe size (1) "
  605. <<"of cooking recipe with output="<<output<<std::endl;*/
  606. return false;
  607. }
  608. // Check the single input item
  609. return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
  610. }
  611. CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
  612. {
  613. return CraftOutput(output, cooktime);
  614. }
  615. CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef *gamedef) const
  616. {
  617. std::vector<std::string> rec;
  618. rec.push_back(recipe);
  619. return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
  620. }
  621. void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  622. IGameDef *gamedef) const
  623. {
  624. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  625. }
  626. CraftHashType CraftDefinitionCooking::getHashType() const
  627. {
  628. if (isGroupRecipeStr(recipe_name))
  629. return CRAFT_HASH_TYPE_COUNT;
  630. return CRAFT_HASH_TYPE_ITEM_NAMES;
  631. }
  632. u64 CraftDefinitionCooking::getHash(CraftHashType type) const
  633. {
  634. if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
  635. return getHashForString(recipe_name);
  636. }
  637. if (type == CRAFT_HASH_TYPE_COUNT) {
  638. return 1;
  639. }
  640. // illegal hash type for this CraftDefinition (pre-condition)
  641. assert(false);
  642. return 0;
  643. }
  644. void CraftDefinitionCooking::initHash(IGameDef *gamedef)
  645. {
  646. if (hash_inited)
  647. return;
  648. hash_inited = true;
  649. recipe_name = craftGetItemName(recipe, gamedef);
  650. }
  651. std::string CraftDefinitionCooking::dump() const
  652. {
  653. std::ostringstream os(std::ios::binary);
  654. os << "(cooking, output=\"" << output
  655. << "\", recipe=\"" << recipe
  656. << "\", cooktime=" << cooktime << ")"
  657. << ", replacements=" << replacements.dump() << ")";
  658. return os.str();
  659. }
  660. /*
  661. CraftDefinitionFuel
  662. */
  663. std::string CraftDefinitionFuel::getName() const
  664. {
  665. return "fuel";
  666. }
  667. bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
  668. {
  669. if (input.method != CRAFT_METHOD_FUEL)
  670. return false;
  671. // Filter empty items out of input
  672. std::vector<std::string> input_filtered;
  673. for (const auto &item : input.items) {
  674. const std::string &name = item.name;
  675. if (!name.empty())
  676. input_filtered.push_back(name);
  677. }
  678. // If there is a wrong number of items in input, no match
  679. if (input_filtered.size() != 1) {
  680. /*dstream<<"Number of input items ("<<input_filtered.size()
  681. <<") does not match recipe size (1) "
  682. <<"of fuel recipe with burntime="<<burntime<<std::endl;*/
  683. return false;
  684. }
  685. // Check the single input item
  686. return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
  687. }
  688. CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
  689. {
  690. return CraftOutput("", burntime);
  691. }
  692. CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *gamedef) const
  693. {
  694. std::vector<std::string> rec;
  695. rec.push_back(recipe);
  696. return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
  697. }
  698. void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  699. IGameDef *gamedef) const
  700. {
  701. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  702. }
  703. CraftHashType CraftDefinitionFuel::getHashType() const
  704. {
  705. if (isGroupRecipeStr(recipe_name))
  706. return CRAFT_HASH_TYPE_COUNT;
  707. return CRAFT_HASH_TYPE_ITEM_NAMES;
  708. }
  709. u64 CraftDefinitionFuel::getHash(CraftHashType type) const
  710. {
  711. if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
  712. return getHashForString(recipe_name);
  713. }
  714. if (type == CRAFT_HASH_TYPE_COUNT) {
  715. return 1;
  716. }
  717. // illegal hash type for this CraftDefinition (pre-condition)
  718. assert(false);
  719. return 0;
  720. }
  721. void CraftDefinitionFuel::initHash(IGameDef *gamedef)
  722. {
  723. if (hash_inited)
  724. return;
  725. hash_inited = true;
  726. recipe_name = craftGetItemName(recipe, gamedef);
  727. }
  728. std::string CraftDefinitionFuel::dump() const
  729. {
  730. std::ostringstream os(std::ios::binary);
  731. os << "(fuel, recipe=\"" << recipe
  732. << "\", burntime=" << burntime << ")"
  733. << ", replacements=" << replacements.dump() << ")";
  734. return os.str();
  735. }
  736. /*
  737. Craft definition manager
  738. */
  739. class CCraftDefManager: public IWritableCraftDefManager
  740. {
  741. public:
  742. CCraftDefManager()
  743. {
  744. m_craft_defs.resize(craft_hash_type_max + 1);
  745. }
  746. virtual ~CCraftDefManager()
  747. {
  748. clear();
  749. }
  750. virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
  751. std::vector<ItemStack> &output_replacement, bool decrementInput,
  752. IGameDef *gamedef) const
  753. {
  754. output.item = "";
  755. output.time = 0;
  756. // If all input items are empty, abort.
  757. bool all_empty = true;
  758. for (const auto &item : input.items) {
  759. if (!item.empty()) {
  760. all_empty = false;
  761. break;
  762. }
  763. }
  764. if (all_empty)
  765. return false;
  766. std::vector<std::string> input_names;
  767. input_names = craftGetItemNames(input.items, gamedef);
  768. std::sort(input_names.begin(), input_names.end());
  769. // Try hash types with increasing collision rate, and return if found.
  770. for (int type = 0; type <= craft_hash_type_max; type++) {
  771. u64 hash = getHashForGrid((CraftHashType) type, input_names);
  772. /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/
  773. // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
  774. // but that doesn't compile for some reason. This does.
  775. auto col_iter = (m_craft_defs[type]).find(hash);
  776. if (col_iter == (m_craft_defs[type]).end())
  777. continue;
  778. const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
  779. // Walk crafting definitions from back to front, so that later
  780. // definitions can override earlier ones.
  781. for (std::vector<CraftDefinition*>::size_type
  782. i = hash_collisions.size(); i > 0; i--) {
  783. CraftDefinition *def = hash_collisions[i - 1];
  784. /*errorstream << "Checking " << input.dump() << std::endl
  785. << " against " << def->dump() << std::endl;*/
  786. if (def->check(input, gamedef)) {
  787. // Check if the crafted node/item exists
  788. CraftOutput out = def->getOutput(input, gamedef);
  789. ItemStack is;
  790. is.deSerialize(out.item, gamedef->idef());
  791. if (!is.isKnown(gamedef->idef())) {
  792. infostream << "trying to craft non-existent "
  793. << out.item << ", ignoring recipe" << std::endl;
  794. continue;
  795. }
  796. // Get output, then decrement input (if requested)
  797. output = out;
  798. if (decrementInput)
  799. def->decrementInput(input, output_replacement, gamedef);
  800. /*errorstream << "Check RETURNS TRUE" << std::endl;*/
  801. return true;
  802. }
  803. }
  804. }
  805. return false;
  806. }
  807. virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
  808. IGameDef *gamedef, unsigned limit=0) const
  809. {
  810. std::vector<CraftDefinition*> recipes;
  811. auto vec_iter = m_output_craft_definitions.find(output.item);
  812. if (vec_iter == m_output_craft_definitions.end())
  813. return recipes;
  814. const std::vector<CraftDefinition*> &vec = vec_iter->second;
  815. recipes.reserve(limit ? MYMIN(limit, vec.size()) : vec.size());
  816. for (std::vector<CraftDefinition*>::size_type i = vec.size();
  817. i > 0; i--) {
  818. CraftDefinition *def = vec[i - 1];
  819. if (limit && recipes.size() >= limit)
  820. break;
  821. recipes.push_back(def);
  822. }
  823. return recipes;
  824. }
  825. virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef)
  826. {
  827. auto vec_iter = m_output_craft_definitions.find(output.item);
  828. if (vec_iter == m_output_craft_definitions.end())
  829. return false;
  830. std::vector<CraftDefinition*> &vec = vec_iter->second;
  831. for (auto def : vec) {
  832. // Recipes are not yet hashed at this point
  833. std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  834. std::vector<CraftDefinition*> new_vec_by_input;
  835. /* We will preallocate necessary memory addresses, so we don't need to reallocate them later.
  836. This would save us some performance. */
  837. new_vec_by_input.reserve(unhashed_inputs_vec.size());
  838. for (auto &i2 : unhashed_inputs_vec) {
  839. if (def != i2) {
  840. new_vec_by_input.push_back(i2);
  841. }
  842. }
  843. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
  844. }
  845. m_output_craft_definitions.erase(output.item);
  846. return true;
  847. }
  848. virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width,
  849. const std::vector<std::string> &recipe, IGameDef *gamedef)
  850. {
  851. bool all_empty = true;
  852. for (const auto &i : recipe) {
  853. if (!i.empty()) {
  854. all_empty = false;
  855. break;
  856. }
  857. }
  858. if (all_empty)
  859. return false;
  860. CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef));
  861. // Recipes are not yet hashed at this point
  862. std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  863. std::vector<CraftDefinition*> new_vec_by_input;
  864. bool got_hit = false;
  865. for (std::vector<CraftDefinition*>::size_type
  866. i = unhashed_inputs_vec.size(); i > 0; i--) {
  867. CraftDefinition *def = unhashed_inputs_vec[i - 1];
  868. /* If the input doesn't match the recipe definition, this recipe definition later
  869. will be added back in source map. */
  870. if (!def->check(input, gamedef)) {
  871. new_vec_by_input.push_back(def);
  872. continue;
  873. }
  874. CraftOutput output = def->getOutput(input, gamedef);
  875. got_hit = true;
  876. auto vec_iter = m_output_craft_definitions.find(output.item);
  877. if (vec_iter == m_output_craft_definitions.end())
  878. continue;
  879. std::vector<CraftDefinition*> &vec = vec_iter->second;
  880. std::vector<CraftDefinition*> new_vec_by_output;
  881. /* We will preallocate necessary memory addresses, so we don't need
  882. to reallocate them later. This would save us some performance. */
  883. new_vec_by_output.reserve(vec.size());
  884. for (auto &vec_i : vec) {
  885. /* If pointers from map by input and output are not same,
  886. we will add 'CraftDefinition*' to a new vector. */
  887. if (def != vec_i) {
  888. /* Adding dereferenced iterator value (which are
  889. 'CraftDefinition' reference) to a new vector. */
  890. new_vec_by_output.push_back(vec_i);
  891. }
  892. }
  893. // Swaps assigned to current key value with new vector for output map.
  894. m_output_craft_definitions[output.item].swap(new_vec_by_output);
  895. }
  896. if (got_hit)
  897. // Swaps value with new vector for input map.
  898. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
  899. return got_hit;
  900. }
  901. virtual std::string dump() const
  902. {
  903. std::ostringstream os(std::ios::binary);
  904. os << "Crafting definitions:\n";
  905. for (int type = 0; type <= craft_hash_type_max; ++type) {
  906. for (auto it = m_craft_defs[type].begin();
  907. it != m_craft_defs[type].end(); ++it) {
  908. for (std::vector<CraftDefinition*>::size_type i = 0;
  909. i < it->second.size(); i++) {
  910. os << "type " << type
  911. << " hash " << it->first
  912. << " def " << it->second[i]->dump()
  913. << "\n";
  914. }
  915. }
  916. }
  917. return os.str();
  918. }
  919. virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
  920. {
  921. verbosestream << "registerCraft: registering craft definition: "
  922. << def->dump() << std::endl;
  923. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
  924. CraftInput input;
  925. std::string output_name = craftGetItemName(
  926. def->getOutput(input, gamedef).item, gamedef);
  927. m_output_craft_definitions[output_name].push_back(def);
  928. }
  929. virtual void clear()
  930. {
  931. for (int type = 0; type <= craft_hash_type_max; ++type) {
  932. for (auto &it : m_craft_defs[type]) {
  933. for (auto &iit : it.second) {
  934. delete iit;
  935. }
  936. it.second.clear();
  937. }
  938. m_craft_defs[type].clear();
  939. }
  940. m_output_craft_definitions.clear();
  941. }
  942. virtual void initHashes(IGameDef *gamedef)
  943. {
  944. // Move the CraftDefs from the unhashed layer into layers higher up.
  945. std::vector<CraftDefinition *> &unhashed =
  946. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  947. for (auto def : unhashed) {
  948. // Initialize and get the definition's hash
  949. def->initHash(gamedef);
  950. CraftHashType type = def->getHashType();
  951. u64 hash = def->getHash(type);
  952. // Enter the definition
  953. m_craft_defs[type][hash].push_back(def);
  954. }
  955. unhashed.clear();
  956. }
  957. private:
  958. std::vector<std::unordered_map<u64, std::vector<CraftDefinition*> > >
  959. m_craft_defs;
  960. std::unordered_map<std::string, std::vector<CraftDefinition*> >
  961. m_output_craft_definitions;
  962. };
  963. IWritableCraftDefManager* createCraftDefManager()
  964. {
  965. return new CCraftDefManager();
  966. }