]> git.lizzy.rs Git - minetest-m13.git/blob - src/craftdef.cpp
Update to 4.6 base
[minetest-m13.git] / src / craftdef.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "craftdef.h"
21
22 #include "irrlichttypes.h"
23 #include "log.h"
24 #include <sstream>
25 #include <set>
26 #include "utility.h"
27 #include "gamedef.h"
28 #include "inventory.h"
29
30
31 // Deserialize an itemstring then return the name of the item
32 static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
33 {
34         ItemStack item;
35         item.deSerialize(itemstring, gamedef->idef());
36         return item.name;
37 }
38
39 // (mapcar craftGetItemName itemstrings)
40 static std::vector<std::string> craftGetItemNames(
41                 const std::vector<std::string> &itemstrings, IGameDef *gamedef)
42 {
43         std::vector<std::string> result;
44         for(std::vector<std::string>::const_iterator
45                         i = itemstrings.begin();
46                         i != itemstrings.end(); i++)
47         {
48                 result.push_back(craftGetItemName(*i, gamedef));
49         }
50         return result;
51 }
52
53 // Get name of each item, and return them as a new list.
54 static std::vector<std::string> craftGetItemNames(
55                 const std::vector<ItemStack> &items, IGameDef *gamedef)
56 {
57         std::vector<std::string> result;
58         for(std::vector<ItemStack>::const_iterator
59                         i = items.begin();
60                         i != items.end(); i++)
61         {
62                 result.push_back(i->name);
63         }
64         return result;
65 }
66
67 // Compute bounding rectangle given a matrix of items
68 // Returns false if every item is ""
69 static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
70                 unsigned int &min_x, unsigned int &max_x,
71                 unsigned int &min_y, unsigned int &max_y)
72 {
73         bool success = false;
74         unsigned int x = 0;
75         unsigned int y = 0;
76         for(std::vector<std::string>::const_iterator
77                         i = items.begin();
78                         i != items.end(); i++)
79         {
80                 if(*i != "")  // Is this an actual item?
81                 {
82                         if(!success)
83                         {
84                                 // This is the first nonempty item
85                                 min_x = max_x = x;
86                                 min_y = max_y = y;
87                                 success = true;
88                         }
89                         else
90                         {
91                                 if(x < min_x) min_x = x;
92                                 if(x > max_x) max_x = x;
93                                 if(y < min_y) min_y = y;
94                                 if(y > max_y) max_y = y;
95                         }
96                 }
97
98                 // Step coordinate
99                 x++;
100                 if(x == width)
101                 {
102                         x = 0;
103                         y++;
104                 }
105         }
106         return success;
107 }
108
109 // Convert a list of item names to a multiset
110 static std::multiset<std::string> craftMakeMultiset(const std::vector<std::string> &names)
111 {
112         std::multiset<std::string> set;
113         for(std::vector<std::string>::const_iterator
114                         i = names.begin();
115                         i != names.end(); i++)
116         {
117                 if(*i != "")
118                         set.insert(*i);
119         }
120         return set;
121 }
122
123 // Removes 1 from each item stack
124 static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
125 {
126         for(std::vector<ItemStack>::iterator
127                         i = input.items.begin();
128                         i != input.items.end(); i++)
129         {
130                 if(i->count != 0)
131                         i->remove(1);
132         }
133 }
134
135 // Removes 1 from each item stack with replacement support
136 // Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
137 //   a water bucket will not be removed but replaced by an empty bucket.
138 static void craftDecrementOrReplaceInput(CraftInput &input,
139                 const CraftReplacements &replacements,
140                 IGameDef *gamedef)
141 {
142         if(replacements.pairs.empty())
143         {
144                 craftDecrementInput(input, gamedef);
145                 return;
146         }
147
148         // Make a copy of the replacements pair list
149         std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
150
151         for(std::vector<ItemStack>::iterator
152                         i = input.items.begin();
153                         i != input.items.end(); i++)
154         {
155                 if(i->count == 1)
156                 {
157                         // Find an appropriate replacement
158                         bool found_replacement = false;
159                         for(std::vector<std::pair<std::string, std::string> >::iterator
160                                         j = pairs.begin();
161                                         j != pairs.end(); j++)
162                         {
163                                 ItemStack from_item;
164                                 from_item.deSerialize(j->first, gamedef->idef());
165                                 if(i->name == from_item.name)
166                                 {
167                                         i->deSerialize(j->second, gamedef->idef());
168                                         found_replacement = true;
169                                         pairs.erase(j);
170                                         break;
171                                 }
172                         }
173                         // No replacement was found, simply decrement count to zero
174                         if(!found_replacement)
175                                 i->remove(1);
176                 }
177                 else if(i->count >= 2)
178                 {
179                         // Ignore replacements for items with count >= 2
180                         i->remove(1);
181                 }
182         }
183 }
184
185 // Dump an itemstring matrix
186 static std::string craftDumpMatrix(const std::vector<std::string> &items,
187                 unsigned int width)
188 {
189         std::ostringstream os(std::ios::binary);
190         os<<"{ ";
191         unsigned int x = 0;
192         for(std::vector<std::string>::const_iterator
193                         i = items.begin();
194                         i != items.end(); i++, x++)
195         {
196                 if(x == width)
197                 {
198                         os<<"; ";
199                         x = 0;
200                 }
201                 else if(x != 0)
202                 {
203                         os<<",";
204                 }
205                 os<<"\""<<(*i)<<"\"";
206         }
207         os<<" }";
208         return os.str();
209 }
210
211 // Dump an item matrix
212 std::string craftDumpMatrix(const std::vector<ItemStack> &items,
213                 unsigned int width)
214 {
215         std::ostringstream os(std::ios::binary);
216         os<<"{ ";
217         unsigned int x = 0;
218         for(std::vector<ItemStack>::const_iterator
219                         i = items.begin();
220                         i != items.end(); i++, x++)
221         {
222                 if(x == width)
223                 {
224                         os<<"; ";
225                         x = 0;
226                 }
227                 else if(x != 0)
228                 {
229                         os<<",";
230                 }
231                 os<<"\""<<(i->getItemString())<<"\"";
232         }
233         os<<" }";
234         return os.str();
235 }
236
237
238 /*
239         CraftInput
240 */
241
242 std::string CraftInput::dump() const
243 {
244         std::ostringstream os(std::ios::binary);
245         os<<"(method="<<((int)method)<<", items="<<craftDumpMatrix(items, width)<<")";
246         return os.str();
247 }
248
249 /*
250         CraftOutput
251 */
252
253 std::string CraftOutput::dump() const
254 {
255         std::ostringstream os(std::ios::binary);
256         os<<"(item=\""<<item<<"\", time="<<time<<")";
257         return os.str();
258 }
259
260 /*
261         CraftReplacements
262 */
263 std::string CraftReplacements::dump() const
264 {
265         std::ostringstream os(std::ios::binary);
266         os<<"{";
267         const char *sep = "";
268         for(std::vector<std::pair<std::string, std::string> >::const_iterator
269                         i = pairs.begin();
270                         i != pairs.end(); i++)
271         {
272                 os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
273                 sep = ",";
274         }
275         os<<"}";
276         return os.str();
277 }
278
279
280 /*
281         CraftDefinition
282 */
283
284 void CraftDefinition::serialize(std::ostream &os) const
285 {
286         writeU8(os, 1); // version
287         os<<serializeString(getName());
288         serializeBody(os);
289 }
290
291 CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
292 {
293         int version = readU8(is);
294         if(version != 1) throw SerializationError(
295                         "unsupported CraftDefinition version");
296         std::string name = deSerializeString(is);
297         CraftDefinition *def = NULL;
298         if(name == "shaped")
299         {
300                 def = new CraftDefinitionShaped;
301         }
302         else if(name == "shapeless")
303         {
304                 def = new CraftDefinitionShapeless;
305         }
306         else if(name == "toolrepair")
307         {
308                 def = new CraftDefinitionToolRepair;
309         }
310         else if(name == "cooking")
311         {
312                 def = new CraftDefinitionCooking;
313         }
314         else if(name == "fuel")
315         {
316                 def = new CraftDefinitionFuel;
317         }
318         else
319         {
320                 infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
321                 throw SerializationError("Unknown CraftDefinition name");
322         }
323         def->deSerializeBody(is, version);
324         return def;
325 }
326
327 /*
328         CraftDefinitionShaped
329 */
330
331 std::string CraftDefinitionShaped::getName() const
332 {
333         return "shaped";
334 }
335
336 bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
337 {
338         if(input.method != CRAFT_METHOD_NORMAL)
339                 return false;
340
341         // Get input item matrix
342         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
343         unsigned int inp_width = input.width;
344         if(inp_width == 0)
345                 return false;
346         while(inp_names.size() % inp_width != 0)
347                 inp_names.push_back("");
348
349         // Get input bounds
350         unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
351         if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
352                 return false;  // it was empty
353
354         // Get recipe item matrix
355         std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
356         unsigned int rec_width = width;
357         if(rec_width == 0)
358                 return false;
359         while(rec_names.size() % rec_width != 0)
360                 rec_names.push_back("");
361
362         // Get recipe bounds
363         unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
364         if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
365                 return false;  // it was empty
366
367         // Different sizes?
368         if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
369                 return false;
370         if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
371                 return false;
372
373         // Verify that all item names in the bounding box are equal
374         unsigned int w = inp_max_x - inp_min_x + 1;
375         unsigned int h = inp_max_y - inp_min_y + 1;
376         for(unsigned int y=0; y<h; y++)
377         for(unsigned int x=0; x<w; x++)
378         {
379                 unsigned int inp_x = inp_min_x + x;
380                 unsigned int inp_y = inp_min_y + y;
381                 unsigned int rec_x = rec_min_x + x;
382                 unsigned int rec_y = rec_min_y + y;
383
384                 if(
385                         inp_names[inp_y * inp_width + inp_x] !=
386                         rec_names[rec_y * rec_width + rec_x]
387                 ){
388                         return false;
389                 }
390         }
391
392         return true;
393 }
394
395 CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
396 {
397         return CraftOutput(output, 0);
398 }
399
400 void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
401 {
402         craftDecrementOrReplaceInput(input, replacements, gamedef);
403 }
404
405 std::string CraftDefinitionShaped::dump() const
406 {
407         std::ostringstream os(std::ios::binary);
408         os<<"(shaped, output=\""<<output
409                 <<"\", recipe="<<craftDumpMatrix(recipe, width)
410                 <<", replacements="<<replacements.dump()<<")";
411         return os.str();
412 }
413
414 void CraftDefinitionShaped::serializeBody(std::ostream &os) const
415 {
416         os<<serializeString(output);
417         writeU16(os, width);
418         writeU16(os, recipe.size());
419         for(u32 i=0; i<recipe.size(); i++)
420                 os<<serializeString(recipe[i]);
421         writeU16(os, replacements.pairs.size());
422         for(u32 i=0; i<replacements.pairs.size(); i++)
423         {
424                 os<<serializeString(replacements.pairs[i].first);
425                 os<<serializeString(replacements.pairs[i].second);
426         }
427 }
428
429 void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
430 {
431         if(version != 1) throw SerializationError(
432                         "unsupported CraftDefinitionShaped version");
433         output = deSerializeString(is);
434         width = readU16(is);
435         recipe.clear();
436         u32 count = readU16(is);
437         for(u32 i=0; i<count; i++)
438                 recipe.push_back(deSerializeString(is));
439         replacements.pairs.clear();
440         count = readU16(is);
441         for(u32 i=0; i<count; i++)
442         {
443                 std::string first = deSerializeString(is);
444                 std::string second = deSerializeString(is);
445                 replacements.pairs.push_back(std::make_pair(first, second));
446         }
447 }
448
449 /*
450         CraftDefinitionShapeless
451 */
452
453 std::string CraftDefinitionShapeless::getName() const
454 {
455         return "shapeless";
456 }
457
458 bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
459 {
460         if(input.method != CRAFT_METHOD_NORMAL)
461                 return false;
462
463         // Get input item multiset
464         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
465         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
466
467         // Get recipe item multiset
468         std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
469         std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
470
471         // Recipe is matched when the multisets coincide
472         return inp_names_multiset == rec_names_multiset;
473 }
474
475 CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
476 {
477         return CraftOutput(output, 0);
478 }
479
480 void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
481 {
482         craftDecrementOrReplaceInput(input, replacements, gamedef);
483 }
484
485 std::string CraftDefinitionShapeless::dump() const
486 {
487         std::ostringstream os(std::ios::binary);
488         os<<"(shapeless, output=\""<<output
489                 <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
490                 <<", replacements="<<replacements.dump()<<")";
491         return os.str();
492 }
493
494 void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
495 {
496         os<<serializeString(output);
497         writeU16(os, recipe.size());
498         for(u32 i=0; i<recipe.size(); i++)
499                 os<<serializeString(recipe[i]);
500         writeU16(os, replacements.pairs.size());
501         for(u32 i=0; i<replacements.pairs.size(); i++)
502         {
503                 os<<serializeString(replacements.pairs[i].first);
504                 os<<serializeString(replacements.pairs[i].second);
505         }
506 }
507
508 void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
509 {
510         if(version != 1) throw SerializationError(
511                         "unsupported CraftDefinitionShapeless version");
512         output = deSerializeString(is);
513         recipe.clear();
514         u32 count = readU16(is);
515         for(u32 i=0; i<count; i++)
516                 recipe.push_back(deSerializeString(is));
517         replacements.pairs.clear();
518         count = readU16(is);
519         for(u32 i=0; i<count; i++)
520         {
521                 std::string first = deSerializeString(is);
522                 std::string second = deSerializeString(is);
523                 replacements.pairs.push_back(std::make_pair(first, second));
524         }
525 }
526
527 /*
528         CraftDefinitionToolRepair
529 */
530
531 static ItemStack craftToolRepair(
532                 const ItemStack &item1,
533                 const ItemStack &item2,
534                 float additional_wear,
535                 IGameDef *gamedef)
536 {
537         IItemDefManager *idef = gamedef->idef();
538         if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
539                         || idef->get(item1.name).type != ITEM_TOOL
540                         || idef->get(item2.name).type != ITEM_TOOL)
541         {
542                 // Failure
543                 return ItemStack();
544         }
545
546         s32 item1_uses = 65536 - (u32) item1.wear;
547         s32 item2_uses = 65536 - (u32) item2.wear;
548         s32 new_uses = item1_uses + item2_uses;
549         s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
550         if(new_wear >= 65536)
551                 return ItemStack();
552         if(new_wear < 0)
553                 new_wear = 0;
554
555         ItemStack repaired = item1;
556         repaired.wear = new_wear;
557         return repaired;
558 }
559
560 std::string CraftDefinitionToolRepair::getName() const
561 {
562         return "toolrepair";
563 }
564
565 bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
566 {
567         if(input.method != CRAFT_METHOD_NORMAL)
568                 return false;
569
570         ItemStack item1;
571         ItemStack item2;
572         for(std::vector<ItemStack>::const_iterator
573                         i = input.items.begin();
574                         i != input.items.end(); i++)
575         {
576                 if(!i->empty())
577                 {
578                         if(item1.empty())
579                                 item1 = *i;
580                         else if(item2.empty())
581                                 item2 = *i;
582                         else
583                                 return false;
584                 }
585         }
586         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
587         return !repaired.empty();
588 }
589
590 CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
591 {
592         ItemStack item1;
593         ItemStack item2;
594         for(std::vector<ItemStack>::const_iterator
595                         i = input.items.begin();
596                         i != input.items.end(); i++)
597         {
598                 if(!i->empty())
599                 {
600                         if(item1.empty())
601                                 item1 = *i;
602                         else if(item2.empty())
603                                 item2 = *i;
604                 }
605         }
606         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
607         return CraftOutput(repaired.getItemString(), 0);
608 }
609
610 void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
611 {
612         craftDecrementInput(input, gamedef);
613 }
614
615 std::string CraftDefinitionToolRepair::dump() const
616 {
617         std::ostringstream os(std::ios::binary);
618         os<<"(toolrepair, additional_wear="<<additional_wear<<")";
619         return os.str();
620 }
621
622 void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
623 {
624         writeF1000(os, additional_wear);
625 }
626
627 void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
628 {
629         if(version != 1) throw SerializationError(
630                         "unsupported CraftDefinitionToolRepair version");
631         additional_wear = readF1000(is);
632 }
633
634 /*
635         CraftDefinitionCooking
636 */
637
638 std::string CraftDefinitionCooking::getName() const
639 {
640         return "cooking";
641 }
642
643 bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
644 {
645         if(input.method != CRAFT_METHOD_COOKING)
646                 return false;
647
648         // Get input item multiset
649         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
650         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
651
652         // Get recipe item multiset
653         std::multiset<std::string> rec_names_multiset;
654         rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
655
656         // Recipe is matched when the multisets coincide
657         return inp_names_multiset == rec_names_multiset;
658 }
659
660 CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
661 {
662         return CraftOutput(output, cooktime);
663 }
664
665 void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
666 {
667         craftDecrementInput(input, gamedef);
668 }
669
670 std::string CraftDefinitionCooking::dump() const
671 {
672         std::ostringstream os(std::ios::binary);
673         os<<"(cooking, output=\""<<output
674                 <<"\", recipe=\""<<recipe
675                 <<"\", cooktime="<<cooktime<<")";
676         return os.str();
677 }
678
679 void CraftDefinitionCooking::serializeBody(std::ostream &os) const
680 {
681         os<<serializeString(output);
682         os<<serializeString(recipe);
683         writeF1000(os, cooktime);
684 }
685
686 void CraftDefinitionCooking::deSerializeBody(std::istream &is, int version)
687 {
688         if(version != 1) throw SerializationError(
689                         "unsupported CraftDefinitionCooking version");
690         output = deSerializeString(is);
691         recipe = deSerializeString(is);
692         cooktime = readF1000(is);
693 }
694
695 /*
696         CraftDefinitionFuel
697 */
698
699 std::string CraftDefinitionFuel::getName() const
700 {
701         return "fuel";
702 }
703
704 bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
705 {
706         if(input.method != CRAFT_METHOD_FUEL)
707                 return false;
708
709         // Get input item multiset
710         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
711         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
712
713         // Get recipe item multiset
714         std::multiset<std::string> rec_names_multiset;
715         rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
716
717         // Recipe is matched when the multisets coincide
718         return inp_names_multiset == rec_names_multiset;
719 }
720
721 CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
722 {
723         return CraftOutput("", burntime);
724 }
725
726 void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
727 {
728         craftDecrementInput(input, gamedef);
729 }
730
731 std::string CraftDefinitionFuel::dump() const
732 {
733         std::ostringstream os(std::ios::binary);
734         os<<"(fuel, recipe=\""<<recipe
735                 <<"\", burntime="<<burntime<<")";
736         return os.str();
737 }
738
739 void CraftDefinitionFuel::serializeBody(std::ostream &os) const
740 {
741         os<<serializeString(recipe);
742         writeF1000(os, burntime);
743 }
744
745 void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
746 {
747         if(version != 1) throw SerializationError(
748                         "unsupported CraftDefinitionFuel version");
749         recipe = deSerializeString(is);
750         burntime = readF1000(is);
751 }
752
753 /*
754         Craft definition manager
755 */
756
757 class CCraftDefManager: public IWritableCraftDefManager
758 {
759 public:
760         virtual ~CCraftDefManager()
761         {
762                 clear();
763         }
764         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
765                         bool decrementInput, IGameDef *gamedef) const
766         {
767                 output.item = "";
768                 output.time = 0;
769
770                 // If all input items are empty, abort.
771                 bool all_empty = true;
772                 for(std::vector<ItemStack>::const_iterator
773                                 i = input.items.begin();
774                                 i != input.items.end(); i++)
775                 {
776                         if(!i->empty())
777                         {
778                                 all_empty = false;
779                                 break;
780                         }
781                 }
782                 if(all_empty)
783                         return false;
784
785                 // Walk crafting definitions from back to front, so that later
786                 // definitions can override earlier ones.
787                 for(std::vector<CraftDefinition*>::const_reverse_iterator
788                                 i = m_craft_definitions.rbegin();
789                                 i != m_craft_definitions.rend(); i++)
790                 {
791                         CraftDefinition *def = *i;
792
793                         /*infostream<<"Checking "<<input.dump()<<std::endl
794                                         <<" against "<<def->dump()<<std::endl;*/
795
796                         try {
797                                 if(def->check(input, gamedef))
798                                 {
799                                         // Get output, then decrement input (if requested)
800                                         output = def->getOutput(input, gamedef);
801                                         if(decrementInput)
802                                                 def->decrementInput(input, gamedef);
803                                         return true;
804                                 }
805                         }
806                         catch(SerializationError &e)
807                         {
808                                 errorstream<<"getCraftResult: ERROR: "
809                                                 <<"Serialization error in recipe "
810                                                 <<def->dump()<<std::endl;
811                                 // then go on with the next craft definition
812                         }
813                 }
814                 return false;
815         }
816         virtual std::string dump() const
817         {
818                 std::ostringstream os(std::ios::binary);
819                 os<<"Crafting definitions:\n";
820                 for(std::vector<CraftDefinition*>::const_iterator
821                                 i = m_craft_definitions.begin();
822                                 i != m_craft_definitions.end(); i++)
823                 {
824                         os<<(*i)->dump()<<"\n";
825                 }
826                 return os.str();
827         }
828         virtual void registerCraft(CraftDefinition *def)
829         {
830                 infostream<<"registerCraft: registering craft definition: "
831                                 <<def->dump()<<std::endl;
832                 m_craft_definitions.push_back(def);
833         }
834         virtual void clear()
835         {
836                 for(std::vector<CraftDefinition*>::iterator
837                                 i = m_craft_definitions.begin();
838                                 i != m_craft_definitions.end(); i++){
839                         delete *i;
840                 }
841                 m_craft_definitions.clear();
842         }
843         virtual void serialize(std::ostream &os) const
844         {
845                 writeU8(os, 0); // version
846                 u16 count = m_craft_definitions.size();
847                 writeU16(os, count);
848                 for(std::vector<CraftDefinition*>::const_iterator
849                                 i = m_craft_definitions.begin();
850                                 i != m_craft_definitions.end(); i++){
851                         CraftDefinition *def = *i;
852                         // Serialize wrapped in a string
853                         std::ostringstream tmp_os(std::ios::binary);
854                         def->serialize(tmp_os);
855                         os<<serializeString(tmp_os.str());
856                 }
857         }
858         virtual void deSerialize(std::istream &is)
859         {
860                 // Clear everything
861                 clear();
862                 // Deserialize
863                 int version = readU8(is);
864                 if(version != 0) throw SerializationError(
865                                 "unsupported CraftDefManager version");
866                 u16 count = readU16(is);
867                 for(u16 i=0; i<count; i++){
868                         // Deserialize a string and grab a CraftDefinition from it
869                         std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
870                         CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
871                         // Register
872                         registerCraft(def);
873                 }
874         }
875 private:
876         std::vector<CraftDefinition*> m_craft_definitions;
877 };
878
879 IWritableCraftDefManager* createCraftDefManager()
880 {
881         return new CCraftDefManager();
882 }
883