Skip to content

Commit

Permalink
Merge pull request #136 from mt-mods/async-net-build
Browse files Browse the repository at this point in the history
Async network build, remove switch_max_range
  • Loading branch information
S-S-X authored Dec 9, 2020
2 parents 6a7b4ec + 5e78d73 commit 19823f3
Show file tree
Hide file tree
Showing 5 changed files with 317 additions and 311 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ Recommended mods that build on the `technic mod`:
| quarry_time_limit | max cpu time in μs allowed per quarry step. |
| quarry_dig_above_nodes | begin digging this many nodes above quarry node. |
| network_overload_reset_time | After network conflict wait this many seconds before attempting to activate conflicting networks again. |
| switch_max_range | max cable length. |
| switch_off_delay_seconds | switching station off delay. |

See defaults for settings here: [technic/config.lua](https://github.com/mt-mods/technic/blob/master/technic/config.lua)
Expand All @@ -143,6 +142,8 @@ See defaults for settings here: [technic/config.lua](https://github.com/mt-mods/

* **/technic_flush_switch_cache** clears the switching station cache (stops all unloaded switches)
* **/powerctrl [on|off]** enable/disable technic power distribution globally
* **/technic_get_active_networks [minlag]** list all active networks with additional network data
* **/technic_clear_network_data** removes all networks and network nodes from the cache

# Contributors

Expand Down
1 change: 0 additions & 1 deletion technic/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ local defaults = {
enable_entity_radiation_damage = "true",
enable_longterm_radiation_damage = "true",
enable_nuclear_reactor_digiline_selfdestruct = "false",
switch_max_range = "256",
switch_off_delay_seconds = "1800",
network_overload_reset_time = "20",
admin_priv = "basic_privs",
Expand Down
207 changes: 133 additions & 74 deletions technic/machines/network.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
--
local S = technic.getter

local switch_max_range = tonumber(technic.config:get("switch_max_range"))
local off_delay_seconds = tonumber(technic.config:get("switch_off_delay_seconds"))

local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}

technic.active_networks = {}
local networks = {}
technic.networks = networks
Expand All @@ -21,6 +22,52 @@ function technic.create_network(sw_pos)
return network_id
end

local function pos_in_array(pos, array)
for _,pos2 in ipairs(array) do
if pos.x == pos2.x and pos.y == pos2.y and pos.z == pos2.z then
return true
end
end
return false
end

function technic.merge_networks(net1, net2)
-- TODO: Optimize for merging small network into larger by first checking network
-- node counts for both networks and keep network id with most nodes.
assert(type(net1) == "table", "Invalid net1 for technic.merge_networks")
assert(type(net2) == "table", "Invalid net2 for technic.merge_networks")
assert(net1 ~= net2, "Deadlock recipe: net1 & net2 equals for technic.merge_networks")
-- Move data in cables table
for node_id,cable_net_id in pairs(cables) do
if cable_net_id == net2.id then
cables[node_id] = net1.id
end
end
-- Move data in machine tables
for _,tablename in ipairs(network_node_arrays) do
for _,pos in ipairs(net2[tablename]) do
table.insert(net1[tablename], pos)
end
end
-- Move data in all_nodes table
for node_id,pos in pairs(net2.all_nodes) do
net1.all_nodes[node_id] = pos
end
-- Merge queues for incomplete networks
if net1.queue and net2.queue then
for _,pos in ipairs(net2.queue) do
if not pos_in_array(pos, net1.queue) then
table.insert(net1.queue, pos)
end
end
else
net1.queue = net1.queue or net2.queue
end
-- Remove links to net2
networks[net2.id] = nil
technic.active_networks[net2.id] = nil
end

function technic.activate_network(network_id, timeout)
-- timeout is optional ttl for network in seconds, if not specified use default
local network = networks[network_id]
Expand Down Expand Up @@ -53,7 +100,6 @@ function technic.remove_network(network_id)
end

-- Remove machine or cable from network
local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
function technic.remove_network_node(network_id, pos)
local network = networks[network_id]
if not network then return end
Expand Down Expand Up @@ -109,11 +155,18 @@ function technic.network2sw_pos(network_id)
end

function technic.network_infotext(network_id, text)
if networks[network_id] == nil then return end
if text then
networks[network_id].infotext = text
else
return networks[network_id].infotext
local network = networks[network_id]
if network then
if text then
network.infotext = text
elseif network.queue then
local count = #network.PR_nodes
+ #network.RE_nodes
+ #network.BA_nodes
return S("Building Network: %d Nodes"):format(count)
else
return network.infotext
end
end
end

Expand Down Expand Up @@ -217,54 +270,52 @@ local function add_network_machine(nodes, pos, network_id, all_nodes, multitier)
end

-- Add a wire node to the LV/MV/HV network
local function add_cable_node(nodes, pos, network_id, queue)
local function add_cable_node(pos, network)
local node_id = poshash(pos)
if not cables[node_id] then
cables[node_id] = network_id
nodes[node_id] = pos
table.insert(queue, pos)
cables[node_id] = network.id
network.all_nodes[node_id] = pos
if network.queue then
table.insert(network.queue, pos)
end
elseif cables[node_id] ~= network.id then
-- Conflicting network connected, merge networks if both are still in building stage
local net2 = networks[cables[node_id]]
if net2 and net2.queue then
technic.merge_networks(network, net2)
end
end
end

-- Generic function to add found connected nodes to the right classification array
local function add_network_node(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, machines, tier, network_id, queue)
local function add_network_node(network, pos, machines)
technic.get_or_load_node(pos)
local name = minetest.get_node(pos).name

if technic.is_tier_cable(name, tier) then
add_cable_node(all_nodes, pos, network_id, queue)
if technic.is_tier_cable(name, network.tier) then
add_cable_node(pos, network)
elseif machines[name] then
if machines[name] == technic.producer then
add_network_machine(PR_nodes, pos, network_id, all_nodes)
add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.receiver then
add_network_machine(RE_nodes, pos, network_id, all_nodes)
add_network_machine(network.RE_nodes, pos, network.id, network.all_nodes)
elseif machines[name] == technic.producer_receiver then
if add_network_machine(PR_nodes, pos, network_id, all_nodes, true) then
table.insert(RE_nodes, pos)
if add_network_machine(network.PR_nodes, pos, network.id, network.all_nodes, true) then
table.insert(network.RE_nodes, pos)
end
elseif machines[name] == technic.battery then
add_network_machine(BA_nodes, pos, network_id, all_nodes)
add_network_machine(network.BA_nodes, pos, network.id, network.all_nodes)
end
end
end

-- Generic function to add single nodes to the right classification array of existing network
function technic.add_network_node(pos, network)
add_network_node(
network.PR_nodes,
network.RE_nodes,
network.BA_nodes,
network.all_nodes,
pos,
technic.machines[network.tier],
network.tier,
network.id,
{}
)
add_network_node(network, pos, technic.machines[network.tier])
end

-- Traverse a network given a list of machines and a cable type name
local function traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, machines, tier, network_id, queue)
local function traverse_network(network, pos, machines)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
Expand All @@ -273,8 +324,8 @@ local function traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, ma
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1}}
for i, cur_pos in pairs(positions) do
if not all_nodes[poshash(cur_pos)] then
add_network_node(PR_nodes, RE_nodes, BA_nodes, all_nodes, cur_pos, machines, tier, network_id, queue)
if not network.all_nodes[poshash(cur_pos)] then
add_network_node(network, cur_pos, machines)
end
end
end
Expand All @@ -287,7 +338,7 @@ end

