diff --git a/code/ZAS/Controller.dm b/code/ZAS/Controller.dm index 9688fabf72..af0dbe838d 100644 --- a/code/ZAS/Controller.dm +++ b/code/ZAS/Controller.dm @@ -80,176 +80,9 @@ Class Procs: /datum/controller/air_system/var/active_zones = 0 /datum/controller/air_system/var/current_cycle = 0 -/datum/controller/air_system/var/update_delay = 5 //How long between check should it try to process atmos again. -/datum/controller/air_system/var/failed_ticks = 0 //How many ticks have runtimed? - -/datum/controller/air_system/var/tick_progress = 0 /datum/controller/air_system/var/next_id = 1 //Used to keep track of zone UIDs. - -#if UNIT_TEST -#define CHECK_SLEEP_ZAS_SETUP // For unit tests we don't care about a smooth lobby screen experience. We care about speed. -#else -#define CHECK_SLEEP_ZAS_SETUP if(++done_turfs > 10000) { done_turfs=0;sleep(world.tick_lag); } -#endif -/datum/controller/air_system/proc/Setup() - //Purpose: Call this at the start to setup air groups geometry - // (Warning: Very processor intensive but only must be done once per round) - //Called by: Gameticker/Master controller - //Inputs: None. - //Outputs: None. - - #if !UNIT_TEST - var/done_turfs = 0 - #endif - #ifndef ZASDBG - set background = 1 - #endif - - admin_notice("Processing Geometry...", R_DEBUG) - sleep(-1) - - var/start_time = world.timeofday - - var/simulated_turf_count = 0 - - for(var/turf/simulated/S in world) - simulated_turf_count++ - S.update_air_properties() - CHECK_SLEEP_ZAS_SETUP - - admin_notice({"Geometry initialized in [round(0.1*(world.timeofday-start_time),0.1)] seconds. - -Total Simulated Turfs: [simulated_turf_count] -Total Zones: [zones.len] -Total Edges: [edges.len] -Total Active Edges: [active_edges.len ? "[active_edges.len]" : "None"] -Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count] -"}, R_DEBUG) - - // Uncomment this if you're having problems finding where active edges are. - /* - for(var/connection_edge/E in active_edges) - world << "Edge became active: [E]." - var/i = 1 - for(var/turf/T in E.connecting_turfs) - world << "[i] [T]:[T.x],[T.y],[T.z]" - i++ - */ - - -// spawn Start() - - -/datum/controller/air_system/proc/Start() - //Purpose: This is kicked off by the master controller, and controls the processing of all atmosphere. - //Called by: Master controller - //Inputs: None. - //Outputs: None. - - #ifndef ZASDBG - set background = 1 - #endif - - while(1) - Tick() - sleep(max(5,update_delay*tick_multiplier)) - - -/datum/controller/air_system/proc/Tick() - . = 1 //Set the default return value, for runtime detection. - - current_cycle++ - - //If there are tiles to update, do so. - tick_progress = "updating turf properties" - - var/list/updating - - if(tiles_to_update.len) - updating = tiles_to_update - tiles_to_update = list() - - #ifdef ZASDBG - var/updated = 0 - #endif - - //defer updating of self-zone-blocked turfs until after all other turfs have been updated. - //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones - //have valid zones when the self-zone-blocked turfs update. - - //This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and - //can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway. - var/list/deferred = list() - - for(var/turf/T in updating) - //check if the turf is self-zone-blocked - if(T.c_airblock(T) & ZONE_BLOCKED) - deferred += T - continue - - T.update_air_properties() - T.post_update_air_properties() - T.needs_air_update = 0 - #ifdef ZASDBG - T.overlays -= mark - updated++ - #endif - //sleep(1) - - for(var/turf/T in deferred) - T.update_air_properties() - T.post_update_air_properties() - T.needs_air_update = 0 - #ifdef ZASDBG - T.overlays -= mark - updated++ - #endif - - #ifdef ZASDBG - if(updated != updating.len) - tick_progress = "[updating.len - updated] tiles left unupdated." - world << "[tick_progress]" - . = 0 - #endif - - //Where gas exchange happens. - if(.) - tick_progress = "processing edges" - - for(var/connection_edge/edge in active_edges) - edge.tick() - - //Process fire zones. - if(.) - tick_progress = "processing fire zones" - - for(var/zone/Z in active_fire_zones) - Z.process_fire() - - //Process hotspots. - if(.) - tick_progress = "processing hotspots" - - for(var/obj/fire/fire in active_hotspots) - fire.process() - - //Process zones. - if(.) - tick_progress = "updating zones" - - active_zones = zones_to_update.len - if(zones_to_update.len) - updating = zones_to_update - zones_to_update = list() - for(var/zone/zone in updating) - zone.tick() - zone.needs_update = 0 - - if(.) - tick_progress = "success" - /datum/controller/air_system/proc/add_zone(zone/z) zones.Add(z) z.name = "Zone [next_id++]" diff --git a/code/ZAS/Diagnostic.dm b/code/ZAS/Diagnostic.dm index cbf35d93bd..29ab678ef1 100644 --- a/code/ZAS/Diagnostic.dm +++ b/code/ZAS/Diagnostic.dm @@ -1,6 +1,12 @@ client/proc/ZoneTick() set category = "Debug" set name = "Process Atmos" + set desc = "Manually run a single tick of the air subsystem" + + // TODO - This might be a useful diagnostic tool. However its complicated to do with StonedMC + // Therefore it is left unimplemented for now until its use actually becomes required. ~Leshana + /* + if(!check_rights(R_DEBUG)) return var/result = air_master.Tick() if(result) @@ -8,7 +14,7 @@ client/proc/ZoneTick() else src << "Failed to process! ([air_master.tick_progress])" - + */ client/proc/Zone_Info(turf/T as null|turf) set category = "Debug" diff --git a/code/ZAS/_docs.dm b/code/ZAS/_docs.dm index 7b28d52688..382f1f0abb 100644 --- a/code/ZAS/_docs.dm +++ b/code/ZAS/_docs.dm @@ -26,11 +26,3 @@ Notes for people who used ZAS before: var/zone/connected_zone = edge.get_connected_zone(zone) */ - -//#define ZASDBG -#define MULTIZAS -#define AIR_BLOCKED 1 -#define ZONE_BLOCKED 2 -#define BLOCKED 3 - -#define ZONE_MIN_SIZE 14 //zones with less than this many turfs will always merge, even if the connection is not direct diff --git a/code/__defines/ZAS.dm b/code/__defines/ZAS.dm new file mode 100644 index 0000000000..8712dd2bf7 --- /dev/null +++ b/code/__defines/ZAS.dm @@ -0,0 +1,6 @@ +// Bitflag values for c_airblock() +#define AIR_BLOCKED 1 // Blocked +#define ZONE_BLOCKED 2 // Not blocked, but zone boundaries will not cross. +#define BLOCKED 3 // Blocked, zone boundaries will not cross even if opened. + +#define ZONE_MIN_SIZE 14 // Zones with less than this many turfs will always merge, even if the connection is not direct diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index 3190e391a4..229d0b94ad 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -7,6 +7,10 @@ 2 for preloading absolutely everything; */ +// ZAS Compile Options +//#define ZASDBG // Uncomment to turn on super detailed ZAS debugging that probably won't even compile. +#define MULTIZAS // Uncomment to turn on Multi-Z ZAS Support! + // If we are doing the map test build, do not include the main maps, only the submaps. #if MAP_TEST #define USING_MAP_DATUM /datum/map diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index eb350656b9..bec4a7953a 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -18,3 +18,4 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define INIT_ORDER_MAPPING 20 // VOREStation Edit #define INIT_ORDER_MACHINES 10 #define INIT_ORDER_LIGHTING 0 +#define INIT_ORDER_AIR -1 diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index c58b2d1381..07e2985bbb 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -88,6 +88,10 @@ /proc/log_unit_test(text) world.log << "## UNIT_TEST: [text]" +/proc/report_progress(var/progress_message) + admin_notice("[progress_message]", R_DEBUG) + to_world_log(progress_message) + //pretty print a direction bitflag, can be useful for debugging. /proc/print_dir(var/dir) var/list/comps = list() diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 3ecb3b7fcb..961f657b2d 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -573,14 +573,11 @@ var/datum/controller/master/Master = new() var/datum/controller/subsystem/SS = S SS.StartLoadingMap() - // ZAS might displace objects as the map loads if an air tick is processed mid-load. - air_processing_killed = TRUE map_loading = TRUE /datum/controller/master/StopLoadingMap(var/quiet = TRUE) if(!quiet) admin_notice("Map is finished. Unlocking.", R_DEBUG) - air_processing_killed = FALSE map_loading = FALSE for(var/S in subsystems) var/datum/controller/subsystem/SS = S diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index ab72c18292..e92ec6a3df 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -11,7 +11,6 @@ var/global/datum/controller/game_controller/master_controller //Set in world.New var/global/controller_iteration = 0 var/global/last_tick_duration = 0 -var/global/air_processing_killed = 0 var/global/pipe_processing_killed = 0 datum/controller/game_controller diff --git a/code/controllers/subsystems/air.dm b/code/controllers/subsystems/air.dm new file mode 100644 index 0000000000..b5d007504a --- /dev/null +++ b/code/controllers/subsystems/air.dm @@ -0,0 +1,278 @@ +// Air update stages +#define SSAIR_TURFS 1 +#define SSAIR_EDGES 2 +#define SSAIR_FIREZONES 3 +#define SSAIR_HOTSPOTS 4 +#define SSAIR_ZONES 5 +#define SSAIR_DONE 6 + +SUBSYSTEM_DEF(air) + name = "Air" + init_order = INIT_ORDER_AIR + priority = 20 + wait = 2 SECONDS // seconds (We probably can speed this up actually) + flags = SS_BACKGROUND // TODO - Should this really be background? It might be important. + runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME + var/static/list/part_names = list("turfs", "edges", "fire zones", "hotspots", "zones") + + var/cost_turfs = 0 + var/cost_edges = 0 + var/cost_firezones = 0 + var/cost_hotspots = 0 + var/cost_zones = 0 + + var/list/currentrun = null + var/current_step = null + + // Updating zone tiles requires temporary storage location of self-zone-blocked turfs across resumes. Used only by process_tiles_to_update. + var/list/selfblock_deferred = null + +/datum/controller/subsystem/air/PreInit() + // Initialize the singleton /datum/controller/air_system + // TODO - We could actually incorporate that into this subsystem! But in the spirit of not fucking with ZAS more than necessary, lets not for now. ~Leshana + air_master = new() + +/datum/controller/subsystem/air/Initialize(timeofday) + report_progress("Processing Geometry...") + + var/simulated_turf_count = 0 + for(var/turf/simulated/S in world) + simulated_turf_count++ + S.update_air_properties() + CHECK_TICK + + admin_notice({"Geometry initialized in [round(0.1*(REALTIMEOFDAY-timeofday),0.1)] seconds. + +Total Simulated Turfs: [simulated_turf_count] +Total Zones: [air_master.zones.len] +Total Edges: [air_master.edges.len] +Total Active Edges: [air_master.active_edges.len ? "[air_master.active_edges.len]" : "None"] +Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_count] +"}, R_DEBUG) + + // Note - Baystation settles the air by running for one tick. We prefer to not have active edges. + // Maps should not have active edges on boot. If we've got some, log it so it can get fixed. + if(air_master.active_edges.len) + var/list/edge_log = list() + for(var/connection_edge/E in air_master.active_edges) + edge_log += "Active Edge [E] ([E.type])" + for(var/turf/T in E.connecting_turfs) + edge_log += "+--- Connecting Turf [T] @ [T.x], [T.y], [T.z]" + log_debug("Active Edges on ZAS Startup\n" + edge_log.Join("\n")) + + ..() + +/datum/controller/subsystem/air/fire(resumed = 0) + var/timer + if(!resumed) + ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle + ASSERT(current_step == null) // Or somehow didn't finish all the steps from last cycle + air_master.current_cycle++ // Begin a new air_master cycle! + current_step = SSAIR_TURFS // Start with Step 1 of course + + INTERNAL_PROCESS_STEP(SSAIR_TURFS, TRUE, process_tiles_to_update, cost_turfs, SSAIR_EDGES) + INTERNAL_PROCESS_STEP(SSAIR_EDGES, FALSE, process_active_edges, cost_edges, SSAIR_FIREZONES) + INTERNAL_PROCESS_STEP(SSAIR_FIREZONES, FALSE, process_active_fire_zones, cost_firezones, SSAIR_HOTSPOTS) + INTERNAL_PROCESS_STEP(SSAIR_HOTSPOTS, FALSE, process_active_hotspots, cost_hotspots, SSAIR_ZONES) + INTERNAL_PROCESS_STEP(SSAIR_ZONES, FALSE, process_zones_to_update, cost_zones, SSAIR_DONE) + + // Okay, we're done! Woo! Got thru a whole air_master cycle! + ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left + ASSERT(current_step == SSAIR_DONE) // And that we didn't somehow skip past the last step + currentrun = null + current_step = null + +/datum/controller/subsystem/air/proc/process_tiles_to_update(resumed = 0) + if (!resumed) + // NOT a copy, because we are supposed to drain active turfs each cycle anyway, so just replace with empty list. + // We still use a separate list tho, to ensure we don't process a turf twice during a single cycle! + src.currentrun = air_master.tiles_to_update + air_master.tiles_to_update = list() + + //defer updating of self-zone-blocked turfs until after all other turfs have been updated. + //this hopefully ensures that non-self-zone-blocked turfs adjacent to self-zone-blocked ones + //have valid zones when the self-zone-blocked turfs update. + //This ensures that doorways don't form their own single-turf zones, since doorways are self-zone-blocked and + //can merge with an adjacent zone, whereas zones that are formed on adjacent turfs cannot merge with the doorway. + ASSERT(src.selfblock_deferred == null) // Sanity check to make sure it was not remaining from last cycle somehow. + src.selfblock_deferred = list() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + var/list/selfblock_deferred = src.selfblock_deferred + + // Run thru the list, processing non-self-zone-blocked and deferring self-zone-blocked + while(currentrun.len) + var/turf/T = currentrun[currentrun.len] + currentrun.len-- + //check if the turf is self-zone-blocked + if(T.c_airblock(T) & ZONE_BLOCKED) + selfblock_deferred += T + if(MC_TICK_CHECK) + return + else + continue + T.update_air_properties() + T.post_update_air_properties() + T.needs_air_update = 0 + #ifdef ZASDBG + T.overlays -= mark + #endif + if(MC_TICK_CHECK) + return + + ASSERT(LAZYLEN(currentrun) == 0) + + // Run thru the deferred list and processing them + while(selfblock_deferred.len) + var/turf/T = selfblock_deferred[selfblock_deferred.len] + selfblock_deferred.len-- + T.update_air_properties() + T.post_update_air_properties() + T.needs_air_update = 0 + #ifdef ZASDBG + T.overlays -= mark + #endif + if(MC_TICK_CHECK) + return + + ASSERT(LAZYLEN(selfblock_deferred) == 0) + src.selfblock_deferred = null + +/datum/controller/subsystem/air/proc/process_active_edges(resumed = 0) + if (!resumed) + src.currentrun = air_master.active_edges.Copy() + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/connection_edge/edge = currentrun[currentrun.len] + currentrun.len-- + if(edge) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded. + edge.tick() + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/air/proc/process_active_fire_zones(resumed = 0) + if (!resumed) + src.currentrun = air_master.active_fire_zones.Copy() + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/zone/Z = currentrun[currentrun.len] + currentrun.len-- + if(Z) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded. + Z.process_fire() + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/air/proc/process_active_hotspots(resumed = 0) + if (!resumed) + src.currentrun = air_master.active_hotspots.Copy() + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/obj/fire/fire = currentrun[currentrun.len] + currentrun.len-- + if(fire) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded. + fire.process() + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/air/proc/process_zones_to_update(resumed = 0) + if (!resumed) + air_master.active_zones = air_master.zones_to_update.len // Save how many zones there were to update this cycle (used by some debugging stuff) + if(!air_master.zones_to_update.len) + return // Nothing to do here this cycle! + // NOT a copy, because we are supposed to drain active turfs each cycle anyway, so just replace with empty list. + // Blanking the public list means we actually are removing processed ones from the list! Maybe we could we use zones_for_update directly? + // But if we dom any zones added to zones_to_update DURING this step will get processed again during this step. + // I don't know if that actually happens? But if it does, it could lead to an infinate loop. Better preserve original semantics. + src.currentrun = air_master.zones_to_update + air_master.zones_to_update = list() + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + while(currentrun.len) + var/zone/zone = currentrun[currentrun.len] + currentrun.len-- + if(zone) // TODO - Do we need to check this? Old one didn't, but old one was single-threaded. + zone.tick() + zone.needs_update = 0 + if(MC_TICK_CHECK) + return + +/datum/controller/subsystem/air/stat_entry(msg_prefix) + var/list/msg = list(msg_prefix) + msg += "S:[current_step ? part_names[current_step] : ""] " + msg += "C:{" + msg += "T [round(cost_turfs, 1)] | " + msg += "E [round(cost_edges, 1)] | " + msg += "F [round(cost_firezones, 1)] | " + msg += "H [round(cost_hotspots, 1)] | " + msg += "Z [round(cost_zones, 1)] " + msg += "}" + if(air_master) + msg += "T:[round((cost ? air_master.tiles_to_update.len/cost : 0), 0.1)]" + ..(msg.Join()) + if(air_master) + air_master.stat_entry() + + +// Since air_master is still a separate controller from SSAir (Wait, why is that again? Get on that...) +// I want it showing up in the statpanel too. We'll just hack it in as a separate line for now. +/datum/controller/air_system/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) + + var/title = " air_master" + var/list/msg = list() + msg += "Zones: [zones.len] " + msg += "Edges: [edges.len] " + msg += "Cycle: [current_cycle] {" + msg += "T [tiles_to_update.len] | " + msg += "E [active_edges.len] | " + msg += "F [active_fire_zones.len] | " + msg += "H [active_hotspots.len] | " + msg += "Z [zones_to_update.len] " + msg += "}" + + stat(title, statclick.update(msg.Join())) + +// ZAS might displace objects as the map loads if an air tick is processed mid-load. +/datum/controller/subsystem/air/StartLoadingMap(var/quiet = TRUE) + can_fire = FALSE + . = ..() + +/datum/controller/subsystem/air/StopLoadingMap(var/quiet = TRUE) + can_fire = TRUE + . = ..() + +// Reboot the air master. A bit hacky right now, but sometimes necessary still. +// TODO - Make this better by SSair and air_master together, then just reboot SSair +/datum/controller/subsystem/air/proc/RebootZAS() + can_fire = FALSE // Pause processing while we reboot + // If we should happen to be in the middle of processing... wait until that finishes. + if (state != SS_IDLE) + report_progress("ZAS Rebuild initiated. Waiting for current air tick to complete before continuing.") + while (state != SS_IDLE) + stoplag() + + var/datum/controller/air_system/old_air = global.air_master + // Invalidate all zones + for(var/zone/zone in old_air.zones) + zone.c_invalidate() + // Destroy the air_master and create a new one. + qdel(old_air) + global.air_master = new + Initialize(REALTIMEOFDAY) + + // Update next_fire so the MC doesn't try to make up for missed ticks. + next_fire = world.time + wait + can_fire = TRUE // Unpause + +#undef SSAIR_TURFS +#undef SSAIR_EDGES +#undef SSAIR_FIREZONES +#undef SSAIR_HOTSPOTS +#undef SSAIR_ZONES +#undef SSAIR_DONE diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 1946a79730..c43a3d54f4 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -786,11 +786,11 @@ var/list/admin_verbs_event_manager = list( set category = "Debug" set name = "Kill Air" set desc = "Toggle Air Processing" - if(air_processing_killed) - air_processing_killed = 0 + if(!SSair.can_fire) + SSair.can_fire = TRUE usr << "Enabled air processing." else - air_processing_killed = 1 + SSair.can_fire = FALSE usr << "Disabled air processing." feedback_add_details("admin_verb","KA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] used 'kill air'.") diff --git a/code/modules/admin/verbs/grief_fixers.dm b/code/modules/admin/verbs/grief_fixers.dm index dd28127e4a..7059fef907 100644 --- a/code/modules/admin/verbs/grief_fixers.dm +++ b/code/modules/admin/verbs/grief_fixers.dm @@ -45,10 +45,7 @@ usr << "\[4/5\] - All turfs reset to roundstart values." - qdel(air_master) - air_master = new - air_master.Setup() - spawn air_master.Start() + SSair.RebootZAS() usr << "\[5/5\] - ZAS Rebooted" world << "Atmosphere restart completed in [(world.timeofday - current_time)/10] seconds." \ No newline at end of file diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 09b7b7bf53..41b8a22f9b 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -274,14 +274,7 @@ var/list/debug_verbs = list ( set name = "Reboot ZAS" if(alert("This will destroy and remake all zone geometry on the whole map.","Reboot ZAS","Reboot ZAS","Nevermind") == "Reboot ZAS") - var/datum/controller/air_system/old_air = air_master - for(var/zone/zone in old_air.zones) - zone.c_invalidate() - qdel(old_air) - air_master = new - air_master.Setup() - spawn air_master.Start() - + SSair.RebootZAS() /client/proc/count_objects_on_z_level() set category = "Mapping" diff --git a/vorestation.dme b/vorestation.dme index 0d77a1e700..3d26a9f182 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -59,6 +59,7 @@ #include "code\__defines\turfs.dm" #include "code\__defines\unit_tests.dm" #include "code\__defines\xenoarcheaology.dm" +#include "code\__defines\ZAS.dm" #include "code\_compatibility\509\_JSON.dm" #include "code\_compatibility\509\JSON Reader.dm" #include "code\_compatibility\509\JSON Writer.dm" @@ -172,7 +173,6 @@ #include "code\controllers\verbs.dm" #include "code\controllers\voting.dm" #include "code\controllers\observer_listener\atom\observer.dm" -#include "code\controllers\Processes\air.dm" #include "code\controllers\Processes\alarm.dm" #include "code\controllers\Processes\chemistry.dm" #include "code\controllers\Processes\emergencyShuttle.dm" @@ -192,6 +192,7 @@ #include "code\controllers\Processes\vote.dm" #include "code\controllers\ProcessScheduler\core\process.dm" #include "code\controllers\ProcessScheduler\core\processScheduler.dm" +#include "code\controllers\subsystems\air.dm" #include "code\controllers\subsystems\creation.dm" #include "code\controllers\subsystems\garbage.dm" #include "code\controllers\subsystems\lighting.dm" @@ -2734,7 +2735,6 @@ #include "code\unit_tests\integrated_circuits\converter.dm" #include "code\unit_tests\integrated_circuits\logic.dm" #include "code\unit_tests\integrated_circuits\trig.dm" -#include "code\ZAS\_docs.dm" #include "code\ZAS\Airflow.dm" #include "code\ZAS\Atom.dm" #include "code\ZAS\Connection.dm"