Files
Aurora.3/code/controllers/subsystems/air.dm
Lohikar 6ef9191275 Kill airlocks' process(), SSpower, and misc. performance tweaks (#2175)
changes:

Airlocks no longer tick (exceptions: firedoors, uranium airlocks)
Airlock commands run before round-start are queued to run at round-start.
Airlock commands that failed to run are scheduled to try again in 2 seconds with timers.
Callbacks can now be registered with SSticker to run at round-start in a non-blocking way.
Added a new subsystem (SSpower) for handling power-related functions. Currently doesn't do a whole lot, but this will likely eventually change.
RCON functionality has been moved from SSmachinery to SSpower.
The global cable list has been moved into SSpower.
Powernet sensors no longer process purely to keep themselves in the global machines list, instead they are added to a list in SSpower.
Power terminals no longer pointlessly tick.
Removed some variables from SSticker that weren't used by anything.
Holographic overlays such as those used by consoles are now cached.
Xenoarcheology setup is now tick-checked.
ZAS now uses post-fire timing.
The req_access and req_one_access lists are no longer initialized by default on all /obj types.
Openturfs will only emit starlight if they are bordering a non-openturf dynamically lit turf.
Powernets are now stored in SSpower instead of being a global.
The global mouse list is now stored in SSmob instead of being a global.
Fixed some weirdness in APCs' Destroy() caused by a merge.
SSwireless now pre-bakes to reduce round-start processing.
SSwireless no longer uses processing queues.
2017-05-11 22:19:51 +03:00

438 lines
11 KiB
Plaintext

