From ef199be034a09cf0202d12d48eb5f71e0e417835 Mon Sep 17 00:00:00 2001 From: Jey Date: Wed, 13 Jan 2016 03:56:17 -0500 Subject: [PATCH 1/4] Add the air currents system. This hook into any atmospheric code (LINDA in our case) via 2 entry points and then generate long distance air currents to help with moving gas around in room / corridors etc. On the current settings, it use about 1/3 the cpu time that linda use in the same scenario (so it makes atmospherics about 33% time heavier) but is about 4 times faster at moving / stabilizing air around. By changing parameters it can be made to be much faster and dangerous but i figured people would not necessarily enjoy being flung into space the second they open the air lock to a breached room. --- code/LINDA/LINDA_system.dm | 1 + code/LINDA/LINDA_turf_tile.dm | 1 + code/controllers/Processes/aircurrents.dm | 480 ++++++++++++++++++++++ html/changelogs/Jey-aircurrents.yml | 35 ++ paradise.dme | 1 + 5 files changed, 518 insertions(+) create mode 100644 code/controllers/Processes/aircurrents.dm create mode 100644 html/changelogs/Jey-aircurrents.yml diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm index 2584192e7b43..7855b45eef03 100644 --- a/code/LINDA/LINDA_system.dm +++ b/code/LINDA/LINDA_system.dm @@ -128,6 +128,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5,air_group=0) CalculateAdjacentTurfs() if(air_master) air_master.add_to_active(src,command) + InvalidateAtmoPassCache() /atom/movable/proc/move_update_air(var/turf/T) if(istype(T,/turf)) diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index 63e5e4fe3252..1bc4afa9914d 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -281,6 +281,7 @@ /turf/simulated/proc/share_air(var/turf/simulated/T) if(T.current_cycle < current_cycle) var/difference + air_currents_master.onGasExchange(src, air.return_pressure(),T.air.return_pressure()) difference = air.share(T.air, atmos_adjacent_turfs_amount) if(difference) if(difference > 0) diff --git a/code/controllers/Processes/aircurrents.dm b/code/controllers/Processes/aircurrents.dm new file mode 100644 index 000000000000..5ec0502a7ef3 --- /dev/null +++ b/code/controllers/Processes/aircurrents.dm @@ -0,0 +1,480 @@ +//handle giving cycles to each individual air currents. +//air currents is a system that keep track of tiles where more than a defined % air was added in a cycle +//it then check neighboring tiles to find a path going away from the source with the most (or least depending on direction) air up to a defined number of turfs and equalise the air on the whole path at once. this will have for impact of spreading atmospherics much faster, heat can also be shared this way +//current will only stop once the air stabilised on the whole current line for a defined number of cycles +//it will also priorize holes open to space since it does not actually work on space, only 1 air current per hole can be active and as such without a multiplier would be dramatically slower than same air exchange between 2 connected room + +#define MINIMUM_AIR_CURRENTS_STARTPRESSURE 10 //any gas exchange with less pressure than this is just ignored if there is no currents in the area +#define MINIMUM_AIR_CURRENTS_EXTRAPRESSURE 3 //any gas exchange with less pressure than this is just ignored if there are currents in the area +#define MINIMUM_AIR_CURRENTS_STARTRATIO 0.3 //minimum ratio of pressure added to a tile in a cycle before its considered for the currents system when its the first +#define MINIMUM_AIR_CURRENTS_EXTRARATIO 0.1 //minimum ratio of pressure needed for subsequent currents +#define AIR_CURRENTS_LENGHT 8 //maximum number of turf considered for a current +#define AIR_CURRENTS_STABLE_CYCLE 3 //number of cycles where no air was exchanged by the current before it can be considered dead +#define AIR_CURRENTS_TOOSLOW_RATIO 0.09 //ratio at which its considered a slow currents and should be removed soon. should be slower than MINIMUM_AIR_CURRENTS_STARTRATIO +#define AIR_CURRENTS_TOOSLOW_CYCLE 10 //number of cycles where less than AIR_CURRENTS_TOOSLOW_RATIO pressure was added to the air current tile before we are considered done +#define AIR_CURRENTS_SPEED_FACTOR 1.0//1.0 cause the air in the current and the source to equilibrate 1 air_master tick in a closed room +#define AIR_CURRENTS_TEMPERATURE_SPEED 0.0001 //affect how fast the temperature spread using the air currents +#define AIR_CURRENTS_PRESSUREPUSH_MULT 2 //due to how the air is averaged on the chain, the pressure difference is actually often much smaller than it should be, so we simply apply add a multiplier +#define AIR_CURRENTS_SPACESIPHON_MULT 4 //if the source of the air current is next to a turf open to space, the current will "consume" AIR_CURRENTS_SPACESIPHON_MULT times the air the src used in its balance. This allow for accounting of the fact most of the air you add in the turf connected to space, end up continuing into space +var/global/datum/controller/process/aircurrents/air_currents_master +/datum/controller/process/aircurrents/var/list/active_air_currents[0] //our list of active air currents + +//uncomments to have a visual debug ingame of how the air currents act. Be warned this add a sizeable performance overhead +//#define AIRCURRENTDEBUG 1 +#ifdef AIRCURRENTDEBUG + +var/obj/effect/overlay/debugsrc +var/obj/effect/overlay/debug1 +var/obj/effect/overlay/debug2 +var/obj/effect/overlay/debug3 +var/obj/effect/overlay/debug4 +var/obj/effect/overlay/debug5 +var/obj/effect/overlay/debug6 +var/obj/effect/overlay/debug6p +#endif +/datum/controller/process/aircurrents/setup() + name = "air_currents" + schedule_interval = 20 + start_delay = 4 + air_currents_master = src +#ifdef AIRCURRENTDEBUG + debugsrc = new /obj/effect/overlay() + debugsrc.icon = 'icons/misc/debug_group.dmi' + debugsrc.icon_state = "" + debugsrc.layer = FLY_LAYER + debugsrc.mouse_opacity = 0 + + debug1 = new /obj/effect/overlay() + debug1.icon = 'icons/misc/debug_rebuild.dmi' + debug1.icon_state = "1" + debug1.layer = FLY_LAYER + debug1.mouse_opacity = 0 + + debug2 = new /obj/effect/overlay() + debug2.icon = 'icons/misc/debug_rebuild.dmi' + debug2.icon_state = "2" + debug2.layer = FLY_LAYER + debug2.mouse_opacity = 0 + + debug3 = new /obj/effect/overlay() + debug3.icon = 'icons/misc/debug_rebuild.dmi' + debug3.icon_state = "3" + debug3.layer = FLY_LAYER + debug3.mouse_opacity = 0 + + debug4 = new /obj/effect/overlay() + debug4.icon = 'icons/misc/debug_rebuild.dmi' + debug4.icon_state = "4" + debug4.layer = FLY_LAYER + debug4.mouse_opacity = 0 + + debug5 = new /obj/effect/overlay() + debug5.icon = 'icons/misc/debug_rebuild.dmi' + debug5.icon_state = "5" + debug5.layer = FLY_LAYER + debug5.mouse_opacity = 0 + + debug6 = new /obj/effect/overlay() + debug6.icon = 'icons/misc/debug_rebuild.dmi' + debug6.icon_state = "6" + debug6.layer = FLY_LAYER + debug6.mouse_opacity = 0 + + debug6p = new /obj/effect/overlay() + debug6p.icon = 'icons/misc/debug_rebuild.dmi' + debug6p.icon_state = "" + debug6p.layer = FLY_LAYER + debug6p.mouse_opacity = 0 +#endif +/datum/controller/process/aircurrents/statProcess() + ..() + stat(null, "[active_air_currents.len] active") + + +/datum/controller/process/aircurrents/doWork() + for (var/turf/simulated/T in active_air_currents) + if (T == null) + active_air_currents -= T + else + T.process_aircurrents() + return 1 + + + +/datum/controller/process/aircurrents/proc/removeTurf(var/turf/simulated/T) + + active_air_currents -= T //remove us, if we still need a current well be readded + if (T == null) + return + T.skipaircurrents = 0 + T.aircurrentsdirection = 0 + T.slowcount = 0 + T.stablecount = 0 + T.opentospace = 0 + var/area/A = get_area(T) + A.aircurrentsturfs -= T + +/datum/controller/process/aircurrents/proc/onGasExchange(var/turf/simulated/T, var/startpressure, var/endpressure) + //temporary + if (!T) + return + var/deltapressure = abs(startpressure - endpressure) + + var/area/A = get_area(T) + if (world.time - A.lastAirCurrentsCreated < 2) + return //try again next time + if (A.aircurrentsturfs.len > 0 || world.time - A.lastAirCurrents < 50) + if (deltapressure < MINIMUM_AIR_CURRENTS_EXTRAPRESSURE) + return + else + if (deltapressure < MINIMUM_AIR_CURRENTS_STARTPRESSURE) + return + if (T in air_currents_master.active_air_currents) + return + + if (A.aircurrentsturfs.len > 0) + //theres other currents in the area, so lets check distance + for (var/turf/simulated/Tcurrents in A.aircurrentsturfs) + var/dist = cheap_hypotenuse(Tcurrents.x, Tcurrents.y, T.x, T.y) + if (dist < AIR_CURRENTS_LENGHT) //its in collision range, lets see if we can do anything about it + if (Tcurrents.opentospace > 0) + return //space holes are very important + if (Tcurrents.aircurrentsdirection < 0 && startpressure < endpressure) //this one is spitting air, so lower priority + removeTurf(Tcurrents) + continue + if (dist < 3) //no placing anything within 3 dist if you cant remove the old one + return + if ((world.time - Tcurrents.lastcurrentseffect > 30 && T.lastcurrentseffect == 0 ) || (T.lastcurrentseffect != 0 && Tcurrents.lastcurrentseffect - T.lastcurrentseffect > 30)) //that tile has been untouched for at least 3 seconds even with a current in range, so go ahead + continue + + return //another in range so sorry no can do + + var/ratio + if (startpressure > endpressure) + if (endpressure == 0) + ratio = startpressure + else + ratio = deltapressure / endpressure + else + if (startpressure == 0) + ratio = endpressure + else + ratio = deltapressure / startpressure + + if (ratio > MINIMUM_AIR_CURRENTS_STARTRATIO || ((A.aircurrentsturfs.len > 0 || world.time - A.lastAirCurrents < 50) && ratio > MINIMUM_AIR_CURRENTS_EXTRARATIO)) + air_currents_master.active_air_currents |= T + A.aircurrentsturfs |= T + T.lastcurrentseffect = world.time + A.lastAirCurrentsCreated = world.time + if (startpressure > endpressure) + T.aircurrentsdirection = -1 + else + T.aircurrentsdirection = 1 + + +/datum/aircurrentspath/var/list/turfsInPath[0] +/datum/aircurrentspath/var/totalpressure=0 + + +/turf/simulated/var/aircurrentsdirection = 0 //1 if the air goes into the turf from the currents, and -1 if it goes out of the turf into the currents +/turf/simulated/var/skipaircurrents = 0 //skip process calls until this reach 0. When we find a case where theres just no path, no point in checking every 0.5 sec +/turf/simulated/var/stablecount = 0 +/turf/simulated/var/slowcount = 0 +/turf/simulated/var/lastcurrentseffect = 0 //used to determine if a tile is reasonably safe from being affected by a current by comparing with source time. If its been more than 10 seconds with no contact, assume there wont ever be any +/turf/simulated/var/opentospace = 0 +/area/var/list/aircurrentsturfs[0] //list containing the currently active currents in the area +/area/var/lastAirCurrents = 0 //used to air currents can become contagious inter area +/area/var/lastAirCurrentsCreated = 0 //only make at most 1 air current per area per 0.2 sec + +/turf/var/list/atmopasscache[4] +/turf/var/list/atmopasscachetime[4] +/turf/proc/InvalidateAtmoPassCache() //this way door opening get their updates instantly. most walls and other airblock destruction and construction also call this + atmopasscachetime[1] = 0 + atmopasscachetime[2] = 0 + atmopasscachetime[3] = 0 + atmopasscachetime[4] = 0 + +//similar to the linda version, except it cache result up to 5seconds and does not update atmos_supeconductivity +//(cache invalidated by air_update_turf which doors call on open / close so that those do not get delayed). +//This does mean that object destruction / repair could take up to 5 seconds to update for the air currents if they did not call air_update_turf() +//this is a necessary compromise to achieve low cpu load with a faster air update speed where needed +/turf/proc/CanAtmosPassCached(var/turf/T, var/D) + if(!istype(T)) return 0 + var/cacheIndex = 0 + switch (D) + if (NORTH) cacheIndex = 1 + if (EAST) cacheIndex = 2 + if (SOUTH) cacheIndex = 3 + if (WEST) cacheIndex = 4 + + if (world.time - atmopasscachetime[cacheIndex] < 50) + return atmopasscache[cacheIndex] + + var/R + if(blocks_air || T.blocks_air) + R = 1 + + for(var/obj/O in contents) + if(!O.CanAtmosPass(T)) + R = 1 + if(O.BlockSuperconductivity()) //the direction and open/closed are already checked on CanAtmosPass() so there are no arguments + atmopasscachetime[cacheIndex] = world.time + atmopasscache[cacheIndex] = 0 + return 0 //no need to keep going, we got all we asked + + for(var/obj/O in T.contents) + if(!O.CanAtmosPass(src)) + R = 1 + if(O.BlockSuperconductivity()) + atmopasscachetime[cacheIndex] = world.time + atmopasscache[cacheIndex] = 0 + return 0 + + if(!R) + atmopasscachetime[cacheIndex] = world.time + atmopasscache[cacheIndex] = 1 + return 1 + atmopasscachetime[cacheIndex] = world.time + atmopasscache[cacheIndex] = 0 + + +/turf/simulated/proc/process_aircurrents() //find us an air current path around us + if (skipaircurrents > 1) + skipaircurrents-- + return + if (skipaircurrents == 1) + air_currents_master.removeTurf(src) + return + + var/list/paths[0] + var/list/usedturfs[0] + + opentospace = 0 + for(var/directionstart in cardinal) //check for a path in all 4 directions + var/turf/sourcetile = get_step(src, directionstart) + if (!CanAtmosPassCached(sourcetile,directionstart)) + continue + + var/datum/aircurrentspath/path = new() + if(istype(sourcetile,/turf/simulated)) //we allow for the first tile to be space, maybe its a hole in the floor, so skip above + var/turf/simulated/source = sourcetile + path.turfsInPath.Add(source) + usedturfs.Add(source) + path.totalpressure += source.air.return_pressure() + if (istype(source, /turf/unsimulated)) //space + opentospace++ + for(var/i=0, i < AIR_CURRENTS_LENGHT, i++) + var/turf/simulated/highestPressureT = null + var/bestPressureVal + if (aircurrentsdirection > 0) + bestPressureVal = -1 + else + bestPressureVal = 999999999 //we are looking for the lowest pressure path to dump air in + for(var/direction in cardinal) + /*if(!(atmos_adjacent_turfs & direction)) + continue*/ + + var/turf/tile = get_step(sourcetile, direction) + if(!istype(tile,/turf/simulated)) + continue + var/turf/simulated/T = tile + if (!sourcetile.CanAtmosPassCached(T,direction)) + continue + if (!T.air) + continue + if (T in path.turfsInPath) + continue //cant step back on our track + var/press = T.air.return_pressure() + if (aircurrentsdirection > 0) + if (press > bestPressureVal) + bestPressureVal = press + highestPressureT = T + else + if (press < bestPressureVal) + bestPressureVal = press + highestPressureT = T + if (bestPressureVal == -1 || bestPressureVal == 999999999) + break + path.turfsInPath.Add(highestPressureT) + usedturfs.Add(highestPressureT) + path.totalpressure +=bestPressureVal + sourcetile = highestPressureT + if (path.turfsInPath.len > 0) + paths.Add(path) + + if (paths.len == 0) + skipaircurrents = 16 + rand(0,8) //try again in about 8-12 sec + return + + var/bestPressureVal + var/datum/aircurrentspath/path = null + if (aircurrentsdirection > 0) + bestPressureVal = -1 + else + bestPressureVal = 999999999 //we are looking for the lowest pressure path to dump air in + for (var/datum/aircurrentspath/possiblePath in paths) //find the best path for our air current + if (aircurrentsdirection > 0) + if (possiblePath.totalpressure > bestPressureVal) + bestPressureVal = possiblePath.totalpressure + path = possiblePath + else + if (possiblePath.totalpressure < bestPressureVal) + bestPressureVal = possiblePath.totalpressure + path = possiblePath + + if (bestPressureVal == -1 || bestPressureVal == 999999999) + skipaircurrents = 16 + rand(0,8) //try again in about 8-12 sec + return null + + + + var/pathpressurestart = src.air.return_pressure() + //now we have a set of turfs we can equalise and push things around on + var/totalOxygen = 0 + var/totalCarbon = 0 + var/totalNitrogen = 0 + var/totalToxins = 0 + var/totalTemperature = 0 + + for (var/turf/simulated/T in path.turfsInPath) + totalOxygen += T.air.oxygen + totalCarbon += T.air.carbon_dioxide + totalNitrogen += T.air.nitrogen + totalToxins += T.air.toxins + totalTemperature += T.air.temperature + + var/averageOxygen = totalOxygen/path.turfsInPath.len + var/averageCarbon = totalCarbon/path.turfsInPath.len + var/averageNitrogen = totalNitrogen/path.turfsInPath.len + var/averageToxins = totalToxins/path.turfsInPath.len + var/averageTemperature = totalTemperature/path.turfsInPath.len + + var/deltaOxy = AIR_CURRENTS_SPEED_FACTOR * (averageOxygen - src.air.oxygen)/(1+air_master.schedule_interval/air_currents_master.schedule_interval) + var/deltaCarb = AIR_CURRENTS_SPEED_FACTOR * (averageCarbon - src.air.carbon_dioxide)/(1+air_master.schedule_interval/air_currents_master.schedule_interval) + var/deltaNitro = AIR_CURRENTS_SPEED_FACTOR * (averageNitrogen - src.air.nitrogen)/(1+air_master.schedule_interval/air_currents_master.schedule_interval) + var/deltaTox = AIR_CURRENTS_SPEED_FACTOR * (averageToxins - src.air.toxins)/(1+air_master.schedule_interval/air_currents_master.schedule_interval) + var/deltaTemp = AIR_CURRENTS_TEMPERATURE_SPEED * (averageTemperature - src.air.temperature)/(1+air_master.schedule_interval/air_currents_master.schedule_interval) + + if (opentospace == 0) + totalOxygen -=deltaOxy + totalCarbon -=deltaCarb + totalNitrogen -=deltaNitro + totalToxins -=deltaTox + totalTemperature -=deltaTemp + else + totalOxygen -=deltaOxy * opentospace * AIR_CURRENTS_SPACESIPHON_MULT //were open to space so grab more air. air currents does not travel on the space tile so without this multiplier hole to space are much slower to act than hole between room + totalCarbon -=deltaCarb * opentospace * AIR_CURRENTS_SPACESIPHON_MULT + totalNitrogen -=deltaNitro * opentospace * AIR_CURRENTS_SPACESIPHON_MULT + totalToxins -=deltaTox * opentospace * AIR_CURRENTS_SPACESIPHON_MULT + totalTemperature -=deltaTemp * opentospace * AIR_CURRENTS_SPACESIPHON_MULT + + + var/totalTurfsPath = path.turfsInPath.len + + src.air.oxygen += deltaOxy + src.air.carbon_dioxide += deltaCarb + src.air.nitrogen += deltaNitro + src.air.toxins += deltaTox + src.air.temperature += deltaTemp + + + + for (var/turf/simulated/T in path.turfsInPath) //equalise the air + air_master.add_to_active(T) + var/area/A = get_area(T) + A.lastAirCurrents = world.time + + T.lastcurrentseffect = world.time + T.air.oxygen = totalOxygen/totalTurfsPath + T.air.carbon_dioxide = totalCarbon/totalTurfsPath + T.air.nitrogen = totalNitrogen/totalTurfsPath + T.air.toxins = totalToxins/totalTurfsPath + T.air.temperature = totalTemperature/totalTurfsPath + + var/pathpressureend = src.air.return_pressure() + var/pathpressuremvm + if (opentospace > 0) + pathpressuremvm = abs(pathpressureend - pathpressurestart) * opentospace * AIR_CURRENTS_SPACESIPHON_MULT //also multiply the pressure moved + else + pathpressuremvm = abs(pathpressureend - pathpressurestart) + + + if (pathpressuremvm < 0) //air went away so the mvm is from the source down the path + var/turf/currentTile = src + for (var/turf/simulated/T in path.turfsInPath) + air_currents_master.consider_path_pressure(currentTile, T, pathpressuremvm) + currentTile = T + else //air goes from the path to the tile so the air pressure push stuff toward src + var/turf/currentTile = path.turfsInPath[path.turfsInPath.len] + var/turf/T + if (path.turfsInPath.len > 0) + for(var/i=path.turfsInPath.len-1,i>0,i--) + T = path.turfsInPath[i] + air_currents_master.consider_path_pressure(currentTile, T, pathpressuremvm*AIR_CURRENTS_PRESSUREPUSH_MULT) + currentTile = T + air_currents_master.consider_path_pressure(currentTile, src, pathpressuremvm*AIR_CURRENTS_PRESSUREPUSH_MULT) //last tile is the src since its not part of the path + + + + if (pathpressuremvm == 0) + stablecount++ + slowcount++ + else + stablecount = 0 + var/pressuremvmratio + if (pathpressureend > pathpressurestart) + pressuremvmratio = pathpressuremvm / pathpressurestart + else + pressuremvmratio = pathpressuremvm / pathpressureend + if (pressuremvmratio < AIR_CURRENTS_TOOSLOW_RATIO) + slowcount++ + else + slowcount = 0 +#ifdef AIRCURRENTDEBUG + spawn(0) + src.overlays |= debugsrc + sleep(0.3) + var/count = 1 + for (var/turf/simulated/T in path.turfsInPath) + switch (count) + if(1) T.overlays |= debug1 + if(2) T.overlays |= debug2 + if(3) T.overlays |= debug3 + if(4) T.overlays |= debug4 + if(5) T.overlays |= debug5 + if(6) T.overlays |= debug6 + else T.overlays |= debug6p + count++ + sleep(0.3) + sleep(3) + src.overlays -= debugsrc + sleep(0.3) + count = 1 + for (var/turf/simulated/T in path.turfsInPath) + switch (count) + if(1) T.overlays -= debug1 + if(2) T.overlays -= debug2 + if(3) T.overlays -= debug3 + if(4) T.overlays -= debug4 + if(5) T.overlays -= debug5 + if(6) T.overlays -= debug6 + else T.overlays -= debug6p + count++ + sleep(0.3) + if (opentospace > 0) + if (stablecount >= AIR_CURRENTS_STABLE_CYCLE*3) + air_currents_master.removeTurf(src) + else if (slowcount >= AIR_CURRENTS_TOOSLOW_CYCLE*3) + air_currents_master.removeTurf(src) + else + if (stablecount >= AIR_CURRENTS_STABLE_CYCLE) + air_currents_master.removeTurf(src) + else if (slowcount >= AIR_CURRENTS_TOOSLOW_CYCLE) + air_currents_master.removeTurf(src) +#endif + +/datum/controller/process/aircurrents/proc/consider_path_pressure(var/turf/simulated/Tsrc, var/turf/simulated/Tdest, var/difference) + air_master.high_pressure_delta |= Tsrc + if(difference > Tsrc.pressure_difference) + Tsrc.pressure_direction = get_dir(Tsrc, Tdest) + Tsrc.pressure_difference = difference \ No newline at end of file diff --git a/html/changelogs/Jey-aircurrents.yml b/html/changelogs/Jey-aircurrents.yml new file mode 100644 index 000000000000..c49e3c2e28b7 --- /dev/null +++ b/html/changelogs/Jey-aircurrents.yml @@ -0,0 +1,35 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. Remove the quotation mark and put in your name when copy+pasting the example changelog. +author: Jey123456 + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit. +# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog. +changes: + - tweak: "Add a system on top of LINDA that create air currents and greatly accelerate atmospheric propagations at minimum load" diff --git a/paradise.dme b/paradise.dme index 580b9217bbfa..f28b89fe16af 100644 --- a/paradise.dme +++ b/paradise.dme @@ -153,6 +153,7 @@ #include "code\controllers\verbs.dm" #include "code\controllers\voting.dm" #include "code\controllers\Processes\air.dm" +#include "code\controllers\Processes\aircurrents.dm" #include "code\controllers\Processes\alarm.dm" #include "code\controllers\Processes\bot.dm" #include "code\controllers\Processes\event.dm" From 386620d133977fa45773bd03645ccb4dc5e614a9 Mon Sep 17 00:00:00 2001 From: Jey Date: Wed, 13 Jan 2016 07:56:05 -0500 Subject: [PATCH 2/4] added SCHECK moved dowork content to process_allcurrents() --- code/controllers/Processes/aircurrents.dm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/code/controllers/Processes/aircurrents.dm b/code/controllers/Processes/aircurrents.dm index 5ec0502a7ef3..5a85d828abdf 100644 --- a/code/controllers/Processes/aircurrents.dm +++ b/code/controllers/Processes/aircurrents.dm @@ -92,14 +92,16 @@ var/obj/effect/overlay/debug6p /datum/controller/process/aircurrents/doWork() + process_allcurrents() + return 1 + +/datum/controller/process/aircurrents/proc/process_allcurrents() for (var/turf/simulated/T in active_air_currents) if (T == null) active_air_currents -= T else T.process_aircurrents() - return 1 - - + SCHECK /datum/controller/process/aircurrents/proc/removeTurf(var/turf/simulated/T) From 92207f69eb47cd930ca1bd5d25f4f0b4e4f99686 Mon Sep 17 00:00:00 2001 From: Jey Date: Wed, 13 Jan 2016 12:58:30 -0500 Subject: [PATCH 3/4] split the aircurrents and airmaster cycle in 4. Instead of once every 20 tick (2 sec) , its doing 1/4 of the work every 5 tick. This has virtually no impact on the total cpu usage of either proc, but it does reduce the lag felt in game by lot. In a moderatly damaged station atmo, air currents proc use about 0.026sec of cpu. But obviously this can go up higher. By splitting it in 4 instead of having that 0.026 sec to soak every 2 second, its a 0.007sec load to handle every 0.5sec. The air system is about twice that but the same logic applies. As long as their cpu per call does not go above 1sec on the current code, then this version of the code should eliminate any lag spikes felts by the player from those 2 specific processes. As a free bonus, it does have for impact that randomly the air seem to update faster even if it does not, because you can see tiles change every 0.5 sec instead of 2sec (but its only 1/4 of the tiles youd normally see every 2 sec) --- code/controllers/Processes/air.dm | 30 +++++++++++++++++++---- code/controllers/Processes/aircurrents.dm | 23 ++++++++++++++--- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/code/controllers/Processes/air.dm b/code/controllers/Processes/air.dm index 58456b76d8b7..59d46da69154 100644 --- a/code/controllers/Processes/air.dm +++ b/code/controllers/Processes/air.dm @@ -1,16 +1,19 @@ var/kill_air = 0 var/global/datum/controller/process/air_system/air_master - +/turf/var/activeturf_airmastercycle = 0 +/turf/var/excitedturf_airmastercycle = 0 /datum/controller/process/air_system var/list/excited_groups = list() var/list/active_turfs = list() var/list/hotspots = list() + //Special functions lists var/list/turf/simulated/active_super_conductivity = list() var/list/turf/simulated/high_pressure_delta = list() + var/sub_cycle = 0 var/current_cycle = 0 var/failed_ticks = 0 var/tick_progress = 0 @@ -24,7 +27,7 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/setup() name = "air" - schedule_interval = 20 // every 2 seconds + schedule_interval = 5 // every 2 seconds start_delay = 4 air_master = src @@ -37,7 +40,10 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/doWork() if(kill_air) return 1 - current_cycle++ + if (sub_cycle >= 4) + current_cycle++ + sub_cycle = 0 + sub_cycle++ process_active_turfs() process_excited_groups() process_high_pressure_delta() @@ -52,13 +58,13 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/proc/process_hotspots() last_hotspots = hotspots.len - for(var/obj/effect/hotspot/H in hotspots) + for(var/obj/effect/hotspot/H in hotspots) //we could split it as well, but at the same time it never represented much load in any profile i saw, so maybe we could just let it be faster H.process() SCHECK /datum/controller/process/air_system/proc/process_super_conductivity() last_asc = active_super_conductivity.len - for(var/turf/simulated/T in active_super_conductivity) + for(var/turf/simulated/T in active_super_conductivity) //we could split it as well, but at the same time it never represented much load in any profile i saw, so maybe we could just let it be faster T.super_conduct() SCHECK @@ -72,8 +78,15 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/proc/process_active_turfs() last_active = active_turfs.len + var/count = 0 for(var/turf/simulated/T in active_turfs) + if (T.activeturf_airmastercycle == current_cycle) + continue + if (count > last_active/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) + break + count++ T.process_cell() + T.activeturf_airmastercycle = current_cycle SCHECK /datum/controller/process/air_system/proc/remove_from_active(var/turf/simulated/T) @@ -99,6 +112,7 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/proc/setup_allturfs(var/turfs_in = world) for(var/turf/simulated/T in turfs_in) + T.excitedturf_airmastercycle = current_cycle T.CalculateAdjacentTurfs() if(!T.blocks_air) T.update_visuals() @@ -119,7 +133,13 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/proc/process_excited_groups() last_excited = excited_groups.len + var/count = 0 for(var/datum/excited_group/EG in excited_groups) + if (T.activeturf_airmastercycle == current_cycle) + continue + if (count > last_excited/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) + break + count++ EG.breakdown_cooldown++ if(EG.breakdown_cooldown == 10) EG.self_breakdown() diff --git a/code/controllers/Processes/aircurrents.dm b/code/controllers/Processes/aircurrents.dm index 5a85d828abdf..6519d5cd36da 100644 --- a/code/controllers/Processes/aircurrents.dm +++ b/code/controllers/Processes/aircurrents.dm @@ -18,7 +18,8 @@ #define AIR_CURRENTS_SPACESIPHON_MULT 4 //if the source of the air current is next to a turf open to space, the current will "consume" AIR_CURRENTS_SPACESIPHON_MULT times the air the src used in its balance. This allow for accounting of the fact most of the air you add in the turf connected to space, end up continuing into space var/global/datum/controller/process/aircurrents/air_currents_master /datum/controller/process/aircurrents/var/list/active_air_currents[0] //our list of active air currents - +/datum/controller/process/aircurrents/var/current_cycle = 0 +/datum/controller/process/aircurrents/var/sub_cycle = 0 //uncomments to have a visual debug ingame of how the air currents act. Be warned this add a sizeable performance overhead //#define AIRCURRENTDEBUG 1 #ifdef AIRCURRENTDEBUG @@ -34,7 +35,7 @@ var/obj/effect/overlay/debug6p #endif /datum/controller/process/aircurrents/setup() name = "air_currents" - schedule_interval = 20 + schedule_interval = 5 start_delay = 4 air_currents_master = src #ifdef AIRCURRENTDEBUG @@ -92,15 +93,29 @@ var/obj/effect/overlay/debug6p /datum/controller/process/aircurrents/doWork() + if (sub_cycle >= 4) + current_cycle++ + sub_cycle = 0 + sub_cycle++ process_allcurrents() return 1 +/turf/var/currentsmastercycle = 0 /datum/controller/process/aircurrents/proc/process_allcurrents() + var/size = active_air_currents.len + var/count = 0 for (var/turf/simulated/T in active_air_currents) if (T == null) active_air_currents -= T - else - T.process_aircurrents() + continue + if (T.currentsmastercycle == current_cycle) + continue + if (count > size/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) + break + count++ + + T.currentsmastercycle = current_cycle + T.process_aircurrents() SCHECK /datum/controller/process/aircurrents/proc/removeTurf(var/turf/simulated/T) From 3b41d71ad82b9e086352d2bf2bed43fc1610c2ea Mon Sep 17 00:00:00 2001 From: Jey Date: Wed, 13 Jan 2016 13:44:44 -0500 Subject: [PATCH 4/4] - wrote the code too quickly and did not notice the exception spam while i tested xD. this should work better --- code/controllers/Processes/air.dm | 9 +++++---- code/controllers/Processes/aircurrents.dm | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/controllers/Processes/air.dm b/code/controllers/Processes/air.dm index 59d46da69154..3fabb770d190 100644 --- a/code/controllers/Processes/air.dm +++ b/code/controllers/Processes/air.dm @@ -2,7 +2,7 @@ var/kill_air = 0 var/global/datum/controller/process/air_system/air_master /turf/var/activeturf_airmastercycle = 0 -/turf/var/excitedturf_airmastercycle = 0 +/datum/excited_group/var/excitedturf_airmastercycle = 0 /datum/controller/process/air_system var/list/excited_groups = list() var/list/active_turfs = list() @@ -85,8 +85,9 @@ var/global/datum/controller/process/air_system/air_master if (count > last_active/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) break count++ - T.process_cell() T.activeturf_airmastercycle = current_cycle + T.process_cell() + SCHECK /datum/controller/process/air_system/proc/remove_from_active(var/turf/simulated/T) @@ -112,7 +113,6 @@ var/global/datum/controller/process/air_system/air_master /datum/controller/process/air_system/proc/setup_allturfs(var/turfs_in = world) for(var/turf/simulated/T in turfs_in) - T.excitedturf_airmastercycle = current_cycle T.CalculateAdjacentTurfs() if(!T.blocks_air) T.update_visuals() @@ -135,11 +135,12 @@ var/global/datum/controller/process/air_system/air_master last_excited = excited_groups.len var/count = 0 for(var/datum/excited_group/EG in excited_groups) - if (T.activeturf_airmastercycle == current_cycle) + if (EG.excitedturf_airmastercycle == current_cycle) continue if (count > last_excited/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) break count++ + EG.excitedturf_airmastercycle = current_cycle EG.breakdown_cooldown++ if(EG.breakdown_cooldown == 10) EG.self_breakdown() diff --git a/code/controllers/Processes/aircurrents.dm b/code/controllers/Processes/aircurrents.dm index 6519d5cd36da..96544db31537 100644 --- a/code/controllers/Processes/aircurrents.dm +++ b/code/controllers/Processes/aircurrents.dm @@ -113,7 +113,6 @@ var/obj/effect/overlay/debug6p if (count > size/4 && sub_cycle < 4) //do at most 1/4 every subcycle, unless its the last one, then do whatever is left (to cover those added etc) break count++ - T.currentsmastercycle = current_cycle T.process_aircurrents() SCHECK