Skip to content

Commit 39d7869

Browse files
committed
Network code refactoring
1 parent d6802c2 commit 39d7869

File tree

4 files changed

+234
-239
lines changed

4 files changed

+234
-239
lines changed

technic/machines/init.lua

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ dofile(path.."/compat/digtron.lua")
2727

2828
dofile(path.."/network.lua")
2929

30+
dofile(path.."/overload.lua")
31+
3032
dofile(path.."/register/init.lua")
3133

3234
-- Tiers

technic/machines/network.lua

+194-72
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
1010
technic.active_networks = {}
1111
local networks = {}
1212
technic.networks = networks
13-
local cables = {}
14-
technic.cables = cables
13+
local technic_cables = {}
14+
technic.cables = technic_cables
1515

1616
local poshash = minetest.hash_node_position
1717
local hashpos = minetest.get_position_from_hash
@@ -38,9 +38,9 @@ function technic.merge_networks(net1, net2)
3838
assert(type(net2) == "table", "Invalid net2 for technic.merge_networks")
3939
assert(net1 ~= net2, "Deadlock recipe: net1 & net2 equals for technic.merge_networks")
4040
-- Move data in cables table
41-
for node_id,cable_net_id in pairs(cables) do
41+
for node_id,cable_net_id in pairs(technic_cables) do
4242
if cable_net_id == net2.id then
43-
cables[node_id] = net1.id
43+
technic_cables[node_id] = net1.id
4444
end
4545
end
4646
-- Move data in machine tables
@@ -90,52 +90,21 @@ end
9090

9191
-- Destroy network data
9292
function technic.remove_network(network_id)
93-
for pos_hash,cable_net_id in pairs(cables) do
93+
for pos_hash,cable_net_id in pairs(technic_cables) do
9494
if cable_net_id == network_id then
95-
cables[pos_hash] = nil
95+
technic_cables[pos_hash] = nil
9696
end
9797
end
9898
networks[network_id] = nil
9999
technic.active_networks[network_id] = nil
100100
end
101101

102-
-- Remove machine or cable from network
103-
function technic.remove_network_node(network_id, pos)
104-
local network = networks[network_id]
105-
if not network then return end
106-
-- Clear hash tables, cannot use table.remove
107-
local node_id = poshash(pos)
108-
cables[node_id] = nil
109-
network.all_nodes[node_id] = nil
110-
-- TODO: All following things can be skipped if node is not machine
111-
-- check here if it is or is not cable
112-
-- or add separate function to remove cables and move responsibility to caller
113-
-- Clear indexed arrays, do NOT leave holes
114-
local machine_removed = false
115-
for _,tblname in ipairs(network_node_arrays) do
116-
local tbl = network[tblname]
117-
for i=#tbl,1,-1 do
118-
local mpos = tbl[i]
119-
if mpos.x == pos.x and mpos.y == pos.y and mpos.z == pos.z then
120-
table.remove(tbl, i)
121-
machine_removed = true
122-
break
123-
end
124-
end
125-
end
126-
if machine_removed then
127-
-- Machine can still be in world, just not connected to any network. If so then disable it.
128-
local node = minetest.get_node(pos)
129-
technic.disable_machine(pos, node)
130-
end
131-
end
132-
133102
function technic.sw_pos2network(pos)
134-
return cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
103+
return technic_cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
135104
end
136105

137106
function technic.pos2network(pos)
138-
return cables[poshash(pos)]
107+
return technic_cables[poshash(pos)]
139108
end
140109

141110
function technic.network2pos(network_id)
@@ -208,38 +177,176 @@ function technic.disable_machine(pos, node)
208177
end
209178
end
210179

211-
--
212-
-- Network overloading (incomplete cheat mitigation)
213-
--
214-
local overload_reset_time = tonumber(technic.config:get("network_overload_reset_time"))
215-
local overloaded_networks = {}
180+
local function match_cable_tier_filter(name, tiers)
181+
-- Helper to check for set of cable tiers
182+
if tiers then
183+
for _, tier in ipairs(tiers) do if technic.is_tier_cable(name, tier) then return true end end
184+
return false
185+
end
186+
return technic.get_cable_tier(name) ~= nil
187+
end
216188

