--[[ Networks ======== Copyright (C) 2021 Joachim Stolberg AGPL v3 See LICENSE.txt for more information Liquid API for liquid pumping and storing nodes ]]-- -- for lazy programmers local S2P = minetest.string_to_pos local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local M = minetest.get_meta local N = tubelib2.get_node_lvm local LQD = function(pos) return (minetest.registered_nodes[N(pos).name] or {}).liquid end networks.liquid = {} networks.registered_networks.liquid = {} -- return list of nodes {pos = ..., indir = ...} of given node_type local function get_network_table(pos, tlib2, outdir, node_type) local netw = networks.get_network_table(pos, tlib2, outdir) if netw then return netw[node_type] or {} end return {} end ------------------------------------------------------------------------------- -- For all types of nodes ------------------------------------------------------------------------------- -- names: list of node names -- tlib2: tubelib2 instance -- node_type: one of "pump", "tank", "junc" -- valid_sides: something like {"L", "R"} or nil -- liquid_callbacks = { -- capa = CAPACITY, -- peek = function(pos, indir), -- returns: liquid name -- put = function(pos, indir, name, amount), -- returns: liquid leftover or 0 -- take = function(pos, indir, name, amount), -- returns: taken, name -- untake = function(pos, indir, name, amount), -- returns: leftover -- } function networks.liquid.register_nodes(names, tlib2, node_type, valid_sides, liquid_callbacks) if node_type == "pump" then assert(not valid_sides or type(valid_sides) == "table") valid_sides = valid_sides or {"B", "R", "F", "L", "D", "U"} elseif node_type == "tank" or node_type == "junc" then assert(not valid_sides or type(valid_sides) == "table") valid_sides = valid_sides or {"B", "R", "F", "L", "D", "U"} elseif node_type and type(node_type) == "string" then valid_sides = valid_sides or {"B", "R", "F", "L", "D", "U"} else error("parameter error") end if node_type == "tank" then assert(type(liquid_callbacks) == "table") end tlib2:add_secondary_node_names(names) networks.registered_networks.liquid[tlib2.tube_type] = tlib2 for _, name in ipairs(names) do local ndef = minetest.registered_nodes[name] local tbl = ndef.networks or {} tbl[tlib2.tube_type] = {ntype = node_type} minetest.override_item(name, {networks = tbl}) minetest.override_item(name, {liquid = liquid_callbacks}) tlib2:set_valid_sides(name, valid_sides) end end -- To be called for each liquid network change via -- tubelib2_on_update2 or register_on_tube_update2 function networks.liquid.update_network(pos, outdir, tlib2, node) local ndef = networks.net_def(pos, tlib2.tube_type) if ndef.ntype == "junc" then outdir = 0 end networks.update_network(pos, outdir, tlib2, node) end ------------------------------------------------------------------------------- -- Client/pump functions ------------------------------------------------------------------------------- -- Determine and return liquid 'name' from the -- remote inventory. function networks.liquid.peek(pos, tlib2, outdir) assert(outdir) for _,item in ipairs(get_network_table(pos, tlib2, outdir, "tank")) do local liq = LQD(item.pos) if liq and liq.peek then return liq.peek(item.pos, item.indir) end end end -- Add given amount of liquid to the remote inventory. -- return leftover amount function networks.liquid.put(pos, tlib2, outdir, name, amount, show_debug_cube) assert(outdir) assert(name) assert(amount and amount > 0) for _,item in ipairs(get_network_table(pos, tlib2, outdir, "tank")) do local liq = LQD(item.pos) if liq and liq.put and liq.peek then -- wrong items? local peek = liq.peek(item.pos, item.indir) if peek and peek ~= name then return amount or 0 end if show_debug_cube then networks.set_marker(item.pos, "put", 1.1, 1) end amount = liq.put(item.pos, item.indir, name, amount) if not amount or amount == 0 then break end end end return amount or 0 end -- Take given amount of liquid from the remote inventory. -- return taken amount and item name function networks.liquid.take(pos, tlib2, outdir, name, amount, show_debug_cube) assert(outdir) assert(amount and amount > 0) local taken = 0 for _,item in ipairs(get_network_table(pos, tlib2, outdir, "tank")) do local liq = LQD(item.pos) if liq and liq.take then if show_debug_cube then networks.set_marker(item.pos, "take", 1.1, 1) end taken, name = liq.take(item.pos, item.indir, name, amount) if taken and name and taken > 0 then break end end end return taken, name end function networks.liquid.untake(pos, tlib2, outdir, name, amount) assert(outdir) assert(name) assert(amount) for _,item in ipairs(get_network_table(pos, tlib2, outdir, "tank")) do local liq = LQD(item.pos) if liq and liq.untake then amount = liq.untake(item.pos, item.indir, name, amount) if not amount or amount == 0 then break end end end return amount or 0 end ------------------------------------------------------------------------------- -- Server/tank local functions ------------------------------------------------------------------------------- function networks.liquid.is_empty(nvm) return not nvm.liquid or (nvm.liquid.amount or 0) <= 0 end function networks.liquid.get_amount(nvm) if nvm.liquid and nvm.liquid.amount then return nvm.liquid.amount end return 0 end function networks.liquid.get_item(nvm) local itemname = "" if nvm.liquid and nvm.liquid.amount and nvm.liquid.amount > 0 and nvm.liquid.name then itemname = nvm.liquid.name.." "..nvm.liquid.amount end return itemname end function networks.liquid.srv_peek(nvm) nvm.liquid = nvm.liquid or {} nvm.liquid.amount = math.floor((nvm.liquid.amount or 0) + 0.5) return nvm.liquid.amount > 0 and nvm.liquid.name end function networks.liquid.srv_put(nvm, name, amount, capa) assert(name) assert(capa and capa > 0) amount = math.floor((amount or 0) + 0.5) nvm.liquid = nvm.liquid or {} nvm.liquid.amount = nvm.liquid.amount or 0 if nvm.liquid.amount == 0 or not nvm.liquid.name then nvm.liquid.name = name nvm.liquid.amount = amount return 0 elseif nvm.liquid.name == name then nvm.liquid.amount = nvm.liquid.amount or 0 if nvm.liquid.amount + amount <= capa then nvm.liquid.amount = nvm.liquid.amount + amount return 0 else local rest = nvm.liquid.amount + amount - capa nvm.liquid.amount = capa return rest end end return amount end function networks.liquid.srv_take(nvm, name, amount) amount = math.floor((amount or 0) + 0.5) nvm.liquid = nvm.liquid or {} if not name or nvm.liquid.name == name then name = nvm.liquid.name nvm.liquid.amount = nvm.liquid.amount or 0 if nvm.liquid.amount > amount then nvm.liquid.amount = nvm.liquid.amount - amount return amount, name else local rest = nvm.liquid.amount local name = nvm.liquid.name nvm.liquid.amount = 0 nvm.liquid.name = nil return rest, name end end return 0 end ------------------------------------------------------------------------------- -- Valve ------------------------------------------------------------------------------- function networks.liquid.turn_valve_on(pos, tlib2, name_off, name_on) local node = N(pos) local meta = M(pos) if node.name == name_off then node.name = name_on minetest.swap_node(pos, node) tlib2:after_place_tube(pos) meta:set_int("networks_param2", node.param2) return true elseif meta:contains("networks_param2_copy") then meta:set_int("networks_param2", meta:get_int("networks_param2_copy")) tlib2:after_place_tube(pos) return true end end function networks.liquid.turn_valve_off(pos, tlib2, name_off, name_on) local node = N(pos) local meta = M(pos) if node.name == name_on then node.name = name_off minetest.swap_node(pos, node) meta:set_int("networks_param2", 0) tlib2:after_dig_tube(pos, node) return true elseif meta:contains("networks_param2") then meta:set_int("networks_param2_copy", meta:get_int("networks_param2")) meta:set_int("networks_param2", 0) tlib2:after_dig_tube(pos, node) return true end end ------------------------------------------------------------------------------- -- Info/Tools ------------------------------------------------------------------------------- -- Return list of liquids of connected tanks -- Node at pos must be a pump function networks.liquid.get_liquids(pos, tlib2) local tbl = {} for _, dir in ipairs(networks.get_node_connection_dirs(pos, tlib2.tube_type)) do for _,item in ipairs(get_network_table(pos, tlib2, dir, "tank")) do local liq = LQD(item.pos) if liq and liq.peek then local liq_name = liq.peek(item.pos, item.indir) if liq_name then local def = minetest.registered_items[liq_name] or minetest.registered_craftitems[liq_name] if def then tbl[def.description] = true end end end end end local out = {} for k,v in pairs(tbl) do out[#out + 1] = k end return out end