first shot
|
@ -0,0 +1,21 @@
|
|||
# Networks [networks]
|
||||
|
||||
A library to build and manage networks based on tubelib2 tubes, pipes, or cables.
|
||||
|
||||
|
||||
### License
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
Code: Licensed under the GNU AGPL version 3 or later. See LICENSE.txt
|
||||
Textures: CC BY-SA 3.0
|
||||
|
||||
|
||||
### Dependencies
|
||||
|
||||
Required: tubelib2
|
||||
|
||||
|
||||
### History
|
||||
|
||||
**2021-05-23 V0.25**
|
||||
- First shot
|
|
@ -0,0 +1,30 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
networks = {}
|
||||
|
||||
-- Version for compatibility checks, see readme.md/history
|
||||
networks.version = 1.00
|
||||
|
||||
if minetest.global_exists("tubelib2") and tubelib2.version < 2.0 then
|
||||
minetest.log("error", "[networks] Networks requires tubelib2 version 2.0 or newer!")
|
||||
return
|
||||
end
|
||||
local MP = minetest.get_modpath("networks")
|
||||
|
||||
dofile(MP .. "/networks.lua")
|
||||
dofile(MP .. "/junction.lua")
|
||||
dofile(MP .. "/storage.lua")
|
||||
dofile(MP .. "/power.lua")
|
||||
--dofile(MP .. "/liquids.lua")
|
||||
-- Only for testing/demo purposes
|
||||
dofile(MP .. "/test.lua")
|
|
@ -0,0 +1,117 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
local function bit(p)
|
||||
return 2 ^ (p - 1) -- 1-based indexing
|
||||
end
|
||||
|
||||
-- Typical call: if hasbit(x, bit(3)) then ...
|
||||
local function hasbit(x, p)
|
||||
return x % (p + p) >= p
|
||||
end
|
||||
|
||||
local function setbit(x, p)
|
||||
return hasbit(x, p) and x or x + p
|
||||
end
|
||||
|
||||
local function get_node_box(val, size, boxes)
|
||||
local fixed = {{-size, -size, -size, size, size, size}}
|
||||
for i = 1,6 do
|
||||
if hasbit(val, bit(i)) then
|
||||
for _,box in ipairs(boxes[i]) do
|
||||
table.insert(fixed, box)
|
||||
end
|
||||
end
|
||||
end
|
||||
return {
|
||||
type = "fixed",
|
||||
fixed = fixed,
|
||||
}
|
||||
end
|
||||
|
||||
-- 'size' is the size of the junction cube without any connection, e.g. 1/8
|
||||
-- 'boxes' is a table with 6 table elements for the 6 possible connection arms
|
||||
-- 'tlib2' is the tubelib2 instance
|
||||
-- 'node' is the node definition with tiles, callback functions, and so on
|
||||
-- 'index' number for the inventory node (default 0)
|
||||
function networks.register_junction(name, size, boxes, tlib2, node, index)
|
||||
local names = {}
|
||||
for idx = 0,63 do
|
||||
local ndef = table.copy(node)
|
||||
if idx == (index or 0) then
|
||||
ndef.groups.not_in_creative_inventory = 0
|
||||
else
|
||||
ndef.groups.not_in_creative_inventory = 1
|
||||
end
|
||||
ndef.drawtype = "nodebox"
|
||||
ndef.node_box = get_node_box(idx, size, boxes)
|
||||
ndef.paramtype2 = "facedir"
|
||||
ndef.on_rotate = screwdriver.disallow -- TODO: allow controlled rotation
|
||||
ndef.drop = name..(index or "0")
|
||||
minetest.register_node(name..idx, ndef)
|
||||
tlib2:add_secondary_node_names({name..idx})
|
||||
-- for the case that 'tlib2.force_to_use_tubes' is set
|
||||
tlib2:add_special_node_names({name..idx})
|
||||
names[#names + 1] = name..idx
|
||||
end
|
||||
return names
|
||||
end
|
||||
|
||||
local SideToDir = {B=1, R=2, F=3, L=4}
|
||||
local function dir_to_dir2(dir, param2)
|
||||
if param2 == 0 then
|
||||
return dir
|
||||
elseif param2 == 1 then
|
||||
return ({4,1,2,3,5,6})[dir]
|
||||
elseif param2 == 2 then
|
||||
return ({3,4,1,2,5,6})[dir]
|
||||
elseif param2 == 3 then
|
||||
return ({2,3,4,1,5,6})[dir]
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
function networks.junction_type(pos, network, default_side, param2)
|
||||
local connected = function(self, pos, dir)
|
||||
if network:is_primary_node(pos, dir) then
|
||||
local param2, npos = self:get_primary_node_param2(pos, dir)
|
||||
if param2 then
|
||||
local d1, d2, _ = self:decode_param2(npos, param2)
|
||||
dir = networks.Flip[dir]
|
||||
return d1 == dir or dir == d2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local val = 0
|
||||
if default_side then
|
||||
val = setbit(val, bit(SideToDir[default_side]))
|
||||
end
|
||||
for dir = 1,6 do
|
||||
local dir2 = dir_to_dir2(dir, param2)
|
||||
if network.force_to_use_tubes then
|
||||
if connected(network, pos, dir) then
|
||||
val = setbit(val, bit(dir2))
|
||||
elseif network:is_special_node(pos, dir) then
|
||||
val = setbit(val, bit(dir2))
|
||||
end
|
||||
else
|
||||
if connected(network, pos, dir) then
|
||||
val = setbit(val, bit(dir2))
|
||||
elseif network:is_secondary_node(pos, dir) then
|
||||
val = setbit(val, bit(dir2))
|
||||
end
|
||||
end
|
||||
end
|
||||
return val
|
||||
end
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
name = networks
|
||||
depends = screwdriver,tubelib2
|
||||
description = Build and manage networks based on tubelib2 tubes/pipes/cables
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
-- Networks definition table for each node definition
|
||||
--
|
||||
-- networks = {
|
||||
-- ele3 = { -- network type
|
||||
-- sides = networks.AllSides, -- node connection sides
|
||||
-- ntype = "con", -- node type(s) (can be a list)
|
||||
-- },
|
||||
-- }
|
||||
|
||||
-- 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 Networks = {} -- cache for networks: {netw_type = {netID = <network>, ...}, ...}
|
||||
local NetIDs = {} -- cache for netw IDs: {pos_hash = {outdir = netID, ...}, ...}
|
||||
|
||||
local MAX_NUM_NODES = 1000
|
||||
local TTL = 5 * 60 -- 5 minutes
|
||||
local Route = {} -- Used to determine the already passed nodes while walking
|
||||
local NumNodes = 0
|
||||
local DirToSide = {"B", "R", "F", "L", "D", "U"}
|
||||
local Sides = {B = true, R = true, F = true, L = true, D = true, U = true}
|
||||
local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6}
|
||||
local Flip = {[0]=0,3,4,1,2,6,5} -- 180 degree turn
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Debugging
|
||||
-------------------------------------------------------------------------------
|
||||
--local function error(pos, msg)
|
||||
-- minetest.log("error", "[networks] "..msg.." at "..P2S(pos).." "..N(pos).name)
|
||||
--end
|
||||
|
||||
--local function count_nodes(node_type, nodes)
|
||||
-- local num = 0
|
||||
-- for _,pos in ipairs(nodes or {}) do
|
||||
-- num = num + 1
|
||||
-- end
|
||||
-- return node_type.."="..num
|
||||
--end
|
||||
|
||||
local function output(network)
|
||||
local tbl = {}
|
||||
for node_type,table in pairs(network) do
|
||||
if type(table) == "table" then
|
||||
tbl[#tbl+1] = "#" .. node_type .. " = " .. #table
|
||||
end
|
||||
end
|
||||
print("Network: "..table.concat(tbl, ", "))
|
||||
end
|
||||
|
||||
local function debug(node_type)
|
||||
local tbl = {}
|
||||
for netID,netw in pairs(Networks[node_type] or {}) do
|
||||
if type(netw) == "table" then
|
||||
tbl[#tbl+1] = string.format("%X", netID)
|
||||
end
|
||||
end
|
||||
return "Networks: "..table.concat(tbl, ", ")
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Helper
|
||||
-------------------------------------------------------------------------------
|
||||
local function hidden_node(pos, netw_type)
|
||||
-- legacy name
|
||||
local name = M(pos):get_string("techage_hidden_nodename")
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.networks then
|
||||
return ndef.networks[netw_type] or {}
|
||||
end
|
||||
-- new name
|
||||
name = M(pos):get_string("networks_hidden_nodename")
|
||||
ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.networks then
|
||||
return ndef.networks[netw_type] or {}
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
-- return the networks table from the node definition
|
||||
local function net_def(pos, netw_type)
|
||||
local ndef = minetest.registered_nodes[N(pos).name]
|
||||
if ndef and ndef.networks then
|
||||
return ndef.networks[netw_type] or {}
|
||||
else -- hidden junction
|
||||
return hidden_node(pos, netw_type)
|
||||
end
|
||||
end
|
||||
|
||||
local function net_def2(pos, node_name, netw_type)
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
if ndef and ndef.networks then
|
||||
return ndef.networks[netw_type] or {}
|
||||
else -- hidden junction
|
||||
return hidden_node(pos, netw_type)
|
||||
end
|
||||
end
|
||||
|
||||
local function connected(tlib2, pos, dir)
|
||||
local param2, npos = tlib2:get_primary_node_param2(pos, dir)
|
||||
if param2 then
|
||||
local d1, d2, num = tlib2:decode_param2(npos, param2)
|
||||
if not num then return end
|
||||
return Flip[dir] == d1 or Flip[dir] == d2
|
||||
end
|
||||
-- secondary nodes allowed?
|
||||
if tlib2.force_to_use_tubes then
|
||||
return tlib2:is_special_node(pos, dir)
|
||||
else
|
||||
return tlib2:is_secondary_node(pos, dir)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Calculate the node outdir based on node.param2 and nominal dir (according to side)
|
||||
local function dir_to_outdir(dir, param2)
|
||||
if dir < 5 then
|
||||
return ((dir + param2 - 1) % 4) + 1
|
||||
end
|
||||
return dir
|
||||
end
|
||||
|
||||
local function indir_to_dir(indir, param2)
|
||||
if indir < 5 then
|
||||
return ((indir - param2 + 5) % 4) + 1
|
||||
end
|
||||
return Flip[indir]
|
||||
end
|
||||
|
||||
local function outdir_to_dir(outdir, param2)
|
||||
if outdir < 5 then
|
||||
return ((outdir - param2 + 3) % 4) + 1
|
||||
end
|
||||
return outdir
|
||||
end
|
||||
|
||||
local function side_to_outdir(pos, side)
|
||||
return dir_to_outdir(SideToDir[side], N(pos).param2)
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Node Connections
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Get tlib2 connection dirs as table
|
||||
-- used e.g. for the connection walk
|
||||
local function get_node_connection_dirs(pos, netw_type)
|
||||
local val = M(pos):get_int(netw_type.."_conn")
|
||||
local tbl = {}
|
||||
if val % 0x40 >= 0x20 then tbl[#tbl+1] = 1 end
|
||||
if val % 0x20 >= 0x10 then tbl[#tbl+1] = 2 end
|
||||
if val % 0x10 >= 0x08 then tbl[#tbl+1] = 3 end
|
||||
if val % 0x08 >= 0x04 then tbl[#tbl+1] = 4 end
|
||||
if val % 0x04 >= 0x02 then tbl[#tbl+1] = 5 end
|
||||
if val % 0x02 >= 0x01 then tbl[#tbl+1] = 6 end
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- store all node sides with tube connections as nodemeta
|
||||
local function store_node_connection_sides(pos, tlib2)
|
||||
local node = N(pos)
|
||||
local val = 0
|
||||
local ndef = net_def2(pos, node.name, tlib2.tube_type)
|
||||
local sides = ndef.sides or ndef.get_sides and ndef.get_sides(pos, node)
|
||||
if sides then
|
||||
for dir = 1,6 do
|
||||
val = val * 2
|
||||
local side = DirToSide[outdir_to_dir(dir, node.param2)]
|
||||
if sides[side] then
|
||||
if connected(tlib2, pos, dir) then
|
||||
val = val + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
M(pos):set_int(tlib2.tube_type.."_conn", val)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Connection Walk
|
||||
-------------------------------------------------------------------------------
|
||||
local function pos_already_reached(pos)
|
||||
local key = minetest.hash_node_position(pos)
|
||||
if not Route[key] and NumNodes < MAX_NUM_NODES then
|
||||
Route[key] = true
|
||||
NumNodes = NumNodes + 1
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- check if the given pipe dir into the node is valid
|
||||
local function valid_indir(pos, indir, node, net_name)
|
||||
local ndef = net_def2(pos, node.name, net_name)
|
||||
local sides = ndef.sides or ndef.get_sides and ndef.get_sides(pos, node)
|
||||
local side = DirToSide[indir_to_dir(indir, node.param2)]
|
||||
if not sides or sides and not sides[side] then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
local function is_junction(pos, name, tube_type)
|
||||
local ndef = net_def2(pos, name, tube_type)
|
||||
-- ntype can be a string or an array of strings or nil
|
||||
if ndef.ntype == "junc" then
|
||||
return true
|
||||
end
|
||||
if type(ndef.ntype) == "table" then
|
||||
for _,ntype in ipairs(ndef.ntype) do
|
||||
if ntype == "junc" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- do the walk through the tubelib2 network
|
||||
-- indir is the direction which should not be covered by the walk
|
||||
-- (coming from there)
|
||||
-- if outdirs is given, only this dirs are used
|
||||
local function connection_walk(pos, outdirs, indir, node, tlib2, clbk)
|
||||
if clbk then clbk(pos, indir, node) end
|
||||
if outdirs or is_junction(pos, node.name, tlib2.tube_type) then
|
||||
for _,outdir in pairs(outdirs or get_node_connection_dirs(pos, tlib2.tube_type)) do
|
||||
local pos2, indir2 = tlib2:get_connected_node_pos(pos, outdir)
|
||||
local node = N(pos2)
|
||||
if pos2 and not pos_already_reached(pos2) and valid_indir(pos2, indir2, node, tlib2.tube_type) then
|
||||
connection_walk(pos2, nil, indir2, node, tlib2, clbk)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function collect_network_nodes(pos, tlib2, outdir)
|
||||
Route = {}
|
||||
NumNodes = 0
|
||||
pos_already_reached(pos)
|
||||
local netw = {}
|
||||
local node = N(pos)
|
||||
local net_name = tlib2.tube_type
|
||||
-- outdir corresponds to the indir coming from
|
||||
connection_walk(pos, outdir and {outdir}, nil, node, tlib2, function(pos, indir, node)
|
||||
local ndef = net_def2(pos, node.name, net_name)
|
||||
-- ntype can be a string or an array of strings or nil
|
||||
local ntypes = ndef.ntype or {}
|
||||
if type(ntypes) == "string" then
|
||||
ntypes = {ntypes}
|
||||
end
|
||||
for _,ntype in ipairs(ntypes) do
|
||||
if not netw[ntype] then netw[ntype] = {} end
|
||||
netw[ntype][#netw[ntype] + 1] = {pos = pos, indir = indir}
|
||||
end
|
||||
end)
|
||||
netw.ttl = minetest.get_gametime() + TTL
|
||||
netw.num_nodes = NumNodes
|
||||
return netw
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Maintain Network
|
||||
-------------------------------------------------------------------------------
|
||||
local function set_network(netw_type, netID, network)
|
||||
if netID then
|
||||
Networks[netw_type] = Networks[netw_type] or {}
|
||||
Networks[netw_type][netID] = network
|
||||
Networks[netw_type][netID].ttl = minetest.get_gametime() + TTL
|
||||
end
|
||||
end
|
||||
|
||||
local function get_network(netw_type, netID)
|
||||
local netw = Networks[netw_type] and Networks[netw_type][netID]
|
||||
if netw then
|
||||
netw.ttl = minetest.get_gametime() + TTL
|
||||
return netw
|
||||
end
|
||||
end
|
||||
|
||||
local function delete_network(netw_type, netID)
|
||||
if Networks[netw_type] and Networks[netw_type][netID] then
|
||||
Networks[netw_type][netID] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- keep data base small and valid
|
||||
-- needed for networks without scheduler
|
||||
local function remove_outdated_networks()
|
||||
local to_be_deleted = {}
|
||||
local t = minetest.get_gametime()
|
||||
for net_name,tbl in pairs(Networks) do
|
||||
for netID,network in pairs(tbl) do
|
||||
local valid = (network.ttl or 0) - t
|
||||
if valid < 0 then
|
||||
to_be_deleted[#to_be_deleted+1] = {net_name, netID}
|
||||
end
|
||||
end
|
||||
end
|
||||
for _,item in ipairs(to_be_deleted) do
|
||||
local net_name, netID = unpack(item)
|
||||
Networks[net_name][netID] = nil
|
||||
end
|
||||
minetest.after(60, remove_outdated_networks)
|
||||
end
|
||||
minetest.after(60, remove_outdated_networks)
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Maintain netID
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Return node netID if available
|
||||
local function get_netID(pos, tlib2, outdir)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
NetIDs[hash] = NetIDs[hash] or {}
|
||||
local netID = NetIDs[hash][outdir]
|
||||
|
||||
if netID and get_network(tlib2.tube_type, netID) then
|
||||
return netID
|
||||
end
|
||||
end
|
||||
|
||||
-- determine network ID (largest hash number of all nodes with given type)
|
||||
local function determine_netID(tlib2, netw)
|
||||
local netID = 0
|
||||
for _, item in ipairs(netw[tlib2.tube_type] or {}) do
|
||||
local outdir = Flip[item.indir]
|
||||
local new = minetest.hash_node_position(item.pos) * 8 + outdir
|
||||
if netID <= new then
|
||||
netID = new
|
||||
end
|
||||
end
|
||||
return netID
|
||||
end
|
||||
|
||||
-- store network ID for each network node
|
||||
local function store_netID(tlib2, netw, netID)
|
||||
for _, item in ipairs(netw[tlib2.tube_type] or {}) do
|
||||
local hash = minetest.hash_node_position(item.pos)
|
||||
local outdir = Flip[item.indir]
|
||||
NetIDs[hash] = NetIDs[hash] or {}
|
||||
NetIDs[hash][outdir] = netID
|
||||
end
|
||||
set_network(tlib2.tube_type, netID, netw)
|
||||
end
|
||||
|
||||
-- delete network and netID for given pos
|
||||
local function delete_netID(pos, tlib2, outdir)
|
||||
local netID = get_netID(pos, tlib2, outdir)
|
||||
|
||||
if netID then
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
NetIDs[hash][outdir] = nil
|
||||
delete_network(tlib2.tube_type, netID)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- API Functions
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
-- Table fo a 180 degree turn
|
||||
networks.Flip = Flip
|
||||
|
||||
-- networks.net_def(pos, netw_type)
|
||||
networks.net_def = net_def
|
||||
|
||||
networks.AllSides = Sides -- table for all 6 node sides
|
||||
|
||||
-- networks.side_to_outdir(pos, side)
|
||||
networks.side_to_outdir = side_to_outdir
|
||||
|
||||
-- networks.node_connections(pos, tlib2)
|
||||
--networks.node_connections = node_connections
|
||||
|
||||
-- networks.collect_network_nodes(pos, tlib2, outdir)
|
||||
--networks.collect_network_nodes = collect_network_nodes
|
||||
|
||||
-- Get node tubelib2 connections as table of outdirs
|
||||
-- networks.get_node_connection_dirs(pos, netw_type)
|
||||
networks.get_node_connection_dirs = get_node_connection_dirs
|
||||
|
||||
networks.MAX_NUM_NODES = MAX_NUM_NODES
|
||||
|
||||
-- To be called from each node via 'tubelib2_on_update2'
|
||||
-- 'output' is optional and only needed for nodes with dedicated
|
||||
-- pipe sides (e.g. pumps).
|
||||
function networks.update_network(pos, outdir, tlib2)
|
||||
store_node_connection_sides(pos, tlib2) -- update node internal data
|
||||
delete_netID(pos, tlib2, outdir) -- delete node netID and network
|
||||
end
|
||||
|
||||
-- Make sure the block has a network and return netID
|
||||
function networks.get_netID(pos, tlib2, outdir)
|
||||
local netID = get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
return netID
|
||||
end
|
||||
|
||||
local netw = collect_network_nodes(pos, tlib2, outdir)
|
||||
output(netw, true)
|
||||
netID = determine_netID(tlib2, netw)
|
||||
if netID > 0 then
|
||||
store_netID(tlib2, netw, netID)
|
||||
return netID
|
||||
end
|
||||
end
|
||||
|
||||
-- return list of nodes {pos = ..., indir = ...} of given network
|
||||
function networks.get_network_table(pos, tlib2, outdir)
|
||||
local netID = networks.get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
local netw = get_network(tlib2.tube_type, netID)
|
||||
return netw or {}
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
function networks.get_default_outdir(pos, tlib2)
|
||||
local dirs = networks.get_node_connection_dirs(pos, tlib2.tube_type)
|
||||
if #dirs > 0 then
|
||||
return dirs[1]
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
---- Overridden method of tubelib2!
|
||||
--function Cable:get_primary_node_param2(pos, dir)
|
||||
-- return techage.get_primary_node_param2(pos, dir)
|
||||
--end
|
||||
|
||||
--function Cable:is_primary_node(pos, dir)
|
||||
-- return techage.is_primary_node(pos, dir)
|
||||
--end
|
||||
|
||||
--function Cable:get_secondary_node(pos, dir)
|
||||
-- local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0])
|
||||
-- local node = self:get_node_lvm(npos)
|
||||
-- if self.secondary_node_names[node.name] or
|
||||
-- self.secondary_node_names[M(npos):get_string("techage_hidden_nodename")] then
|
||||
-- return node, npos, true
|
||||
-- end
|
||||
--end
|
||||
|
||||
--function Cable:is_secondary_node(pos, dir)
|
||||
-- local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0])
|
||||
-- local node = self:get_node_lvm(npos)
|
||||
-- return self.secondary_node_names[node.name] or
|
||||
-- self.secondary_node_names[M(npos):get_string("techage_hidden_nodename")]
|
||||
--end
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- 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 DEFAULT_CAPA = 100
|
||||
|
||||
local Power = networks.Power -- {netID = {curr_load, max_capa}}
|
||||
|
||||
-- Determine load and capa for the all network storage systems
|
||||
local function get_storage_load(pos, tlib2, outdir)
|
||||
local netw = networks.get_network_table(pos, tlib2, outdir)
|
||||
local max_capa = DEFAULT_CAPA
|
||||
local curr_load = 0
|
||||
for _,item in ipairs(netw.sto or {}) do
|
||||
local ndef = networks.net_def(pos, tlib2.tube_type)
|
||||
local cload, mcapa = N(pos).get_storage_load(pos)
|
||||
curr_load = curr_load + cload
|
||||
max_capa = max_capa + mcapa
|
||||
end
|
||||
return {curr_load = curr_load, max_capa = max_capa}
|
||||
end
|
||||
|
||||
-- Function checks for a power grid, not for enough power
|
||||
-- For consumers, outdir is optional
|
||||
function networks.power_available(pos, tlib2, outdir)
|
||||
outdir = outdir or networks.get_default_outdir(pos, tlib2)
|
||||
local netID = networks.get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
local pwr = Power[netID] or get_storage_load(pos, tlib2, outdir)
|
||||
return pwr.curr_load > 0
|
||||
end
|
||||
end
|
||||
|
||||
-- For consumers, outdir is optional
|
||||
function networks.consume_power(pos, tlib2, outdir, amount)
|
||||
outdir = outdir or networks.get_default_outdir(pos, tlib2)
|
||||
local netID = networks.get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
local pwr = Power[netID] or get_storage_load(pos, tlib2, outdir)
|
||||
if pwr.curr_load >= amount then
|
||||
pwr.curr_load = pwr.curr_load - amount
|
||||
Power[netID] = pwr
|
||||
return amount
|
||||
else
|
||||
local consumed = pwr.curr_load
|
||||
pwr.curr_load = 0
|
||||
Power[netID] = pwr
|
||||
return consumed
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function networks.provide_power(pos, tlib2, outdir, amount)
|
||||
print(1)
|
||||
local netID = networks.get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
print(2)
|
||||
local pwr = Power[netID] or get_storage_load(pos, tlib2, outdir)
|
||||
if pwr.curr_load + amount <= pwr.max_capa then
|
||||
print(3)
|
||||
pwr.curr_load = pwr.curr_load + amount
|
||||
Power[netID] = pwr
|
||||
return amount
|
||||
else
|
||||
print(4)
|
||||
local provided = pwr.max_capa - pwr.curr_load
|
||||
pwr.curr_load = pwr.max_capa
|
||||
Power[netID] = pwr
|
||||
return provided
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Function returns the load/charge as ratio (0..1)
|
||||
-- Param outdir is optional
|
||||
function networks.get_storage_load(pos, tlib2, outdir)
|
||||
outdir = outdir or networks.get_default_outdir(pos, tlib2)
|
||||
local netID = networks.get_netID(pos, tlib2, outdir)
|
||||
if netID then
|
||||
local pwr = Power[netID] or get_storage_load(pos, tlib2, outdir)
|
||||
return pwr.curr_load / pwr.max_capa
|
||||
end
|
||||
return 0
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
local storage = minetest.get_mod_storage()
|
||||
|
||||
local function update_mod_storage()
|
||||
storage:set_int("Version", 1)
|
||||
storage:set_string("Power", minetest.serialize(networks.Power or {}))
|
||||
|
||||
-- run every 10 minutes
|
||||
minetest.after(600, update_mod_storage)
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
local version = storage:get_int("Version")
|
||||
networks.Power = minetest.deserialize(storage:get_string("Power")) or {}
|
||||
minetest.after(600, update_mod_storage)
|
||||
end)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
update_mod_storage()
|
||||
end)
|
||||
|
|
@ -0,0 +1,397 @@
|
|||
--[[
|
||||
|
||||
Networks
|
||||
========
|
||||
|
||||
Copyright (C) 2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
local CYCLE_TIME = 2
|
||||
local STORAGE_CAPA = 500
|
||||
local GEN_MAX = 20
|
||||
local CON_MAX = 5
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Debugging
|
||||
-------------------------------------------------------------------------------
|
||||
local function debug_info(pos, text)
|
||||
local marker = minetest.add_entity(pos, "networks:marker_cube")
|
||||
if marker ~= nil then
|
||||
if text == "add" then
|
||||
marker:set_nametag_attributes({color = "#FF0000", text = "add__________"})
|
||||
elseif text == "del" then
|
||||
marker:set_nametag_attributes({color = "#00FF00", text = "_____del_____"})
|
||||
elseif text == "noc" then
|
||||
marker:set_nametag_attributes({color = "#0000FF", text = "__________noc"})
|
||||
end
|
||||
minetest.after(6, marker.remove, marker)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity(":networks:marker_cube", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"networks_marker.png",
|
||||
"networks_marker.png",
|
||||
"networks_marker.png",
|
||||
"networks_marker.png",
|
||||
"networks_marker.png",
|
||||
"networks_marker.png",
|
||||
},
|
||||
physical = false,
|
||||
visual_size = {x = 1.1, y = 1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 8,
|
||||
},
|
||||
on_punch = function(self)
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Cable
|
||||
-------------------------------------------------------------------------------
|
||||
local Cable = tubelib2.Tube:new({
|
||||
-- North, East, South, West, Down, Up
|
||||
-- dirs_to_check = {1,2,3,4}, -- horizontal only
|
||||
-- dirs_to_check = {5,6}, -- vertical only
|
||||
dirs_to_check = {1,2,3,4,5,6},
|
||||
max_tube_length = 20,
|
||||
show_infotext = true,
|
||||
tube_type = "ele",
|
||||
primary_node_names = {"networks:cableS", "networks:cableA"},
|
||||
secondary_node_names = {
|
||||
"networks:generator", "networks:consumer",
|
||||
"networks:junction", "networks:storage"},
|
||||
after_place_tube = function(pos, param2, tube_type, num_tubes, tbl)
|
||||
minetest.swap_node(pos, {name = "networks:cable"..tube_type, param2 = param2})
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_node("networks:cableS", {
|
||||
description = "Cable",
|
||||
tiles = { -- Top, base, right, left, front, back
|
||||
"networks_cable.png",
|
||||
"networks_cable.png",
|
||||
"networks_cable.png",
|
||||
"networks_cable.png",
|
||||
"networks_hole.png",
|
||||
"networks_hole.png",
|
||||
},
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
if not Cable:after_place_tube(pos, placer, pointed_thing) then
|
||||
minetest.remove_node(pos)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Cable:after_dig_tube(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
paramtype2 = "facedir", -- important!
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-3/16, -3/16, -4/8, 3/16, 3/16, 4/8},
|
||||
},
|
||||
},
|
||||
on_rotate = screwdriver.disallow, -- important!
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {crumbly = 3, cracky = 3, snappy = 3},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("networks:cableA", {
|
||||
description = "Cable",
|
||||
tiles = { -- Top, base, right, left, front, back
|
||||
"networks_cable.png",
|
||||
"networks_hole.png",
|
||||
"networks_cable.png",
|
||||
"networks_cable.png",
|
||||
"networks_cable.png",
|
||||
"networks_hole.png",
|
||||
},
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Cable:after_dig_tube(pos, oldnode, oldmetadata)
|
||||
end,
|
||||
paramtype2 = "facedir", -- important!
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-3/16, -4/8, -3/16, 3/16, 3/16, 3/16},
|
||||
{-3/16, -3/16, -4/8, 3/16, 3/16, -3/16},
|
||||
},
|
||||
},
|
||||
on_rotate = screwdriver.disallow, -- important!
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {crumbly = 3, cracky = 3, snappy = 3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
drop = "networks:cableS",
|
||||
})
|
||||
|
||||
local size = 3/16
|
||||
local Boxes = {
|
||||
{{-size, -size, size, size, size, 0.5 }}, -- z+
|
||||
{{-size, -size, -size, 0.5, size, size}}, -- x+
|
||||
{{-size, -size, -0.5, size, size, size}}, -- z-
|
||||
{{-0.5, -size, -size, size, size, size}}, -- x-
|
||||
{{-size, -0.5, -size, size, size, size}}, -- y-
|
||||
{{-size, -size, -size, size, 0.5, size}}, -- y+
|
||||
}
|
||||
|
||||
networks.register_junction("networks:junction", size, Boxes, Cable, {
|
||||
description = "Junction",
|
||||
tiles = {"networks_cable.png"},
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
local name = "networks:junction"..networks.junction_type(pos, Cable)
|
||||
minetest.swap_node(pos, {name = name, param2 = 0})
|
||||
Cable:after_place_node(pos)
|
||||
end,
|
||||
tubelib2_on_update2 = function(pos, dir1, tlib2, node)
|
||||
local name = "networks:junction"..networks.junction_type(pos, Cable)
|
||||
minetest.swap_node(pos, {name = name, param2 = 0})
|
||||
networks.update_network(pos, nil, tlib2)
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Cable:after_dig_node(pos)
|
||||
end,
|
||||
networks = {
|
||||
ele = {
|
||||
sides = networks.AllSides, -- connection sides for cables
|
||||
ntype = "junc",
|
||||
},
|
||||
},
|
||||
paramtype = "light",
|
||||
use_texture_alpha = "clip",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {crumbly = 3, cracky = 3, snappy = 3},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Generator
|
||||
-------------------------------------------------------------------------------
|
||||
minetest.register_node("networks:generator", {
|
||||
description = "Generator",
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
'networks_gen.png',
|
||||
'networks_gen.png',
|
||||
'networks_gen.png',
|
||||
'networks_gen.png',
|
||||
'networks_gen.png',
|
||||
'networks_conn.png',
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local outdir = networks.side_to_outdir(pos, "F")
|
||||
M(pos):set_int("outdir", outdir)
|
||||
Cable:after_place_node(pos, {outdir})
|
||||
minetest.get_node_timer(pos):start(2)
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local outdir = tonumber(oldmetadata.fields.outdir or 0)
|
||||
Cable:after_dig_node(pos, {outdir})
|
||||
end,
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.provided = networks.provide_power(pos, Cable, outdir, GEN_MAX)
|
||||
M(pos):set_string("infotext", "running "..mem.provided)
|
||||
return true
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
if mem.running then
|
||||
mem.running = false
|
||||
M(pos):set_string("infotext", "stopped")
|
||||
minetest.get_node_timer(pos):stop()
|
||||
else
|
||||
mem.provided = mem.provided or 0
|
||||
mem.running = true
|
||||
M(pos):set_string("infotext", "running "..mem.provided)
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end
|
||||
end,
|
||||
|
||||
networks = {
|
||||
ele = {
|
||||
sides = networks.AllSides,
|
||||
ntype = "gen",
|
||||
},
|
||||
},
|
||||
|
||||
paramtype2 = "facedir", -- important!
|
||||
on_rotate = screwdriver.disallow, -- important!
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
groups = {crumbly = 3, cracky = 3, snappy = 3},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Consumer
|
||||
-------------------------------------------------------------------------------
|
||||
local function swap_node(pos, name)
|
||||
local node = tubelib2.get_node_lvm(pos)
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
local function on_turn_on(pos, tlib2)
|
||||
swap_node(pos, "networks:consumer_on")
|
||||
M(pos):set_string("infotext", "on")
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.running = true
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end
|
||||
|
||||
local function on_turn_off(pos, tlib2)
|
||||
swap_node(pos, "networks:consumer")
|
||||
M(pos):set_string("infotext", "off")
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.running = false
|
||||
minetest.get_node_timer(pos):stop()
|
||||
end
|
||||
|
||||
local function node_timer(pos, elapsed)
|
||||
local consumed = networks.consume_power(pos, Cable, nil, CON_MAX)
|
||||
if consumed < CON_MAX then
|
||||
on_turn_off(pos, Cable)
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
if not mem.running and networks.power_available(pos, Cable) then
|
||||
on_turn_on(pos, Cable)
|
||||
else
|
||||
on_turn_off(pos, Cable)
|
||||
end
|
||||
end
|
||||
|
||||
local function after_place_node(pos)
|
||||
M(pos):set_string("infotext", "off")
|
||||
Cable:after_place_node(pos)
|
||||
end
|
||||
|
||||
local function after_dig_node(pos, oldnode)
|
||||
Cable:after_dig_node(pos)
|
||||
tubelib2.del_mem(pos)
|
||||
end
|
||||
|
||||
local function tubelib2_on_update2(pos, outdir, tlib2, node)
|
||||
networks.update_network(pos, outdir, tlib2)
|
||||
end
|
||||
|
||||
local netdef = {
|
||||
ele = {
|
||||
sides = networks.AllSides, -- connection sides for cables
|
||||
ntype = "con",
|
||||
},
|
||||
}
|
||||
|
||||
minetest.register_node("networks:consumer", {
|
||||
description = "Consumer",
|
||||
tiles = {'networks_con.png'},
|
||||
|
||||
on_turn_on = on_turn_on,
|
||||
on_timer = node_timer,
|
||||
on_rightclick = on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
tubelib2_on_update2 = tubelib2_on_update2,
|
||||
networks = netdef,
|
||||
paramtype = "light",
|
||||
light_source = 0,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy = 2, cracky = 2, crumbly = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("networks:consumer_on", {
|
||||
description = "Consumer",
|
||||
tiles = {'networks_con.png'},
|
||||
|
||||
on_turn_off = on_turn_off,
|
||||
on_timer = node_timer,
|
||||
on_rightclick = on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
tubelib2_on_update2 = tubelib2_on_update2,
|
||||
networks = netdef,
|
||||
paramtype = "light",
|
||||
light_source = minetest.LIGHT_MAX,
|
||||
paramtype2 = "facedir",
|
||||
diggable = false,
|
||||
drop = "",
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Storage
|
||||
-------------------------------------------------------------------------------
|
||||
minetest.register_node("networks:storage", {
|
||||
description = "Storage",
|
||||
tiles = {"networks_sto.png"},
|
||||
on_timer = function(pos, elapsed)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
mem.load = networks.get_storage_load(pos, Cable)
|
||||
M(pos):set_string("infotext", "load = "..math.floor(mem.load * 100))
|
||||
return true
|
||||
end,
|
||||
after_place_node = function(pos)
|
||||
Cable:after_place_node(pos)
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode)
|
||||
Cable:after_dig_node(pos)
|
||||
tubelib2.del_mem(pos)
|
||||
end,
|
||||
tubelib2_on_update2 = function(pos, outdir, tlib2, node)
|
||||
networks.update_network(pos, outdir, tlib2)
|
||||
end,
|
||||
get_storage_load = function(pos)
|
||||
local mem = tubelib2.get_mem(pos)
|
||||
return mem.load or 0, STORAGE_CAPA
|
||||
end,
|
||||
networks = {
|
||||
ele = {
|
||||
sides = networks.AllSides,
|
||||
ntype = "sto",
|
||||
},
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy = 2, cracky = 2, crumbly = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
After Width: | Height: | Size: 125 B |
After Width: | Height: | Size: 108 B |
After Width: | Height: | Size: 110 B |
After Width: | Height: | Size: 116 B |
After Width: | Height: | Size: 129 B |
After Width: | Height: | Size: 195 B |
After Width: | Height: | Size: 118 B |