virtual bool getCraftResult(CraftInput &input, CraftOutput &output, bool decrementInput, IGameDef *gamedef) const { output.item = ""; output.time = 0; // If all input items are empty, abort. bool all_empty = true; for (std::vector<ItemStack>::const_iterator it = input.items.begin(); it != input.items.end(); it++) { if (!it->empty()) { all_empty = false; break; } } if (all_empty) return false; std::vector<std::string> 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. for (int type = 0; type <= craft_hash_type_max; type++) { u64 hash = getHashForGrid((CraftHashType) type, input_names); /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/ // 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<u64, std::vector<CraftDefinition*> >::const_iterator col_iter = (m_craft_defs[type]).find(hash); if (col_iter == (m_craft_defs[type]).end()) continue; const std::vector<CraftDefinition*> &hash_collisions = col_iter->second; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for (std::vector<CraftDefinition*>::const_reverse_iterator it = hash_collisions.rbegin(); it != hash_collisions.rend(); it++) { CraftDefinition *def = *it; /*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, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ return true; } } } return false; }
CraftDefinition* CraftDefinition::deSerialize(std::istream &is) { int version = readU8(is); if(version != 1) throw SerializationError( "unsupported CraftDefinition version"); std::string name = deSerializeString(is); CraftDefinition *def = NULL; if(name == "shaped") { def = new CraftDefinitionShaped; } else if(name == "shapeless") { def = new CraftDefinitionShapeless; } else if(name == "toolrepair") { def = new CraftDefinitionToolRepair; } else if(name == "cooking") { def = new CraftDefinitionCooking; } else if(name == "fuel") { def = new CraftDefinitionFuel; } else { infostream<<"Unknown CraftDefinition name=\""<<name<<"\""<<std::endl; throw SerializationError("Unknown CraftDefinition name"); } def->deSerializeBody(is, version); return def; }
virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width, const std::vector<std::string> &recipe, IGameDef *gamedef) { bool all_empty = true; for (const auto &i : recipe) { if (!i.empty()) { all_empty = false; break; } } if (all_empty) return false; CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef)); // Recipes are not yet hashed at this point std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]; std::vector<CraftDefinition*> new_vec_by_input; bool got_hit = false; for (std::vector<CraftDefinition*>::size_type i = unhashed_inputs_vec.size(); i > 0; i--) { CraftDefinition *def = unhashed_inputs_vec[i - 1]; /* If the input doesn't match the recipe definition, this recipe definition later will be added back in source map. */ if (!def->check(input, gamedef)) { new_vec_by_input.push_back(def); continue; } CraftOutput output = def->getOutput(input, gamedef); got_hit = true; auto vec_iter = m_output_craft_definitions.find(output.item); if (vec_iter == m_output_craft_definitions.end()) continue; std::vector<CraftDefinition*> &vec = vec_iter->second; std::vector<CraftDefinition*> new_vec_by_output; /* We will preallocate necessary memory addresses, so we don't need to reallocate them later. This would save us some performance. */ new_vec_by_output.reserve(vec.size()); for (auto &vec_i : vec) { /* If pointers from map by input and output are not same, we will add 'CraftDefinition*' to a new vector. */ if (def != vec_i) { /* Adding dereferenced iterator value (which are 'CraftDefinition' reference) to a new vector. */ new_vec_by_output.push_back(vec_i); } } // Swaps assigned to current key value with new vector for output map. m_output_craft_definitions[output.item].swap(new_vec_by_output); } if (got_hit) // Swaps value with new vector for input map. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input); return got_hit; }
virtual bool getCraftResult(CraftInput &input, CraftOutput &output, bool decrementInput, IGameDef *gamedef) const { output.item = ""; output.time = 0; // If all input items are empty, abort. bool all_empty = true; for(std::vector<ItemStack>::const_iterator i = input.items.begin(); i != input.items.end(); i++) { if(!i->empty()) { all_empty = false; break; } } if(all_empty) return false; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for(std::vector<CraftDefinition*>::const_reverse_iterator i = m_craft_definitions.rbegin(); i != m_craft_definitions.rend(); i++) { CraftDefinition *def = *i; /*infostream<<"Checking "<<input.dump()<<std::endl <<" against "<<def->dump()<<std::endl;*/ try { if(def->check(input, gamedef)) { // Get output, then decrement input (if requested) output = def->getOutput(input, gamedef); if(decrementInput) def->decrementInput(input, gamedef); return true; } } catch(SerializationError &e) { errorstream<<"getCraftResult: ERROR: " <<"Serialization error in recipe " <<def->dump()<<std::endl; // then go on with the next craft definition } } return false; }
virtual void serialize(std::ostream &os) const { writeU8(os, 0); // version u16 count = m_craft_definitions.size(); writeU16(os, count); for(std::vector<CraftDefinition*>::const_iterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++){ CraftDefinition *def = *i; // Serialize wrapped in a string std::ostringstream tmp_os(std::ios::binary); def->serialize(tmp_os); os<<serializeString(tmp_os.str()); } }
virtual void initHashes(IGameDef *gamedef) { // Move the CraftDefs from the unhashed layer into layers higher up. for (std::vector<CraftDefinition*>::iterator it = (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).begin(); it != (m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0]).end(); it++) { CraftDefinition *def = *it; // Initialize and get the definition's hash def->initHash(gamedef); CraftHashType type = def->getHashType(); u64 hash = def->getHash(type); // Enter the definition m_craft_defs[type][hash].push_back(def); } m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].clear(); }
virtual void deSerialize(std::istream &is) { // Clear everything clear(); // Deserialize int version = readU8(is); if(version != 0) throw SerializationError( "unsupported CraftDefManager version"); u16 count = readU16(is); for(u16 i=0; i<count; i++){ // Deserialize a string and grab a CraftDefinition from it std::istringstream tmp_is(deSerializeString(is), std::ios::binary); CraftDefinition def; def.deSerialize(tmp_is); // Register registerCraft(def); } }
virtual void registerCraft(const CraftDefinition &def) { infostream<<"registerCraft: registering craft definition: " <<def.dump()<<std::endl; if(def.input.width > 3 || def.input.height() > 3){ errorstream<<"registerCraft: input size is larger than 3x3," <<" ignoring"<<std::endl; return; } m_craft_definitions.push_back(new CraftDefinition(def)); }
virtual bool getCraftRecipe(CraftInput &input, CraftOutput &output, IGameDef *gamedef) const { CraftOutput tmpout; tmpout.item = ""; tmpout.time = 0; // If output item is empty, abort. if(output.item.empty()) return false; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for(std::vector<CraftDefinition*>::const_reverse_iterator i = m_craft_definitions.rbegin(); i != m_craft_definitions.rend(); i++) { CraftDefinition *def = *i; /*infostream<<"Checking "<<input.dump()<<std::endl <<" against "<<def->dump()<<std::endl;*/ try { tmpout = def->getOutput(input, gamedef); if(tmpout.item.substr(0,output.item.length()) == output.item) { // Get output, then decrement input (if requested) input = def->getInput(output, gamedef); return true; } } catch(SerializationError &e) { errorstream<<"getCraftResult: ERROR: " <<"Serialization error in recipe " <<def->dump()<<std::endl; // then go on with the next craft definition } } return false; }
virtual bool getCraftResult(CraftInput &input, CraftOutput &output, std::vector<ItemStack> &output_replacement, bool decrementInput, IGameDef *gamedef) const { output.item = ""; output.time = 0; // If all input items are empty, abort. bool all_empty = true; for (const auto &item : input.items) { if (!item.empty()) { all_empty = false; break; } } if (all_empty) return false; std::vector<std::string> 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. for (int type = 0; type <= craft_hash_type_max; type++) { u64 hash = getHashForGrid((CraftHashType) type, input_names); /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/ // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];" // but that doesn't compile for some reason. This does. auto col_iter = (m_craft_defs[type]).find(hash); if (col_iter == (m_craft_defs[type]).end()) continue; const std::vector<CraftDefinition*> &hash_collisions = col_iter->second; // Walk crafting definitions from back to front, so that later // definitions can override earlier ones. for (std::vector<CraftDefinition*>::size_type i = hash_collisions.size(); i > 0; i--) { CraftDefinition *def = hash_collisions[i - 1]; /*errorstream << "Checking " << input.dump() << std::endl << " against " << def->dump() << std::endl;*/ if (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; } // Get output, then decrement input (if requested) output = out; if (decrementInput) def->decrementInput(input, output_replacement, gamedef); /*errorstream << "Check RETURNS TRUE" << std::endl;*/ return true; } } } return false; }
// get_all_craft_recipes(result item) int ModApiCraft::l_get_all_craft_recipes(lua_State *L) { NO_MAP_LOCK_REQUIRED; std::string o_item = luaL_checkstring(L,1); IGameDef *gdef = getServer(L); ICraftDefManager *cdef = gdef->cdef(); CraftInput input; CraftOutput output(o_item,0); std::vector<CraftDefinition*> recipes_list = cdef->getCraftRecipes(output, gdef); if (recipes_list.empty()) { lua_pushnil(L); return 1; } // Get the table insert function lua_getglobal(L, "table"); lua_getfield(L, -1, "insert"); int table_insert = lua_gettop(L); lua_newtable(L); int table = lua_gettop(L); for (std::vector<CraftDefinition*>::const_iterator i = recipes_list.begin(); i != recipes_list.end(); i++) { CraftOutput tmpout; tmpout.item = ""; tmpout.time = 0; CraftDefinition *def = *i; tmpout = def->getOutput(input, gdef); std::string query = tmpout.item; char *fmtpos, *fmt = &query[0]; if (strtok_r(fmt, " ", &fmtpos) == output.item) { input = def->getInput(output, gdef); lua_pushvalue(L, table_insert); lua_pushvalue(L, table); lua_newtable(L); int k = 1; lua_newtable(L); for(std::vector<ItemStack>::const_iterator i = input.items.begin(); i != input.items.end(); i++, k++) { if (i->empty()) continue; lua_pushinteger(L,k); lua_pushstring(L,i->name.c_str()); lua_settable(L, -3); } lua_setfield(L, -2, "items"); setintfield(L, -1, "width", input.width); switch (input.method) { case CRAFT_METHOD_NORMAL: lua_pushstring(L,"normal"); break; case CRAFT_METHOD_COOKING: lua_pushstring(L,"cooking"); break; case CRAFT_METHOD_FUEL: lua_pushstring(L,"fuel"); break; default: lua_pushstring(L,"unknown"); } lua_setfield(L, -2, "type"); lua_pushstring(L, &tmpout.item[0]); lua_setfield(L, -2, "output"); if (lua_pcall(L, 2, 0, 0)) script_error(L); } } return 1; }
virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi, IGameDef *gamedef) const { if(input_cpi.width > 3){ errorstream<<"getCraftResult(): ERROR: " <<"input_cpi.width > 3; Failing to craft."<<std::endl; return NULL; } InventoryItem *input_items[9]; for(u32 y=0; y<3; y++) for(u32 x=0; x<3; x++) { u32 i=y*3+x; if(x >= input_cpi.width || y >= input_cpi.height()) input_items[i] = NULL; else input_items[i] = input_cpi.items[y*input_cpi.width+x]; } for(core::list<CraftDefinition*>::ConstIterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++) { CraftDefinition *def = *i; /*infostream<<"Checking "<<createInput(input_cpi).dump()<<std::endl <<" against "<<def->input.dump() <<" (output=\""<<def->output<<"\")"<<std::endl;*/ try { CraftPointerInput spec_cpi = createPointerInput(def->input, gamedef); if(spec_cpi.width > 3){ errorstream<<"getCraftResult: ERROR: " <<"spec_cpi.width > 3 in recipe " <<def->dump()<<std::endl; continue; } InventoryItem *spec_items[9]; for(u32 y=0; y<3; y++) for(u32 x=0; x<3; x++) { u32 i=y*3+x; if(x >= spec_cpi.width || y >= spec_cpi.height()) spec_items[i] = NULL; else spec_items[i] = spec_cpi.items[y*spec_cpi.width+x]; } bool match = checkItemCombination(input_items, spec_items); if(match){ std::istringstream iss(def->output, std::ios::binary); return InventoryItem::deSerialize(iss, gamedef); } } catch(SerializationError &e) { errorstream<<"getCraftResult: ERROR: " <<"Serialization error in recipe " <<def->dump()<<std::endl; // then go on with the next craft definition } } return NULL; }