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