]> git.lizzy.rs Git - minetest.git/blob - src/script/cpp_api/s_item.cpp
Fix builtin inventory list crash when size = 0 (#7297)
[minetest.git] / src / script / cpp_api / s_item.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 #include "cpp_api/s_item.h"
21 #include "cpp_api/s_internal.h"
22 #include "common/c_converter.h"
23 #include "common/c_content.h"
24 #include "lua_api/l_item.h"
25 #include "lua_api/l_inventory.h"
26 #include "server.h"
27 #include "log.h"
28 #include "util/pointedthing.h"
29 #include "inventory.h"
30 #include "inventorymanager.h"
31
32 bool ScriptApiItem::item_OnDrop(ItemStack &item,
33                 ServerActiveObject *dropper, v3f pos)
34 {
35         SCRIPTAPI_PRECHECKHEADER
36
37         int error_handler = PUSH_ERROR_HANDLER(L);
38
39         // Push callback function on stack
40         if (!getItemCallback(item.name.c_str(), "on_drop"))
41                 return false;
42
43         // Call function
44         LuaItemStack::create(L, item);
45         objectrefGetOrCreate(L, dropper);
46         pushFloatPos(L, pos);
47         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
48         if (!lua_isnil(L, -1)) {
49                 try {
50                         item = read_item(L, -1, getServer()->idef());
51                 } catch (LuaError &e) {
52                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
53                 }
54         }
55         lua_pop(L, 2);  // Pop item and error handler
56         return true;
57 }
58
59 bool ScriptApiItem::item_OnPlace(ItemStack &item,
60                 ServerActiveObject *placer, const PointedThing &pointed)
61 {
62         SCRIPTAPI_PRECHECKHEADER
63
64         int error_handler = PUSH_ERROR_HANDLER(L);
65
66         // Push callback function on stack
67         if (!getItemCallback(item.name.c_str(), "on_place"))
68                 return false;
69
70         // Call function
71         LuaItemStack::create(L, item);
72
73         if (!placer)
74                 lua_pushnil(L);
75         else
76                 objectrefGetOrCreate(L, placer);
77
78         pushPointedThing(pointed);
79         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
80         if (!lua_isnil(L, -1)) {
81                 try {
82                         item = read_item(L, -1, getServer()->idef());
83                 } catch (LuaError &e) {
84                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
85                 }
86         }
87         lua_pop(L, 2);  // Pop item and error handler
88         return true;
89 }
90
91 bool ScriptApiItem::item_OnUse(ItemStack &item,
92                 ServerActiveObject *user, const PointedThing &pointed)
93 {
94         SCRIPTAPI_PRECHECKHEADER
95
96         int error_handler = PUSH_ERROR_HANDLER(L);
97
98         // Push callback function on stack
99         if (!getItemCallback(item.name.c_str(), "on_use"))
100                 return false;
101
102         // Call function
103         LuaItemStack::create(L, item);
104         objectrefGetOrCreate(L, user);
105         pushPointedThing(pointed);
106         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
107         if(!lua_isnil(L, -1)) {
108                 try {
109                         item = read_item(L, -1, getServer()->idef());
110                 } catch (LuaError &e) {
111                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
112                 }
113         }
114         lua_pop(L, 2);  // Pop item and error handler
115         return true;
116 }
117
118 bool ScriptApiItem::item_OnSecondaryUse(ItemStack &item, ServerActiveObject *user)
119 {
120         SCRIPTAPI_PRECHECKHEADER
121
122         int error_handler = PUSH_ERROR_HANDLER(L);
123
124         if (!getItemCallback(item.name.c_str(), "on_secondary_use"))
125                 return false;
126
127         LuaItemStack::create(L, item);
128         objectrefGetOrCreate(L, user);
129         PointedThing pointed;
130         pointed.type = POINTEDTHING_NOTHING;
131         pushPointedThing(pointed);
132         PCALL_RES(lua_pcall(L, 3, 1, error_handler));
133         if (!lua_isnil(L, -1)) {
134                 try {
135                         item = read_item(L, -1, getServer()->idef());
136                 } catch (LuaError &e) {
137                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
138                 }
139         }
140         lua_pop(L, 2);  // Pop item and error handler
141         return true;
142 }
143
144 bool ScriptApiItem::item_OnCraft(ItemStack &item, ServerActiveObject *user,
145                 const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
146 {
147         SCRIPTAPI_PRECHECKHEADER
148
149         int error_handler = PUSH_ERROR_HANDLER(L);
150
151         lua_getglobal(L, "core");
152         lua_getfield(L, -1, "on_craft");
153         LuaItemStack::create(L, item);
154         objectrefGetOrCreate(L, user);
155
156         // Push inventory list
157         std::vector<ItemStack> items;
158         for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
159                 items.push_back(old_craft_grid->getItem(i));
160         }
161         push_items(L, items);
162
163         InvRef::create(L, craft_inv);
164         PCALL_RES(lua_pcall(L, 4, 1, error_handler));
165         if (!lua_isnil(L, -1)) {
166                 try {
167                         item = read_item(L, -1, getServer()->idef());
168                 } catch (LuaError &e) {
169                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
170                 }
171         }
172         lua_pop(L, 2);  // Pop item and error handler
173         return true;
174 }
175
176 bool ScriptApiItem::item_CraftPredict(ItemStack &item, ServerActiveObject *user,
177                 const InventoryList *old_craft_grid, const InventoryLocation &craft_inv)
178 {
179         SCRIPTAPI_PRECHECKHEADER
180         sanity_check(old_craft_grid);
181         int error_handler = PUSH_ERROR_HANDLER(L);
182
183         lua_getglobal(L, "core");
184         lua_getfield(L, -1, "craft_predict");
185         LuaItemStack::create(L, item);
186         objectrefGetOrCreate(L, user);
187
188         //Push inventory list
189         std::vector<ItemStack> items;
190         for (u32 i = 0; i < old_craft_grid->getSize(); i++) {
191                 items.push_back(old_craft_grid->getItem(i));
192         }
193         push_items(L, items);
194
195         InvRef::create(L, craft_inv);
196         PCALL_RES(lua_pcall(L, 4, 1, error_handler));
197         if (!lua_isnil(L, -1)) {
198                 try {
199                         item = read_item(L, -1, getServer()->idef());
200                 } catch (LuaError &e) {
201                         throw LuaError(std::string(e.what()) + ". item=" + item.name);
202                 }
203         }
204         lua_pop(L, 2);  // Pop item and error handler
205         return true;
206 }
207
208 // Retrieves core.registered_items[name][callbackname]
209 // If that is nil or on error, return false and stack is unchanged
210 // If that is a function, returns true and pushes the
211 // function onto the stack
212 // If core.registered_items[name] doesn't exist, core.nodedef_default
213 // is tried instead so unknown items can still be manipulated to some degree
214 bool ScriptApiItem::getItemCallback(const char *name, const char *callbackname,
215                 const v3s16 *p)
216 {
217         lua_State* L = getStack();
218
219         lua_getglobal(L, "core");
220         lua_getfield(L, -1, "registered_items");
221         lua_remove(L, -2); // Remove core
222         luaL_checktype(L, -1, LUA_TTABLE);
223         lua_getfield(L, -1, name);
224         lua_remove(L, -2); // Remove registered_items
225         // Should be a table
226         if (lua_type(L, -1) != LUA_TTABLE) {
227                 // Report error and clean up
228                 errorstream << "Item \"" << name << "\" not defined";
229                 if (p)
230                         errorstream << " at position " << PP(*p);
231                 errorstream << std::endl;
232                 lua_pop(L, 1);
233
234                 // Try core.nodedef_default instead
235                 lua_getglobal(L, "core");
236                 lua_getfield(L, -1, "nodedef_default");
237                 lua_remove(L, -2);
238                 luaL_checktype(L, -1, LUA_TTABLE);
239         }
240
241         setOriginFromTable(-1);
242
243         lua_getfield(L, -1, callbackname);
244         lua_remove(L, -2); // Remove item def
245         // Should be a function or nil
246         if (lua_type(L, -1) == LUA_TFUNCTION) {
247                 return true;
248         }
249
250         if (!lua_isnil(L, -1)) {
251                 errorstream << "Item \"" << name << "\" callback \""
252                         << callbackname << "\" is not a function" << std::endl;
253         }
254         lua_pop(L, 1);
255         return false;
256 }
257
258 void ScriptApiItem::pushPointedThing(const PointedThing& pointed)
259 {
260         lua_State* L = getStack();
261
262         push_pointed_thing(L, pointed);
263 }
264