Large drawer controller refactoring

This commit is contained in:
Linus Jahn 2020-06-09 20:01:17 +02:00
parent 7cf8cb33aa
commit 62b5fa51ae
No known key found for this signature in database
GPG Key ID: 4663231A91A1E27B
5 changed files with 199 additions and 236 deletions

@ -177,9 +177,26 @@ function drawers.remove_drawer_upgrade(pos, listname, index, stack, player)
drawers.update_drawer_upgrades(pos)
end
function drawers.drawer_insert_object(pos, node, stack, direction)
--[[
Inserts an incoming stack into a specific slot of a drawer.
]]
function drawers.drawer_insert_object(pos, stack, visualid)
local visual = drawers.get_visual(pos, visualid)
if not visual then
return stack
end
return visual:try_insert_stack(stack, true)
end
--[[
Inserts an incoming stack into a drawer and uses all slots.
]]
function drawers.drawer_insert_object_from_tube(pos, node, stack, direction)
local drawer_visuals = drawers.drawer_visuals[core.serialize(pos)]
if not drawer_visuals then return stack end
if not drawer_visuals then
return stack
end
-- first try to insert in the correct slot (if there are already items)
local leftover = stack
@ -198,14 +215,29 @@ function drawers.drawer_insert_object(pos, node, stack, direction)
return leftover
end
function drawers.drawer_can_insert_object(pos, node, stack, direction)
--[[
Returns how much (count) of a stack can be inserted to a drawer slot.
]]
function drawers.drawer_can_insert_stack(pos, stack, visualid)
local visual = drawers.get_visual(pos, visualid)
if not visual then
return 0
end
return visual:can_insert_stack(stack)
end
--[[
Returns whether a stack can be (partially) inserted to any slot of a drawer.
]]
function drawers.drawer_can_insert_stack_from_tube(pos, node, stack, direction)
local drawer_visuals = drawers.drawer_visuals[core.serialize(pos)]
if not drawer_visuals then
return false
end
for _, visual in pairs(drawer_visuals) do
if visual.itemName == "" or (visual.itemName == stack:get_name() and visual.count ~= visual.maxCount) then
if visual:can_insert_stack(stack) > 0 then
return true
end
end
@ -233,6 +265,19 @@ function drawers.drawer_take_item(pos, itemstack)
return ItemStack()
end
--[[
Returns the content of a drawer slot.
]]
function drawers.drawer_get_content(pos, visualid)
local drawer_meta = core.get_meta(pos)
return {
name = drawer_meta:get_string("name" .. visualid),
count = drawer_meta:get_int("count" .. visualid),
maxCount = drawer_meta:get_int("max_count" .. visualid)
}
end
function drawers.register_drawer(name, def)
def.description = def.description or S("Wooden")
def.drawtype = "nodebox"
@ -263,9 +308,9 @@ function drawers.register_drawer(name, def)
def.groups.tubedevice_receiver = 1
def.tube = def.tube or {}
def.tube.insert_object = def.tube.insert_object or
drawers.drawer_insert_object
drawers.drawer_insert_object_from_tube
def.tube.can_insert = def.tube.can_insert or
drawers.drawer_can_insert_object
drawers.drawer_can_insert_stack_from_tube
def.tube.connect_sides = {left = 1, right = 1, back = 1, top = 1,
bottom = 1}

