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