Lag War 2: Revengeance (#21950)

<img width="750" height="449" alt="image"
src="https://github.com/user-attachments/assets/29c06609-eb62-42be-9b9c-334f9fe88636"
/>

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 <john.wildkins@gmail.com>
This commit is contained in:
Wildkins
2026-03-09 21:47:50 +00:00
committed by GitHub
parent 1beef3d476
commit afd8d9d8e0
14 changed files with 128 additions and 60 deletions
+1
View File
@@ -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"
+1 -1
View File
@@ -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)
+1 -2
View File
@@ -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)
+88
View File
@@ -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
-18
View File
@@ -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)
+5 -9
View File
@@ -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)
+2
View File
@@ -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)
+2 -1
View File
@@ -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)
@@ -103,3 +103,6 @@
/// If true, ignores weather effects
var/resists_weather = FALSE
/// Time since last weather effect
var/weather_cooldown_time = 0
+7 -10
View File
@@ -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)
@@ -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.
+1 -1
View File
@@ -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)
@@ -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
+14
View File
@@ -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."