Begin, the Lag Wars Have (#21839)

<img width="700" height="848" alt="image"
src="https://github.com/user-attachments/assets/e0db50af-35db-43c7-b78e-6c1ea5ac569c"
/>

you take a tiny little two year LOA and this happens
this PR hits some large lag culprits:
- UpdateOverlays() getting spammed by human/update_icon. This was 0.85%
of server CPU time during the event. Doesn't sound like a whole lot but
it's insane. 99% of it was caused by sloppy duplicate calls of
update_icon in set_dir (as well as update icon itself!). Refactored to
stop that
- Makes Follow menu update manually, which cuts down on the 2.8 million
REF() calls it made during the event. This was ~1% of CPU time including
ui_data as well.
- Gets rid of a bunch of random runtimes and some lighting-related
harddels.
- NanoUIs now clean themselves up when their owner qdels rather than
forcing the owner to do REF(src) and close all matching UIs. saves us
~0.1% server cpu time! wowza!
This commit is contained in:
Wildkins
2026-02-09 18:20:48 -05:00
committed by GitHub
parent 6ad39a46f7
commit 3d183d7262
19 changed files with 103 additions and 80 deletions
-4
View File
@@ -115,10 +115,6 @@
if (!isturf(src))
cleanup_events(src)
var/ui_key = REF(src)
if(LAZYISIN(SSnanoui.open_uis, ui_key))
SSnanoui.close_uis(src)
//BEGIN: ECS SHIT
var/list/dc = _datum_components
if(dc)
+1 -1
View File
@@ -155,7 +155,7 @@
take_damage(25)
return
else
return attackby(user, user)
return attackby(null, user)
/obj/machinery/door/window/emag_act(var/remaining_charges, var/mob/user)
if (density && operable())
+2 -5
View File
@@ -257,10 +257,7 @@
/obj/item/Destroy()
if(ismob(loc))
var/mob/m = loc
m.drop_from_inventory(src)
m.update_inv_r_hand()
m.update_inv_l_hand()
src.loc = null
m.drop_from_inventory(src, null)
if(!QDELETED(action))
QDEL_NULL(action) // /mob/living/proc/handle_actions() creates it, for ungodly reasons
@@ -930,7 +927,7 @@ GLOBAL_LIST_INIT(slot_flags_enumeration, list(
user.langchat_speech("holds up [src].", viewers, GLOB.all_languages, skip_language_check = TRUE, animation_style = LANGCHAT_FAST_POP, additional_styles = list("langchat_small", "emote"))
for (var/mob/M in viewers)
if(!user.is_invisible_to(M))
M.show_message("<b>[user]</b> holds up [icon2html(src, viewers)] [src]. <a href='byond://?src=[REF(M)];lookitem=[REF(src)]'>Take a closer look.</a>",1)
M.show_message("<b>[user]</b> holds up [icon2html(src, M)] [src]. <a href='byond://?src=[REF(M)];lookitem=[REF(src)]'>Take a closer look.</a>",1)
/mob/living/carbon/verb/showoff()
set name = "Show Held Item"
+3 -3
View File
@@ -132,13 +132,12 @@
set category = "Ghost"
set desc = "Follow and haunt a mob."
var/datum/tgui_module/follow_menu/GM = new /datum/tgui_module/follow_menu(usr)
GM.ui_interact(usr)
GLOB.follow_menu.ui_interact(src)
// This is the ghost's follow verb with an argument
/mob/abstract/ghost/proc/ManualFollow(var/atom/movable/target)
if(!target)
return
return FALSE
//Stops orbit if there's any; TG doesn't do this, but if you don't it breaks the orbiting reference
//if you are jumping from one mob to another, hence why we're doing it here
@@ -154,6 +153,7 @@
to_chat(src, SPAN_NOTICE("Now following \the <b>[target]</b>."))
update_sight()
return TRUE
/mob/abstract/ghost/proc/update_sight()
//if they are on a restricted level, then set the ghost vision for them.
@@ -109,9 +109,9 @@
var/datum/sprite_accessory/hair/hair = GLOB.hair_styles_list[h_style]
var/datum/sprite_accessory/facial_hair/facial = GLOB.facial_hair_styles_list[f_style]
if(!facial.keep_as_skeleton && f_style)
if(facial && !facial.keep_as_skeleton)
f_style = "Shaved"
if(!hair.keep_as_skeleton && h_style)
if(hair && !hair.keep_as_skeleton)
h_style = "Bald"
update_hair(0)
@@ -1466,7 +1466,7 @@
self = 1
if ((src.species.flags & NO_BLOOD) || (status_flags & FAKEDEATH))
to_chat(usr, SPAN_WARNING(self ? "You have no pulse." : "[src] has no pulse!"))
to_chat(usr, SPAN_WARNING("[self ? "You have" : "[src] has"] no pulse!"))
return
if(!self)
@@ -38,7 +38,6 @@
var/size_multiplier = 1 //multiplier for the mob's icon size
var/damage_multiplier = 1 //multiplies melee combat damage
var/icon_update = 1 //whether icon updating shall take place
var/default_lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE
@@ -122,9 +122,9 @@
/mob/living/carbon/human/set_dir(var/new_dir, ignore_facing_dir = FALSE)
. = ..()
if(. && tail_style)
update_tail_showing(1)
update_tail_showing(!lying)
if(lying)
update_icon(forceDirUpdate = TRUE)
update_icon(TRUE)
/mob/living/carbon/human/Move()
. = ..()
@@ -995,7 +995,9 @@
/datum/species/proc/handle_stance_damage(var/mob/living/carbon/human/H, var/damage_only = FALSE)
var/static/support_limbs = list(
BP_L_LEG = BP_R_LEG,
BP_L_FOOT = BP_R_FOOT
BP_L_FOOT = BP_R_FOOT,
BP_R_LEG = BP_L_LEG,
BP_R_FOOT = BP_L_FOOT
)
var/has_opposite_limb = FALSE
@@ -104,26 +104,29 @@ There are several things that need to be remembered:
var/list/overlays_raw[TOTAL_LAYERS] // Our set of "raw" overlays that can be modified, but cannot be directly applied to the mob without preprocessing.
var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed
#define UPDATE_ICON_IGNORE_DIRECTION_UPDATE -1
// Updates overlays from overlays_raw.
/mob/living/carbon/human/update_icon(var/forceDirUpdate = FALSE)
if (QDELETED(src))
return // No point.
var/update_lying = (lying_prev != lying) || forceDirUpdate || size_multiplier != 1
var/draw_specific_icon = (!lying || species.prone_icon)
if(update_lying && draw_specific_icon)
update_inv_l_hand(FALSE)
update_inv_r_hand(FALSE)
update_hud() //TODO: remove the need for this
ClearOverlays()
if(cloaked)
icon = 'icons/mob/human.dmi'
icon_state = "body_cloaked"
AddOverlays(list(overlays_raw[L_HAND_LAYER], overlays_raw[R_HAND_LAYER]))
else if (icon_update)
SetOverlays(list(overlays_raw[L_HAND_LAYER], overlays_raw[R_HAND_LAYER]))
else
var/list/ovr = list()
if (icon != stand_icon)
icon = stand_icon
var/list/ovr = list()
// We manually add each element instead of just using Copy() so that lists are appended instead of inserted.
for (var/item in overlays_raw)
if (item)
@@ -137,18 +140,20 @@ There are several things that need to be remembered:
var/icon/aura_overlay = icon(A.icon, icon_state = A.icon_state)
ovr += aura_overlay
AddOverlays(ovr)
if (((lying_prev != lying) || forceDirUpdate || size_multiplier != 1) && forceDirUpdate != UPDATE_ICON_IGNORE_DIRECTION_UPDATE)
if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone.
var/matrix/M = matrix()
SetOverlays(ovr)
if(update_lying) //Only rotate them if we're not drawing a specific icon for being prone.
var/matrix/M = matrix()
M.Scale(size_multiplier)
if(draw_specific_icon)
M.Translate(0, 16*(size_multiplier-1))
animate(src, transform = M, time = ANIM_LYING_TIME)
else
switch(src.dir)
if(SOUTH,EAST)
M.Turn(90)
else
M.Turn(-90)
M.Scale(size_multiplier)
M.Translate(1,-6)
animate(src, transform = M, time = (forceDirUpdate ? 0 : ANIM_LYING_TIME))
@@ -157,15 +162,6 @@ There are several things that need to be remembered:
if(istype(src.r_hand, /obj/item/gun) && lying)
HeldObjectDirTransform(slot_r_hand, src.dir)
else
update_inv_l_hand(FALSE)
update_inv_r_hand(FALSE)
update_icon(UPDATE_ICON_IGNORE_DIRECTION_UPDATE)
var/matrix/M = matrix()
M.Scale(size_multiplier)
M.Translate(0, 16*(size_multiplier-1))
animate(src, transform = M, time = ANIM_LYING_TIME)
UpdateOverlays()
lying_prev = lying
@@ -196,9 +192,7 @@ There are several things that need to be remembered:
animate(item_image, transform = item_transform)
overlays_raw[layer] = item_image
update_icon(UPDATE_ICON_IGNORE_DIRECTION_UPDATE)
#undef UPDATE_ICON_IGNORE_DIRECTION_UPDATE
update_icon()
//DAMAGE OVERLAYS
//constructs damage icon for each organ from mask * damage field and saves it in our overlays_raw list (as a list of icons).
@@ -1245,7 +1239,7 @@ There are several things that need to be remembered:
overlays_raw[L_HAND_LAYER] = null
if(update_icons)
update_icon(forceDirUpdate = TRUE)
update_icon(TRUE)
/mob/living/carbon/human/update_inv_r_hand(update_icons = TRUE)
if (QDELETED(src))
@@ -1284,7 +1278,7 @@ There are several things that need to be remembered:
overlays_raw[R_HAND_LAYER] = null
if(update_icons)
update_icon(forceDirUpdate = TRUE)
update_icon(TRUE)
/mob/living/carbon/human/update_inv_wrists(var/update_icons=1)
if (QDELETED(src))
@@ -1389,7 +1383,7 @@ There are several things that need to be remembered:
if(species.tail && !(mutations & HUSK) && !(mutations & SKELETON) && !(wear_suit && wear_suit.flags_inv & HIDETAIL))
var/icon/tail_s = get_tail_icon()
overlays_raw[tail_layer] = image(tail_s, icon_state = "[tail_style]_s")
animate_tail_reset()
animate_tail_reset(FALSE)
update_tail_accessory(FALSE)
if(update_icons)
@@ -1414,7 +1408,7 @@ There are several things that need to be remembered:
return tail_icon
/mob/living/carbon/human/proc/set_tail_state(var/mob_state)
/mob/living/carbon/human/proc/set_tail_state(var/mob_state, var/update = TRUE)
if(!tail_style)
return
@@ -1428,8 +1422,7 @@ There are several things that need to be remembered:
if(tail_overlay && species.tail_animation)
if(tail_overlay.icon_state != mob_state)
tail_overlay.icon_state = mob_state
update_tail_accessory()
update_icon()
update_tail_accessory(update)
return tail_overlay
return null
@@ -1461,11 +1454,11 @@ There are several things that need to be remembered:
/mob/living/carbon/human/proc/animate_tail_fast()
set_tail_state("[tail_style]_loop")
/mob/living/carbon/human/proc/animate_tail_reset()
/mob/living/carbon/human/proc/animate_tail_reset(var/update = TRUE)
if(stat != DEAD && !lying)
set_tail_state("[tail_style]_idle")
set_tail_state("[tail_style]_idle", update)
else
set_tail_state("[tail_style]_static")
set_tail_state("[tail_style]_static", update)
/mob/living/carbon/human/proc/animate_tail_stop(var/update_icons=1)
set_tail_state("[tail_style]_static")
+10 -2
View File
@@ -1,3 +1,5 @@
GLOBAL_DATUM_INIT(follow_menu, /datum/tgui_module/follow_menu, new)
/datum/tgui_module/follow_menu/ui_interact(var/mob/user, var/datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
@@ -14,9 +16,15 @@
return
if(action == "follow_target")
ghost.ManualFollow(locate(params["follow_target"]) in GLOB.mob_list)
if(!ghost.ManualFollow(locate(params["follow_target"]) in GLOB.mob_list))
to_chat(ghost, SPAN_WARNING("Failed to follow selected target. Refreshing mob list..."))
return TRUE
/datum/tgui_module/follow_menu/ui_data(mob/user)
if(action == "refresh")
ui.send_full_update()
return TRUE
/datum/tgui_module/follow_menu/ui_static_data(mob/user)
var/list/data = list()
// Don't worry about this is_mod check being for storytellers as well - all it does is highlight antags in red.
var/is_mod = check_rights(R_MOD|R_ADMIN, 0, user) || isstoryteller(user)
+1 -1
View File
@@ -519,7 +519,7 @@ nanoui is used to open and update nano browser uis
* @return nothing
*/
/datum/nanoui/process(update = 0)
if (!src_object || !user)
if (QDELETED(src_object) || QDELETED(user))
close()
return
+5 -6
View File
@@ -194,12 +194,11 @@
/obj/item/organ/external/hand/is_malfunctioning()
. = ..()
if(!.)
if(owner.is_mechanical())
var/actuator_type = limb_name == BP_L_HAND ? BP_ACTUATORS_LEFT : BP_ACTUATORS_RIGHT
var/obj/item/organ/internal/machine/actuators/actuator = owner.internal_organs_by_name[actuator_type]
if(!actuator || (actuator.status & ORGAN_DEAD))
return TRUE
if(!. && owner?.is_mechanical())
var/actuator_type = limb_name == BP_L_HAND ? BP_ACTUATORS_LEFT : BP_ACTUATORS_RIGHT
var/obj/item/organ/internal/machine/actuators/actuator = owner.internal_organs_by_name[actuator_type]
if(!actuator || (actuator.status & ORGAN_DEAD))
return TRUE
/obj/item/organ/external/hand/take_damage(brute, burn, damage_flags, used_weapon, list/forbidden_limbs, silent)
. = ..()
@@ -312,7 +312,7 @@
M.hallucination = max(M.hallucination, drug_strength)
if(prob(15))
to_chat(SPAN_GOOD(pick("The floor is melting...", "Everything is so much brighter! Wow!", "Everything is shifting around you.")))
to_chat(M, SPAN_GOOD(pick("The floor is melting...", "Everything is so much brighter! Wow!", "Everything is shifting around you.")))
/singleton/reagent/drugs/night_juice
+1
View File
@@ -170,6 +170,7 @@
"can_force" = shuttle.can_force(),
"can_rename_ship" = can_rename_ship,
"ship_name" = shuttle.name,
"current_location" = shuttle.get_location_name(),
)
/obj/machinery/computer/shuttle_control/ui_act(action, params)
+2 -4
View File
@@ -202,15 +202,13 @@
if(powered && cell?.charge < charge_use && !organic)
return FALSE
on = TRUE
set_light_range_power_color(initial(light_range))
set_light_on(on)
set_light(initial(light_range))
update_icon()
return TRUE
/obj/vehicle/proc/turn_off()
on = FALSE
set_light_range_power_color(0)
set_light_on(on)
set_light(0)
update_icon()
/obj/vehicle/emag_act(var/remaining_charges, mob/user as mob)
+24
View File
@@ -0,0 +1,24 @@
# Your name.
author: Wildkins
# 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: "Every single datum no longer does REF(src) on /Destroy to see if it has an open nanoui"
- bugfix: "Showing held items no longer causes every recording device to runtime."
- refactor: "Every obj/item/Destroy no longer updates the held mob's icon three times."
- refactor: "Follow menu is now a global UI and should no longer DDOS the server when 50 people have it open"
- refactor: "Follow menu no longer auto-updates the list of mobs while open, use the refresh button."
- bugfix: "Fix runtime when a /human without hair or facial hair would be skeletonized"
- refactor: "Cleaned up human icon update code. Lying / direction changes no longer cause four update_icon calls. Tailed species turning no longer causes six."
- bugfix: "Fix runtime caused by detached / ownerless mechanical hands."
- bugfix: "Fix colorspace runtime."
- bugfix: "Fix lighting harddel caused by using improper handlers for static lights."
- bugfix: "Lifts now grey out the current location, instead of throwing a runtime when you click it."
- bugfix: "Fix runtime when a bloodless/fakedeath mob has their pulse taken."
+14 -11
View File
@@ -30,17 +30,20 @@ export const FollowMenu = (props, context) => {
<Section
title="Follow Menu"
buttons={
<Input
autoFocus
autoSelect
placeholder="Search by name"
width="40vw"
maxLength={512}
onInput={(e, value) => {
setSearchTerm(value);
}}
value={searchTerm}
/>
<>
<Input
autoFocus
autoSelect
placeholder="Search by name"
width="40vw"
maxLength={512}
onInput={(e, value) => {
setSearchTerm(value);
}}
value={searchTerm}
/>
<Button content="Refresh" onClick={() => act('refresh')} />
</>
}
>
{data.categories.sort().map((category) => (
@@ -14,6 +14,7 @@ export type ShuttleControlConsoleMultiLiftData = {
can_pick: boolean;
destination_name: string;
destinations: string[];
current_location: string;
};
export const ShuttleControlConsoleMultiLift = (props, context) => {
@@ -30,7 +31,9 @@ export const ShuttleControlConsoleMultiLift = (props, context) => {
<Button
content="Go To"
icon="arrow-right-to-bracket"
disabled={!data.can_pick}
disabled={
!data.can_pick || data.current_location === destination
}
onClick={() => act('pick', { destination: destination })}
/>
</LabeledList.Item>