Fix All Exoplanet Atmosphere Lag (#22924)

Y'know, probably. It certainly seems to work locally!

changes:
- code_imp: "Exoplanets now cache their main outdoor atmospheric region
instead of trying to search their entire map once per second."
- code_imp: "Exoplanet atmospheric calculations now run only when
necessary using new update_revision counter."
- code_imp: "Exoplanet atmospheres now wait until all random ruins have
been placed before trying to resolve the air everywhere."
- admin: "Adds new admin debug verb, 'Toggle Non-Horizon Temperature
Graphics', which toggles all off-ship gas temp graphics to address lag
in a pinch, if still necessary, without removing actual environmental
hazard."
This commit is contained in:
Batrachophreno
2026-08-02 01:25:35 +00:00
committed by GitHub
parent 9eb7413a75
commit d2e5fbca75
7 changed files with 190 additions and 8 deletions
+7 -1
View File
@@ -6,7 +6,13 @@
/turf/simulated/proc/update_graphic(list/graphic_add = null, list/graphic_remove = null)
if(graphic_add && LAZYLEN(graphic_add))
add_vis_contents(graphic_add)
if(SSair.suppress_nonhorizon_temperature_graphics && !is_station_level(z))
for(var/obj/gas_overlay/graphic in graphic_add)
if(graphic.gas_id == GAS_HEAT || graphic.gas_id == GAS_COLD)
continue
add_vis_contents(graphic)
else
add_vis_contents(graphic_add)
if(graphic_remove && LAZYLEN(graphic_remove))
remove_vis_contents(graphic_remove)
+4
View File
@@ -8,6 +8,7 @@ Class Vars:
name - A name of the format "Zone [#]", used for debugging.
invalid - True if the zone has been erased and is no longer eligible for processing.
needs_update - True if the zone has been added to the update list.
update_revision - Incremented when needs_update flips on, and retained after needs_update is cleared.
edges - A list of edges that connect to this zone.
air - The gas mixture that any turfs in this zone will return. Values are per-tile with a group multiplier.
@@ -47,6 +48,8 @@ Class Procs:
var/list/fire_tiles
var/needs_update = 0
/// Counter incremented when zone is dirtied for processing.
var/update_revision = 0
var/list/connection_edge/edges
@@ -172,6 +175,7 @@ Class Procs:
to_chat(M, "P: [XGM_PRESSURE(air)] kPa V: [air.volume]L T: [air.temperature]K ([air.temperature - T0C]C)")
to_chat(M, "O2 per N2: [(air.gas[GAS_NITROGEN] ? air.gas[GAS_OXYGEN]/air.gas[GAS_NITROGEN] : "N/A")] Moles: [air.total_moles]")
to_chat(M, "Simulated: [contents.len] ([air.group_multiplier])")
to_chat(M, "Update Revision: [update_revision]")
//to_chat(M, "Unsimulated: [unsimulated_contents.len]")
//to_chat(M, "Edges: [LAZYLEN(edges)]")
if(invalid) to_chat(M, "Invalid!")
+34
View File
@@ -88,6 +88,9 @@ SUBSYSTEM_DEF(air)
var/active_zones = 0
var/next_id = 1
/// Prevents gas temp graphics from being attached to turfs on non-Horizon z-levels.
var/suppress_nonhorizon_temperature_graphics = FALSE
#ifdef ZASDBG
var/updated = 0
#endif
@@ -297,6 +300,36 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
if (processing_zones)
processing_zones -= z
/// Suppresses or restores temperature graphics on every currently loaded non-station z-level.
/datum/controller/subsystem/air/proc/set_nonhorizon_temperature_graphics_suppressed(suppress)
suppress_nonhorizon_temperature_graphics = suppress
. = 0
for(var/z_level in 1 to world.maxz)
if(is_station_level(z_level))
continue
for(var/turf/simulated/T in Z_TURFS(z_level))
var/list/temperature_graphics
if(suppress)
for(var/obj/gas_overlay/graphic in T.vis_contents)
if(graphic.gas_id == GAS_HEAT || graphic.gas_id == GAS_COLD)
LAZYADD(temperature_graphics, graphic)
else if(TURF_HAS_VALID_ZONE(T))
for(var/obj/gas_overlay/graphic in T.zone.air.graphic)
if(graphic.gas_id == GAS_HEAT || graphic.gas_id == GAS_COLD)
LAZYADD(temperature_graphics, graphic)
if(LAZYLEN(temperature_graphics))
if(suppress)
T.update_graphic(graphic_remove = temperature_graphics)
else
T.update_graphic(graphic_add = temperature_graphics)
. += length(temperature_graphics)
CHECK_TICK
/datum/controller/subsystem/air/proc/air_blocked(turf/A, turf/B)
#ifdef ZASDBG
ASSERT(isturf(A))
@@ -386,6 +419,7 @@ Total Unsimulated Turfs: [world.maxx*world.maxy*world.maxz - simulated_turf_coun
return
zones_to_update += Z
Z.needs_update = 1
Z.update_revision++
/datum/controller/subsystem/air/proc/mark_edge_sleeping(connection_edge/E)
#ifdef ZASDBG
+1
View File
@@ -205,6 +205,7 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/cmd_admin_delete,
/client/proc/cmd_debug_del_all,
/client/proc/air_report,
/client/proc/toggle_nonhorizon_temperature_graphics,
/client/proc/reload_admins,
/client/proc/print_random_map,
/client/proc/create_random_map,
+18
View File
@@ -41,6 +41,24 @@
usr << browse(HTML_SKELETON(output), "window=airreport")
/client/proc/toggle_nonhorizon_temperature_graphics()
set category = "Debug"
set name = "Toggle Non-Horizon Temperature Graphics"
set desc = "Toggle gas temperature graphics on all non-Horizon z-levels."
if(!check_rights(R_DEBUG))
return
if(!SSair)
to_chat(src, SPAN_WARNING("The air subsystem is unavailable."))
return
var/suppress = !SSair.suppress_nonhorizon_temperature_graphics
to_chat(src, SPAN_NOTICE("[suppress ? "Disabling" : "Re-enabling"] non-Horizon temperature graphics. This may take several ticks."))
var/updated_graphics = SSair.set_nonhorizon_temperature_graphics_suppressed(suppress)
log_and_message_admins("has [suppress ? "disabled" : "enabled"] GAS_HEAT and GAS_COLD graphics on non-Horizon z-levels ([updated_graphics] graphic reference\s updated).")
feedback_add_details("admin_verb", "TNTG")
/client/proc/fix_next_move()
set category = "Debug"
set name = "Unfreeze Everyone"
+119 -7
View File
@@ -7,6 +7,10 @@
var/list/animals = list()
var/max_animal_count
var/datum/gas_mixture/atmosphere
/// Cached main ZAS surface zone per generated z-level. Resolved lazily after ZAS finishes building.
var/list/main_planetary_zones = list()
/// Last ZAS update revision processed per generated z-level.
var/list/main_planetary_zone_atmosphere_revisions = list()
var/list/breathgas = list() //list of gases animals/plants require to survive
var/badgas //id of gas that is toxic to life here
@@ -222,6 +226,7 @@
generate_features()
theme.after_map_generation(src)
generate_landing(2)
finalize_outdoor_atmosphere()
update_biome()
generate_planet_image()
START_PROCESSING(SSprocessing, src)
@@ -272,17 +277,74 @@
if(!atmosphere)
continue
var/zone/Z
for(var/i = 1 to maxx)
var/turf/simulated/T = locate(i, 2, zlevel)
if(istype(T) && T.zone && T.zone.contents.len > (maxx*maxy*0.25)) //if it's a zone quarter of zlevel, good enough odds it's planetary main one
Z = T.zone
break
if(Z && !length(Z.fire_tiles) && !atmosphere.compare(Z.air)) //let fire die out first if there is one
var/zone/Z = get_main_planetary_zone(zlevel)
if(!Z)
continue
var/zlevel_key = "[zlevel]"
var/last_atmosphere_revision = main_planetary_zone_atmosphere_revisions[zlevel_key]
if(!isnull(last_atmosphere_revision) && last_atmosphere_revision == Z.update_revision)
continue
// Let ZAS settle and fire die out first if there is one
if(Z.needs_update || length(Z.fire_tiles))
continue
if(!atmosphere.compare(Z.air))
var/datum/gas_mixture/daddy = new() //make a fake 'planet' zone gas
daddy.copy_from(atmosphere)
daddy.group_multiplier = Z.air.group_multiplier
Z.air.equalize(daddy)
SSair.mark_zone_update(Z)
main_planetary_zone_atmosphere_revisions[zlevel_key] = Z.update_revision
/// Attempts to not only identify the largest air zone, but also cache it for subsequent ticks.
/obj/effect/overmap/visitable/sector/exoplanet/proc/get_main_planetary_zone(zlevel)
var/zlevel_key = "[zlevel]"
var/zone/cached_zone = main_planetary_zones[zlevel_key]
// ZAS may invalidate and rebuild zones after terrain changes. Keep the cached zone only while it is still valid!
if(cached_zone && !cached_zone.invalid)
return cached_zone
main_planetary_zones -= zlevel_key
main_planetary_zone_atmosphere_revisions -= zlevel_key
// Exoplanet transition edges are unsimulated, so only scan the generated interior when resolving the main surface zone.
var/min_x = TRANSITIONEDGE + 1
var/min_y = TRANSITIONEDGE + 1
var/max_x = maxx - (TRANSITIONEDGE + 1)
var/max_y = maxy - (TRANSITIONEDGE + 1)
var/turf/lower_left = locate(min_x, min_y, zlevel)
var/turf/upper_right = locate(max_x, max_y, zlevel)
if(!lower_left || !upper_right)
return null
// If it's a zone quarter of zlevel, good enough odds it's planetary main one.
var/minimum_zone_size = maxx * maxy * 0.25
var/zone/best_zone
var/best_zone_size = 0
var/list/checked_zones = list()
var/zas_pending = FALSE
for(var/turf/simulated/T in block(lower_left, upper_right))
// Dynamic turf generation queues ZAS updates... Do not cache a main zone while the surface may still be settling!
if(T.needs_air_update)
zas_pending = TRUE
continue
if(!TURF_HAS_VALID_ZONE(T))
continue
var/zone/current_zone = T.zone
if(current_zone in checked_zones)
continue
checked_zones += current_zone
var/current_zone_size = current_zone.contents.len
if(current_zone_size <= best_zone_size || current_zone_size <= minimum_zone_size)
continue
best_zone = current_zone
best_zone_size = current_zone_size
// Try again on a later processing tick once ZAS has finished assigning/rebuilding the planet's surface zones
if(zas_pending)
return null
if(best_zone)
main_planetary_zones[zlevel_key] = best_zone
return best_zone
/obj/effect/overmap/visitable/sector/exoplanet/proc/remove_animal(mob/M)
animals -= M
@@ -318,6 +380,56 @@
/obj/effect/overmap/visitable/sector/exoplanet/proc/generate_features()
spawned_features = seedRuins(map_z, features_budget, possible_features, /area/exoplanet, maxx, maxy)
/**
* Applies the generated planetary atmosphere AFTER terrain and ruins finish loading.
*
* Exoplanet turf subtypes initialize themselves with the planet's air, but ruins and
* terrain generation may place ordinary simulated turfs which otherwise retain their
* type's standard atmosphere. The area's mapped is_outside value is authoritative:
* OUTSIDE_NO preserves sealed interiors, while every outdoor turf receives planetary air.
*
* Without this behavior, you see weird cross-contamination issues, especially in cases
* where maps don't have their /areas configured properly.
*/
/obj/effect/overmap/visitable/sector/exoplanet/proc/finalize_outdoor_atmosphere()
if(!atmosphere)
return
var/list/outdoor_zones = list()
for(var/zlevel in map_z)
var/turf/lower_left = locate(TRANSITIONEDGE + 1, TRANSITIONEDGE + 1, zlevel)
var/turf/upper_right = locate(maxx - (TRANSITIONEDGE + 1), maxy - (TRANSITIONEDGE + 1), zlevel)
if(!lower_left || !upper_right)
continue
for(var/turf/simulated/T in block(lower_left, upper_right))
var/area/A = get_area(T)
if(!A || A.is_outside == OUTSIDE_NO)
continue
T.initial_gas = atmosphere.gas.Copy()
T.temperature = atmosphere.temperature
// Dynamic z-levels are created after SSair initializes. Usually these turfs are
// still unzoned... but if ZAS already found them, take care not to break them.
if(TURF_HAS_VALID_ZONE(T))
outdoor_zones |= T.zone
else if(T.air)
T.air.group_multiplier = 1
T.air.copy_from(atmosphere)
else
T.make_air()
CHECK_TICK
// A zone stores ONE shared mixture, so update it once rather than once per member turf.
for(var/zone/Z as anything in outdoor_zones)
if(Z.invalid)
continue
Z.air.copy_from(atmosphere)
SSair.mark_zone_update(Z)
CHECK_TICK
/obj/effect/overmap/visitable/sector/exoplanet/proc/update_biome()
for(var/datum/seed/S as anything in seeds)
adapt_seed(S)
@@ -0,0 +1,7 @@
author: Batrachophrenoboocosmomachia
delete-after: True
changes:
- code_imp: "Exoplanets now cache their main outdoor atmospheric region instead of trying to search their entire map once per second."
- code_imp: "Exoplanet atmospheric calculations now run only when necessary using new update_revision counter."
- code_imp: "Exoplanet atmospheres now wait until all random ruins have been placed before trying to resolve the air everywhere."
- admin: "Adds new admin debug verb, 'Toggle Non-Horizon Temperature Graphics', which toggles all off-ship gas temp graphics to address lag in a pinch, if still necessary, without removing actual environmental hazard."