Rewrite drawer controller scanning

This fixes the issue that some drawers have not been found by the
controller.
This commit is contained in:
Linus Jahn 2019-05-18 23:02:00 +02:00
parent 3a12b831a0
commit 885f8daf7b
No known key found for this signature in database
GPG Key ID: 4663231A91A1E27B
11 changed files with 563 additions and 444 deletions

@ -4,7 +4,7 @@ Version 0.4.3
License of source code: License of source code:
----------------------- -----------------------
Copyright (C) 2017 LNJ <git@lnj.li> Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2016 Mango Tango <mtango688@gmail.com> Copyright (C) 2016 Mango Tango <mtango688@gmail.com>
MIT License MIT License
@ -76,6 +76,9 @@ Copyright (C) 2014 Justin Aquadro (MIT):
textures/drawers_birch_wood_front_2.png textures/drawers_birch_wood_front_2.png
textures/drawers_birch_wood_front_4.png textures/drawers_birch_wood_front_4.png
textures/drawers_birch_wood.png textures/drawers_birch_wood.png
textures/drawers_controller_front.png
textures/drawers_controller_side.png
textures/drawers_controller_top.png
textures/drawers_dark_oak_wood_front_1.png textures/drawers_dark_oak_wood_front_1.png
textures/drawers_dark_oak_wood_front_2.png textures/drawers_dark_oak_wood_front_2.png
textures/drawers_dark_oak_wood_front_4.png textures/drawers_dark_oak_wood_front_4.png
@ -109,8 +112,6 @@ Copyright (C) 2014 Justin Aquadro (MIT):
textures/drawers_wood_front_2.png textures/drawers_wood_front_2.png
textures/drawers_wood_front_4.png textures/drawers_wood_front_4.png
textures/drawers_wood.png textures/drawers_wood.png
textures/drawer_controller_side.png
textures/drawer_controller_top_bottom.png
Everything not listed in here: Everything not listed in here:
Copyright (C) 2017 LNJ <git@lnj.li> (MIT) Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im> (MIT)

