#include "lua_api/l_craft.h"
-#include "common/c_internal.h"
+#include "lua_api/l_internal.h"
+#include "lua_api/l_item.h"
#include "common/c_converter.h"
#include "common/c_content.h"
#include "server.h"
-#include "lua_api/l_item.h"
-
-extern "C" {
-#include "lauxlib.h"
-}
-
-ModApiCraft::ModApiCraft()
- : ModApiBase() {
-
-}
+#include "craftdef.h"
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)
// 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++;
// 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);
}
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);
}
*/
if(type == "shaped"){
std::string output = getstringfield_default(L, table, "output", "");
- if(output == "")
- throw LuaError(L, "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(L, "Crafting definition is missing a recipe"
+ throw LuaError("Crafting definition is missing a recipe"
" (output=\"" + output + "\")");
if(!readCraftRecipeShaped(L, -1, width, recipe))
- throw LuaError(L, "Invalid crafting recipe"
+ throw LuaError("Invalid crafting recipe"
" (output=\"" + output + "\")");
CraftReplacements replacements;
if(!lua_isnil(L, -1))
{
if(!readCraftReplacements(L, -1, replacements))
- throw LuaError(L, "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(L, "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(L, "Crafting definition (shapeless)"
+ throw LuaError("Crafting definition (shapeless)"
" is missing a recipe"
" (output=\"" + output + "\")");
if(!readCraftRecipeShapeless(L, -1, recipe))
- throw LuaError(L, "Invalid crafting recipe"
+ throw LuaError("Invalid crafting recipe"
" (output=\"" + output + "\")");
CraftReplacements replacements;
if(!lua_isnil(L, -1))
{
if(!readCraftReplacements(L, -1, replacements))
- throw LuaError(L, "Invalid replacements"
+ throw LuaError("Invalid replacements"
" (output=\"" + output + "\")");
}
CraftDefinition *def = new CraftDefinitionShapeless(
output, recipe, replacements);
- craftdef->registerCraft(def);
+ craftdef->registerCraft(def, getServer(L));
}
/*
CraftDefinitionToolRepair
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(L, "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(L, "Crafting definition (cooking)"
+ if (recipe.empty())
+ throw LuaError("Crafting definition (cooking)"
" is missing a recipe"
" (output=\"" + output + "\")");
if(!lua_isnil(L, -1))
{
if(!readCraftReplacements(L, -1, replacements))
- throw LuaError(L, "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(L, "Crafting definition (fuel)"
+ if (recipe.empty())
+ throw LuaError("Crafting definition (fuel)"
" is missing a recipe");
float burntime = getfloatfield_default(L, table, "burntime", 1.0);
if(!lua_isnil(L, -1))
{
if(!readCraftReplacements(L, -1, replacements))
- throw LuaError(L, "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(L, "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);
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());
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;
}
{
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, "error: %s", lua_tostring(L, -1));
- }
- }
+ std::string item = luaL_checkstring(L, 1);
+ IGameDef *gdef = getGameDef(L);
+ CraftOutput output(item, 0);
+ auto recipes = gdef->cdef()->getCraftRecipes(output, gdef);
+
+ push_craft_recipes(L, gdef, recipes, output);
return 1;
}
-bool ModApiCraft::Initialize(lua_State* L, int top) {
- bool retval = true;
-
- retval &= API_FCT(get_all_craft_recipes);
- retval &= API_FCT(get_craft_recipe);
- retval &= API_FCT(get_craft_result);
- retval &= API_FCT(register_craft);
-
- return retval;
+void ModApiCraft::Initialize(lua_State *L, int top)
+{
+ API_FCT(get_all_craft_recipes);
+ API_FCT(get_craft_recipe);
+ API_FCT(get_craft_result);
+ API_FCT(register_craft);
+ API_FCT(clear_craft);
}
-ModApiCraft modapicraft_prototype;
+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);
+}