diff --git a/inventory.lua b/inventory.lua index 693f882..88cb6be 100644 --- a/inventory.lua +++ b/inventory.lua @@ -1,33 +1,52 @@ local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil -local function sendMessage(pos, msg, channel) - if channel == nil then - channel = minetest.get_meta(pos):get_string("channel") - end - digilines.receptor_send(pos,digilines.rules.default,channel,msg) +-- Sends a message onto the Digilines network. +-- pos: the position of the Digilines chest node. +-- action: the action string indicating what happened. +-- stack: the ItemStack that the action acted on (optional). +-- side: which side of the chest the action occurred (optional). +local function send_message(pos, action, stack, side) + local channel = minetest.get_meta(pos):get_string("channel") + local msg = { + action = action, + stack = stack and stack:to_table(), + -- Duplicate the vector in case the caller expects it not to change. + side = side and vector.new(side) + } + digilines.receptor_send(pos, digilines.rules.default, channel, msg) end -local function maybeString(stack) - if type(stack)=='string' then return stack - elseif type(stack)=='table' then return dump(stack) - else return stack:to_string() +-- Checks if the inventory has become empty and, if so, sends an empty message. +local function check_empty(pos) + if minetest.get_meta(pos):get_inventory():is_empty("main") then + send_message(pos, "empty") end end -local function can_insert(pos, stack) - local can = minetest.get_meta(pos):get_inventory():room_for_item("main", stack) - if can then - sendMessage(pos,"put "..maybeString(stack)) - else - -- overflow and lost means that items are gonna be out as entities :/ - sendMessage(pos,"lost "..maybeString(stack)) +-- Checks if the inventory has become full for a particular type of item and, +-- if so, sends a full message. +local function check_full(pos, stack) + local one_item_stack = ItemStack(stack) + one_item_stack:set_count(1) + if not minetest.get_meta(pos):get_inventory():room_for_item("main", one_item_stack) then + send_message(pos, "full", one_item_stack) end - return can end local tubeconn = pipeworks_enabled and "^pipeworks_tube_connection_wooden.png" or "" local tubescan = pipeworks_enabled and function(pos) pipeworks.scan_for_tube_objects(pos) end or nil +-- A place to remember things from allow_metadata_inventory_put to +-- on_metadata_inventory_put. This is a hack due to issue +-- minetest/minetest#6534 that should be removed once that’s fixed. +local last_inventory_put_index +local last_inventory_put_stack + +-- A place to remember things from allow_metadata_inventory_take to +-- tube.remove_items. This is a hack due to issue minetest-mods/pipeworks#205 +-- that should be removed once that’s fixed. +local last_inventory_take_index + minetest.register_alias("digilines_inventory:chest", "digilines:chest") minetest.register_node("digilines:chest", { description = "Digiline Chest", @@ -86,63 +105,106 @@ minetest.register_node("digilines:chest", { return not pipeworks.connects.facingFront(i,param2) end, input_inventory = "main", - can_insert = function(pos, _, stack) - return can_insert(pos, stack) + can_insert = function(pos, _, stack, direction) + local ret = minetest.get_meta(pos):get_inventory():room_for_item("main", stack) + if not ret then + -- The stack cannot be accepted. It will never be passed to + -- insert_object, but it should be reported as a toverflow. + -- Here, direction = direction item is moving, which is into + -- side. + local side = vector.multiply(direction, -1) + send_message(pos, "toverflow", stack, side) + end + return ret end, - insert_object = function(pos, _, stack) - local inv = minetest.get_meta(pos):get_inventory() - local leftover = inv:add_item("main", stack) - local count = leftover:get_count() - if count == 0 then - local derpstack = stack:get_name()..' 1' - if not inv:room_for_item("main", derpstack) then - -- when you can't put a single more of whatever you just put, - -- you'll get a put for it, then a full - sendMessage(pos,"full "..maybeString(stack)..' '..tostring(count)) - end - else - -- this happens when the chest has received two stacks in a row and - -- filled up exactly with the first one. - -- You get a put for the first stack, a put for the second - -- and then a overflow with the first in stack and the second in leftover - -- and NO full? - sendMessage(pos,"overflow "..maybeString(stack)..' '..tostring(count)) + insert_object = function(pos, _, stack, direction) + -- Here, direction = direction item is moving, which is into side. + local side = vector.multiply(direction, -1) + local leftover = minetest.get_meta(pos):get_inventory():add_item("main", stack) + local leftover_count = leftover:get_count() + local stack_count = stack:get_count() + if leftover_count ~= stack_count then + -- Some items were added. Report them. + stack:set_count(stack_count - leftover_count) + send_message(pos, "tput", stack, side) + check_full(pos, stack) + end + if leftover_count ~= 0 then + -- Some items could not be added and bounced back. Report them. + send_message(pos, "toverflow", leftover, side) end return leftover end, + remove_items = function(pos, _, stack, dir, count) + -- Here, stack is the ItemStack in our own inventory that is being + -- pulled from, NOT the stack that is actually pulled out. + -- Combining it with count gives the stack that is pulled out. + -- Also, note that Pipeworks doesn’t pass the index to this + -- function, so we use the one recorded in + -- allow_metadata_inventory_take; because we don’t implement + -- tube.can_remove, Pipeworks will call + -- allow_metadata_inventory_take instead and will pass it the + -- index. + local taken = stack:take_item(count) + minetest.get_meta(pos):get_inventory():set_stack("main", last_inventory_take_index, stack) + send_message(pos, "ttake", taken, dir) + check_empty(pos) + return taken + end, }, - allow_metadata_inventory_put = function(pos, _, _, stack) - if not can_insert(pos, stack) then - sendMessage(pos,"uoverflow "..maybeString(stack)) - end + allow_metadata_inventory_put = function(pos, _, index, stack) + -- Remember what was in the target slot before the put; see + -- on_metadata_inventory_put for why we care. + last_inventory_put_index = index + last_inventory_put_stack = minetest.get_meta(pos):get_inventory():get_stack("main", index) return stack:get_count() end, - on_metadata_inventory_move = function(pos, _, _, _, _, _, player) + allow_metadata_inventory_take = function(_, _, index, stack) + -- Remember the index value; see tube.remove_items for why we care. + last_inventory_take_index = index + return stack:get_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 chest at "..minetest.pos_to_string(pos)) end, - on_metadata_inventory_put = function(pos, _, _, stack, player) - local channel = minetest.get_meta(pos):get_string("channel") - local send = function(msg) - sendMessage(pos,msg,channel) - end - -- direction is only for furnaces - -- as the item has already been put, can_insert should return false if the chest is now full. - local derpstack = stack:get_name()..' 1' - if can_insert(pos,derpstack) then - send("uput "..maybeString(stack)) + on_metadata_inventory_put = function(pos, _, index, stack, player) + -- Get what was in the target slot before the put; it has disappeared + -- by now (been replaced by the result of the put action) but we saved + -- it in allow_metadata_inventory_put. This should always work + -- (allow_metadata_inventory_put should AFAICT always be called + -- immediately before on_metadata_inventory_put), but in case of + -- something weird happening, just fall back to using an empty + -- ItemStack rather than crashing. + local old_stack + if last_inventory_put_index == index then + old_stack = last_inventory_put_stack + last_inventory_put_index = nil + last_inventory_put_stack = nil else - send("ufull "..maybeString(stack)) + old_stack = ItemStack(nil) end + -- If the player tries to place a stack into an inventory, there’s + -- already a stack there, and the existing stack is either of a + -- different item type or full, then obviously the stacks can’t be + -- merged; instead the stacks are swapped. This information is not + -- reported to mods (Minetest core neither tells us that a particular + -- action was a swap, nor tells us a take followed by a put). In core, + -- the condition for swapping is that you try to add the new stack to + -- the existing stack and the leftovers are as big as the original + -- stack to put. Replicate that logic here using the old stack saved in + -- allow_metadata_inventory_put. If a swap happened, report it to the + -- Digilines network as a utake followed by a uput. + local leftovers = old_stack:add_item(stack) + if leftovers:get_count() == stack:get_count() then + send_message(pos, "utake", old_stack) + end + send_message(pos, "uput", stack) + check_full(pos, stack) minetest.log("action", player:get_player_name().." puts stuff into chest at "..minetest.pos_to_string(pos)) end, - on_metadata_inventory_take = function(pos, listname, _, stack, player) - local meta = minetest.get_meta(pos) - local channel = meta:get_string("channel") - local inv = meta:get_inventory() - if inv:is_empty(listname) then - sendMessage(pos, "empty", channel) - end - sendMessage(pos,"utake "..maybeString(stack)) + on_metadata_inventory_take = function(pos, _, index, stack, player) + send_message(pos, "utake", stack) + check_empty(pos) minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos)) end })