217-
local function overload_network(network_id)
218-
local network = networks[network_id]
219-
if network then
220-
network.supply = 0
221-
network.battery_charge = 0
189+
local function get_neighbors(pos, tiers)
190+
local tier_machines = tiers and technic.machines[tiers[1]]
191+
local is_cable = match_cable_tier_filter(minetest.get_node(pos).name, tiers)
192+
local network = is_cable and technic.networks[technic.pos2network(pos)]
193+
local cables = {}
194+
local machines = {}
195+
local positions = {
196+
{x=pos.x+1, y=pos.y, z=pos.z},
197+
{x=pos.x-1, y=pos.y, z=pos.z},
198+
{x=pos.x, y=pos.y+1, z=pos.z},
199+
{x=pos.x, y=pos.y-1, z=pos.z},
200+
{x=pos.x, y=pos.y, z=pos.z+1},
201+
{x=pos.x, y=pos.y, z=pos.z-1},
202+
}
203+
for _,connected_pos in ipairs(positions) do
204+
local name = minetest.get_node(connected_pos).name
205+
if tier_machines and tier_machines[name] then
206+
table.insert(machines, connected_pos)
207+
elseif match_cable_tier_filter(name, tiers) then
208+
local cable_network = technic.networks[technic.pos2network(connected_pos)]
209+
table.insert(cables,{
210+
pos = connected_pos,
211+
network = cable_network,
212+
})
213+
if not network then network = cable_network end
214+
end
222215
end
223-
overloaded_networks[network_id] = minetest.get_us_time() + (overload_reset_time * 1000 * 1000)
216+
return network, cables, machines
224217
end
225-
technic.overload_network = overload_network
226218

227-
local function reset_overloaded(network_id)
228-
local remaining = math.max(0, overloaded_networks[network_id] - minetest.get_us_time())
229-
if remaining == 0 then
230-
-- Clear cache, remove overload and restart network
231-
technic.remove_network(network_id)
232-
overloaded_networks[network_id] = nil
219+
function technic.place_network_node(pos, tiers, name)
220+
-- Get connections and primary network if there's any
221+
local network, cables, machines = get_neighbors(pos, tiers)
222+
if not network then
223+
-- We're evidently not on a network, nothing to add ourselves to
224+
return
225+
end
226+
227+
-- Attach to primary network, this must be done before building branches from this position
228+
technic.add_network_node(pos, network)
229+
if not match_cable_tier_filter(name, tiers) then
230+
if technic.machines[tiers[1]][name] == technic.producer_receiver then
231+
-- FIXME: Multi tier machine like supply converter should also attach to other networks around pos.
232+
-- Preferably also with connection rules defined for machine.
233+
-- nodedef.connect_sides could be used to generate these rules.
234+
-- For now, assume that all multi network machines belong to technic.producer_receiver group:
235+
-- Get cables and networks around PR_RE machine
236+
local _, machine_cables, _ = get_neighbors(pos)
237+
for _,connection in ipairs(machine_cables) do
238+
if connection.network and connection.network.id ~= network.id then
239+
-- Attach PR_RE machine to secondary networks (last added is primary until above note is resolved)
240+
technic.add_network_node(pos, connection.network)
241+
end
242+
end
243+
else
244+
-- Check connected cables for foreign networks, overload if machine was connected to multiple networks
245+
for _, connection in ipairs(cables) do
246+
if connection.network and connection.network.id ~= network.id then
247+
technic.overload_network(connection.network.id)
248+
technic.overload_network(network.id)
249+
end
250+
end
251+
end
252+
-- Machine added, skip all network building
253+
return
254+
end
255+
256+
-- Attach neighbor machines if cable was added
257+
for _,machine_pos in ipairs(machines) do
258+
technic.add_network_node(machine_pos, network)
259+
end
260+
261+
-- Attach neighbor cables
262+
for _,connection in ipairs(cables) do
263+
if connection.network then
264+
if connection.network.id ~= network.id then
265+
-- Remove network if position belongs to another network
266+
-- FIXME: Network requires partial rebuild but avoid doing it here if possible.
267+
-- This might cause problems when merging two active networks into one
268+
technic.remove_network(network.id)
269+
technic.remove_network(connection.network.id)
270+
connection.network = nil
271+
end
272+
else
273+
-- There's cable that does not belong to any network, attach whole branch
274+
technic.add_network_node(connection.pos, network)
275+
technic.add_network_branch({connection.pos}, network)
276+
end
277+
end
278+
end
279+
280+
-- Remove machine or cable from network
281+
local function remove_network_node(network_id, pos)
282+
local network = networks[network_id]
283+
if not network then return end
284+
-- Clear hash tables, cannot use table.remove
285+
local node_id = poshash(pos)
286+
technic_cables[node_id] = nil
287+
network.all_nodes[node_id] = nil
288+
-- TODO: All following things can be skipped if node is not machine
289+
-- check here if it is or is not cable
290+
-- or add separate function to remove cables and move responsibility to caller
291+
-- Clear indexed arrays, do NOT leave holes
292+
local machine_removed = false
293+
for _,tblname in ipairs(network_node_arrays) do
294+
local tbl = network[tblname]
295+
for i=#tbl,1,-1 do
296+
local mpos = tbl[i]
297+
if mpos.x == pos.x and mpos.y == pos.y and mpos.z == pos.z then
298+
table.remove(tbl, i)
299+
machine_removed = true
300+
break
301+
end
302+
end
303+
end
304+
if machine_removed then
305+
-- Machine can still be in world, just not connected to any network. If so then disable it.
306+
local node = minetest.get_node(pos)
307+
technic.disable_machine(pos, node)
233308
end
234-
-- Returns 0 when network reset or remaining time if reset timer has not expired yet
235-
return remaining
236309
end
237-
technic.reset_overloaded = reset_overloaded
238310

