]> git.lizzy.rs Git - minetest.git/blob - src/craftdef.cpp
57a1851af044775d7441970c95a92ffb97d1b2b6
[minetest.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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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
264 std::string CraftReplacements::dump() const
265 {
266         std::ostringstream os(std::ios::binary);
267         os<<"{";
268         const char *sep = "";
269         for(std::vector<std::pair<std::string, std::string> >::const_iterator
270                         i = pairs.begin();
271                         i != pairs.end(); i++)
272         {
273                 os<<sep<<"\""<<(i->first)<<"\"=>\""<<(i->second)<<"\"";
274                 sep = ",";
275         }
276         os<<"}";
277         return os.str();
278 }
279
280 void CraftReplacements::serialize(std::ostream &os) const
281 {
282         writeU16(os, pairs.size());
283         for(u32 i=0; i<pairs.size(); i++)
284         {
285                 os<<serializeString(pairs[i].first);
286                 os<<serializeString(pairs[i].second);
287         }
288 }
289
290 void CraftReplacements::deSerialize(std::istream &is)
291 {
292         pairs.clear();
293         u32 count = readU16(is);
294         for(u32 i=0; i<count; i++)
295         {
296                 std::string first = deSerializeString(is);
297                 std::string second = deSerializeString(is);
298                 pairs.push_back(std::make_pair(first, second));
299         }
300 }
301
302 /*
303         CraftDefinition
304 */
305
306 void CraftDefinition::serialize(std::ostream &os) const
307 {
308         writeU8(os, 1); // version
309         os<<serializeString(getName());
310         serializeBody(os);
311 }
312
313 CraftDefinition* CraftDefinition::deSerialize(std::istream &is)
314 {
315         int version = readU8(is);
316         if(version != 1) throw SerializationError(
317                         "unsupported CraftDefinition version");
318         std::string name = deSerializeString(is);
319         CraftDefinition *def = NULL;
320         if(name == "shaped")
321         {
322                 def = new CraftDefinitionShaped;
323         }
324         else if(name == "shapeless")
325         {
326                 def = new CraftDefinitionShapeless;
327         }
328         else if(name == "toolrepair")
329         {
330                 def = new CraftDefinitionToolRepair;
331         }
332         else if(name == "cooking")
333         {
334                 def = new CraftDefinitionCooking;
335         }
336         else if(name == "fuel")
337         {
338                 def = new CraftDefinitionFuel;
339         }
340         else
341         {
342                 infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl;
343                 throw SerializationError("Unknown CraftDefinition name");
344         }
345         def->deSerializeBody(is, version);
346         return def;
347 }
348
349 /*
350         CraftDefinitionShaped
351 */
352
353 std::string CraftDefinitionShaped::getName() const
354 {
355         return "shaped";
356 }
357
358 bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
359 {
360         if(input.method != CRAFT_METHOD_NORMAL)
361                 return false;
362
363         // Get input item matrix
364         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
365         unsigned int inp_width = input.width;
366         if(inp_width == 0)
367                 return false;
368         while(inp_names.size() % inp_width != 0)
369                 inp_names.push_back("");
370
371         // Get input bounds
372         unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0;
373         if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y))
374                 return false;  // it was empty
375
376         // Get recipe item matrix
377         std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
378         unsigned int rec_width = width;
379         if(rec_width == 0)
380                 return false;
381         while(rec_names.size() % rec_width != 0)
382                 rec_names.push_back("");
383
384         // Get recipe bounds
385         unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
386         if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y))
387                 return false;  // it was empty
388
389         // Different sizes?
390         if(inp_max_x - inp_min_x != rec_max_x - rec_min_x)
391                 return false;
392         if(inp_max_y - inp_min_y != rec_max_y - rec_min_y)
393                 return false;
394
395         // Verify that all item names in the bounding box are equal
396         unsigned int w = inp_max_x - inp_min_x + 1;
397         unsigned int h = inp_max_y - inp_min_y + 1;
398         for(unsigned int y=0; y<h; y++)
399         for(unsigned int x=0; x<w; x++)
400         {
401                 unsigned int inp_x = inp_min_x + x;
402                 unsigned int inp_y = inp_min_y + y;
403                 unsigned int rec_x = rec_min_x + x;
404                 unsigned int rec_y = rec_min_y + y;
405
406                 if(
407                         inp_names[inp_y * inp_width + inp_x] !=
408                         rec_names[rec_y * rec_width + rec_x]
409                 ){
410                         return false;
411                 }
412         }
413
414         return true;
415 }
416
417 CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
418 {
419         return CraftOutput(output, 0);
420 }
421
422 void CraftDefinitionShaped::decrementInput(CraftInput &input, IGameDef *gamedef) const
423 {
424         craftDecrementOrReplaceInput(input, replacements, gamedef);
425 }
426
427 std::string CraftDefinitionShaped::dump() const
428 {
429         std::ostringstream os(std::ios::binary);
430         os<<"(shaped, output=\""<<output
431                 <<"\", recipe="<<craftDumpMatrix(recipe, width)
432                 <<", replacements="<<replacements.dump()<<")";
433         return os.str();
434 }
435
436 void CraftDefinitionShaped::serializeBody(std::ostream &os) const
437 {
438         os<<serializeString(output);
439         writeU16(os, width);
440         writeU16(os, recipe.size());
441         for(u32 i=0; i<recipe.size(); i++)
442                 os<<serializeString(recipe[i]);
443         replacements.serialize(os);
444 }
445
446 void CraftDefinitionShaped::deSerializeBody(std::istream &is, int version)
447 {
448         if(version != 1) throw SerializationError(
449                         "unsupported CraftDefinitionShaped version");
450         output = deSerializeString(is);
451         width = readU16(is);
452         recipe.clear();
453         u32 count = readU16(is);
454         for(u32 i=0; i<count; i++)
455                 recipe.push_back(deSerializeString(is));
456         replacements.deSerialize(is);
457 }
458
459 /*
460         CraftDefinitionShapeless
461 */
462
463 std::string CraftDefinitionShapeless::getName() const
464 {
465         return "shapeless";
466 }
467
468 bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
469 {
470         if(input.method != CRAFT_METHOD_NORMAL)
471                 return false;
472
473         // Get input item multiset
474         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
475         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
476
477         // Get recipe item multiset
478         std::vector<std::string> rec_names = craftGetItemNames(recipe, gamedef);
479         std::multiset<std::string> rec_names_multiset = craftMakeMultiset(rec_names);
480
481         // Recipe is matched when the multisets coincide
482         return inp_names_multiset == rec_names_multiset;
483 }
484
485 CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
486 {
487         return CraftOutput(output, 0);
488 }
489
490 void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const
491 {
492         craftDecrementOrReplaceInput(input, replacements, gamedef);
493 }
494
495 std::string CraftDefinitionShapeless::dump() const
496 {
497         std::ostringstream os(std::ios::binary);
498         os<<"(shapeless, output=\""<<output
499                 <<"\", recipe="<<craftDumpMatrix(recipe, recipe.size())
500                 <<", replacements="<<replacements.dump()<<")";
501         return os.str();
502 }
503
504 void CraftDefinitionShapeless::serializeBody(std::ostream &os) const
505 {
506         os<<serializeString(output);
507         writeU16(os, recipe.size());
508         for(u32 i=0; i<recipe.size(); i++)
509                 os<<serializeString(recipe[i]);
510         replacements.serialize(os);
511 }
512
513 void CraftDefinitionShapeless::deSerializeBody(std::istream &is, int version)
514 {
515         if(version != 1) throw SerializationError(
516                         "unsupported CraftDefinitionShapeless version");
517         output = deSerializeString(is);
518         recipe.clear();
519         u32 count = readU16(is);
520         for(u32 i=0; i<count; i++)
521                 recipe.push_back(deSerializeString(is));
522         replacements.deSerialize(is);
523 }
524
525 /*
526         CraftDefinitionToolRepair
527 */
528
529 static ItemStack craftToolRepair(
530                 const ItemStack &item1,
531                 const ItemStack &item2,
532                 float additional_wear,
533                 IGameDef *gamedef)
534 {
535         IItemDefManager *idef = gamedef->idef();
536         if(item1.count != 1 || item2.count != 1 || item1.name != item2.name
537                         || idef->get(item1.name).type != ITEM_TOOL
538                         || idef->get(item2.name).type != ITEM_TOOL)
539         {
540                 // Failure
541                 return ItemStack();
542         }
543
544         s32 item1_uses = 65536 - (u32) item1.wear;
545         s32 item2_uses = 65536 - (u32) item2.wear;
546         s32 new_uses = item1_uses + item2_uses;
547         s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
548         if(new_wear >= 65536)
549                 return ItemStack();
550         if(new_wear < 0)
551                 new_wear = 0;
552
553         ItemStack repaired = item1;
554         repaired.wear = new_wear;
555         return repaired;
556 }
557
558 std::string CraftDefinitionToolRepair::getName() const
559 {
560         return "toolrepair";
561 }
562
563 bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
564 {
565         if(input.method != CRAFT_METHOD_NORMAL)
566                 return false;
567
568         ItemStack item1;
569         ItemStack item2;
570         for(std::vector<ItemStack>::const_iterator
571                         i = input.items.begin();
572                         i != input.items.end(); i++)
573         {
574                 if(!i->empty())
575                 {
576                         if(item1.empty())
577                                 item1 = *i;
578                         else if(item2.empty())
579                                 item2 = *i;
580                         else
581                                 return false;
582                 }
583         }
584         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
585         return !repaired.empty();
586 }
587
588 CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
589 {
590         ItemStack item1;
591         ItemStack item2;
592         for(std::vector<ItemStack>::const_iterator
593                         i = input.items.begin();
594                         i != input.items.end(); i++)
595         {
596                 if(!i->empty())
597                 {
598                         if(item1.empty())
599                                 item1 = *i;
600                         else if(item2.empty())
601                                 item2 = *i;
602                 }
603         }
604         ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
605         return CraftOutput(repaired.getItemString(), 0);
606 }
607
608 void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const
609 {
610         craftDecrementInput(input, gamedef);
611 }
612
613 std::string CraftDefinitionToolRepair::dump() const
614 {
615         std::ostringstream os(std::ios::binary);
616         os<<"(toolrepair, additional_wear="<<additional_wear<<")";
617         return os.str();
618 }
619
620 void CraftDefinitionToolRepair::serializeBody(std::ostream &os) const
621 {
622         writeF1000(os, additional_wear);
623 }
624
625 void CraftDefinitionToolRepair::deSerializeBody(std::istream &is, int version)
626 {
627         if(version != 1) throw SerializationError(
628                         "unsupported CraftDefinitionToolRepair version");
629         additional_wear = readF1000(is);
630 }
631
632 /*
633         CraftDefinitionCooking
634 */
635
636 std::string CraftDefinitionCooking::getName() const
637 {
638         return "cooking";
639 }
640
641 bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
642 {
643         if(input.method != CRAFT_METHOD_COOKING)
644                 return false;
645
646         // Get input item multiset
647         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
648         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
649
650         // Get recipe item multiset
651         std::multiset<std::string> rec_names_multiset;
652         rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
653
654         // Recipe is matched when the multisets coincide
655         return inp_names_multiset == rec_names_multiset;
656 }
657
658 CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
659 {
660         return CraftOutput(output, cooktime);
661 }
662
663 void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const
664 {
665         craftDecrementOrReplaceInput(input, replacements, gamedef);
666 }
667
668 std::string CraftDefinitionCooking::dump() const
669 {
670         std::ostringstream os(std::ios::binary);
671         os<<"(cooking, output=\""<<output
672                 <<"\", recipe=\""<<recipe
673                 <<"\", cooktime="<<cooktime<<")"
674                 <<", replacements="<<replacements.dump()<<")";
675         return os.str();
676 }
677
678 void CraftDefinitionCooking::serializeBody(std::ostream &os) const
679 {
680         os<<serializeString(output);
681         os<<serializeString(recipe);
682         writeF1000(os, cooktime);
683         replacements.serialize(os);
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         replacements.deSerialize(is);
694 }
695
696 /*
697         CraftDefinitionFuel
698 */
699
700 std::string CraftDefinitionFuel::getName() const
701 {
702         return "fuel";
703 }
704
705 bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
706 {
707         if(input.method != CRAFT_METHOD_FUEL)
708                 return false;
709
710         // Get input item multiset
711         std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
712         std::multiset<std::string> inp_names_multiset = craftMakeMultiset(inp_names);
713
714         // Get recipe item multiset
715         std::multiset<std::string> rec_names_multiset;
716         rec_names_multiset.insert(craftGetItemName(recipe, gamedef));
717
718         // Recipe is matched when the multisets coincide
719         return inp_names_multiset == rec_names_multiset;
720 }
721
722 CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
723 {
724         return CraftOutput("", burntime);
725 }
726
727 void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const
728 {
729         craftDecrementOrReplaceInput(input, replacements, gamedef);
730 }
731
732 std::string CraftDefinitionFuel::dump() const
733 {
734         std::ostringstream os(std::ios::binary);
735         os<<"(fuel, recipe=\""<<recipe
736                 <<"\", burntime="<<burntime<<")"
737                 <<", replacements="<<replacements.dump()<<")";
738         return os.str();
739 }
740
741 void CraftDefinitionFuel::serializeBody(std::ostream &os) const
742 {
743         os<<serializeString(recipe);
744         writeF1000(os, burntime);
745         replacements.serialize(os);
746 }
747
748 void CraftDefinitionFuel::deSerializeBody(std::istream &is, int version)
749 {
750         if(version != 1) throw SerializationError(
751                         "unsupported CraftDefinitionFuel version");
752         recipe = deSerializeString(is);
753         burntime = readF1000(is);
754         replacements.deSerialize(is);
755 }
756
757 /*
758         Craft definition manager
759 */
760
761 class CCraftDefManager: public IWritableCraftDefManager
762 {
763 public:
764         virtual ~CCraftDefManager()
765         {
766                 clear();
767         }
768         virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
769                         bool decrementInput, IGameDef *gamedef) const
770         {
771                 output.item = "";
772                 output.time = 0;
773
774                 // If all input items are empty, abort.
775                 bool all_empty = true;
776                 for(std::vector<ItemStack>::const_iterator
777                                 i = input.items.begin();
778                                 i != input.items.end(); i++)
779                 {
780                         if(!i->empty())
781                         {
782                                 all_empty = false;
783                                 break;
784                         }
785                 }
786                 if(all_empty)
787                         return false;
788
789                 // Walk crafting definitions from back to front, so that later
790                 // definitions can override earlier ones.
791                 for(std::vector<CraftDefinition*>::const_reverse_iterator
792                                 i = m_craft_definitions.rbegin();
793                                 i != m_craft_definitions.rend(); i++)
794                 {
795                         CraftDefinition *def = *i;
796
797                         /*infostream<<"Checking "<<input.dump()<<std::endl
798                                         <<" against "<<def->dump()<<std::endl;*/
799
800                         try {
801                                 if(def->check(input, gamedef))
802                                 {
803                                         // Get output, then decrement input (if requested)
804                                         output = def->getOutput(input, gamedef);
805                                         if(decrementInput)
806                                                 def->decrementInput(input, gamedef);
807                                         return true;
808                                 }
809                         }
810                         catch(SerializationError &e)
811                         {
812                                 errorstream<<"getCraftResult: ERROR: "
813                                                 <<"Serialization error in recipe "
814                                                 <<def->dump()<<std::endl;
815                                 // then go on with the next craft definition
816                         }
817                 }
818                 return false;
819         }
820         virtual std::string dump() const
821         {
822                 std::ostringstream os(std::ios::binary);
823                 os<<"Crafting definitions:\n";
824                 for(std::vector<CraftDefinition*>::const_iterator
825                                 i = m_craft_definitions.begin();
826                                 i != m_craft_definitions.end(); i++)
827                 {
828                         os<<(*i)->dump()<<"\n";
829                 }
830                 return os.str();
831         }
832         virtual void registerCraft(CraftDefinition *def)
833         {
834                 verbosestream<<"registerCraft: registering craft definition: "
835                                 <<def->dump()<<std::endl;
836                 m_craft_definitions.push_back(def);
837         }
838         virtual void clear()
839         {
840                 for(std::vector<CraftDefinition*>::iterator
841                                 i = m_craft_definitions.begin();
842                                 i != m_craft_definitions.end(); i++){
843                         delete *i;
844                 }
845                 m_craft_definitions.clear();
846         }
847         virtual void serialize(std::ostream &os) const
848         {
849                 writeU8(os, 0); // version
850                 u16 count = m_craft_definitions.size();
851                 writeU16(os, count);
852                 for(std::vector<CraftDefinition*>::const_iterator
853                                 i = m_craft_definitions.begin();
854                                 i != m_craft_definitions.end(); i++){
855                         CraftDefinition *def = *i;
856                         // Serialize wrapped in a string
857                         std::ostringstream tmp_os(std::ios::binary);
858                         def->serialize(tmp_os);
859                         os<<serializeString(tmp_os.str());
860                 }
861         }
862         virtual void deSerialize(std::istream &is)
863         {
864                 // Clear everything
865                 clear();
866                 // Deserialize
867                 int version = readU8(is);
868                 if(version != 0) throw SerializationError(
869                                 "unsupported CraftDefManager version");
870                 u16 count = readU16(is);
871                 for(u16 i=0; i<count; i++){
872                         // Deserialize a string and grab a CraftDefinition from it
873                         std::istringstream tmp_is(deSerializeString(is), std::ios::binary);
874                         CraftDefinition *def = CraftDefinition::deSerialize(tmp_is);
875                         // Register
876                         registerCraft(def);
877                 }
878         }
879 private:
880         std::vector<CraftDefinition*> m_craft_definitions;
881 };
882
883 IWritableCraftDefManager* createCraftDefManager()
884 {
885         return new CCraftDefManager();
886 }
887