From 5fe26c41a14b44825b5e378347c6be76f8c65084 Mon Sep 17 00:00:00 2001
From: CheffieGithub <113442598+CheffieGithub@users.noreply.github.com>
Date: Sun, 22 Feb 2026 14:41:45 +0000
Subject: [PATCH 1/5] genesis call and list init cleanup
---
code/__DEFINES/_globals.dm | 11 +--
code/__DEFINES/preferences.dm | 7 ++
code/__DEFINES/roguetown.dm | 2 -
code/__HELPERS/global_lists.dm | 70 +---------------
code/__HELPERS/mobs.dm | 4 -
code/__HELPERS/unsorted.dm | 8 --
code/_globalvars/configuration.dm | 2 +-
code/_globalvars/lists/emote.dm | 14 ++++
code/_globalvars/lists/flavor_misc.dm | 10 ---
code/_globalvars/lists/import.dm | 9 ++
code/_globalvars/lists/mobs.dm | 33 ++++----
code/_globalvars/lists/patron.dm | 37 +++++++++
code/_globalvars/lists/preferences.dm | 23 ++++++
code/_globalvars/lists/slapcrafting.dm | 45 ++++++----
code/_globalvars/lists/surgery.dm | 23 +++++-
code/controllers/failsafe.dm | 1 +
code/controllers/globals.dm | 12 +--
code/controllers/master.dm | 7 +-
code/controllers/subsystem/dbcore.dm | 4 +-
code/controllers/subsystem/job.dm | 4 +-
code/controllers/subsystem/storyteller.dm | 2 +-
code/datums/gods/_faith.dm | 2 -
code/datums/gods/_patron.dm | 2 -
.../molten_materials/metal_combine_recipes.dm | 2 -
code/datums/profiling.dm | 3 +-
code/datums/quirks/_base.dm | 2 +-
code/game/world.dm | 82 +++++++++++++++----
.../client/preferences/_preferences.dm | 28 +++----
.../preferences/preferences_savefile.dm | 6 +-
code/modules/error_handler/error_handler.dm | 5 --
code/modules/inquisitor_supplies/_base.dm | 3 -
.../mob/dead/new_player/sprite_accessories.dm | 43 +++++-----
.../mob/living/carbon/human/species.dm | 12 +--
code/ze_genesis_call/genesis_call.dm | 48 +++++++++++
vanderlin.dme | 5 ++
35 files changed, 338 insertions(+), 233 deletions(-)
create mode 100644 code/_globalvars/lists/emote.dm
create mode 100644 code/_globalvars/lists/import.dm
create mode 100644 code/_globalvars/lists/patron.dm
create mode 100644 code/_globalvars/lists/preferences.dm
create mode 100644 code/ze_genesis_call/genesis_call.dm
diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm
index 6e1e5ba352e..ffdabebb53a 100644
--- a/code/__DEFINES/_globals.dm
+++ b/code/__DEFINES/_globals.dm
@@ -1,4 +1,5 @@
-//See also controllers/globals.dm
+// See also controllers/globals.dm
+// See initialization order in /code/game/world.dm
/// Creates a global initializer with a given InitValue expression, do not use
#define GLOBAL_MANAGED(X, InitValue)\
@@ -20,11 +21,11 @@
#define GLOBAL_PROTECT(X)
#endif
-/// Standard BYOND global, do not use
-#define GLOBAL_REAL_VAR(X) var/global/##X
+/// Standard BYOND global, seriously do not use without an earthshakingly good reason
+#define GLOBAL_REAL_VAR(X) var/global/##X;
-/// Standard typed BYOND global, do not use
-#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X
+/// Standard typed BYOND global, seriously do not use without an earthshakingly good reason
+#define GLOBAL_REAL(X, Typepath) var/global##Typepath/##X;
/// Defines a global var on the controller, do not use
#define GLOBAL_RAW(X) /datum/controller/global_vars/var/global##X
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index d4bf081c269..e5431e4c697 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -214,3 +214,10 @@ DEFINE_BITFIELD(toggles_maptext, list(
//"Disable hover text" = DISABLE_HOVER_TEXT,
"Disable runechat" = DISABLE_RUNECHAT,
))
+
+/// The non gender specific list that we get from init_sprite_accessory_subtypes()
+#define DEFAULT_SPRITE_LIST "default_sprites"
+/// The male specific list that we get from init_sprite_accessory_subtypes()
+#define MALE_SPRITE_LIST "male_sprites"
+/// The female specific list that we get from init_sprite_accessory_subtypes()
+#define FEMALE_SPRITE_LIST "female_sprites"
diff --git a/code/__DEFINES/roguetown.dm b/code/__DEFINES/roguetown.dm
index b914972fbb7..4af15cbc8bf 100644
--- a/code/__DEFINES/roguetown.dm
+++ b/code/__DEFINES/roguetown.dm
@@ -8,8 +8,6 @@
/// Currently same as ALL_ICONOCLAST_PATRONS, but in text format because byond sucks and won't read things properly.
#define COLORFUL_PATRONS list("Psydon", "Astrata", "Noc", "Dendor", "Abyssor", "Necra", "Ravox", "Xylix", "Pestra", "Malum", "Eora", "Graggar", "Zizo", "Matthios", "Baotha")
-GLOBAL_LIST_INIT(curse_names, list())
-
#define TEN_CURSES list(\
/datum/curse/astrata,\
/datum/curse/noc,\
diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm
index 1418b59f7e9..f823dd85856 100644
--- a/code/__HELPERS/global_lists.dm
+++ b/code/__HELPERS/global_lists.dm
@@ -2,70 +2,10 @@
/////Initial Building/////
//////////////////////////
-/proc/make_datum_references_lists()
- init_quirk_registry()
- //underwear
- init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
- //undershirt
- init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
-
- //Species
- for(var/datum/species/species as anything in subtypesof(/datum/species))
- species = new species()
- GLOB.species_list[species.id] = species.type
-
- if(species.check_roundstart_eligible())
- GLOB.roundstart_species += species.id
-
- sortTim(GLOB.species_list, GLOBAL_PROC_REF(cmp_text_dsc))
- sortTim(GLOB.roundstart_species, GLOBAL_PROC_REF(cmp_text_dsc))
-
- //Surgery steps
- for(var/path in subtypesof(/datum/surgery_step))
- GLOB.surgery_steps += new path()
- sortTim(GLOB.surgery_steps, GLOBAL_PROC_REF(cmp_typepaths_asc))
-
- //Surgeries
- for(var/path in subtypesof(/datum/surgery))
- GLOB.surgeries_list += new path()
- sortTim(GLOB.surgeries_list, GLOBAL_PROC_REF(cmp_typepaths_asc))
-
+/proc/make_datum_reference_lists()
// Keybindings
init_keybindings()
- init_molten_recipes()
- init_slapcraft_steps()
- init_slapcraft_recipes()
- init_curse_names()
-
- GLOB.emote_list = init_emote_list()
-
- // Faiths
- for(var/datum/faith/faith as anything in subtypesof(/datum/faith))
- if(IS_ABSTRACT(faith))
- continue
-
- faith = new faith()
- GLOB.faith_list[faith.type] = faith
-
- // Inquisition Hermes list
- for(var/path in subtypesof(/datum/inqports)) // Why is this here
- var/datum/inqports/inqports = new path()
- GLOB.inqsupplies[path] = inqports
-
- // Patron Gods
- for(var/datum/patron/patron as anything in subtypesof(/datum/patron))
- if(IS_ABSTRACT(patron))
- continue
-
- patron = new patron()
-
- GLOB.patron_list[patron.type] = patron
-
- LAZYINITLIST(GLOB.patrons_by_faith[patron.associated_faith])
-
- GLOB.patrons_by_faith[patron.associated_faith][patron.type] = patron
-
//creates every subtype of prototype (excluding prototype) and adds it to list L.
//if no list/L is provided, one is created.
/proc/init_subtypes(prototype, list/L)
@@ -91,11 +31,3 @@
for(var/path as anything in subtypesof(prototype))
L[path] = new path()
return L
-
-/proc/init_curse_names()
- GLOB.curse_names = list()
- for(var/datum/curse/curse_type as anything in subtypesof(/datum/curse))
- if(IS_ABSTRACT(curse_type))
- continue
- GLOB.curse_names |= initial(curse_type.name)
- GLOB.curse_names[initial(curse_type.name)] = new curse_type
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index fe62ae6ec76..024947988c7 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -25,8 +25,6 @@
return "000"
/proc/random_underwear(gender)
- if(!GLOB.underwear_list.len)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
switch(gender)
if(MALE)
return pick(GLOB.underwear_m)
@@ -36,8 +34,6 @@
return pick(GLOB.underwear_list)
/proc/random_undershirt(gender)
- if(!GLOB.undershirt_list.len)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f)
switch(gender)
if(MALE)
return pick(GLOB.undershirt_m)
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index 3a4e3b97e00..13fd505acda 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -674,14 +674,6 @@ GLOBAL_LIST_INIT(WALLITEMS_INVERSE, typecacheof(list(
/proc/stack_trace(msg)
CRASH(msg)
-GLOBAL_REAL_VAR(list/stack_trace_storage)
-/proc/gib_stack_trace()
- stack_trace_storage = list()
- stack_trace()
- stack_trace_storage.Cut(1, min(3,stack_trace_storage.len))
- . = stack_trace_storage
- stack_trace_storage = null
-
//Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm.
//Increases delay as the server gets more overloaded,
diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm
index ac5135c04df..547b0a88aaa 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -1,6 +1,6 @@
GLOBAL_REAL(config, /datum/controller/configuration)
-GLOBAL_DATUM(revdata, /datum/getrev)
+GLOBAL_DATUM_INIT(revdata, /datum/getrev, new)
GLOBAL_VAR(host)
GLOBAL_VAR(station_name)
diff --git a/code/_globalvars/lists/emote.dm b/code/_globalvars/lists/emote.dm
new file mode 100644
index 00000000000..c53bc188868
--- /dev/null
+++ b/code/_globalvars/lists/emote.dm
@@ -0,0 +1,14 @@
+GLOBAL_LIST_INIT(emote_list, init_emote_list())
+
+/proc/init_emote_list()
+ var/list/emotes = list()
+ for(var/datum/emote/emote as anything in subtypesof(/datum/emote))
+ if(IS_ABSTRACT(emote))
+ continue
+
+ emote = new emote()
+
+ LAZYADDASSOCLIST(emotes, emote.key, emote)
+ LAZYADDASSOCLIST(emotes, emote.key_third_person, emote)
+
+ return emotes
diff --git a/code/_globalvars/lists/flavor_misc.dm b/code/_globalvars/lists/flavor_misc.dm
index 66ed26d5c5b..1fb430b0f2e 100644
--- a/code/_globalvars/lists/flavor_misc.dm
+++ b/code/_globalvars/lists/flavor_misc.dm
@@ -1,13 +1,3 @@
-//Preferences stuff
- //Underwear
-GLOBAL_LIST_EMPTY(underwear_list) //stores /datum/sprite_accessory/underwear indexed by name
-GLOBAL_LIST_EMPTY(underwear_m) //stores only underwear name
-GLOBAL_LIST_EMPTY(underwear_f) //stores only underwear name
- //Undershirts
-GLOBAL_LIST_EMPTY(undershirt_list) //stores /datum/sprite_accessory/undershirt indexed by name
-GLOBAL_LIST_EMPTY(undershirt_m) //stores only undershirt name
-GLOBAL_LIST_EMPTY(undershirt_f) //stores only undershirt name
-
GLOBAL_LIST_INIT(color_list_ethereal, list("F Class(Green)" = "97ee63", "F2 Class (Light Green)" = "00fa9a", "F3 Class (Dark Green)" = "37835b", "M Class (Red)" = "9c3030", "M1 Class (Purple)" = "ee82ee", "G Class (Yellow)" = "fbdf56", "O Class (Blue)" = "3399ff", "A Class (Cyan)" = "00ffff"))
GLOBAL_LIST_INIT(ghost_forms_with_directions_list, list(
diff --git a/code/_globalvars/lists/import.dm b/code/_globalvars/lists/import.dm
new file mode 100644
index 00000000000..bb8331ed4d0
--- /dev/null
+++ b/code/_globalvars/lists/import.dm
@@ -0,0 +1,9 @@
+GLOBAL_LIST_INIT(inqsupplies, init_inqsupplies())
+
+/proc/init_inqsupplies()
+ var/list/supplies = list()
+ for(var/datum/inqports/path as anything in subtypesof(/datum/inqports))
+ if(IS_ABSTRACT(path))
+ continue
+ supplies[path] = new path()
+ return supplies
diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm
index f267c0259c5..1a2d7853027 100644
--- a/code/_globalvars/lists/mobs.dm
+++ b/code/_globalvars/lists/mobs.dm
@@ -35,15 +35,28 @@ GLOBAL_LIST_EMPTY(aiEyes)
GLOBAL_LIST_EMPTY(language_datum_instances)
GLOBAL_LIST_EMPTY(all_languages)
+GLOBAL_LIST_EMPTY(roundstart_species)
/// Associative list of species id to type
-GLOBAL_LIST_EMPTY(species_list)
+GLOBAL_LIST_INIT(species_list, init_species_lists())
+
+/proc/init_species_lists()
+ var/list/species_list = list()
+ for(var/datum/species/species as anything in subtypesof(/datum/species))
+ species = new species()
+ species_list[species.id] = species.type
+
+ if(species.check_roundstart_eligible())
+ GLOB.roundstart_species += species.id
+
+ sortTim(species_list, GLOBAL_PROC_REF(cmp_text_dsc))
+ sortTim(GLOB.roundstart_species, GLOBAL_PROC_REF(cmp_text_dsc))
+
+ return species_list
GLOBAL_LIST_EMPTY(latejoin_ai_cores)
GLOBAL_LIST_EMPTY(mob_config_movespeed_type_lookup)
-GLOBAL_LIST_EMPTY(emote_list)
-
GLOBAL_LIST_INIT(dangerous_turfs, typecacheof(list(
/turf/open/lava,
/turf/open/openspace,
@@ -67,20 +80,6 @@ GLOBAL_LIST_INIT(dangerous_turfs, typecacheof(list(
for(var/mob/M as anything in GLOB.mob_list)
M.update_config_movespeed()
-/proc/init_emote_list()
- . = list()
- for(var/path in subtypesof(/datum/emote))
- var/datum/emote/E = new path()
- if(!.[E.key])
- .[E.key] = list(E)
- else
- .[E.key] += E
-
- if(!.[E.key_third_person])
- .[E.key_third_person] = list(E)
- else
- .[E.key_third_person] |= E
-
/// Cultures can't be interacted with so we only ever need as many datums as exist
GLOBAL_LIST_INIT(culture_singletons, init_culture_singletons())
diff --git a/code/_globalvars/lists/patron.dm b/code/_globalvars/lists/patron.dm
new file mode 100644
index 00000000000..ecc30a54622
--- /dev/null
+++ b/code/_globalvars/lists/patron.dm
@@ -0,0 +1,37 @@
+GLOBAL_LIST_INIT(faith_list, init_faith_list())
+
+/proc/init_faith_list()
+ var/list/faiths = list()
+ for(var/datum/faith/faith as anything in subtypesof(/datum/faith))
+ if(IS_ABSTRACT(faith))
+ continue
+
+ faiths[faith] = new faith()
+
+ return faiths
+
+GLOBAL_LIST_EMPTY(patrons_by_faith)
+GLOBAL_LIST_INIT(patron_list, init_patron_list())
+
+/proc/init_patron_list()
+ var/list/faiths = list()
+ for(var/datum/patron/patron as anything in subtypesof(/datum/patron))
+ if(IS_ABSTRACT(patron))
+ continue
+
+ faiths[patron] = new patron()
+
+ LAZYADDASSOCLIST(GLOB.patrons_by_faith, patron::associated_faith, patron)
+
+ return faiths
+
+GLOBAL_LIST_INIT(curse_names, init_curse_names())
+
+/proc/init_curse_names()
+ var/list/curses = list()
+ for(var/datum/curse/curse_type as anything in subtypesof(/datum/curse))
+ if(IS_ABSTRACT(curse_type))
+ continue
+
+ LAZYADDASSOC(curses, curse_type::name, new curse_type)
+ return curses
diff --git a/code/_globalvars/lists/preferences.dm b/code/_globalvars/lists/preferences.dm
new file mode 100644
index 00000000000..aca7205ce5a
--- /dev/null
+++ b/code/_globalvars/lists/preferences.dm
@@ -0,0 +1,23 @@
+GLOBAL_LIST_EMPTY(underwear_m)
+GLOBAL_LIST_EMPTY(underwear_f)
+GLOBAL_LIST_INIT(underwear_list, init_underwear_list())
+
+/proc/init_underwear_list()
+ var/list/underwear = init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear)
+
+ GLOB.underwear_m = underwear[MALE_SPRITE_LIST]
+ GLOB.underwear_f = underwear[FEMALE_SPRITE_LIST]
+
+ return underwear[DEFAULT_SPRITE_LIST]
+
+GLOBAL_LIST_EMPTY(undershirt_m)
+GLOBAL_LIST_EMPTY(undershirt_f)
+GLOBAL_LIST_INIT(undershirt_list, init_undershirt_list())
+
+/proc/init_undershirt_list()
+ var/list/undershirts = init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt)
+
+ GLOB.undershirt_m = undershirts[MALE_SPRITE_LIST]
+ GLOB.undershirt_f = undershirts[FEMALE_SPRITE_LIST]
+
+ return undershirts[DEFAULT_SPRITE_LIST]
diff --git a/code/_globalvars/lists/slapcrafting.dm b/code/_globalvars/lists/slapcrafting.dm
index 8ed6684be6b..878da9a1549 100644
--- a/code/_globalvars/lists/slapcrafting.dm
+++ b/code/_globalvars/lists/slapcrafting.dm
@@ -1,40 +1,49 @@
GLOBAL_LIST_EMPTY(slapcraft_firststep_recipe_cache)
-GLOBAL_LIST_EMPTY(slapcraft_categorized_recipes)
-GLOBAL_LIST_EMPTY(slapcraft_steps)
-GLOBAL_LIST_EMPTY(slapcraft_recipes)
+GLOBAL_LIST_INIT(slapcraft_steps, init_slapcraft_steps())
+
+/proc/init_slapcraft_steps()
+ var/list/step_list = list()
+ for(var/datum/slapcraft_step/type as anything in typesof(/datum/slapcraft_step))
+ if(IS_ABSTRACT(type))
+ continue
+ step_list[type] = new type()
+
+ return step_list
+
+GLOBAL_LIST_EMPTY(slapcraft_categorized_recipes)
+GLOBAL_LIST_INIT(slapcraft_recipes, init_slapcraft_recipes())
/proc/init_slapcraft_recipes()
- var/list/recipe_list = GLOB.slapcraft_recipes
- for(var/datum/type as anything in typesof(/datum/slapcraft_recipe))
+ var/list/recipe_list = list()
+ for(var/datum/slapcraft_recipe/type as anything in typesof(/datum/slapcraft_recipe))
if(IS_ABSTRACT(type))
continue
+
var/datum/slapcraft_recipe/recipe = new type()
recipe_list[type] = recipe
// Add the recipe to the categorized global list, which is used for the handbook UI
- if(!GLOB.slapcraft_categorized_recipes[recipe.category])
+ if(!length(GLOB.slapcraft_categorized_recipes[recipe.category]))
GLOB.slapcraft_categorized_recipes[recipe.category] = list()
- if(!GLOB.slapcraft_categorized_recipes[recipe.category][recipe.subcategory])
+
+ if(!length(GLOB.slapcraft_categorized_recipes[recipe.category][recipe.subcategory]))
GLOB.slapcraft_categorized_recipes[recipe.category][recipe.subcategory] = list()
+
GLOB.slapcraft_categorized_recipes[recipe.category][recipe.subcategory] += recipe
+ return recipe_list
+
+GLOBAL_LIST_INIT(molten_recipes, init_molten_recipes())
/proc/init_molten_recipes()
- var/list/recipe_list = GLOB.molten_recipes
- for(var/datum/type as anything in typesof(/datum/molten_recipe))
+ var/list/recipe_list = list()
+ for(var/datum/molten_recipe/type as anything in typesof(/datum/molten_recipe))
if(IS_ABSTRACT(type))
continue
- var/datum/molten_recipe/recipe = new type()
- recipe_list |= recipe
-
+ recipe_list += new type()
-/proc/init_slapcraft_steps()
- var/list/step_list = GLOB.slapcraft_steps
- for(var/datum/type as anything in typesof(/datum/slapcraft_step))
- if(IS_ABSTRACT(type))
- continue
- step_list[type] = new type()
+ return recipe_list
/// Gets cached recipes for a type. This is a method of optimizating recipe lookup. Ugly but gets the job done.
/// also WARNING: This will make it so all recipes whose first step is not type checked will not work, which all recipes that I can think of will be.
diff --git a/code/_globalvars/lists/surgery.dm b/code/_globalvars/lists/surgery.dm
index d8d1ef39d86..7c0ed9ff955 100644
--- a/code/_globalvars/lists/surgery.dm
+++ b/code/_globalvars/lists/surgery.dm
@@ -1,4 +1,23 @@
/// List of all surgery datums
-GLOBAL_LIST_EMPTY(surgeries_list)
+GLOBAL_LIST_INIT(surgeries_list, init_surgeries())
+
+/proc/init_surgeries()
+ var/list/surgeries = list()
+ for(var/datum/surgery/path as anything in subtypesof(/datum/surgery))
+ if(IS_ABSTRACT(path))
+ continue
+ surgeries += new path()
+ sortTim(surgeries, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ return surgeries
+
/// List of all surgery step datums
-GLOBAL_LIST_EMPTY(surgery_steps)
+GLOBAL_LIST_INIT(surgery_steps, init_surgery_steps())
+
+/proc/init_surgery_steps()
+ var/list/steps = list()
+ for(var/datum/surgery_step/path as anything in subtypesof(/datum/surgery_step))
+ if(IS_ABSTRACT(path))
+ continue
+ steps += new path()
+ sortTim(steps, GLOBAL_PROC_REF(cmp_typepaths_asc))
+ return steps
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index 6a6d4c58d6a..c108f94a401 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -4,6 +4,7 @@
* Pretty much pokes the MC to make sure it's still alive.
**/
+// See initialization order in /code/game/world.dm
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm
index bd1010c8881..26f193181d0 100644
--- a/code/controllers/globals.dm
+++ b/code/controllers/globals.dm
@@ -1,3 +1,4 @@
+// See initialization order in /code/game/world.dm
GLOBAL_REAL(GLOB, /datum/controller/global_vars)
/datum/controller/global_vars
@@ -19,11 +20,8 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
var/list/controller_vars = exclude_these.vars.Copy()
controller_vars["vars"] = null
gvars_datum_in_built_vars = controller_vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
- //QDEL_IN(exclude_these, 0) //signal logging isn't ready
- QDEL_NULL(exclude_these)
-
- log_world("[vars.len - gvars_datum_in_built_vars.len] global variables")
+ QDEL_IN(exclude_these, 0) //signal logging isn't ready
Initialize()
@@ -45,7 +43,7 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
gvars_datum_init_order = list()
gvars_datum_protected_varlist = list(NAMEOF(src, gvars_datum_protected_varlist) = TRUE)
var/list/global_procs = typesof(/datum/controller/global_vars/proc)
- var/expected_len = vars.len - gvars_datum_in_built_vars.len
+ var/expected_len = length(vars) - length(gvars_datum_in_built_vars)
if(global_procs.len != expected_len)
warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!")
if(global_procs.len)
@@ -53,9 +51,13 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars)
for(var/I in global_procs)
expected_global_procs -= replacetext("[I]", "InitGlobal", "")
log_world("Missing procs: [expected_global_procs.Join(", ")]")
+
for(var/I in global_procs)
var/start_tick = world.time
call(src, I)()
var/end_tick = world.time
if(end_tick - start_tick)
warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!")
+
+ // Someone make it so this call isn't necessary
+ make_datum_reference_lists()
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 250099e6831..e37a100b281 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -7,14 +7,9 @@
*
**/
-//This is the ABSOLUTE ONLY THING that should init globally like this
-//2019 update: the failsafe,config and Global controllers also do it
+// See initialization order in /code/game/world.dm
GLOBAL_REAL(Master, /datum/controller/master)
-//THIS IS THE INIT ORDER
-//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
-//GOT IT MEMORIZED?
-
/datum/controller/master
name = "Master"
diff --git a/code/controllers/subsystem/dbcore.dm b/code/controllers/subsystem/dbcore.dm
index b8f102cae85..c5aced810e2 100644
--- a/code/controllers/subsystem/dbcore.dm
+++ b/code/controllers/subsystem/dbcore.dm
@@ -171,7 +171,9 @@ SUBSYSTEM_DEF(dbcore)
else
log_sql("Database is not enabled in configuration.")
-/datum/controller/subsystem/dbcore/proc/SetRoundID()
+/datum/controller/subsystem/dbcore/proc/InitializeRound()
+ CheckSchemaVersion()
+
if(!Connect())
return
var/datum/DBQuery/query_round_initialize = SSdbcore.NewQuery(
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index e798623d926..0f87e43e380 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -140,7 +140,7 @@ SUBSYSTEM_DEF(job)
JobDebug("GRJ incompatible with species, Player: [player], Job: [job.title], Species: [player_prefs.pref_species.name]")
continue
- if(length(job.allowed_patrons) && !(player_prefs.selected_patron.type in job.allowed_patrons))
+ if(length(job.allowed_patrons) && !(player_prefs.selected_patron in job.allowed_patrons))
JobDebug("GRJ incompatible with patron, Player: [player], Job: [job.title], Species: [player_prefs.pref_species.name]")
continue
@@ -218,7 +218,7 @@ SUBSYSTEM_DEF(job)
JobDebug("Eligibility failed: species blacklisted, Player: [player], Job: [job.title]")
return FALSE
- if(length(job.allowed_patrons) && !(player_prefs.selected_patron.type in job.allowed_patrons))
+ if(length(job.allowed_patrons) && !(player_prefs.selected_patron in job.allowed_patrons))
JobDebug("Eligibility failed: patron, Player: [player], Job: [job.title]")
return FALSE
diff --git a/code/controllers/subsystem/storyteller.dm b/code/controllers/subsystem/storyteller.dm
index e3c3a99711d..8ffdf9db9e8 100644
--- a/code/controllers/subsystem/storyteller.dm
+++ b/code/controllers/subsystem/storyteller.dm
@@ -1538,7 +1538,7 @@ SUBSYSTEM_DEF(gamemode)
if(roundstart && istype(client?.mob, /mob/dead/new_player))
var/mob/dead/new_player/player = client.mob
if(player.ready == PLAYER_READY_TO_PLAY)
- GLOB.patron_follower_counts[client.prefs.selected_patron.name]++
+ GLOB.patron_follower_counts[client.prefs.selected_patron::name]++
var/mob/living/living = client.mob
if(!istype(living))
diff --git a/code/datums/gods/_faith.dm b/code/datums/gods/_faith.dm
index fe707d40bdc..cc7c0383f97 100644
--- a/code/datums/gods/_faith.dm
+++ b/code/datums/gods/_faith.dm
@@ -1,5 +1,3 @@
-GLOBAL_LIST_EMPTY(faith_list)
-
/datum/faith
abstract_type = /datum/faith
/// Name of the faith
diff --git a/code/datums/gods/_patron.dm b/code/datums/gods/_patron.dm
index b6e16da6253..c2ca2307dcb 100644
--- a/code/datums/gods/_patron.dm
+++ b/code/datums/gods/_patron.dm
@@ -1,5 +1,3 @@
-GLOBAL_LIST_EMPTY(patron_list)
-GLOBAL_LIST_EMPTY(patrons_by_faith)
GLOBAL_LIST_EMPTY(prayers)
/datum/patron
diff --git a/code/datums/molten_materials/metal_combine_recipes.dm b/code/datums/molten_materials/metal_combine_recipes.dm
index 30e6cef3acf..af5e2dd822b 100644
--- a/code/datums/molten_materials/metal_combine_recipes.dm
+++ b/code/datums/molten_materials/metal_combine_recipes.dm
@@ -1,5 +1,3 @@
-GLOBAL_LIST_INIT(molten_recipes, list())
-
/datum/molten_recipe
abstract_type = /datum/molten_recipe
var/name = "Generic Molten Recipe"
diff --git a/code/datums/profiling.dm b/code/datums/profiling.dm
index 1eec8787116..4e53f7b91e5 100644
--- a/code/datums/profiling.dm
+++ b/code/datums/profiling.dm
@@ -1,5 +1,6 @@
//these are real globals so you can use profiling to profile early world init stuff.
-GLOBAL_REAL_VAR(list/PROFILE_STORE)
+
+GLOBAL_REAL(PROFILE_STORE, /list)
GLOBAL_REAL_VAR(PROFILE_LINE)
GLOBAL_REAL_VAR(PROFILE_FILE)
GLOBAL_REAL_VAR(PROFILE_SLEEPCHECK)
diff --git a/code/datums/quirks/_base.dm b/code/datums/quirks/_base.dm
index 61e7d5798bb..6573cfffdbb 100644
--- a/code/datums/quirks/_base.dm
+++ b/code/datums/quirks/_base.dm
@@ -1,4 +1,4 @@
-GLOBAL_LIST_INIT(quirk_registry, list())
+GLOBAL_LIST_INIT(quirk_registry, init_quirk_registry())
GLOBAL_LIST_EMPTY(quirk_singletons)
GLOBAL_LIST_EMPTY(quirk_points_by_type)
diff --git a/code/game/world.dm b/code/game/world.dm
index 8793b26a905..143b321d816 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -11,25 +11,48 @@ GLOBAL_VAR(tracy_init_error)
GLOBAL_PROTECT(tracy_init_error)
GLOBAL_VAR(tracy_init_reason)
GLOBAL_PROTECT(tracy_init_reason)
+
/**
- * World creation
+ * WORLD INITIALIZATION
+ * THIS IS THE INIT ORDER:
*
- * Here is where a round itself is actually begun and setup, lots of important config changes happen here
- * * db connection setup
- * * config loaded from files
- * * loads admins
- * * Sets up the dynamic menu system
- * * and most importantly, calls initialize on the master subsystem, starting the game loop that causes the rest of the game to begin processing and setting up
+ * BYOND =>
+ * - (secret init native) =>
+ * - world.Genesis() =>
+ * - world.init_byond_tracy()
+ * - (Start native profiling)
+ * - world.init_debugger()
+ * - Master =>
+ * - config *unloaded
+ * - (all subsystems) PreInit()
+ * - GLOB =>
+ * - make_datum_reference_lists()
+ * - (/static variable inits, reverse declaration order)
+ * - (all pre-mapped atoms) /atom/New()
+ * - world.New() =>
+ * - config.Load()
+ * - world.InitTgs() =>
+ * - TgsNew() *may sleep
+ * - GLOB.rev_data.load_tgs_info()
+ * - world.ConfigLoaded() =>
+ * - SSdbcore.InitializeRound()
+ * - world.SetupLogs()
+ * - load_admins()
+ * - ...
+ * - Master.Initialize() =>
+ * - (all subsystems) Initialize()
+ * - Master.StartProcessing() =>
+ * - Master.Loop() =>
+ * - Failsafe
+ * - world.RunUnattendedFunctions()
*
- * Note this happens after the Master subsystem is created (as that is a global datum), this means all the subsystems exist,
- * but they have not been Initialized at this point, only their New proc has run
- *
- * Nothing happens until something moves. ~Albert Einstein
+ * Now listen up because I want to make something clear:
+ * If something is not in this list it should almost definitely be handled by a subsystem Initialize()ing
+ * If whatever it is that needs doing doesn't fit in a subsystem you probably aren't trying hard enough tbhfam
*
+ * GOT IT MEMORIZED?
+ * - Dominion/Cyberboss
*/
-/world/proc/_()
- var/static/_ = world.Genesis()
-
/**
* THIS !!!SINGLE!!! PROC IS WHERE ANY FORM OF INIITIALIZATION THAT CAN'T BE PERFORMED IN MASTER/NEW() IS DONE
@@ -70,14 +93,37 @@ GLOBAL_PROTECT(tracy_init_reason)
#undef USE_TRACY_PARAMETER
+/**
+ * World creation
+ *
+ * Here is where a round itself is actually begun and setup.
+ * * db connection setup
+ * * config loaded from files
+ * * loads admins
+ * * Sets up the dynamic menu system
+ * * and most importantly, calls initialize on the master subsystem, starting the game loop that causes the rest of the game to begin processing and setting up
+ *
+ *
+ * Nothing happens until something moves. ~Albert Einstein
+ *
+ * For clarity, this proc gets triggered later in the initialization pipeline, it is not the first thing to happen, as it might seem.
+ *
+ * Initialization Pipeline:
+ * Global vars are new()'ed, (including config, glob, and the master controller will also new and preinit all subsystems when it gets new()ed)
+ * Compiled in maps are loaded (mainly centcom). all areas/turfs/objs/mobs(ATOMs) in these maps will be new()ed
+ * world/New() (You are here)
+ * Once world/New() returns, client's can connect.
+ * 1 second sleep
+ * Master Controller initialization.
+ * Subsystem initialization.
+ * Non-compiled-in maps are maploaded, all atoms are new()ed
+ * All atoms in both compiled and uncompiled maps are initialized()
+ */
/world/New()
-
log_world("World loaded at [time_stamp()]!")
GLOB.config_error_log = GLOB.world_manifest_log = GLOB.world_pda_log = GLOB.world_job_debug_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = "data/logs/config_error.[GUID()].log" //temporary file used to record errors with loading config, moved to log directory once logging is set bl
- make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
-
TgsNew(new /datum/tgs_event_handler/impl, TGS_SECURITY_TRUSTED)
GLOB.revdata = new
@@ -89,7 +135,7 @@ GLOBAL_PROTECT(tracy_init_reason)
//SetupLogs depends on the RoundID, so lets check
//DB schema and set RoundID if we can
// SSdbcore.CheckSchemaVersion()
- SSdbcore.SetRoundID()
+ SSdbcore.InitializeRound()
var/timestamp = replacetext(time_stamp(), ":", ".")
if(!GLOB.round_id) // we do not have a db connected, back to pointless random numbers
diff --git a/code/modules/client/preferences/_preferences.dm b/code/modules/client/preferences/_preferences.dm
index cffa9b168ae..8246a247172 100644
--- a/code/modules/client/preferences/_preferences.dm
+++ b/code/modules/client/preferences/_preferences.dm
@@ -620,7 +620,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
@@ -737,7 +737,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
if(!winexists(user, "preferences_browser"))
return
- var/datum/faith/selected_faith = GLOB.faith_list[selected_patron.associated_faith]
+ var/datum/faith/selected_faith = GLOB.faith_list[selected_patron::associated_faith]
var/datum/job/high_job
for(var/job_type in job_preferences)
if(job_preferences[job_type] != JP_HIGH)
@@ -759,7 +759,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
if(update_all || ("species" in fields_to_update))
params["species"] = pref_species.name
if(update_all || ("patron" in fields_to_update))
- params["patron"] = selected_patron.name
+ params["patron"] = selected_patron::name
if(update_all || ("pq" in fields_to_update))
params["pq"] = get_playerquality(user.ckey, text = TRUE)
if(update_all || ("age" in fields_to_update))
@@ -1617,7 +1617,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
if(!faith.preference_accessible(src))
continue
faiths_named["\The [faith.name]"] = faith
- var/faith_input = browser_input_list(user, "SELECT YOUR HERO'S BELIEF", "PUPPETS ON STRINGS", faiths_named, "\The [selected_patron.associated_faith::name]")
+ var/faith_input = browser_input_list(user, "SELECT YOUR HERO'S BELIEF", "PUPPETS ON STRINGS", faiths_named, "\The [selected_patron::associated_faith::name]")
if(faith_input)
var/datum/faith/faith = faiths_named[faith_input]
to_chat(user, "Faith: [faith.name]")
@@ -1626,7 +1626,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
if("patron")
var/list/patrons_named = list()
- for(var/datum/patron/patron as anything in GLOB.patrons_by_faith[selected_patron.associated_faith || initial(default_patron.associated_faith)])
+ for(var/datum/patron/patron as anything in GLOB.patrons_by_faith[selected_patron::associated_faith || default_patron::associated_faith])
patron = GLOB.patron_list[patron]
if(!patron.preference_accessible(src))
continue
@@ -1634,18 +1634,18 @@ GLOBAL_LIST_INIT(name_adjustments, list())
patrons_named[pref_name] = patron
if(length(patrons_named))
- var/datum/faith/current_faith = GLOB.faith_list[selected_patron.associated_faith] || GLOB.faith_list[initial(default_patron.associated_faith)]
+ var/datum/faith/current_faith = GLOB.faith_list[selected_patron::associated_faith] || GLOB.faith_list[default_patron::associated_faith]
var/god_input = browser_input_list(user, "SELECT YOUR HERO'S PATRON GOD", uppertext("\The [current_faith.name]"), patrons_named, selected_patron)
if(god_input)
selected_patron = patrons_named[god_input]
- to_chat(user, "Patron: [selected_patron]")
- to_chat(user, "Domain: [selected_patron.domain]")
- to_chat(user, "Background: [selected_patron.desc]")
- to_chat(user, "Flawed aspects: [selected_patron.flaws]")
- to_chat(user, "Likely Worshippers: [selected_patron.worshippers]")
- to_chat(user, "Considers these to be Sins: [selected_patron.sins]")
- to_chat(user, "Blessed with boon(s): [selected_patron.boons]")
+ to_chat(user, "Patron: [selected_patron::name]")
+ to_chat(user, "Domain: [selected_patron::domain]")
+ to_chat(user, "Background: [selected_patron::desc]")
+ to_chat(user, "Flawed aspects: [selected_patron::flaws]")
+ to_chat(user, "Likely Worshippers: [selected_patron::worshippers]")
+ to_chat(user, "Considers these to be Sins: [selected_patron::sins]")
+ to_chat(user, "Blessed with boon(s): [selected_patron::boons]")
if("voice")
var/new_voice = input(user, "SELECT YOUR HERO'S VOICE COLOR", "THE THROAT","#"+voice_color) as color|null
@@ -2531,7 +2531,7 @@ GLOBAL_LIST_INIT(name_adjustments, list())
"\[SEX LOCK\]",
"Sexes Needed:
[sexes_text]"
)
- if(length(job.allowed_patrons) && !(user.client.prefs.selected_patron.type in job.allowed_patrons))
+ if(length(job.allowed_patrons) && !(user.client.prefs.selected_patron in job.allowed_patrons))
var/list/patron_list = list()
for(var/mult_patron in job.allowed_patrons)
var/datum/patron/P = new mult_patron
diff --git a/code/modules/client/preferences/preferences_savefile.dm b/code/modules/client/preferences/preferences_savefile.dm
index af86bf8c5e6..cb2e73f248c 100644
--- a/code/modules/client/preferences/preferences_savefile.dm
+++ b/code/modules/client/preferences/preferences_savefile.dm
@@ -373,11 +373,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
var/patron_typepath
S["selected_patron"] >> patron_typepath
- if(patron_typepath)
- selected_patron = GLOB.patron_list[patron_typepath]
if(!selected_patron) //failsafe
- selected_patron = GLOB.patron_list[default_patron]
+ selected_patron = default_patron
//Custom names
for(var/custom_name_id in GLOB.preferences_custom_names)
@@ -509,7 +507,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["job_preferences"], job_preferences)
//Patron
- WRITE_FILE(S["selected_patron"], selected_patron.type)
+ WRITE_FILE(S["selected_patron"], selected_patron)
// Organs
WRITE_FILE(S["customizer_entries"], customizer_entries)
diff --git a/code/modules/error_handler/error_handler.dm b/code/modules/error_handler/error_handler.dm
index 2a22d709b3d..64266f95e58 100644
--- a/code/modules/error_handler/error_handler.dm
+++ b/code/modules/error_handler/error_handler.dm
@@ -50,11 +50,6 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
Reboot(reason = 1)
return
- if (islist(stack_trace_storage))
- for (var/line in splittext(E.desc, "\n"))
- if (text2ascii(line) != 32)
- stack_trace_storage += line
-
var/static/list/error_last_seen = list()
var/static/list/error_cooldown = list() /* Error_cooldown items will either be positive(cooldown time) or negative(silenced error)
If negative, starts at -1, and goes down by 1 each time that error gets skipped*/
diff --git a/code/modules/inquisitor_supplies/_base.dm b/code/modules/inquisitor_supplies/_base.dm
index 33bf8c62746..57339a84534 100644
--- a/code/modules/inquisitor_supplies/_base.dm
+++ b/code/modules/inquisitor_supplies/_base.dm
@@ -1,6 +1,3 @@
-GLOBAL_LIST_EMPTY(inqsupplies)
-
-
/datum/inqports
var/name = null
var/item_type = null
diff --git a/code/modules/mob/dead/new_player/sprite_accessories.dm b/code/modules/mob/dead/new_player/sprite_accessories.dm
index 5d1c82801d6..b05973af741 100644
--- a/code/modules/mob/dead/new_player/sprite_accessories.dm
+++ b/code/modules/mob/dead/new_player/sprite_accessories.dm
@@ -16,34 +16,33 @@
from doing this unless you absolutely know what you are doing, and have defined a
conversion in savefile.dm
*/
-/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female, roundstart = FALSE, female_same = FALSE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
- if(!istype(L))
- L = list()
- if(!istype(male))
- male = list()
- if(!istype(female))
- female = list()
+
+/proc/init_sprite_accessory_subtypes(prototype)
+ var/list/all_accessories = list(
+ DEFAULT_SPRITE_LIST = list(),
+ MALE_SPRITE_LIST = list(),
+ FEMALE_SPRITE_LIST = list(),
+ )
for(var/datum/sprite_accessory/accessory as anything in subtypesof(prototype))
if(IS_ABSTRACT(accessory))
continue
- accessory = new accessory()
-
- if(roundstart && accessory.locked)
- continue
-
- L[accessory.name] = accessory
-
- if(accessory.gender == MALE)
- male[accessory.name] = accessory
- if(female_same)
- female[accessory.name] = accessory
- else if(accessory.gender == FEMALE)
- female[accessory.name] = accessory
+ if(accessory::icon_state)
+ all_accessories[DEFAULT_SPRITE_LIST][accessory::name] = new accessory()
else
- male[accessory.name] = accessory
- female[accessory.name] = accessory
+ all_accessories[DEFAULT_SPRITE_LIST] += accessory::name
+
+ switch(accessory::gender)
+ if(MALE)
+ all_accessories[MALE_SPRITE_LIST] += accessory::name
+ if(FEMALE)
+ all_accessories[FEMALE_SPRITE_LIST] += accessory::name
+ else
+ all_accessories[MALE_SPRITE_LIST] += accessory::name
+ all_accessories[FEMALE_SPRITE_LIST] += accessory::name
+
+ return all_accessories
/datum/sprite_accessory
var/use_static //determines if the accessory will be skipped by color preferences
diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm
index 1f212f48ac0..8b516de6e91 100644
--- a/code/modules/mob/living/carbon/human/species.dm
+++ b/code/modules/mob/living/carbon/human/species.dm
@@ -1,6 +1,3 @@
-// This code handles different species in the game.
-GLOBAL_LIST_EMPTY(roundstart_species)
-
/datum/species
/// The name used for examine text and so on
var/name
@@ -477,10 +474,7 @@ GLOBAL_LIST_EMPTY(roundstart_species)
return " [pick(possible_surnames)]"
/datum/species/proc/get_spec_undies_list(gender)
- if(!GLOB.underwear_list.len)
- init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f)
-
- var/list/used_list = GLOB.underwear_list
+ var/list/used_list
if(gender == MALE)
used_list = GLOB.underwear_m
else if(gender == FEMALE)
@@ -490,7 +484,9 @@ GLOBAL_LIST_EMPTY(roundstart_species)
var/list/spec_undies = list()
for(var/name in used_list)
- var/datum/sprite_accessory/accessory = used_list[name]
+ var/datum/sprite_accessory/accessory = GLOB.underwear_list[name]
+ if(!accessory)
+ continue
if(!accessory.roundstart)
continue
if(!(used_species_id in accessory.specuse))
diff --git a/code/ze_genesis_call/genesis_call.dm b/code/ze_genesis_call/genesis_call.dm
new file mode 100644
index 00000000000..28d9e510aa4
--- /dev/null
+++ b/code/ze_genesis_call/genesis_call.dm
@@ -0,0 +1,48 @@
+/*
+ You look around.
+
+ There is nothing but naught about you.
+
+ You've come to the end of the world.
+
+ You get a feeling that you really shouldn't be here.
+
+ Ever.
+
+ But with all ends come beginnings.
+
+ As you turn to leave, you spot it out of the corner of your eye.
+
+ Your eye widen in wonder as you look upon the the legendary treasure.
+
+ After all these years of pouring through shitcode
+ your endevours have brought you to...
+*/
+
+/**
+ * THE GENESIS CALL
+ *
+ * THE VERY FIRST LINE OF DM CODE TO EXECUTE
+ * Ong this must be done after !!!EVERYTHING!!! else
+ * NO IFS ANDS OR BUTS
+ * it's a hack, not an example of any sort, and DEFINITELY should NOT be emulated
+ * IT JUST HAS TO BE LAST!!!!!!
+ * If you want to do something in the initialization pipeline
+ * FIRST RTFM IN /code/game/world.dm
+ * AND THEN NEVER RETURN TO THIS PLACE
+ *
+ * If you're still here, here's an explanation:
+ * BYOND loves to tell you about its loving spouse /global
+ * But it's actually having a sexy an affair with /static
+ * Specifically statics in procs
+ * Priority is given to these lines of code in REVERSE order of declaration in the .dme
+ * Which is why this file has a funky name
+ * So this is what we use to call world.Genesis()
+ * It's a nameless, no-op function, because it does absolutely nothing
+ * It exists to hold a static var which is initialized to null
+ * It's on /world to hide it from reflection
+ * Painful right? Good, now you share my suffering
+ * Please lock the door on your way out
+ */
+/world/proc/_()
+ var/static/_ = world.Genesis()
diff --git a/vanderlin.dme b/vanderlin.dme
index a63e675b469..0ed892230e8 100644
--- a/vanderlin.dme
+++ b/vanderlin.dme
@@ -338,15 +338,19 @@
#include "code\_globalvars\lists\ambience.dm"
#include "code\_globalvars\lists\bounties.dm"
#include "code\_globalvars\lists\client.dm"
+#include "code\_globalvars\lists\emote.dm"
#include "code\_globalvars\lists\flavor_misc.dm"
#include "code\_globalvars\lists\icons.dm"
+#include "code\_globalvars\lists\import.dm"
#include "code\_globalvars\lists\keybindings.dm"
#include "code\_globalvars\lists\mapping.dm"
#include "code\_globalvars\lists\mobs.dm"
#include "code\_globalvars\lists\names.dm"
#include "code\_globalvars\lists\objects.dm"
#include "code\_globalvars\lists\particles.dm"
+#include "code\_globalvars\lists\patron.dm"
#include "code\_globalvars\lists\poll_ignore.dm"
+#include "code\_globalvars\lists\preferences.dm"
#include "code\_globalvars\lists\slapcrafting.dm"
#include "code\_globalvars\lists\speech.dm"
#include "code\_globalvars\lists\surgery.dm"
@@ -3786,6 +3790,7 @@
#include "code\native_say\say_channels\me.dm"
#include "code\native_say\say_channels\ooc.dm"
#include "code\native_say\say_channels\say.dm"
+#include "code\ze_genesis_call\genesis_call.dm"
#include "interface\interface.dm"
#include "interface\menu.dm"
#include "interface\stylesheet.dm"
From 589e31a822d6265f14c0d34c21639b246f670a52 Mon Sep 17 00:00:00 2001
From: CheffieGithub <113442598+CheffieGithub@users.noreply.github.com>
Date: Sun, 22 Feb 2026 14:43:18 +0000
Subject: [PATCH 2/5] world.dm
---
code/world.dm | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/code/world.dm b/code/world.dm
index b5af052dc71..9bc71ede5d0 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -1,14 +1,12 @@
//This file is just for the necessary /world definition
-//Try looking in game/world.dm
+//Try looking in /code/game/world.dm, where initialization order is defined
/**
- * # WorldR
+ * # World
*
* Two possibilities exist: either we are alone in the Universe or we are not. Both are equally terrifying. ~ Arthur C. Clarke
*
* The byond world object stores some basic byond level config, and has a few hub specific procs for managing hub visiblity
- *
- * The world /New() is the root of where a round itself begins
*/
/world
mob = /mob/dead/new_player
From 21c06c99638b5053ac3fa46153b33b9657d4a99b Mon Sep 17 00:00:00 2001
From: CheffieGithub <113442598+CheffieGithub@users.noreply.github.com>
Date: Sun, 22 Feb 2026 16:21:14 +0000
Subject: [PATCH 3/5] profiler and logs
---
code/__DEFINES/_tick.dm | 12 +-
code/__DEFINES/subsystems.dm | 3 +
code/__DEFINES/tracy.dm | 4 +-
code/__HELPERS/_logging.dm | 15 +-
code/__HELPERS/roundend.dm | 2 -
code/_compile_options.dm | 5 +
code/_globalvars/logging.dm | 146 ++++++++++-------
.../configuration/configuration.dm | 5 +
.../configuration/entries/general.dm | 11 +-
code/controllers/subsystem/profiler.dm | 70 ++++++++
code/controllers/subsystem/statpanel.dm | 21 ++-
code/controllers/subsystem/ticker.dm | 2 +-
code/controllers/subsystem/time_track.dm | 74 ++++++++-
code/game/world.dm | 154 ++++++------------
code/modules/admin/admin_verbs.dm | 43 +++--
code/modules/admin/verbs/mapping.dm | 38 -----
code/modules/debugging/debugger.dm | 55 +++++++
code/modules/debugging/tracy.dm | 64 ++++++++
code/modules/unit_tests/unit_test.dm | 1 -
vanderlin.dme | 3 +
20 files changed, 482 insertions(+), 246 deletions(-)
create mode 100644 code/controllers/subsystem/profiler.dm
create mode 100644 code/modules/debugging/debugger.dm
create mode 100644 code/modules/debugging/tracy.dm
diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm
index 852d1e560a0..e4ade94c20f 100644
--- a/code/__DEFINES/_tick.dm
+++ b/code/__DEFINES/_tick.dm
@@ -1,3 +1,5 @@
+#define MAPTICK_LAST_INTERNAL_TICK_USAGE (world.map_cpu)
+
/// Tick limit while running normally
#define TICK_LIMIT_RUNNING 80
/// Tick limit used to resume things in stoplag
@@ -22,12 +24,12 @@
///like TICK_CHECK but for half the budget
#define TICK_CHECK_LOW ( TICK_USAGE > (Master.current_ticklimit * 0.5))
-/// Returns true if tick usage is above 95, for high priority usage
-#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
-/// runs stoplag if tick_usage is above 95, for high priority usage
-#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
-
/// Checks if a sleeping proc is running before or after the master controller
#define RUNNING_BEFORE_MASTER ( Master.last_run != null && Master.last_run != world.time )
/// Returns true if a verb ought to yield to the MC (IE: queue up to be processed by a subsystem)
#define VERB_SHOULD_YIELD ( TICK_CHECK || RUNNING_BEFORE_MASTER )
+
+/// Returns true if tick usage is above 95, for high priority usage
+#define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 )
+/// runs stoplag if tick_usage is above 95, for high priority usage
+#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 )
diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 3ebd905dea8..bf55ef32441 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -100,6 +100,8 @@
// Subsystem init_order, from highest priority to lowest priority
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
+
+#define INIT_ORDER_PROFILER 101
#define INIT_ORDER_ASSETS 101
#define INIT_ORDER_TITLE 100
#define INIT_ORDER_GARBAGE 99
@@ -128,6 +130,7 @@
#define INIT_ORDER_TERRAIN 49
#define INIT_ORDER_DUNGEON 48
#define INIT_ORDER_NETWORKS 45
+#define INIT_ORDER_TIMETRACK 44
#define INIT_ORDER_SPATIAL_GRID 43
#define INIT_ORDER_ECONOMY 40
#define INIT_ORDER_OUTPUTS 35
diff --git a/code/__DEFINES/tracy.dm b/code/__DEFINES/tracy.dm
index a622428b528..02f98fe5d5e 100644
--- a/code/__DEFINES/tracy.dm
+++ b/code/__DEFINES/tracy.dm
@@ -1,5 +1,5 @@
/// File path used for the "enable tracy next round" functionality
/// The server port is appended to the end of the filename to avoid conflicts if multiple servers share the same data folder.
-#define TRACY_ENABLE_PATH "data/enable_tracy.[world.port]"
+#define TRACY_ENABLE_PATH "data/enable_tracy.[world.port]"
/// The DLL path for byond-tracy.
-#define TRACY_DLL_PATH (world.system_type == MS_WINDOWS ? "prof.dll" : "./libprof.so")
+#define TRACY_DLL_PATH (world.system_type == MS_WINDOWS ? "prof.dll" : "./libprof.so")
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index bfb95e89d9d..b250cce9a8d 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -4,7 +4,11 @@
#define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound)
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
-#define WRITE_LOG(log, text) text2file(text,log) //rustg_log_write
+
+//This is an external call, "true" and "false" are how rust parses out booleans
+#define WRITE_LOG(log, text) rustg_log_write(log, text, "true")
+#define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false")
+
#define TIMETOTEXT4LOGS time2text(world.timeofday,"hh:mm:ss")
//print a warning message to world.log
#define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].")
@@ -78,10 +82,6 @@
if (CONFIG_GET(flag/log_virus))
WRITE_LOG(GLOB.world_virus_log, "\[[TIMETOTEXT4LOGS]\] VIRUS: [text]")
-/proc/log_cloning(text, mob/initiator)
- if(CONFIG_GET(flag/log_cloning))
- WRITE_LOG(GLOB.world_cloning_log, "\[[TIMETOTEXT4LOGS]\] CLONING: [text]")
-
/proc/log_paper(text)
WRITE_LOG(GLOB.world_paper_log, "\[[TIMETOTEXT4LOGS]\] PAPER: [text]")
@@ -215,6 +215,11 @@
/proc/log_hunted(text)
WRITE_LOG(GLOB.hunted_log, text)
+/// Logging for game performance
+/proc/log_perf(list/perf_info)
+ . = "[perf_info.Join(",")]\n"
+ WRITE_LOG_NO_FORMAT(GLOB.perf_log, .)
+
/* ui logging */
/**
* Appends a tgui-related log entry. All arguments are optional.
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 4d680b0beda..507633f4ea7 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -113,8 +113,6 @@
log_game("The round has ended.")
- INVOKE_ASYNC(world, TYPE_PROC_REF(/world, flush_byond_tracy))
-
to_chat(world, "
So ends this tale of Vanderlin.")
get_end_reason()
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index 2433ea31231..277d9aaaad4 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -26,6 +26,11 @@
#define USE_CUSTOM_ERROR_HANDLER
#endif
+#if defined(OPENDREAM) && !defined(SPACEMAN_DMM) && !defined(CIBUILDING)
+// The code is being compiled for OpenDream, and not just for the CI linting.
+#define OPENDREAM_REAL
+#endif
+
#ifdef TESTING
#define DATUMVAR_DEBUGGING_MODE
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 97efb8192ac..7754e91ee09 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -1,62 +1,87 @@
-GLOBAL_VAR(log_directory)
-GLOBAL_PROTECT(log_directory)
-GLOBAL_VAR(world_game_log)
-GLOBAL_PROTECT(world_game_log)
-GLOBAL_VAR(world_runtime_log)
-GLOBAL_PROTECT(world_runtime_log)
-GLOBAL_VAR(world_qdel_log)
-GLOBAL_PROTECT(world_qdel_log)
-GLOBAL_VAR(world_attack_log)
-GLOBAL_PROTECT(world_attack_log)
-GLOBAL_VAR(world_href_log)
-GLOBAL_PROTECT(world_href_log)
GLOBAL_VAR(round_id)
GLOBAL_PROTECT(round_id)
-GLOBAL_VAR(config_error_log)
-GLOBAL_PROTECT(config_error_log)
-GLOBAL_VAR(sql_error_log)
-GLOBAL_PROTECT(sql_error_log)
-GLOBAL_VAR(world_pda_log)
-GLOBAL_PROTECT(world_pda_log)
-GLOBAL_VAR(world_telecomms_log)
-GLOBAL_PROTECT(world_telecomms_log)
-GLOBAL_VAR(world_manifest_log)
-GLOBAL_PROTECT(world_manifest_log)
-GLOBAL_VAR(query_debug_log)
-GLOBAL_PROTECT(query_debug_log)
-GLOBAL_VAR(world_job_debug_log)
-GLOBAL_PROTECT(world_job_debug_log)
-GLOBAL_VAR(world_mecha_log)
-GLOBAL_PROTECT(world_mecha_log)
-GLOBAL_VAR(world_virus_log)
-GLOBAL_PROTECT(world_virus_log)
-GLOBAL_VAR(world_asset_log)
-GLOBAL_PROTECT(world_asset_log)
-GLOBAL_VAR(world_cloning_log)
-GLOBAL_PROTECT(world_cloning_log)
-GLOBAL_VAR(world_map_error_log)
-GLOBAL_PROTECT(world_map_error_log)
-GLOBAL_VAR(world_paper_log)
-GLOBAL_PROTECT(world_paper_log)
-GLOBAL_VAR(tgui_log)
-GLOBAL_PROTECT(tgui_log)
-GLOBAL_VAR(character_list_log)
-GLOBAL_PROTECT(character_list_log)
-GLOBAL_VAR(hunted_log)
-GLOBAL_PROTECT(hunted_log)
-GLOBAL_LIST_EMPTY(character_list)
-GLOBAL_LIST_EMPTY(character_ckey_list)
-GLOBAL_LIST_EMPTY(actors_list)
-GLOBAL_VAR(rogue_round_id)
+/// The directory in which ALL log files should be stored
+GLOBAL_VAR(log_directory)
+GLOBAL_PROTECT(log_directory)
+
+#define DECLARE_LOG_NAMED(log_var_name, log_file_name, start)\
+GLOBAL_VAR(##log_var_name);\
+GLOBAL_PROTECT(##log_var_name);\
+/world/_initialize_log_files(temp_log_override = null){\
+ ..();\
+ GLOB.##log_var_name = temp_log_override || "[GLOB.log_directory]/[##log_file_name].log";\
+ if(!temp_log_override && ##start){\
+ start_log(GLOB.##log_var_name);\
+ }\
+}
+
+#define DECLARE_LOG(log_name, start) DECLARE_LOG_NAMED(##log_name, "[copytext(#log_name, 1, length(#log_name) - 4)]", start)
+#define START_LOG TRUE
+#define DONT_START_LOG FALSE
+
+/// Populated by log declaration macros to set log file names and start messages
+/world/proc/_initialize_log_files(temp_log_override = null)
+ // Needs to be here to avoid compiler warnings
+ SHOULD_CALL_PARENT(TRUE)
+ return
+
+// All individual log files
+DECLARE_LOG(config_error_log, DONT_START_LOG)
+DECLARE_LOG(dynamic_log, DONT_START_LOG)
+DECLARE_LOG(lua_log, DONT_START_LOG)
+DECLARE_LOG(perf_log, DONT_START_LOG) // Declared here but name is set in time_track subsystem
+DECLARE_LOG(query_debug_log, DONT_START_LOG)
+DECLARE_LOG(signals_log, DONT_START_LOG)
+DECLARE_LOG(tgui_log, START_LOG)
+#ifdef REFERENCE_DOING_IT_LIVE
+DECLARE_LOG_NAMED(harddel_log, "harddels", START_LOG)
+#endif
+#if defined(UNIT_TESTS) || defined(SPACEMAN_DMM)
+DECLARE_LOG_NAMED(test_log, "tests", START_LOG)
+#endif
+DECLARE_LOG_NAMED(filter_log, "filters", DONT_START_LOG)
+DECLARE_LOG_NAMED(sql_error_log, "sql", DONT_START_LOG)
+DECLARE_LOG_NAMED(character_list_log, "chracter", DONT_START_LOG)
+DECLARE_LOG_NAMED(hunted_log, "hunted", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_asset_log, "asset", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_attack_log, "attack", START_LOG)
+DECLARE_LOG_NAMED(world_econ_log, "econ", START_LOG)
+DECLARE_LOG_NAMED(world_game_log, "game", START_LOG)
+DECLARE_LOG_NAMED(world_href_log, "hrefs", START_LOG)
+DECLARE_LOG_NAMED(world_job_debug_log, "job_debug", START_LOG)
+DECLARE_LOG_NAMED(world_manifest_log, "manifest", START_LOG)
+DECLARE_LOG_NAMED(world_map_error_log, "map_errors", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_mecha_log, "mecha", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_mob_tag_log, "mob_tags", START_LOG)
+DECLARE_LOG_NAMED(world_paper_log, "paper", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_pda_log, "pda", START_LOG)
+DECLARE_LOG_NAMED(world_qdel_log, "qdel", START_LOG)
+DECLARE_LOG_NAMED(world_runtime_log, "runtime", START_LOG)
+DECLARE_LOG_NAMED(world_shuttle_log, "shuttle", START_LOG)
+DECLARE_LOG_NAMED(world_silicon_log, "silicon", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_speech_indicators_log, "speech_indicators", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_telecomms_log, "telecomms", START_LOG)
+DECLARE_LOG_NAMED(world_tool_log, "tools", DONT_START_LOG)
+DECLARE_LOG_NAMED(world_uplink_log, "uplink", START_LOG)
+DECLARE_LOG_NAMED(world_virus_log, "virus", DONT_START_LOG)
+/// Log associated with [/proc/log_suspicious_login()]
+/// Intended to hold all logins that failed due to suspicious circumstances such as ban detection, CID randomisation etc.
+DECLARE_LOG_NAMED(world_suspicious_login_log, "suspicious_logins", DONT_START_LOG)
-GLOBAL_LIST_EMPTY(bombers)
-GLOBAL_PROTECT(bombers)
GLOBAL_LIST_EMPTY(admin_log)
GLOBAL_PROTECT(admin_log)
-GLOBAL_LIST_EMPTY(lastsignalers) //keeps last 100 signals here in format: "[src] used [REF(src)] @ location [src.loc]: [freq]/[code]"
+
+/// All bomb related messages
+GLOBAL_LIST_EMPTY(bombers)
+GLOBAL_PROTECT(bombers)
+
+/// All signals here in format: "[src] used [REF(src)] @ location [src.loc]: [freq]/[code]"
+GLOBAL_LIST_EMPTY(lastsignalers)
GLOBAL_PROTECT(lastsignalers)
-GLOBAL_LIST_EMPTY(lawchanges) //Stores who uploaded laws to which silicon-based lifeform, and what the law was
+
+/// Stores who uploaded laws to which silicon-based lifeform, and what the law was
+GLOBAL_LIST_EMPTY(lawchanges)
GLOBAL_PROTECT(lawchanges)
GLOBAL_LIST_EMPTY(combatlog)
@@ -68,8 +93,6 @@ GLOBAL_PROTECT(OOClog)
GLOBAL_LIST_EMPTY(adminlog)
GLOBAL_PROTECT(adminlog)
-GLOBAL_LIST_EMPTY(active_turfs_startlist)
-
/////Picture logging
GLOBAL_VAR(picture_log_directory)
GLOBAL_PROTECT(picture_log_directory)
@@ -78,9 +101,14 @@ GLOBAL_VAR_INIT(picture_logging_id, 1)
GLOBAL_PROTECT(picture_logging_id)
GLOBAL_VAR(picture_logging_prefix)
GLOBAL_PROTECT(picture_logging_prefix)
-/////
-#ifdef REFERENCE_DOING_IT_LIVE
-GLOBAL_LIST_EMPTY(harddel_log)
-GLOBAL_PROTECT(harddel_log)
-#endif
+GLOBAL_VAR(rogue_round_id)
+
+GLOBAL_LIST_EMPTY(character_list)
+GLOBAL_LIST_EMPTY(character_ckey_list)
+GLOBAL_LIST_EMPTY(actors_list)
+
+#undef DECLARE_LOG
+#undef DECLARE_LOG_NAMED
+#undef START_LOG
+#undef DONT_START_LOG
diff --git a/code/controllers/configuration/configuration.dm b/code/controllers/configuration/configuration.dm
index 1b8cb4fdff2..9b9387331b3 100644
--- a/code/controllers/configuration/configuration.dm
+++ b/code/controllers/configuration/configuration.dm
@@ -21,6 +21,9 @@
var/motd
var/policy
+ /// If the configuration is loaded
+ var/loaded = FALSE
+
var/static/regex/ic_filter_regex
/datum/controller/configuration/proc/admin_reload()
@@ -56,6 +59,8 @@
LoadChatFilter()
LoadRelays()
+ loaded = TRUE
+
if(Master)
Master.OnConfigLoad()
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 3efddbba728..58fb9b7b1f6 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -64,8 +64,6 @@
/datum/config_entry/flag/log_virus // log virology data
-/datum/config_entry/flag/log_cloning // log cloning actions.
-
/datum/config_entry/flag/log_vote // log voting
/datum/config_entry/flag/log_whisper // log client whisper
@@ -523,3 +521,12 @@
*/
/datum/config_entry/number/tgui_max_chunk_count
default = 32
+
+/datum/config_entry/flag/auto_profile
+
+/datum/config_entry/number/profiler_interval
+ default = 300 SECONDS
+
+/datum/config_entry/flag/forbid_all_profiling
+
+/datum/config_entry/flag/forbid_admin_profiling
diff --git a/code/controllers/subsystem/profiler.dm b/code/controllers/subsystem/profiler.dm
new file mode 100644
index 00000000000..b902d1a8ece
--- /dev/null
+++ b/code/controllers/subsystem/profiler.dm
@@ -0,0 +1,70 @@
+SUBSYSTEM_DEF(profiler)
+ name = "Profiler"
+ init_order = INIT_ORDER_PROFILER
+ runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
+ wait = 300 SECONDS
+ var/fetch_cost = 0
+ var/write_cost = 0
+
+/datum/controller/subsystem/profiler/stat_entry(msg)
+ msg += "F:[round(fetch_cost,1)]ms"
+ msg += "|W:[round(write_cost,1)]ms"
+ return msg
+
+/datum/controller/subsystem/profiler/Initialize()
+ if(CONFIG_GET(flag/auto_profile))
+ StartProfiling()
+ else
+ StopProfiling() //Stop the early start profiler
+ wait = CONFIG_GET(number/profiler_interval)
+ return ..()
+
+/datum/controller/subsystem/profiler/OnConfigLoad()
+ if(CONFIG_GET(flag/auto_profile))
+ StartProfiling()
+ can_fire = TRUE
+ else
+ StopProfiling()
+ can_fire = FALSE
+
+/datum/controller/subsystem/profiler/fire()
+ DumpFile()
+
+/datum/controller/subsystem/profiler/Shutdown()
+ if(CONFIG_GET(flag/auto_profile))
+ DumpFile(allow_yield = FALSE)
+ world.Profile(PROFILE_CLEAR, type = "sendmaps")
+ return ..()
+
+/datum/controller/subsystem/profiler/proc/StartProfiling()
+ world.Profile(PROFILE_START)
+ world.Profile(PROFILE_START, type = "sendmaps")
+
+/datum/controller/subsystem/profiler/proc/StopProfiling()
+ world.Profile(PROFILE_STOP)
+ world.Profile(PROFILE_STOP, type = "sendmaps")
+
+/datum/controller/subsystem/profiler/proc/DumpFile(allow_yield = TRUE)
+ var/timer = TICK_USAGE_REAL
+ var/current_profile_data = world.Profile(PROFILE_REFRESH, format = "json")
+ var/current_sendmaps_data = world.Profile(PROFILE_REFRESH, type = "sendmaps", format="json")
+ fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+ if(allow_yield)
+ CHECK_TICK
+
+ if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, profiling stopped manually before dump.")
+ var/prof_file = file("[GLOB.log_directory]/profiler/profiler-[round(world.time * 0.1, 10)].json")
+ if(fexists(prof_file))
+ fdel(prof_file)
+ if(!length(current_sendmaps_data)) //Would be nice to have explicit proc to check this
+ stack_trace("Warning, sendmaps profiling stopped manually before dump.")
+ var/sendmaps_file = file("[GLOB.log_directory]/profiler/sendmaps-[round(world.time * 0.1, 10)].json")
+ if(fexists(sendmaps_file))
+ fdel(sendmaps_file)
+
+ timer = TICK_USAGE_REAL
+ WRITE_FILE(prof_file, current_profile_data)
+ WRITE_FILE(sendmaps_file, current_sendmaps_data)
+ write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
+
diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm
index e25171e18e0..950805efa48 100644
--- a/code/controllers/subsystem/statpanel.dm
+++ b/code/controllers/subsystem/statpanel.dm
@@ -165,12 +165,31 @@ SUBSYSTEM_DEF(statpanels)
list("", "World Time:", "[world.time]"),
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)]%))\n (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
+ 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)]%))\n (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)"),
list("", "Master Controller:", Master.stat_entry(), text_ref(Master)),
list("", "Failsafe Controller:", Failsafe.stat_entry(), text_ref(Failsafe)),
list("", "", "")
)
+#if defined(MC_TAB_TRACY_INFO) || defined(SPACEMAN_DMM)
+ var/static/tracy_dll
+ var/static/tracy_present
+ if(isnull(tracy_dll))
+ tracy_dll = TRACY_DLL_PATH
+ tracy_present = fexists(tracy_dll)
+ if(tracy_present)
+ if(Tracy.enabled)
+ mc_data.Insert(2, list(list("byond-tracy:", "Active (reason: [Tracy.init_reason || "N/A"])")))
+ else if(Tracy.error)
+ mc_data.Insert(2, list(list("byond-tracy:", "Errored ([Tracy.error])")))
+ else if(fexists(TRACY_ENABLE_PATH))
+ mc_data.Insert(2, list(list("byond-tracy:", "Queued for next round")))
+ else
+ mc_data.Insert(2, list(list("byond-tracy:", "Inactive")))
+ else
+ mc_data.Insert(2, list(list("byond-tracy:", "[tracy_dll] not present")))
+#endif
+
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(), text_ref(sub_system))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index eab98c59c2c..7457737efa5 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -394,7 +394,7 @@ SUBSYSTEM_DEF(ticker)
*/
PostSetup()
- INVOKE_ASYNC(world, TYPE_PROC_REF(/world, flush_byond_tracy))
+
log_game("GAME SETUP: postsetup success")
return TRUE
diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm
index acc817ad1f3..eed02cd1ca3 100644
--- a/code/controllers/subsystem/time_track.dm
+++ b/code/controllers/subsystem/time_track.dm
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(time_track)
name = "Time Tracking"
wait = 100
- flags = SS_NO_INIT|SS_NO_TICK_CHECK
+ init_order = INIT_ORDER_TIMETRACK
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/time_dilation_current = 0
@@ -15,6 +15,53 @@ SUBSYSTEM_DEF(time_track)
var/last_tick_realtime = 0
var/last_tick_byond_time = 0
var/last_tick_tickcount = 0
+ var/list/sendmaps_names_map = list(
+ "SendMaps" = "send_maps",
+ "SendMaps: Initial housekeeping" = "initial_house",
+ "SendMaps: Cleanup" = "cleanup",
+ "SendMaps: Client loop" = "client_loop",
+ "SendMaps: Per client" = "per_client",
+ "SendMaps: Per client: Deleted images" = "deleted_images",
+ "SendMaps: Per client: HUD update" = "hud_update",
+ "SendMaps: Per client: Statpanel update" = "statpanel_update",
+ "SendMaps: Per client: Map data" = "map_data",
+ "SendMaps: Per client: Map data: Check eye position" = "check_eye_pos",
+ "SendMaps: Per client: Map data: Update chunks" = "update_chunks",
+ "SendMaps: Per client: Map data: Send turfmap updates" = "turfmap_updates",
+ "SendMaps: Per client: Map data: Send changed turfs" = "changed_turfs",
+ "SendMaps: Per client: Map data: Send turf chunk info" = "turf_chunk_info",
+ "SendMaps: Per client: Map data: Send obj changes" = "obj_changes",
+ "SendMaps: Per client: Map data: Send mob changes" = "mob_changes",
+ "SendMaps: Per client: Map data: Send notable turf visual contents" = "send_turf_vis_conts",
+ "SendMaps: Per client: Map data: Send pending animations" = "pending_animations",
+ "SendMaps: Per client: Map data: Look for movable changes" = "look_for_movable_changes",
+ "SendMaps: Per client: Map data: Look for movable changes: Check notable turf visual contents" = "check_turf_vis_conts",
+ "SendMaps: Per client: Map data: Look for movable changes: Check HUD/image visual contents" = "check_hud/image_vis_contents",
+ "SendMaps: Per client: Map data: Look for movable changes: Loop through turfs in range" = "turfs_in_range",
+ "SendMaps: Per client: Map data: Look for movable changes: Movables examined" = "movables_examined",
+ )
+
+/datum/controller/subsystem/time_track/Initialize(start_timeofday)
+ . = ..()
+ GLOB.perf_log = "[GLOB.log_directory]/perf-[GLOB.rogue_round_id ? GLOB.rogue_round_id : "NULL"]-[SSmapping.config?.map_name].csv"
+ world.Profile(PROFILE_RESTART, type = "sendmaps")
+ //Need to do the sendmaps stuff in its own file, since it works different then everything else
+ var/list/sendmaps_shorthands = list()
+ for(var/proper_name in sendmaps_names_map)
+ sendmaps_shorthands += sendmaps_names_map[proper_name]
+ sendmaps_shorthands += "[sendmaps_names_map[proper_name]]_count"
+ log_perf(
+ list(
+ "time",
+ "players",
+ "tidi",
+ "tidi_fastavg",
+ "tidi_avg",
+ "tidi_slowavg",
+ "maptick",
+ "num_timers",
+ ) + sendmaps_shorthands
+ )
/datum/controller/subsystem/time_track/fire()
@@ -36,4 +83,29 @@ SUBSYSTEM_DEF(time_track)
last_tick_realtime = current_realtime
last_tick_byond_time = current_byondtime
last_tick_tickcount = current_tickcount
+
+ var/sendmaps_json = world.Profile(PROFILE_REFRESH, type = "sendmaps", format="json")
+ var/list/send_maps_data = json_decode(sendmaps_json)
+ var/send_maps_sort = send_maps_data.Copy() //Doing it like this guarentees us a properly sorted list
+
+ for(var/list/packet in send_maps_data)
+ send_maps_sort[packet["name"]] = packet
+
+ var/list/send_maps_values = list()
+ for(var/list/packet in send_maps_sort)
+ send_maps_values += packet["value"]
+ send_maps_values += packet["calls"]
+
SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[SQLtime()]" = list("current" = "[time_dilation_current]", "avg_fast" = "[time_dilation_avg_fast]", "avg" = "[time_dilation_avg]", "avg_slow" = "[time_dilation_avg_slow]")))
+ log_perf(
+ list(
+ world.time,
+ length(GLOB.clients),
+ time_dilation_current,
+ time_dilation_avg_fast,
+ time_dilation_avg,
+ time_dilation_avg_slow,
+ MAPTICK_LAST_INTERNAL_TICK_USAGE,
+ length(SStimer.timer_id_dict),
+ ) + send_maps_values
+ )
diff --git a/code/game/world.dm b/code/game/world.dm
index 143b321d816..7d2b166f8d3 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -3,14 +3,6 @@
#define USE_TRACY_PARAMETER "tracy"
GLOBAL_VAR(restart_counter)
-GLOBAL_VAR(tracy_log)
-GLOBAL_PROTECT(tracy_log)
-GLOBAL_VAR(tracy_initialized)
-GLOBAL_PROTECT(tracy_initialized)
-GLOBAL_VAR(tracy_init_error)
-GLOBAL_PROTECT(tracy_init_error)
-GLOBAL_VAR(tracy_init_reason)
-GLOBAL_PROTECT(tracy_init_reason)
/**
* WORLD INITIALIZATION
@@ -65,29 +57,33 @@ GLOBAL_PROTECT(tracy_init_reason)
RETURN_TYPE(/datum/controller/master)
if(!tracy_initialized)
- GLOB.tracy_initialized = FALSE
-#ifndef OPENDREAM
- if(!tracy_initialized)
+ Tracy = new
#ifdef USE_BYOND_TRACY
-#warn USE_BYOND_TRACY is enabled
- var/should_init_tracy = TRUE
- GLOB.tracy_init_reason = "USE_BYOND_TRACY defined"
+ if(Tracy.enable("USE_BYOND_TRACY defined"))
+ Genesis(tracy_initialized = TRUE)
+ return
#else
- var/should_init_tracy = FALSE
+ var/tracy_enable_reason
if(USE_TRACY_PARAMETER in params)
- should_init_tracy = TRUE
- GLOB.tracy_init_reason = "world.params"
+ tracy_enable_reason = "world.params"
if(fexists(TRACY_ENABLE_PATH))
- GLOB.tracy_init_reason ||= "enabled for round"
+ tracy_enable_reason ||= "enabled for round"
SEND_TEXT(world.log, "[TRACY_ENABLE_PATH] exists, initializing byond-tracy!")
- should_init_tracy = TRUE
fdel(TRACY_ENABLE_PATH)
-#endif
- if(should_init_tracy)
- init_byond_tracy()
+ if(!isnull(tracy_enable_reason) && Tracy.enable(tracy_enable_reason))
Genesis(tracy_initialized = TRUE)
return
#endif
+
+ Profile(PROFILE_RESTART)
+ Profile(PROFILE_RESTART, type = "sendmaps")
+
+ // Write everything to this log file until we get to SetupLogs() later
+ _initialize_log_files("data/logs/config_error.[GUID()].log")
+
+ // Init the debugger first so we can debug Master
+ // Debugger = new
+
// THAT'S IT, WE'RE DONE, THE. FUCKING. END.
Master = new
@@ -218,15 +214,12 @@ GLOBAL_PROTECT(tracy_init_reason)
GLOB.picture_logging_prefix = "O_[override_dir]_"
GLOB.picture_log_directory = "data/picture_logs/[override_dir]"
- if(GLOB.tracy_log)
- rustg_file_write("[GLOB.tracy_log]", "[GLOB.log_directory]/tracy.loc")
- else if(!isnull(GLOB.tracy_init_error))
- stack_trace("byond-tracy failed to initialize: [GLOB.tracy_init_error]")
+ if(Tracy.trace_path)
+ rustg_file_write("[Tracy.trace_path]", "[GLOB.log_directory]/tracy.loc")
GLOB.world_game_log = "[GLOB.log_directory]/game.log"
GLOB.world_mecha_log = "[GLOB.log_directory]/mecha.log"
GLOB.world_virus_log = "[GLOB.log_directory]/virus.log"
- GLOB.world_cloning_log = "[GLOB.log_directory]/cloning.log"
GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
GLOB.world_attack_log = "[GLOB.log_directory]/attack.log"
GLOB.world_pda_log = "[GLOB.log_directory]/pda.log"
@@ -374,19 +367,22 @@ GLOBAL_PROTECT(tracy_init_reason)
qdel(src) //shut it down
/world/Reboot(reason = 0, fast_track = FALSE)
- var/round_end_sound = pick('sound/roundend/knave.ogg',
- 'sound/roundend/twohours.ogg',
- 'sound/roundend/rest.ogg',
- 'sound/roundend/gather.ogg',
- 'sound/roundend/bravery.ogg',
- 'sound/roundend/enjoy.ogg',
- 'sound/roundend/fatcuppapiss.ogg',
- 'sound/roundend/intermission.ogg',
- 'sound/roundend/motherfuckers.ogg',
- 'sound/roundend/poppop.ogg',
- 'sound/roundend/cursedswords.ogg',
- 'sound/roundend/dwarfs.ogg')
- for(var/client/thing in GLOB.clients)
+ var/round_end_sound = pick(
+ 'sound/roundend/knave.ogg',
+ 'sound/roundend/twohours.ogg',
+ 'sound/roundend/rest.ogg',
+ 'sound/roundend/gather.ogg',
+ 'sound/roundend/bravery.ogg',
+ 'sound/roundend/enjoy.ogg',
+ 'sound/roundend/fatcuppapiss.ogg',
+ 'sound/roundend/intermission.ogg',
+ 'sound/roundend/motherfuckers.ogg',
+ 'sound/roundend/poppop.ogg',
+ 'sound/roundend/cursedswords.ogg',
+ 'sound/roundend/dwarfs.ogg',
+ )
+
+ for(var/client/thing as anything in GLOB.clients)
if(!thing)
continue
thing << sound(round_end_sound)
@@ -425,7 +421,8 @@ GLOBAL_PROTECT(tracy_init_reason)
if(do_hard_reboot)
log_world("World hard rebooted at [time_stamp()]")
shutdown_logging() // See comment below.
- shutdown_byond_tracy()
+ QDEL_NULL(Tracy)
+ QDEL_NULL(Debugger)
SSplexora._Shutdown()
TgsEndProcess()
return ..()
@@ -434,8 +431,11 @@ GLOBAL_PROTECT(tracy_init_reason)
log_world("World rebooted at [time_stamp()]")
shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss.
+ QDEL_NULL(Tracy)
+ QDEL_NULL(Debugger)
+
TgsReboot() // TGS can decide to kill us right here, so it's important to do it last
- shutdown_byond_tracy()
+
..()
#endif
@@ -462,7 +462,8 @@ GLOBAL_PROTECT(tracy_init_reason)
return s
/world/Del()
- shutdown_byond_tracy()
+ QDEL_NULL(Tracy)
+ QDEL_NULL(Debugger)
. = ..()
/*
/world/proc/update_status()
@@ -527,20 +528,10 @@ GLOBAL_PROTECT(tracy_init_reason)
hub_password = "SORRYNOPASSWORD"
/**
-
-
* Handles incresing the world's maxx var and intializing the new turfs and assigning them to the global area.
-
-
* If map_load_z_cutoff is passed in, it will only load turfs up to that z level, inclusive.
-
-
* This is because maploading will handle the turfs it loads itself.
-
-
*/
-
-
/world/proc/increase_max_x(new_maxx, map_load_z_cutoff = maxz)
if(new_maxx <= maxx)
return
@@ -556,22 +547,6 @@ GLOBAL_PROTECT(tracy_init_reason)
SSmobs.MaxZChanged()
SSai_controllers.on_max_z_changed()
-
-/*
-#ifdef TESTING
-/client/verb/maxzcdec()
- set category = "DEBUGTEST"
- set name = "decr"
- set desc = ""
- world.decrementMaxZ()
- to_chat(src, "\nMaxz [world.maxz]")
-#endif
-
-/world/proc/decrementMaxZ()
- maxz = 1
-// SSmobs.MaxZDec()
-// SSidlenpcpool.MaxZdec()
-*/
/world/proc/change_fps(new_value = 20)
if(new_value <= 0)
CRASH("change_fps() called with [new_value] new_value.")
@@ -581,7 +556,6 @@ GLOBAL_PROTECT(tracy_init_reason)
fps = new_value
on_tickrate_change()
-
/world/proc/change_tick_lag(new_value = 0.5)
if(new_value <= 0)
CRASH("change_tick_lag() called with [new_value] new_value.")
@@ -591,45 +565,11 @@ GLOBAL_PROTECT(tracy_init_reason)
tick_lag = new_value
on_tickrate_change()
-
/world/proc/on_tickrate_change()
SStimer?.reset_buckets()
-/world/proc/init_byond_tracy()
- if(!fexists(TRACY_DLL_PATH))
- SEND_TEXT(world.log, "Error initializing byond-tracy: [TRACY_DLL_PATH] not found!")
- CRASH("Error initializing byond-tracy: [TRACY_DLL_PATH] not found!")
-
- var/init_result = call_ext(TRACY_DLL_PATH, "init")("block")
- if(length(init_result) != 0 && init_result[1] == ".") // if first character is ., then it returned the output filename
- SEND_TEXT(world.log, "byond-tracy initialized (logfile: [init_result])")
- GLOB.tracy_initialized = TRUE
- return GLOB.tracy_log = init_result
- else if(init_result == "already initialized")
- GLOB.tracy_initialized = TRUE
- SEND_TEXT(world.log, "byond-tracy already initialized ([GLOB.tracy_log ? "logfile: [GLOB.tracy_log]" : "no logfile"])")
- else if(init_result != "0")
- GLOB.tracy_init_error = init_result
- SEND_TEXT(world.log, "Error initializing byond-tracy: [init_result]")
- CRASH("Error initializing byond-tracy: [init_result]")
- else
- GLOB.tracy_initialized = TRUE
- SEND_TEXT(world.log, "byond-tracy initialized (no logfile)")
-
-/world/proc/shutdown_byond_tracy()
- if(GLOB.tracy_initialized)
- SEND_TEXT(world.log, "Shutting down byond-tracy")
- GLOB.tracy_initialized = FALSE
- call_ext(TRACY_DLL_PATH, "destroy")()
-
-/world/proc/flush_byond_tracy()
- // if GLOB.tracy_log is set, that means we're using para-tracy, which should have this.
- if(GLOB.tracy_initialized && GLOB.tracy_log)
- SEND_TEXT(world.log, "Flushing byond-tracy log")
- var/flush_result = call_ext(TRACY_DLL_PATH, "flush")()
- if(flush_result != "0")
- SEND_TEXT(world.log, "Error flushing byond-tracy log: [flush_result]")
- CRASH("Error flushing byond-tracy log: [flush_result]")
- SEND_TEXT(world.log, "Flushed byond-tracy log")
+/world/Profile(command, type, format)
+ if((command & PROFILE_STOP) || !global.config?.loaded || !CONFIG_GET(flag/forbid_all_profiling))
+ . = ..()
#undef RESTART_COUNTER_PATH
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index a9dbaa2a5b6..aeaf36cef17 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -964,59 +964,58 @@ GLOBAL_PROTECT(admin_verbs_hideable)
popup.set_content(dat)
popup.open()
+#ifndef OPENDREAM
/client/proc/tracy_next_round()
set name = "Toggle Tracy Next Round"
set desc = "Toggle running the byond-tracy profiler next round"
set category = "Debug"
+
if(!check_rights_for(src, R_DEBUG))
return
-#ifndef OPENDREAM
+
if(!fexists(TRACY_DLL_PATH))
to_chat(src, span_danger("byond-tracy library ([TRACY_DLL_PATH]) not present!"))
return
+
if(fexists(TRACY_ENABLE_PATH))
fdel(TRACY_ENABLE_PATH)
else
rustg_file_write("[ckey]", TRACY_ENABLE_PATH)
+
message_admins(span_adminnotice("[key_name_admin(src)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round."))
log_admin("[key_name(src)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.")
-#else
- to_chat(src, span_danger("byond-tracy is not supported on OpenDream, sorry!"))
-#endif
/client/proc/start_tracy()
set name = "Run Tracy Now"
set desc = "Start running the byond-tracy profiler immediately."
set category = "Debug"
+
if(!check_rights_for(src, R_DEBUG))
return
-#ifndef OPENDREAM
- if(GLOB.tracy_initialized)
+
+ if(Tracy.enabled)
to_chat(src, span_warning("byond-tracy is already running!"))
return
- else if(GLOB.tracy_init_error)
- to_chat(src, span_danger("byond-tracy failed to initialize during an earlier attempt: [GLOB.tracy_init_error]"))
- return
- else if(!fexists(TRACY_DLL_PATH))
- to_chat(src, span_danger("byond-tracy library ([TRACY_DLL_PATH]) not present!"))
+ else if(Tracy.error)
+ to_chat(src, span_danger("byond-tracy failed to initialize during an earlier attempt: [Tracy.error]"))
return
+
message_admins(span_adminnotice("[key_name_admin(src)] is trying to start the byond-tracy profiler."))
log_admin("[key_name(src)] is trying to start the byond-tracy profiler.")
- GLOB.tracy_initialized = FALSE
- GLOB.tracy_init_reason = "[ckey]"
- world.init_byond_tracy()
- if(GLOB.tracy_init_error)
- to_chat(src, span_danger("byond-tracy failed to initialize: [GLOB.tracy_init_error]"))
- message_admins(span_adminnotice("[key_name_admin(src)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])"))
- log_admin("[key_name(src)] tried to start the byond-tracy profiler, but it failed to initialize ([GLOB.tracy_init_error])")
+
+ if(!Tracy.enable("[ckey]"))
+ var/error = Tracy.error || "N/A"
+ to_chat(src, span_danger("byond-tracy failed to initialize: [error]"))
+ message_admins(span_adminnotice("[key_name_admin(src)] tried to start the byond-tracy profiler, but it failed to initialize ([error])"))
+ log_admin("[key_name(src)] tried to start the byond-tracy profiler, but it failed to initialize ([error])")
return
+
to_chat(src, span_notice("byond-tracy successfully started!"))
message_admins(span_adminnotice("[key_name_admin(src)] started the byond-tracy profiler."))
log_admin("[key_name(src)] started the byond-tracy profiler.")
- if(GLOB.tracy_log)
- rustg_file_write("[GLOB.tracy_log]", "[GLOB.log_directory]/tracy.loc")
-#else
- to_chat(src, span_danger("byond-tracy is not supported on OpenDream, sorry!"))
+
+ if(Tracy.trace_path)
+ rustg_file_write("[Tracy.trace_path]", "[GLOB.log_directory]/tracy.loc")
#endif
/// Debug verb for seeing at a glance what all spells have as set requirements
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index abf340de863..1c0d412ddde 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -33,8 +33,6 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/cmd_admin_rejuvenate,
/datum/admins/proc/show_traitor_panel,
/client/proc/disable_communication,
- /client/proc/cmd_show_at_list,
- /client/proc/cmd_show_at_markers,
/client/proc/manipulate_organs,
/client/proc/start_line_profiling,
/client/proc/stop_line_profiling,
@@ -86,42 +84,6 @@ GLOBAL_LIST_EMPTY(dirty_vars)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Intercom Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-/client/proc/cmd_show_at_list()
- set category = "Debug.Mapping"
- set name = "Show roundstart AT list"
- set desc = ""
-
- var/dat = {"Coordinate list of Active Turfs at Roundstart
-
Real-time Active Turfs list you can see in Air Subsystem at active_turfs var
"}
-
- for(var/turf/T as anything in GLOB.active_turfs_startlist)
- dat += "[ADMIN_VERBOSEJMP(T)]\n"
- dat += "
"
-
- usr << browse(dat, "window=at_list")
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turfs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
-/client/proc/cmd_show_at_markers()
- set category = "Debug.Mapping"
- set name = "Show roundstart AT markers"
- set desc = ""
-
- var/count = 0
- for(var/obj/effect/abstract/marker/at/AT in GLOB.all_abstract_markers)
- qdel(AT)
- count++
-
- if(count)
- to_chat(usr, "[count] AT markers removed.")
- else
- for(var/t in GLOB.active_turfs_startlist)
- new /obj/effect/abstract/marker/at(t)
- count++
- to_chat(usr, "[count] AT markers placed.")
-
- SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Roundstart Active Turf Markers")
-
/client/proc/enable_debug_verbs()
set category = "Debug.Core"
set name = "Debug verbs - Enable"
diff --git a/code/modules/debugging/debugger.dm b/code/modules/debugging/debugger.dm
new file mode 100644
index 00000000000..ce9d19efedc
--- /dev/null
+++ b/code/modules/debugging/debugger.dm
@@ -0,0 +1,55 @@
+
+/// The debugger instance.
+/// This is a GLOBAL_REAL because it initializes before the MC or GLOB.
+/// Really only used to check to see if the debugger is enabled or not,
+/// and to separate debugger-related code into its own thing.
+GLOBAL_REAL(Debugger, /datum/debugger)
+
+/datum/debugger
+ /// Is the debugger enabled?
+ VAR_FINAL/enabled = FALSE
+ /// The error text, if initializing the debugger errored.
+ VAR_FINAL/error
+ /// The path to the auxtools debug DLL, if it sets.
+ /// Defaults to the environmental variable AUXTOOLS_DEBUG_DLL.
+ VAR_FINAL/dll_path
+
+/datum/debugger/New(dll_path)
+ if(!isnull(Debugger))
+ CRASH("Attempted to initialize /datum/debugger when global.Debugger is already set!")
+ Debugger = src
+#ifndef OPENDREAM_REAL
+ src.dll_path = dll_path || world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
+ enable()
+#endif
+
+/datum/debugger/Destroy()
+#ifndef OPENDREAM_REAL
+ if(enabled)
+ call_ext(dll_path, "auxtools_shutdown")()
+#endif
+ return ..()
+
+/// Attempt to enable the debugger.
+/datum/debugger/proc/enable()
+#ifndef OPENDREAM_REAL
+ if(enabled)
+ CRASH("Attempted to enable debugger while its already enabled, somehow.")
+ if(!dll_path)
+ return FALSE
+ var/result = call_ext(dll_path, "auxtools_init")()
+ if(result != "SUCCESS")
+ error = result
+ return FALSE
+ enable_debugging()
+ enabled = TRUE
+ return TRUE
+#else
+ return FALSE
+#endif
+
+/datum/debugger/vv_edit_var(var_name, var_value)
+ return FALSE // no.
+
+/datum/debugger/CanProcCall(procname)
+ return FALSE // double no.
diff --git a/code/modules/debugging/tracy.dm b/code/modules/debugging/tracy.dm
new file mode 100644
index 00000000000..9840a253fb9
--- /dev/null
+++ b/code/modules/debugging/tracy.dm
@@ -0,0 +1,64 @@
+/// The byond-tracy instance.
+/// This is a GLOBAL_REAL because it is the VERY FIRST THING to initialize, even before the MC or GLOB.
+GLOBAL_REAL(Tracy, /datum/tracy)
+
+/datum/tracy
+ /// Is byond-tracy enabled and running?
+ VAR_FINAL/enabled = FALSE
+ /// The error text, if initializing byond-tracy errored.
+ VAR_FINAL/error
+ /// A description of what / who enabled byond-tracy.
+ VAR_FINAL/init_reason
+ /// A path to the file containing the output trace, if any.
+ VAR_FINAL/trace_path
+
+/datum/tracy/New()
+ if(!isnull(Tracy))
+ CRASH("Attempted to initialize /datum/tracy when global.Tracy is already set!")
+ Tracy = src
+
+/datum/tracy/Destroy()
+#ifndef OPENDREAM_REAL
+ if(enabled)
+ call_ext(TRACY_DLL_PATH, "destroy")()
+#endif
+ return ..()
+
+/// Tries to initialize byond-tracy.
+/datum/tracy/proc/enable(init_reason)
+#ifndef OPENDREAM_REAL
+ if(enabled)
+ return TRUE
+ src.init_reason = init_reason
+ if(!fexists(TRACY_DLL_PATH))
+ error = "[TRACY_DLL_PATH] not found"
+ SEND_TEXT(world.log, "Error initializing byond-tracy: [error]")
+ return FALSE
+
+ var/init_result = call_ext(TRACY_DLL_PATH, "init")("block")
+ if(length(init_result) != 0 && init_result[1] == ".") // if first character is ., then it returned the output filename
+ SEND_TEXT(world.log, "byond-tracy initialized (logfile: [init_result])")
+ enabled = TRUE
+ return TRUE
+ else if(init_result == "already initialized") // not gonna question it.
+ enabled = TRUE
+ SEND_TEXT(world.log, "byond-tracy already initialized ([trace_path ? "logfile: [trace_path]" : "no logfile"])")
+ return TRUE
+ else if(init_result != "0")
+ error = init_result
+ SEND_TEXT(world.log, "Error initializing byond-tracy: [init_result]")
+ return FALSE
+ else
+ enabled = TRUE
+ SEND_TEXT(world.log, "byond-tracy initialized (no logfile)")
+ return TRUE
+#else
+ error = "OpenDream not supported"
+ return FALSE
+#endif
+
+/datum/tracy/vv_edit_var(var_name, var_value)
+ return FALSE // no.
+
+/datum/tracy/CanProcCall(procname)
+ return FALSE // double no.
diff --git a/code/modules/unit_tests/unit_test.dm b/code/modules/unit_tests/unit_test.dm
index 2382832f305..adb73fda730 100644
--- a/code/modules/unit_tests/unit_test.dm
+++ b/code/modules/unit_tests/unit_test.dm
@@ -13,7 +13,6 @@ You can use the run_loc_bottom_left and run_loc_top_right to get turfs for testi
GLOBAL_DATUM(current_test, /datum/unit_test)
GLOBAL_VAR_INIT(failed_any_test, FALSE)
-GLOBAL_VAR(test_log)
/// When unit testing, all logs sent to log_mapping are stored here and retrieved in log_mapping unit test.
GLOBAL_LIST_EMPTY(unit_test_mapping_logs)
diff --git a/vanderlin.dme b/vanderlin.dme
index 0ed892230e8..69961582889 100644
--- a/vanderlin.dme
+++ b/vanderlin.dme
@@ -461,6 +461,7 @@
#include "code\controllers\subsystem\ping.dm"
#include "code\controllers\subsystem\plexora.dm"
#include "code\controllers\subsystem\pollution.dm"
+#include "code\controllers\subsystem\profiler.dm"
#include "code\controllers\subsystem\property_management.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\randomized_travel_tiles.dm"
@@ -2574,6 +2575,8 @@
#include "code\modules\crafting\slapcrafting\steps\add_to_structure.dm"
#include "code\modules\crafting\slapcrafting\steps\give_item.dm"
#include "code\modules\crafting\slapcrafting\steps\use_items.dm"
+#include "code\modules\debugging\debugger.dm"
+#include "code\modules\debugging\tracy.dm"
#include "code\modules\detectivework\footprints_and_rag.dm"
#include "code\modules\discord\accountlink.dm"
#include "code\modules\discord\discord_link_record.dm"
From db34101415328a1f079dee70c2388d019a3e7791 Mon Sep 17 00:00:00 2001
From: CheffieGithub <113442598+CheffieGithub@users.noreply.github.com>
Date: Sun, 22 Feb 2026 16:29:51 +0000
Subject: [PATCH 4/5] adsdsad
---
code/datums/quirks/_base.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/datums/quirks/_base.dm b/code/datums/quirks/_base.dm
index 6573cfffdbb..dcaa307f3cc 100644
--- a/code/datums/quirks/_base.dm
+++ b/code/datums/quirks/_base.dm
@@ -1,6 +1,6 @@
-GLOBAL_LIST_INIT(quirk_registry, init_quirk_registry())
GLOBAL_LIST_EMPTY(quirk_singletons)
GLOBAL_LIST_EMPTY(quirk_points_by_type)
+GLOBAL_LIST_INIT(quirk_registry, init_quirk_registry())
/proc/init_quirk_registry()
GLOB.quirk_registry = list()
From b2dd651b7c479a98779d83da6b8238389dd744b2 Mon Sep 17 00:00:00 2001
From: CheffieGithub <113442598+CheffieGithub@users.noreply.github.com>
Date: Sun, 22 Feb 2026 18:12:30 +0000
Subject: [PATCH 5/5] chat
---
code/modules/admin/admin_verbs.dm | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index aeaf36cef17..0dcacf7ed4f 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -964,7 +964,6 @@ GLOBAL_PROTECT(admin_verbs_hideable)
popup.set_content(dat)
popup.open()
-#ifndef OPENDREAM
/client/proc/tracy_next_round()
set name = "Toggle Tracy Next Round"
set desc = "Toggle running the byond-tracy profiler next round"
@@ -973,6 +972,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if(!check_rights_for(src, R_DEBUG))
return
+#ifndef OPENDREAM
if(!fexists(TRACY_DLL_PATH))
to_chat(src, span_danger("byond-tracy library ([TRACY_DLL_PATH]) not present!"))
return
@@ -984,6 +984,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
message_admins(span_adminnotice("[key_name_admin(src)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round."))
log_admin("[key_name(src)] [fexists(TRACY_ENABLE_PATH) ? "enabled" : "disabled"] the byond-tracy profiler for next round.")
+#endif
/client/proc/start_tracy()
set name = "Run Tracy Now"
@@ -993,6 +994,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
if(!check_rights_for(src, R_DEBUG))
return
+#ifndef OPENDREAM
if(Tracy.enabled)
to_chat(src, span_warning("byond-tracy is already running!"))
return