239-
local function is_overloaded(network_id)
240-
return overloaded_networks[network_id]
311+
function technic.remove_network_node(pos, tiers, name)
312+
-- Get the network and neighbors
313+
local network, cables, machines = get_neighbors(pos, tiers)
314+
if not network then return end
315+
316+
if not match_cable_tier_filter(name, tiers) then
317+
-- Machine removed, skip cable checks to prevent unnecessary network cleanups
318+
for _,connection in ipairs(cables) do
319+
if connection.network then
320+
-- Remove machine from all networks around it
321+
remove_network_node(connection.network.id, pos)
322+
end
323+
end
324+
return
325+
end
326+
327+
if #cables == 1 then
328+
-- Dead end cable removed, remove it from the network
329+
remove_network_node(network.id, pos)
330+
-- Remove neighbor machines from network if cable was removed
331+
if match_cable_tier_filter(name, tiers) then
332+
for _,machine_pos in ipairs(machines) do
333+
local net, _, _ = get_neighbors(machine_pos, tiers)
334+
if not net then
335+
-- Remove machine from network if it does not have other connected cables
336+
remove_network_node(network.id, machine_pos)
337+
end
338+
end
339+
end
340+
else
341+
-- TODO: Check branches around and switching stations for branches:
342+
-- remove branches that do not have switching station. Switching stations not tracked but could be easily tracked.
343+
-- remove branches not connected to another branch. Individual branches not tracked, requires simple AI heuristics.
344+
-- move branches that have switching station to new networks without checking or loading actual nodes in world.
345+
-- To do all this network must be aware of individual branches and switching stations, might not be worth it...
346+
-- For now remove whole network and let ABM rebuild it
347+
technic.remove_network(network.id)
348+
end
241349
end
242-
technic.is_overloaded = is_overloaded
243350

