From 85b2d5043dbc9eb277bf57dd6dc5147ae08fe978 Mon Sep 17 00:00:00 2001
From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
Date: Sun, 30 Oct 2022 00:09:15 -0700
Subject: [PATCH] Optimizes qdel related things (slight init time savings)
(#70729)
* Moves spawners and decals to a different init/delete scheme
Rather then fully creating and then immediately deleting these things,
we instead do the bare minimum.
This is faster, if in theory more fragile. We should be safe since any
errors should be caught in compile since this is very close to a
"static" action. It does mean these atoms cannot use signals, etc.
* Potentially saves init time, mostly cleans up a silly pattern
We use sleeps and INVOKE_ASYNC to ensure that handing back turfs doesn't
block a space reservation, but this by nature consumes up to the
threshold and a bit more of whatever working block we were in.
This is silly. Should just be a subsystem, so I made it one, with
support for awaiting its finish if you want to
* Optimizes garbage/proc/Queue slightly
Queue takes about 1.6 seconds to process 26k items right now.
The MASSIVE majority of this time is spent on using \ref
This is because \ref returns a string, and that string requires being
inserted into the global cache of strings we store
What I'm doing is caching the result of ANY \ref on the datum it's
applied to. This ensures previous uses will never decay from the string
tree.
This saves about 0.2 seconds of init
---
code/__DEFINES/_helpers.dm | 7 +++
code/__DEFINES/typeids.dm | 2 +-
code/__HELPERS/icons.dm | 6 +--
code/__HELPERS/jatum.dm | 2 +-
code/__HELPERS/ref.dm | 4 +-
code/controllers/subsystem/garbage.dm | 8 ++--
code/controllers/subsystem/mapping.dm | 48 ++++++++++++++-----
code/controllers/subsystem/statpanel.dm | 12 ++---
code/controllers/subsystem/timer.dm | 4 +-
code/controllers/subsystem/vis_overlays.dm | 2 +-
code/datums/datum.dm | 5 ++
code/datums/position_point_vector.dm | 14 +++---
code/game/gamemodes/dynamic/dynamic.dm | 20 ++++----
code/game/objects/effects/decals/decal.dm | 15 ++++--
code/game/objects/effects/misc.dm | 17 +++++++
.../objects/effects/spawners/bombspawner.dm | 9 ++--
code/game/objects/effects/spawners/costume.dm | 3 +-
.../effects/spawners/random/entertainment.dm | 3 +-
.../effects/spawners/random/maintenance.dm | 3 +-
.../objects/effects/spawners/random/random.dm | 11 +----
.../objects/effects/spawners/structure.dm | 4 --
.../effects/spawners/xeno_egg_delivery.dm | 3 +-
.../objects/items/AI_modules/_AI_modules.dm | 5 +-
code/game/objects/items/stacks/stack.dm | 2 +-
code/modules/admin/admin.dm | 2 +-
code/modules/admin/sound_emitter.dm | 12 ++---
code/modules/admin/verbs/lua/lua_editor.dm | 12 ++---
code/modules/admin/verbs/lua/lua_state.dm | 4 +-
.../view_variables/reference_tracking.dm | 10 ++--
code/modules/asset_cache/assets/vending.dm | 6 +--
code/modules/events/holiday/xmas.dm | 4 +-
code/modules/language/language_holder.dm | 2 +-
.../space_management/space_reservation.dm | 10 ++--
33 files changed, 157 insertions(+), 114 deletions(-)
diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm
index 574c4da8dc7..08d4b39906e 100644
--- a/code/__DEFINES/_helpers.dm
+++ b/code/__DEFINES/_helpers.dm
@@ -13,3 +13,10 @@
/// subtypesof(), typesof() without the parent path
#define subtypesof(typepath) ( typesof(typepath) - typepath )
+
+/// Takes a datum as input, returns its ref string, or a cached version of it
+/// This allows us to cache \ref creation, which ensures it'll only ever happen once per datum, saving string tree time
+/// It is slightly less optimal then a []'d datum, but the cost is massively outweighed by the potential savings
+/// It will only work for datums mind, for datum reasons
+/// : because of the embedded typecheck
+#define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]"))
diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm
index 275f7719f07..7c69dd29de3 100644
--- a/code/__DEFINES/typeids.dm
+++ b/code/__DEFINES/typeids.dm
@@ -3,6 +3,6 @@
#define TYPEID_NORMAL_LIST "f"
//helper macros
#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, -7) ) )
-#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST)
+#define IS_NORMAL_LIST(L) (GET_TYPEID(text_ref(L)) == TYPEID_NORMAL_LIST)
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 37c7b753c87..b87f82b77fa 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -1181,7 +1181,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
if(isicon(icon) && isfile(icon))
//icons compiled in from 'icons/path/to/dmi_file.dmi' at compile time are weird and arent really /icon objects,
///but they pass both isicon() and isfile() checks. theyre the easiest case since stringifying them gives us the path we want
- var/icon_ref = "\ref[icon]"
+ var/icon_ref = text_ref(icon)
var/locate_icon_string = "[locate(icon_ref)]"
icon_path = locate_icon_string
@@ -1192,7 +1192,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
// the rsc reference returned by fcopy_rsc() will be stringifiable to "icons/path/to/dmi_file.dmi"
var/rsc_ref = fcopy_rsc(icon)
- var/icon_ref = "\ref[rsc_ref]"
+ var/icon_ref = text_ref(rsc_ref)
var/icon_path_string = "[locate(icon_ref)]"
@@ -1202,7 +1202,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/rsc_ref = fcopy_rsc(icon)
//if its the text path of an existing dmi file, the rsc reference returned by fcopy_rsc() will be stringifiable to a dmi path
- var/rsc_ref_ref = "\ref[rsc_ref]"
+ var/rsc_ref_ref = text_ref(rsc_ref)
var/rsc_ref_string = "[locate(rsc_ref_ref)]"
icon_path = rsc_ref_string
diff --git a/code/__HELPERS/jatum.dm b/code/__HELPERS/jatum.dm
index d00e6a68642..f11e369007f 100644
--- a/code/__HELPERS/jatum.dm
+++ b/code/__HELPERS/jatum.dm
@@ -42,7 +42,7 @@
"path" = value
)
- var/ref = "\ref[value]"
+ var/ref = text_ref(value)
var/existing_ref = seen_references[ref]
if(existing_ref)
return list(
diff --git a/code/__HELPERS/ref.dm b/code/__HELPERS/ref.dm
index de1ba392b43..365df03dc88 100644
--- a/code/__HELPERS/ref.dm
+++ b/code/__HELPERS/ref.dm
@@ -1,7 +1,7 @@
/**
* \ref behaviour got changed in 512 so this is necesary to replicate old behaviour.
* If it ever becomes necesary to get a more performant REF(), this lies here in wait
- * #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : "\ref[thing]")
+ * #define REF(thing) (thing && isdatum(thing) && (thing:datum_flags & DF_USE_TAG) && thing:tag ? "[thing:tag]" : text_ref(thing))
**/
/proc/REF(input)
if(isdatum(input))
@@ -12,4 +12,4 @@
thing.datum_flags &= ~DF_USE_TAG
else
return "\[[url_encode(thing.tag)]\]"
- return "\ref[input]"
+ return text_ref(input)
diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm
index 3d365d9951d..65c1f5d083d 100644
--- a/code/controllers/subsystem/garbage.dm
+++ b/code/controllers/subsystem/garbage.dm
@@ -207,7 +207,7 @@ SUBSYSTEM_DEF(garbage)
var/type = D.type
var/datum/qdel_item/I = items[type]
- log_world("## TESTING: GC: -- \ref[D] | [type] was unable to be GC'd --")
+ log_world("## TESTING: GC: -- [text_ref(D)] | [type] was unable to be GC'd --")
#ifdef TESTING
for(var/c in GLOB.admins) //Using testing() here would fill the logs with ADMIN_VV garbage
var/client/admin = c
@@ -249,7 +249,7 @@ SUBSYSTEM_DEF(garbage)
HardDelete(D)
return
var/gctime = world.time
- var/refid = "\ref[D]"
+ var/refid = text_ref(D)
D.gc_destroyed = gctime
var/list/queue = queues[level]
@@ -261,7 +261,7 @@ SUBSYSTEM_DEF(garbage)
++delslasttick
++totaldels
var/type = D.type
- var/refID = "\ref[D]"
+ var/refID = text_ref(D)
var/tick_usage = TICK_USAGE
del(D)
@@ -376,7 +376,7 @@ SUBSYSTEM_DEF(garbage)
D.find_references() //This breaks ci. Consider it insurance against somehow pring reftracking on accident
if (QDEL_HINT_IFFAIL_FINDREFERENCE) //qdel will, if REFERENCE_TRACKING is enabled and the object fails to collect, display all references to this object.
SSgarbage.Queue(D)
- SSgarbage.reference_find_on_fail["\ref[D]"] = TRUE
+ SSgarbage.reference_find_on_fail[text_ref(D)] = TRUE
#endif
else
#ifdef TESTING
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index c3be6142a1e..fe39a4e0102 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(mapping)
name = "Mapping"
init_order = INIT_ORDER_MAPPING
- flags = SS_NO_FIRE
+ runlevels = ALL
var/list/nuke_tiles = list()
var/list/nuke_threats = list()
@@ -54,6 +54,8 @@ SUBSYSTEM_DEF(mapping)
var/list/turf/unused_turfs = list() //Not actually unused turfs they're unused but reserved for use for whatever requests them. "[zlevel_of_turf]" = list(turfs)
var/list/datum/turf_reservations //list of turf reservations
var/list/used_turfs = list() //list of turf = datum/turf_reservation
+ /// List of lists of turfs to reserve
+ var/list/lists_to_reserve = list()
var/list/reservation_ready = list()
var/clearing_reserved_turfs = FALSE
@@ -149,6 +151,33 @@ SUBSYSTEM_DEF(mapping)
return SS_INIT_SUCCESS
+/datum/controller/subsystem/mapping/fire(resumed)
+ // Cache for sonic speed
+ var/list/unused_turfs = src.unused_turfs
+ var/list/world_contents = GLOB.areas_by_type[world.area].contents
+ var/list/lists_to_reserve = src.lists_to_reserve
+ var/index = 0
+ while(length(lists_to_reserve))
+ var/list/packet = lists_to_reserve[index + 1]
+ var/packetlen = length(packet)
+ while(packetlen)
+ if(MC_TICK_CHECK)
+ lists_to_reserve.Cut(1, index)
+ return
+ var/turf/T = packet[packetlen]
+ T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
+ LAZYINITLIST(unused_turfs["[T.z]"])
+ unused_turfs["[T.z]"] |= T
+ T.flags_1 |= UNUSED_RESERVATION_TURF
+ world_contents += T
+ packet.len--
+ packetlen = length(packet)
+
+ index++
+ // If we're here, we're done with that lad
+ lists_to_reserve.len--
+ lists_to_reserve.Cut(1, index)
+
/datum/controller/subsystem/mapping/proc/calculate_default_z_level_gravities()
for(var/z_level in 1 to length(z_list))
calculate_z_level_gravity(z_level)
@@ -646,15 +675,12 @@ GLOBAL_LIST_EMPTY(the_station_areas)
reservation_ready["[z]"] = TRUE
clearing_reserved_turfs = FALSE
-/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs)
- for(var/i in turfs)
- var/turf/T = i
- T.empty(RESERVED_TURF_TYPE, RESERVED_TURF_TYPE, null, TRUE)
- LAZYINITLIST(unused_turfs["[T.z]"])
- unused_turfs["[T.z]"] |= T
- T.flags_1 |= UNUSED_RESERVATION_TURF
- GLOB.areas_by_type[world.area].contents += T
- CHECK_TICK
+/// Schedules a group of turfs to be handed back to the reservation system's control
+/// If await is true, will sleep until the turfs are finished work
+/datum/controller/subsystem/mapping/proc/reserve_turfs(list/turfs, await = FALSE)
+ lists_to_reserve += list(turfs)
+ if(await)
+ UNTIL(!length(turfs))
//DO NOT CALL THIS PROC DIRECTLY, CALL wipe_reservations().
/datum/controller/subsystem/mapping/proc/do_wipe_turf_reservations()
@@ -672,7 +698,7 @@ GLOBAL_LIST_EMPTY(the_station_areas)
clearing |= used_turfs //used turfs is an associative list, BUT, reserve_turfs() can still handle it. If the code above works properly, this won't even be needed as the turfs would be freed already.
unused_turfs.Cut()
used_turfs.Cut()
- reserve_turfs(clearing)
+ reserve_turfs(clearing, await = TRUE)
///Initialize all biomes, assoc as type || instance
/datum/controller/subsystem/mapping/proc/initialize_biomes()
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index 816c7943e4b..9ebaf85a63d 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -237,16 +237,16 @@ SUBSYSTEM_DEF(statpanels)
list("CPU:", world.cpu),
list("Instances:", "[num2text(world.contents.len, 10)]"),
list("World Time:", "[world.time]"),
- list("Globals:", GLOB.stat_entry(), "\ref[GLOB]"),
- list("[config]:", config.stat_entry(), "\ref[config]"),
+ list("Globals:", GLOB.stat_entry(), text_ref(GLOB)),
+ list("[config]:", config.stat_entry(), text_ref(config)),
list("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
- list("Master Controller:", Master.stat_entry(), "\ref[Master]"),
- list("Failsafe Controller:", Failsafe.stat_entry(), "\ref[Failsafe]"),
+ list("Master Controller:", Master.stat_entry(), text_ref(Master)),
+ list("Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)),
list("","")
)
for(var/datum/controller/subsystem/sub_system as anything in Master.subsystems)
- mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), "\ref[sub_system]")
- mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", "\ref[GLOB.cameranet]")
+ mc_data[++mc_data.len] = list("\[[sub_system.state_letter()]][sub_system.name]", sub_system.stat_entry(), text_ref(sub_system))
+ mc_data[++mc_data.len] = list("Camera Net", "Cameras: [GLOB.cameranet.cameras.len] | Chunks: [GLOB.cameranet.chunks.len]", text_ref(GLOB.cameranet))
///immediately update the active statpanel tab of the target client
/datum/controller/subsystem/statpanels/proc/immediate_send_stat_data(client/target)
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index 451793f5b9e..7efd2680c04 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -505,8 +505,8 @@ SUBSYSTEM_DEF(timer)
/datum/timedevent/proc/bucketJoin()
// Generate debug-friendly name for timer
var/static/list/bitfield_flags = list("TIMER_UNIQUE", "TIMER_OVERRIDE", "TIMER_CLIENT_TIME", "TIMER_STOPPABLE", "TIMER_NO_HASH_WAIT", "TIMER_LOOP")
- name = "Timer: [id] (\ref[src]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield_to_list(flags, bitfield_flags), ", ")], \
- callBack: \ref[callBack], callBack.object: [callBack.object]\ref[callBack.object]([getcallingtype()]), \
+ name = "Timer: [id] ([text_ref(src)]), TTR: [timeToRun], wait:[wait] Flags: [jointext(bitfield_to_list(flags, bitfield_flags), ", ")], \
+ callBack: [text_ref(callBack)], callBack.object: [callBack.object][text_ref(callBack.object)]([getcallingtype()]), \
callBack.delegate:[callBack.delegate]([callBack.arguments ? callBack.arguments.Join(", ") : ""]), source: [source]"
if (bucket_joined)
diff --git a/code/controllers/subsystem/vis_overlays.dm b/code/controllers/subsystem/vis_overlays.dm
index ddda44bbbbb..1afe1c62100 100644
--- a/code/controllers/subsystem/vis_overlays.dm
+++ b/code/controllers/subsystem/vis_overlays.dm
@@ -42,7 +42,7 @@ SUBSYSTEM_DEF(vis_overlays)
else
overlay = _create_new_vis_overlay(icon, iconstate, layer, plane, dir, alpha, add_appearance_flags)
overlay.cache_expiration = -1
- var/cache_id = "\ref[overlay]@{[world.time]}"
+ var/cache_id = "[text_ref(overlay)]@{[world.time]}"
vis_overlay_cache[cache_id] = overlay
. = overlay
thing.vis_contents += overlay
diff --git a/code/datums/datum.dm b/code/datums/datum.dm
index 8cc88b9dc53..12b9d3d9655 100644
--- a/code/datums/datum.dm
+++ b/code/datums/datum.dm
@@ -40,6 +40,11 @@
/// Datum level flags
var/datum_flags = NONE
+ /// A cached version of our \ref
+ /// The brunt of \ref costs are in creating entries in the string tree (a tree of immutable strings)
+ /// This avoids doing that more then once per datum by ensuring ref strings always have a reference to them after they're first pulled
+ var/cached_ref
+
/// A weak reference to another datum
var/datum/weakref/weak_reference
diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm
index 0438d514195..f02183eab90 100644
--- a/code/datums/position_point_vector.dm
+++ b/code/datums/position_point_vector.dm
@@ -23,7 +23,7 @@
return ATAN2((b.y - a.y), (b.x - a.x))
/// For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
-/datum/position
+/datum/position
var/x = 0
var/y = 0
var/z = 0
@@ -68,7 +68,7 @@
return new /datum/point(src)
/// A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
-/datum/point
+/datum/point
var/x = 0
var/y = 0
var/z = 0
@@ -83,7 +83,7 @@
return p
/// First argument can also be a /datum/position or /atom.
-/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0)
+/datum/point/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0)
if(istype(_x, /datum/position))
var/datum/position/P = _x
_x = P.x
@@ -110,7 +110,7 @@
/datum/point/proc/debug_out()
var/turf/T = return_turf()
- return "\ref[src] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
+ return "[text_ref(src)] aX [x] aY [y] aZ [z] pX [return_px()] pY [return_py()] mX [T.x] mY [T.y] mZ [T.z]"
/datum/point/proc/move_atom_to_src(atom/movable/AM)
AM.forceMove(return_turf())
@@ -134,11 +134,11 @@
/datum/point/vector
/// Pixels per iteration
- var/speed = 32
+ var/speed = 32
var/iteration = 0
var/angle = 0
/// Calculated x movement amounts to prevent having to do trig every step.
- var/mpx = 0
+ var/mpx = 0
/// Calculated y movement amounts to prevent having to do trig every step.
var/mpy = 0
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
@@ -158,7 +158,7 @@
starting_z = z
/// Same effect as initiliaze_location, but without setting the starting_x/y/z
-/datum/point/vector/proc/set_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
+/datum/point/vector/proc/set_location(tile_x, tile_y, tile_z, p_x = 0, p_y = 0)
if(!isnull(tile_x))
x = ((tile_x - 1) * world.icon_size) + world.icon_size * 0.5 + p_x + 1
if(!isnull(tile_y))
diff --git a/code/game/gamemodes/dynamic/dynamic.dm b/code/game/gamemodes/dynamic/dynamic.dm
index 62fa9f467df..63be0696780 100644
--- a/code/game/gamemodes/dynamic/dynamic.dm
+++ b/code/game/gamemodes/dynamic/dynamic.dm
@@ -191,23 +191,23 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
/datum/game_mode/dynamic/admin_panel()
var/list/dat = list("
Game Mode PanelGame Mode Panel
")
- dat += "Dynamic Mode \[VV\] \[Refresh\]
"
+ dat += "Dynamic Mode \[VV\] \[Refresh\]
"
dat += "Threat Level: [threat_level]
"
dat += "Budgets (Roundstart/Midrounds): [initial_round_start_budget]/[threat_level - initial_round_start_budget]
"
- dat += "Midround budget to spend: [mid_round_budget] \[Adjust\] \[View Log\]
"
+ dat += "Midround budget to spend: [mid_round_budget] \[Adjust\] \[View Log\]
"
dat += "
"
dat += "Parameters: centre = [threat_curve_centre] ; width = [threat_curve_width].
"
dat += "Split parameters: centre = [roundstart_split_curve_centre] ; width = [roundstart_split_curve_width].
"
dat += "On average, [peaceful_percentage]% of the rounds are more peaceful.
"
- dat += "Forced extended: [GLOB.dynamic_forced_extended ? "On" : "Off"]
"
- dat += "No stacking (only one round-ender): [GLOB.dynamic_no_stacking ? "On" : "Off"]
"
- dat += "Stacking limit: [GLOB.dynamic_stacking_limit] \[Adjust\]"
+ dat += "Forced extended: [GLOB.dynamic_forced_extended ? "On" : "Off"]
"
+ dat += "No stacking (only one round-ender): [GLOB.dynamic_no_stacking ? "On" : "Off"]
"
+ dat += "Stacking limit: [GLOB.dynamic_stacking_limit] \[Adjust\]"
dat += "
"
- dat += "\[Force Next Latejoin Ruleset\]
"
+ dat += "\[Force Next Latejoin Ruleset\]
"
if (forced_latejoin_rule)
- dat += {"-> [forced_latejoin_rule.name] <-
"}
- dat += "\[Execute Midround Ruleset\]
"
+ dat += {"-> [forced_latejoin_rule.name] <-
"}
+ dat += "\[Execute Midround Ruleset\]
"
dat += "
"
dat += "Executed rulesets: "
if (executed_rules.len > 0)
@@ -217,13 +217,13 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
else
dat += "none.
"
dat += "
Injection Timers: ([get_heavy_midround_injection_chance(dry_run = TRUE)]% heavy midround chance)
"
- dat += "Latejoin: [DisplayTimeText(latejoin_injection_cooldown-world.time)] \[Now!\]
"
+ dat += "Latejoin: [DisplayTimeText(latejoin_injection_cooldown-world.time)] \[Now!\]
"
var/next_injection = next_midround_injection()
if (next_injection == INFINITY)
dat += "All midrounds have been exhausted."
else
- dat += "Midround: [DisplayTimeText(next_injection - world.time)] \[Now!\]
"
+ dat += "Midround: [DisplayTimeText(next_injection - world.time)] \[Now!\]
"
usr << browse(dat.Join(), "window=gamemode_panel;size=500x500")
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 629e72ad9d7..a4cf5dfa465 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -37,20 +37,29 @@
layer = TURF_DECAL_LAYER
anchored = TRUE
+// This is with the intent of optimizing mapload
+// See spawners for more details since we use the same pattern
+// Basically rather then creating and deleting ourselves, why not just do the bare minimum?
/obj/effect/turf_decal/Initialize(mapload)
- . = ..()
+ SHOULD_CALL_PARENT(FALSE)
+ if(flags_1 & INITIALIZED_1)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ flags_1 |= INITIALIZED_1
+
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddElement(/datum/element/decal, icon, icon_state, dir, null, null, alpha, color, null, FALSE, null)
return INITIALIZE_HINT_QDEL
+/obj/effect/turf_decal/Destroy(force)
+ SHOULD_CALL_PARENT(FALSE)
#ifdef UNIT_TESTS
// If we don't do this, turf decals will end up stacking up on a tile, and break the overlay limit
// I hate it too bestie
-/obj/effect/turf_decal/Destroy()
if(GLOB.running_create_and_destroy)
var/turf/T = loc
T.RemoveElement(/datum/element/decal, icon, icon_state, dir, null, null, alpha, color, null, FALSE, null)
- return ..()
#endif
+ moveToNullspace()
+ return QDEL_HINT_QUEUE
diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm
index 6be05751ce0..cbd96e74ff9 100644
--- a/code/game/objects/effects/misc.dm
+++ b/code/game/objects/effects/misc.dm
@@ -21,6 +21,23 @@
/obj/effect/spawner
name = "object spawner"
+// Brief explanation:
+// Rather then setting up and then deleting spawners, we block all atomlike setup
+// and do the absolute bare minimum
+// This is with the intent of optimizing mapload
+/obj/effect/spawner/Initialize(mapload)
+ SHOULD_CALL_PARENT(FALSE)
+ if(flags_1 & INITIALIZED_1)
+ stack_trace("Warning: [src]([type]) initialized multiple times!")
+ flags_1 |= INITIALIZED_1
+
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/spawner/Destroy(force)
+ SHOULD_CALL_PARENT(FALSE)
+ moveToNullspace()
+ return QDEL_HINT_QUEUE
+
/obj/effect/list_container
name = "list container"
diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm
index 64fc60062e6..9d1da48cd29 100644
--- a/code/game/objects/effects/spawners/bombspawner.dm
+++ b/code/game/objects/effects/spawners/bombspawner.dm
@@ -6,16 +6,16 @@
name = "bomb"
icon = 'icons/hud/screen_gen.dmi'
icon_state = "x"
- /* Gasmixes for tank_one and tank_two of the ttv respectively.
+ /* Gasmixes for tank_one and tank_two of the ttv respectively.
* Populated on /obj/effect/spawner/newbomb/Initialize, depopulated right after by the children procs.
*/
var/datum/gas_mixture/first_gasmix
var/datum/gas_mixture/second_gasmix
-/**
+/**
* The part of code that actually spawns the bomb. Always call the parent's initialize first for subtypes of these.
*
- * Arguments:
+ * Arguments:
* * assembly - An assembly typepath to add to the ttv.
*/
/obj/effect/spawner/newbomb/Initialize(mapload, assembly = null)
@@ -33,7 +33,6 @@
newassembly.on_attach()
newassembly.holder = ttv
ttv.update_appearance()
- return INITIALIZE_HINT_QDEL
/obj/effect/spawner/newbomb/proc/calculate_pressure(datum/gas_mixture/gasmix, pressure)
return pressure * gasmix.volume/(R_IDEAL_GAS_EQUATION*gasmix.temperature)
@@ -85,7 +84,7 @@
first_gasmix.assert_gas(/datum/gas/hypernoblium)
first_gasmix.assert_gas(/datum/gas/tritium)
second_gasmix.assert_gas(/datum/gas/oxygen)
-
+
first_gasmix.gases[/datum/gas/hypernoblium][MOLES] = REACTION_OPPRESSION_THRESHOLD - 0.01
first_gasmix.gases[/datum/gas/tritium][MOLES] = 0.5 * calculate_pressure(first_gasmix, TANK_LEAK_PRESSURE - 1)
second_gasmix.gases[/datum/gas/oxygen][MOLES] = calculate_pressure(second_gasmix, TANK_LEAK_PRESSURE-1)
diff --git a/code/game/objects/effects/spawners/costume.dm b/code/game/objects/effects/spawners/costume.dm
index 187004aba39..a32a2c33f5b 100644
--- a/code/game/objects/effects/spawners/costume.dm
+++ b/code/game/objects/effects/spawners/costume.dm
@@ -7,11 +7,10 @@
var/list/items
/obj/effect/spawner/costume/Initialize(mapload)
- ..()
+ . = ..()
if(items?.len)
for(var/path in items)
new path(loc)
- return INITIALIZE_HINT_QDEL
/obj/effect/spawner/costume/chicken
name = "chicken costume spawner"
diff --git a/code/game/objects/effects/spawners/random/entertainment.dm b/code/game/objects/effects/spawners/random/entertainment.dm
index 22d5f681edf..d7378314636 100644
--- a/code/game/objects/effects/spawners/random/entertainment.dm
+++ b/code/game/objects/effects/spawners/random/entertainment.dm
@@ -238,8 +238,7 @@
/obj/effect/spawner/random/entertainment/toy/Initialize(mapload)
loot += GLOB.arcade_prize_pool
- . = ..()
- return INITIALIZE_HINT_QDEL
+ return ..()
/obj/effect/spawner/random/entertainment/plushie
name = "plushie spawner"
diff --git a/code/game/objects/effects/spawners/random/maintenance.dm b/code/game/objects/effects/spawners/random/maintenance.dm
index 8652bc3c5d0..5481123bbff 100644
--- a/code/game/objects/effects/spawners/random/maintenance.dm
+++ b/code/game/objects/effects/spawners/random/maintenance.dm
@@ -10,8 +10,7 @@
/obj/effect/spawner/random/maintenance/Initialize(mapload)
loot = GLOB.maintenance_loot
-
- . = ..()
+ return ..()
/obj/effect/spawner/random/maintenance/proc/hide()
invisibility = INVISIBILITY_OBSERVER
diff --git a/code/game/objects/effects/spawners/random/random.dm b/code/game/objects/effects/spawners/random/random.dm
index 5cf8ba0506a..ece6656b4fa 100644
--- a/code/game/objects/effects/spawners/random/random.dm
+++ b/code/game/objects/effects/spawners/random/random.dm
@@ -13,8 +13,6 @@
var/loot_type_path
/// The subtypes (this excludes the provided path) to combine with the loot list
var/loot_subtype_path
- /// Whether the spawner should immediately spawn loot and cleanup on Initialize()
- var/spawn_on_init = TRUE
/// How many items will be spawned
var/spawn_loot_count = 1
/// If the same item can be spawned twice
@@ -32,14 +30,7 @@
/obj/effect/spawner/random/Initialize(mapload)
. = ..()
-
- if(should_spawn_on_init())
- spawn_loot()
- return INITIALIZE_HINT_QDEL
-
-/// Helper proc that returns TRUE if the spawner should spawn loot in Initialise() and FALSE otherwise. Override this to change spawning behaviour.
-/obj/effect/spawner/random/proc/should_spawn_on_init()
- return spawn_on_init
+ spawn_loot()
///If the spawner has any loot defined, randomly picks some and spawns it. Does not cleanup the spawner.
/obj/effect/spawner/random/proc/spawn_loot(lootcount_override)
diff --git a/code/game/objects/effects/spawners/structure.dm b/code/game/objects/effects/spawners/structure.dm
index 86b4a5868ae..a523cd00720 100644
--- a/code/game/objects/effects/spawners/structure.dm
+++ b/code/game/objects/effects/spawners/structure.dm
@@ -10,13 +10,9 @@ again.
/obj/effect/spawner/structure/Initialize(mapload)
. = ..()
-
for(var/spawn_type in spawn_list)
new spawn_type(loc)
- return INITIALIZE_HINT_QDEL
-
-
//normal windows
/obj/effect/spawner/structure/window
diff --git a/code/game/objects/effects/spawners/xeno_egg_delivery.dm b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
index 75d5a26612d..395d1454e0d 100644
--- a/code/game/objects/effects/spawners/xeno_egg_delivery.dm
+++ b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
@@ -5,7 +5,7 @@
var/announcement_time = 1200
/obj/effect/spawner/xeno_egg_delivery/Initialize(mapload)
- ..()
+ . = ..()
var/turf/T = get_turf(src)
new /obj/structure/alien/egg(T)
@@ -16,4 +16,3 @@
log_game("An alien egg has been delivered to [AREACOORD(T)]")
var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(T, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
- return INITIALIZE_HINT_QDEL
diff --git a/code/game/objects/items/AI_modules/_AI_modules.dm b/code/game/objects/items/AI_modules/_AI_modules.dm
index 346b3c12e45..dccef7514fc 100644
--- a/code/game/objects/items/AI_modules/_AI_modules.dm
+++ b/code/game/objects/items/AI_modules/_AI_modules.dm
@@ -167,17 +167,16 @@
color = "#00FF00"
/obj/effect/spawner/round_default_module/Initialize(mapload)
- ..()
+ . = ..()
var/datum/ai_laws/default_laws = get_round_default_lawset()
//try to spawn a law board, since they may have special functionality (asimov setting subjects)
for(var/obj/item/ai_module/core/full/potential_lawboard as anything in subtypesof(/obj/item/ai_module/core/full))
if(initial(potential_lawboard.law_id) != initial(default_laws.id))
continue
potential_lawboard = new potential_lawboard(loc)
- return INITIALIZE_HINT_QDEL
+ return
//spawn the fallback instead
new /obj/item/ai_module/core/round_default_fallback(loc)
- return INITIALIZE_HINT_QDEL
///When the default lawset spawner cannot find a module object to spawn, it will spawn this, and this sets itself to the round default.
///This is so /datum/lawsets can be picked even if they have no module for themselves.
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 985fbfb19c5..45a7cc5ea0d 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -232,7 +232,7 @@
"res_amount" = R.res_amount,
"max_res_amount" = R.max_res_amount,
"req_amount" = R.req_amount,
- "ref" = "\ref[R]",
+ "ref" = text_ref(R),
)
/**
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 2af98275724..fdaaa615a17 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -24,7 +24,7 @@
dat += "(Force Roundstart Rulesets)
"
if (GLOB.dynamic_forced_roundstart_ruleset.len > 0)
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
- dat += {"-> [rule.name] <-
"}
+ dat += {"-> [rule.name] <-
"}
dat += "(Clear Rulesets)
"
dat += "(Dynamic mode options)
"
dat += "
"
diff --git a/code/modules/admin/sound_emitter.dm b/code/modules/admin/sound_emitter.dm
index 20de4bfcf58..c96253381b9 100644
--- a/code/modules/admin/sound_emitter.dm
+++ b/code/modules/admin/sound_emitter.dm
@@ -58,16 +58,16 @@
/obj/effect/sound_emitter/proc/edit_emitter(mob/user)
var/dat = ""
- dat += "Label: [maptext ? maptext : "No label set!"]
"
+ dat += "Label: [maptext ? maptext : "No label set!"]
"
dat += "
"
- dat += "Sound File: [sound_file ? sound_file : "No file chosen!"]
"
- dat += "Volume: [sound_volume]%
"
+ dat += "Sound File: [sound_file ? sound_file : "No file chosen!"]
"
+ dat += "Volume: [sound_volume]%
"
dat += "
"
- dat += "Mode: [motus_operandi]
"
+ dat += "Mode: [motus_operandi]
"
if(motus_operandi != SOUND_EMITTER_LOCAL)
- dat += "Range: [emitter_range][emitter_range == SOUND_EMITTER_RADIUS ? "[play_radius]-tile radius" : ""]
"
+ dat += "Range: [emitter_range][emitter_range == SOUND_EMITTER_RADIUS ? "[play_radius]-tile radius" : ""]
"
dat += "
"
- dat += "Play Sound (interrupts other sound emitter sounds)"
+ dat += "Play Sound (interrupts other sound emitter sounds)"
var/datum/browser/popup = new(user, "emitter", "", 500, 600)
popup.set_content(dat)
popup.open()
diff --git a/code/modules/admin/verbs/lua/lua_editor.dm b/code/modules/admin/verbs/lua/lua_editor.dm
index 0754f10902c..9e0fb17f86a 100644
--- a/code/modules/admin/verbs/lua/lua_editor.dm
+++ b/code/modules/admin/verbs/lua/lua_editor.dm
@@ -20,7 +20,7 @@
. = ..()
if(state)
current_state = state
- LAZYADDASSOCLIST(SSlua.editors, "\ref[current_state]", src)
+ LAZYADDASSOCLIST(SSlua.editors, text_ref(current_state), src)
/datum/lua_editor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -32,7 +32,7 @@
/datum/lua_editor/Destroy(force, ...)
. = ..()
if(current_state)
- LAZYREMOVEASSOC(SSlua.editors, "\ref[current_state]", src)
+ LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
/datum/lua_editor/ui_state(mob/user)
return GLOB.debug_state
@@ -107,16 +107,16 @@
return TRUE
var/datum/lua_state/new_state = new(state_name)
SSlua.states += new_state
- LAZYREMOVEASSOC(SSlua.editors, "\ref[current_state]", src)
+ LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
current_state = new_state
- LAZYADDASSOCLIST(SSlua.editors, "\ref[current_state]", src)
+ LAZYADDASSOCLIST(SSlua.editors, text_ref(current_state), src)
page = 0
return TRUE
if("switchState")
var/state_index = params["index"]
- LAZYREMOVEASSOC(SSlua.editors, "\ref[current_state]", src)
+ LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
current_state = SSlua.states[state_index]
- LAZYADDASSOCLIST(SSlua.editors, "\ref[current_state]", src)
+ LAZYADDASSOCLIST(SSlua.editors, text_ref(current_state), src)
page = 0
return TRUE
if("runCode")
diff --git a/code/modules/admin/verbs/lua/lua_state.dm b/code/modules/admin/verbs/lua/lua_state.dm
index a647a4ae587..64805917201 100644
--- a/code/modules/admin/verbs/lua/lua_state.dm
+++ b/code/modules/admin/verbs/lua/lua_state.dm
@@ -95,7 +95,7 @@ GLOBAL_PROTECT(lua_usr)
var/datum/weakref/weak_ref = path_element
var/resolved = weak_ref.hard_resolve()
if(!resolved)
- return list("status" = "errored", "param" = "Weakref in function path ([weak_ref] \ref[weak_ref]) resolved to null.", "name" = jointext(function, "."))
+ return list("status" = "errored", "param" = "Weakref in function path ([weak_ref] [text_ref(weak_ref)]) resolved to null.", "name" = jointext(function, "."))
new_function_path += resolved
else
new_function_path += path_element
@@ -163,7 +163,7 @@ GLOBAL_PROTECT(lua_usr)
__lua_kill_task(internal_id, task_info)
/datum/lua_state/proc/update_editors()
- var/list/editor_list = LAZYACCESS(SSlua.editors, "\ref[src]")
+ var/list/editor_list = LAZYACCESS(SSlua.editors, text_ref(src))
if(editor_list)
for(var/datum/lua_editor/editor as anything in editor_list)
SStgui.update_uis(editor)
diff --git a/code/modules/admin/view_variables/reference_tracking.dm b/code/modules/admin/view_variables/reference_tracking.dm
index 2339f06b81d..46c073de185 100644
--- a/code/modules/admin/view_variables/reference_tracking.dm
+++ b/code/modules/admin/view_variables/reference_tracking.dm
@@ -104,11 +104,11 @@
found_refs[varname] = TRUE
continue //End early, don't want these logging
#endif
- log_reftracker("Found [type] \ref[src] in [datum_container.type]'s \ref[datum_container] [varname] var. [container_name]")
+ log_reftracker("Found [type] [text_ref(src)] in [datum_container.type]'s [text_ref(datum_container)] [varname] var. [container_name]")
continue
if(islist(variable))
- DoSearchVar(variable, "[container_name] \ref[datum_container] -> [varname] (list)", recursive_limit - 1, search_time)
+ DoSearchVar(variable, "[container_name] [text_ref(datum_container)] -> [varname] (list)", recursive_limit - 1, search_time)
else if(islist(potential_container))
var/normal = IS_NORMAL_LIST(potential_container)
@@ -124,7 +124,7 @@
found_refs[potential_cache] = TRUE
continue //End early, don't want these logging
#endif
- log_reftracker("Found [type] \ref[src] in list [container_name].")
+ log_reftracker("Found [type] [text_ref(src)] in list [container_name].")
continue
var/assoc_val = null
@@ -137,7 +137,7 @@
found_refs[potential_cache] = TRUE
continue //End early, don't want these logging
#endif
- log_reftracker("Found [type] \ref[src] in list [container_name]\[[element_in_list]\]")
+ log_reftracker("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]")
continue
//We need to run both of these checks, since our object could be hiding in either of them
//Check normal sublists
@@ -151,7 +151,7 @@
thing_to_del.qdel_and_find_ref_if_fail(force)
/datum/proc/qdel_and_find_ref_if_fail(force = FALSE)
- SSgarbage.reference_find_on_fail["\ref[src]"] = TRUE
+ SSgarbage.reference_find_on_fail[text_ref(src)] = TRUE
qdel(src, force)
#endif
diff --git a/code/modules/asset_cache/assets/vending.dm b/code/modules/asset_cache/assets/vending.dm
index 01a6afcf524..574836f0c12 100644
--- a/code/modules/asset_cache/assets/vending.dm
+++ b/code/modules/asset_cache/assets/vending.dm
@@ -20,11 +20,11 @@
var/icon_states_string
for (var/an_icon_state in icon_states_list)
if (!icon_states_string)
- icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
+ icon_states_string = "[json_encode(an_icon_state)]([text_ref(an_icon_state)])"
else
- icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
+ icon_states_string += ", [json_encode(an_icon_state)]([text_ref(an_icon_state)])"
- stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
+ stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)]([text_ref(icon_state)]), icon_states=[icon_states_string]")
continue
#endif
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index e5c096d8d9e..5fb5feda3f1 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -55,14 +55,12 @@
var/christmas_tree = /obj/structure/flora/tree/pine/xmas/presents
/obj/effect/spawner/xmastree/Initialize(mapload)
- ..()
+ . = ..()
if((CHRISTMAS in SSevents.holidays) && christmas_tree)
new christmas_tree(get_turf(src))
else if((FESTIVE_SEASON in SSevents.holidays) && festive_tree)
new festive_tree(get_turf(src))
- return INITIALIZE_HINT_QDEL
-
/obj/effect/spawner/xmastree/rdrod
name = "festivus pole spawner"
festive_tree = /obj/structure/festivus
diff --git a/code/modules/language/language_holder.dm b/code/modules/language/language_holder.dm
index e426925fb09..4658edd71ec 100644
--- a/code/modules/language/language_holder.dm
+++ b/code/modules/language/language_holder.dm
@@ -55,7 +55,7 @@ Key procs
/// Initializes, and copies in the languages from the current atom if available.
/datum/language_holder/New(atom/_owner)
if(_owner && QDELETED(_owner))
- CRASH("Langauge holder added to a qdeleting thing, what the fuck \ref[_owner]")
+ CRASH("Langauge holder added to a qdeleting thing, what the fuck [text_ref(_owner)]")
owner = _owner
if(istype(owner, /datum/mind))
var/datum/mind/M = owner
diff --git a/code/modules/mapping/space_management/space_reservation.dm b/code/modules/mapping/space_management/space_reservation.dm
index 4d4c6b34daa..e85d09aa800 100644
--- a/code/modules/mapping/space_management/space_reservation.dm
+++ b/code/modules/mapping/space_management/space_reservation.dm
@@ -14,11 +14,11 @@
turf_type = /turf/open/space/transit
/datum/turf_reservation/proc/Release()
- var/v = reserved_turfs.Copy()
- for(var/i in reserved_turfs)
- reserved_turfs -= i
- SSmapping.used_turfs -= i
- INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/proc/reserve_turfs, v)
+ var/list/reserved_copy = reserved_turfs.Copy()
+ SSmapping.used_turfs -= reserved_turfs
+ reserved_turfs = list()
+ // Makes the linter happy, even tho we don't await this
+ INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/proc/reserve_turfs, reserved_copy)
/datum/turf_reservation/proc/Reserve(width, height, zlevel)
if(width > world.maxx || height > world.maxy || width < 1 || height < 1)