@@ -10,8 +10,8 @@ local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
1010technic .active_networks = {}
1111local networks = {}
1212technic .networks = networks
13- local cables = {}
14- technic .cables = cables
13+ local technic_cables = {}
14+ technic .cables = technic_cables
1515
1616local poshash = minetest .hash_node_position
1717local 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
9090
9191-- Destroy network data
9292function 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
100100end
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-
133102function 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 })]
135104end
136105
137106function technic .pos2network (pos )
138- return cables [poshash (pos )]
107+ return technic_cables [poshash (pos )]
139108end
140109
141110function technic .network2pos (network_id )
@@ -208,38 +177,176 @@ function technic.disable_machine(pos, node)
208177 end
209178end
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
224217end
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
236309end
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
241349end
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
249356local 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
269376local 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
438545--
439546local node_technic_run = {}
440547minetest .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
0 commit comments