local function get_network(network_id, tier)
local cached = networks[network_id]
if cached and cached.tier == tier then
if cached and not cached.queue and cached.tier == tier then
touch_nodes(cached.PR_nodes, tier)
touch_nodes(cached.BA_nodes, tier)
touch_nodes(cached.RE_nodes, tier)
Expand All @@ -298,27 +349,23 @@ end

function technic.add_network_branch(queue, network)
-- Adds whole branch to network, queue positions can be used to bypass sub branches
local PR_nodes = network.PR_nodes -- Indexed array
local BA_nodes = network.BA_nodes -- Indexed array
local RE_nodes = network.RE_nodes -- Indexed array
local all_nodes = network.all_nodes -- Hash table
local network_id = network.id
local tier = network.tier
local machines = technic.machines[tier]
local sw_pos = technic.network2sw_pos(network_id)
local machines = technic.machines[network.tier]
--print(string.format("technic.add_network_branch(%s, %s, %.17g)",queue,minetest.pos_to_string(sw_pos),network.id))
local t1 = minetest.get_us_time()
while next(queue) do
local to_visit = {}
for _, pos in ipairs(queue) do
if vector.distance(pos, sw_pos) > switch_max_range then
-- max range exceeded
return
end
traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos,
machines, tier, network_id, to_visit)
network.queue = to_visit
traverse_network(network, pos, machines)
end
queue = to_visit
if minetest.get_us_time() - t1 > 10000 then
-- time limit exceeded
break
end
end
-- Set build queue for network if network build was not finished within time limits
network.queue = #queue > 0 and queue
end