@ -46,16 +46,15 @@ local S, NS = dofile(MP.."/intllib.lua")
local default_loaded = core.get_modpath("default") and default
local mcl_loaded = core.get_modpath("mcl_core") and mcl_core
local pipeworks_loaded = core.get_modpath("pipeworks") and pipeworks
local digilines_loaded = core.get_modpath("digilines") and digilines
local controller_interval = tonumber(core.settings:get("drawers_controller_interval")) or 1.0
local function controller_formspec(pos, meta_current_state)
local function controller_formspec(pos)
local formspec =
"size[8,8.5]"..
drawers.gui_bg..
drawers.gui_bg_img..
drawers.gui_slots..
"label[0,0;" .. S("Current State: ") .. meta_current_state .. "]" ..
"label[0,0;" .. S("Drawer Controller") .. "]" ..
"list[current_name;src;3.5,1.75;1,1;]"..
"list[current_player;main;0,4.25;8,1;]"..
"list[current_player;main;0,5.5;8,3;8]"..
@ -63,7 +62,7 @@ local function controller_formspec(pos, meta_current_state)
"listring[current_name;src]"..
"listring[current_player;main]"
if digilines and pipeworks then
if digilines_loaded and pipeworks_loaded then
formspec = formspec .. "field[1,3.5;4,1;digilineChannel;" .. S("Digiline Channel") .. ";${digilineChannel}]"
formspec = formspec .. "button_exit[5,3.2;2,1;saveChannel;" .. S("Save") .. "]"
end
@ -149,20 +148,12 @@ local function add_drawer_to_inventory(controllerInventory, pos)
-- If we already indexed this item previously, check which drawer
-- has the most space and have that one be the one indexed
if controllerInventory[item_id] then
local indexed_drawer_meta = core.get_meta({
controllerInventory[item_id]["drawer_pos"],
})
local indexed_drawer_meta_count = indexed_drawer_meta:get_int(
"count" .. controllerInventory[item_id]["visualid"])
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int(
"max_count" .. controllerInventory[item_id]["visualid"])
local content = drawers.drawer_get_content(controllerInventory[item_id].drawer_pos, controllerInventory[item_id].visualid)
local new_content = drawers.drawer_get_content(pos, slot_id)
local drawer_meta_count = meta:get_int("count" .. slot_id)
local drawer_meta_max_count = meta:get_int("max_count" .. slot_id)
-- If the already indexed drawer has less space, we override the table index for that item with the new drawer
if (indexed_drawer_meta_max_count - indexed_drawer_meta_count)
< (drawer_meta_max_count - drawer_meta_count) then
-- If the already indexed drawer has less space, we override the
-- table index for that item with the new drawer
if (new_content.maxCount - new_content.count) > (content.maxCount - content.count) then
controllerInventory[item_id] = controller_index_slot(pos, slot_id)
end
else
@ -231,183 +222,75 @@ local function controller_get_drawer_index(pos, itemstring)
-- If the index has not been created, the item isn't in the index, the
-- item in the drawer is no longer the same item in the index, or the item
-- is in the index but it's full, run the index_drawers function.
local drawers_table_index = core.deserialize(meta:get_string("drawers_table_index"))
local drawer_net_index = core.deserialize(meta:get_string("drawers_table_index"))
-- If the index has not been created
if not drawers_table_index then
drawers_table_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
if not drawer_net_index then
drawer_net_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawer_net_index))
-- If the item isn't in the index
elseif not drawers_table_index[itemstring] then
drawers_table_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
elseif not drawer_net_index[itemstring] then
drawer_net_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawer_net_index))
-- If the item is in the index but either the name that was indexed is not
-- the same as what is currently in the drawer or the drawer is full
elseif drawers_table_index[itemstring] then
local visualid = drawers_table_index[itemstring]["visualid"]
local indexed_drawer_meta = core.get_meta({
drawers_table_index[itemstring]["drawer_pos"]
})
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
if indexed_drawer_meta_name ~= itemstring or indexed_drawer_meta_count >= indexed_drawer_meta_max_count then
drawers_table_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
elseif drawer_net_index[itemstring] then
local content = drawers.drawer_get_content(drawer_net_index[itemstring].drawer_pos, drawer_net_index[itemstring].visualid)
if content.name ~= itemstring or content.count >= content.maxCount then
drawer_net_index = index_drawers(pos)
meta:set_string("drawers_table_index", core.serialize(drawer_net_index))
end
end
return drawers_table_index
return drawer_net_index
end
local function controller_node_timer(pos, elapsed)
local function controller_insert_to_drawers(pos, stack)
-- Inizialize metadata
local meta = core.get_meta(pos)
local meta_current_state = meta:get_string("current_state")
local meta_times_ran_while_jammed = meta:get_float("times_ran_while_jammed")
local meta_jammed_item_name = meta:get_string("jammed_item_name")
local inv = meta:get_inventory()
local src = inv:get_stack("src", 1)
local src_name = src:get_name()
--[[
There are four scenarios for the item slot in the controller.
1: No item is in the controller.
2: Item is not stackable.
3. Item is allowed and there is either an existing drawer for that item with room or an empty drawer.
4: Item is allowed, but there is no room.
local drawer_net_index = controller_get_drawer_index(pos, stack:get_name())
There are three different possibilities for "current_state".
1: "running" which means means it's operating normally.
2: "stopped" meaning the controller makes no attempt to put in the item possibly due to being unallowed for various reasons.
3: "jammed" meaning the item is allowed in to drawers, but there was no space to deposit it last time it ran.
]]
--[[
If current state is jammed, the item that jammed it is the same item in the
src inv slot, and the amount of times ran while jammed is 8 or higher, we
set the current state to stopped. Will possibly want to make an option in the
formspec to ignore this an continue running if the user plans on using the
system in a way that may cause frequent jams making it a hassle to manually
clear it each time
]]
if meta_current_state == "jammed" and meta_jammed_item_name == src_name and meta_times_ran_while_jammed >= 2 then
meta:set_string("current_state", "stopped")
meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
return true
end
-- If current state is stopped, and the item that jammed it is the same
-- item in the src inv slot, we don't do anything
if meta_current_state == "stopped" and meta_jammed_item_name == src_name then
return true
end
-- If current state is stopped, and the item that jammed it is not the
-- same item in the src inv slot, we set the current state to running and
-- clear the jam counter.
if meta_current_state == "stopped" and meta_jammed_item_name ~= src_name then
meta:set_string("current_state", "running")
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_float("times_ran_while_jammed", 0)
end
-- If no item is in the controller, nothing is searched and current_state
-- is set to running and no jams.
if inv:is_empty("src") then
meta:set_string("current_state", "running")
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_float("times_ran_while_jammed", 0)
return true
end
-- If a non stackable item is in the controller, such as a written book,
-- set the current_state to stopped because they are not allowed in drawers
if src:get_stack_max() == 1 then
meta:set_string("current_state", "stopped")
meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
meta:set_string("jammed_item_name", src_name)
meta:set_float("times_ran_while_jammed", 1)
return true
end
local drawers_table_index = controller_get_drawer_index(pos, src_name)
-- This might not be needed, but my concern is if the above indexing takes
-- enough time, there could be a "race condition" where the item in the src
-- inventory is no longer the same item when we checked before or the
-- quantity of the items changed so I'm having it grab the item stack again
-- just in case.
-- If a race condition does occur, items could be lost or duplicated
src = inv:get_stack("src", 1)
src_name = src:get_name()
local src_count = src:get_count()
local src_stack_max = src:get_stack_max()
-- At this point, the item either was in the index or everything was reindexed so we check again
-- If there is a drawer with the item and it isn't full, we will put the items we can in to it
if drawers_table_index[src_name] then
local indexed_drawer_pos = drawers_table_index[src_name]["drawer_pos"]
local visualid = drawers_table_index[src_name]["visualid"]
local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
-- We check if there is a drawer with the item and it isn't full. We will
-- put the items we can into it.
if drawer_net_index[stack:get_name()] then
local drawer_pos = drawer_net_index[stack:get_name()]["drawer_pos"]
local visualid = drawer_net_index[stack:get_name()]["visualid"]
local content = drawers.drawer_get_content(drawer_pos, visualid)
-- If the the item in the drawer is the same as the one we are trying to
-- store, the drawer is not full, and the drawer entity is loaded, we
-- will put the items in the drawer
if indexed_drawer_meta_name == src_name and indexed_drawer_meta_count < indexed_drawer_meta_max_count and drawers.drawer_visuals[core.serialize(indexed_drawer_pos)] then
local leftover = drawers.drawer_insert_object(indexed_drawer_pos, nil, src, nil)
inv:set_stack("src", 1, leftover)
-- Set the controller metadata
meta:set_string("current_state", "running")
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_float("times_ran_while_jammed", 0)
else
meta:set_string("current_state", "jammed")
meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
meta:set_string("jammed_item_name", src_name)
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
if content.name == stack:get_name() and
content.count < content.maxCount and
drawers.drawer_visuals[core.serialize(drawer_pos)] then
return drawers.drawer_insert_object(drawer_pos, stack, visualid)
end
elseif drawers_table_index["empty"] then
local indexed_drawer_pos = drawers_table_index["empty"]["drawer_pos"]
local visualid = drawers_table_index["empty"]["visualid"]
local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
elseif drawer_net_index["empty"] then
local drawer_pos = drawer_net_index["empty"]["drawer_pos"]
local visualid = drawer_net_index["empty"]["visualid"]
local content = drawers.drawer_get_content(drawer_pos, visualid)
-- If the drawer is still empty and the drawer entity is loaded, we will put the items in the drawer
if indexed_drawer_meta_name == "" and drawers.drawer_visuals[core.serialize(indexed_drawer_pos)] then
local leftover = drawers.drawer_insert_object(indexed_drawer_pos, nil, src, nil)
inv:set_stack("src", 1, leftover)
if content.name == "" and drawers.drawer_visuals[core.serialize(drawer_pos)] then
local leftover = drawers.drawer_insert_object(drawer_pos, stack, visualid)
-- Add the item to the drawers table index and set the empty one to nil
drawers_table_index["empty"] = nil
drawers_table_index[src_name] = indexed_drawer_pos
drawer_net_index["empty"] = nil
drawer_net_index[stack:get_name()] = controller_index_slot(drawer_pos, visualid)
-- Set the controller metadata
meta:set_string("current_state", "running")
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_float("times_ran_while_jammed", 0)
meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
else
meta:set_string("current_state", "jammed")
meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
meta:set_string("jammed_item_name", src_name)
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
meta:set_string("drawers_table_index", core.serialize(drawer_net_index))
return leftover
end
else
meta:set_string("current_state", "jammed")
meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
meta:set_string("jammed_item_name", src_name)
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
end
return true
return stack
end
local function controller_can_dig(pos, player)
@ -418,15 +301,10 @@ end
local function controller_on_construct(pos)
local meta = core.get_meta(pos)
meta:set_string("current_state", "running")
meta:set_float("times_ran_while_jammed", 0)
meta:set_string("jammed_item_name", "")
meta:set_string("drawers_table_index", "")
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_string("formspec", controller_formspec(pos))
meta:get_inventory():set_size("src", 1)
core.get_node_timer(pos):start(controller_interval)
end
local function controller_on_blast(pos)
@ -438,14 +316,31 @@ local function controller_on_blast(pos)
end
local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player)
if core.is_protected(pos, player:get_player_name()) then
if (player and core.is_protected(pos, player:get_player_name())) or listname ~= "src" then
return 0
end
if listname == "src" then
return stack:get_count()
local drawer_net_index = controller_get_drawer_index(pos, stack:get_name())
if drawer_net_index[stack:get_name()] then
local drawer = drawer_net_index[stack:get_name()]
if drawers.drawer_get_content(drawer.drawer_pos, drawer.visualid).name == stack:get_name() then
return drawers.drawer_can_insert_stack(drawer.drawer_pos, stack, drawer["visualid"])
end
end
if drawer_net_index["empty"] then
local drawer = drawer_net_index["empty"]
if drawers.drawer_get_content(drawer.drawer_pos, drawer.visualid).name == "" then
return drawers.drawer_can_insert_stack(drawer.drawer_pos, stack, drawer.visualid)
end
end
return 0
end
local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
local meta = core.get_meta(pos)
local inv = meta:get_inventory()
@ -460,6 +355,18 @@ local function controller_allow_metadata_inventory_take(pos, listname, index, st
return stack:get_count()
end
local function controller_on_metadata_inventory_put(pos, listname, index, stack, player)
if listname ~= "src" then
return
end
local inv = core.get_meta(pos):get_inventory()
local complete_stack = inv:get_stack("src", 1)
local leftover = controller_insert_to_drawers(pos, complete_stack)
inv:set_stack("src", 1, leftover)
end
local function controller_on_digiline_receive(pos, _, channel, msg)
local meta = core.get_meta(pos)
@ -468,21 +375,16 @@ local function controller_on_digiline_receive(pos, _, channel, msg)
end
local item = ItemStack(msg)
local itemstring = item:get_name()
local drawers_index = controller_get_drawer_index(pos, item:get_name())
local drawers_index = controller_get_drawer_index(pos, itemstring)
if not drawers_index[itemstring] then
if not drawers_index[item:get_name()] then
-- we can't do anything: the requested item doesn't exist
return
end
local drawer_data = drawers_index[itemstring]
local taken_stack = drawers.drawer_take_item(drawers_index[item:get_name()]["drawer_pos"], item)
local dir = core.facedir_to_dir(core.get_node(pos).param2)
local taken_stack = drawers.drawer_take_item(drawer_data["drawer_pos"], item)
local node = core.get_node(pos)
local dir = core.facedir_to_dir(node.param2)
pipeworks.tube_inject_item(pos, pos, dir, taken_stack:to_string())
end
@ -545,8 +447,8 @@ local function register_controller()
def.can_dig = controller_can_dig
def.on_construct = controller_on_construct
def.on_blast = controller_on_blast
def.on_timer = controller_node_timer
def.on_receive_fields = controller_on_receive_fields
def.on_metadata_inventory_put = controller_on_metadata_inventory_put
def.allow_metadata_inventory_put = controller_allow_metadata_inventory_put
def.allow_metadata_inventory_move = controller_allow_metadata_inventory_move
@ -558,16 +460,11 @@ local function register_controller()
def.tube = {}
def.tube.insert_object = function(pos, node, stack, tubedir)
-- add stack to inventory
local remaining_stack = core.get_meta(pos):get_inventory():add_item("src", stack)
-- kick off controller work
controller_node_timer(pos)
return remaining_stack
return controller_insert_to_drawers(pos, stack)
end
def.tube.can_insert = function(pos, node, stack, tubedir)
return core.get_meta(pos):get_inventory():room_for_item("src", stack)
return controller_allow_metadata_inventory_put(pos, "src", nil, stack, nil)
end
def.tube.connect_sides = {
@ -578,7 +475,7 @@ local function register_controller()
def.after_dig_node = pipeworks.after_dig
end
if digilines and pipeworks then
if digilines_loaded and pipeworks_loaded then
def.digiline = {
receptor = {},
effector = {

@ -208,6 +208,26 @@ function drawers.remove_visuals(pos)
end
end
--[[
Returns the visual object for the visualid of the drawer at pos.
visualid can be: "", "1", "2", ... or 1, 2, ...
]]
function drawers.get_visual(pos, visualid)
local drawer_visuals = drawers.drawer_visuals[core.serialize(pos)]
if not drawer_visuals then
return nil
end
-- not a real index (starts with 1)
local index = tonumber(visualid)
if visualid == "" then
index = 1
end
return drawer_visuals[index]
end
function drawers.update_drawer_upgrades(pos)
local node = core.get_node(pos)
local ndef = core.registered_nodes[node.name]

@ -276,57 +276,59 @@ core.register_entity("drawers:visual", {
return self:take_items(ItemStack(self.itemName):get_stack_max())
end,
try_insert_stack = function(self, itemstack, insert_stack)
can_insert_stack = function(self, stack)
if stack:get_name() == "" or stack:get_count() <= 0 then
return 0
end
-- don't allow unstackable stacks
if self.itemName == "" and stack:get_stack_max() ~= 1 then
return stack:get_count()
end
if self.itemName ~= stack:get_name() then
return 0
end
if (self.count + stack:get_count()) <= self.maxCount then
return stack:get_count()
end
return self.maxCount - self.count
end,
try_insert_stack = function(self, itemstack, insert_all)
local stackCount = itemstack:get_count()
local stackName = itemstack:get_name()
-- if nothing to be added, return
if stackCount <= 0 then return itemstack end
-- if no itemstring, return
if stackName == "" then return itemstack end
local insertCount = self:can_insert_stack(itemstack)
-- only add one, if player holding sneak key
if not insert_stack then
stackCount = 1
end
-- if current itemstring is not empty
if self.itemName ~= "" then
-- check if same item
if stackName ~= self.itemName then return itemstack end
else -- is empty
self.itemName = stackName
self.count = 0
-- get new stack max
self.itemStackMax = ItemStack(self.itemName):get_stack_max()
self.maxCount = self.itemStackMax * self.stackMaxFactor
end
-- Don't add items stackable only to 1
if self.itemStackMax == 1 then
self.itemName = ""
if insertCount == 0 then
return itemstack
end
-- set new counts:
-- if new count is more than max_count
if (self.count + stackCount) > self.maxCount then
itemstack:set_count(self.count + stackCount - self.maxCount)
self.count = self.maxCount
else -- new count fits
self.count = self.count + stackCount
-- this is for only removing one
itemstack:set_count(itemstack:get_count() - stackCount)
-- only add one, if player holding sneak key
if not insert_all then
insertCount = 1
end
-- update infotext, texture
-- in case the drawer was empty, initialize count, itemName, maxCount
if self.itemName == "" then
self.count = 0
self.itemName = itemstack:get_name()
self.maxCount = itemstack:get_stack_max() * self.stackMaxFactor
end
-- update everything
self.count = self.count + insertCount
self:updateInfotext()
self:updateTexture()
self:saveMetaData()
if itemstack:get_count() == 0 then itemstack = ItemStack("") end
-- return leftover
itemstack:take_item(insertCount)
if itemstack:get_count() == 0 then
return ItemStack("")
end
return itemstack
end,

@ -1 +0,0 @@
drawers_controller_interval (Drawer Controller update interval in seconds) float 1.0 0.1 10.0