Detached inventory callbacks and reworked node metadata callbacks
authorPerttu Ahola <celeron55@gmail.com>
Tue, 24 Jul 2012 23:36:54 +0000 (02:36 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Tue, 24 Jul 2012 23:36:54 +0000 (02:36 +0300)
builtin/builtin.lua
builtin/deprecated.lua
builtin/detached_inventory.lua [new file with mode: 0644]
builtin/item.lua
doc/lua_api.txt
games/minimal/mods/default/init.lua
games/minimal/mods/experimental/init.lua
src/guiFormSpecMenu.cpp
src/inventorymanager.cpp
src/scriptapi.cpp
src/scriptapi.h

index bd5adf9e715668b7200c18156f9d3ba44f1462cf..a17841fc8c391e45c65f4c5017677aa9544cfdd7 100644 (file)
@@ -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")
 
index 67210d525bff3ad51a401467960958915cc94ea4..d41e2c44c53b28efa81e34167ca90b7893d1d6b3 100644 (file)
@@ -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 (file)
index 0000000..3757f13
--- /dev/null
@@ -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
+
index bd34efe798a737c0d3f1be7f3a0a521a33e9a632..2a9b4ff27ee306cf7c1c04205ee34c1c1b75ebf3 100644 (file)
@@ -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
index c9fafd65fbc072076ea38118a226cdb745b1b4b9..5fb2c34e38144960fd230f381a8620da714c6d45 100644 (file)
@@ -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
+}
+
index 428dfd9f497f00640ea25c92f1c87bb3093f82a4..fe7ab955ec26f199784af79b837ef2f1a41c7971 100644 (file)
@@ -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;]"..
index 498c64623f535befc36b7a5449393a99a03c69c0..0b45aeb62fc8bbffeb4cc0002f4b7a1a4a0de89e 100644 (file)
@@ -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")
index 3eb056625fb8af9b9343cf5af47d142f4061d43e..7ded2b37eea2d2c7dcb8c0ff3e6747406685f44c 100644 (file)
@@ -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);
index 1369cb0d7c402caafcb7251381b406643cbff4e4..5412a5dca5e1d916858540c3ff68710085cdeff6 100644 (file)
@@ -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)
index 9959ddd748d6d0afa1f52425f034f63e4e7c2225..d28f8c7c1f07e911acf88a206a14a5d1a3266488 100644 (file)
@@ -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));
 }
 
 /*
index 2bacdebad1a784aaf15ca83f8bc39c00e88c756c..baaf061a90d9be866ca9d24212ce7cce5edd9e82 100644 (file)
@@ -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);