-- Battery charge status updates for network
Expand Down Expand Up @@ -348,34 +395,46 @@ local function sma(period)
end

function technic.build_network(network_id)
technic.remove_network(network_id)
local sw_pos = technic.network2sw_pos(network_id)
local tier = technic.sw_pos2tier(sw_pos)
if not tier then
return
end
local network = {
-- Basic network data and lookup table for attached nodes (no switching stations)
id = network_id, tier = tier, all_nodes = {},
-- Indexed arrays for iteration by machine type
PR_nodes = {}, RE_nodes = {}, BA_nodes = {},
-- Power generation, usage and capacity related variables
supply = 0, demand = 0, battery_charge = 0, battery_charge_max = 0,
BA_count_active = 0, BA_charge_active = 0, battery_supply = 0, battery_demand = 0,
-- Battery status update function
update_battery = update_battery,
-- Network activation and excution control
timeout = 0, skip = 0, lag = 0, average_lag = sma(5)
}
-- Add first cable (one that is holding network id) and build network
local queue = {}
add_cable_node(network.all_nodes, technic.network2pos(network_id), network_id, queue)
technic.add_network_branch(queue, network)
local network = networks[network_id]
if network and not network.queue then
-- Network exists complete and cached
return network.PR_nodes, network.BA_nodes, network.RE_nodes
elseif not network then
-- Build new network if network does not exist
technic.remove_network(network_id)
local sw_pos = technic.network2sw_pos(network_id)
local tier = sw_pos and technic.sw_pos2tier(sw_pos)
if not tier then
-- Failed to get initial cable node for network
return
end
network = {
-- Build queue
queue = {},
-- Basic network data and lookup table for attached nodes (no switching stations)
id = network_id, tier = tier, all_nodes = {},
-- Indexed arrays for iteration by machine type
PR_nodes = {}, RE_nodes = {}, BA_nodes = {},
-- Power generation, usage and capacity related variables
supply = 0, demand = 0, battery_charge = 0, battery_charge_max = 0,
BA_count_active = 0, BA_charge_active = 0, battery_supply = 0, battery_demand = 0,
-- Battery status update function
update_battery = update_battery,
-- Network activation and excution control
timeout = 0, skip = 0, lag = 0, average_lag = sma(5)
}
-- Add first cable (one that is holding network id) and build network
add_cable_node(technic.network2pos(network_id), network)
end
-- Continue building incomplete network
technic.add_network_branch(network.queue, network)
network.battery_count = #network.BA_nodes
-- Add newly built network to cache array
networks[network_id] = network
-- And return producers, batteries and receivers (should this simply return network?)
return network.PR_nodes, network.BA_nodes, network.RE_nodes
if not network.queue then
-- And return producers, batteries and receivers (should this simply return network?)
return network.PR_nodes, network.BA_nodes, network.RE_nodes
end
end

--
Expand Down Expand Up @@ -426,7 +485,7 @@ function technic.network_run(network_id)
local network
if tier then
PR_nodes, BA_nodes, RE_nodes = get_network(network_id, tier)
if technic.is_overloaded(network_id) then return end
if not PR_nodes or technic.is_overloaded(network_id) then return end
network = networks[network_id]
else
--dprint("Not connected to a network")
Expand Down
2 changes: 1 addition & 1 deletion technic/spec/fixtures/mineunit
Loading

0 comments on commit 19823f3

Please sign in to comment.