From afd8d9d8e0f7aecf081abc8ffe63ba993ad7993d Mon Sep 17 00:00:00 2001 From: Wildkins Date: Mon, 9 Mar 2026 17:47:50 -0400 Subject: [PATCH] Lag War 2: Revengeance (#21950) image Ports: tgstation/tgstation#75769, tgstation/tgstation#72572, tgstation/tgstation#93054 Fixes the holodeck causing 800 trillion hard dels that brings the server to its knees. Stops turrets, weather system, and overmap contacts from generating 500+ timers every tick by just using vars instead. --------- Signed-off-by: Wildkins --- aurorastation.dme | 1 + code/__DEFINES/mobs.dm | 2 +- code/__HELPERS/area_movement.dm | 3 +- code/__HELPERS/duplicating.dm | 88 +++++++++++++++++++ code/__HELPERS/unsorted.dm | 18 ---- code/game/machinery/portable_turret.dm | 14 ++- code/modules/holodeck/HolodeckControl.dm | 2 + code/modules/mob/living/life.dm | 3 +- code/modules/mob/living/living_defines.dm | 3 + code/modules/overmap/contacts/_contacts.dm | 17 ++-- .../overmap/contacts/contact_sensors.dm | 7 +- code/modules/weather/weather_fsm_states.dm | 2 +- code/modules/weather/weather_mob_tracking.dm | 14 --- html/changelogs/johnwildkins-lol.yml | 14 +++ 14 files changed, 128 insertions(+), 60 deletions(-) create mode 100644 code/__HELPERS/duplicating.dm create mode 100644 html/changelogs/johnwildkins-lol.yml diff --git a/aurorastation.dme b/aurorastation.dme index 86b12c9934e..cf918ba819f 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -203,6 +203,7 @@ #include "code\__HELPERS\atoms.dm" #include "code\__HELPERS\data_structures.dm" #include "code\__HELPERS\dll_call.dm" +#include "code\__HELPERS\duplicating.dm" #include "code\__HELPERS\emissives.dm" #include "code\__HELPERS\files.dm" #include "code\__HELPERS\functional.dm" diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index c9901acae9a..4a832647a1d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -632,4 +632,4 @@ /// Recharges from external lighting. Slower at recharging from external sources. #define POWER_SUPPLY_SOLAR 8 - +#define WEATHER_COOLDOWN_TIME (5 SECONDS) diff --git a/code/__HELPERS/area_movement.dm b/code/__HELPERS/area_movement.dm index 859848c63bd..fc00fdb95ee 100644 --- a/code/__HELPERS/area_movement.dm +++ b/code/__HELPERS/area_movement.dm @@ -110,8 +110,7 @@ for (var/thing in ST) var/atom/movable/AM = thing - var/atom/movable/copy = DuplicateObject(AM, 1) - copy.forceMove(TT) + var/atom/movable/copy = duplicate_object(AM, TT) . += copy SSair.mark_for_update(TT) diff --git a/code/__HELPERS/duplicating.dm b/code/__HELPERS/duplicating.dm new file mode 100644 index 00000000000..ef89c72c32f --- /dev/null +++ b/code/__HELPERS/duplicating.dm @@ -0,0 +1,88 @@ +///List of all vars that will not be copied over when using duplicate_object() +GLOBAL_LIST_INIT(duplicate_forbidden_vars, list("actions", + "_active_timers", + "appearance", + "area", + "bodyparts", + "ckey", + "computer_id", + "contents", + "cooldowns", + "_datum_components", + "emissive_overlay", + "group", + "hud_list", + "important_recursive_contents", + "internal_organs", + "internal_organs_by_name", + "_listen_lookup", + "organs", + "organs_by_name", + "key", + "lastKnownIP", + "loc", + "locs", + "overlays", + "parent", + "parent_type", + "pixloc", + "power_supply", + "reagents", + "_signal_procs", + "status_traits", + "stat", + "tag", + "tgui_shared_states", + "type", + "vars", + "verbs", + "x", "y", "z", +)) +GLOBAL_PROTECT(duplicate_forbidden_vars) + +/** + * # duplicate_object + * + * Makes a copy of an item and transfers most vars over, barring GLOB.duplicate_forbidden_vars + * Args: + * original - Atom being duplicated + * spawning_location - Turf where the duplicated atom will be spawned at. + */ +/proc/duplicate_object(atom/original, turf/spawning_location) + RETURN_TYPE(original.type) + if(!original) + return + + var/atom/made_copy = new original.type(spawning_location) + + for(var/atom_vars in (original.vars - GLOB.duplicate_forbidden_vars)) + var/var_value = original.vars[atom_vars] + if(isdatum(var_value)) + continue // this would reference the original's object, that will break when it is used or deleted. + + if(islist(var_value)) + var/list/var_list = var_value + var_value = var_list.Copy() + + made_copy.vars[atom_vars] = var_value + + if(!iscarbon(made_copy)) + return made_copy + + var/mob/living/carbon/original_carbon = original + var/mob/living/carbon/copied_carbon = made_copy + var/obj/item/organ/internal/brain/original_brain = original_carbon.internal_organs_by_name[BP_BRAIN] + original_brain.transfer_identity(copied_carbon) + + if(!ishuman(made_copy)) + return made_copy + + var/mob/living/carbon/human/original_human = original + //transfer implants, we do this so the original's implants being removed won't destroy ours. + for(var/obj/item/organ/external/limb in original_human.organs) + var/list/implants = limb.implants + for(var/obj/item/implant/original_implants as anything in implants) + var/obj/item/implant/copied_implant = new original_implants.type + copied_implant.implantInMob(made_copy) + + return made_copy diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index d7e9c4acb86..3bb3be93463 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -782,24 +782,6 @@ Turf and target are seperate in case you want to teleport some distance from a t if(A.vars.Find(lowertext(varname))) return 1 else return 0 -/proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) - if(!original) - return null - - var/obj/O = null - - if(sameloc) - O=new original.type(original.loc) - else - O=new original.type(locate(0,0,0)) - - if(perfectcopy) - if((O) && (original)) - for(var/V in original.vars) - if(!(V in list("type","loc","locs","vars", "parent", "parent_type","verbs","ckey","key"))) - O.vars[V] = original.vars[V] - return O - /proc/get_cardinal_dir(atom/A, atom/B) var/dx = abs(B.x - A.x) var/dy = abs(B.y - A.y) diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index f37586faa48..d8f792b2249 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -547,14 +547,10 @@ /obj/machinery/porta_turret/process() //the main machinery process - if(stat & (NOPOWER|BROKEN)) - //if the turret has no power or is broken, make the turret pop down if it hasn't already - popDown() - return - - if(!enabled) - //if the turret is off, make it pop down - popDown() + if((stat & (NOPOWER|BROKEN)) || !enabled) + //if the turret has no power, is broken, or off, make the turret pop down if it hasn't already + if(raised || raising) + popDown() return if(auto_repair && (health < maxhealth)) @@ -579,7 +575,7 @@ if(!tryToShootAt(targets)) tryToShootAt(secondarytargets) - if(!targets.len && !secondarytargets.len) + if(raised && !targets.len && !secondarytargets.len) resetting = addtimer(CALLBACK(src, PROC_REF(reset)), 6 SECONDS, TIMER_UNIQUE | TIMER_STOPPABLE) // no valid targets, close the cover else if(resetting) deltimer(resetting) diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm index 11ef8f5e9d8..8cbe8f1018e 100644 --- a/code/modules/holodeck/HolodeckControl.dm +++ b/code/modules/holodeck/HolodeckControl.dm @@ -315,6 +315,8 @@ GLOBAL_LIST_EMPTY_TYPED(holodeck_controls, /obj/machinery/computer/holodeck_cont active = 1 update_use_power(POWER_USE_ACTIVE) + QDEL_LIST(holodeck_landmarks) + for(var/item in holographic_objs) derez(item) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 8deed2926d0..2ee3398ab75 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -220,7 +220,8 @@ // Handle physical effects of weather. var/singleton/state/weather/weather_state var/obj/abstract/weather_system/weather = get_affecting_weather() - if(weather) + if(weather_cooldown_time <= world.time && weather) + weather_cooldown_time = world.time + WEATHER_COOLDOWN_TIME weather_state = weather.weather_system.current_state if(istype(weather_state)) weather_state.handle_exposure(src, get_weather_exposure(weather), weather) diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 7f05147cb29..858a414a09c 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -103,3 +103,6 @@ /// If true, ignores weather effects var/resists_weather = FALSE + + /// Time since last weather effect + var/weather_cooldown_time = 0 diff --git a/code/modules/overmap/contacts/_contacts.dm b/code/modules/overmap/contacts/_contacts.dm index 58a43197167..18986c07783 100644 --- a/code/modules/overmap/contacts/_contacts.dm +++ b/code/modules/overmap/contacts/_contacts.dm @@ -3,7 +3,7 @@ var/name = "Unknown" // Contact name. var/image/marker // Image overlay attached to the contact. - var/pinged = FALSE // Used to animate overmap effects. + var/pinged_until = 0 // Used to animate overmap effects. var/list/images = list() // Our list of images to cast to users. var/image/radar // Radar image for sonar esque effect @@ -70,18 +70,15 @@ M.client.images |= images -/datum/overmap_contact/proc/ping() - if(pinged) +/datum/overmap_contact/proc/ping(time_delay = 0) + if(pinged_until > world.time) return - pinged = TRUE + pinged_until = world.time + time_delay + 1 SECOND effect.opacity = initial(effect.opacity) show() - animate(marker, alpha=255, 0.5 SECOND, 1, LINEAR_EASING) - addtimer(CALLBACK(src, PROC_REF(unping)), 1 SECOND) - -/datum/overmap_contact/proc/unping() - animate(marker, alpha=75, 2 SECOND, 1, LINEAR_EASING) - pinged = FALSE + animate(marker, alpha=75, time_delay, 1, flags = ANIMATION_END_NOW) + animate(alpha=255, 1 SECOND, 1) + animate(alpha=75, 2 SECOND, 1) /datum/overmap_contact/Destroy() if(owner) diff --git a/code/modules/overmap/contacts/contact_sensors.dm b/code/modules/overmap/contacts/contact_sensors.dm index daa0c08ba64..7225e2023f9 100644 --- a/code/modules/overmap/contacts/contact_sensors.dm +++ b/code/modules/overmap/contacts/contact_sensors.dm @@ -139,10 +139,9 @@ continue // Update identification information for this record. record.update_marker_icon() - - var/time_delay = max((SENSOR_TIME_DELAY * get_dist(linked, contact)),1) - if(!record.pinged) - addtimer(CALLBACK(record, PROC_REF(ping)), time_delay) + if(record.pinged_until <= world.time) + var/time_delay = max((SENSOR_TIME_DELAY * get_dist(linked, contact)), 1 SECOND) + record.ping(time_delay) /** * Adds an overmap object to the known contacts. diff --git a/code/modules/weather/weather_fsm_states.dm b/code/modules/weather/weather_fsm_states.dm index fb49554897e..5dbd7fd2bce 100644 --- a/code/modules/weather/weather_fsm_states.dm +++ b/code/modules/weather/weather_fsm_states.dm @@ -70,7 +70,7 @@ ABSTRACT_TYPE(/singleton/state/weather) if(!weather.show_weather(M)) weather.show_wind(M) - if(exposure != WEATHER_IGNORE && weather.set_cooldown(M)) + if(exposure != WEATHER_IGNORE) if(exposure == WEATHER_EXPOSED) handle_exposure_effects(M, weather) else if(exposure == WEATHER_ROOFED) diff --git a/code/modules/weather/weather_mob_tracking.dm b/code/modules/weather/weather_mob_tracking.dm index d0e4a9cd43b..47b634a926a 100644 --- a/code/modules/weather/weather_mob_tracking.dm +++ b/code/modules/weather/weather_mob_tracking.dm @@ -1,23 +1,9 @@ GLOBAL_LIST_EMPTY(current_mob_ambience) /obj/abstract/weather_system - // Weakref lists used to track mobs within our weather // system; alternative to keeping big lists of actual mobs or // having mobs constantly poked by weather systems. - var/tmp/list/mobs_on_cooldown = list() // Has this mob recently been messed with by the weather? var/tmp/list/mob_shown_weather = list() // Has this mob been sent the summary message about the current weather? var/tmp/list/mob_shown_wind = list() // Has this mob been sent the summary message about the current wind? - -// 'cooldown' in this context refers to weather effects like hail damage or being shown cosmetic messages. -/obj/abstract/weather_system/proc/set_cooldown(var/mob/living/M, var/delay = 5 SECONDS) - var/mobref = WEAKREF(M) - if(!(mobref in mobs_on_cooldown)) - mobs_on_cooldown[mobref] = TRUE - addtimer(CALLBACK(src, PROC_REF(clear_cooldown), mobref), delay) - return TRUE - return FALSE - -/obj/abstract/weather_system/proc/clear_cooldown(var/mobref) - mobs_on_cooldown -= mobref diff --git a/html/changelogs/johnwildkins-lol.yml b/html/changelogs/johnwildkins-lol.yml new file mode 100644 index 00000000000..70c98969a70 --- /dev/null +++ b/html/changelogs/johnwildkins-lol.yml @@ -0,0 +1,14 @@ +# Your name. +author: JohnWildkins + +# 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: + - refactor: "Ported object duplication handling from /tg/ to fix holodeck generating massive lag spikes." + - refactor: "Refactored weather, turrets, and overmap contacts to not use timers to reduce the number created + deleted over a round substantially."