]> git.lizzy.rs Git - minetest.git/blobdiff - src/script/lua_api/l_craft.cpp
Support packing arbitrary graphs (#12289)
[minetest.git] / src / script / lua_api / l_craft.cpp
index aaca84c5601291977ed9194ac0db5c6b6109c832..137b210be2a8c13876ef5715b8ed4a06cdaa1f84 100644 (file)
@@ -34,7 +34,6 @@ struct EnumString ModApiCraft::es_CraftMethod[] =
        {0, NULL},
 };
 
-
 // helper for register_craft
 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
                int &width, std::vector<std::string> &recipe)
@@ -58,7 +57,7 @@ bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
                        // key at index -2 and value at index -1
                        if(!lua_isstring(L, -1))
                                return false;
-                       recipe.push_back(lua_tostring(L, -1));
+                       recipe.emplace_back(readParam<std::string>(L, -1));
                        // removes value, keeps key for next iteration
                        lua_pop(L, 1);
                        colcount++;
@@ -91,7 +90,7 @@ bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
                // key at index -2 and value at index -1
                if(!lua_isstring(L, -1))
                        return false;
-               recipe.push_back(lua_tostring(L, -1));
+               recipe.emplace_back(readParam<std::string>(L, -1));
                // removes value, keeps key for next iteration
                lua_pop(L, 1);
        }
@@ -116,15 +115,14 @@ bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
                lua_rawgeti(L, -1, 1);
                if(!lua_isstring(L, -1))
                        return false;
-               std::string replace_from = lua_tostring(L, -1);
+               std::string replace_from = readParam<std::string>(L, -1);
                lua_pop(L, 1);
                lua_rawgeti(L, -1, 2);
                if(!lua_isstring(L, -1))
                        return false;
-               std::string replace_to = lua_tostring(L, -1);
+               std::string replace_to = readParam<std::string>(L, -1);
                lua_pop(L, 1);
-               replacements.pairs.push_back(
-                               std::make_pair(replace_from, replace_to));
+               replacements.pairs.emplace_back(replace_from, replace_to);
                // removes value, keeps key for next iteration
                lua_pop(L, 1);
        }
