diff options
-rw-r--r-- | builtin/builtin.lua | 1 | ||||
-rw-r--r-- | builtin/deprecated.lua | 3 | ||||
-rw-r--r-- | builtin/detached_inventory.lua | 19 | ||||
-rw-r--r-- | builtin/item.lua | 35 | ||||
-rw-r--r-- | doc/lua_api.txt | 72 | ||||
-rw-r--r-- | games/minimal/mods/default/init.lua | 63 | ||||
-rw-r--r-- | games/minimal/mods/experimental/init.lua | 26 | ||||
-rw-r--r-- | src/guiFormSpecMenu.cpp | 3 | ||||
-rw-r--r-- | src/inventorymanager.cpp | 365 | ||||
-rw-r--r-- | src/scriptapi.cpp | 410 | ||||
-rw-r--r-- | src/scriptapi.h | 61 |
11 files changed, 819 insertions, 239 deletions
diff --git a/builtin/builtin.lua b/builtin/builtin.lua index bd5adf9e7..a17841fc8 100644 --- a/builtin/builtin.lua +++ b/builtin/builtin.lua @@ -21,4 +21,5 @@ dofile(minetest.get_modpath("__builtin").."/privileges.lua") dofile(minetest.get_modpath("__builtin").."/auth.lua") dofile(minetest.get_modpath("__builtin").."/chatcommands.lua") dofile(minetest.get_modpath("__builtin").."/static_spawn.lua") +dofile(minetest.get_modpath("__builtin").."/detached_inventory.lua") diff --git a/builtin/deprecated.lua b/builtin/deprecated.lua index 67210d525..d41e2c44c 100644 --- a/builtin/deprecated.lua +++ b/builtin/deprecated.lua @@ -16,4 +16,7 @@ minetest.digprop_woodlike = digprop_err minetest.digprop_leaveslike = digprop_err minetest.digprop_glasslike = digprop_err +minetest.node_metadata_inventory_move_allow_all = function() + minetest.log("info", "WARNING: minetest.node_metadata_inventory_move_allow_all is obsolete and does nothing.") +end diff --git a/builtin/detached_inventory.lua b/builtin/detached_inventory.lua new file mode 100644 index 000000000..3757f1387 --- /dev/null +++ b/builtin/detached_inventory.lua @@ -0,0 +1,19 @@ +-- Minetest: builtin/detached_inventory.lua + +minetest.detached_inventories = {} + +function minetest.create_detached_inventory(name, callbacks) + local stuff = {} + stuff.name = name + if callbacks then + stuff.allow_move = callbacks.allow_move + stuff.allow_put = callbacks.allow_put + stuff.allow_take = callbacks.allow_take + stuff.on_move = callbacks.on_move + stuff.on_put = callbacks.on_put + stuff.on_take = callbacks.on_take + end + minetest.detached_inventories[name] = stuff + return minetest.create_detached_inventory_raw(name) +end + diff --git a/builtin/item.lua b/builtin/item.lua index bd34efe79..2a9b4ff27 100644 --- a/builtin/item.lua +++ b/builtin/item.lua @@ -318,41 +318,6 @@ function minetest.node_dig(pos, node, digger) end end -function minetest.node_metadata_inventory_move_allow_all(pos, from_list, - from_index, to_list, to_index, count, player) - minetest.log("verbose", "node_metadata_inventory_move_allow_all") - local meta = minetest.env:get_meta(pos) - local inv = meta:get_inventory() - - local from_stack = inv:get_stack(from_list, from_index) - local taken_items = from_stack:take_item(count) - inv:set_stack(from_list, from_index, from_stack) - - local to_stack = inv:get_stack(to_list, to_index) - to_stack:add_item(taken_items) - inv:set_stack(to_list, to_index, to_stack) -end - -function minetest.node_metadata_inventory_offer_allow_all(pos, listname, index, stack, player) - minetest.log("verbose", "node_metadata_inventory_offer_allow_all") - local meta = minetest.env:get_meta(pos) - local inv = meta:get_inventory() - local the_stack = inv:get_stack(listname, index) - the_stack:add_item(stack) - inv:set_stack(listname, index, the_stack) - return ItemStack("") -end - -function minetest.node_metadata_inventory_take_allow_all(pos, listname, index, count, player) - minetest.log("verbose", "node_metadata_inventory_take_allow_all") - local meta = minetest.env:get_meta(pos) - local inv = meta:get_inventory() - local the_stack = inv:get_stack(listname, index) - local taken_items = the_stack:take_item(count) - inv:set_stack(listname, index, the_stack) - return taken_items -end - -- This is used to allow mods to redefine minetest.item_place and so on -- NOTE: This is not the preferred way. Preferred way is to provide enough -- callbacks to not require redefining global functions. -celeron55 diff --git a/doc/lua_api.txt b/doc/lua_api.txt index c9fafd65f..5fb2c34e3 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -851,7 +851,8 @@ minetest.get_inventory(location) -> InvRef ^ location = eg. {type="player", name="celeron55"} {type="node", pos={x=, y=, z=}} {type="detached", name="creative"} -minetest.create_detached_inventory(name) -> InvRef +minetest.create_detached_inventory(name, callbacks) -> InvRef +^ callbacks: See "Detached inventory callbacks" ^ Creates a detached inventory. If it already exists, it is cleared. Item handling: @@ -1361,35 +1362,25 @@ Node definition (register_node) ^ Called when an UI form (eg. sign text input) returns data ^ default: nil - on_metadata_inventory_move = func(pos, from_list, from_index, - to_list, to_index, count, player), - ^ Called when a player wants to move items inside the metadata - ^ Should move items, or some items, if permitted. If not, should do - nothing. - ^ The engine ensures the action is valid, i.e. the stack fits at the - given position - ^ default: minetest.node_metadata_inventory_move_allow_all - - on_metadata_inventory_offer = func(pos, listname, index, stack, player), - ^ Called when a player wants to put something into the metadata - inventory - ^ Should check if the action is permitted (the engine ensures the - action is valid, i.e. the stack fits at the given position) - ^ If permitted, modify the metadata inventory and return the - "leftover" stack (normally nil). - ^ If not permitted, return itemstack. - ^ default: minetest.node_metadata_inventory_offer_allow_all - - on_metadata_inventory_take = func(pos, listname, index, count, player), - ^ Called when a player wants to take something out of the metadata - inventory - ^ Should check if the action is permitted (the engine ensures the - action is valid, i.e. there's a stack of at least “count” items at - that position) - ^ If permitted, modify the metadata inventory and return the - stack of items - ^ If not permitted, return nil. - ^ default: minetest.node_metadata_inventory_take_allow_all + allow_metadata_inventory_move = func(pos, from_list, from_index, + to_list, to_index, count, player), + ^ Called when a player wants to move items inside the inventory + ^ Return value: number of items allowed to move + + allow_metadata_inventory_put = func(pos, listname, index, stack, player), + ^ Called when a player wants to put something into the inventory + ^ Return value: number of items allowed to put + + allow_metadata_inventory_take = func(pos, listname, index, count, player), + ^ Called when a player wants to take something out of the inventory + ^ Return value: number of items allowed to take + + on_metadata_inventory_move = func(pos, from_list, from_index, + to_list, to_index, count, player), + on_metadata_inventory_put = func(pos, listname, index, stack, player), + on_metadata_inventory_take = func(pos, listname, index, count, player), + ^ Called after the actual action has happened, according to what was allowed. + ^ No return value } Recipe for register_craft: (shaped) @@ -1446,3 +1437,24 @@ Chatcommand definition (register_chatcommand) func = function(name, param), -- called when command is run } +Detached inventory callbacks +{ + allow_move = func(inv, from_list, from_index, to_list, to_index, count, player), + ^ Called when a player wants to move items inside the inventory + ^ Return value: number of items allowed to move + + allow_put = func(inv, listname, index, stack, player), + ^ Called when a player wants to put something into the inventory + ^ Return value: number of items allowed to put + + allow_take = func(inv, listname, index, count, player), + ^ Called when a player wants to take something out of the inventory + ^ Return value: number of items allowed to take + + on_move = func(inv, from_list, from_index, to_list, to_index, count, player), + on_put = func(inv, listname, index, stack, player), + on_take = func(inv, listname, index, count, player), + ^ Called after the actual action has happened, according to what was allowed. + ^ No return value +} + diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index 428dfd9f4..fe7ab955e 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1150,7 +1150,7 @@ minetest.register_node("default:chest", { on_construct = function(pos) local meta = minetest.env:get_meta(pos) meta:set_string("formspec", - "invsize[8,9;]".. + "size[8,9]".. "list[current_name;main;0,0;8,4;]".. "list[current_player;main;0,5;8,4;]") meta:set_string("infotext", "Chest") @@ -1162,25 +1162,6 @@ minetest.register_node("default:chest", { local inv = meta:get_inventory() return inv:is_empty("main") end, - on_metadata_inventory_move = function(pos, from_list, from_index, - to_list, to_index, count, player) - minetest.log("action", player:get_player_name().. - " moves stuff in chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_move_allow_all( - pos, from_list, from_index, to_list, to_index, count, player) - end, - on_metadata_inventory_offer = function(pos, listname, index, stack, player) - minetest.log("action", player:get_player_name().. - " moves stuff to chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_offer_allow_all( - pos, listname, index, stack, player) - end, - on_metadata_inventory_take = function(pos, listname, index, count, player) - minetest.log("action", player:get_player_name().. - " takes stuff from chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_take_allow_all( - pos, listname, index, count, player) - end, }) local function has_locked_chest_privilege(meta, player) @@ -1207,7 +1188,7 @@ minetest.register_node("default:chest_locked", { on_construct = function(pos) local meta = minetest.env:get_meta(pos) meta:set_string("formspec", - "invsize[8,9;]".. + "size[8,9]".. "list[current_name;main;0,0;8,4;]".. "list[current_player;main;0,5;8,4;]") meta:set_string("infotext", "Locked Chest") @@ -1220,53 +1201,55 @@ minetest.register_node("default:chest_locked", { local inv = meta:get_inventory() return inv:is_empty("main") end, - on_metadata_inventory_move = function(pos, from_list, from_index, - to_list, to_index, count, player) + allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) local meta = minetest.env:get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. meta:get_string("owner").." at ".. minetest.pos_to_string(pos)) - return + return 0 end - minetest.log("action", player:get_player_name().. - " moves stuff in locked chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_move_allow_all( - pos, from_list, from_index, to_list, to_index, count, player) + return count end, - on_metadata_inventory_offer = function(pos, listname, index, stack, player) + allow_metadata_inventory_put = function(pos, listname, index, stack, player) local meta = minetest.env:get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. meta:get_string("owner").." at ".. minetest.pos_to_string(pos)) - return stack + return 0 end - minetest.log("action", player:get_player_name().. - " moves stuff to locked chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_offer_allow_all( - pos, listname, index, stack, player) + return stack:get_count() end, - on_metadata_inventory_take = function(pos, listname, index, count, player) + allow_metadata_inventory_take = function(pos, listname, index, count, player) local meta = minetest.env:get_meta(pos) if not has_locked_chest_privilege(meta, player) then minetest.log("action", player:get_player_name().. " tried to access a locked chest belonging to ".. meta:get_string("owner").." at ".. minetest.pos_to_string(pos)) - return + return 0 end + return count + end, + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + minetest.log("action", player:get_player_name().. + " moves stuff in locked chest at "..minetest.pos_to_string(pos)) + end, + on_metadata_inventory_put = function(pos, listname, index, stack, player) + minetest.log("action", player:get_player_name().. + " moves stuff to locked chest at "..minetest.pos_to_string(pos)) + end, + on_metadata_inventory_take = function(pos, listname, index, count, player) minetest.log("action", player:get_player_name().. " takes stuff from locked chest at "..minetest.pos_to_string(pos)) - return minetest.node_metadata_inventory_take_allow_all( - pos, listname, index, count, player) end, }) default.furnace_inactive_formspec = - "invsize[8,9;]".. + "size[8,9]".. "image[2,2;1,1;default_furnace_fire_bg.png]".. "list[current_name;fuel;2,3;1,1;]".. "list[current_name;src;2,1;1,1;]".. @@ -1405,7 +1388,7 @@ minetest.register_abm({ meta:set_string("infotext","Furnace active: "..percent.."%") hacky_swap_node(pos,"default:furnace_active") meta:set_string("formspec", - "invsize[8,9;]".. + "size[8,9]".. "image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:".. (100-percent)..":default_furnace_fire_fg.png]".. "list[current_name;fuel;2,3;1,1;]".. diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua index 498c64623..0b45aeb62 100644 --- a/games/minimal/mods/experimental/init.lua +++ b/games/minimal/mods/experimental/init.lua @@ -516,7 +516,7 @@ minetest.register_craft({ --[[minetest.register_on_joinplayer(function(player) minetest.after(3, function() - player:set_inventory_formspec("invsize[8,7.5;]".. + player:set_inventory_formspec("size[8,7.5]".. "image[1,0.6;1,2;player.png]".. "list[current_player;main;0,3.5;8,4;]".. "list[current_player;craft;3,0;3,3;]".. @@ -525,7 +525,29 @@ minetest.register_craft({ end)]] -- Create a detached inventory -local inv = minetest.create_detached_inventory("test_inventory") +local inv = minetest.create_detached_inventory("test_inventory", { + allow_move = function(inv, from_list, from_index, to_list, to_index, count, player) + experimental.print_to_everything("allow move asked") + return count -- Allow all + end, + allow_put = function(inv, listname, index, stack, player) + experimental.print_to_everything("allow put asked") + return 1 -- Allow only 1 + end, + allow_take = function(inv, listname, index, count, player) + experimental.print_to_everything("allow take asked") + return 4 -- Allow 4 at max + end, + on_move = function(inv, from_list, from_index, to_list, to_index, count, player) + experimental.print_to_everything(player:get_player_name().." moved items") + end, + on_put = function(inv, listname, index, stack, player) + experimental.print_to_everything(player:get_player_name().." put items") + end, + on_take = function(inv, listname, index, count, player) + experimental.print_to_everything(player:get_player_name().." took items") + end, +}) inv:set_size("main", 4*6) inv:add_item("main", "experimental:tester_tool_1") inv:add_item("main", "experimental:tnt 5") diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 3eb056625..7ded2b37e 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -215,10 +215,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } else{ invsize.Y = stof(f.next(";")); - errorstream<<"WARNING: invsize is deprecated, use size"<<std::endl; f.next("]"); } - infostream<<"size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl; + infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl; padding = v2s32(screensize.Y/40, screensize.Y/40); spacing = v2s32(screensize.Y/12, screensize.Y/13); diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 1369cb0d7..5412a5dca 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -41,25 +41,25 @@ std::string InventoryLocation::dump() const void InventoryLocation::serialize(std::ostream &os) const { - switch(type){ - case InventoryLocation::UNDEFINED: - os<<"undefined"; - break; - case InventoryLocation::CURRENT_PLAYER: - os<<"current_player"; - break; - case InventoryLocation::PLAYER: - os<<"player:"<<name; - break; - case InventoryLocation::NODEMETA: - os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z; - break; - case InventoryLocation::DETACHED: - os<<"detached:"<<name; - break; - default: - assert(0); - } + switch(type){ + case InventoryLocation::UNDEFINED: + os<<"undefined"; + break; + case InventoryLocation::CURRENT_PLAYER: + os<<"current_player"; + break; + case InventoryLocation::PLAYER: + os<<"player:"<<name; + break; + case InventoryLocation::NODEMETA: + os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z; + break; + case InventoryLocation::DETACHED: + os<<"detached:"<<name; + break; + default: + assert(0); + } } void InventoryLocation::deSerialize(std::istream &is) @@ -198,77 +198,96 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<", to_list=\""<<to_list<<"\""<<std::endl; return; } + + /* + Collect information of endpoints + */ + + int try_take_count = count; + if(try_take_count == 0) + try_take_count = list_from->getItem(from_i).count; + + int src_can_take_count = 0xffff; + int dst_can_put_count = 0xffff; - // Handle node metadata move - if(from_inv.type == InventoryLocation::NODEMETA && - to_inv.type == InventoryLocation::NODEMETA && - from_inv.p != to_inv.p) + /* Query detached inventories */ + + // Move occurs in the same detached inventory + if(from_inv.type == InventoryLocation::DETACHED && + to_inv.type == InventoryLocation::DETACHED && + from_inv.name == to_inv.name) { - errorstream<<"Directly moving items between two nodes is " - <<"disallowed."<<std::endl; - return; + lua_State *L = player->getEnv()->getLua(); + src_can_take_count = scriptapi_detached_inventory_allow_move( + L, from_inv.name, from_list, from_i, + to_list, to_i, try_take_count, player); + dst_can_put_count = src_can_take_count; + } + else + { + // Destination is detached + if(to_inv.type == InventoryLocation::DETACHED) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = try_take_count; + dst_can_put_count = scriptapi_detached_inventory_allow_put( + L, to_inv.name, to_list, to_i, src_item, player); + } + // Source is detached + if(from_inv.type == InventoryLocation::DETACHED) + { + lua_State *L = player->getEnv()->getLua(); + src_can_take_count = scriptapi_detached_inventory_allow_take( + L, from_inv.name, from_list, from_i, try_take_count, player); + } } - else if(from_inv.type == InventoryLocation::NODEMETA && + + /* Query node metadata inventories */ + + // Both endpoints are nodemeta + // Move occurs in the same nodemeta inventory + if(from_inv.type == InventoryLocation::NODEMETA && to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { lua_State *L = player->getEnv()->getLua(); - int count0 = count; - if(count0 == 0) - count0 = list_from->getItem(from_i).count; - infostream<<player->getDescription()<<" moving "<<count0 - <<" items inside node at "<<PP(from_inv.p)<<std::endl; - scriptapi_node_on_metadata_inventory_move(L, from_inv.p, - from_list, from_i, to_list, to_i, count0, player); - } - // Handle node metadata take - else if(from_inv.type == InventoryLocation::NODEMETA) - { - lua_State *L = player->getEnv()->getLua(); - int count0 = count; - if(count0 == 0) - count0 = list_from->getItem(from_i).count; - infostream<<player->getDescription()<<" taking "<<count0 - <<" items from node at "<<PP(from_inv.p)<<std::endl; - ItemStack return_stack = scriptapi_node_on_metadata_inventory_take( - L, from_inv.p, from_list, from_i, count0, player); - if(return_stack.count == 0) - infostream<<"Node metadata gave no items"<<std::endl; - return_stack = list_to->addItem(to_i, return_stack); - list_to->addItem(return_stack); // Force return of everything - } - // Handle node metadata offer - else if(to_inv.type == InventoryLocation::NODEMETA) - { - lua_State *L = player->getEnv()->getLua(); - int count0 = count; - if(count0 == 0) - count0 = list_from->getItem(from_i).count; - ItemStack offer_stack = list_from->takeItem(from_i, count0); - infostream<<player->getDescription()<<" offering " - <<offer_stack.count<<" items to node at " - <<PP(to_inv.p)<<std::endl; - ItemStack reject_stack = scriptapi_node_on_metadata_inventory_offer( - L, to_inv.p, to_list, to_i, offer_stack, player); - if(reject_stack.count == offer_stack.count) - infostream<<"Node metadata rejected all items"<<std::endl; - else if(reject_stack.count != 0) - infostream<<"Node metadata rejected some items"<<std::endl; - reject_stack = list_from->addItem(from_i, reject_stack); - list_from->addItem(reject_stack); // Force return of everything - } - // Handle regular move + src_can_take_count = scriptapi_nodemeta_inventory_allow_move( + L, from_inv.p, from_list, from_i, + to_list, to_i, try_take_count, player); + dst_can_put_count = src_can_take_count; + } else { - /* - This performs the actual movement - - If something is wrong (source item is empty, destination is the - same as source), nothing happens - */ - list_from->moveItem(from_i, list_to, to_i, count); - - infostream<<"IMoveAction::apply(): moved " + // Destination is nodemeta + if(to_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = try_take_count; + dst_can_put_count = scriptapi_nodemeta_inventory_allow_put( + L, to_inv.p, to_list, to_i, src_item, player); + } + // Source is nodemeta + if(from_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + src_can_take_count = scriptapi_nodemeta_inventory_allow_take( + L, from_inv.p, from_list, from_i, try_take_count, player); + } + } + + /* Modify count according to collected data */ + int new_count = try_take_count; + if(new_count > src_can_take_count) + new_count = src_can_take_count; + if(new_count > dst_can_put_count) + new_count = dst_can_put_count; + + /* If no items will be moved, don't go further */ + if(new_count == 0) + { + infostream<<"IMoveAction::apply(): move was completely disallowed: " <<" count="<<count <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" @@ -277,6 +296,98 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<" list=\""<<to_list<<"\"" <<" i="<<to_i <<std::endl; + return; + } + + count = new_count; + + /* + Perform actual move + + If something is wrong (source item is empty, destination is the + same as source), nothing happens + */ + list_from->moveItem(from_i, list_to, to_i, count); + + infostream<<"IMoveAction::apply(): moved " + <<" count="<<count + <<" from inv=\""<<from_inv.dump()<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<" to inv=\""<<to_inv.dump()<<"\"" + <<" list=\""<<to_list<<"\"" + <<" i="<<to_i + <<std::endl; + + /* + Report move to endpoints + */ + + /* Detached inventories */ + + // Both endpoints are same detached + if(from_inv.type == InventoryLocation::DETACHED && + to_inv.type == InventoryLocation::DETACHED && + from_inv.name == to_inv.name) + { + lua_State *L = player->getEnv()->getLua(); + scriptapi_detached_inventory_on_move( + L, from_inv.name, from_list, from_i, + to_list, to_i, count, player); + } + else + { + // Destination is detached + if(to_inv.type == InventoryLocation::DETACHED) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = count; + scriptapi_detached_inventory_on_put( + L, to_inv.name, to_list, to_i, src_item, player); + } + // Source is detached + if(from_inv.type == InventoryLocation::DETACHED) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = count; + scriptapi_detached_inventory_on_take( + L, from_inv.name, from_list, from_i, src_item.count, player); + } + } + + /* Node metadata inventories */ + + // Both endpoints are same nodemeta + if(from_inv.type == InventoryLocation::NODEMETA && + to_inv.type == InventoryLocation::NODEMETA && + from_inv.p == to_inv.p) + { + lua_State *L = player->getEnv()->getLua(); + scriptapi_nodemeta_inventory_on_move( + L, from_inv.p, from_list, from_i, + to_list, to_i, count, player); + } + else{ + // Destination is nodemeta + if(to_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = count; + scriptapi_nodemeta_inventory_on_put( + L, to_inv.p, to_list, to_i, src_item, player); + } + // Source is nodemeta + else if(from_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + ItemStack src_item = list_from->getItem(from_i); + src_item.count = count; + scriptapi_nodemeta_inventory_on_take( + L, from_inv.p, from_list, from_i, src_item.count, player); + } } mgr->setInventoryModified(from_inv); @@ -361,47 +472,61 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame return; } - ItemStack item1; + /* + Collect information of endpoints + */ + + int take_count = list_from->getItem(from_i).count; + if(count != 0 && count < take_count) + take_count = count; + int src_can_take_count = take_count; - // Handle node metadata take - if(from_inv.type == InventoryLocation::NODEMETA) + // Source is detached + if(from_inv.type == InventoryLocation::DETACHED) { lua_State *L = player->getEnv()->getLua(); - int count0 = count; - if(count0 == 0) - count0 = list_from->getItem(from_i).count; - infostream<<player->getDescription()<<" dropping "<<count0 - <<" items from node at "<<PP(from_inv.p)<<std::endl; - ItemStack return_stack = scriptapi_node_on_metadata_inventory_take( - L, from_inv.p, from_list, from_i, count0, player); - if(return_stack.count == 0) - infostream<<"Node metadata gave no items"<<std::endl; - item1 = return_stack; + src_can_take_count = scriptapi_detached_inventory_allow_take( + L, from_inv.name, from_list, from_i, take_count, player); } - else + + // Source is nodemeta + if(from_inv.type == InventoryLocation::NODEMETA) { - // Take item from source list - if(count == 0) - item1 = list_from->changeItem(from_i, ItemStack()); - else - item1 = list_from->takeItem(from_i, count); + lua_State *L = player->getEnv()->getLua(); + src_can_take_count = scriptapi_nodemeta_inventory_allow_take( + L, from_inv.p, from_list, from_i, take_count, player); } - // Drop the item and apply the returned ItemStack - ItemStack item2 = item1; - if(scriptapi_item_on_drop(player->getEnv()->getLua(), item2, player, + if(src_can_take_count < take_count) + take_count = src_can_take_count; + + int actually_dropped_count = 0; + + // Drop the item + ItemStack item1 = list_from->getItem(from_i); + if(scriptapi_item_on_drop(player->getEnv()->getLua(), item1, player, player->getBasePosition() + v3f(0,1,0))) { - if(g_settings->getBool("creative_mode") == true - && from_inv.type == InventoryLocation::PLAYER) - item2 = item1; // creative mode + actually_dropped_count = take_count - item1.count; + + if(actually_dropped_count == 0){ + infostream<<"Actually dropped no items"<<std::endl; + return; + } - list_from->addItem(from_i, item2); + // Don't remove from inventory in creative mode + if(g_settings->getBool("creative_mode") == true + && from_inv.type == InventoryLocation::PLAYER){ + } + else{ + // Take item from source list + ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count); - // Unless we have put the same amount back as we took in the first place, - // set inventory modified flag - if(item2.count != item1.count) + if(item2.count != actually_dropped_count) + errorstream<<"Could not take dropped count of items"<<std::endl; + mgr->setInventoryModified(from_inv); + } } infostream<<"IDropAction::apply(): dropped " @@ -409,6 +534,26 @@ void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<std::endl; + + /* + Report drop to endpoints + */ + + // Source is detached + if(from_inv.type == InventoryLocation::DETACHED) + { + lua_State *L = player->getEnv()->getLua(); + scriptapi_detached_inventory_on_take( + L, from_inv.name, from_list, from_i, actually_dropped_count, player); + } + + // Source is nodemeta + if(from_inv.type == InventoryLocation::NODEMETA) + { + lua_State *L = player->getEnv()->getLua(); + scriptapi_nodemeta_inventory_on_take( + L, from_inv.p, from_list, from_i, actually_dropped_count, player); + } } void IDropAction::clientApply(InventoryManager *mgr, IGameDef *gamedef) diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 9959ddd74..d28f8c7c1 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -4579,8 +4579,8 @@ static int l_get_inventory(lua_State *L) return 1; } -// create_detached_inventory(name) -static int l_create_detached_inventory(lua_State *L) +// create_detached_inventory_raw(name) +static int l_create_detached_inventory_raw(lua_State *L) { const char *name = luaL_checkstring(L, 1); if(get_server(L)->createDetachedInventory(name) != NULL){ @@ -4866,7 +4866,7 @@ static const struct luaL_Reg minetest_f [] = { {"chat_send_player", l_chat_send_player}, {"get_player_privs", l_get_player_privs}, {"get_inventory", l_get_inventory}, - {"create_detached_inventory", l_create_detached_inventory}, + {"create_detached_inventory_raw", l_create_detached_inventory_raw}, {"get_dig_params", l_get_dig_params}, {"get_hit_params", l_get_hit_params}, {"get_current_modname", l_get_current_modname}, @@ -5734,7 +5734,128 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, script_error(L, "error: %s", lua_tostring(L, -1)); } -void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, +/* + Node metadata inventory callbacks +*/ + +// Return number of accepted items to be moved +int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_move")) + return count; + + // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos + push_v3s16(L, p); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 7, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be put +int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_put")) + return stack.count; + + // Call function(pos, listname, index, stack, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be taken +int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p, + const std::string &listname, int index, int count, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + INodeDefManager *ndef = get_server(L)->ndef(); + + // If node doesn't exist, we don't know what callback to call + MapNode node = get_env(L)->getMap().getNodeNoEx(p); + if(node.getContent() == CONTENT_IGNORE) + return 0; + + // Push callback function on stack + if(!get_item_callback(L, ndef->get(node).name.c_str(), + "allow_metadata_inventory_take")) + return count; + + // Call function(pos, listname, index, count, player) + // pos + push_v3s16(L, p); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return luaL_checkinteger(L, -1); +} + +// Report moved items +void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p, const std::string &from_list, int from_index, const std::string &to_list, int to_index, int count, ServerActiveObject *player) @@ -5756,18 +5877,26 @@ void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, return; // function(pos, from_list, from_index, to_list, to_index, count, player) + // pos push_v3s16(L, p); + // from_list lua_pushstring(L, from_list.c_str()); + // from_index lua_pushinteger(L, from_index + 1); + // to_list lua_pushstring(L, to_list.c_str()); + // to_index lua_pushinteger(L, to_index + 1); + // count lua_pushinteger(L, count); + // player objectref_get_or_create(L, player); if(lua_pcall(L, 7, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); } -ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, +// Report put items +void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p, const std::string &listname, int index, ItemStack &stack, ServerActiveObject *player) { @@ -5780,25 +5909,30 @@ ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, // If node doesn't exist, we don't know what callback to call MapNode node = get_env(L)->getMap().getNodeNoEx(p); if(node.getContent() == CONTENT_IGNORE) - return stack; + return; // Push callback function on stack if(!get_item_callback(L, ndef->get(node).name.c_str(), - "on_metadata_inventory_offer")) - return stack; + "on_metadata_inventory_put")) + return; // Call function(pos, listname, index, stack, player) + // pos push_v3s16(L, p); + // listname lua_pushstring(L, listname.c_str()); + // index lua_pushinteger(L, index + 1); + // stack LuaItemStack::create(L, stack); + // player objectref_get_or_create(L, player); - if(lua_pcall(L, 5, 1, 0)) + if(lua_pcall(L, 5, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); - return read_item(L, -1); } -ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, +// Report taken items +void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p, const std::string &listname, int index, int count, ServerActiveObject *player) { @@ -5811,22 +5945,270 @@ ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, // If node doesn't exist, we don't know what callback to call MapNode node = get_env(L)->getMap().getNodeNoEx(p); if(node.getContent() == CONTENT_IGNORE) - return ItemStack(); + return; // Push callback function on stack if(!get_item_callback(L, ndef->get(node).name.c_str(), "on_metadata_inventory_take")) - return ItemStack(); + return; // Call function(pos, listname, index, count, player) + // pos push_v3s16(L, p); + // listname lua_pushstring(L, listname.c_str()); + // index lua_pushinteger(L, index + 1); + // count lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +/* + Detached inventory callbacks +*/ + +// Retrieves minetest.detached_inventories[name][callbackname] +// If that is nil or on error, return false and stack is unchanged +// If that is a function, returns true and pushes the +// function onto the stack +static bool get_detached_inventory_callback(lua_State *L, + const std::string &name, const char *callbackname) +{ + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "detached_inventories"); + lua_remove(L, -2); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, name.c_str()); + lua_remove(L, -2); + // Should be a table + if(lua_type(L, -1) != LUA_TTABLE) + { + errorstream<<"Item \""<<name<<"\" not defined"<<std::endl; + lua_pop(L, 1); + return false; + } + lua_getfield(L, -1, callbackname); + lua_remove(L, -2); + // Should be a function or nil + if(lua_type(L, -1) == LUA_TFUNCTION) + { + return true; + } + else if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + return false; + } + else + { + errorstream<<"Detached inventory \""<<name<<"\" callback \"" + <<callbackname<<"\" is not a function"<<std::endl; + lua_pop(L, 1); + return false; + } +} + +// Return number of accepted items to be moved +int scriptapi_detached_inventory_allow_move(lua_State *L, + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "allow_move")) + return count; + + // function(inv, from_list, from_index, to_list, to_index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 7, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be put +int scriptapi_detached_inventory_allow_put(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "allow_put")) + return stack.count; // All will be accepted + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player objectref_get_or_create(L, player); if(lua_pcall(L, 5, 1, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); - return read_item(L, -1); + return luaL_checkinteger(L, -1); +} + +// Return number of accepted items to be taken +int scriptapi_detached_inventory_allow_take(lua_State *L, + const std::string &name, + const std::string &listname, int index, int count, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "allow_take")) + return count; // All will be accepted + + // Call function(inv, listname, index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 1, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + return luaL_checkinteger(L, -1); +} + +// Report moved items +void scriptapi_detached_inventory_on_move(lua_State *L, + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "on_move")) + return; + + // function(inv, from_list, from_index, to_list, to_index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // from_list + lua_pushstring(L, from_list.c_str()); + // from_index + lua_pushinteger(L, from_index + 1); + // to_list + lua_pushstring(L, to_list.c_str()); + // to_index + lua_pushinteger(L, to_index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 7, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +// Report put items +void scriptapi_detached_inventory_on_put(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "on_put")) + return; + + // Call function(inv, listname, index, stack, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // stack + LuaItemStack::create(L, stack); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); +} + +// Report taken items +void scriptapi_detached_inventory_on_take(lua_State *L, + const std::string &name, + const std::string &listname, int index, int count, + ServerActiveObject *player) +{ + realitycheck(L); + assert(lua_checkstack(L, 20)); + StackUnroller stack_unroller(L); + + // Push callback function on stack + if(!get_detached_inventory_callback(L, name, "on_take")) + return; + + // Call function(inv, listname, index, count, player) + // inv + InventoryLocation loc; + loc.setDetached(name); + InvRef::create(L, loc); + // listname + lua_pushstring(L, listname.c_str()); + // index + lua_pushinteger(L, index + 1); + // count + lua_pushinteger(L, count); + // player + objectref_get_or_create(L, player); + if(lua_pcall(L, 5, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); } /* diff --git a/src/scriptapi.h b/src/scriptapi.h index 2bacdebad..baaf061a9 100644 --- a/src/scriptapi.h +++ b/src/scriptapi.h @@ -101,17 +101,66 @@ void scriptapi_node_on_receive_fields(lua_State *L, v3s16 p, const std::string &formname, const std::map<std::string, std::string> &fields, ServerActiveObject *sender); -// Moves items -void scriptapi_node_on_metadata_inventory_move(lua_State *L, v3s16 p, + +/* Node metadata inventory callbacks */ +// Return number of accepted items to be moved +int scriptapi_nodemeta_inventory_allow_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); +// Return number of accepted items to be put +int scriptapi_nodemeta_inventory_allow_put(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +// Return number of accepted items to be taken +int scriptapi_nodemeta_inventory_allow_take(lua_State *L, v3s16 p, + const std::string &listname, int index, int count, + ServerActiveObject *player); +// Report moved items +void scriptapi_nodemeta_inventory_on_move(lua_State *L, v3s16 p, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); +// Report put items +void scriptapi_nodemeta_inventory_on_put(lua_State *L, v3s16 p, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +// Report taken items +void scriptapi_nodemeta_inventory_on_take(lua_State *L, v3s16 p, + const std::string &listname, int index, int count, + ServerActiveObject *player); + +/* Detached inventory callbacks */ +// Return number of accepted items to be moved +int scriptapi_detached_inventory_allow_move(lua_State *L, + const std::string &name, + const std::string &from_list, int from_index, + const std::string &to_list, int to_index, + int count, ServerActiveObject *player); +// Return number of accepted items to be put +int scriptapi_detached_inventory_allow_put(lua_State *L, + const std::string &name, + const std::string &listname, int index, ItemStack &stack, + ServerActiveObject *player); +// Return number of accepted items to be taken +int scriptapi_detached_inventory_allow_take(lua_State *L, + const std::string &name, + const std::string &listname, int index, int count, + ServerActiveObject *player); +// Report moved items +void scriptapi_detached_inventory_on_move(lua_State *L, + const std::string &name, const std::string &from_list, int from_index, const std::string &to_list, int to_index, int count, ServerActiveObject *player); -// Inserts items, returns rejected items -ItemStack scriptapi_node_on_metadata_inventory_offer(lua_State *L, v3s16 p, +// Report put items +void scriptapi_detached_inventory_on_put(lua_State *L, + const std::string &name, const std::string &listname, int index, ItemStack &stack, ServerActiveObject *player); -// Takes items, returns taken items -ItemStack scriptapi_node_on_metadata_inventory_take(lua_State *L, v3s16 p, +// Report taken items +void scriptapi_detached_inventory_on_take(lua_State *L, + const std::string &name, const std::string &listname, int index, int count, ServerActiveObject *player); |