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())
-
[selected_patron.name]
+
[selected_patron::name]
@@ -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