Rework Digilines chest

Fix various bugs regarding spurious messages being sent, item stack
swapping not being reported properly, etc.. Change from sending strings
to sending tables. Enhance Pipeworks tube support by reporting which
side an item is added or removed via tube or filter-injector.
This commit is contained in:
Christopher Head 2017-10-12 00:55:45 -07:00 committed by Auke Kok
parent 925b318800
commit 9e4f1a5bab

@ -1,33 +1,52 @@
local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil local pipeworks_enabled = minetest.get_modpath("pipeworks") ~= nil
local function sendMessage(pos, msg, channel) -- Sends a message onto the Digilines network.
if channel == nil then -- pos: the position of the Digilines chest node.
channel = minetest.get_meta(pos):get_string("channel") -- action: the action string indicating what happened.
end -- stack: the ItemStack that the action acted on (optional).
digilines.receptor_send(pos,digilines.rules.default,channel,msg) -- 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 end
local function maybeString(stack) -- Checks if the inventory has become empty and, if so, sends an empty message.
if type(stack)=='string' then return stack local function check_empty(pos)
elseif type(stack)=='table' then return dump(stack) if minetest.get_meta(pos):get_inventory():is_empty("main") then
else return stack:to_string() send_message(pos, "empty")
end end
end end
local function can_insert(pos, stack) -- Checks if the inventory has become full for a particular type of item and,
local can = minetest.get_meta(pos):get_inventory():room_for_item("main", stack) -- if so, sends a full message.
if can then local function check_full(pos, stack)
sendMessage(pos,"put "..maybeString(stack)) local one_item_stack = ItemStack(stack)
else one_item_stack:set_count(1)
-- overflow and lost means that items are gonna be out as entities :/ if not minetest.get_meta(pos):get_inventory():room_for_item("main", one_item_stack) then
sendMessage(pos,"lost "..maybeString(stack)) send_message(pos, "full", one_item_stack)
end end
return can
end end
local tubeconn = pipeworks_enabled and "^pipeworks_tube_connection_wooden.png" or "" 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 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 thats 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 thats fixed.
local last_inventory_take_index
minetest.register_alias("digilines_inventory:chest", "digilines:chest") minetest.register_alias("digilines_inventory:chest", "digilines:chest")
minetest.register_node("digilines:chest", { minetest.register_node("digilines:chest", {
description = "Digiline Chest", description = "Digiline Chest",
@ -86,63 +105,106 @@ minetest.register_node("digilines:chest", {
return not pipeworks.connects.facingFront(i,param2) return not pipeworks.connects.facingFront(i,param2)
end, end,
input_inventory = "main", input_inventory = "main",
can_insert = function(pos, _, stack) can_insert = function(pos, _, stack, direction)
return can_insert(pos, stack) 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, end,
insert_object = function(pos, _, stack) insert_object = function(pos, _, stack, direction)
local inv = minetest.get_meta(pos):get_inventory() -- Here, direction = direction item is moving, which is into side.
local leftover = inv:add_item("main", stack) local side = vector.multiply(direction, -1)
local count = leftover:get_count() local leftover = minetest.get_meta(pos):get_inventory():add_item("main", stack)
if count == 0 then local leftover_count = leftover:get_count()
local derpstack = stack:get_name()..' 1' local stack_count = stack:get_count()
if not inv:room_for_item("main", derpstack) then if leftover_count ~= stack_count then
-- when you can't put a single more of whatever you just put, -- Some items were added. Report them.
-- you'll get a put for it, then a full stack:set_count(stack_count - leftover_count)
sendMessage(pos,"full "..maybeString(stack)..' '..tostring(count)) send_message(pos, "tput", stack, side)
end check_full(pos, stack)
else end
-- this happens when the chest has received two stacks in a row and if leftover_count ~= 0 then
-- filled up exactly with the first one. -- Some items could not be added and bounced back. Report them.
-- You get a put for the first stack, a put for the second send_message(pos, "toverflow", leftover, side)
-- and then a overflow with the first in stack and the second in leftover
-- and NO full?
sendMessage(pos,"overflow "..maybeString(stack)..' '..tostring(count))
end end
return leftover return leftover
end, 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 doesnt pass the index to this
-- function, so we use the one recorded in
-- allow_metadata_inventory_take; because we dont 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) allow_metadata_inventory_put = function(pos, _, index, stack)
if not can_insert(pos, stack) then -- Remember what was in the target slot before the put; see
sendMessage(pos,"uoverflow "..maybeString(stack)) -- on_metadata_inventory_put for why we care.
end last_inventory_put_index = index
last_inventory_put_stack = minetest.get_meta(pos):get_inventory():get_stack("main", index)
return stack:get_count() return stack:get_count()
end, 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)) minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_put = function(pos, _, _, stack, player) on_metadata_inventory_put = function(pos, _, index, stack, player)
local channel = minetest.get_meta(pos):get_string("channel") -- Get what was in the target slot before the put; it has disappeared
local send = function(msg) -- by now (been replaced by the result of the put action) but we saved
sendMessage(pos,msg,channel) -- it in allow_metadata_inventory_put. This should always work
end -- (allow_metadata_inventory_put should AFAICT always be called
-- direction is only for furnaces -- immediately before on_metadata_inventory_put), but in case of
-- as the item has already been put, can_insert should return false if the chest is now full. -- something weird happening, just fall back to using an empty
local derpstack = stack:get_name()..' 1' -- ItemStack rather than crashing.
if can_insert(pos,derpstack) then local old_stack
send("uput "..maybeString(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 else
send("ufull "..maybeString(stack)) old_stack = ItemStack(nil)
end end
-- If the player tries to place a stack into an inventory, theres
-- already a stack there, and the existing stack is either of a
-- different item type or full, then obviously the stacks cant 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)) minetest.log("action", player:get_player_name().." puts stuff into chest at "..minetest.pos_to_string(pos))
end, end,
on_metadata_inventory_take = function(pos, listname, _, stack, player) on_metadata_inventory_take = function(pos, _, index, stack, player)
local meta = minetest.get_meta(pos) send_message(pos, "utake", stack)
local channel = meta:get_string("channel") check_empty(pos)
local inv = meta:get_inventory()
if inv:is_empty(listname) then
sendMessage(pos, "empty", channel)
end
sendMessage(pos,"utake "..maybeString(stack))
minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos)) minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos))
end end
}) })