--[[ Tube Library ============ Copyright (C) 2017 Joachim Stolberg LGPLv2.1+ See LICENSE.txt for more information distributor.lua: A more complex node acting as server and client. This node claims a position number and registers its message and items interface. The Distributor supports the following messages: - topic = "start", payload = nil - topic = "stop" , payload = nil ]]-- -- Return a key/value table with all items and the corresponding stack numbers local function invlist_content_as_kvlist(list) local res = {} for idx,items in ipairs(list) do local name = items:get_name() if name ~= "" then res[name] = idx end end return res end -- Return the total number of list entries local function invlist_num_entries(list) local res = 0 for _,items in ipairs(list) do local name = items:get_name() if name ~= "" then res = res + items:get_count() end end return res end -- Return a flat table with all items local function invlist_entries_as_list(list) local res = {} for _,items in ipairs(list) do local name = items:get_name() local count = items:get_count() if name ~= "" then for i = 1,count do res[#res+1] = name end end end return res end local function AddToTbl(kvTbl, new_items) for _, l in ipairs(new_items) do kvTbl[l] = true end return kvTbl end local function distributor_formspec(running) return "size[8,8.5]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "list[context;src;0,0;2,4;]".. "image[2,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]".. "button_exit[2,3;1,1;button;OK]".. "checkbox[3,0;running1;On;"..dump(running[1]).."]".. "checkbox[3,1;running2;On;"..dump(running[2]).."]".. "checkbox[3,2;running3;On;"..dump(running[3]).."]".. "checkbox[3,3;running4;On;"..dump(running[4]).."]".. "image[3.6,0;0.3,1;tubelib_red.png]".. "image[3.6,1;0.3,1;tubelib_green.png]".. "image[3.6,2;0.3,1;tubelib_blue.png]".. "image[3.6,3;0.3,1;tubelib_yellow.png]".. "list[context;red;4,0;4,1;]".. "list[context;green;4,1;4,1;]".. "list[context;blue;4,2;4,1;]".. "list[context;yellow;4,3;4,1;]".. "list[current_player;main;0,4.5;8,4;]".. "listring[context;src]".. "listring[current_player;main]" end local function allow_metadata_inventory_put(pos, listname, index, stack, player) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local list = inv:get_list(listname) if minetest.is_protected(pos, player:get_player_name()) then return 0 end if listname == "src" then return stack:get_count() elseif invlist_num_entries(list) < 4 then return 1 else return 0 end end local function allow_metadata_inventory_take(pos, listname, index, stack, player) if minetest.is_protected(pos, player:get_player_name()) then return 0 end return stack:get_count() end local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local stack = inv:get_stack(from_list, from_index) return allow_metadata_inventory_put(pos, to_list, to_index, stack, player) end local SlotColors = {"red", "green", "blue", "yellow"} local Num2Ascii = {"F", "L", "B", "R"} -- color to side translation local FilterCache = {} -- local cache for filter settings local function filter_settings(pos) local hash = minetest.hash_node_position(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local facedir = meta:get_int("facedir") local running = minetest.deserialize(meta:get_string("running")) local kvFilterItemNames = {} -- { = true,...} local kvSide2ItemNames = {} -- {"F" = {,...},...} -- collect all filter settings for idx,slot in ipairs(SlotColors) do local side = Num2Ascii[idx] if running[idx] == true then local list = inv:get_list(slot) local filter = invlist_entries_as_list(list) AddToTbl(kvFilterItemNames, filter) kvSide2ItemNames[side] = filter end end FilterCache[hash] = { kvFilterItemNames = kvFilterItemNames, kvSide2ItemNames = kvSide2ItemNames } end local function start_the_machine(pos) local node = minetest.get_node(pos) node.name = "tubelib:distributor_active" minetest.swap_node(pos, node) minetest.get_node_timer(pos):start(2) local meta = minetest.get_meta(pos) local number = meta:get_string("number") meta:set_string("infotext", "Tubelib Distributor "..number..": running") end local function stop_the_machine(pos) local node = minetest.get_node(pos) node.name = "tubelib:distributor" minetest.swap_node(pos, node) minetest.get_node_timer(pos):stop() local meta = minetest.get_meta(pos) local number = meta:get_string("number") meta:set_string("infotext", "Tubelib Distributor "..number..": stopped") end -- move items to the output slots local function keep_running(pos, elapsed) local meta = minetest.get_meta(pos) local facedir = meta:get_int("facedir") local slot_idx = meta:get_int("slot_idx") or 1 meta:set_int("slot_idx", (slot_idx + 1) % 4) local side = Num2Ascii[slot_idx+1] local listname = SlotColors[slot_idx+1] local inv = meta:get_inventory() local list = inv:get_list("src") local kvSrc = invlist_content_as_kvlist(list) -- calculate the filter settings only once local hash = minetest.hash_node_position(pos) if FilterCache[hash] == nil then filter_settings(pos) end -- read data from Cache -- all filter items as key/value { = true,...} local kvFilterItemNames = FilterCache[hash].kvFilterItemNames -- filter items of one slot as list {,...} local names = FilterCache[hash].kvSide2ItemNames[side] if names == nil then -- filter closed return true end -- move items from configured filters to the output if next(names) then for _,name in ipairs(names) do if kvSrc[name] then local item = tubelib.get_this_item(inv, "src", kvSrc[name]) -- <<=== tubelib if item then if not tubelib.push_items(pos, facedir, side, item) then -- <<=== tubelib tubelib.put_item(inv, "src", item) end end end end end -- move additional items from unconfigured filters to the output if next(names) == nil then for name,_ in pairs(kvSrc) do if kvFilterItemNames[name] == nil then -- not in the filter so far? local item = tubelib.get_this_item(inv, "src", kvSrc[name]) -- <<=== tubelib if item then if not tubelib.push_items(pos, facedir, side, item) then -- <<=== tubelib tubelib.put_item(inv, "src", item) end end end end end return true end local function on_receive_fields(pos, formname, fields, player) if minetest.is_protected(pos, player:get_player_name()) then return end local meta = minetest.get_meta(pos) local running = minetest.deserialize(meta:get_string("running")) if fields.running1 ~= nil then running[1] = fields.running1 == "true" elseif fields.running2 ~= nil then running[2] = fields.running2 == "true" elseif fields.running3 ~= nil then running[3] = fields.running3 == "true" elseif fields.running4 ~= nil then running[4] = fields.running4 == "true" end meta:set_string("running", minetest.serialize(running)) meta:set_string("formspec", distributor_formspec(running)) if fields.button ~= nil then filter_settings(pos) if running[1] or running[2] or running[3] or running[4] then start_the_machine(pos) else stop_the_machine(pos) end end end minetest.register_node("tubelib:distributor", { description = "Tubelib Distributor", tiles = { -- up, down, right, left, back, front 'tubelib_distributor.png', 'tubelib_distributor.png', 'tubelib_distributor_yellow.png', 'tubelib_distributor_green.png', "tubelib_distributor_red.png", "tubelib_distributor_blue.png", }, after_place_node = function(pos, placer) local number = tubelib.get_node_number(pos, "tubelib:distributor") -- <<=== tubelib local meta = minetest.get_meta(pos) local facedir = minetest.dir_to_facedir(placer:get_look_dir(), false) local placer_name = placer:get_player_name() local running = {false,false,false,false} meta:set_string("infotext", "Tubelib Distributor "..number..": stopped") meta:set_string("formspec", distributor_formspec(running)) meta:set_string("running", minetest.serialize(running)) meta:set_string("number", number) meta:set_int("facedir", facedir) local inv = meta:get_inventory() inv:set_size('src', 8) inv:set_size('yellow', 4) inv:set_size('green', 4) inv:set_size('red', 4) inv:set_size('blue', 4) end, on_receive_fields = on_receive_fields, on_dig = function(pos, node, puncher, pointed_thing) if minetest.is_protected(pos, puncher:get_player_name()) then return end local meta = minetest.get_meta(pos) local inv = meta:get_inventory() if inv:is_empty("src") then minetest.node_dig(pos, node, puncher, pointed_thing) tubelib.remove_node(pos) -- <<=== tubelib end end, allow_metadata_inventory_put = allow_metadata_inventory_put, allow_metadata_inventory_take = allow_metadata_inventory_take, allow_metadata_inventory_move = allow_metadata_inventory_move, paramtype2 = "facedir", groups = {cracky=1}, is_ground_content = false, }) minetest.register_node("tubelib:distributor_active", { description = "Tubelib Distributor", tiles = { -- up, down, right, left, back, front { image = "tubelib_distributor_active.png", backface_culling = false, animation = { type = "vertical_frames", aspect_w = 32, aspect_h = 32, length = 2.0, }, }, 'tubelib_distributor.png', 'tubelib_distributor_yellow.png', 'tubelib_distributor_green.png', "tubelib_distributor_red.png", "tubelib_distributor_blue.png", }, on_receive_fields = on_receive_fields, allow_metadata_inventory_put = allow_metadata_inventory_put, allow_metadata_inventory_take = allow_metadata_inventory_take, allow_metadata_inventory_move = allow_metadata_inventory_move, on_timer = keep_running, paramtype2 = "facedir", groups = {crumbly=0, not_in_creative_inventory=1}, is_ground_content = false, }) minetest.register_craft({ output = "tubelib:distributor 2", recipe = { {"group:wood", "default:steel_ingot", "group:wood"}, {"tubelib:tube1", "default:mese_crystal", "tubelib:tube1"}, {"group:wood", "default:steel_ingot", "group:wood"}, }, }) --------------------------------------------------------------- tubelib tubelib.register_node("tubelib:distributor", {"tubelib:distributor_active"}, { on_pull_item = function(pos) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() return tubelib.get_item(inv, "src") end, on_push_item = function(pos, item) local meta = minetest.get_meta(pos) local inv = meta:get_inventory() return tubelib.put_item(inv, "src", item) end, on_recv_message = function(pos, topic, payload) if topic == "start" then start_the_machine(pos) elseif topic == "stop" then stop_the_machine(pos) end end, }) --------------------------------------------------------------- tubelib