]> git.lizzy.rs Git - dragonfireclient.git/blob - src/script/lua_api/l_craft.cpp
Add core.request_insecure_environment()
[dragonfireclient.git] / src / script / lua_api / l_craft.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 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
21 #include "lua_api/l_craft.h"
22 #include "lua_api/l_internal.h"
23 #include "lua_api/l_item.h"
24 #include "common/c_converter.h"
25 #include "common/c_content.h"
26 #include "server.h"
27 #include "craftdef.h"
28
29 struct EnumString ModApiCraft::es_CraftMethod[] =
30 {
31         {CRAFT_METHOD_NORMAL, "normal"},
32         {CRAFT_METHOD_COOKING, "cooking"},
33         {CRAFT_METHOD_FUEL, "fuel"},
34         {0, NULL},
35 };
36
37
38 // helper for register_craft
39 bool ModApiCraft::readCraftRecipeShaped(lua_State *L, int index,
40                 int &width, std::vector<std::string> &recipe)
41 {
42         if(index < 0)
43                 index = lua_gettop(L) + 1 + index;
44
45         if(!lua_istable(L, index))
46                 return false;
47
48         lua_pushnil(L);
49         int rowcount = 0;
50         while(lua_next(L, index) != 0){
51                 int colcount = 0;
52                 // key at index -2 and value at index -1
53                 if(!lua_istable(L, -1))
54                         return false;
55                 int table2 = lua_gettop(L);
56                 lua_pushnil(L);
57                 while(lua_next(L, table2) != 0){
58                         // key at index -2 and value at index -1
59                         if(!lua_isstring(L, -1))
60                                 return false;
61                         recipe.push_back(lua_tostring(L, -1));
62                         // removes value, keeps key for next iteration
63                         lua_pop(L, 1);
64                         colcount++;
65                 }
66                 if(rowcount == 0){
67                         width = colcount;
68                 } else {
69                         if(colcount != width)
70                                 return false;
71                 }
72                 // removes value, keeps key for next iteration
73                 lua_pop(L, 1);
74                 rowcount++;
75         }
76         return width != 0;
77 }
78
79 // helper for register_craft
80 bool ModApiCraft::readCraftRecipeShapeless(lua_State *L, int index,
81                 std::vector<std::string> &recipe)
82 {
83         if(index < 0)
84                 index = lua_gettop(L) + 1 + index;
85
86         if(!lua_istable(L, index))
87                 return false;
88
89         lua_pushnil(L);
90         while(lua_next(L, index) != 0){
91                 // key at index -2 and value at index -1
92                 if(!lua_isstring(L, -1))
93                         return false;
94                 recipe.push_back(lua_tostring(L, -1));
95                 // removes value, keeps key for next iteration
96                 lua_pop(L, 1);
97         }
98         return true;
99 }
100
101 // helper for register_craft
102 bool ModApiCraft::readCraftReplacements(lua_State *L, int index,
103                 CraftReplacements &replacements)
104 {
105         if(index < 0)
106                 index = lua_gettop(L) + 1 + index;
107
108         if(!lua_istable(L, index))
109                 return false;
110
111         lua_pushnil(L);
112         while(lua_next(L, index) != 0){
113                 // key at index -2 and value at index -1
114                 if(!lua_istable(L, -1))
115                         return false;
116                 lua_rawgeti(L, -1, 1);
117                 if(!lua_isstring(L, -1))
118                         return false;
119                 std::string replace_from = lua_tostring(L, -1);
120                 lua_pop(L, 1);
121                 lua_rawgeti(L, -1, 2);
122                 if(!lua_isstring(L, -1))
123                         return false;
124                 std::string replace_to = lua_tostring(L, -1);
125                 lua_pop(L, 1);
126                 replacements.pairs.push_back(
127                                 std::make_pair(replace_from, replace_to));
128                 // removes value, keeps key for next iteration
129                 lua_pop(L, 1);
130         }
131         return true;
132 }
133 // register_craft({output=item, recipe={{item00,item10},{item01,item11}})
134 int ModApiCraft::l_register_craft(lua_State *L)
135 {
136         NO_MAP_LOCK_REQUIRED;
137         //infostream<<"register_craft"<<std::endl;
138         luaL_checktype(L, 1, LUA_TTABLE);
139         int table = 1;
140
141         // Get the writable craft definition manager from the server
142         IWritableCraftDefManager *craftdef =
143                         getServer(L)->getWritableCraftDefManager();
144
145         std::string type = getstringfield_default(L, table, "type", "shaped");
146
147         /*
148                 CraftDefinitionShaped
149         */
150         if(type == "shaped"){
151                 std::string output = getstringfield_default(L, table, "output", "");
152                 if(output == "")
153                         throw LuaError("Crafting definition is missing an output");
154
155                 int width = 0;
156                 std::vector<std::string> recipe;
157                 lua_getfield(L, table, "recipe");
158                 if(lua_isnil(L, -1))
159                         throw LuaError("Crafting definition is missing a recipe"
160                                         " (output=\"" + output + "\")");
161                 if(!readCraftRecipeShaped(L, -1, width, recipe))
162                         throw LuaError("Invalid crafting recipe"
163                                         " (output=\"" + output + "\")");
164
165                 CraftReplacements replacements;
166                 lua_getfield(L, table, "replacements");
167                 if(!lua_isnil(L, -1))
168                 {
169                         if(!readCraftReplacements(L, -1, replacements))
170                                 throw LuaError("Invalid replacements"
171                                                 " (output=\"" + output + "\")");
172                 }
173
174                 CraftDefinition *def = new CraftDefinitionShaped(
175                                 output, width, recipe, replacements);
176                 craftdef->registerCraft(def, getServer(L));
177         }
178         /*
179                 CraftDefinitionShapeless
180         */
181         else if(type == "shapeless"){
182                 std::string output = getstringfield_default(L, table, "output", "");
183                 if(output == "")
184                         throw LuaError("Crafting definition (shapeless)"
185                                         " is missing an output");
186
187                 std::vector<std::string> recipe;
188                 lua_getfield(L, table, "recipe");
189                 if(lua_isnil(L, -1))
190                         throw LuaError("Crafting definition (shapeless)"
191                                         " is missing a recipe"
192                                         " (output=\"" + output + "\")");
193                 if(!readCraftRecipeShapeless(L, -1, recipe))
194                         throw LuaError("Invalid crafting recipe"
195                                         " (output=\"" + output + "\")");
196
197                 CraftReplacements replacements;
198                 lua_getfield(L, table, "replacements");
199                 if(!lua_isnil(L, -1))
200                 {
201                         if(!readCraftReplacements(L, -1, replacements))
202                                 throw LuaError("Invalid replacements"
203                                                 " (output=\"" + output + "\")");
204                 }
205
206                 CraftDefinition *def = new CraftDefinitionShapeless(
207                                 output, recipe, replacements);
208                 craftdef->registerCraft(def, getServer(L));
209         }
210         /*
211                 CraftDefinitionToolRepair
212         */
213         else if(type == "toolrepair"){
214                 float additional_wear = getfloatfield_default(L, table,
215                                 "additional_wear", 0.0);
216
217                 CraftDefinition *def = new CraftDefinitionToolRepair(
218                                 additional_wear);
219                 craftdef->registerCraft(def, getServer(L));
220         }
221         /*
222                 CraftDefinitionCooking
223         */
224         else if(type == "cooking"){
225                 std::string output = getstringfield_default(L, table, "output", "");
226                 if(output == "")
227                         throw LuaError("Crafting definition (cooking)"
228                                         " is missing an output");
229
230                 std::string recipe = getstringfield_default(L, table, "recipe", "");
231                 if(recipe == "")
232                         throw LuaError("Crafting definition (cooking)"
233                                         " is missing a recipe"
234                                         " (output=\"" + output + "\")");
235
236                 float cooktime = getfloatfield_default(L, table, "cooktime", 3.0);
237
238                 CraftReplacements replacements;
239                 lua_getfield(L, table, "replacements");
240                 if(!lua_isnil(L, -1))
241                 {
242                         if(!readCraftReplacements(L, -1, replacements))
243                                 throw LuaError("Invalid replacements"
244                                                 " (cooking output=\"" + output + "\")");
245                 }
246
247                 CraftDefinition *def = new CraftDefinitionCooking(
248                                 output, recipe, cooktime, replacements);
249                 craftdef->registerCraft(def, getServer(L));
250         }
251         /*
252                 CraftDefinitionFuel
253         */
254         else if(type == "fuel"){
255                 std::string recipe = getstringfield_default(L, table, "recipe", "");
256                 if(recipe == "")
257                         throw LuaError("Crafting definition (fuel)"
258                                         " is missing a recipe");
259
260                 float burntime = getfloatfield_default(L, table, "burntime", 1.0);
261
262                 CraftReplacements replacements;
263                 lua_getfield(L, table, "replacements");
264                 if(!lua_isnil(L, -1))
265                 {
266                         if(!readCraftReplacements(L, -1, replacements))
267                                 throw LuaError("Invalid replacements"
268                                                 " (fuel recipe=\"" + recipe + "\")");
269                 }
270
271                 CraftDefinition *def = new CraftDefinitionFuel(
272                                 recipe, burntime, replacements);
273                 craftdef->registerCraft(def, getServer(L));
274         }
275         else
276         {
277                 throw LuaError("Unknown crafting definition type: \"" + type + "\"");
278         }
279
280         lua_pop(L, 1);
281         return 0; /* number of results */
282 }
283
284 // get_craft_result(input)
285 int ModApiCraft::l_get_craft_result(lua_State *L)
286 {
287         NO_MAP_LOCK_REQUIRED;
288
289         int input_i = 1;
290         std::string method_s = getstringfield_default(L, input_i, "method", "normal");
291         enum CraftMethod method = (CraftMethod)getenumfield(L, input_i, "method",
292                                 es_CraftMethod, CRAFT_METHOD_NORMAL);
293         int width = 1;
294         lua_getfield(L, input_i, "width");
295         if(lua_isnumber(L, -1))
296                 width = luaL_checkinteger(L, -1);
297         lua_pop(L, 1);
298         lua_getfield(L, input_i, "items");
299         std::vector<ItemStack> items = read_items(L, -1,getServer(L));
300         lua_pop(L, 1); // items
301
302         IGameDef *gdef = getServer(L);
303         ICraftDefManager *cdef = gdef->cdef();
304         CraftInput input(method, width, items);
305         CraftOutput output;
306         bool got = cdef->getCraftResult(input, output, true, gdef);
307         lua_newtable(L); // output table
308         if(got){
309                 ItemStack item;
310                 item.deSerialize(output.item, gdef->idef());
311                 LuaItemStack::create(L, item);
312                 lua_setfield(L, -2, "item");
313                 setintfield(L, -1, "time", output.time);
314         } else {
315                 LuaItemStack::create(L, ItemStack());
316                 lua_setfield(L, -2, "item");
317                 setintfield(L, -1, "time", 0);
318         }
319         lua_newtable(L); // decremented input table
320         lua_pushstring(L, method_s.c_str());
321         lua_setfield(L, -2, "method");
322         lua_pushinteger(L, width);
323         lua_setfield(L, -2, "width");
324         push_items(L, input.items);
325         lua_setfield(L, -2, "items");
326         return 2;
327 }
328
329
330 void push_craft_recipe(lua_State *L, IGameDef *gdef,
331                 const CraftDefinition *recipe,
332                 const CraftOutput &tmpout)
333 {
334         CraftInput input = recipe->getInput(tmpout, gdef);
335         CraftOutput output = recipe->getOutput(input, gdef);
336
337         lua_newtable(L); // items
338         std::vector<ItemStack>::const_iterator iter = input.items.begin();
339         for (u16 j = 1; iter != input.items.end(); iter++, j++) {
340                 if (iter->empty())
341                         continue;
342                 lua_pushstring(L, iter->name.c_str());
343                 lua_rawseti(L, -2, j);
344         }
345         lua_setfield(L, -2, "items");
346         setintfield(L, -1, "width", input.width);
347         switch (input.method) {
348         case CRAFT_METHOD_NORMAL:
349                 lua_pushstring(L, "normal");
350                 break;
351         case CRAFT_METHOD_COOKING:
352                 lua_pushstring(L, "cooking");
353                 break;
354         case CRAFT_METHOD_FUEL:
355                 lua_pushstring(L, "fuel");
356                 break;
357         default:
358                 lua_pushstring(L, "unknown");
359         }
360         lua_setfield(L, -2, "type");
361         lua_pushstring(L, tmpout.item.c_str());
362         lua_setfield(L, -2, "output");
363 }
364
365 void push_craft_recipes(lua_State *L, IGameDef *gdef,
366                 const std::vector<CraftDefinition*> &recipes,
367                 const CraftOutput &output)
368 {
369         lua_createtable(L, recipes.size(), 0);
370
371         if (recipes.empty()) {
372                 lua_pushnil(L);
373                 return;
374         }
375
376         std::vector<CraftDefinition*>::const_iterator it = recipes.begin();
377         for (unsigned i = 0; it != recipes.end(); ++it) {
378                 lua_newtable(L);
379                 push_craft_recipe(L, gdef, *it, output);
380                 lua_rawseti(L, -2, ++i);
381         }
382 }
383
384
385 // get_craft_recipe(result item)
386 int ModApiCraft::l_get_craft_recipe(lua_State *L)
387 {
388         NO_MAP_LOCK_REQUIRED;
389
390         std::string item = luaL_checkstring(L, 1);
391         Server *server = getServer(L);
392         CraftOutput output(item, 0);
393         std::vector<CraftDefinition*> recipes = server->cdef()
394                         ->getCraftRecipes(output, server, 1);
395
396         lua_createtable(L, 1, 0);
397
398         if (recipes.empty()) {
399                 lua_pushnil(L);
400                 lua_setfield(L, -2, "items");
401                 setintfield(L, -1, "width", 0);
402                 return 1;
403         }
404         push_craft_recipe(L, server, recipes[0], output);
405         return 1;
406 }
407
408 // get_all_craft_recipes(result item)
409 int ModApiCraft::l_get_all_craft_recipes(lua_State *L)
410 {
411         NO_MAP_LOCK_REQUIRED;
412
413         std::string item = luaL_checkstring(L, 1);
414         Server *server = getServer(L);
415         CraftOutput output(item, 0);
416         std::vector<CraftDefinition*> recipes = server->cdef()
417                         ->getCraftRecipes(output, server);
418
419         push_craft_recipes(L, server, recipes, output);
420         return 1;
421 }
422
423 void ModApiCraft::Initialize(lua_State *L, int top)
424 {
425         API_FCT(get_all_craft_recipes);
426         API_FCT(get_craft_recipe);
427         API_FCT(get_craft_result);
428         API_FCT(register_craft);
429 }