2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
local networks = { }
local net_members = { }
local netname = 1
2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
local mod_storage = minetest.get_mod_storage ( )
2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
networks = minetest.deserialize ( mod_storage : get_string ( " networks " ) ) or { }
net_members = minetest.deserialize ( mod_storage : get_string ( " net_members " ) ) or { }
netname = mod_storage : get_int ( " netname " ) or 1
2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
local function save_data ( )
--print("saving")
mod_storage : set_string ( " networks " , minetest.serialize ( networks ) )
mod_storage : set_string ( " net_members " , minetest.serialize ( net_members ) )
mod_storage : set_int ( " netname " , netname )
end
-- centralized network creation for consistency
local function new_network ( pos )
local hash = minetest.hash_node_position ( pos )
print ( " new network: hash: " .. hash .. " name: " .. netname ) ;
networks [ hash ] = {
hash = hash ,
pos = { x = pos.x , y = pos.y , z = pos.z } ,
fluid = ' air ' ,
name = netname ,
count = 1 ,
inputs = {
[ hash ] = 1 ,
} ,
outputs = { } ,
buffer = 0 ,
in_pressure = - 32000 ,
}
net_members [ hash ] = hash
netname = netname + 1
return networks [ hash ] , hash
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
-- check nearby nodes for existing networks
local function check_merge ( pos )
local hash = minetest.hash_node_position ( pos )
local merge_list = { }
local current_net = nil
local found_net = 0
local check_net = function ( npos )
local nhash = minetest.hash_node_position ( npos )
local nphash = net_members [ nhash ]
if nphash ~= nil then
local pnet = networks [ nphash ]
if nil == current_net then
print ( " joining existing network: " .. pnet.name )
net_members [ hash ] = nphash
current_net = nphash
pnet.count = pnet.count + 1
pnet.inputs [ hash ] = 1
table.insert ( merge_list , pnet )
elseif current_net == nphash then
print ( " alternate connection to existing network " )
else
print ( " found seconday network: " .. pnet.name )
table.insert ( merge_list , pnet )
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
found_net = 1
2013-10-14 18:46:45 -07:00
end
end
2018-06-17 20:48:04 -07:00
check_net ( { x = pos.x , y = pos.y - 1 , z = pos.z } )
check_net ( { x = pos.x , y = pos.y + 1 , z = pos.z } )
check_net ( { x = pos.x + 1 , y = pos.y , z = pos.z } )
check_net ( { x = pos.x - 1 , y = pos.y , z = pos.z } )
check_net ( { x = pos.x , y = pos.y , z = pos.z + 1 } )
check_net ( { x = pos.x , y = pos.y , z = pos.z - 1 } )
return found_net , merge_list
end
2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
-- merge a list if networks, if the are multiple nets in the list
local function try_merge ( merge_list )
if # merge_list > 1 then
print ( " \n merging " .. # merge_list .. " networks " )
local biggest = { count = 0 }
local mlookup = { }
for _ , n in ipairs ( merge_list ) do
mlookup [ n.hash ] = 1
if n.count > biggest.count then
biggest = n
2013-10-14 18:46:45 -07:00
end
end
2018-06-17 20:48:04 -07:00
mlookup [ biggest.hash ] = 0
for k , v in pairs ( net_members ) do
if mlookup [ v ] == 1 then
net_members [ k ] = biggest.hash
end
end
for _ , n in ipairs ( merge_list ) do
if n.hash ~= biggest.hash then
biggest.count = biggest.count + n.count
2018-06-19 20:09:16 -07:00
networks [ n.hash ] = nil -- delete old networks
2018-06-17 20:48:04 -07:00
end
end
2018-06-19 20:09:16 -07:00
return biggest
2013-10-14 18:46:45 -07:00
end
2018-06-19 20:09:16 -07:00
return merge_list [ 1 ]
2013-10-14 18:46:45 -07:00
end
2018-06-19 20:30:54 -07:00
local function pnet_for ( pos )
local hash = minetest.hash_node_position ( pos )
local ph = net_members [ hash ]
if ph == nil then
return nil , hash
end
return networks [ ph ] , hash
end
local function walk_net ( opos )
local members = { }
local count = 0
local opnet = pnet_for ( opos )
if opnet == nil then
return nil , 0 , nil
end
local stack = { }
table.insert ( stack , opos )
while # stack > 0 do
local pos = table.remove ( stack )
local pnet , hash = pnet_for ( pos )
if pnet ~= nil and members [ hash ] == nil then
-- only count members of the original node's network
if pnet.name == opnet.name then
members [ hash ] = pos
count = count + 1
-- print(" found net node: " .. minetest.pos_to_string(pos))
table.insert ( stack , { x = pos.x - 1 , y = pos.y , z = pos.z } )
table.insert ( stack , { x = pos.x + 1 , y = pos.y , z = pos.z } )
table.insert ( stack , { x = pos.x , y = pos.y - 1 , z = pos.z } )
table.insert ( stack , { x = pos.x , y = pos.y + 1 , z = pos.z } )
table.insert ( stack , { x = pos.x , y = pos.y , z = pos.z - 1 } )
table.insert ( stack , { x = pos.x , y = pos.y , z = pos.z + 1 } )
end
end
end
return members , count , opnet
end
-- crawls the network and assigns found nodes to the new network
local function rebase_network ( pos )
local net , phash , hash = bitumen.pipes . get_net ( pos )
-- print(dump(pos))
if hash == phash then
print ( " trying to rebase network to the same spot " )
return { name = 9999999 }
end
local pipes = walk_net ( pos )
-- print(dump(pipes))
local new_net , new_phash = new_network ( pos )
-- switch all the members
for h , p in pairs ( pipes ) do
--print("reassigning " .. minetest.pos_to_string(p))
net_members [ h ] = new_phash
new_net.count = new_net.count + 1
end
return new_net , new_phash
end
2018-06-17 20:48:04 -07:00
bitumen.pipes = { }
2013-10-14 18:46:45 -07:00
2018-06-17 20:48:04 -07:00
-- used by external machines to connect to a network in their on_construct callback
2018-06-18 23:52:09 -07:00
bitumen.pipes . on_construct = function ( pos )
2018-06-17 20:48:04 -07:00
local found_net , merge_list = check_merge ( pos )
if found_net == 0 then
local hash = minetest.hash_node_position ( pos )
local net = new_network ( pos )
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
2018-06-19 20:09:16 -07:00
local pnet = try_merge ( merge_list )
2018-06-19 23:11:04 -07:00
--pnet.count = pnet.count + 1 -- TODO: this might be implicit somewhere else
2018-06-17 20:48:04 -07:00
save_data ( )
2013-10-14 18:46:45 -07:00
end
2018-06-19 20:30:54 -07:00
bitumen.pipes . after_destruct = function ( pos )
local hash = minetest.hash_node_position ( pos )
2018-06-19 20:09:16 -07:00
local phash = net_members [ hash ]
2018-06-19 20:30:54 -07:00
if phash == nil then
print ( " wtf: pipe has no network in after_destruct " )
return
end
2018-06-19 20:09:16 -07:00
local pnet = networks [ phash ]
if pnet == nil then
print ( " wtf: no network in after_destruct for pipe " )
return
end
-- remove this node from the network
net_members [ hash ] = nil
2018-06-19 20:30:54 -07:00
pnet.count = pnet.count - 1
-- neighboring nodes
local check_pos = {
{ x = pos.x + 1 , y = pos.y , z = pos.z } ,
{ x = pos.x - 1 , y = pos.y , z = pos.z } ,
{ x = pos.x , y = pos.y + 1 , z = pos.z } ,
{ x = pos.x , y = pos.y - 1 , z = pos.z } ,
{ x = pos.x , y = pos.y , z = pos.z + 1 } ,
{ x = pos.x , y = pos.y , z = pos.z - 1 } ,
}
2018-06-19 20:09:16 -07:00
2018-06-19 20:30:54 -07:00
local stack = { }
local found = 0
-- check neighbors for network membership
for _ , p in ipairs ( check_pos ) do
local h = minetest.hash_node_position ( p )
local lphash = net_members [ h ]
if lphash ~= nil then
local lpnet = networks [ lphash ]
-- only process pipes/fixtures on the same network as the destroyed pipe
if lpnet and lpnet.name == pnet.name then
stack [ h ] = vector.new ( p )
found = found + 1
--print("check stack: "..p.x..","..p.y..","..p.z)
else
print ( " no lpnet " )
end
else
print ( " no lphash " .. p.x .. " , " .. p.y .. " , " .. p.z )
end
2018-06-19 20:09:16 -07:00
end
2018-06-19 20:30:54 -07:00
-- don't need to split the network if this was just on the end
if found > 1 then
--print("check to split the network")
for h , p in pairs ( stack ) do
print ( dump ( p ) )
print ( dump ( h ) )
-- BUG: spouts and intakes can be counted as pipes when walking the network
-- just rename the net
local new_pnet = rebase_network ( p )
-- print("split off pnet ".. new_pnet.name .. " at " .. minetest.pos_to_string(p))
-- all fluid is lost in the network atm
-- some networks might get orphaned, for example, the first
-- net to be rebased in a loop
end
end
2018-06-19 20:09:16 -07:00
2018-06-19 20:30:54 -07:00
save_data ( )
2018-06-19 20:09:16 -07:00
end
2018-06-17 20:48:04 -07:00
-- used by external machines to find the network for a node
bitumen.pipes . get_net = function ( pos )
local hash = minetest.hash_node_position ( pos )
local phash = net_members [ hash ]
if phash == nil then
return nil , nil , hash
end
return networks [ phash ] , phash , hash
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
-- used by external machines to add fluid into the pipe network
2018-06-19 20:09:16 -07:00
-- returns amount of fluid successfully pushed into the network
2018-06-17 20:48:04 -07:00
bitumen.pipes . push_fluid = function ( pos , fluid , amount , extra_pressure )
local hash = minetest.hash_node_position ( pos )
local phash = net_members [ hash ]
if phash == nil then
2018-06-22 23:31:23 -07:00
print ( " no network to push to " )
2018-06-17 20:48:04 -07:00
return 0 -- no network
end
local pnet = networks [ phash ]
if pnet.fluid == ' air ' or pnet.buffer == 0 then
2018-06-19 20:09:16 -07:00
if minetest.registered_nodes [ fluid ]
and minetest.registered_nodes [ fluid ] . groups.petroleum ~= nil then
2018-06-17 20:48:04 -07:00
-- BUG: check for "full" nodes
pnet.fluid = fluid
else
2018-06-22 23:31:23 -07:00
print ( " here " .. fluid .. " " .. dump ( minetest.registered_nodes [ fluid ] ) )
2018-06-17 20:48:04 -07:00
return 0 -- no available liquids
end
else -- only suck in existing fluid
if fluid ~= pnet.fluid and fluid ~= pnet.fluid .. " _full " then
2018-06-22 23:31:23 -07:00
--print("wrong fluid")
2018-06-17 20:48:04 -07:00
return 0
end
end
2018-06-19 20:09:16 -07:00
if amount < 0 then
print ( " !!!!!!!!!!!! push amount less than zero? " )
2018-06-17 20:48:04 -07:00
return 0
end
local input_pres = pos.y + extra_pressure
pnet.in_pressure = pnet.in_pressure or - 32000
if pnet.in_pressure > input_pres then
print ( " backflow at intake: " .. pnet.in_pressure .. " > " .. input_pres )
return 0
end
pnet.in_pressure = math.max ( pnet.in_pressure , input_pres )
2018-06-22 23:31:23 -07:00
print ( " net pressure: " .. pnet.in_pressure )
2018-06-17 20:48:04 -07:00
local rate = amount --math.max(1, math.ceil(ulevel / 2))
local cap = 64
local take = math.max ( 0 , math.min ( amount , cap - pnet.buffer ) )
pnet.buffer = pnet.buffer + take
return take
2013-10-14 18:46:45 -07:00
end
2018-06-18 19:42:02 -07:00
-- used by external machines to remove fluid from the pipe network
-- returns amount and fluid type
2018-06-19 20:30:54 -07:00
bitumen.pipes . take_fluid = function ( pos , max_amount , backpressure )
2018-06-18 19:42:02 -07:00
local hash = minetest.hash_node_position ( pos )
local phash = net_members [ hash ]
if phash == nil then
return 0 , " air " -- no network
end
local pnet = networks [ phash ]
if pnet.buffer <= 0 then
--print("spout: no water in pipe")
return 0 , " air " -- no water in the pipe
end
-- hack
pnet.in_pressure = pnet.in_pressure or - 32000
if pnet.in_pressure <= pos.y then
print ( " insufficient pressure at spout: " .. pnet.in_pressure .. " < " .. pos.y )
return 0 , " air "
end
local take = math.min ( pnet.buffer , max_amount )
pnet.buffer = pnet.buffer - take
2018-06-19 20:30:54 -07:00
if pnet.buffer == 0 then
-- print("pipe drained " .. pnet.name) -- BUG: there might be a bug where a low pressure input can add fluid to the network with a higher spout, then a higher intake with low flow can raise the pressure of the previous fluid to the level of the comparatively lower spout
pnet.in_pressure = backpressure or pos.y
end
2018-06-18 19:42:02 -07:00
return take , pnet.fluid
end
2013-10-14 18:46:45 -07:00
2018-06-19 20:09:16 -07:00
-- take or push into the network, based on given pressure
-- returns change in fluid and fluid type
-- negative if fluid was pushed into the network
-- positive if fluid was taken from the network
bitumen.pipes . buffer = function ( pos , fluid , my_pres , avail_push , cap_take , can_change_fluid )
local hash = minetest.hash_node_position ( pos )
local phash = net_members [ hash ]
if phash == nil then
2018-06-19 20:30:54 -07:00
print ( " no net " )
2018-06-19 20:09:16 -07:00
return 0 , " air " -- no network
end
local pnet = networks [ phash ]
2018-06-19 20:30:54 -07:00
-- print("pressure ["..pnet.name.."] ".. pnet.in_pressure .. " - " .. my_pres)
2018-06-19 20:09:16 -07:00
if pnet.in_pressure <= my_pres then
-- push into the network
2018-06-19 20:30:54 -07:00
-- print("push")
2018-06-19 20:09:16 -07:00
return - bitumen.pipes . push_fluid ( pos , fluid , avail_push , my_pres ) , fluid
else
2018-06-19 20:30:54 -07:00
-- print("take")
2018-06-19 20:09:16 -07:00
if pnet.fluid == fluid or can_change_fluid then
2018-06-19 20:30:54 -07:00
-- print("can take " .. cap_take)
return bitumen.pipes . take_fluid ( pos , cap_take , my_pres )
2018-06-19 20:09:16 -07:00
else
2018-06-19 20:30:54 -07:00
-- print("wrong type ".. pnet.fluid .. " - ".. fluid)
2018-06-19 20:09:16 -07:00
return 0 , " air " -- wrong fluid type
end
end
end
2018-06-17 20:48:04 -07:00
minetest.register_node ( " bitumen:intake " , {
2018-06-18 19:42:02 -07:00
description = " Petroleum Intake " ,
2018-06-17 20:48:04 -07:00
drawtype = " nodebox " ,
node_box = {
type = " connected " ,
fixed = { { - .1 , - .1 , - .1 , .1 , .1 , .1 } } ,
-- connect_bottom =
connect_front = { { - .1 , - .1 , - .5 , .1 , .1 , .1 } } ,
connect_left = { { - .5 , - .1 , - .1 , - .1 , .1 , .1 } } ,
connect_back = { { - .1 , - .1 , .1 , .1 , .1 , .5 } } ,
connect_right = { { .1 , - .1 , - .1 , .5 , .1 , .1 } } ,
connect_bottom = { { - .1 , - .5 , - .1 , .1 , .1 , .1 } } ,
} ,
connects_to = { " group:petroleum_pipe " , " group:petroleum_fixture " } ,
paramtype = " light " ,
is_ground_content = false ,
tiles = { " default_tin_block.png " } ,
walkable = true ,
groups = { cracky = 3 , petroleum_fixture = 1 , } ,
on_construct = function ( pos )
print ( " \n intake placed at " .. pos.x .. " , " .. pos.y .. " , " .. pos.z )
local found_net , merge_list = check_merge ( pos )
if found_net == 0 then
local hash = minetest.hash_node_position ( pos )
local net = new_network ( pos )
2018-07-06 23:55:38 -07:00
net.in_pressure = pos.y
2018-06-17 20:48:04 -07:00
net.inputs [ hash ] = 1
end
try_merge ( merge_list )
save_data ( )
2018-06-19 20:30:54 -07:00
end ,
after_destruct = bitumen.pipes . after_destruct ,
2018-06-17 20:48:04 -07:00
} )
minetest.register_abm ( {
nodenames = { " bitumen:intake " } ,
neighbors = { " group:petroleum " } ,
interval = 1 ,
chance = 1 ,
action = function ( pos )
local hash = minetest.hash_node_position ( pos )
pos.y = pos.y + 1
local unode = minetest.get_node ( pos )
local phash = net_members [ hash ]
local pnet = networks [ phash ]
if pnet.fluid == ' air ' or pnet.buffer == 0 then
if minetest.registered_nodes [ unode.name ] . groups.petroleum ~= nil then
-- BUG: check for "full" nodes
pnet.fluid = unode.name
else
return -- no available liquids
end
else -- only suck in existing fluid
if unode.name ~= pnet.fluid and unode.name ~= pnet.fluid .. " _full " then
--print("no water near intake")
return
end
end
local ulevel = minetest.get_node_level ( pos )
if ulevel < 1 then
print ( " !!!!!!!!!!!! intake level less than one? " )
return
end
pnet.in_pressure = pnet.in_pressure or - 32000
if pnet.in_pressure > pos.y - 1 then
print ( " backflow at intake: " .. pnet.in_pressure .. " > " .. ( pos.y - 1 ) )
return
end
pnet.in_pressure = math.max ( pnet.in_pressure , pos.y - 1 )
local rate = math.max ( 1 , math.ceil ( ulevel / 2 ) )
local cap = 64
local take = math.max ( 0 , math.min ( ulevel , cap - pnet.buffer ) )
pnet.buffer = pnet.buffer + take
--print("intake took "..take.. " water")
if ulevel - rate > 0 then
minetest.set_node_level ( pos , ulevel - take )
else
minetest.set_node ( pos , { name = " air " } )
end
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
} )
minetest.register_node ( " bitumen:spout " , {
2018-06-18 19:42:02 -07:00
description = " Petroleum Spout " ,
2018-06-17 20:48:04 -07:00
drawtype = " nodebox " ,
node_box = {
type = " connected " ,
fixed = { { - .1 , - .1 , - .1 , .1 , .1 , .1 } } ,
-- connect_bottom =
connect_front = { { - .1 , - .1 , - .5 , .1 , .1 , .1 } } ,
connect_left = { { - .5 , - .1 , - .1 , - .1 , .1 , .1 } } ,
connect_back = { { - .1 , - .1 , .1 , .1 , .1 , .5 } } ,
connect_right = { { .1 , - .1 , - .1 , .5 , .1 , .1 } } ,
connect_top = { { - .1 , - .1 , - .1 , .1 , .5 , .1 } } ,
} ,
connects_to = { " group:petroleum_pipe " , " group:petroleum_fixture " } ,
paramtype = " light " ,
is_ground_content = false ,
tiles = { " default_copper_block.png " } ,
walkable = true ,
groups = { cracky = 3 , petroleum_fixture = 1 , } ,
on_construct = function ( pos )
print ( " \n spout placed at " .. pos.x .. " , " .. pos.y .. " , " .. pos.z )
local found_net , merge_list = check_merge ( pos )
if found_net == 0 then
local hash = minetest.hash_node_position ( pos )
local pnet = new_network ( pos )
pnet.outputs [ hash ] = 1
end
try_merge ( merge_list )
save_data ( )
2018-06-19 20:30:54 -07:00
end ,
after_destruct = bitumen.pipes . after_destruct ,
2018-06-17 20:48:04 -07:00
} )
minetest.register_abm ( {
nodenames = { " bitumen:spout " } ,
-- neighbors = {"group:fresh_water"},
interval = 1 ,
chance = 1 ,
action = function ( pos )
local hash = minetest.hash_node_position ( pos )
local phash = net_members [ hash ]
local pnet = networks [ phash ]
if pnet.buffer <= 0 then
--print("spout: no water in pipe")
return -- no water in the pipe
end
-- hack
pnet.in_pressure = pnet.in_pressure or - 32000
if pnet.in_pressure <= pos.y then
print ( " insufficient pressure at spout: " .. pnet.in_pressure .. " < " .. pos.y )
return
end
pos.y = pos.y - 1
local bnode = minetest.get_node ( pos )
local avail = 10 -- pnet.buffer / #pnet.outputs
if bnode.name == pnet.fluid then
local blevel = minetest.get_node_level ( pos )
local cap = 64 - blevel
local out = math.min ( cap , math.min ( avail , cap ) )
--print("cap: ".. cap .." avail: ".. avail .. " out: "..out)
pnet.buffer = pnet.buffer - out
minetest.set_node_level ( pos , blevel + out )
elseif bnode.name == " air " then
local out = math.min ( 64 , math.max ( 0 , avail ) )
pnet.buffer = pnet.buffer - out
minetest.set_node ( pos , { name = pnet.fluid } )
minetest.set_node_level ( pos , out )
end
2013-10-14 18:46:45 -07:00
end
2018-06-17 20:48:04 -07:00
} )
minetest.register_node ( " bitumen:pipe " , {
2018-06-18 19:42:02 -07:00
description = " petroleum pipe " ,
2018-06-17 20:48:04 -07:00
drawtype = " nodebox " ,
node_box = {
type = " connected " ,
fixed = { { - .1 , - .1 , - .1 , .1 , .1 , .1 } } ,
-- connect_bottom =
connect_front = { { - .1 , - .1 , - .5 , .1 , .1 , .1 } } ,
connect_left = { { - .5 , - .1 , - .1 , - .1 , .1 , .1 } } ,
connect_back = { { - .1 , - .1 , .1 , .1 , .1 , .5 } } ,
connect_right = { { .1 , - .1 , - .1 , .5 , .1 , .1 } } ,
connect_top = { { - .1 , - .1 , - .1 , .1 , .5 , .1 } } ,
connect_bottom = { { - .1 , - .5 , - .1 , .1 , .1 , .1 } } ,
} ,
connects_to = { " group:petroleum_pipe " , " group:petroleum_fixture " } ,
paramtype = " light " ,
is_ground_content = false ,
tiles = { " default_steel_block.png " } ,
walkable = true ,
groups = { cracky = 3 , petroleum_pipe = 1 , } ,
on_construct = function ( pos )
print ( " \n pipe placed at " .. pos.x .. " , " .. pos.y .. " , " .. pos.z )
local found_net , merge_list = check_merge ( pos )
if found_net == 0 then
local net = new_network ( pos )
end
try_merge ( merge_list )
save_data ( )
end ,
2018-06-19 20:30:54 -07:00
after_destruct = bitumen.pipes . after_destruct ,
2018-06-17 20:48:04 -07:00
} )
2018-06-19 20:09:16 -07:00