From b2f3eac474573145b8fed7e864a21140c87b2e20 Mon Sep 17 00:00:00 2001 From: Charlie Nolan Date: Tue, 16 Sep 2025 10:49:19 -0700 Subject: [PATCH] Add safeguards to `share_many_airs` and `gas_mixture` (#30077) * Add safeguards to `share_many_airs` * More safeguards! * Crash out of HE pipe heat transfer if transfer value is not sane. --- code/__DEFINES/_math.dm | 3 + .../atmospherics/gasmixtures/gas_mixture.dm | 59 ++++++++++++++++--- .../atmospherics/machinery/datum_pipeline.dm | 14 ++++- 3 files changed, 66 insertions(+), 10 deletions(-) diff --git a/code/__DEFINES/_math.dm b/code/__DEFINES/_math.dm index 6ce1dddaf1b..9e6a3e8be9a 100644 --- a/code/__DEFINES/_math.dm +++ b/code/__DEFINES/_math.dm @@ -254,3 +254,6 @@ /// Returns the hex value of a decimal number. len == length of returned string. #define num2hex(X, len) uppertext(num2text(X, len, 16)) + +/// Tests if the value is in the given range. +#define IS_IN_BOUNDS(val, lower, upper) ((val) >= (lower) && (val) <= (upper)) diff --git a/code/modules/atmospherics/gasmixtures/gas_mixture.dm b/code/modules/atmospherics/gasmixtures/gas_mixture.dm index 81f9985d576..92f20f384d6 100644 --- a/code/modules/atmospherics/gasmixtures/gas_mixture.dm +++ b/code/modules/atmospherics/gasmixtures/gas_mixture.dm @@ -58,43 +58,78 @@ What are the archived variables for? return private_oxygen /datum/gas_mixture/proc/set_oxygen(value) - private_oxygen = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_oxygen = clamped /datum/gas_mixture/proc/carbon_dioxide() return private_carbon_dioxide /datum/gas_mixture/proc/set_carbon_dioxide(value) - private_carbon_dioxide = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_carbon_dioxide = clamped /datum/gas_mixture/proc/nitrogen() return private_nitrogen /datum/gas_mixture/proc/set_nitrogen(value) - private_nitrogen = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_nitrogen = clamped /datum/gas_mixture/proc/toxins() return private_toxins /datum/gas_mixture/proc/set_toxins(value) - private_toxins = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_toxins = clamped /datum/gas_mixture/proc/sleeping_agent() return private_sleeping_agent /datum/gas_mixture/proc/set_sleeping_agent(value) - private_sleeping_agent = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_sleeping_agent = clamped /datum/gas_mixture/proc/agent_b() return private_agent_b /datum/gas_mixture/proc/set_agent_b(value) - private_agent_b = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_agent_b = clamped /datum/gas_mixture/proc/temperature() return private_temperature /datum/gas_mixture/proc/set_temperature(value) - private_temperature = value + if(isnan(value) || !isnum(value)) + CRASH("Bad value: [value]") + var/clamped = clamp(value, 0, 1e10) + if(value != clamped) + stack_trace("Out-of-bounds value [value] clamped to [clamped].") + private_temperature = clamped /datum/gas_mixture/proc/hotspot_temperature() return private_hotspot_temperature @@ -662,7 +697,7 @@ What are the archived variables for? private_hotspot_volume = milla[MILLA_INDEX_HOTSPOT_VOLUME] private_fuel_burnt = milla[MILLA_INDEX_FUEL_BURNT] -/proc/share_many_airs(list/mixtures) +/proc/share_many_airs(list/mixtures, atom/root) var/total_volume = 0 var/total_oxygen = 0 var/total_nitrogen = 0 @@ -691,9 +726,12 @@ What are the archived variables for? total_sleeping_agent += G.private_sleeping_agent total_agent_b += G.private_agent_b - if(total_volume <= 0) + if(total_volume == 0) return + if(total_volume < 0 || isnan(total_volume) || !isnum(total_volume) || total_oxygen < 0 || isnan(total_oxygen) || !isnum(total_oxygen) || total_nitrogen < 0 || isnan(total_nitrogen) || !isnum(total_nitrogen) || total_toxins < 0 || isnan(total_toxins) || !isnum(total_toxins) || total_carbon_dioxide < 0 || isnan(total_carbon_dioxide) || !isnum(total_carbon_dioxide) || total_sleeping_agent < 0 || isnan(total_sleeping_agent) || !isnum(total_sleeping_agent) || total_agent_b < 0 || isnan(total_agent_b) || !isnum(total_agent_b)) + CRASH("A pipenet with [length(mixtures)] connected airs is corrupt and cannot flow safely. Pipenet root is [root] at ([root.x], [root.y], [root.z]).") + // If we don't have a significant temperature difference, check for a significant gas amount difference. if(!must_share) for(var/datum/gas_mixture/G in mixtures) @@ -737,6 +775,9 @@ What are the archived variables for? if(total_heat_capacity > 0) temperature = total_thermal_energy/total_heat_capacity + if(temperature <= 0 || isnan(temperature) || !isnum(temperature)) + CRASH("A pipenet with [length(mixtures)] connected airs is corrupt and cannot flow safely. Pipenet root is [root] at ([root.x], [root.y], [root.z]).") + // Update individual gas_mixtures by volume ratio. for(var/datum/gas_mixture/G in mixtures) if(QDELETED(G)) diff --git a/code/modules/atmospherics/machinery/datum_pipeline.dm b/code/modules/atmospherics/machinery/datum_pipeline.dm index 0399408819b..a526f327f49 100644 --- a/code/modules/atmospherics/machinery/datum_pipeline.dm +++ b/code/modules/atmospherics/machinery/datum_pipeline.dm @@ -160,6 +160,9 @@ var/heat = thermal_conductivity*delta_temperature* \ (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + if(!IS_IN_BOUNDS(heat, -1e10, 1e10)) + CRASH("Sharing [partial_heat_capacity] @ [pipeline.air.temperature()]K with solid-wall environment [modeled_location.heat_capacity] @ [modeled_location.temperature]K produced out-of-bounds heat transfer [heat]!") + pipeline.air.set_temperature(pipeline.air.temperature() - heat / total_heat_capacity) modeled_location.temperature += heat/modeled_location.heat_capacity @@ -179,6 +182,9 @@ self_temperature_delta = -heat/total_heat_capacity sharer_temperature_delta = heat/sharer_heat_capacity + + if(!IS_IN_BOUNDS(heat, -1e10, 1e10)) + CRASH("Sharing [partial_heat_capacity] @ [pipeline.air.temperature()]K with environment [sharer_heat_capacity] @ [environment.temperature()]K produced out-of-bounds heat transfer [heat]!") else return 1 @@ -194,6 +200,8 @@ var/heat = thermal_conductivity * delta_temperature * \ (partial_heat_capacity * target.heat_capacity / (partial_heat_capacity + target.heat_capacity)) + if(!IS_IN_BOUNDS(heat, -1e10, 1e10)) + CRASH("Sharing [partial_heat_capacity] @ [pipeline.air.temperature()]K with static environment [target.heat_capacity] @ [target.temperature]K produced out-of-bounds heat transfer [heat]!") pipeline.air.set_temperature(pipeline.air.temperature() - heat / total_heat_capacity) pipeline.update = TRUE @@ -232,7 +240,11 @@ if(C.connected_device) GL += C.portableConnectorReturnAir() - share_many_airs(GL) + if(length(members)) + share_many_airs(GL, members[1]) + else if(length(other_atmosmch)) + share_many_airs(GL, other_atmosmch[1]) + // If neither has anything, GL will have no volumen, so nothing to share. /datum/pipeline/proc/add_ventcrawler(mob/living/crawler) if(!(crawler in crawlers))