/var/datum/controller/subsystem/air/SSair
/*
Overview:
The air controller does everything. There are tons of procs in here.
Class Vars:
zones - All zones currently holding one or more turfs.
edges - All processing edges.
tiles_to_update - Tiles scheduled to update next tick.
zones_to_update - Zones which have had their air changed and need air archival.
active_hotspots - All processing fire objects.
active_zones - The number of zones which were archived last tick. Used in debug verbs.
next_id - The next UID to be applied to a zone. Mostly useful for debugging purposes as zones do not need UIDs to function.
Class Procs:
mark_for_update(turf/T)
Adds the turf to the update list. When updated, update_air_properties() will be called.
When stuff changes that might affect airflow, call this. It's basically the only thing you need.
add_zone(zone/Z) and remove_zone(zone/Z)
Adds zones to the zones list. Does not mark them for update.
air_blocked(turf/A, turf/B)
Returns a bitflag consisting of:
AIR_BLOCKED - The connection between turfs is physically blocked. No air can pass.
ZONE_BLOCKED - There is a door between the turfs, so zones cannot cross. Air may or may not be permeable.
has_valid_zone(turf/T)
Checks the presence and validity of T's zone.
May be called on unsimulated turfs, returning 0.
merge(zone/A, zone/B)
Called when zones have a direct connection and equivalent pressure and temperature.
Merges the zones to create a single zone.
connect(turf/simulated/A, turf/B)
Called by turf/update_air_properties(). The first argument must be simulated.
Creates a connection between A and B.
mark_zone_update(zone/Z)
Adds zone to the update list. Unlike mark_for_update(), this one is called automatically whenever
air is returned from a simulated turf.
equivalent_pressure(zone/A, zone/B)
Currently identical to A.air.compare(B.air). Returns 1 when directly connected zones are ready to be merged.
get_edge(zone/A, zone/B)
get_edge(zone/A, turf/B)
Gets a valid connection_edge between A and B, creating a new one if necessary.
has_same_air(turf/A, turf/B)
Used to determine if an unsimulated edge represents a specific turf.
Simulated edges use connection_edge/contains_zone() for the same purpose.
Returns 1 if A has identical gases and temperature to B.
remove_edge(connection_edge/edge)
Called when an edge is erased. Removes it from processing.
*/
/datum/controller/subsystem/air
name = "Air"
priority = SS_PRIORITY_AIR
init_order = SS_INIT_AIR
flags = SS_POST_FIRE_TIMING
//Geometry lists
var/list/zones = list()
var/list/edges = list()
//Geometry updates lists
var/list/tiles_to_update = list()
var/list/zones_to_update = list()
var/list/active_fire_zones = list()
var/list/active_hotspots = list()
var/list/active_edges = list()
var/tmp/list/deferred = list()
var/tmp/list/processing_edges
var/tmp/list/processing_fires
var/tmp/list/processing_hotspots
var/tmp/list/processing_zones
var/active_zones = 0
var/next_id = 1
/datum/controller/subsystem/air/proc/reboot()
set waitfor = FALSE
// Stop processing while we rebuild.
can_fire = FALSE
// Make sure we don't rebuild mid-tick.
if (state != SS_IDLE)
admin_notice(span("danger", "ZAS Rebuild initiated. Waiting for current air tick to complete before continuing."), R_DEBUG)
UNTIL(state == SS_IDLE)
while (zones.len)
var/zone/zone = zones[zones.len]
zones.len--
zone.c_invalidate()
edges.Cut()
tiles_to_update.Cut()
zones_to_update.Cut()
active_fire_zones.Cut()
active_hotspots.Cut()
active_edges.Cut()
// Re-run setup without air settling.
Initialize(REALTIMEOFDAY, simulate = FALSE)
// Update next_fire so the MC doesn't try to make up for missed ticks.
next_fire = world.time + wait
can_fire = TRUE
/datum/controller/subsystem/air/stat_entry()
..("TtU:[tiles_to_update.len] ZtU:[zones_to_update.len] AFZ:[active_fire_zones.len] AH:[active_hotspots.len] AE:[active_edges.len]")
/datum/controller/subsystem/air/New()
NEW_SS_GLOBAL(SSair)
/datum/controller/subsystem/air/Initialize(timeofday, simulate = TRUE)
var/starttime = REALTIMEOFDAY
admin_notice(span("danger", "Processing Geometry..."), R_DEBUG)
var/simulated_turf_count = 0
for(var/turf/simulated/S in turfs)
simulated_turf_count++
S.update_air_properties()
CHECK_TICK
admin_notice({"<span class='info'>
Total Simulated Turfs: [simulated_turf_count]
Total Zones: [zones.len]
Total Edges: [edges.len]
Total Active Edges: [active_edges.len ? "<span class='danger'>[active_edges.len]</span>" : "None"]
Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count]
</span>"}, R_DEBUG)
admin_notice(span("danger", "Geometry processing completed in [(REALTIMEOFDAY - starttime)/10] seconds!"), R_DEBUG)
if (simulate)
admin_notice(span("danger", "Settling air..."), R_DEBUG)
starttime = REALTIMEOFDAY
fire(FALSE, TRUE)
admin_notice(span("danger", "Air settling completed in [(REALTIMEOFDAY - starttime)/10] seconds!"), R_DEBUG)
..()
/datum/controller/subsystem/air/fire(resumed = FALSE, no_mc_tick = FALSE)
if (!resumed)
processing_edges = active_edges.Copy()
processing_fires = active_fire_zones.Copy()
processing_hotspots = active_hotspots.Copy()
var/list/curr_tiles = tiles_to_update
var/list/curr_defer = deferred
var/list/curr_edges = processing_edges
var/list/curr_fire = processing_fires
var/list/curr_hotspot = processing_hotspots
var/list/curr_zones = zones_to_update
while (curr_tiles.len)
var/turf/T = curr_tiles[curr_tiles.len]
curr_tiles.len--
if (QDELETED(T))
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
continue
//check if the turf is self-zone-blocked
if(T.c_airblock(T) & ZONE_BLOCKED)
deferred += T
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
continue
T.update_air_properties()
T.post_update_air_properties()
T.needs_air_update = 0
#ifdef ZASDBG
T.overlays -= mark
updated++
#endif
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
while (curr_defer.len)
var/turf/T = curr_defer[curr_defer.len]
curr_defer.len--
T.update_air_properties()
T.post_update_air_properties()
T.needs_air_update = 0
#ifdef ZASDBG
T.overlays -= mark
updated++
#endif
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
while (curr_edges.len)
var/connection_edge/edge = curr_edges[curr_edges.len]
curr_edges.len--
if (!edge)
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
continue
edge.tick()
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
while (curr_fire.len)
var/zone/Z = curr_fire[curr_fire.len]
curr_fire.len--
Z.process_fire()
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
while (curr_hotspot.len)
var/obj/fire/F = curr_hotspot[curr_hotspot.len]
curr_hotspot.len--
F.process()
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
while (curr_zones.len)
var/zone/Z = curr_zones[curr_zones.len]
curr_zones.len--
Z.tick()
Z.needs_update = FALSE
if (no_mc_tick)
CHECK_TICK
else if (MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/add_zone(zone/z)
zones += z
z.name = "Zone [next_id++]"
mark_zone_update(z)
/datum/controller/subsystem/air/proc/remove_zone(zone/z)
zones -= z
zones_to_update -= z
if (processing_zones)
processing_zones -= z
/datum/controller/subsystem/air/proc/air_blocked(turf/A, turf/B)
#ifdef ZASDBG
ASSERT(isturf(A))
ASSERT(isturf(B))
#endif
var/ablock = A.c_airblock(B)
if(ablock == BLOCKED) return BLOCKED
return ablock | B.c_airblock(A)
/datum/controller/subsystem/air/proc/has_valid_zone(turf/simulated/T)
#ifdef ZASDBG
ASSERT(istype(T))
#endif
return istype(T) && T.zone && !T.zone.invalid
/datum/controller/subsystem/air/proc/merge(zone/A, zone/B)
#ifdef ZASDBG
ASSERT(istype(A))
ASSERT(istype(B))
ASSERT(!A.invalid)
ASSERT(!B.invalid)
ASSERT(A != B)
#endif
if(A.contents.len < B.contents.len)
A.c_merge(B)
mark_zone_update(B)
else
B.c_merge(A)
mark_zone_update(A)
/datum/controller/subsystem/air/proc/connect(turf/simulated/A, turf/simulated/B)
#ifdef ZASDBG
ASSERT(istype(A))
ASSERT(isturf(B))
ASSERT(A.zone)
ASSERT(!A.zone.invalid)
//ASSERT(B.zone)
ASSERT(A != B)
#endif
var/block = air_blocked(A,B)
if(block & AIR_BLOCKED) return
var/direct = !(block & ZONE_BLOCKED)
var/space = !istype(B)
if(!space)
if(min(A.zone.contents.len, B.zone.contents.len) < ZONE_MIN_SIZE || (direct && (equivalent_pressure(A.zone,B.zone) || times_fired == 0)))
merge(A.zone,B.zone)
return
var
a_to_b = get_dir(A,B)
b_to_a = get_dir(B,A)
if(!A.connections) A.connections = new
if(!B.connections) B.connections = new
if(A.connections.get(a_to_b))
return
if(B.connections.get(b_to_a))
return
if(!space)
if(A.zone == B.zone) return
var/connection/c = new /connection(A,B)
A.connections.place(c, a_to_b)
B.connections.place(c, b_to_a)
if(direct) c.mark_direct()
/datum/controller/subsystem/air/proc/mark_for_update(turf/T)
#ifdef ZASDBG
ASSERT(isturf(T))
#endif
if(T.needs_air_update)
return
tiles_to_update += T
#ifdef ZASDBG
T.overlays += mark
#endif
T.needs_air_update = 1
/datum/controller/subsystem/air/proc/mark_zone_update(zone/Z)
#ifdef ZASDBG
ASSERT(istype(Z))
#endif
if(Z.needs_update)
return
zones_to_update += Z
Z.needs_update = 1
/datum/controller/subsystem/air/proc/mark_edge_sleeping(connection_edge/E)
#ifdef ZASDBG
ASSERT(istype(E))
#endif
if(E.sleeping)
return
active_edges -= E
E.sleeping = 1
/datum/controller/subsystem/air/proc/mark_edge_active(connection_edge/E)
#ifdef ZASDBG
ASSERT(istype(E))
#endif
if(!E.sleeping)
return
active_edges += E
E.sleeping = 0
/datum/controller/subsystem/air/proc/equivalent_pressure(zone/A, zone/B)
return A.air.compare(B.air)
/datum/controller/subsystem/air/proc/get_edge(zone/A, zone/B)
if(istype(B))
for(var/connection_edge/zone/edge in A.edges)
if(edge.contains_zone(B))
return edge
var/connection_edge/edge = new/connection_edge/zone(A,B)
edges += edge
edge.recheck()
return edge
else
for(var/connection_edge/unsimulated/edge in A.edges)
if(has_same_air(edge.B,B))
return edge
var/connection_edge/edge = new/connection_edge/unsimulated(A,B)
edges += edge
edge.recheck()
return edge
/datum/controller/subsystem/air/proc/has_same_air(turf/A, turf/B)
if(A.oxygen != B.oxygen)
return 0
if(A.nitrogen != B.nitrogen)
return 0
if(A.phoron != B.phoron)
return 0
if(A.carbon_dioxide != B.carbon_dioxide)
return 0
if(A.temperature != B.temperature)
return 0
return 1
/datum/controller/subsystem/air/proc/remove_edge(connection_edge/E)
edges -= E
if(!E.sleeping)
active_edges -= E