diff options
Diffstat (limited to 'digilines')
-rw-r--r-- | digilines/.luacheckrc | 2 | ||||
-rw-r--r-- | digilines/init.lua | 10 | ||||
-rw-r--r-- | digilines/internal.lua | 11 | ||||
-rw-r--r-- | digilines/inventory.lua | 283 | ||||
-rw-r--r-- | digilines/lcd.lua | 2 |
5 files changed, 240 insertions, 68 deletions
diff --git a/digilines/.luacheckrc b/digilines/.luacheckrc index 6d89a3f..b356748 100644 --- a/digilines/.luacheckrc +++ b/digilines/.luacheckrc @@ -1,5 +1,7 @@ read_globals = { + "vector", + "screwdriver", "minetest", "default", "pipeworks", diff --git a/digilines/init.lua b/digilines/init.lua index 21c1c8e..93c94e9 100644 --- a/digilines/init.lua +++ b/digilines/init.lua @@ -64,19 +64,19 @@ minetest.register_craft({ } }) --- former submods -if minetest.is_yes(minetest.setting_get("digilines_enable_inventory") or true) then +-- For minetest 0.4 support returned nil are also tested: ~= false +if minetest.settings:get_bool("digilines_enable_inventory", true) ~= false then dofile(modpath .. "/inventory.lua") end -if minetest.is_yes(minetest.setting_get("digilines_enable_lcd") or true) then +if minetest.settings:get_bool("digilines_enable_lcd", true) ~= false then dofile(modpath .. "/lcd.lua") end -if minetest.is_yes(minetest.setting_get("digilines_enable_lightsensor") or true) then +if minetest.settings:get_bool("digilines_enable_lightsensor", true) ~= false then dofile(modpath .. "/lightsensor.lua") end -if minetest.is_yes(minetest.setting_get("digilines_enable_rtc") or true) then +if minetest.settings:get_bool("digilines_enable_rtc", true) ~= false then dofile(modpath .. "/rtc.lua") end diff --git a/digilines/internal.lua b/digilines/internal.lua index 2528f35..05c93fa 100644 --- a/digilines/internal.lua +++ b/digilines/internal.lua @@ -1,6 +1,7 @@ function digilines.getspec(node) - if not minetest.registered_nodes[node.name] then return false end - return minetest.registered_nodes[node.name].digiline + local def = minetest.registered_nodes[node.name] + if not def then return false end + return def.digilines or def.digiline end function digilines.importrules(spec, node) @@ -86,6 +87,12 @@ local function queue_dequeue(queue) end function digilines.transmit(pos, channel, msg, checked) + local checkedID = minetest.hash_node_position(pos) + if checked[checkedID] then + return + end + checked[checkedID] = true + digilines.vm_begin() local queue = queue_new() queue_enqueue(queue, pos) diff --git a/digilines/inventory.lua b/digilines/inventory.lua index 693f882..86b1536 100644 --- a/digilines/inventory.lua +++ b/digilines/inventory.lua @@ -1,33 +1,56 @@ 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). +-- from_slot: the slot number that is taken from (optional). +-- to_slot: the slot number that is put into (optional). +-- side: which side of the chest the action occurred (optional). +local function send_message(pos, action, stack, from_slot, to_slot, side) + local channel = minetest.get_meta(pos):get_string("channel") + local msg = { + action = action, + stack = stack and stack:to_table(), + from_slot = from_slot, + to_slot = to_slot, + -- 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 +109,203 @@ 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, nil, nil, side) + end + return ret end, - insert_object = function(pos, _, stack) + insert_object = function(pos, _, original_stack, direction) + -- Here, direction = direction item is moving, which is into side. + local side = vector.multiply(direction, -1) 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)) + local inv_contents = inv:get_list("main") + local any_put = false + local stack = original_stack + local stack_name = stack:get_name() + local stack_count = stack:get_count() + -- Walk the inventory, adding items to existing stacks of the same + -- type. + for i = 1, #inv_contents do + local existing_stack = inv_contents[i] + if not existing_stack:is_empty() and existing_stack:get_name() == stack_name then + local leftover = existing_stack:add_item(stack) + local leftover_count = leftover:get_count() + if leftover_count ~= stack_count then + -- We put some items into the slot. Update the slot in + -- the inventory, tell Digilines listeners about it, + -- and keep looking for the a place to put the + -- leftovers if any. + any_put = true + inv:set_stack("main", i, existing_stack) + local stack_that_was_put + if leftover_count == 0 then + stack_that_was_put = stack + else + stack_that_was_put = ItemStack(stack) + stack_that_was_put:set_count(stack_count - leftover_count) + end + send_message(pos, "tput", stack_that_was_put, nil, i, side) + stack = leftover + stack_count = leftover_count + if stack_count == 0 then + break + end + end + end + end + if stack_count ~= 0 then + -- Walk the inventory, adding items to empty slots. + for i = 1, #inv_contents do + local existing_stack = inv_contents[i] + if existing_stack:is_empty() then + local leftover = existing_stack:add_item(stack) + local leftover_count = leftover:get_count() + if leftover_count ~= stack_count then + -- We put some items into the slot. Update the slot in + -- the inventory, tell Digilines listeners about it, + -- and keep looking for the a place to put the + -- leftovers if any. + any_put = true + inv:set_stack("main", i, existing_stack) + local stack_that_was_put + if leftover_count == 0 then + stack_that_was_put = stack + else + stack_that_was_put = ItemStack(stack) + stack_that_was_put:set_count(stack_count - leftover_count) + end + send_message(pos, "tput", stack_that_was_put, nil, i, side) + stack = leftover + stack_count = leftover_count + if stack_count == 0 then + break + end + end + end 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)) end - return leftover + if any_put then + check_full(pos, original_stack) + end + if stack_count ~= 0 then + -- Some items could not be added and bounced back. Report them. + send_message(pos, "toverflow", stack, nil, nil, side) + end + return stack + 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, last_inventory_take_index, nil, 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) - minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos)) + 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_put = function(pos, _, _, stack, player) - local channel = minetest.get_meta(pos):get_string("channel") - local send = function(msg) - sendMessage(pos,msg,channel) + on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) + -- See what would happen if we were to move the items back from in the + -- opposite direction. In the event of a normal move, this must + -- succeed, because a normal move subtracts some items from the from + -- stack and adds them to the to stack; the two stacks naturally must + -- be compatible and so the reverse operation must succeed. However, if + -- the user *swaps* the two stacks instead, then due to issue + -- minetest/minetest#6534, this function is only called once; however, + -- when it is called, the stack that used to be in the to stack has + -- already been moved to the from stack, so we can detect the situation + -- by the fact that the reverse move will fail due to the from stack + -- being incompatible with its former contents. + local inv = minetest.get_meta(pos):get_inventory() + local from_stack = inv:get_stack("main", from_index) + local to_stack = inv:get_stack("main", to_index) + local reverse_move_stack = ItemStack(to_stack) + reverse_move_stack:set_count(count) + local swapped = from_stack:add_item(reverse_move_stack):get_count() == count + if swapped then + local channel = minetest.get_meta(pos):get_string("channel") + to_stack:set_count(count) + local msg = { + action = "uswap", + -- The slot and stack do not match because this function is + -- called after the action has taken place, but the Digilines + -- message is from the perspective of a viewer who hasn’t + -- observed the movement yet. + x_stack = to_stack:to_table(), + x_slot = from_index, + y_stack = from_stack:to_table(), + y_slot = to_index, + } + digilines.receptor_send(pos, digilines.rules.default, channel, msg) + else + to_stack:set_count(count) + send_message(pos, "umove", to_stack, from_index, to_index) 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)) + minetest.log("action", player:get_player_name().." moves stuff in chest at "..minetest.pos_to_string(pos)) + end, + 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, index) + end + send_message(pos, "uput", stack, nil, index) + 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, index) + check_empty(pos) minetest.log("action", player:get_player_name().." takes stuff from chest at "..minetest.pos_to_string(pos)) end }) diff --git a/digilines/lcd.lua b/digilines/lcd.lua index a59b0b2..37ce3e0 100644 --- a/digilines/lcd.lua +++ b/digilines/lcd.lua @@ -178,7 +178,7 @@ end local spawn_entity = function(pos) if not get_entity(pos) then - local text = minetest.add_entity(pos, "digilines_lcd:text") + minetest.add_entity(pos, "digilines_lcd:text") rotate_text(pos) end end |