X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fcraftdef.cpp;h=21060519839072636a897a13efb31c58645c535b;hb=e1142ee57f2d7b59a86f6d0d72ae043844bc3121;hp=022b98da3285ed5e842b24c115e078546274a2b8;hpb=96989e0a6aa3ab069b5aeeab44a6280d6d51364a;p=minetest.git diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 022b98da3..210605198 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -29,7 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/serialize.h" #include "util/string.h" #include "util/numeric.h" -#include "strfnd.h" +#include "util/strfnd.h" #include "exceptions.h" inline bool isGroupRecipeStr(const std::string &rec_name) @@ -37,6 +37,15 @@ inline bool isGroupRecipeStr(const std::string &rec_name) return str_starts_with(rec_name, std::string("group:")); } +static bool hasGroupItem(const std::vector &recipe) +{ + for (const auto &item : recipe) { + if (isGroupRecipeStr(item)) + return true; + } + return false; +} + inline u64 getHashForString(const std::string &recipe_str) { /*errorstream << "Hashing craft string \"" << recipe_str << '"';*/ @@ -49,17 +58,17 @@ static u64 getHashForGrid(CraftHashType type, const std::vector &gr case CRAFT_HASH_TYPE_ITEM_NAMES: { std::ostringstream os; bool is_first = true; - for (size_t i = 0; i < grid_names.size(); i++) { - if (grid_names[i] != "") { - os << (is_first ? "" : "\n") << grid_names[i]; + for (const std::string &grid_name : grid_names) { + if (!grid_name.empty()) { + os << (is_first ? "" : "\n") << grid_name; is_first = false; } } return getHashForString(os.str()); } case CRAFT_HASH_TYPE_COUNT: { u64 cnt = 0; - for (size_t i = 0; i < grid_names.size(); i++) - if (grid_names[i] != "") + for (const std::string &grid_name : grid_names) + if (!grid_name.empty()) cnt++; return cnt; } case CRAFT_HASH_TYPE_UNHASHED: @@ -90,7 +99,7 @@ static bool inputItemMatchesRecipe(const std::string &inp_name, all_groups_match = false; break; } - } while (!f.atend()); + } while (!f.at_end()); if (all_groups_match) return true; } @@ -112,9 +121,9 @@ static std::vector craftGetItemNames( const std::vector &itemstrings, IGameDef *gamedef) { std::vector result; - for (std::vector::size_type i = 0; - i < itemstrings.size(); i++) { - result.push_back(craftGetItemName(itemstrings[i], gamedef)); + result.reserve(itemstrings.size()); + for (const auto &itemstring : itemstrings) { + result.push_back(craftGetItemName(itemstring, gamedef)); } return result; } @@ -124,9 +133,9 @@ static std::vector craftGetItemNames( const std::vector &items, IGameDef *gamedef) { std::vector result; - for (std::vector::size_type i = 0; - i < items.size(); i++) { - result.push_back(items[i].name); + result.reserve(items.size()); + for (const auto &item : items) { + result.push_back(item.name); } return result; } @@ -136,10 +145,10 @@ static std::vector craftGetItems( const std::vector &items, IGameDef *gamedef) { std::vector result; - for (std::vector::size_type i = 0; - i < items.size(); i++) { - result.push_back(ItemStack(std::string(items[i]), (u16)1, - (u16)0, "", gamedef->getItemDefManager())); + result.reserve(items.size()); + for (const auto &item : items) { + result.emplace_back(std::string(item), (u16)1, + (u16)0, gamedef->getItemDefManager()); } return result; } @@ -153,10 +162,9 @@ static bool craftGetBounds(const std::vector &items, unsigned int w bool success = false; unsigned int x = 0; unsigned int y = 0; - for (std::vector::size_type i = 0; - i < items.size(); i++) { + for (const std::string &item : items) { // Is this an actual item? - if (items[i] != "") { + if (!item.empty()) { if (!success) { // This is the first nonempty item min_x = max_x = x; @@ -183,10 +191,9 @@ static bool craftGetBounds(const std::vector &items, unsigned int w // Removes 1 from each item stack static void craftDecrementInput(CraftInput &input, IGameDef *gamedef) { - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - if (input.items[i].count != 0) - input.items[i].remove(1); + for (auto &item : input.items) { + if (item.count != 0) + item.remove(1); } } @@ -206,28 +213,25 @@ static void craftDecrementOrReplaceInput(CraftInput &input, // Make a copy of the replacements pair list std::vector > pairs = replacements.pairs; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - ItemStack &item = input.items[i]; + for (auto &item : input.items) { // Find an appropriate replacement bool found_replacement = false; - for (std::vector >::iterator - j = pairs.begin(); - j != pairs.end(); ++j) { - if (item.name == craftGetItemName(j->first, gamedef)) { + for (auto j = pairs.begin(); j != pairs.end(); ++j) { + if (inputItemMatchesRecipe(item.name, j->first, gamedef->idef())) { if (item.count == 1) { item.deSerialize(j->second, gamedef->idef()); found_replacement = true; pairs.erase(j); break; - } else { - ItemStack rep; - rep.deSerialize(j->second, gamedef->idef()); - item.remove(1); - found_replacement = true; - output_replacements.push_back(rep); - break; } + + ItemStack rep; + rep.deSerialize(j->second, gamedef->idef()); + item.remove(1); + found_replacement = true; + output_replacements.push_back(rep); + break; + } } // No replacement was found, simply decrement count by one @@ -242,12 +246,13 @@ static std::string craftDumpMatrix(const std::vector &items, { std::ostringstream os(std::ios::binary); os << "{ "; + unsigned int x = 0; for(std::vector::size_type i = 0; - i < items.size(); i++) { - if (i == width) { + i < items.size(); i++, x++) { + if (x == width) { os << "; "; - i = 0; - } else if (i != 0) { + x = 0; + } else if (x != 0) { os << ","; } os << '"' << items[i] << '"'; @@ -262,12 +267,13 @@ std::string craftDumpMatrix(const std::vector &items, { std::ostringstream os(std::ios::binary); os << "{ "; + unsigned int x = 0; for (std::vector::size_type i = 0; - i < items.size(); i++) { - if (i == width) { + i < items.size(); i++, x++) { + if (x == width) { os << "; "; - i = 0; - } else if (i != 0) { + x = 0; + } else if (x != 0) { os << ","; } os << '"' << (items[i].getItemString()) << '"'; @@ -281,6 +287,15 @@ std::string craftDumpMatrix(const std::vector &items, CraftInput */ +bool CraftInput::empty() const +{ + for (const auto &item : items) { + if (!item.empty()) + return false; + } + return true; +} + std::string CraftInput::dump() const { std::ostringstream os(std::ios::binary); @@ -309,9 +324,7 @@ std::string CraftReplacements::dump() const std::ostringstream os(std::ios::binary); os<<"{"; const char *sep = ""; - for (std::vector >::size_type i = 0; - i < pairs.size(); i++) { - const std::pair &repl_p = pairs[i]; + for (const auto &repl_p : pairs) { os << sep << '"' << (repl_p.first) << "\"=>\"" << (repl_p.second) << '"'; @@ -325,6 +338,19 @@ std::string CraftReplacements::dump() const CraftDefinitionShaped */ +CraftDefinitionShaped::CraftDefinitionShaped( + const std::string &output_, + unsigned int width_, + const std::vector &recipe_, + const CraftReplacements &replacements_): + output(output_), width(width_), recipe(recipe_), replacements(replacements_) +{ + if (hasGroupItem(recipe)) + priority = PRIORITY_SHAPED_AND_GROUPS; + else + priority = PRIORITY_SHAPED; +} + std::string CraftDefinitionShaped::getName() const { return "shaped"; @@ -341,7 +367,7 @@ bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) co if (inp_width == 0) return false; while (inp_names.size() % inp_width != 0) - inp_names.push_back(""); + inp_names.emplace_back(""); // Get input bounds unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0; @@ -360,7 +386,7 @@ bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) co if (rec_width == 0) return false; while (rec_names.size() % rec_width != 0) - rec_names.push_back(""); + rec_names.emplace_back(""); // Get recipe bounds unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0; @@ -412,22 +438,6 @@ void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector &recipe_, + const CraftReplacements &replacements_): + output(output_), recipe(recipe_), replacements(replacements_) +{ + if (hasGroupItem(recipe)) + priority = PRIORITY_SHAPELESS_AND_GROUPS; + else + priority = PRIORITY_SHAPELESS; +} + std::string CraftDefinitionShapeless::getName() const { return "shapeless"; @@ -472,10 +499,8 @@ bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) // Filter empty items out of input std::vector input_filtered; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - const ItemStack &item = input.items[i]; - if (item.name != "") + for (const auto &item : input.items) { + if (!item.name.empty()) input_filtered.push_back(item.name); } @@ -534,22 +559,6 @@ void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vectoridef(); if (item1.count != 1 || item2.count != 1 || item1.name != item2.name || idef->get(item1.name).type != ITEM_TOOL - || idef->get(item2.name).type != ITEM_TOOL) { + || itemgroup_get(idef->get(item1.name).groups, "disable_repair") == 1) { // Failure return ItemStack(); } @@ -620,9 +640,7 @@ bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef ItemStack item1; ItemStack item2; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - const ItemStack &item = input.items[i]; + for (const auto &item : input.items) { if (!item.empty()) { if (item1.empty()) item1 = item; @@ -640,9 +658,7 @@ CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameD { ItemStack item1; ItemStack item2; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - const ItemStack &item = input.items[i]; + for (const auto &item : input.items) { if (!item.empty()) { if (item1.empty()) item1 = item; @@ -657,7 +673,7 @@ CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameD CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const { std::vector stack; - stack.push_back(ItemStack()); + stack.emplace_back(); return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack); } @@ -678,6 +694,19 @@ std::string CraftDefinitionToolRepair::dump() const CraftDefinitionCooking */ +CraftDefinitionCooking::CraftDefinitionCooking( + const std::string &output_, + const std::string &recipe_, + float cooktime_, + const CraftReplacements &replacements_): + output(output_), recipe(recipe_), cooktime(cooktime_), replacements(replacements_) +{ + if (isGroupRecipeStr(recipe)) + priority = PRIORITY_SHAPELESS_AND_GROUPS; + else + priority = PRIORITY_SHAPELESS; +} + std::string CraftDefinitionCooking::getName() const { return "cooking"; @@ -690,10 +719,9 @@ bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) c // Filter empty items out of input std::vector input_filtered; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - const std::string &name = input.items[i].name; - if (name != "") + for (const auto &item : input.items) { + const std::string &name = item.name; + if (!name.empty()) input_filtered.push_back(name); } @@ -727,25 +755,19 @@ void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector input_filtered; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - const std::string &name = input.items[i].name; - if (name != "") + for (const auto &item : input.items) { + const std::string &name = item.name; + if (!name.empty()) input_filtered.push_back(name); } @@ -819,25 +857,19 @@ void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector &output_replacement, bool decrementInput, IGameDef *gamedef) const { - output.item = ""; - output.time = 0; - - // If all input items are empty, abort. - bool all_empty = true; - for (std::vector::size_type i = 0; - i < input.items.size(); i++) { - if (!input.items[i].empty()) { - all_empty = false; - break; - } - } - if (all_empty) + if (input.empty()) return false; std::vector input_names; input_names = craftGetItemNames(input.items, gamedef); std::sort(input_names.begin(), input_names.end()); - // Try hash types with increasing collision rate, and return if found. + // Try hash types with increasing collision rate + // while remembering the latest, highest priority recipe. + CraftDefinition::RecipePriority priority_best = + CraftDefinition::PRIORITY_NO_RECIPE; + CraftDefinition *def_best = nullptr; for (int type = 0; type <= craft_hash_type_max; type++) { u64 hash = getHashForGrid((CraftHashType) type, input_names); @@ -904,8 +934,7 @@ class CCraftDefManager: public IWritableCraftDefManager // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];" // but that doesn't compile for some reason. This does. - std::map >::const_iterator - col_iter = (m_craft_defs[type]).find(hash); + auto col_iter = (m_craft_defs[type]).find(hash); if (col_iter == (m_craft_defs[type]).end()) continue; @@ -920,17 +949,30 @@ class CCraftDefManager: public IWritableCraftDefManager /*errorstream << "Checking " << input.dump() << std::endl << " against " << def->dump() << std::endl;*/ - if (def->check(input, gamedef)) { - // Get output, then decrement input (if requested) - output = def->getOutput(input, gamedef); - if (decrementInput) - def->decrementInput(input, output_replacement, gamedef); - /*errorstream << "Check RETURNS TRUE" << std::endl;*/ - return true; + CraftDefinition::RecipePriority priority = def->getPriority(); + if (priority > priority_best + && def->check(input, gamedef)) { + // Check if the crafted node/item exists + CraftOutput out = def->getOutput(input, gamedef); + ItemStack is; + is.deSerialize(out.item, gamedef->idef()); + if (!is.isKnown(gamedef->idef())) { + infostream << "trying to craft non-existent " + << out.item << ", ignoring recipe" << std::endl; + continue; + } + + output = out; + priority_best = priority; + def_best = def; } } } - return false; + if (priority_best == CraftDefinition::PRIORITY_NO_RECIPE) + return false; + if (decrementInput) + def_best->decrementInput(input, output_replacement, gamedef); + return true; } virtual std::vector getCraftRecipes(CraftOutput &output, @@ -938,8 +980,7 @@ class CCraftDefManager: public IWritableCraftDefManager { std::vector recipes; - std::map >::const_iterator - vec_iter = m_output_craft_definitions.find(output.item); + auto vec_iter = m_output_craft_definitions.find(output.item); if (vec_iter == m_output_craft_definitions.end()) return recipes; @@ -958,14 +999,60 @@ class CCraftDefManager: public IWritableCraftDefManager return recipes; } + + virtual bool clearCraftsByOutput(const CraftOutput &output, IGameDef *gamedef) + { + auto to_clear = m_output_craft_definitions.find(output.item); + + if (to_clear == m_output_craft_definitions.end()) + return false; + + for (auto def : to_clear->second) { + // Recipes are not yet hashed at this point + std::vector &defs = m_craft_defs[(int)CRAFT_HASH_TYPE_UNHASHED][0]; + defs.erase(std::remove(defs.begin(), defs.end(), def), defs.end()); + delete def; + } + m_output_craft_definitions.erase(to_clear); + return true; + } + + virtual bool clearCraftsByInput(const CraftInput &input, IGameDef *gamedef) + { + if (input.empty()) + return false; + + // Recipes are not yet hashed at this point + std::vector &defs = m_craft_defs[(int)CRAFT_HASH_TYPE_UNHASHED][0]; + std::vector new_defs; + bool got_hit = false; + for (auto def : defs) { + if (!def->check(input, gamedef)) { + new_defs.push_back(def); + continue; + } + got_hit = true; + std::string output = def->getOutput(input, gamedef).item; + delete def; + auto it = m_output_craft_definitions.find(craftGetItemName(output, gamedef)); + if (it == m_output_craft_definitions.end()) + continue; + std::vector &outdefs = it->second; + outdefs.erase(std::remove(outdefs.begin(), outdefs.end(), def), outdefs.end()); + } + if (got_hit) + defs.swap(new_defs); + + return got_hit; + } + virtual std::string dump() const { std::ostringstream os(std::ios::binary); os << "Crafting definitions:\n"; - for (int type = 0; type <= craft_hash_type_max; type++) { - for (std::map >::const_iterator - it = (m_craft_defs[type]).begin(); - it != (m_craft_defs[type]).end(); it++) { + for (int type = 0; type <= craft_hash_type_max; ++type) { + for (auto it = m_craft_defs[type].begin(); + it != m_craft_defs[type].end(); ++it) { for (std::vector::size_type i = 0; i < it->second.size(); i++) { os << "type " << type @@ -979,8 +1066,8 @@ class CCraftDefManager: public IWritableCraftDefManager } virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef) { - verbosestream << "registerCraft: registering craft definition: " - << def->dump() << std::endl; + TRACESTREAM(<< "registerCraft: registering craft definition: " + << def->dump() << std::endl); m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def); CraftInput input; @@ -990,16 +1077,12 @@ class CCraftDefManager: public IWritableCraftDefManager } virtual void clear() { - for (int type = 0; type <= craft_hash_type_max; type++) { - for (std::map >::iterator - it = m_craft_defs[type].begin(); - it != m_craft_defs[type].end(); it++) { - for (std::vector::iterator - iit = it->second.begin(); - iit != it->second.end(); ++iit) { - delete *iit; + for (int type = 0; type <= craft_hash_type_max; ++type) { + for (auto &it : m_craft_defs[type]) { + for (auto &iit : it.second) { + delete iit; } - it->second.clear(); + it.second.clear(); } m_craft_defs[type].clear(); } @@ -1010,10 +1093,7 @@ class CCraftDefManager: public IWritableCraftDefManager // Move the CraftDefs from the unhashed layer into layers higher up. std::vector &unhashed = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]; - for (std::vector::size_type i = 0; - i < unhashed.size(); i++) { - CraftDefinition *def = unhashed[i]; - + for (auto def : unhashed) { // Initialize and get the definition's hash def->initHash(gamedef); CraftHashType type = def->getHashType(); @@ -1025,13 +1105,13 @@ class CCraftDefManager: public IWritableCraftDefManager unhashed.clear(); } private: - //TODO: change both maps to unordered_map when c++11 can be used - std::vector > > m_craft_defs; - std::map > m_output_craft_definitions; + std::vector > > + m_craft_defs; + std::unordered_map > + m_output_craft_definitions; }; IWritableCraftDefManager* createCraftDefManager() { return new CCraftDefManager(); } -