Fixes big roundstart ductnets breaking world init (#58659)

closes: #58623 (Having two stationary plumbing tanks connected broke the map)

you can now map infinitely huge plumbing networks

Byond mistakes big chains of connecting ducts for an infinite loops, so when a lot of ducts (about 60) initialize at once and chain connect, byond kills the worldtick.

Plumbing already had an internal duct limit of about 800~ ducts, which has now finally been fixed aswell. The plumbing subsystem (SSfluids, I need to rename this shit) tells one duct to start connecting and uses the timer subsystem to call them one by one.
This commit is contained in:
Time-Green
2021-05-09 10:04:28 +02:00
committed by GitHub
parent d03686981c
commit 3fb86cc311
2 changed files with 18 additions and 33 deletions
+13 -3
View File
@@ -1,23 +1,29 @@
///We handle the unity part of plumbing. We track who is connected to who.
/datum/ductnet
///Stuff that can supply chems
var/list/suppliers = list()
////Stuff that can take chems
var/list/demanders = list()
///All the ducts that make this network
var/list/obj/machinery/duct/ducts = list()
///Max reagents we can carry per tick
var/capacity
///Add a duct to our network
/datum/ductnet/proc/add_duct(obj/machinery/duct/D)
if(!D || (D in ducts))
return
ducts += D
D.duct = src
///Remove a duct from our network and commit suicide, because this is probably easier than to check who that duct was connected to and what part of us was lost
/datum/ductnet/proc/remove_duct(obj/machinery/duct/ducting)
destroy_network(FALSE)
for(var/obj/machinery/duct/D in ducting.neighbours)
addtimer(CALLBACK(D, /obj/machinery/duct/proc/reconnect), 0) //all needs to happen after the original duct that was destroyed finishes destroying itself
addtimer(CALLBACK(D, /obj/machinery/duct/proc/generate_connects), 0)
addtimer(CALLBACK(D, /obj/machinery/duct/proc/attempt_connect)) //needs to happen after qdel
qdel(src)
///add a plumbing object to either demanders or suppliers
/datum/ductnet/proc/add_plumber(datum/component/plumbing/P, dir)
if(!P.can_add(src, dir))
@@ -28,6 +34,7 @@
else if(dir & P.demand_connects)
demanders += P
return TRUE
///remove a plumber. we dont delete ourselves because ductnets dont persist through plumbing objects
/datum/ductnet/proc/remove_plumber(datum/component/plumbing/P)
suppliers.Remove(P) //we're probably only in one of these, but Remove() is inherently sane so this is fine
@@ -38,6 +45,7 @@
P.ducts -= dir
if(!ducts.len) //there were no ducts, so it was a direct connection. we destroy ourselves since a ductnet with only one plumber and no ducts is worthless
destroy_network()
///we combine ductnets. this occurs when someone connects to seperate sets of fluid ducts
/datum/ductnet/proc/assimilate(datum/ductnet/D)
ducts.Add(D.ducts)
@@ -53,7 +61,9 @@
var/obj/machinery/duct/M = A
M.duct = src //forget your old master
destroy_network()
D.ducts.Cut() //clear this so the other network doesnt clear the ducts along with themselves (this took the life out of me)
D.destroy_network()
///destroy the network and tell all our ducts and plumbers we are gone
/datum/ductnet/proc/destroy_network(delete=TRUE)
for(var/A in suppliers + demanders)
+5 -30
View File
@@ -63,9 +63,7 @@ All the important duct code:
if(D.duct_layer & duct_layer)
disconnect_duct()
if(active)
attempt_connect()
attempt_connect()
AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE)
///start looking around us for stuff to connect to
@@ -132,9 +130,11 @@ All the important duct code:
else
create_duct()
duct.add_duct(D)
add_neighbour(D, direction)
//tell our buddy its time to pass on the torch of connecting to pipes. This shouldn't ever infinitely loop since it only works on pipes that havent been inductrinated
D.attempt_connect()
//Delegate to timer subsystem so its handled the next tick and doesnt cause byond to mistake it for an infinite loop and kill the game
addtimer(CALLBACK(D, .proc/attempt_connect))
return TRUE
@@ -170,31 +170,6 @@ All the important duct code:
new drop_on_wrench(drop_location())
qdel(src)
///''''''''''''''''optimized''''''''''''''''' proc for quickly reconnecting after a duct net was destroyed
/obj/machinery/duct/proc/reconnect()
if(neighbours.len && !duct)
create_duct()
for(var/atom/movable/AM in neighbours)
if(istype(AM, /obj/machinery/duct))
var/obj/machinery/duct/D = AM
if(D.duct)
if(D.duct == duct) //we're already connected
continue
else
duct.assimilate(D.duct)
continue
else
duct.add_duct(D)
D.reconnect()
else
if(AM in get_step(src, neighbours[AM])) //did we move?
for(var/plumber in AM.GetComponents(/datum/component/plumbing))
if(!plumber) //apparently yes it will be null hahahaasahsdvashufv
return
connect_plumber(plumber, neighbours[AM])
else
neighbours -= AM //we moved
///Special proc to draw a new connect frame based on neighbours. not the norm so we can support multiple duct kinds
/obj/machinery/duct/proc/generate_connects()
if(lock_connects)