You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

547 lines
15 KiB

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