From 62b5fa51ae0bbb8a2375a8bc920b4fec2b9907c2 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Tue, 9 Jun 2020 20:01:17 +0200 Subject: [PATCH] Large drawer controller refactoring --- lua/api.lua | 57 ++++++++- lua/controller.lua | 279 ++++++++++++++------------------------------- lua/helpers.lua | 20 ++++ lua/visual.lua | 78 +++++++------ settingtypes.txt | 1 - 5 files changed, 199 insertions(+), 236 deletions(-) diff --git a/lua/api.lua b/lua/api.lua index 4fe31df..ea1e150 100755 --- a/lua/api.lua +++ b/lua/api.lua @@ -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} diff --git a/lua/controller.lua b/lua/controller.lua index 0e726a6..322e7a1 100644 --- a/lua/controller.lua +++ b/lua/controller.lua @@ -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,12 +316,29 @@ 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) @@ -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 = { diff --git a/lua/helpers.lua b/lua/helpers.lua index 8ee47b7..61078bd 100755 --- a/lua/helpers.lua +++ b/lua/helpers.lua @@ -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] diff --git a/lua/visual.lua b/lua/visual.lua index bb01751..3510723 100755 --- a/lua/visual.lua +++ b/lua/visual.lua @@ -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, diff --git a/settingtypes.txt b/settingtypes.txt index a42e370..e69de29 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -1 +0,0 @@ -drawers_controller_interval (Drawer Controller update interval in seconds) float 1.0 0.1 10.0