244351
--
245352
-- Functions to traverse the electrical network
@@ -248,18 +355,18 @@ technic.is_overloaded = is_overloaded
248355
-- Add a machine node to the LV/MV/HV network
249356
local function add_network_machine(nodes, pos, network_id, all_nodes, multitier)
250357
local node_id = poshash(pos)
251-
local net_id_old = cables[node_id]
358+
local net_id_old = technic_cables[node_id]
252359
if net_id_old == nil or (multitier and net_id_old ~= network_id and all_nodes[node_id] == nil) then
253360
-- Add machine to network only if it is not already added
254361
table.insert(nodes, pos)
255362
-- FIXME: Machines connecting to multiple networks should have way to store multiple network ids
256-
cables[node_id] = network_id
363+
technic_cables[node_id] = network_id
257364
all_nodes[node_id] = pos
258365
return true
259366
elseif not multitier and net_id_old ~= network_id then
260367
-- Do not allow running from multiple networks, trigger overload
261-
overload_network(network_id)
262-
overload_network(net_id_old)
368+
technic.overload_network(network_id)
369+
technic.overload_network(net_id_old)
263370
local meta = minetest.get_meta(pos)
264371
meta:set_string("infotext",S("Network Overloaded"))
265372
end
@@ -268,15 +375,15 @@ end
268375
-- Add a wire node to the LV/MV/HV network
269376
local function add_cable_node(pos, network)
270377
local node_id = poshash(pos)
271-
if not cables[node_id] then
272-
cables[node_id] = network.id
378+
if not technic_cables[node_id] then
379+
technic_cables[node_id] = network.id
273380
network.all_nodes[node_id] = pos
274381
if network.queue then
275382
table.insert(network.queue, pos)
276383
end
277-
elseif cables[node_id] ~= network.id then
384+
elseif technic_cables[node_id] ~= network.id then
278385
-- Conflicting network connected, merge networks if both are still in building stage
279-
local net2 = networks[cables[node_id]]
386+
local net2 = networks[technic_cables[node_id]]
280387
if net2 and net2.queue then
281388
technic.merge_networks(network, net2)
282389
end
@@ -288,7 +395,7 @@ local function add_network_node(network, pos, machines)
288395
technic.get_or_load_node(pos)
289396
local name = minetest.get_node(pos).name
290397

291-
if technic.is_tier_cable(name, network.tier) then
398+
if technic.get_cable_tier(name) == network.tier then
292399
add_cable_node(pos, network)
293400
elseif machines[name] then
294401
if machines[name] == technic.producer then
@@ -438,6 +545,21 @@ end
438545
--
439546
local node_technic_run = {}
440547
minetest.register_on_mods_loaded(function()
548+
for name, tiers in pairs(technic.machine_tiers) do
549+
local nodedef = minetest.registered_nodes[name]
550+
local on_construct = type(nodedef.on_construct) == "function" and nodedef.on_construct
551+
local on_destruct = type(nodedef.on_destruct) == "function" and nodedef.on_destruct
552+
local place_node = technic.place_network_node
553+
local remove_node = technic.remove_network_node
554+
minetest.override_item(name, {
555+
on_construct = on_construct
556+
and function(pos) on_construct(pos) place_node(pos, tiers, name) end
557+
or function(pos) place_node(pos, tiers, name) end,
558+
on_destruct = on_destruct
559+
and function(pos) on_destruct(pos) remove_node(pos, tiers, name) end
560+
or function(pos) remove_node(pos, tiers, name) end,
561+
})
562+
end
441563
for name, _ in pairs(technic.machine_tiers) do
442564
if type(minetest.registered_nodes[name].technic_run) == "function" then
443565
node_technic_run[name] = minetest.registered_nodes[name].technic_run

technic/machines/overload.lua

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
--
2+
-- Network overloading (incomplete cheat mitigation)
3+
--
4+
5+
local overload_reset_time = technic.config:get_int("network_overload_reset_time")
6+
local overloaded_networks = {}
7+
local networks = technic.networks
8+
9+
function technic.overload_network(network_id)
10+
local network = networks[network_id]
11+
if network then
12+
network.supply = 0
13+
network.battery_charge = 0
14+
end
15+
overloaded_networks[network_id] = minetest.get_us_time() + (overload_reset_time * 1000 * 1000)
16+
end
17+
18+
function technic.reset_overloaded(network_id)
19+
local remaining = math.max(0, overloaded_networks[network_id] - minetest.get_us_time())
20+
if remaining == 0 then
21+
-- Clear cache, remove overload and restart network
22+
technic.remove_network(network_id)
23+
overloaded_networks[network_id] = nil
24+
end
25+
-- Returns 0 when network reset or remaining time if reset timer has not expired yet
26+
return remaining
27+
end
28+
29+
function technic.is_overloaded(network_id)
30+
return overloaded_networks[network_id]
31+
end

0 commit comments

Comments
 (0)