diff --git a/locale/drawers.be.tr b/locale/drawers.be.tr index 8baecb4..ccd4d08 100644 --- a/locale/drawers.be.tr +++ b/locale/drawers.be.tr @@ -6,6 +6,7 @@ Empty=Пустая Drawer Controller=Кантролер шуфляды Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% поўны) @1 @2 (@3% full)=@1 @2 (@3% поўны) Acacia Wood=Драўніна акацыі diff --git a/locale/drawers.cs.tr b/locale/drawers.cs.tr index 7db5087..3db69d4 100644 --- a/locale/drawers.cs.tr +++ b/locale/drawers.cs.tr @@ -6,6 +6,7 @@ Empty=Prázdný Drawer Controller= Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (z @2 % plný) @1 @2 (@3% full)=@1 @2 (z @3 % plný) Acacia Wood=ze dřeva akácie diff --git a/locale/drawers.de.tr b/locale/drawers.de.tr index 6536f78..dff6cc2 100644 --- a/locale/drawers.de.tr +++ b/locale/drawers.de.tr @@ -6,6 +6,7 @@ Empty=Leer Drawer Controller=Schubfach Steuerung Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% voll) @1 @2 (@3% full)=@1 @2 (@3% voll) Acacia Wood=Akazienholz diff --git a/locale/drawers.el.tr b/locale/drawers.el.tr index 770fbc0..9c8c17b 100644 --- a/locale/drawers.el.tr +++ b/locale/drawers.el.tr @@ -6,6 +6,7 @@ Empty=Κενό Drawer Controller=Ελεγκτής συρταριού Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% Πλήρης) @1 @2 (@3% full)= Acacia Wood=Ξύλο ακακίας diff --git a/locale/drawers.eo.tr b/locale/drawers.eo.tr index cffceab..2aeb435 100644 --- a/locale/drawers.eo.tr +++ b/locale/drawers.eo.tr @@ -6,6 +6,7 @@ Empty=Malplena Drawer Controller=Regilo de tirkesto Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (plena je @2%) @1 @2 (@3% full)=@1 @2 (plena je @3%) Acacia Wood=Akacia ligno diff --git a/locale/drawers.es.tr b/locale/drawers.es.tr index 156d8bd..434768a 100644 --- a/locale/drawers.es.tr +++ b/locale/drawers.es.tr @@ -6,6 +6,7 @@ Empty=Vacío Drawer Controller=Controlador de Gavetas Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)= @1 @2 (@3% full)= Acacia Wood=Madera de acacia diff --git a/locale/drawers.et.tr b/locale/drawers.et.tr index b791618..a491770 100644 --- a/locale/drawers.et.tr +++ b/locale/drawers.et.tr @@ -6,6 +6,7 @@ Empty=Tühi Drawer Controller=Salve juhtpult Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% täis) @1 @2 (@3% full)=@1 @2 (@3% täis) Acacia Wood=Akaatsia puidust diff --git a/locale/drawers.fr.tr b/locale/drawers.fr.tr index 2c38b0f..d84d3ea 100644 --- a/locale/drawers.fr.tr +++ b/locale/drawers.fr.tr @@ -6,6 +6,7 @@ Empty=Vide Drawer Controller=Contrôleur de Tiroirs Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% plein) @1 @2 (@3% full)=@1 @2 (@3% plein) Acacia Wood=Bois d'acacia diff --git a/locale/drawers.id.tr b/locale/drawers.id.tr index 6e839c7..68465cd 100644 --- a/locale/drawers.id.tr +++ b/locale/drawers.id.tr @@ -6,6 +6,7 @@ Empty=Kosong Drawer Controller=Pengendali Laci Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% penuh) @1 @2 (@3% full)=@1 @2 (@3% penuh) Acacia Wood=Kayu Akasia diff --git a/locale/drawers.it.tr b/locale/drawers.it.tr index 7ca1652..7c8a0e5 100644 --- a/locale/drawers.it.tr +++ b/locale/drawers.it.tr @@ -6,6 +6,7 @@ Empty=Vuoto Drawer Controller= Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% pieno) @1 @2 (@3% full)=@1 @2 (@3% pieno) Acacia Wood=Legno di acacia diff --git a/locale/drawers.ms.tr b/locale/drawers.ms.tr index 01ae4bf..c683731 100644 --- a/locale/drawers.ms.tr +++ b/locale/drawers.ms.tr @@ -6,6 +6,7 @@ Empty=Kosong Drawer Controller=Pengawal Laci Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% penuh) @1 @2 (@3% full)=@1 @2 (@3% penuh) Acacia Wood=Papan Kayu Akasia diff --git a/locale/drawers.nb.tr b/locale/drawers.nb.tr index 7183b81..15ef0cf 100644 --- a/locale/drawers.nb.tr +++ b/locale/drawers.nb.tr @@ -6,6 +6,7 @@ Empty=Tom Drawer Controller=Skuffekontrollør Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% full) @1 @2 (@3% full)=@1 @2 (@3% full) Acacia Wood=Akasie diff --git a/locale/drawers.nl.tr b/locale/drawers.nl.tr index a76b0db..beb6a87 100644 --- a/locale/drawers.nl.tr +++ b/locale/drawers.nl.tr @@ -6,6 +6,7 @@ Empty=Leeg Drawer Controller= Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% vol) @1 @2 (@3% full)=@1 @2 (@3% vol) Acacia Wood=Acacia Hout diff --git a/locale/drawers.pl.tr b/locale/drawers.pl.tr index dfe234a..829d1e5 100644 --- a/locale/drawers.pl.tr +++ b/locale/drawers.pl.tr @@ -6,6 +6,7 @@ Empty=Pusta Drawer Controller=Kontroler szuflady Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% napełnione) @1 @2 (@3% full)=@1 @2 (@3% napełnione) Acacia Wood=Drewno akacjowe diff --git a/locale/drawers.pt.tr b/locale/drawers.pt.tr index 73a38b7..4de356e 100644 --- a/locale/drawers.pt.tr +++ b/locale/drawers.pt.tr @@ -6,6 +6,7 @@ Empty=Vazio Drawer Controller=Controlador de Armários Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% cheio) @1 @2 (@3% full)=@1 @2 (@3% cheio) Acacia Wood=Lenha de Acácia diff --git a/locale/drawers.pt_BR.tr b/locale/drawers.pt_BR.tr index 73a38b7..4de356e 100644 --- a/locale/drawers.pt_BR.tr +++ b/locale/drawers.pt_BR.tr @@ -6,6 +6,7 @@ Empty=Vazio Drawer Controller=Controlador de Armários Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (@2% cheio) @1 @2 (@3% full)=@1 @2 (@3% cheio) Acacia Wood=Lenha de Acácia diff --git a/locale/drawers.ru.tr b/locale/drawers.ru.tr index e865058..e0e7848 100644 --- a/locale/drawers.ru.tr +++ b/locale/drawers.ru.tr @@ -6,6 +6,7 @@ Empty=Пусто Drawer Controller=Контролер ящиков Digiline Channel=Канал digiline Save=Сохранить +Drawer Distribution= @1 (@2% full)=@1 (заполнен на @2%) @1 @2 (@3% full)=@1 @2 (заполнен на @3%) Acacia Wood=акации diff --git a/locale/drawers.sv.tr b/locale/drawers.sv.tr index e12ba8e..ce65d85 100644 --- a/locale/drawers.sv.tr +++ b/locale/drawers.sv.tr @@ -6,6 +6,7 @@ Empty=Tom Drawer Controller=Lådkontroll Digiline Channel=Digilinekanal Save=Spara +Drawer Distribution= @1 (@2% full)=@1 (@2% fullt) @1 @2 (@3% full)=@1 @2 (@3% fullt) Acacia Wood=Acaciaträ diff --git a/locale/drawers.tr.tr b/locale/drawers.tr.tr index bbcfb55..781fac1 100644 --- a/locale/drawers.tr.tr +++ b/locale/drawers.tr.tr @@ -6,6 +6,7 @@ Empty=Boş Drawer Controller=Çekmece Denetleyicisi Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)=@1 (%@2 dolu) @1 @2 (@3% full)=@1 @2 (%@3 dolu) Acacia Wood=Akasya Ahşap diff --git a/locale/drawers.uk.tr b/locale/drawers.uk.tr index fac86fd..f6ca809 100644 --- a/locale/drawers.uk.tr +++ b/locale/drawers.uk.tr @@ -6,6 +6,7 @@ Empty=Пусто Drawer Controller=Контролер Шухляд Digiline Channel=Канал Digiline Save=Зберегти +Drawer Distribution= @1 (@2% full)=@1 (заповнена на @2%) @1 @2 (@3% full)=@2 x@1 (заповнена на @3%) Acacia Wood=акації diff --git a/locale/drawers.zh_CN.tr b/locale/drawers.zh_CN.tr index 61f6f4f..845339d 100644 --- a/locale/drawers.zh_CN.tr +++ b/locale/drawers.zh_CN.tr @@ -6,6 +6,7 @@ Empty=空的 Drawer Controller= Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)= @1 @2 (@3% full)= Acacia Wood=金合欢木 diff --git a/locale/template.txt b/locale/template.txt index 879054d..d6852a8 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -6,6 +6,7 @@ Empty= Drawer Controller= Digiline Channel= Save= +Drawer Distribution= @1 (@2% full)= @1 @2 (@3% full)= Acacia Wood= diff --git a/lua/controller.lua b/lua/controller.lua index 2fda667..cbd5815 100644 --- a/lua/controller.lua +++ b/lua/controller.lua @@ -46,41 +46,19 @@ local pipeworks_loaded = core.get_modpath("pipeworks") and pipeworks local digilines_loaded = core.get_modpath("digilines") and digilines local techage_loaded = core.get_modpath("techage") and techage -local function controller_formspec(pos) - local formspec = - "size[9,8.5]".. - drawers.gui_bg.. - drawers.gui_slots.. - "label[0,0;" .. S("Drawer Controller") .. "]" .. - "list[current_name;src;4,1.75;1,1;]".. - drawers.inventory_list(4.25) .. - "listring[current_player;main]".. - "listring[current_name;src]".. - "listring[current_player;main]" - - 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") .. "]" +local function is_valid_drawer_index_slot(net_index, item_name) + local item_index = net_index and net_index[item_name] + if not (item_index and item_index.drawer_positions and item_index.visualid) then + return false end - return formspec -end - -local function is_valid_drawer_index_slot(net_index, item_name) - return net_index and - net_index[item_name] and - net_index[item_name].drawer_pos and - net_index[item_name].drawer_pos.x and - net_index[item_name].drawer_pos.y and - net_index[item_name].drawer_pos.z and - net_index[item_name].visualid -end + for _, position in ipairs(item_index.drawer_positions) do + if not (position.x and position.y and position.z) then + return false + end + end -local function controller_index_slot(pos, visualid) - return { - drawer_pos = pos, - visualid = visualid - } + return true end local function compare_pos(pos1, pos2) @@ -148,24 +126,10 @@ local function add_drawer_to_inventory(controllerInventory, pos) local item_id = meta:get_string("name" .. slot_id) local drawer_meta_entity_infotext = meta:get_string("entity_infotext" .. slot_id) - if item_id == "" and not controllerInventory["empty"] then - controllerInventory["empty"] = controller_index_slot(pos, slot_id) - elseif item_id ~= "" then - -- 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 content = drawers.drawer_get_content(controllerInventory[item_id].drawer_pos, controllerInventory[item_id].visualid) - local new_content = drawers.drawer_get_content(pos, slot_id) - - -- 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 - controllerInventory[item_id] = controller_index_slot(pos, slot_id) - end - end + -- keep track of each instance of an item (or lack thereof) + local key = (item_id == "" and "empty") or item_id + controllerInventory[key] = controllerInventory[key] or {} + table.insert(controllerInventory[key], {drawer_pos=pos, visualid=slot_id}) i = i + 1 end @@ -217,32 +181,34 @@ end --[[ Returns a table of all stored itemstrings in the drawer network with their - drawer position and visualid. + drawer positions and visualid. It uses the cached data, if possible, but if the itemstring is not contained the network is reindexed. ]] local function controller_get_drawer_index(pos, itemstring) local meta = core.get_meta(pos) + local drawer_net_index = core.deserialize(meta:get_string("drawers_table_index")) -- 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 drawer_net_index = core.deserialize(meta:get_string("drawers_table_index")) - - -- If the index has not been created - -- If the item isn't in the index (or the index is corrupted) if not is_valid_drawer_index_slot(drawer_net_index, itemstring) then drawer_net_index = index_drawers(pos) meta:set_string("drawers_table_index", core.serialize(drawer_net_index)) -- There is a valid entry in the index: check that the entry is still up-to-date else - local content = drawers.drawer_get_content( - drawer_net_index[itemstring].drawer_pos, - drawer_net_index[itemstring].visualid) + local up_to_date = true + for _, index in ipairs(drawer_net_index[itemstring]) do + local content = drawers.drawer_get_content(index.drawer_pos, index.visualid) + if content.name ~= itemstring or content.count >= content.maxCount then + up_to_date = false + break + end + end - if content.name ~= itemstring or content.count >= content.maxCount then + if not up_to_date then drawer_net_index = index_drawers(pos) meta:set_string("drawers_table_index", core.serialize(drawer_net_index)) end @@ -256,45 +222,90 @@ local function controller_insert_to_drawers(pos, stack) local meta = core.get_meta(pos) local inv = meta:get_inventory() - local drawer_net_index = controller_get_drawer_index(pos, stack:get_name()) - - -- 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 content.name == stack:get_name() and - content.count < content.maxCount and - drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then - return drawers.drawer_insert_object(drawer_pos, stack, visualid) + local item_name = stack:get_name() + local drawer_net_index = controller_get_drawer_index(pos, item_name) + local leftover = ItemStack(stack) + local distribute = meta:get_string("distribute") == "true" + + -- Fill partially filled drawers first + if drawer_net_index[item_name] then + for _, drawer in ipairs(drawer_net_index[item_name]) do + local drawer_pos = drawer.drawer_pos + local visualid = drawer.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 + local content = drawers.drawer_get_content(drawer_pos, visualid) + if content.name == item_name and + content.count < content.maxCount and + drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then + leftover = drawers.drawer_insert_object(drawer_pos, leftover, visualid) + if leftover:is_empty() or not distribute then + break + end + end end - 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) + end - -- If the drawer is still empty and the drawer entity is loaded, we will - -- put the items in the drawer - if content.name == "" and drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then - local leftover = drawers.drawer_insert_object(drawer_pos, stack, visualid) + -- Fill empty drawers if any leftover remains + if not leftover:is_empty() and drawer_net_index["empty"] then + local i = 1 + while i <= #drawer_net_index["empty"] do + local drawer = drawer_net_index["empty"][i] + local drawer_pos = drawer.drawer_pos + local visualid = drawer.visualid + + -- If the drawer is still empty and the drawer entity is loaded, we + -- will put the items in the drawer + local content = drawers.drawer_get_content(drawer_pos, visualid) + if content.name == "" and drawers.drawer_visuals[core.hash_node_position(drawer_pos)] then + leftover = drawers.drawer_insert_object(drawer_pos, leftover, visualid) + + -- Add the item to the drawers table index and remove the empty one + table.remove(drawer_net_index["empty"], i) + drawer_net_index[item_name] = drawer_net_index[item_name] or {} + table.insert(drawer_net_index[item_name], {drawer_pos=drawer_pos, visualid=visualid}) + + if leftover:is_empty() or not distribute then + break + end + else + i = i + 1 + end + end - -- Add the item to the drawers table index and set the empty one to nil - drawer_net_index["empty"] = nil - drawer_net_index[stack:get_name()] = controller_index_slot(drawer_pos, visualid) + -- Set the controller metadata + meta:set_string("drawers_table_index", core.serialize(drawer_net_index)) + end - -- Set the controller metadata - meta:set_string("drawers_table_index", core.serialize(drawer_net_index)) + return leftover +end - return leftover - end +local function controller_update_formspec(pos) + local meta = core.get_meta(pos) + + local formspec = + "size[9,8.5]".. + drawers.gui_bg.. + drawers.gui_slots.. + "label[0,0;" .. S("Drawer Controller") .. "]" .. + "list[current_name;src;4,1.75;1,1;]".. + drawers.inventory_list(4.25) .. + "listring[current_player;main]".. + "listring[current_name;src]".. + "listring[current_player;main]" + + local distribute = meta:get_string("distribute") == "true" and "true" or "false" + local checkbox_x = 0.7 + if digilines_loaded and pipeworks_loaded then + formspec = formspec .. "field[0.8,3.5;3,1;digilineChannel;" .. S("Digiline Channel") .. ";${digilineChannel}]" + formspec = formspec .. "button_exit[3.5,3.2;2,1;saveChannel;" .. S("Save") .. "]" + checkbox_x = 5.7 end + formspec = formspec .. "checkbox[" .. checkbox_x .. ",3.2;distribute;" .. S("Drawer Distribution") .. ";" .. distribute .. "]" - return stack + meta:set_string("formspec", formspec) end local function controller_can_dig(pos, player) @@ -306,7 +317,7 @@ end local function controller_on_construct(pos) local meta = core.get_meta(pos) meta:set_string("drawers_table_index", "") - meta:set_string("formspec", controller_formspec(pos)) + controller_update_formspec(pos) meta:get_inventory():set_size("src", 1) end @@ -319,30 +330,39 @@ local function controller_on_blast(pos) return drops end -local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player) +local function controller_allow_metadata_inventory_put(pos, listname, _, stack, player) if (player and core.is_protected(pos, player:get_player_name())) or listname ~= "src" then return 0 end - local drawer_net_index = controller_get_drawer_index(pos, stack:get_name()) + local leftover = ItemStack(stack) + local stack_name = stack:get_name() + local drawer_net_index = controller_get_drawer_index(pos, stack_name) + local distribute = core.get_meta(pos):get_string("distribute") == "true" + + local function try_index(index, item_name) + index = drawer_net_index[index] + if not index then return end - if drawer_net_index[stack:get_name()] then - local drawer = drawer_net_index[stack:get_name()] + for _, drawer in ipairs(index) do + local drawer_pos = drawer.drawer_pos + local visualid = drawer.visualid - 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"]) + if drawers.drawer_get_content(drawer_pos, visualid).name == item_name then + local can_insert = drawers.drawer_can_insert_stack(drawer_pos, leftover, visualid) + leftover:take_item(can_insert) + if can_insert > 0 and (leftover:is_empty() or not distribute) then + return true + end + end 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 + if not try_index(stack_name, stack_name) then + try_index("empty", "") end - return 0 + return stack:get_count() - leftover:get_count() end local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) @@ -382,20 +402,32 @@ local function controller_on_digiline_receive(pos, _, channel, msg) return -- Protect against ItemStack(...) errors end - local item = ItemStack(msg) - local drawers_index = controller_get_drawer_index(pos, item:get_name()) + local stack = ItemStack(msg) + local item_name = stack:get_name() + local drawers_index = controller_get_drawer_index(pos, item_name) + local index = drawers_index[item_name] - if not drawers_index[item:get_name()] then + if not index then -- we can't do anything: the requested item doesn't exist return end - 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 = ItemStack() + local distribute = meta:get_string("distribute") == "true" + for _, drawer in ipairs(index) do + local from_drawer = drawers.drawer_take_item(drawer.drawer_pos, stack) + local drawer_amount = from_drawer:get_count() + stack:take_item(drawer_amount) + taken_stack:add_item(from_drawer) + + if drawer_amount > 0 and (stack:is_empty() or not distribute) then + break + end + end -- prevent crash if taken_stack ended up with a nil value - if taken_stack then + if taken_stack and not taken_stack:is_empty() then + local dir = core.facedir_to_dir(core.get_node(pos).param2) pipeworks.tube_inject_item(pos, pos, dir, taken_stack:to_string()) end end @@ -408,6 +440,11 @@ local function controller_on_receive_fields(pos, formname, fields, sender) if fields.saveChannel then meta:set_string("digilineChannel", fields.digilineChannel) end + if fields.distribute then + meta:set_string("distribute", fields.distribute) + end + + controller_update_formspec(pos) end -- Registers the drawer controller @@ -514,6 +551,26 @@ end -- register drawer controller register_controller() +core.register_lbm({ + label = "Update cached controller index", + name = "drawers:update_controller_index", + nodenames = {"drawers:controller"}, + run_at_every_load = false, + action = function(pos, node) + local meta = core.get_meta(pos) + local drawer_net_index = core.deserialize(meta:get_string("drawers_table_index")) + if drawer_net_index then + for item, index in pairs(drawer_net_index) do + if index.drawer_pos then + drawer_net_index[item] = {table.copy(index)} + end + end + end + meta:set_string("drawers_table_index", core.serialize(drawer_net_index)) + controller_update_formspec(pos) + end +}) + if default_loaded then core.register_craft({ output = 'drawers:controller',