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 "lua_api/l_inventory.h"
21 #include "lua_api/l_internal.h"
22 #include "lua_api/l_item.h"
23 #include "common/c_converter.h"
24 #include "common/c_content.h"
25 #include "server.h"
26 #include "server/serverinventorymgr.h"
27 #include "remoteplayer.h"
28
29 /*
30 InvRef
31 */
checkobject(lua_State * L,int narg)32 InvRef* InvRef::checkobject(lua_State *L, int narg)
33 {
34 luaL_checktype(L, narg, LUA_TUSERDATA);
35 void *ud = luaL_checkudata(L, narg, className);
36 if(!ud) luaL_typerror(L, narg, className);
37 return *(InvRef**)ud; // unbox pointer
38 }
39
getinv(lua_State * L,InvRef * ref)40 Inventory* InvRef::getinv(lua_State *L, InvRef *ref)
41 {
42 return getServerInventoryMgr(L)->getInventory(ref->m_loc);
43 }
44
getlist(lua_State * L,InvRef * ref,const char * listname)45 InventoryList* InvRef::getlist(lua_State *L, InvRef *ref,
46 const char *listname)
47 {
48 NO_MAP_LOCK_REQUIRED;
49 Inventory *inv = getinv(L, ref);
50 if(!inv)
51 return NULL;
52 return inv->getList(listname);
53 }
54
reportInventoryChange(lua_State * L,InvRef * ref)55 void InvRef::reportInventoryChange(lua_State *L, InvRef *ref)
56 {
57 // Inform other things that the inventory has changed
58 getServerInventoryMgr(L)->setInventoryModified(ref->m_loc);
59 }
60
61 // Exported functions
62
63 // garbage collector
gc_object(lua_State * L)64 int InvRef::gc_object(lua_State *L) {
65 InvRef *o = *(InvRef **)(lua_touserdata(L, 1));
66 delete o;
67 return 0;
68 }
69
70 // is_empty(self, listname) -> true/false
l_is_empty(lua_State * L)71 int InvRef::l_is_empty(lua_State *L)
72 {
73 NO_MAP_LOCK_REQUIRED;
74 InvRef *ref = checkobject(L, 1);
75 const char *listname = luaL_checkstring(L, 2);
76 InventoryList *list = getlist(L, ref, listname);
77 if(list && list->getUsedSlots() > 0){
78 lua_pushboolean(L, false);
79 } else {
80 lua_pushboolean(L, true);
81 }
82 return 1;
83 }
84
85 // get_size(self, listname)
l_get_size(lua_State * L)86 int InvRef::l_get_size(lua_State *L)
87 {
88 NO_MAP_LOCK_REQUIRED;
89 InvRef *ref = checkobject(L, 1);
90 const char *listname = luaL_checkstring(L, 2);
91 InventoryList *list = getlist(L, ref, listname);
92 if(list){
93 lua_pushinteger(L, list->getSize());
94 } else {
95 lua_pushinteger(L, 0);
96 }
97 return 1;
98 }
99
100 // get_width(self, listname)
l_get_width(lua_State * L)101 int InvRef::l_get_width(lua_State *L)
102 {
103 NO_MAP_LOCK_REQUIRED;
104 InvRef *ref = checkobject(L, 1);
105 const char *listname = luaL_checkstring(L, 2);
106 InventoryList *list = getlist(L, ref, listname);
107 if(list){
108 lua_pushinteger(L, list->getWidth());
109 } else {
110 lua_pushinteger(L, 0);
111 }
112 return 1;
113 }
114
115 // set_size(self, listname, size)
l_set_size(lua_State * L)116 int InvRef::l_set_size(lua_State *L)
117 {
118 NO_MAP_LOCK_REQUIRED;
119 InvRef *ref = checkobject(L, 1);
120 const char *listname = luaL_checkstring(L, 2);
121
122 int newsize = luaL_checknumber(L, 3);
123 if (newsize < 0) {
124 lua_pushboolean(L, false);
125 return 1;
126 }
127
128 Inventory *inv = getinv(L, ref);
129 if(inv == NULL){
130 lua_pushboolean(L, false);
131 return 1;
132 }
133 if(newsize == 0){
134 inv->deleteList(listname);
135 reportInventoryChange(L, ref);
136 lua_pushboolean(L, true);
137 return 1;
138 }
139 InventoryList *list = inv->getList(listname);
140 if(list){
141 list->setSize(newsize);
142 } else {
143 list = inv->addList(listname, newsize);
144 if (!list)
145 {
146 lua_pushboolean(L, false);
147 return 1;
148 }
149 }
150 reportInventoryChange(L, ref);
151 lua_pushboolean(L, true);
152 return 1;
153 }
154
155 // set_width(self, listname, size)
l_set_width(lua_State * L)156 int InvRef::l_set_width(lua_State *L)
157 {
158 NO_MAP_LOCK_REQUIRED;
159 InvRef *ref = checkobject(L, 1);
160 const char *listname = luaL_checkstring(L, 2);
161 int newwidth = luaL_checknumber(L, 3);
162 Inventory *inv = getinv(L, ref);
163 if(inv == NULL){
164 return 0;
165 }
166 InventoryList *list = inv->getList(listname);
167 if(list){
168 list->setWidth(newwidth);
169 } else {
170 return 0;
171 }
172 reportInventoryChange(L, ref);
173 return 0;
174 }
175
176 // get_stack(self, listname, i) -> itemstack
l_get_stack(lua_State * L)177 int InvRef::l_get_stack(lua_State *L)
178 {
179 NO_MAP_LOCK_REQUIRED;
180 InvRef *ref = checkobject(L, 1);
181 const char *listname = luaL_checkstring(L, 2);
182 int i = luaL_checknumber(L, 3) - 1;
183 InventoryList *list = getlist(L, ref, listname);
184 ItemStack item;
185 if(list != NULL && i >= 0 && i < (int) list->getSize())
186 item = list->getItem(i);
187 LuaItemStack::create(L, item);
188 return 1;
189 }
190
191 // set_stack(self, listname, i, stack) -> true/false
l_set_stack(lua_State * L)192 int InvRef::l_set_stack(lua_State *L)
193 {
194 NO_MAP_LOCK_REQUIRED;
195 InvRef *ref = checkobject(L, 1);
196 const char *listname = luaL_checkstring(L, 2);
197 int i = luaL_checknumber(L, 3) - 1;
198 ItemStack newitem = read_item(L, 4, getServer(L)->idef());
199 InventoryList *list = getlist(L, ref, listname);
200 if(list != NULL && i >= 0 && i < (int) list->getSize()){
201 list->changeItem(i, newitem);
202 reportInventoryChange(L, ref);
203 lua_pushboolean(L, true);
204 } else {
205 lua_pushboolean(L, false);
206 }
207 return 1;
208 }
209
210 // get_list(self, listname) -> list or nil
l_get_list(lua_State * L)211 int InvRef::l_get_list(lua_State *L)
212 {
213 NO_MAP_LOCK_REQUIRED;
214 InvRef *ref = checkobject(L, 1);
215 const char *listname = luaL_checkstring(L, 2);
216 Inventory *inv = getinv(L, ref);
217 if(inv){
218 push_inventory_list(L, inv, listname);
219 } else {
220 lua_pushnil(L);
221 }
222 return 1;
223 }
224
225 // set_list(self, listname, list)
l_set_list(lua_State * L)226 int InvRef::l_set_list(lua_State *L)
227 {
228 NO_MAP_LOCK_REQUIRED;
229 InvRef *ref = checkobject(L, 1);
230 const char *listname = luaL_checkstring(L, 2);
231 Inventory *inv = getinv(L, ref);
232 if(inv == NULL){
233 return 0;
234 }
235 InventoryList *list = inv->getList(listname);
236 if(list)
237 read_inventory_list(L, 3, inv, listname,
238 getServer(L), list->getSize());
239 else
240 read_inventory_list(L, 3, inv, listname, getServer(L));
241 reportInventoryChange(L, ref);
242 return 0;
243 }
244
245 // get_lists(self) -> list of InventoryLists
l_get_lists(lua_State * L)246 int InvRef::l_get_lists(lua_State *L)
247 {
248 NO_MAP_LOCK_REQUIRED;
249 InvRef *ref = checkobject(L, 1);
250 Inventory *inv = getinv(L, ref);
251 if (!inv) {
252 return 0;
253 }
254 std::vector<const InventoryList*> lists = inv->getLists();
255 std::vector<const InventoryList*>::iterator iter = lists.begin();
256 lua_createtable(L, 0, lists.size());
257 for (; iter != lists.end(); iter++) {
258 const char* name = (*iter)->getName().c_str();
259 lua_pushstring(L, name);
260 push_inventory_list(L, inv, name);
261 lua_rawset(L, -3);
262 }
263 return 1;
264 }
265
266 // set_lists(self, lists)
l_set_lists(lua_State * L)267 int InvRef::l_set_lists(lua_State *L)
268 {
269 NO_MAP_LOCK_REQUIRED;
270 InvRef *ref = checkobject(L, 1);
271 Inventory *inv = getinv(L, ref);
272 if (!inv) {
273 return 0;
274 }
275
276 // Make a temporary inventory in case reading fails
277 Inventory *tempInv(inv);
278 tempInv->clear();
279
280 Server *server = getServer(L);
281
282 lua_pushnil(L);
283 luaL_checktype(L, 2, LUA_TTABLE);
284 while (lua_next(L, 2)) {
285 const char *listname = lua_tostring(L, -2);
286 read_inventory_list(L, -1, tempInv, listname, server);
287 lua_pop(L, 1);
288 }
289 inv = tempInv;
290 return 0;
291 }
292
293 // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
294 // Returns the leftover stack
l_add_item(lua_State * L)295 int InvRef::l_add_item(lua_State *L)
296 {
297 NO_MAP_LOCK_REQUIRED;
298 InvRef *ref = checkobject(L, 1);
299 const char *listname = luaL_checkstring(L, 2);
300 ItemStack item = read_item(L, 3, getServer(L)->idef());
301 InventoryList *list = getlist(L, ref, listname);
302 if(list){
303 ItemStack leftover = list->addItem(item);
304 if(leftover.count != item.count)
305 reportInventoryChange(L, ref);
306 LuaItemStack::create(L, leftover);
307 } else {
308 LuaItemStack::create(L, item);
309 }
310 return 1;
311 }
312
313 // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false
314 // Returns true if the item completely fits into the list
l_room_for_item(lua_State * L)315 int InvRef::l_room_for_item(lua_State *L)
316 {
317 NO_MAP_LOCK_REQUIRED;
318 InvRef *ref = checkobject(L, 1);
319 const char *listname = luaL_checkstring(L, 2);
320 ItemStack item = read_item(L, 3, getServer(L)->idef());
321 InventoryList *list = getlist(L, ref, listname);
322 if(list){
323 lua_pushboolean(L, list->roomForItem(item));
324 } else {
325 lua_pushboolean(L, false);
326 }
327 return 1;
328 }
329
330 // contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
331 // Returns true if the list contains the given count of the given item
l_contains_item(lua_State * L)332 int InvRef::l_contains_item(lua_State *L)
333 {
334 NO_MAP_LOCK_REQUIRED;
335 InvRef *ref = checkobject(L, 1);
336 const char *listname = luaL_checkstring(L, 2);
337 ItemStack item = read_item(L, 3, getServer(L)->idef());
338 InventoryList *list = getlist(L, ref, listname);
339 bool match_meta = false;
340 if (lua_isboolean(L, 4))
341 match_meta = readParam<bool>(L, 4);
342 if (list) {
343 lua_pushboolean(L, list->containsItem(item, match_meta));
344 } else {
345 lua_pushboolean(L, false);
346 }
347 return 1;
348 }
349
350 // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack
351 // Returns the items that were actually removed
l_remove_item(lua_State * L)352 int InvRef::l_remove_item(lua_State *L)
353 {
354 NO_MAP_LOCK_REQUIRED;
355 InvRef *ref = checkobject(L, 1);
356 const char *listname = luaL_checkstring(L, 2);
357 ItemStack item = read_item(L, 3, getServer(L)->idef());
358 InventoryList *list = getlist(L, ref, listname);
359 if(list){
360 ItemStack removed = list->removeItem(item);
361 if(!removed.empty())
362 reportInventoryChange(L, ref);
363 LuaItemStack::create(L, removed);
364 } else {
365 LuaItemStack::create(L, ItemStack());
366 }
367 return 1;
368 }
369
370 // get_location() -> location (like get_inventory(location))
l_get_location(lua_State * L)371 int InvRef::l_get_location(lua_State *L)
372 {
373 NO_MAP_LOCK_REQUIRED;
374 InvRef *ref = checkobject(L, 1);
375 const InventoryLocation &loc = ref->m_loc;
376 switch(loc.type){
377 case InventoryLocation::PLAYER:
378 lua_newtable(L);
379 lua_pushstring(L, "player");
380 lua_setfield(L, -2, "type");
381 lua_pushstring(L, loc.name.c_str());
382 lua_setfield(L, -2, "name");
383 return 1;
384 case InventoryLocation::NODEMETA:
385 lua_newtable(L);
386 lua_pushstring(L, "node");
387 lua_setfield(L, -2, "type");
388 push_v3s16(L, loc.p);
389 lua_setfield(L, -2, "pos");
390 return 1;
391 case InventoryLocation::DETACHED:
392 lua_newtable(L);
393 lua_pushstring(L, "detached");
394 lua_setfield(L, -2, "type");
395 lua_pushstring(L, loc.name.c_str());
396 lua_setfield(L, -2, "name");
397 return 1;
398 case InventoryLocation::UNDEFINED:
399 case InventoryLocation::CURRENT_PLAYER:
400 break;
401 }
402 lua_newtable(L);
403 lua_pushstring(L, "undefined");
404 lua_setfield(L, -2, "type");
405 return 1;
406 }
407
408
InvRef(const InventoryLocation & loc)409 InvRef::InvRef(const InventoryLocation &loc):
410 m_loc(loc)
411 {
412 }
413
414 // Creates an InvRef and leaves it on top of stack
415 // Not callable from Lua; all references are created on the C side.
create(lua_State * L,const InventoryLocation & loc)416 void InvRef::create(lua_State *L, const InventoryLocation &loc)
417 {
418 NO_MAP_LOCK_REQUIRED;
419 InvRef *o = new InvRef(loc);
420 *(void **)(lua_newuserdata(L, sizeof(void *))) = o;
421 luaL_getmetatable(L, className);
422 lua_setmetatable(L, -2);
423 }
createPlayer(lua_State * L,RemotePlayer * player)424 void InvRef::createPlayer(lua_State *L, RemotePlayer *player)
425 {
426 NO_MAP_LOCK_REQUIRED;
427 InventoryLocation loc;
428 loc.setPlayer(player->getName());
429 create(L, loc);
430 }
createNodeMeta(lua_State * L,v3s16 p)431 void InvRef::createNodeMeta(lua_State *L, v3s16 p)
432 {
433 InventoryLocation loc;
434 loc.setNodeMeta(p);
435 create(L, loc);
436 }
437
Register(lua_State * L)438 void InvRef::Register(lua_State *L)
439 {
440 lua_newtable(L);
441 int methodtable = lua_gettop(L);
442 luaL_newmetatable(L, className);
443 int metatable = lua_gettop(L);
444
445 lua_pushliteral(L, "__metatable");
446 lua_pushvalue(L, methodtable);
447 lua_settable(L, metatable); // hide metatable from Lua getmetatable()
448
449 lua_pushliteral(L, "__index");
450 lua_pushvalue(L, methodtable);
451 lua_settable(L, metatable);
452
453 lua_pushliteral(L, "__gc");
454 lua_pushcfunction(L, gc_object);
455 lua_settable(L, metatable);
456
457 lua_pop(L, 1); // drop metatable
458
459 luaL_openlib(L, 0, methods, 0); // fill methodtable
460 lua_pop(L, 1); // drop methodtable
461
462 // Cannot be created from Lua
463 //lua_register(L, className, create_object);
464 }
465
466 const char InvRef::className[] = "InvRef";
467 const luaL_Reg InvRef::methods[] = {
468 luamethod(InvRef, is_empty),
469 luamethod(InvRef, get_size),
470 luamethod(InvRef, set_size),
471 luamethod(InvRef, get_width),
472 luamethod(InvRef, set_width),
473 luamethod(InvRef, get_stack),
474 luamethod(InvRef, set_stack),
475 luamethod(InvRef, get_list),
476 luamethod(InvRef, set_list),
477 luamethod(InvRef, get_lists),
478 luamethod(InvRef, set_lists),
479 luamethod(InvRef, add_item),
480 luamethod(InvRef, room_for_item),
481 luamethod(InvRef, contains_item),
482 luamethod(InvRef, remove_item),
483 luamethod(InvRef, get_location),
484 {0,0}
485 };
486
487 // get_inventory(location)
l_get_inventory(lua_State * L)488 int ModApiInventory::l_get_inventory(lua_State *L)
489 {
490 InventoryLocation loc;
491
492 lua_getfield(L, 1, "type");
493 std::string type = luaL_checkstring(L, -1);
494 lua_pop(L, 1);
495
496 if(type == "node"){
497 MAP_LOCK_REQUIRED;
498 lua_getfield(L, 1, "pos");
499 v3s16 pos = check_v3s16(L, -1);
500 loc.setNodeMeta(pos);
501
502 if (getServerInventoryMgr(L)->getInventory(loc) != NULL)
503 InvRef::create(L, loc);
504 else
505 lua_pushnil(L);
506 return 1;
507 }
508
509 NO_MAP_LOCK_REQUIRED;
510 if (type == "player") {
511 lua_getfield(L, 1, "name");
512 loc.setPlayer(luaL_checkstring(L, -1));
513 lua_pop(L, 1);
514 } else if (type == "detached") {
515 lua_getfield(L, 1, "name");
516 loc.setDetached(luaL_checkstring(L, -1));
517 lua_pop(L, 1);
518 }
519
520 if (getServerInventoryMgr(L)->getInventory(loc) != NULL)
521 InvRef::create(L, loc);
522 else
523 lua_pushnil(L);
524 return 1;
525 // END NO_MAP_LOCK_REQUIRED;
526
527 }
528
529 // create_detached_inventory_raw(name, [player_name])
l_create_detached_inventory_raw(lua_State * L)530 int ModApiInventory::l_create_detached_inventory_raw(lua_State *L)
531 {
532 NO_MAP_LOCK_REQUIRED;
533 const char *name = luaL_checkstring(L, 1);
534 std::string player = readParam<std::string>(L, 2, "");
535 if (getServerInventoryMgr(L)->createDetachedInventory(name, getServer(L)->idef(), player) != NULL) {
536 InventoryLocation loc;
537 loc.setDetached(name);
538 InvRef::create(L, loc);
539 } else {
540 lua_pushnil(L);
541 }
542 return 1;
543 }
544
545 // remove_detached_inventory_raw(name)
l_remove_detached_inventory_raw(lua_State * L)546 int ModApiInventory::l_remove_detached_inventory_raw(lua_State *L)
547 {
548 NO_MAP_LOCK_REQUIRED;
549 const std::string &name = luaL_checkstring(L, 1);
550 lua_pushboolean(L, getServerInventoryMgr(L)->removeDetachedInventory(name));
551 return 1;
552 }
553
Initialize(lua_State * L,int top)554 void ModApiInventory::Initialize(lua_State *L, int top)
555 {
556 API_FCT(create_detached_inventory_raw);
557 API_FCT(remove_detached_inventory_raw);
558 API_FCT(get_inventory);
559 }
560