@ -17,7 +17,7 @@ MineClone 2 mods are only optional dependencies for crafting recipes.
- [x] Add 2x2 and 1x2 drawers - [x] Add 2x2 and 1x2 drawers
- [ ] Add compacting drawers for auto-crafting blocks/ingots/fragments - [ ] Add compacting drawers for auto-crafting blocks/ingots/fragments
- [ ] Add a key (or something similar) for locking the item (so the item is - [ ] Add a key (or something similar) for locking the item (so the item is
also displayed at count 0) also displayed at count 0)
- [ ] Add duct tape to transport drawers - [ ] Add duct tape to transport drawers
- [x] Support pipeworks - [x] Support pipeworks
- [ ] Support hoppers (needs hoppers mod change) - [ ] Support hoppers (needs hoppers mod change)
@ -40,5 +40,5 @@ alternatively you can also [email](mailto:git@lnj.li) me.
* [Minetest Forums](https://forum.minetest.net/viewtopic.php?f=9&t=17134) * [Minetest Forums](https://forum.minetest.net/viewtopic.php?f=9&t=17134)
* [Minetest Wiki](http://wiki.minetest.net/Mods/Storage_Drawers) * [Minetest Wiki](http://wiki.minetest.net/Mods/Storage_Drawers)
* [Weblate](https://hosted.weblate.org/projects/minetest/mod-storage-drawers/) * [Weblate](https://hosted.weblate.org/projects/minetest/mod-storage-drawers/)
* [GitHub](http://github.com/lnj2/drawers/) * [GitHub](http://github.com/minetest-mods/drawers/)

@ -1,7 +1,7 @@
--[[ --[[
Minetest Mod Storage Drawers - A Mod adding storage drawers Minetest Mod Storage Drawers - A Mod adding storage drawers
Copyright (C) 2017 LNJ <git@lnj.li> Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
MIT License MIT License
@ -51,6 +51,7 @@ drawers.enable_1x1 = not core.settings:get_bool("drawers_disable_1x1")
drawers.enable_1x2 = not core.settings:get_bool("drawers_disable_1x2") drawers.enable_1x2 = not core.settings:get_bool("drawers_disable_1x2")
drawers.enable_2x2 = not core.settings:get_bool("drawers_disable_2x2") drawers.enable_2x2 = not core.settings:get_bool("drawers_disable_2x2")
drawers.CONTROLLER_RANGE = 8
-- --
-- GUI -- GUI
@ -334,7 +335,7 @@ if core.get_modpath("mcl_core") and mcl_core then
core.register_node("drawers:trim", { core.register_node("drawers:trim", {
description = S("Wooden Trim"), description = S("Wooden Trim"),
tiles = {"drawers_trim.png"}, tiles = {"drawers_trim.png"},
groups = {handy = 1, axey = 1, flammable = 3, wood = 1, building_block = 1, material_wood = 1}, groups = {drawer_connector = 1, handy = 1, axey = 1, flammable = 3, wood = 1, building_block = 1, material_wood = 1},
_mcl_blast_resistance = 15, _mcl_blast_resistance = 15,
_mcl_hardness = 2, _mcl_hardness = 2,
}) })
@ -342,7 +343,7 @@ else
core.register_node("drawers:trim", { core.register_node("drawers:trim", {
description = S("Wooden Trim"), description = S("Wooden Trim"),
tiles = {"drawers_trim.png"}, tiles = {"drawers_trim.png"},
groups = {drawer = 1, choppy = 3, oddly_breakable_by_hand = 2}, groups = {drawer_connector = 1, choppy = 3, oddly_breakable_by_hand = 2},
}) })
end end

@ -1,7 +1,7 @@
--[[ --[[
Minetest Mod Storage Drawers - A Mod adding storage drawers Minetest Mod Storage Drawers - A Mod adding storage drawers
Copyright (C) 2017 LNJ <git@lnj.li> Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2016 Mango Tango <mtango688@gmail.com> Copyright (C) 2016 Mango Tango <mtango688@gmail.com>
MIT License MIT License

@ -1,433 +1,550 @@
--[[ --[[
Minetest Mod Storage Drawers - A Mod adding storage drawers Minetest Mod Storage Drawers - A Mod adding storage drawers
Copyright (C) 2018 isaiah658 Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
Copyright (C) 2017 LNJ <git@lnj.li> Copyright (C) 2018 isaiah658
MIT License MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
]]-- ]]--
--[[ The gist of how the drawers mod stores data is that there are entities --[[ The gist of how the drawers mod stores data is that there are entities
and the drawer node itself. The entities are needed to allow having multiple and the drawer node itself. The entities are needed to allow having multiple
drawers in one node. The entities and node each store metadata about the item drawers in one node. The entities and node each store metadata about the item
counts and such. It is necessary to change both at once otherwise in some cases counts and such. It is necessary to change both at once otherwise in some cases
the entity values are used and in other cases the node metadata is used. the entity values are used and in other cases the node metadata is used.
The gist of how the controller works is this. The drawer controller scans the The gist of how the controller works is this. The drawer controller scans the
adjacent tiles (length and height is configurable) and puts the item names and adjacent tiles and puts the item names and other info such as coordinates and
other info such as coordinates and the visualid of the entity in a table. That the visualid of the entity in a table. That table is saved in the controllers
table is saved in the controllers metadata. The table is used to help prevent metadata. The table is used to help prevent needing to scan all the drawers to
needing to scan all the drawers to deposit an item in certain situations. The deposit an item in certain situations. The table is only updated on an as needed
table is only updated on an as needed basis, not by a specific time/interval. basis, not by a specific time/interval. Controllers that have no items will not
Controllers that have no items will not continue scanning drawers. ]]-- continue scanning drawers. ]]--
-- Load support for intllib. -- Load support for intllib.
local MP = core.get_modpath(core.get_current_modname()) local MP = core.get_modpath(core.get_current_modname())
local S, NS = dofile(MP.."/intllib.lua") local S, NS = dofile(MP.."/intllib.lua")
local controller_interval = tonumber(core.setting_get("drawers_controller_interval")) or 7.0 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 function controller_can_dig(pos, player)
local meta = core.get_meta(pos); local controller_interval = tonumber(core.setting_get("drawers_controller_interval")) or 7.0
local inv = meta:get_inventory()
return inv:is_empty("src") local function controller_formspec(pos, meta_current_state)
end local formspec =
"size[8,8.5]"..
local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player) drawers.gui_bg..
if core.is_protected(pos, player:get_player_name()) then drawers.gui_bg_img..
return 0 drawers.gui_slots..
end "label[0,0;" .. S("Current State: ") .. meta_current_state .. "]" ..
if listname == "src" then "list[current_name;src;3.5,1.75;1,1;]"..
return stack:get_count() "list[current_player;main;0,4.25;8,1;]"..
end "list[current_player;main;0,5.5;8,3;8]"..
end "listring[current_player;main]"..
"listring[current_name;src]"..
local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) "listring[current_player;main]"
local meta = core.get_meta(pos) return formspec
local inv = meta:get_inventory() end
local stack = inv:get_stack(from_list, from_index)
return controller_allow_metadata_inventory_put(pos, to_list, to_index, stack, player) local function controller_index_slot(pos, visualid)
end return {
drawer_pos_x = pos.x,
local function controller_allow_metadata_inventory_take(pos, listname, index, stack, player) drawer_pos_y = pos.y,
if core.is_protected(pos, player:get_player_name()) then drawer_pos_z = pos.z,
return 0 visualid = visualid
end }
return stack:get_count() end
end
local function compare_pos(pos1, pos2)
local function controller_formspec(pos, meta_current_state) return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
local formspec = end
"size[8,8.5]"..
drawers.gui_bg.. local function contains_pos(list, p)
drawers.gui_bg_img.. for _,v in ipairs(list) do
drawers.gui_slots.. if compare_pos(v, p) then
"label[0,0;" .. S("Current State: ") .. meta_current_state .. "]" .. return true
"list[current_name;src;3.5,1.75;1,1;]".. end
"list[current_player;main;0,4.25;8,1;]".. end
"list[current_player;main;0,5.5;8,3;8]".. return false
"listring[current_player;main]".. end
"listring[current_name;src]"..
"listring[current_player;main]" -- iterator for iterating from 1 -> to
return formspec local function range(to)
end local i = 0
return function()
local function index_drawers(pos) if i == to then
--[[ The pos parameter is the controllers position return nil
end
We store the item name as a string key and the value is a table with position x, i = i + 1
position y, position z, and visualid. Those are all strings as well with the return i, i
values assigned to them that way we don't need to worry about the ordering of end
the table. The count and max count are not stored as those values have a high end
potential of being outdated quickly. It's better to grab the values from the
drawer when needed so you know you are working with accurate numbers. local function pos_in_range(pos1, pos2)
local diff = {
Indexing starts on the row (meaning same y coordinate) of the controller on the pos1.x - pos2.x,
adjacent sides and moves each column on the same y level. A row is searched until pos1.y - pos2.y,
it either hits the max length setting size or until a node that isn't a drawer is pos1.z - pos2.z
indexed and then moves up on (y coordinate increases by 1) and starts the process }
until the y coord is reaches the max height or until a node that isn't a drawer for _,v in ipairs(diff) do
is indexed and at the point it stops indexing all together. This makes it so all if v < 0 then
drawers need to be next to each other on the rows without spacing or other blocks v = v * -1
in between. ]]-- end
if v > drawers.CONTROLLER_RANGE then
local drawers_table_index = {} return false
local x_or_z_axis = 1 end
end
-- Variables for managing the max length and max height that is searched in each adjacent direction from the controller return true
-- These could potentially be exposed to the user through the formspec, allowed to bigger with upgrades, etc end
local max_search_length = 8
local max_search_height = 8 local function add_drawer_to_inventory(controllerInventory, pos)
-- the number of slots is saved as drawer group
-- Index the x axis and z axis in both the positive and negative directions local slots = core.get_item_group(core.get_node(pos).name, "drawer")
for x_z = 1,4 do if not slots then
for y = 1,max_search_height do return
for x_or_z = 1,max_search_length do end
-- x_z if for controlling which axis and direction is being searched
-- x_or_z is for the column in each row that is searched local meta = core.get_meta(pos)
-- x_or_z_axis is used for breaking out of the loop if the first block searched in a row is not a drawer if not meta then
x_or_z_axis = x_or_z return
local drawer_pos end
-- If x_z is 1, we check the positive x axis
if x_z == 1 then local i = 1
drawer_pos = {x = pos.x + x_or_z, y = pos.y + y - 1, z = pos.z} while i <= slots do
-- If x_z is 2, we check the negative x axis -- nothing is appended in case the drawer has only one slot
elseif x_z == 2 then local slot_id = ""
drawer_pos = {x = pos.x - x_or_z, y = pos.y + y - 1, z = pos.z} if slots ~= 1 then
-- If x_z is 3, we check the positive z axis slot_id = tostring(i)
elseif x_z == 3 then end
drawer_pos = {x = pos.x, y = pos.y + y - 1, z = pos.z + x_or_z}
-- If x_z is 4, we check the negative z axis local item_id = meta:get_string("name" .. slot_id)
elseif x_z == 4 then local drawer_meta_entity_infotext = meta:get_string("entity_infotext" .. slot_id)
drawer_pos = {x = pos.x, y = pos.y + y - 1, z = pos.z - x_or_z}
end if item_id == "" and not controllerInventory["empty"] then
local drawer_meta = core.get_meta(drawer_pos) controllerInventory["empty"] = controller_index_slot(pos, slot_id)
local drawer_node = core.get_node(drawer_pos) elseif item_id ~= "" then
-- If we already indexed this item previously, check which drawer
-- There might be a better way to know if the node is a drawer other than matching a string in the node name -- has the most space and have that one be the one indexed
-- Can't trust metadata only in case another mod has a block with the same metadata strings if controllerInventory[item_id] then
if string.match(drawer_node.name, 'drawers:') and drawer_node.name ~= "drawers:controller" and drawer_meta ~= nil then local indexed_drawer_meta = core.get_meta({
for i = 0,4 do x = controllerInventory[item_id]["drawer_pos_x"],
-- This is needed for the special case where drawers that store one item don't have an id appended to them y = controllerInventory[item_id]["drawer_pos_y"],
local visualid = i z = controllerInventory[item_id]["drawer_pos_z"]}
if i == 0 then )
visualid = "" local indexed_drawer_meta_count = indexed_drawer_meta:get_int(
end "count" .. controllerInventory[item_id]["visualid"])
local drawer_meta_name = drawer_meta:get_string("name" .. visualid) local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int(
local drawer_meta_entity_infotext = drawer_meta:get_string("entity_infotext" .. visualid) "max_count" .. controllerInventory[item_id]["visualid"])
-- Only one empty drawer needs to be indexed because everything is indexed again when an item isn't found in the index
if drawer_meta_name == "" and not drawers_table_index["empty"] and drawer_meta_entity_infotext ~= "" then local drawer_meta_count = meta:get_int("count" .. slot_id)
drawers_table_index["empty"] = {drawer_pos_x = drawer_pos.x, drawer_pos_y = drawer_pos.y, drawer_pos_z = drawer_pos.z, visualid = visualid} local drawer_meta_max_count = meta:get_int("max_count" .. slot_id)
elseif drawer_meta_name ~= "" then
-- If we already indexed this item previously, check which drawer has the most space and have that one be the one indexed -- If the already indexed drawer has less space, we override the table index for that item with the new drawer
if drawers_table_index[drawer_meta_name] then if (indexed_drawer_meta_max_count - indexed_drawer_meta_count)
local indexed_drawer_meta = core.get_meta({x = drawers_table_index[drawer_meta_name]["drawer_pos_x"], y = drawers_table_index[drawer_meta_name]["drawer_pos_y"], z = drawers_table_index[drawer_meta_name]["drawer_pos_z"]}) < (drawer_meta_max_count - drawer_meta_count) then
local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. drawers_table_index[drawer_meta_name]["visualid"]) controllerInventory[item_id] = controller_index_slot(pos, slot_id)
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. drawers_table_index[drawer_meta_name]["visualid"]) end
local drawer_meta_count = drawer_meta:get_int("count" .. visualid) else
local drawer_meta_max_count = drawer_meta:get_int("max_count" .. visualid) controllerInventory[item_id] = controller_index_slot(pos, slot_id)
-- If the already indexed drawer has less space, we override the table index for that item with the new drawer end
if indexed_drawer_meta_max_count - indexed_drawer_meta_count < drawer_meta_max_count - drawer_meta_count then end
drawers_table_index[drawer_meta_name] = {drawer_pos_x = drawer_pos.x, drawer_pos_y = drawer_pos.y, drawer_pos_z = drawer_pos.z, visualid = visualid}
end i = i + 1
else end
drawers_table_index[drawer_meta_name] = {drawer_pos_x = drawer_pos.x, drawer_pos_y = drawer_pos.y, drawer_pos_z = drawer_pos.z, visualid = visualid} end
end
-- If the drawer contained something and was a drawer type that only holds one item, stop the loop as there is no need to search through other drawer types local function find_connected_drawers(controller_pos, pos, foundPositions)
if i == 0 then foundPositions = foundPositions or {}
break pos = pos or controller_pos
end
end local newPositions = core.find_nodes_in_area(
end {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
-- If the node isn't a drawer or doesn't have metadata, we break the loop to stop searching the row {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1},
else {"group:drawer", "group:drawer_connector"}
break )
end
end for _,p in ipairs(newPositions) do
-- If we break out of the above loop while x or z is 1, it means the first block searched in a row did not contain a drawer. -- check that this node hasn't been scanned yet
-- All searching for an axis is stopped when a row starts with a non-drawer. if not compare_pos(pos, p) and not contains_pos(foundPositions, p)
if x_or_z_axis == 1 then and pos_in_range(controller_pos, pos) then
break -- add new position
end table.insert(foundPositions, p)
end -- search for other drawers from the new pos
end find_connected_drawers(controller_pos, p, foundPositions)
end
return drawers_table_index end
end
return foundPositions
local function controller_node_timer(pos, elapsed) end
-- Inizialize metadata
local meta = core.get_meta(pos) local function index_drawers(pos)
local meta_current_state = meta:get_string("current_state") --[[
local meta_times_ran_while_jammed = meta:get_float("times_ran_while_jammed") The pos parameter is the controllers position
local meta_jammed_item_name = meta:get_string("jammed_item_name")
local inv = meta:get_inventory() We store the item name as a string key and the value is a table with position x,
local src = inv:get_stack("src", 1) position y, position z, and visualid. Those are all strings as well with the
local src_name = src:get_name() values assigned to them that way we don't need to worry about the ordering of
the table. The count and max count are not stored as those values have a high
--[[ There are four scenarios for the item slot in the controller. potential of being outdated quickly. It's better to grab the values from the
1: No item is in the controller. drawer when needed so you know you are working with accurate numbers.
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 controllerInventory = {}
for _,drawerPos in ipairs(find_connected_drawers(pos)) do
There are three different possibilities for "current_state". add_drawer_to_inventory(controllerInventory, drawerPos)
1: "running" which means means it's operating normally. end
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. ]]-- return controllerInventory
end
--[[ 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 local function controller_node_timer(pos, elapsed)
set the current state to stopped. Will possibly want to make an option in the -- Inizialize metadata
formspec to ignore this an continue running if the user plans on using the local meta = core.get_meta(pos)
system in a way that may cause frequent jams making it a hassle to manually local meta_current_state = meta:get_string("current_state")
clear it each time ]]-- local meta_times_ran_while_jammed = meta:get_float("times_ran_while_jammed")
if meta_current_state == "jammed" and meta_jammed_item_name == src_name and meta_times_ran_while_jammed >= 8 then local meta_jammed_item_name = meta:get_string("jammed_item_name")
meta:set_string("current_state", "stopped") local inv = meta:get_inventory()
meta:set_string("formspec", controller_formspec(pos, S("Stopped"))) local src = inv:get_stack("src", 1)
return true local src_name = src:get_name()
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 There are four scenarios for the item slot in the controller.
if meta_current_state == "stopped" and meta_jammed_item_name == src_name then 1: No item is in the controller.
return true 2: Item is not stackable.
end 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.
-- 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 There are three different possibilities for "current_state".
meta:set_string("current_state", "running") 1: "running" which means means it's operating normally.
meta:set_string("formspec", controller_formspec(pos, S("Running"))) 2: "stopped" meaning the controller makes no attempt to put in the item possibly due to being unallowed for various reasons.
meta:set_float("times_ran_while_jammed", 0) 3: "jammed" meaning the item is allowed in to drawers, but there was no space to deposit it last time it ran.
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 If current state is jammed, the item that jammed it is the same item in the
meta:set_string("current_state", "running") src inv slot, and the amount of times ran while jammed is 8 or higher, we
meta:set_string("formspec", controller_formspec(pos, S("Running"))) set the current state to stopped. Will possibly want to make an option in the
meta:set_float("times_ran_while_jammed", 0) formspec to ignore this an continue running if the user plans on using the
return true system in a way that may cause frequent jams making it a hassle to manually
end clear it each time
]]
-- 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 meta_current_state == "jammed" and meta_jammed_item_name == src_name and meta_times_ran_while_jammed >= 2 then
if src:get_stack_max() == 1 then meta:set_string("current_state", "stopped")
meta:set_string("current_state", "stopped") meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
meta:set_string("formspec", controller_formspec(pos, S("Stopped"))) return true
meta:set_string("jammed_item_name", src_name) end
meta:set_float("times_ran_while_jammed", 1)
return true -- If current state is stopped, and the item that jammed it is the same
end -- item in the src inv slot, we don't do anything
if meta_current_state == "stopped" and meta_jammed_item_name == src_name then
-- 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 return true
local drawers_table_index = core.deserialize(meta:get_string("drawers_table_index")) end
-- If the index has not been created
if not drawers_table_index then -- If current state is stopped, and the item that jammed it is not the
drawers_table_index = index_drawers(pos) -- same item in the src inv slot, we set the current state to running and
meta:set_string("drawers_table_index", core.serialize(drawers_table_index)) -- clear the jam counter.
-- If the item isn't in the index if meta_current_state == "stopped" and meta_jammed_item_name ~= src_name then
elseif not drawers_table_index[src_name] then meta:set_string("current_state", "running")
drawers_table_index = index_drawers(pos) meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_string("drawers_table_index", core.serialize(drawers_table_index)) meta:set_float("times_ran_while_jammed", 0)
-- 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 end
elseif drawers_table_index[src_name] then
local visualid = drawers_table_index[src_name]["visualid"] -- If no item is in the controller, nothing is searched and current_state
local indexed_drawer_meta = core.get_meta({x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]}) -- is set to running and no jams.
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid) if inv:is_empty("src") then
local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid) meta:set_string("current_state", "running")
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid) meta:set_string("formspec", controller_formspec(pos, S("Running")))
if indexed_drawer_meta_name ~= src_name or indexed_drawer_meta_count >= indexed_drawer_meta_max_count then meta:set_float("times_ran_while_jammed", 0)
drawers_table_index = index_drawers(pos) return true
meta:set_string("drawers_table_index", core.serialize(drawers_table_index)) end
end
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
-- 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 src:get_stack_max() == 1 then
-- If a race condition does occur, items could be lost or duplicated meta:set_string("current_state", "stopped")
src = inv:get_stack("src", 1) meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
src_name = src:get_name() meta:set_string("jammed_item_name", src_name)
local src_count = src:get_count() meta:set_float("times_ran_while_jammed", 1)
local src_stack_max = src:get_stack_max() return true
end
-- 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 the index has not been created, the item isn't in the index, the
if drawers_table_index[src_name] then -- item in the drawer is no longer the same item in the index, or the item
local indexed_drawer_pos = {x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]} -- is in the index but it's full, run the index_drawers function.
local visualid = drawers_table_index[src_name]["visualid"] local drawers_table_index = core.deserialize(meta:get_string("drawers_table_index"))
local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid) -- If the index has not been created
local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid) if not drawers_table_index then
local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid) drawers_table_index = index_drawers(pos)
-- 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 meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
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) -- If the item isn't in the index
inv:set_stack("src", 1, leftover) elseif not drawers_table_index[src_name] then
-- Set the controller metadata drawers_table_index = index_drawers(pos)
meta:set_string("current_state", "running") meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
meta:set_string("formspec", controller_formspec(pos, S("Running")))
meta:set_float("times_ran_while_jammed", 0) -- If the item is in the index but either the name that was indexed is not
else -- the same as what is currently in the drawer or the drawer is full
meta:set_string("current_state", "jammed") elseif drawers_table_index[src_name] then
meta:set_string("formspec", controller_formspec(pos, S("Jammed"))) local visualid = drawers_table_index[src_name]["visualid"]
meta:set_string("jammed_item_name", src_name) local indexed_drawer_meta = core.get_meta({x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]})
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1) local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
end local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
elseif drawers_table_index["empty"] then local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
local indexed_drawer_pos = {x = drawers_table_index["empty"]["drawer_pos_x"], y = drawers_table_index["empty"]["drawer_pos_y"], z = drawers_table_index["empty"]["drawer_pos_z"]} if indexed_drawer_meta_name ~= src_name or indexed_drawer_meta_count >= indexed_drawer_meta_max_count then
local visualid = drawers_table_index["empty"]["visualid"] drawers_table_index = index_drawers(pos)
local indexed_drawer_meta = core.get_meta(indexed_drawer_pos) meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid) end
-- If the drawer is still empty and the drawer entity is loaded, we will put the items in the drawer end
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) -- This might not be needed, but my concern is if the above indexing takes
inv:set_stack("src", 1, leftover) -- enough time, there could be a "race condition" where the item in the src
-- Add the item to the drawers table index and set the empty one to nil -- inventory is no longer the same item when we checked before or the
drawers_table_index["empty"] = nil -- quantity of the items changed so I'm having it grab the item stack again
drawers_table_index[src_name] = {drawer_pos_x = indexed_drawer_pos.x, drawer_pos_y = indexed_drawer_pos.y, drawer_pos_z = indexed_drawer_pos.z, visualid = visualid} -- just in case.
-- Set the controller metadata -- If a race condition does occur, items could be lost or duplicated
meta:set_string("current_state", "running") src = inv:get_stack("src", 1)
meta:set_string("formspec", controller_formspec(pos, S("Running"))) src_name = src:get_name()
meta:set_float("times_ran_while_jammed", 0) local src_count = src:get_count()
meta:set_string("drawers_table_index", core.serialize(drawers_table_index)) local src_stack_max = src:get_stack_max()
else
meta:set_string("current_state", "jammed") -- At this point, the item either was in the index or everything was reindexed so we check again
meta:set_string("formspec", controller_formspec(pos, S("Jammed"))) -- If there is a drawer with the item and it isn't full, we will put the items we can in to it
meta:set_string("jammed_item_name", src_name) if drawers_table_index[src_name] then
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1) local indexed_drawer_pos = {x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]}
end local visualid = drawers_table_index[src_name]["visualid"]
else local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
meta:set_string("current_state", "jammed") local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
meta:set_string("formspec", controller_formspec(pos, S("Jammed"))) local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
meta:set_string("jammed_item_name", src_name) local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1) -- 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
end 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)
return true inv:set_stack("src", 1, leftover)
end -- Set the controller metadata
meta:set_string("current_state", "running")
-- Set the controller definition using a table to allow for pipeworks and potentially other mod support meta:set_string("formspec", controller_formspec(pos, S("Running")))
local controller_def = {} meta:set_float("times_ran_while_jammed", 0)
-- MCL2 requires a few different groups and parameters that default does not else
if core.get_modpath("mcl_core") and mcl_core then meta:set_string("current_state", "jammed")
controller_def.groups = {pickaxey = 1, stone = 1, building_block = 1, material_stone = 1} meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
controller_def._mcl_blast_resistance = 30 meta:set_string("jammed_item_name", src_name)
controller_def._mcl_hardness = 1.5 meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
else end
controller_def.groups = {cracky = 3, level = 2} elseif drawers_table_index["empty"] then
end local indexed_drawer_pos = {x = drawers_table_index["empty"]["drawer_pos_x"], y = drawers_table_index["empty"]["drawer_pos_y"], z = drawers_table_index["empty"]["drawer_pos_z"]}
controller_def.description = S("Drawer Controller") local visualid = drawers_table_index["empty"]["visualid"]
controller_def.tiles = {"drawer_controller_top_bottom.png", "drawer_controller_top_bottom.png", "drawer_controller_side.png", "drawer_controller_side.png", "drawer_controller_side.png", "drawer_controller_side.png"} local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
controller_def.can_dig = controller_can_dig local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
controller_def.on_construct = function(pos) -- If the drawer is still empty and the drawer entity is loaded, we will put the items in the drawer
local meta = core.get_meta(pos) if indexed_drawer_meta_name == "" and drawers.drawer_visuals[core.serialize(indexed_drawer_pos)] then
local inv = meta:get_inventory() local leftover = drawers.drawer_insert_object(indexed_drawer_pos, nil, src, nil)
inv:set_size('src', 1) inv:set_stack("src", 1, leftover)
meta:set_string("current_state", "running") -- Add the item to the drawers table index and set the empty one to nil
meta:set_float("times_ran_while_jammed", 0) drawers_table_index["empty"] = nil
meta:set_string("jammed_item_name", "") drawers_table_index[src_name] = {drawer_pos_x = indexed_drawer_pos.x, drawer_pos_y = indexed_drawer_pos.y, drawer_pos_z = indexed_drawer_pos.z, visualid = visualid}
meta:set_string("drawers_table_index", "") -- Set the controller metadata
meta:set_string("formspec", controller_formspec(pos, S("Running"))) meta:set_string("current_state", "running")
local timer = core.get_node_timer(pos) meta:set_string("formspec", controller_formspec(pos, S("Running")))
timer:start(controller_interval) meta:set_float("times_ran_while_jammed", 0)
end meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
controller_def.on_blast = function(pos) else
local drops = {} meta:set_string("current_state", "jammed")
default.get_inventory_drops(pos, "src", drops) meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
drops[#drops+1] = "drawers:controller" meta:set_string("jammed_item_name", src_name)
core.remove_node(pos) meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
return drops end
end else
controller_def.on_timer = controller_node_timer meta:set_string("current_state", "jammed")
controller_def.allow_metadata_inventory_put = controller_allow_metadata_inventory_put meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
controller_def.allow_metadata_inventory_move = controller_allow_metadata_inventory_move meta:set_string("jammed_item_name", src_name)
controller_def.allow_metadata_inventory_take = controller_allow_metadata_inventory_take meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
end
-- Mostly copied from the drawers in the drawer mod to add pipeworks support
if core.get_modpath("pipeworks") and pipeworks then return true
controller_def.groups.tubedevice = 1 end
controller_def.groups.tubedevice_receiver = 1
controller_def.tube = controller_def.tube or {} local function controller_can_dig(pos, player)
controller_def.tube.insert_object = function(pos, node, stack, tubedir) local meta = core.get_meta(pos);
local meta = core.get_meta(pos) local inv = meta:get_inventory()
local inv = meta:get_inventory() return inv:is_empty("src")
return inv:add_item("src", stack) end
end
controller_def.tube.can_insert = function(pos, node, stack, tubedir) local function controller_on_construct(pos)
local meta = core.get_meta(pos) local meta = core.get_meta(pos)
local inv = meta:get_inventory() meta:set_string("current_state", "running")
return inv:room_for_item("src", stack) meta:set_float("times_ran_while_jammed", 0)
end meta:set_string("jammed_item_name", "")
controller_def.tube.connect_sides = {left = 1, right = 1, back = 1, front = 1, meta:set_string("drawers_table_index", "")
top = 1, bottom = 1} meta:set_string("formspec", controller_formspec(pos, S("Running")))
controller_def.after_place_node = pipeworks.after_place
controller_def.after_dig_node = pipeworks.after_dig meta:get_inventory():set_size("src", 1)
end
core.get_node_timer(pos):start(controller_interval)
core.register_node('drawers:controller', controller_def) end
-- Because the rest of the drawers mod doesn't have a hard depend on default, I changed the recipe to have an alternative local function controller_on_blast(pos)
if core.get_modpath("default") and default then local drops = {}
core.register_craft({ default.get_inventory_drops(pos, "src", drops)
output = 'drawers:controller', drops[#drops+1] = "drawers:controller"
recipe = { core.remove_node(pos)
{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'}, return drops
{'default:tin_ingot', 'group:drawer', 'default:copper_ingot'}, end
{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
} local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player)
}) if core.is_protected(pos, player:get_player_name()) then
elseif core.get_modpath("mcl_core") and mcl_core then return 0
core.register_craft({ end
output = 'drawers:controller', if listname == "src" then
recipe = { return stack:get_count()
{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'}, end
{'mcl_core:gold_ingot', 'group:drawer', 'mcl_core:gold_ingot'}, end
{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
} local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
}) local meta = core.get_meta(pos)
else local inv = meta:get_inventory()
core.register_craft({ local stack = inv:get_stack(from_list, from_index)
output = 'drawers:controller', return controller_allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
recipe = { end
{'group:stone', 'group:stone', 'group:stone'},
{'group:stone', 'group:drawer', 'group:stone'}, local function controller_allow_metadata_inventory_take(pos, listname, index, stack, player)
{'group:stone', 'group:stone', 'group:stone'}, if core.is_protected(pos, player:get_player_name()) then
} return 0
}) end
end return stack:get_count()
end
-- Registers the drawer controller
local function register_controller()
-- Set the controller definition using a table to allow for pipeworks and
-- potentially other mod support
local def = {}
def.description = S("Drawer Controller")
def.drawtype = "nodebox"
def.node_box = { type = "fixed", fixed = drawers.node_box_simple }
def.collision_box = { type = "regular" }
def.selection_box = { type = "regular" }
def.paramtype = "light"
def.paramtype2 = "facedir"
def.legacy_facedir_simple = true
-- add pipe connectors, if pipeworks is enabled
if pipeworks_loaded then
def.tiles = {
"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
"drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
"drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
"drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
"drawers_controller_front.png"
}
else
def.tiles = {
"drawers_controller_top.png",
"drawers_controller_top.png",
"drawers_controller_side.png",
"drawers_controller_side.png",
"drawers_controller_top.png",
"drawers_controller_front.png"
}
end
-- MCL2 requires a few different groups and parameters that MTG does not
if mcl_loaded then
def.groups = {
pickaxey = 1, stone = 1, building_block = 1, material_stone = 1
}
def._mcl_blast_resistance = 30
def._mcl_hardness = 1.5
else
def.groups = {
cracky = 3, level = 2
}
end
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.allow_metadata_inventory_put = controller_allow_metadata_inventory_put
def.allow_metadata_inventory_move = controller_allow_metadata_inventory_move
def.allow_metadata_inventory_take = controller_allow_metadata_inventory_take
if pipeworks_loaded then
def.groups.tubedevice = 1
def.groups.tubedevice_receiver = 1
def.tube = {}
def.tube.insert_object = function(pos, node, stack, tubedir)
return core.get_meta(pos):get_inventory():add_item("src", stack)
end
def.tube.can_insert = function(pos, node, stack, tubedir)
return core.get_meta(pos):get_inventory():room_for_item("src", stack)
end
def.tube.connect_sides = {
left = 1, right = 1, back = 1, top = 1, bottom = 1
}
def.after_place_node = pipeworks.after_place
def.after_dig_node = pipeworks.after_dig
end
core.register_node("drawers:controller", def)
end
-- register drawer controller
register_controller()
if default_loaded then
core.register_craft({
output = 'drawers:controller',
recipe = {
{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
{'default:tin_ingot', 'group:drawer', 'default:copper_ingot'},
{'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
}
})
elseif mcl_loaded then
core.register_craft({
output = 'drawers:controller',
recipe = {
{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
{'mcl_core:gold_ingot', 'group:drawer', 'mcl_core:gold_ingot'},
{'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
}
})
else
-- Because the rest of the drawers mod doesn't have a hard depend on
-- default, I changed the recipe to have an alternative
core.register_craft({
output = 'drawers:controller',
recipe = {
{'group:stone', 'group:stone', 'group:stone'},
{'group:stone', 'group:drawer', 'group:stone'},
{'group:stone', 'group:stone', 'group:stone'},
}
})
end

@ -1,7 +1,7 @@
--[[ --[[
Minetest Mod Storage Drawers - A Mod adding storage drawers Minetest Mod Storage Drawers - A Mod adding storage drawers
Copyright (C) 2017 LNJ <git@lnj.li> Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
MIT License MIT License

Binary file not shown.

Before

Width:  |  Height:  |  Size: 577 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B