@@ -149,17 +147,17 @@ int ModApiCraft::l_register_craft(lua_State *L)
        */
        if(type == "shaped"){
                std::string output = getstringfield_default(L, table, "output", "");
-               if(output == "")
-                       throw LuaError(NULL, "Crafting definition is missing an output");
+               if (output.empty())
+                       throw LuaError("Crafting definition is missing an output");
 
                int width = 0;
                std::vector<std::string> recipe;
                lua_getfield(L, table, "recipe");
                if(lua_isnil(L, -1))
-                       throw LuaError(NULL, "Crafting definition is missing a recipe"
+                       throw LuaError("Crafting definition is missing a recipe"
                                        " (output=\"" + output + "\")");
                if(!readCraftRecipeShaped(L, -1, width, recipe))
-                       throw LuaError(NULL, "Invalid crafting recipe"
+                       throw LuaError("Invalid crafting recipe"
                                        " (output=\"" + output + "\")");
 
                CraftReplacements replacements;
@@ -167,31 +165,31 @@ int ModApiCraft::l_register_craft(lua_State *L)
                if(!lua_isnil(L, -1))
                {
                        if(!readCraftReplacements(L, -1, replacements))
-                               throw LuaError(NULL, "Invalid replacements"
+                               throw LuaError("Invalid replacements"
                                                " (output=\"" + output + "\")");
                }
 
                CraftDefinition *def = new CraftDefinitionShaped(
                                output, width, recipe, replacements);
-               craftdef->registerCraft(def);
+               craftdef->registerCraft(def, getServer(L));
        }
        /*
                CraftDefinitionShapeless
        */
        else if(type == "shapeless"){
                std::string output = getstringfield_default(L, table, "output", "");
-               if(output == "")
-                       throw LuaError(NULL, "Crafting definition (shapeless)"
+               if (output.empty())
+                       throw LuaError("Crafting definition (shapeless)"
                                        " is missing an output");
 
                std::vector<std::string> recipe;
                lua_getfield(L, table, "recipe");
                if(lua_isnil(L, -1))
-                       throw LuaError(NULL, "Crafting definition (shapeless)"
+                       throw LuaError("Crafting definition (shapeless)"
                                        " is missing a recipe"
                                        " (output=\"" + output + "\")");
                if(!readCraftRecipeShapeless(L, -1, recipe))
-                       throw LuaError(NULL, "Invalid crafting recipe"
+                       throw LuaError("Invalid crafting recipe"
                                        " (output=\"" + output + "\")");
 
                CraftReplacements replacements;
@@ -199,13 +197,13 @@ int ModApiCraft::l_register_craft(lua_State *L)
                if(!lua_isnil(L, -1))
                {
                        if(!readCraftReplacements(L, -1, replacements))
-                               throw LuaError(NULL, "Invalid replacements"
+                               throw LuaError("Invalid replacements"
                                                " (output=\"" + output + "\")");
                }
 
                CraftDefinition *def = new CraftDefinitionShapeless(
                                output, recipe, replacements);
-               craftdef->registerCraft(def);
+               craftdef->registerCraft(def, getServer(L));
        }
        /*
                CraftDefinitionToolRepair
@@ -216,20 +214,20 @@ int ModApiCraft::l_register_craft(lua_State *L)
 
                CraftDefinition *def = new CraftDefinitionToolRepair(
                                additional_wear);
-               craftdef->registerCraft(def);
+               craftdef->registerCraft(def, getServer(L));
        }
        /*
                CraftDefinitionCooking
        */
        else if(type == "cooking"){
                std::string output = getstringfield_default(L, table, "output", "");
-               if(output == "")
-                       throw LuaError(NULL, "Crafting definition (cooking)"
+               if (output.empty())
+                       throw LuaError("Crafting definition (cooking)"
                                        " is missing an output");
 
                std::string recipe = getstringfield_default(L, table, "recipe", "");
-               if(recipe == "")
-                       throw LuaError(NULL, "Crafting definition (cooking)"
+               if (recipe.empty())
+                       throw LuaError("Crafting definition (cooking)"
                                        " is missing a recipe"
                                        " (output=\"" + output + "\")");
 
@@ -240,21 +238,21 @@ int ModApiCraft::l_register_craft(lua_State *L)
                if(!lua_isnil(L, -1))
                {
                        if(!readCraftReplacements(L, -1, replacements))
-                               throw LuaError(NULL, "Invalid replacements"
+                               throw LuaError("Invalid replacements"
                                                " (cooking output=\"" + output + "\")");
                }
 
                CraftDefinition *def = new CraftDefinitionCooking(
                                output, recipe, cooktime, replacements);
-               craftdef->registerCraft(def);
+               craftdef->registerCraft(def, getServer(L));
        }
        /*
                CraftDefinitionFuel
        */
        else if(type == "fuel"){
                std::string recipe = getstringfield_default(L, table, "recipe", "");
-               if(recipe == "")
-                       throw LuaError(NULL, "Crafting definition (fuel)"
+               if (recipe.empty())
+                       throw LuaError("Crafting definition (fuel)"
                                        " is missing a recipe");
 
                float burntime = getfloatfield_default(L, table, "burntime", 1.0);
@@ -264,29 +262,118 @@ int ModApiCraft::l_register_craft(lua_State *L)
                if(!lua_isnil(L, -1))
                {
                        if(!readCraftReplacements(L, -1, replacements))
-                               throw LuaError(NULL, "Invalid replacements"
+                               throw LuaError("Invalid replacements"
                                                " (fuel recipe=\"" + recipe + "\")");
                }
 
                CraftDefinition *def = new CraftDefinitionFuel(
                                recipe, burntime, replacements);
-               craftdef->registerCraft(def);
+               craftdef->registerCraft(def, getServer(L));
        }
        else
        {
-               throw LuaError(NULL, "Unknown crafting definition type: \"" + type + "\"");
+               throw LuaError("Unknown crafting definition type: \"" + type + "\"");
        }
 
        lua_pop(L, 1);
        return 0; /* number of results */
 }
 
+// clear_craft({[output=item], [recipe={{item00,item10},{item01,item11}}])
+int ModApiCraft::l_clear_craft(lua_State *L)
+{
+       NO_MAP_LOCK_REQUIRED;
+       luaL_checktype(L, 1, LUA_TTABLE);
+       int table = 1;
+
+       // Get the writable craft definition manager from the server
+       IWritableCraftDefManager *craftdef =
+                       getServer(L)->getWritableCraftDefManager();
+
+       std::string output = getstringfield_default(L, table, "output", "");
+       std::string type = getstringfield_default(L, table, "type", "shaped");
+       CraftOutput c_output(output, 0);
+       if (!output.empty()) {
+               if (craftdef->clearCraftsByOutput(c_output, getServer(L))) {
+                       lua_pushboolean(L, true);
+                       return 1;
+               }
+
+               warningstream << "No craft recipe known for output" << std::endl;
+               lua_pushboolean(L, false);
+               return 1;
+       }
+       std::vector<std::string> recipe;
+       int width = 0;
+       CraftMethod method = CRAFT_METHOD_NORMAL;
+       /*
+               CraftDefinitionShaped
+       */
+       if (type == "shaped") {
+               lua_getfield(L, table, "recipe");
+               if (lua_isnil(L, -1))
+                       throw LuaError("Either output or recipe has to be defined");
+               if (!readCraftRecipeShaped(L, -1, width, recipe))
+                       throw LuaError("Invalid crafting recipe");
+       }
+       /*
+               CraftDefinitionShapeless
+       */
+       else if (type == "shapeless") {
+               lua_getfield(L, table, "recipe");
+               if (lua_isnil(L, -1))
+                       throw LuaError("Either output or recipe has to be defined");
+               if (!readCraftRecipeShapeless(L, -1, recipe))
+                       throw LuaError("Invalid crafting recipe");
+       }
+       /*
+               CraftDefinitionCooking
+       */
+       else if (type == "cooking") {
+               method = CRAFT_METHOD_COOKING;
+               std::string rec = getstringfield_default(L, table, "recipe", "");
+               if (rec.empty())
+                       throw LuaError("Crafting definition (cooking)"
+                                       " is missing a recipe");
+               recipe.push_back(rec);
+       }
+       /*
+               CraftDefinitionFuel
+       */
+       else if (type == "fuel") {
+               method = CRAFT_METHOD_FUEL;
+               std::string rec = getstringfield_default(L, table, "recipe", "");
+               if (rec.empty())
+                       throw LuaError("Crafting definition (fuel)"
+                                       " is missing a recipe");
+               recipe.push_back(rec);
+       } else {
+               throw LuaError("Unknown crafting definition type: \"" + type + "\"");
+       }
+
+       std::vector<ItemStack> items;
+       items.reserve(recipe.size());
+       for (const auto &item : recipe)
+               items.emplace_back(item, 1, 0, getServer(L)->idef());
+       CraftInput input(method, width, items);
+
+       if (!craftdef->clearCraftsByInput(input, getServer(L))) {
+               warningstream << "No craft recipe matches input" << std::endl;
+               lua_pushboolean(L, false);
+               return 1;
+       }
+
+       lua_pushboolean(L, true);
+       return 1;
+}
+
 // get_craft_result(input)
 int ModApiCraft::l_get_craft_result(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
+       IGameDef *gdef = getGameDef(L);
 
-       int input_i = 1;
+       const int input_i = 1;
        std::string method_s = getstringfield_default(L, input_i, "method", "normal");
        enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
                                es_CraftMethod, CRAFT_METHOD_NORMAL);
@@ -296,25 +383,29 @@ int ModApiCraft::l_get_craft_result(lua_State *L)
                width = luaL_checkinteger(L, -1);
        lua_pop(L, 1);
        lua_getfield(L, input_i, "items");
-       std::vector<ItemStack> items = read_items(L, -1,getServer(L));
+       std::vector<ItemStack> items = read_items(L, -1, gdef);
        lua_pop(L, 1); // items
 
-       IGameDef *gdef = getServer(L);
        ICraftDefManager *cdef = gdef->cdef();
        CraftInput input(method, width, items);
        CraftOutput output;
-       bool got = cdef->getCraftResult(input, output, true, gdef);
+       std::vector<ItemStack> output_replacements;
+       bool got = cdef->getCraftResult(input, output, output_replacements, true, gdef);
        lua_newtable(L); // output table
-       if(got){
+       if (got) {
                ItemStack item;
                item.deSerialize(output.item, gdef->idef());
                LuaItemStack::create(L, item);
                lua_setfield(L, -2, "item");
                setintfield(L, -1, "time", output.time);
+               push_items(L, output_replacements);
+               lua_setfield(L, -2, "replacements");
        } else {
                LuaItemStack::create(L, ItemStack());
                lua_setfield(L, -2, "item");
                setintfield(L, -1, "time", 0);
+               lua_newtable(L);
+               lua_setfield(L, -2, "replacements");
        }
        lua_newtable(L); // decremented input table
        lua_pushstring(L, method_s.c_str());
@@ -326,56 +417,89 @@ int ModApiCraft::l_get_craft_result(lua_State *L)
        return 2;
 }
 
+
+static void push_craft_recipe(lua_State *L, IGameDef *gdef,
+               const CraftDefinition *recipe,
+               const CraftOutput &tmpout)
+{
+       CraftInput input = recipe->getInput(tmpout, gdef);
+       CraftOutput output = recipe->getOutput(input, gdef);
+
+       lua_newtable(L); // items
+       std::vector<ItemStack>::const_iterator iter = input.items.begin();
+       for (u16 j = 1; iter != input.items.end(); ++iter, j++) {
+               if (iter->empty())
+                       continue;
+               lua_pushstring(L, iter->name.c_str());
+               lua_rawseti(L, -2, j);
+       }
+       lua_setfield(L, -2, "items");
+       setintfield(L, -1, "width", input.width);
+
+       std::string method_s;
+       switch (input.method) {
+       case CRAFT_METHOD_NORMAL:
+               method_s = "normal";
+               break;
+       case CRAFT_METHOD_COOKING:
+               method_s = "cooking";
+               break;
+       case CRAFT_METHOD_FUEL:
+               method_s = "fuel";
+               break;
+       default:
+               method_s = "unknown";
+       }
+       lua_pushstring(L, method_s.c_str());
+       lua_setfield(L, -2, "method");
+
+       // Deprecated, only for compatibility's sake
+       lua_pushstring(L, method_s.c_str());
+       lua_setfield(L, -2, "type");
+
+       lua_pushstring(L, output.item.c_str());
+       lua_setfield(L, -2, "output");
+}
+
+static void push_craft_recipes(lua_State *L, IGameDef *gdef,
+               const std::vector<CraftDefinition*> &recipes,
+               const CraftOutput &output)
+{
+       if (recipes.empty()) {
+               lua_pushnil(L);
+               return;
+       }
+
+       lua_createtable(L, recipes.size(), 0);
+
+       std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
+       for (unsigned i = 0; it != recipes.end(); ++it) {
+               lua_newtable(L);
+               push_craft_recipe(L, gdef, *it, output);
+               lua_rawseti(L, -2, ++i);
+       }
+}
+
+
 // get_craft_recipe(result item)
 int ModApiCraft::l_get_craft_recipe(lua_State *L)
 {
        NO_MAP_LOCK_REQUIRED;
 
-       int k = 1;
-       int input_i = 1;
-       std::string o_item = luaL_checkstring(L,input_i);
+       std::string item = luaL_checkstring(L, 1);
+       IGameDef *gdef = getGameDef(L);
+       CraftOutput output(item, 0);
+       auto recipes = gdef->cdef()->getCraftRecipes(output, gdef, 1);
 
-       IGameDef *gdef = getServer(L);
-       ICraftDefManager *cdef = gdef->cdef();
-       CraftInput input;
-       CraftOutput output(o_item,0);
-       bool got = cdef->getCraftRecipe(input, output, gdef);
-       lua_newtable(L); // output table
-       if(got){
-               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");
-       } else {
+       lua_createtable(L, 1, 0);
+
+       if (recipes.empty()) {
                lua_pushnil(L);
                lua_setfield(L, -2, "items");
                setintfield(L, -1, "width", 0);
+               return 1;
        }
+       push_craft_recipe(L, gdef, recipes[0], output);
        return 1;
 }
 
@@ -384,59 +508,12 @@ 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;
-       recipes_list = cdef->getCraftRecipes(output, gdef);
-       if (recipes_list.empty()) {
-               lua_pushnil(L);
-               return 1;
-       }
+       std::string item = luaL_checkstring(L, 1);
+       IGameDef *gdef = getGameDef(L);
+       CraftOutput output(item, 0);
+       auto recipes = gdef->cdef()->getCraftRecipes(output, gdef);
 
-       lua_createtable(L, recipes_list.size(), 0);
-       std::vector<CraftDefinition*>::const_iterator iter = recipes_list.begin();
-       for (u16 i = 0; iter != recipes_list.end(); iter++) {
-               CraftOutput tmpout;
-               tmpout.item = "";
-               tmpout.time = 0;
-               tmpout = (*iter)->getOutput(input, gdef);
-               std::string query = tmpout.item;
-               char *fmtpos, *fmt = &query[0];
-               if (strtok_r(fmt, " ", &fmtpos) == output.item) {
-                       input = (*iter)->getInput(output, gdef);
-                       lua_newtable(L);
-                       lua_newtable(L); // items
-                       std::vector<ItemStack>::const_iterator iter = input.items.begin();
-                       for (u16 j = 1; iter != input.items.end(); iter++, j++) {
-                               if (iter->empty())
-                                       continue;
-                               lua_pushstring(L, iter->name.c_str());
-                               lua_rawseti(L, -2, j);
-                       }
-                       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");
-                       lua_rawseti(L, -2, ++i);
-               }
-       }
+       push_craft_recipes(L, gdef, recipes, output);
        return 1;
 }
 
@@ -446,4 +523,13 @@ void ModApiCraft::Initialize(lua_State *L, int top)
        API_FCT(get_craft_recipe);
        API_FCT(get_craft_result);
        API_FCT(register_craft);
+       API_FCT(clear_craft);
+}
+
+void ModApiCraft::InitializeAsync(lua_State *L, int top)
+{
+       // all read-only functions
+       API_FCT(get_all_craft_recipes);
+       API_FCT(get_craft_recipe);
+       API_FCT(get_craft_result);
 }