From 262ef2a63a5102e0e0542cb2eb5edadc20af2b2c Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 02:53:28 -0500 Subject: [PATCH 01/28] start mc update --- citadel.dme | 8 +- code/__DEFINES/MC.dm | 31 +++++ code/__DEFINES/controllers/_subsystems.dm | 18 +++ code/__DEFINES/metrics.dm | 6 + code/controllers/master.dm | 134 +++++++++++++++---- code/controllers/subsystem.dm | 18 +-- code/modules/metrics/api.dm | 78 ----------- code/modules/metrics/api/nested_numerical.dm | 23 ++++ code/modules/metrics/api/numerical.dm | 23 ++++ code/modules/metrics/api/spatial_series.dm | 31 +++++ code/modules/metrics/api/string_set.dm | 17 +++ code/modules/metrics/api/time_series.dm | 30 +++++ code/modules/metrics/metrics/controllers.dm | 4 + 13 files changed, 303 insertions(+), 118 deletions(-) create mode 100644 code/__DEFINES/metrics.dm delete mode 100644 code/modules/metrics/api.dm create mode 100644 code/modules/metrics/api/nested_numerical.dm create mode 100644 code/modules/metrics/api/numerical.dm create mode 100644 code/modules/metrics/api/spatial_series.dm create mode 100644 code/modules/metrics/api/string_set.dm create mode 100644 code/modules/metrics/api/time_series.dm create mode 100644 code/modules/metrics/metrics/controllers.dm diff --git a/citadel.dme b/citadel.dme index a6da8994250c..98411a41d277 100644 --- a/citadel.dme +++ b/citadel.dme @@ -65,6 +65,7 @@ #include "code\__DEFINES\math.dm" #include "code\__DEFINES\matrices.dm" #include "code\__DEFINES\MC.dm" +#include "code\__DEFINES\metrics.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\move_force.dm" #include "code\__DEFINES\movement.dm" @@ -3494,9 +3495,14 @@ #include "code\modules\media\media_tracks.dm" #include "code\modules\media\mediamanager.dm" #include "code\modules\media\walkpod.dm" -#include "code\modules\metrics\api.dm" #include "code\modules\metrics\metric.dm" #include "code\modules\metrics\metric_base.dm" +#include "code\modules\metrics\api\nested_numerical.dm" +#include "code\modules\metrics\api\numerical.dm" +#include "code\modules\metrics\api\spatial_series.dm" +#include "code\modules\metrics\api\string_set.dm" +#include "code\modules\metrics\api\time_series.dm" +#include "code\modules\metrics\metrics\controllers.dm" #include "code\modules\mining\mine_turfs.dm" #include "code\modules\mining\drilling\drill.dm" #include "code\modules\mining\drilling\scanner.dm" diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 0cfbf865dd76..537c2ff0c660 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -23,6 +23,37 @@ #define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum} #define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum +//* Recreate_MC() return values *// + +#define MC_RESTART_RTN_FAILED -1 +#define MC_RESTART_RTN_COOLDOWN 0 +#define MC_RESTART_RTN_SUCCESS 1 + +//* Master Controller Loop() return values *// + +/// New initialize stage happened +#define MC_LOOP_RTN_NEWSTAGES 1 +/// We want the MC to exit. +#define MC_LOOP_RTN_GRACEFUL_EXIT 2 + +//* Initialization Stages *// +//* After each stage, the MC starts ticking that stage while later stages are still waiting to init. *// + +/// Nothing has been completed. This is not a real stage. +#define MC_INIT_STAGE_MIN 0 + +/// Early initializations required for server function; database, timers, tgui, etc +#define MC_INIT_STAGE_BACKEND 1 +/// Pre-mapload initializations +#define MC_INIT_STAGE_EARLY 2 +/// Mapload +#define MC_INIT_STAGE_WORLD 3 +/// Late +#define MC_INIT_STAGE_LATE 4 + +/// Last init stage we need to do. +#define MC_INIT_STAGE_MAX 4 + //! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier) /// subsystem does not initialize. diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index 3a5f1b5360b2..7e0e839cb960 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -11,6 +11,24 @@ // #define SUBSYSTEM_INITIALIZED_INITIALIZING 1 // #define SUBSYSTEM_INITIALIZED_DONE 2 +//* Subsystem `Initialize()` returns *// + +/** + * Negative values incidate a failure or warning of some kind, positive are good. + * 0 and 1 are unused so that TRUE and FALSE are guarenteed to be invalid values. + */ + +/// Subsystem failed to initialize entirely. Print a warning, log, and disable firing. +#define SS_INIT_FAILURE -2 +/// The default return value which must be overriden. Will succeed with a warning. +#define SS_INIT_NONE -1 +/// Subsystem initialized sucessfully. +#define SS_INIT_SUCCESS 2 +/// If your system doesn't need to be initialized (by being disabled or something) +#define SS_INIT_NO_NEED 3 +/// Succesfully initialized, BUT do not announce it to players (generally to hide game mechanics it would otherwise spoil) +#define SS_INIT_NO_MESSAGE 4 + //! ## Initialization subsystem /// New should not call Initialize. diff --git a/code/__DEFINES/metrics.dm b/code/__DEFINES/metrics.dm new file mode 100644 index 000000000000..4286dc004792 --- /dev/null +++ b/code/__DEFINES/metrics.dm @@ -0,0 +1,6 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +//* Metric Categories *// + +#define METRIC_CATEGORY_SERVER "server" diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 71dc99baf347..98ab190380c2 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -63,7 +63,14 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/random_seed - //* Processing Variables *// + //* Global State *// + //* These are tracked through MC restarts. *// + + /// The current initialization stage we're at. + var/static/init_stage_completed = MC_INIT_STAGE_MIN + + //* Processing Variables *// + //* These are set during a Loop(). *// /// total fire_priority of all non-background subsystems in the queue var/queue_priority_count = 0 @@ -75,6 +82,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// End of queue linked list (used for appending to the list). var/datum/controller/subsystem/queue_tail + //* Control Variables *// + //* These are accessed globally and are used to allow the MC to control the server's tick when outside of a MC proc. *// + /** * current tick limit, assigned before running a subsystem. * used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits. @@ -129,13 +139,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new for(var/datum/controller/subsystem/S in _subsystems) S.Preload(FALSE) - /datum/controller/master/Destroy() ..() // Tell qdel() to Del() this object. return QDEL_HINT_HARDDEL_NOW - /datum/controller/master/Shutdown() processing = FALSE tim_sort(subsystems, GLOBAL_PROC_REF(cmp_subsystem_init)) @@ -146,18 +154,21 @@ GLOBAL_REAL(Master, /datum/controller/master) = new log_world("Shutdown complete") - /** + * MC reboot proc. + * * Returns: * - 1 If we created a new mc. * - 0 If we couldn't due to a recent restart. * - -1 If we encountered a runtime trying to recreate it. */ /proc/Recreate_MC() - usr = null // yeah let's not contaminate the MC call stack with our usr. - . = -1 // So if we runtime, things know we failed. + // Do not contaminate `usr`; if this is set, the MC main loop will have the usr of whoever called it, + // which results in all procs called by the MC inheriting that usr. + usr = null + . = MC_RESTART_RTN_FAILED // So if we runtime, things know we failed. if (world.time < Master.restart_timeout) - return 0 + return MC_RESTART_RTN_COOLDOWN if (world.time < Master.restart_clear) Master.restart_count *= 0.5 @@ -168,10 +179,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new try new/datum/controller/master() catch - return -1 - - return 1 + return MC_RESTART_RTN_FAILED + return MC_RESTART_RTN_SUCCESS /datum/controller/master/Recover() var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" @@ -222,7 +232,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new to_chat(world, SPAN_BOLDANNOUNCE("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) Initialize(20, TRUE) - /** * Please don't stuff random bullshit here, * Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize() @@ -233,12 +242,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(delay) sleep(delay) - if(tgs_prime) - world.TgsInitializationComplete() - if(init_sss) init_subtypes(/datum/controller/subsystem, subsystems) + init_stage_completed = MC_INIT_STAGE_MIN + var/mc_started = FALSE + to_chat(world, SPAN_BOLDANNOUNCE("Initializing subsystems...")) // Sort subsystems by init_order, so they initialize in the correct order. @@ -270,11 +279,14 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(world.system_type == MS_WINDOWS && CONFIG_GET(flag/toast_notification_on_init) && !length(GLOB.clients)) world.shelleo("start /min powershell -ExecutionPolicy Bypass -File tools/initToast/initToast.ps1 -name \"[world.name]\" -icon %CD%\\icons\\CS13_16.png -port [world.port]") - // Set world options. + var/initialized_tod = REALTIMEOFDAY + // Set world options. world.set_fps(config_legacy.fps) - var/initialized_tod = REALTIMEOFDAY + if(tgs_prime) + world.TgsInitializationComplete() + if(sleep_offline_after_initializations) world.sleep_offline = TRUE @@ -282,14 +294,91 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(sleep_offline_after_initializations) // && CONFIG_GET(flag/resume_after_initializations)) world.sleep_offline = FALSE - initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 - initialized = TRUE - // Loop. Master.StartProcessing(0) +/** + * Initialize a given subsystem and handle the results. + * + * Arguments: + * * subsystem - the subsystem to initialize. + */ +/datum/controller/master/proc/initialize_subsystem(datum/controller/subsystem/subsystem) + // Do not re-init already initialized subsystems if it's somehow called again. + if (subsystem.flags & SS_NO_INIT || subsystem.initialized) + return + + // todo: dylib high-precision timers + var/rtod_start = REALTIMEOFDAY + + var/initialize_result = subsystem.Initialize() + + var/rtod_end = REALTIMEOFDAY + var/took_seconds = round((rtod_end - rtod_start) / 10, 0.01) + + metric_set_nested_numerical(/datum/metric/nested_numerical/subsystem_init_time, "[subsystem.type]", took_seconds) + + switch(initialize_result) + if(SS_INIT_FAILURE) + if(SS_INIT_NONE) + warning("[subsystem.name] subsystem does not implement Initialize() or it returns ..(). If the former is true, the SS_NO_INIT flag should be set for this subsystem.") + if(SS_INIT_SUCCESS) + if(SS_INIT_NO_MESSAGE) + if(SS_INIT_NO_NEED) + else + warning("[subsystem.name] subsystem initialized, returning invalid result [result]. This is a bug.") + + // just returned ..() or didn't implement Initialize() at all + if(result == SS_INIT_NONE) + + if(result != SS_INIT_FAILURE) + // Some form of success, implicit failure, or the SS in unused. + subsystem.initialized = TRUE + + SEND_SIGNAL(subsystem, COMSIG_SUBSYSTEM_POST_INITIALIZE) + else + // The subsystem officially reports that it failed to init and wishes to be treated as such. + subsystem.initialized = FALSE + subsystem.can_fire = FALSE + + #warn below + // The rest of this proc is printing the world log and chat message. + var/message_prefix + + // If true, print the chat message with boldwarning text. + var/chat_warning = FALSE + + switch(result) + if(SS_INIT_FAILURE) + message_prefix = "Failed to initialize [subsystem.name] subsystem after" + chat_warning = TRUE + if(SS_INIT_SUCCESS, SS_INIT_NO_MESSAGE) + message_prefix = "Initialized [subsystem.name] subsystem within" + if(SS_INIT_NO_NEED) + // This SS is disabled or is otherwise shy. + return + else + // SS_INIT_NONE or an invalid value. + message_prefix = "Initialized [subsystem.name] subsystem with errors within" + chat_warning = TRUE + + var/message = "[message_prefix] [seconds] second[seconds == 1 ? "" : "s"]!" + var/chat_message = chat_warning ? span_boldwarning(message) : span_boldannounce(message) + + if(result != SS_INIT_NO_MESSAGE) + to_chat(world, chat_message) + log_world(message) + + // initialized = TRUE + // var/time = (REALTIMEOFDAY - start_timeofday) / 10 + // var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!" + // to_chat(world, SPAN_BOLDANNOUNCE("[msg]")) + // log_world(msg) + // log_subsystem("INIT", msg) + // return time + #warn above /datum/controller/master/proc/SetRunLevel(new_runlevel) var/old_runlevel = current_runlevel @@ -324,7 +413,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now") Failsafe.defcon = 2 - /** * Main loop! * This is where the magic happens. @@ -492,7 +580,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new sleep(world.tick_lag * (processing * sleep_delta)) - /** * This is what decides if something should run. * @@ -532,7 +619,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new . = TRUE - /// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage. /datum/controller/master/proc/RunQueue() . = FALSE @@ -618,7 +704,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new current_tick_budget -= queue_node_priority - if (queue_node_tick_usage < 0) queue_node_tick_usage = 0 @@ -729,11 +814,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new log_world("MC: SoftReset: Finished.") . = TRUE - /datum/controller/master/stat_entry() return "(TickRate:[Master.processing]) (Iteration:[Master.iteration])" - /datum/controller/master/StartLoadingMap() // todo: this is kind of awful because this procs every subsystem unnecessarily // you might say this is microoptimizations but this is called a seriously high number of times during a load. @@ -759,7 +842,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate) */ - /datum/controller/master/proc/OnConfigLoad() for (var/thing in subsystems) var/datum/controller/subsystem/SS = thing diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index b12deb3adb41..ef0377a89b5f 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -21,6 +21,10 @@ * Use or create defines such as [INIT_ORDER_DEFAULT] so we can see the order in one file. */ var/init_order = INIT_ORDER_DEFAULT + /** + * Which stage does this subsystem init at. Earlier stages can fire while later stages init. + */ + var/init_stage = MC_INIT_STAGE_WORLD /** * Time to wait (in deciseconds) between each call to fire(). @@ -44,12 +48,6 @@ */ var/subsystem_flags = NONE - /** - * Which stage does this subsystem init at. - * Earlier stages can fire while later stages init. - */ - //var/init_stage = INITSTAGE_MAIN - /// This var is set to TRUE after the subsystem has been initialized. // todo: see __DEFINES/controllers/_subsystems.dm; this shouldn't just be TRUE / FALSE var/initialized = FALSE @@ -334,13 +332,7 @@ * This is expected to be overriden by subtypes. */ /datum/controller/subsystem/Initialize(start_timeofday) - initialized = TRUE - var/time = (REALTIMEOFDAY - start_timeofday) / 10 - var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!" - to_chat(world, SPAN_BOLDANNOUNCE("[msg]")) - log_world(msg) - log_subsystem("INIT", msg) - return time + return SS_INIT_NONE /** * Hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc. diff --git a/code/modules/metrics/api.dm b/code/modules/metrics/api.dm deleted file mode 100644 index f8dd53c35b56..000000000000 --- a/code/modules/metrics/api.dm +++ /dev/null @@ -1,78 +0,0 @@ -//* This file is explicitly licensed under the MIT license. *// -//* Copyright (c) 2024 Citadel Station Developers *// - -/** - * counter metric - * - * * numerical, can only go up through a round - */ -/datum/metric/counter - -/** - * increments a counter metric by an amount, defaulting to 1 - * - * * The time at which this is called does matter. The recorded metric will be at the current - * time of the recording. - * - * This is what you can use for things like: - * - * * How many times an admin verb was pressed in a round - * * How many times a thing happened in a round - */ -/proc/metric_record_counter(datum/metric/counter/typepath, amount = 1) - return - -/** - * records a series of values at given times - * - * * Supports a number, string, or both. - * - * This is what you can use for things like: - * - * * Time dilation tracking - */ -/datum/metric/series - /// has numerical data to graph - /// - /// * doesn't limit the data, only determines if we try to pull a graph - var/graph_exists = FALSE - /// representation of numerical data in graph - /// - /// * valid values are ["average", "tally"] - var/graph_collate = "tally" - -/** - * records a number or a string in a series metric - * - * * The time at which this is called does matter. The recorded metric will be at the current - * time of the recording. - */ -/proc/metric_record_series(datum/metric/series/typepath, tally, comment) - return - -/** - * Records an event at a specific tile of a map - * - * * Supports a single tile event with annotation of number and/or string - * * Supports a rectangular event with annotation of number and/or string - * - * This is usually used for game-map purposes, but is actually usable as an arbitrary - * spatial metric if you need it for whatever reason. - * - * This is what you can use for things like: - * - * * Tracking where people died - * * Tracking what explodes - * * Tracking what goes wrong where - */ -/datum/metric/spatial - /// Whether the spatial metric corrosponds to the actual in-game map - var/is_game_world = FALSE - /// Don't render a tally of '1' - var/elide_singular_tally = TRUE - -/proc/metric_record_spatial_single(datum/metric/spatial/typepath, x, y, level_id, tally, comment) - return - -/proc/metric_record_spatial_box(datum/metric/spatial/typepath, x1, y1, x2, y2, level_id, tally, comment) - return diff --git a/code/modules/metrics/api/nested_numerical.dm b/code/modules/metrics/api/nested_numerical.dm new file mode 100644 index 000000000000..6bcddacf95da --- /dev/null +++ b/code/modules/metrics/api/nested_numerical.dm @@ -0,0 +1,23 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * nested numerical metric + * + * This is what you can use for things like: + * + * * How many times an admin verb was pressed in a round + */ +/datum/metric/nested_numerical + +/** + * overwrites a nested numerical metric + */ +/proc/metric_set_nested_numerical(datum/metric/nested_numerical/typepath, key = "--unset--", amount) + return + +/** + * increments a nested numerical metric + */ +/proc/metric_increment_nested_numerical(datum/metric/nested_numerical/typepath, key = "--unset--", amount = 1) + return diff --git a/code/modules/metrics/api/numerical.dm b/code/modules/metrics/api/numerical.dm new file mode 100644 index 000000000000..517efd200f01 --- /dev/null +++ b/code/modules/metrics/api/numerical.dm @@ -0,0 +1,23 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * numerical metric + * + * This is what you can use for things like: + * + * * How many times a thing happened in a round + */ +/datum/metric/numerical + +/** + * overwrites a numerical metric + */ +/proc/metric_set_numerical(datum/metric/numerical/typepath, amount) + return + +/** + * increments a numerical metric + */ +/proc/metric_increment_numerical(datum/metric/numerical/typepath, amount = 1) + return diff --git a/code/modules/metrics/api/spatial_series.dm b/code/modules/metrics/api/spatial_series.dm new file mode 100644 index 000000000000..36fa66f3bce3 --- /dev/null +++ b/code/modules/metrics/api/spatial_series.dm @@ -0,0 +1,31 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Records an event at a specific tile of a map + * + * * Supports a single tile event with annotation of number and/or string + * * Supports a rectangular event with annotation of number and/or string + * + * This is usually used for game-map purposes, but is actually usable as an arbitrary + * spatial metric if you need it for whatever reason. + * + * This is what you can use for things like: + * + * * Tracking where people died + * * Tracking what explodes + * * Tracking what goes wrong where + */ +/datum/metric/spatial_series + /// Whether the spatial metric corrosponds to the actual in-game map + var/is_game_world = FALSE + /// Don't render a tally of '1' + var/elide_singular_tally = TRUE + /// We care about time + var/is_time_series = FALSE + +/proc/metric_record_spatial_series_single(datum/metric/spatial_series/typepath, x, y, level_id, tally, comment) + return + +/proc/metric_record_spatial_series_box(datum/metric/spatial_series/typepath, x1, y1, x2, y2, level_id, tally, comment) + return diff --git a/code/modules/metrics/api/string_set.dm b/code/modules/metrics/api/string_set.dm new file mode 100644 index 000000000000..c3b75fc7ba12 --- /dev/null +++ b/code/modules/metrics/api/string_set.dm @@ -0,0 +1,17 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Records an unique set of strings. + * + * This is what you can use for things like: + * + * * What subsystems failed init + */ +/datum/metric/string_set + +/** + * records a string into a string set + */ +/proc/metric_record_string_set(datum/metric/string_set/typepath, string) + return diff --git a/code/modules/metrics/api/time_series.dm b/code/modules/metrics/api/time_series.dm new file mode 100644 index 000000000000..94cea76620bc --- /dev/null +++ b/code/modules/metrics/api/time_series.dm @@ -0,0 +1,30 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * records a series of values at given times + * + * * Supports a number, string, or both. + * + * This is what you can use for things like: + * + * * Time dilation tracking + */ +/datum/metric/time_series + /// has numerical data to graph + /// + /// * doesn't limit the data, only determines if we try to pull a graph + var/graph_exists = FALSE + /// representation of numerical data in graph + /// + /// * valid values are ["average", "tally"] + var/graph_collate = "tally" + +/** + * records a number or a string in a series metric + * + * * The time at which this is called does matter. The recorded metric will be at the current + * time of the recording. + */ +/proc/metric_record_time_series(datum/metric/time_series/typepath, value, comment) + return diff --git a/code/modules/metrics/metrics/controllers.dm b/code/modules/metrics/metrics/controllers.dm new file mode 100644 index 000000000000..9fa1d5837fa3 --- /dev/null +++ b/code/modules/metrics/metrics/controllers.dm @@ -0,0 +1,4 @@ +/datum/metric/nested_numerical/subsystem_init_time + id = "subsystem-init-time" + name = "Init Time - Subsystem" + category = METRIC_CATEGORY_SERVER From dd8d553c52cfa785f249a5109cdcd67aa760510a Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 03:46:19 -0500 Subject: [PATCH 02/28] mc-side hcnages --- code/__DEFINES/MC.dm | 8 +- code/__DEFINES/controllers/_subsystems.dm | 5 + code/controllers/master.dm | 266 ++++++++++++---------- code/controllers/subsystem.dm | 12 +- code/controllers/subsystem/atoms.dm | 2 + 5 files changed, 168 insertions(+), 125 deletions(-) diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 537c2ff0c660..9b914b17940d 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -31,6 +31,8 @@ //* Master Controller Loop() return values *// +/// Unknown or error +#define MC_LOOP_RTN_UNKNOWN 0 /// New initialize stage happened #define MC_LOOP_RTN_NEWSTAGES 1 /// We want the MC to exit. @@ -38,9 +40,7 @@ //* Initialization Stages *// //* After each stage, the MC starts ticking that stage while later stages are still waiting to init. *// - -/// Nothing has been completed. This is not a real stage. -#define MC_INIT_STAGE_MIN 0 +//* MC init stages must be a positive number, and init stages must all be consequetive! *// /// Early initializations required for server function; database, timers, tgui, etc #define MC_INIT_STAGE_BACKEND 1 @@ -52,6 +52,8 @@ #define MC_INIT_STAGE_LATE 4 /// Last init stage we need to do. +/// +/// * This must be set to the maximum INIT_STAGE. #define MC_INIT_STAGE_MAX 4 //! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier) diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index 7e0e839cb960..e1ee97370702 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -69,6 +69,11 @@ //! ### SS runlevels +//* Runlevels *// +//* Must be powers of 2. Runlevels should be in order of progression. *// +//* Only subsystem with a runlevel matching the MC's will be ticked. *// +//* The first runlevel (value '1') will be the default runlevel when the MC is first initialized. *// + /// "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set). #define RUNLEVEL_INIT 0 /// Initial runlevel before setup. Returns to here if setup fails. diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 98ab190380c2..5a3fa715c3cb 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -67,7 +67,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //* These are tracked through MC restarts. *// /// The current initialization stage we're at. - var/static/init_stage_completed = MC_INIT_STAGE_MIN + var/static/init_stage_completed = 0 //* Processing Variables *// //* These are set during a Loop(). *// @@ -235,70 +235,90 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /** * Please don't stuff random bullshit here, * Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize() + * + * @params + * * delay - wait this many deciseconds before initializing + * * init_sss - initialize all subsystems + * * tgs_prime - notify TGS that initializations are done after */ /datum/controller/master/Initialize(delay, init_sss, tgs_prime) set waitfor = FALSE if(delay) sleep(delay) - if(init_sss) init_subtypes(/datum/controller/subsystem, subsystems) - init_stage_completed = MC_INIT_STAGE_MIN + // Announce start + to_chat(world, SPAN_BOLDANNOUNCE("Initializing subsystems...")) var/mc_started = FALSE - to_chat(world, SPAN_BOLDANNOUNCE("Initializing subsystems...")) + // We want to initialize subsystems by stage, in the init_order provided for subsystems within the same stage. + init_stage_completed = 0 + var/list/stage_sorted_subsystems = new(MC_INIT_STAGE_MAX) + for(var/i in 1 to MC_INIT_STAGE_MAX) + stage_sorted_subsystems[i] = list() // Sort subsystems by init_order, so they initialize in the correct order. tim_sort(subsystems, GLOBAL_PROC_REF(cmp_subsystem_init)) - var/start_timeofday = REALTIMEOFDAY - // Initialize subsystems. - current_ticklimit = config_legacy.tick_limit_mc_init - for (var/datum/controller/subsystem/SS in subsystems) - if (SS.subsystem_flags & SS_NO_INIT) - continue + // Collect subsystems by init_stage. This has precedence over init_order. + for(var/datum/controller/subsystem/subsystem as anything in subsystems) + var/subsystem_init_stage = subsystem.init_stage + if (!isnum(subsystem_init_stage) || subsystem_init_stage < 1 || subsystem_init_stage > MC_INIT_STAGE_MAX || round(subsystem_init_stage) != subsystem_init_stage) + stack_trace("ERROR: MC: subsystem `[subsystem.type]` has invalid init_stage: `[subsystem_init_stage]`. Setting to `[MC_INIT_STAGE_MAX]`") + subsystem_init_stage = subsystem.init_stage = MC_INIT_STAGE_MAX + stage_sorted_subsystems[subsystem_init_stage] += subsystem - SS.Initialize(REALTIMEOFDAY) - CHECK_TICK + // Sort subsystems by display setting for easy access. + sortTim(subsystems, GLOBAL_PROC_REF(cmp_subsystem_display)) - current_ticklimit = TICK_LIMIT_RUNNING - var/time = (REALTIMEOFDAY - start_timeofday) / 10 + // Initialize subsystems. The ticker loop will be started immediately upon the first stage being done. + var/rtod_start = REALTIMEOFDAY - var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!" + for(var/current_init_stage in 1 to INITSTAGE_MAX) + for(var/datum/controller/subsystem/subsystem in stage_sorted_subsystems[current_init_stage]) + initialize_subsystem(subsystem) + CHECK_TICK + init_stage_completed = current_init_stage + if(!mc_started) + mc_started = TRUE + if(!current_runlevel) + // intentionally not using the defines here as the MC does not care about runlevel semantics; + // the first runlevel is always used. + SetRunLevel(1) + Master.StartProcessing(0) + + var/end_rtod = REALTIMEOFDAY + var/took_seconds = round((end_rtod - start_rtod) / 10, 0.01) + + // Announce, log, and record end + var/msg = "Initializations complete within [took_seconds] second[took_seconds == 1 ? "" : "s"]!" to_chat(world, SPAN_BOLDANNOUNCE("[msg]")) log_world(msg) - if (!current_runlevel) - SetRunLevel(RUNLEVEL_LOBBY) - - // Sort subsystems by display setting for easy access. - tim_sort(subsystems, GLOBAL_PROC_REF(cmp_subsystem_display)) - - if(world.system_type == MS_WINDOWS && CONFIG_GET(flag/toast_notification_on_init) && !length(GLOB.clients)) - world.shelleo("start /min powershell -ExecutionPolicy Bypass -File tools/initToast/initToast.ps1 -name \"[world.name]\" -icon %CD%\\icons\\CS13_16.png -port [world.port]") - + // Record initialization finish. var/initialized_tod = REALTIMEOFDAY // Set world options. world.set_fps(config_legacy.fps) + // Fire initialization toast + if(world.system_type == MS_WINDOWS && CONFIG_GET(flag/toast_notification_on_init) && !length(GLOB.clients)) + world.shelleo("start /min powershell -ExecutionPolicy Bypass -File tools/initToast/initToast.ps1 -name \"[world.name]\" -icon %CD%\\icons\\CS13_16.png -port [world.port]") + + // Tell TGS we're initialized if(tgs_prime) world.TgsInitializationComplete() + // Handle sleeping offline after initializations. if(sleep_offline_after_initializations) world.sleep_offline = TRUE - - sleep(1) - + sleep(1 TICK) if(sleep_offline_after_initializations) // && CONFIG_GET(flag/resume_after_initializations)) world.sleep_offline = FALSE initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 - // Loop. - Master.StartProcessing(0) - /** * Initialize a given subsystem and handle the results. * @@ -320,66 +340,43 @@ GLOBAL_REAL(Master, /datum/controller/master) = new metric_set_nested_numerical(/datum/metric/nested_numerical/subsystem_init_time, "[subsystem.type]", took_seconds) + // "[message_prefix] [seconds] seconds." + var/message_prefix + // tell everyone? + var/tell_everyone + // is this a warning? + var/chat_warning + switch(initialize_result) if(SS_INIT_FAILURE) + message_prefix = "Failed to initialize [subsystem.name] subsystem after" + tell_everyone = TRUE + chat_warning = TRUE + subsystem.initialized = FALSE if(SS_INIT_NONE) + message_prefix = "Initialized [subsystem.name] subsystem with errors within" + tell_everyone = TRUE + chat_warning = TRUE + subsystem.initialized = TRUE warning("[subsystem.name] subsystem does not implement Initialize() or it returns ..(). If the former is true, the SS_NO_INIT flag should be set for this subsystem.") if(SS_INIT_SUCCESS) + message_prefix = "Initialized [subsystem.name] subsystem within" + tell_everyone = TRUE + subsystem.initialized = TRUE if(SS_INIT_NO_MESSAGE) - if(SS_INIT_NO_NEED) - else - warning("[subsystem.name] subsystem initialized, returning invalid result [result]. This is a bug.") - - // just returned ..() or didn't implement Initialize() at all - if(result == SS_INIT_NONE) - - if(result != SS_INIT_FAILURE) - // Some form of success, implicit failure, or the SS in unused. - subsystem.initialized = TRUE - - SEND_SIGNAL(subsystem, COMSIG_SUBSYSTEM_POST_INITIALIZE) - else - // The subsystem officially reports that it failed to init and wishes to be treated as such. - subsystem.initialized = FALSE - subsystem.can_fire = FALSE - - #warn below - // The rest of this proc is printing the world log and chat message. - var/message_prefix - - // If true, print the chat message with boldwarning text. - var/chat_warning = FALSE - - switch(result) - if(SS_INIT_FAILURE) - message_prefix = "Failed to initialize [subsystem.name] subsystem after" - chat_warning = TRUE - if(SS_INIT_SUCCESS, SS_INIT_NO_MESSAGE) message_prefix = "Initialized [subsystem.name] subsystem within" + subsystem.initialized = TRUE if(SS_INIT_NO_NEED) - // This SS is disabled or is otherwise shy. - return else - // SS_INIT_NONE or an invalid value. - message_prefix = "Initialized [subsystem.name] subsystem with errors within" - chat_warning = TRUE + warning("[subsystem.name] subsystem initialized, returning invalid result [result]. This is a bug.") - var/message = "[message_prefix] [seconds] second[seconds == 1 ? "" : "s"]!" - var/chat_message = chat_warning ? span_boldwarning(message) : span_boldannounce(message) + var/message = "[message_prefix] [took_seconds] second[took_seconds == 1 ? "" : "s"]." + var/chat_message = chat_warning ? SPAN_BOLDWARNING(message) : SPAN_BOLDANNOUNCE(MESSAGE) - if(result != SS_INIT_NO_MESSAGE) + if(tell_everyone && message_prefix) to_chat(world, chat_message) log_world(message) - // initialized = TRUE - // var/time = (REALTIMEOFDAY - start_timeofday) / 10 - // var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!" - // to_chat(world, SPAN_BOLDANNOUNCE("[msg]")) - // log_world(msg) - // log_subsystem("INIT", msg) - // return time - #warn above - /datum/controller/master/proc/SetRunLevel(new_runlevel) var/old_runlevel = current_runlevel if(isnull(old_runlevel)) @@ -400,11 +397,17 @@ GLOBAL_REAL(Master, /datum/controller/master) = new sleep(delay) testing("Master starting processing") - var/rtn = Loop() - if (rtn > 0 || processing < 0) - return // This was suppose to happen. + var/started_stage + var/rtn = MC_LOOP_RTN_UNKNOWN + do + started_stage = init_stage_completed + rtn = Loop(started_stage) + while (rtn == MC_LOOP_RTN_NEWSTAGES && processing > 0 && started_stage < init_stage_completed) + + if (rtn >= MC_LOOP_RTN_GRACEFUL_EXIT || processing < 0) + return //this was suppose to happen. - // Loop ended, restart the mc. + //loop ended, restart the mc log_game("MC crashed or runtimed, restarting") message_admins("MC crashed or runtimed, restarting") var/rtn2 = Recreate_MC() @@ -417,7 +420,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new * Main loop! * This is where the magic happens. */ -/datum/controller/master/proc/Loop() +/datum/controller/master/proc/Loop(init_stage) . = -1 // Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because local vars rock @@ -428,7 +431,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/timer = world.time for (var/thing in subsystems) var/datum/controller/subsystem/SS = thing - if (SS.subsystem_flags & SS_NO_FIRE) + // Skip non-firing + if(SS.subsystem_flags & SS_NO_FIRE) + continue + // Skip those that are after our init stage, or are not initialized. + if(SS.init_stage > init_stage || !SS.initialized) continue SS.queued_time = 0 @@ -485,33 +492,46 @@ GLOBAL_REAL(Master, /datum/controller/master) = new while (1) tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag))) var/starting_tick_usage = TICK_USAGE + + // If another stage of init was completed, restart. + if (init_stage != init_stage_completed) + return MC_LOOP_RTN_NEWSTAGES + // If we're paused for some reason, well, pause. if (processing <= 0) current_ticklimit = TICK_LIMIT_RUNNING sleep(10) continue - /** - * Anti-tick-contention heuristics: - * If there are mutiple sleeping procs running before us hogging the cpu, we have to run later. - * (because sleeps are processed in the order received, longer sleeps are more likely to run first) - */ - if (starting_tick_usage > TICK_LIMIT_MC) // If there isn't enough time to bother doing anything this tick, sleep a bit. - sleep_delta *= 2 - current_ticklimit = TICK_LIMIT_RUNNING * 0.5 - sleep(world.tick_lag * (processing * sleep_delta)) - continue + // If we're fully initialized, run normal tick heuristics. Otherwise, always run every tick. + if (init_stage == INITSTAGE_MAX) + /** + * Anti-tick-contention heuristics: + * If there are mutiple sleeping procs running before us hogging the cpu, we have to run later. + * (because sleeps are processed in the order received, longer sleeps are more likely to run first) + */ + if (starting_tick_usage > TICK_LIMIT_MC) + // If there isn't enough time to bother doing anything this tick, sleep increasingly longer times. + sleep_delta *= 2 + // Instruct CHECK_TICK to use a lot less tick than it usually wouldb e allowed to. + current_ticklimit = TICK_LIMIT_RUNNING * 0.5 + sleep(world.tick_lag * (processing * sleep_delta)) + continue - /** - * Byond resumed us late. - * Assume it might have to do the same next tick. - */ - if (last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time) - sleep_delta += 1 + /** + * Byond resumed us late. + * Assume it might have to do the same next tick. + */ + if (last_run + CEILING(world.tick_lag * (processing * sleep_delta), world.tick_lag) < world.time) + sleep_delta += 1 - sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) // Decay sleep_delta. + // Decay sleep_delta + sleep_delta = MC_AVERAGE_FAST(sleep_delta, 1) - if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) // We ran 3/4 of the way into the tick. - sleep_delta += 1 + // We ran 3/4 of the way into the tick + if (starting_tick_usage > (TICK_LIMIT_MC*0.75)) + sleep_delta += 1 + else + sleep_delta = 1 //# Debug. if (make_runtime) @@ -553,30 +573,40 @@ GLOBAL_REAL(Master, /datum/controller/master) = new continue if (queue_head) - if (RunQueue() <= 0) - if (!SoftReset(SStickersubsystems, runlevel_sorted_subsystems)) - log_world("MC: SoftReset() failed, crashing") - return - - if (!error_level) - iteration++ - + if (RunQueue() <= 0) //error running queue + stack_trace("MC: RunQueue failed. Current error_level is [round(error_level, 0.25)]") + if (error_level > 1) //skip the first error, + if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) + CRASH("MC: SoftReset() failed, exiting loop()") + + if (error_level <= 2) //after 3 strikes stop incrmenting our iteration so failsafe enters defcon + iteration++ + else + cached_runlevel = null //3 strikes, Lets also reset the runlevel lists + current_ticklimit = TICK_LIMIT_RUNNING + sleep((1 SECONDS) * error_level) + error_level++ + continue error_level++ - current_ticklimit = TICK_LIMIT_RUNNING - sleep(10) - continue - - error_level-- - if (!queue_head) // Reset the counts if the queue is empty, in the off chance they get out of sync. + if (error_level > 0) + error_level = max(MC_AVERAGE_SLOW(error_level-1, error_level), 0) + if (!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync queue_priority_count = 0 queue_priority_count_bg = 0 iteration++ last_run = world.time src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) - current_ticklimit = TICK_LIMIT_RUNNING - if (processing * sleep_delta <= world.tick_lag) - current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) // Reserve the tail 1/4 of the next tick for the mc if we plan on running next tick. + + // We're about to go to sleep. Set the tick budget for other sleeping procs. + if (init_stage != INITSTAGE_MAX) + // Still initializing, allow up to 100% dilation (50% of normal FPS). + current_ticklimit = TICK_LIMIT_RUNNING * 2 + else + // Already initialized; use normal heuristics. + current_ticklimit = TICK_LIMIT_RUNNING + if (processing * sleep_delta <= world.tick_lag) + current_ticklimit -= (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc if we plan on running next tick sleep(world.tick_lag * (processing * sleep_delta)) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index ef0377a89b5f..f1ee35bda4d2 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -15,16 +15,24 @@ */ name = "fire coderbus" + //* Initialization & Shutdown *// + /** * Order of initialization. * Higher numbers are initialized first, lower numbers later. * Use or create defines such as [INIT_ORDER_DEFAULT] so we can see the order in one file. + * + * * This is secondary to [init_stage]. */ var/init_order = INIT_ORDER_DEFAULT /** * Which stage does this subsystem init at. Earlier stages can fire while later stages init. + * + * * This is higher in precedence than [init_order]. */ var/init_stage = MC_INIT_STAGE_WORLD + /// This var is set to TRUE after the subsystem has been initialized. + var/initialized = FALSE /** * Time to wait (in deciseconds) between each call to fire(). @@ -48,10 +56,6 @@ */ var/subsystem_flags = NONE - /// This var is set to TRUE after the subsystem has been initialized. - // todo: see __DEFINES/controllers/_subsystems.dm; this shouldn't just be TRUE / FALSE - var/initialized = FALSE - /** * Set to FALSE to prevent fire() calls, mostly for admin use or subsystems that may be resumed later. * use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick. diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 74ebf6cd6021..47cb6a658304 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -3,6 +3,8 @@ SUBSYSTEM_DEF(atoms) init_order = INIT_ORDER_ATOMS subsystem_flags = SS_NO_FIRE + #warn time to stop using `initialized` variable! + /// A stack of list(source, desired initialized state) /// We read the source of init changes from the last entry, and assert that all changes will come with a reset var/list/initialized_state = list() From 24b5f288475cc6413da07fec889d4381a39dc265 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 03:58:42 -0500 Subject: [PATCH 03/28] init stages --- code/__DEFINES/MC.dm | 26 ++++++++++++------- code/controllers/master.dm | 15 ++++++----- code/controllers/subsystem.dm | 4 +-- code/controllers/subsystem/ai_holders.dm | 3 ++- code/controllers/subsystem/ai_movement.dm | 2 +- code/controllers/subsystem/ai_scheduling.dm | 2 +- code/controllers/subsystem/air.dm | 3 ++- code/controllers/subsystem/alarm.dm | 2 +- .../controllers/subsystem/ambient_lighting.dm | 2 +- code/controllers/subsystem/ao.dm | 2 +- code/controllers/subsystem/assets.dm | 6 ++--- code/controllers/subsystem/atoms.dm | 3 ++- .../subsystem/characters/_characters.dm | 2 +- code/controllers/subsystem/dbcore/_dbcore.dm | 3 ++- code/controllers/subsystem/early_init.dm | 3 ++- code/controllers/subsystem/events.dm | 2 +- code/controllers/subsystem/fail2topic.dm | 3 ++- code/controllers/subsystem/game_master.dm | 2 +- code/controllers/subsystem/holomaps.dm | 2 +- code/controllers/subsystem/icon_smooth.dm | 4 +-- code/controllers/subsystem/job/_job.dm | 3 ++- code/controllers/subsystem/legacy_atc.dm | 2 +- code/controllers/subsystem/legacy_lore.dm | 2 +- code/controllers/subsystem/lighting.dm | 2 +- code/controllers/subsystem/lobby.dm | 3 ++- code/controllers/subsystem/machines.dm | 2 +- .../controllers/subsystem/mapping/_mapping.dm | 3 +-- code/controllers/subsystem/materials.dm | 2 +- code/controllers/subsystem/media_tracks.dm | 2 +- code/controllers/subsystem/minimaps.dm | 2 +- code/controllers/subsystem/nightshift.dm | 5 ++-- code/controllers/subsystem/overlays.dm | 3 ++- code/controllers/subsystem/overmap.dm | 2 +- .../subsystem/persistence/persistence.dm | 5 +--- code/controllers/subsystem/planets.dm | 2 +- code/controllers/subsystem/plants.dm | 2 +- code/controllers/subsystem/preferences.dm | 3 ++- .../subsystem/processing/circuits.dm | 2 +- .../subsystem/processing/instruments.dm | 3 ++- code/controllers/subsystem/repository.dm | 3 ++- code/controllers/subsystem/shuttles.dm | 3 +-- code/controllers/subsystem/sound/_sound.dm | 3 ++- code/controllers/subsystem/spatial_grids.dm | 1 + code/controllers/subsystem/statpanel.dm | 7 ++--- code/controllers/subsystem/supply.dm | 2 +- code/controllers/subsystem/ticker.dm | 3 +-- code/controllers/subsystem/transfer.dm | 2 +- code/controllers/subsystem/vis_overlays.dm | 2 +- code/controllers/subsystem/vote.dm | 4 +-- code/controllers/subsystem/world.dm | 2 +- code/controllers/subsystem/xenoarch.dm | 2 +- code/controllers/subsystem/zcopy.dm | 2 +- 52 files changed, 93 insertions(+), 84 deletions(-) diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 9b914b17940d..80fa63e3e1e0 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -43,27 +43,35 @@ //* MC init stages must be a positive number, and init stages must all be consequetive! *// /// Early initializations required for server function; database, timers, tgui, etc -#define MC_INIT_STAGE_BACKEND 1 +#define INIT_STAGE_BACKEND 1 /// Pre-mapload initializations -#define MC_INIT_STAGE_EARLY 2 +#define INIT_STAGE_EARLY 2 /// Mapload -#define MC_INIT_STAGE_WORLD 3 +#define INIT_STAGE_WORLD 3 /// Late -#define MC_INIT_STAGE_LATE 4 +#define INIT_STAGE_LATE 4 /// Last init stage we need to do. /// /// * This must be set to the maximum INIT_STAGE. -#define MC_INIT_STAGE_MAX 4 +#define INIT_STAGE_MAX 4 //! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier) -/// subsystem does not initialize. +/** + * Subsystem does not need Initialize() called. + * + * * The subsystem will still fire when its init stage is completed, unless it is + * marked with [SS_NO_FIRE] or its `can_fire` is set to FALSE. + */ #define SS_NO_INIT (1<<0) -/** subsystem does not fire. */ -/// (like can_fire = 0, but keeps it from getting added to the processing subsystems list) -/// (Requires a MC restart to change) +/** + * Subsystem does not need fire() called / does not require scheduling and ticking. + * + * * This is exactly like setting `can_fire` to FALSE, but is permanent without a MC restart. + * * Use for subsystems that will never require processing, rather than one that needs it turned off and on now and then. + */ #define SS_NO_FIRE (1<<1) /** subsystem only runs on spare cpu (after all non-background subsystems have ran that tick) */ diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 5a3fa715c3cb..d1054e5e0f45 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -255,8 +255,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // We want to initialize subsystems by stage, in the init_order provided for subsystems within the same stage. init_stage_completed = 0 - var/list/stage_sorted_subsystems = new(MC_INIT_STAGE_MAX) - for(var/i in 1 to MC_INIT_STAGE_MAX) + var/list/stage_sorted_subsystems = new(INIT_STAGE_MAX) + for(var/i in 1 to INIT_STAGE_MAX) stage_sorted_subsystems[i] = list() // Sort subsystems by init_order, so they initialize in the correct order. @@ -265,9 +265,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // Collect subsystems by init_stage. This has precedence over init_order. for(var/datum/controller/subsystem/subsystem as anything in subsystems) var/subsystem_init_stage = subsystem.init_stage - if (!isnum(subsystem_init_stage) || subsystem_init_stage < 1 || subsystem_init_stage > MC_INIT_STAGE_MAX || round(subsystem_init_stage) != subsystem_init_stage) - stack_trace("ERROR: MC: subsystem `[subsystem.type]` has invalid init_stage: `[subsystem_init_stage]`. Setting to `[MC_INIT_STAGE_MAX]`") - subsystem_init_stage = subsystem.init_stage = MC_INIT_STAGE_MAX + if (!isnum(subsystem_init_stage) || subsystem_init_stage < 1 || subsystem_init_stage > INIT_STAGE_MAX || round(subsystem_init_stage) != subsystem_init_stage) + stack_trace("ERROR: MC: subsystem `[subsystem.type]` has invalid init_stage: `[subsystem_init_stage]`. Setting to `[INIT_STAGE_MAX]`") + subsystem_init_stage = subsystem.init_stage = INIT_STAGE_MAX stage_sorted_subsystems[subsystem_init_stage] += subsystem // Sort subsystems by display setting for easy access. @@ -352,7 +352,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new message_prefix = "Failed to initialize [subsystem.name] subsystem after" tell_everyone = TRUE chat_warning = TRUE - subsystem.initialized = FALSE + // Since this is an explicit failure, shut its ticking off. + subsystem.subsystem_flags |= SS_NO_FIRE if(SS_INIT_NONE) message_prefix = "Initialized [subsystem.name] subsystem with errors within" tell_everyone = TRUE @@ -435,7 +436,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(SS.subsystem_flags & SS_NO_FIRE) continue // Skip those that are after our init stage, or are not initialized. - if(SS.init_stage > init_stage || !SS.initialized) + if(SS.init_stage > init_stage) continue SS.queued_time = 0 diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index f1ee35bda4d2..6e4f5e8f6111 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -30,7 +30,7 @@ * * * This is higher in precedence than [init_order]. */ - var/init_stage = MC_INIT_STAGE_WORLD + var/init_stage = INIT_STAGE_WORLD /// This var is set to TRUE after the subsystem has been initialized. var/initialized = FALSE @@ -335,7 +335,7 @@ * Used to initialize the subsystem AFTER the map has loaded. * This is expected to be overriden by subtypes. */ -/datum/controller/subsystem/Initialize(start_timeofday) +/datum/controller/subsystem/Initialize() return SS_INIT_NONE /** diff --git a/code/controllers/subsystem/ai_holders.dm b/code/controllers/subsystem/ai_holders.dm index 2ecfc9b96c02..a4b304c37575 100644 --- a/code/controllers/subsystem/ai_holders.dm +++ b/code/controllers/subsystem/ai_holders.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(ai_holders) subsystem_flags = NONE priority = FIRE_PRIORITY_AI_HOLDERS init_order = INIT_ORDER_AI_HOLDERS + init_stage = INIT_STAGE_EARLY wait = 0 /// all ticking ai holders @@ -37,7 +38,7 @@ SUBSYSTEM_DEF(ai_holders) /datum/controller/subsystem/ai_holders/Initialize() active_holders = list() rebuild() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ai_holders/on_ticklag_changed(old_ticklag, new_ticklag) rebuild() diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index 619744ad82fb..92238d41b5af 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -45,7 +45,7 @@ SUBSYSTEM_DEF(ai_movement) moving_ais = list() rebuild() init_ai_pathfinders() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ai_movement/on_ticklag_changed(old_ticklag, new_ticklag) rebuild() diff --git a/code/controllers/subsystem/ai_scheduling.dm b/code/controllers/subsystem/ai_scheduling.dm index 0919a8e4c08a..ec549ab98192 100644 --- a/code/controllers/subsystem/ai_scheduling.dm +++ b/code/controllers/subsystem/ai_scheduling.dm @@ -34,7 +34,7 @@ SUBSYSTEM_DEF(ai_scheduling) /datum/controller/subsystem/ai_scheduling/Initialize() rebuild() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ai_scheduling/on_ticklag_changed(old_ticklag, new_ticklag) rebuild() diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index a7b59ec711b0..ed3d01c9f284 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -131,7 +131,8 @@ SUBSYSTEM_DEF(air) ) #endif - return ..() + + return SS_INIT_SUCCESS /datum/controller/subsystem/air/fire(resumed = FALSE) var/timer diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm index 749ce940ffcb..9a20e427768e 100644 --- a/code/controllers/subsystem/alarm.dm +++ b/code/controllers/subsystem/alarm.dm @@ -17,7 +17,7 @@ SUBSYSTEM_DEF(alarms) /datum/controller/subsystem/alarms/Initialize() SSalarms.all_handlers = list(atmosphere_alarm, camera_alarm, fire_alarm, motion_alarm, power_alarm) - . = ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/alarms/fire(resumed = FALSE) if(!resumed) diff --git a/code/controllers/subsystem/ambient_lighting.dm b/code/controllers/subsystem/ambient_lighting.dm index 222be9449a7f..56b5e46c45c5 100644 --- a/code/controllers/subsystem/ambient_lighting.dm +++ b/code/controllers/subsystem/ambient_lighting.dm @@ -13,7 +13,7 @@ SUBSYSTEM_DEF(ambient_lighting) /datum/controller/subsystem/ambient_lighting/Initialize(start_timeofday) fire(FALSE, TRUE) - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ambient_lighting/fire(resumed = FALSE, no_mc_tick = FALSE) var/list/curr = queued diff --git a/code/controllers/subsystem/ao.dm b/code/controllers/subsystem/ao.dm index f038c6ff78d8..2baa405c8281 100644 --- a/code/controllers/subsystem/ao.dm +++ b/code/controllers/subsystem/ao.dm @@ -13,7 +13,7 @@ SUBSYSTEM_DEF(ao) /datum/controller/subsystem/ao/Initialize(start_timeofday) fire(FALSE, TRUE) - ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ao/fire(resumed = 0, no_mc_tick = FALSE) if (!resumed) diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index 210359902069..eff83427a200 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(assets) name = "Assets" init_order = INIT_ORDER_ASSETS + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE /// asset packs by type; this is for hardcoded assets @@ -31,13 +32,11 @@ SUBSYSTEM_DEF(assets) /datum/controller/subsystem/assets/Initialize(timeofday) // detect_cache_worthiness() - for(var/datum/asset_pack/path as anything in typesof(/datum/asset_pack)) if(path == initial(path.abstract_type)) continue var/datum/asset_pack/instance = new path register_asset_pack(instance, TRUE) - #ifndef DO_NOT_DEFER_ASSETS if(initial(instance.load_deferred)) continue @@ -48,8 +47,7 @@ SUBSYSTEM_DEF(assets) #else instance.load() #endif - - return ..() + return SS_INIT_SUCCESS /** * register an asset pack to make it able to be resolved or loaded diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 47cb6a658304..2844434b1203 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(atoms) name = "Atoms" init_order = INIT_ORDER_ATOMS + init_stage = INIT_STAGE_WORLD subsystem_flags = SS_NO_FIRE #warn time to stop using `initialized` variable! @@ -35,7 +36,7 @@ SUBSYSTEM_DEF(atoms) InitializeAtoms() initialized = INITIALIZATION_INNEW_REGULAR - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms, list/atoms_to_return) if(initialized == INITIALIZATION_INSSATOMS) diff --git a/code/controllers/subsystem/characters/_characters.dm b/code/controllers/subsystem/characters/_characters.dm index 139ea5f58572..3fce249db414 100644 --- a/code/controllers/subsystem/characters/_characters.dm +++ b/code/controllers/subsystem/characters/_characters.dm @@ -29,7 +29,7 @@ SUBSYSTEM_DEF(characters) stack_trace("what?") continue P.Initialize() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/characters/Recover() . = ..() diff --git a/code/controllers/subsystem/dbcore/_dbcore.dm b/code/controllers/subsystem/dbcore/_dbcore.dm index 728980a93fc8..106a7fe09119 100644 --- a/code/controllers/subsystem/dbcore/_dbcore.dm +++ b/code/controllers/subsystem/dbcore/_dbcore.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(dbcore) subsystem_flags = SS_BACKGROUND wait = 1 MINUTES init_order = INIT_ORDER_DBCORE + init_stage = INIT_STAGE_BACKEND var/failed_connection_timeout = 0 var/schema_mismatch = 0 @@ -26,7 +27,7 @@ SUBSYSTEM_DEF(dbcore) if(2) message_admins("Could not get schema version from database") - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/dbcore/fire() for(var/I in active_queries) diff --git a/code/controllers/subsystem/early_init.dm b/code/controllers/subsystem/early_init.dm index 603af16e47fc..6beebb4771f6 100644 --- a/code/controllers/subsystem/early_init.dm +++ b/code/controllers/subsystem/early_init.dm @@ -1,9 +1,10 @@ SUBSYSTEM_DEF(early_init) name = "Early Init" init_order = INIT_ORDER_EARLY_INIT + init_stage= INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE /datum/controller/subsystem/early_init/Initialize() init_inventory_slot_meta() init_crayon_decal_meta() - return ..() + return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index f846f197e125..b04bfb4b2e39 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -33,7 +33,7 @@ SUBSYSTEM_DEF(events) // unfortunately, character setup server startup hooks fire before /Initialize so :/ // SScharactersetup but not shit when :) InitializeHolidays() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/events/fire(resumed) if (!resumed) diff --git a/code/controllers/subsystem/fail2topic.dm b/code/controllers/subsystem/fail2topic.dm index cabfd73553a1..74e5c24040cf 100644 --- a/code/controllers/subsystem/fail2topic.dm +++ b/code/controllers/subsystem/fail2topic.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(fail2topic) name = "Fail2Topic" init_order = INIT_ORDER_FAIL2TOPIC + init_stage = INIT_STAGE_BACKEND subsystem_flags = SS_BACKGROUND runlevels = ALL @@ -28,7 +29,7 @@ SUBSYSTEM_DEF(fail2topic) subsystem_flags |= SS_NO_FIRE can_fire = FALSE - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/fail2topic/fire() if(length(rate_limiting)) diff --git a/code/controllers/subsystem/game_master.dm b/code/controllers/subsystem/game_master.dm index b6ec462c586c..6167c617639e 100644 --- a/code/controllers/subsystem/game_master.dm +++ b/code/controllers/subsystem/game_master.dm @@ -30,7 +30,7 @@ SUBSYSTEM_DEF(gamemaster) suspended = FALSE else sleep(30 SECONDS) - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/gamemaster/fire(resumed) if(SSticker && SSticker.current_state == GAME_STATE_PLAYING && !suspended) diff --git a/code/controllers/subsystem/holomaps.dm b/code/controllers/subsystem/holomaps.dm index b0ed4e6325a8..f26e2d178eb2 100644 --- a/code/controllers/subsystem/holomaps.dm +++ b/code/controllers/subsystem/holomaps.dm @@ -16,4 +16,4 @@ SUBSYSTEM_DEF(holomaps) /datum/controller/subsystem/holomaps/Initialize(timeofday) generateHoloMinimaps() - . = ..() + return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm index 737fcee26167..a2008657cea3 100644 --- a/code/controllers/subsystem/icon_smooth.dm +++ b/code/controllers/subsystem/icon_smooth.dm @@ -44,9 +44,7 @@ SUBSYSTEM_DEF(icon_smooth) smoothing_atom.smooth_icon() CHECK_TICK #endif - - return ..() - + return SS_INIT_SUCCESS /datum/controller/subsystem/icon_smooth/proc/add_to_queue(atom/thing) if(thing.smoothing_flags & SMOOTH_QUEUED) diff --git a/code/controllers/subsystem/job/_job.dm b/code/controllers/subsystem/job/_job.dm index 8d3e88e0349b..dc63fb444b0f 100644 --- a/code/controllers/subsystem/job/_job.dm +++ b/code/controllers/subsystem/job/_job.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(job) name = "Job" init_order = INIT_ORDER_JOBS + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE /// List of all jobs @@ -27,7 +28,7 @@ SUBSYSTEM_DEF(job) if(!length(occupations)) setup_occupations() reconstruct_job_ui_caches() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/job/Recover() init_access() diff --git a/code/controllers/subsystem/legacy_atc.dm b/code/controllers/subsystem/legacy_atc.dm index 68ed238958f5..407bc57ad910 100644 --- a/code/controllers/subsystem/legacy_atc.dm +++ b/code/controllers/subsystem/legacy_atc.dm @@ -41,7 +41,7 @@ SUBSYSTEM_DEF(legacy_atc) next_message = world.time + initial_delay START_PROCESSING(SSobj, src) - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/legacy_atc/process(delta_time) if(world.time >= next_message) diff --git a/code/controllers/subsystem/legacy_lore.dm b/code/controllers/subsystem/legacy_lore.dm index 163dedfd9e91..56001882b5b8 100644 --- a/code/controllers/subsystem/legacy_lore.dm +++ b/code/controllers/subsystem/legacy_lore.dm @@ -13,4 +13,4 @@ SUBSYSTEM_DEF(legacy_lore) if(initial(instance.name)) instance = new path() organizations[path] = instance - return ..() + return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 5bff0ce41f92..2413696bd967 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -114,7 +114,7 @@ SUBSYSTEM_DEF(lighting) ) log_subsystem("lighting", "NOv:[overlaycount] L:[processed_lights] C:[processed_corners] O:[processed_overlays]") - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/lighting/proc/InitializeZlev(zlev) for (var/thing in Z_ALL_TURFS(zlev)) diff --git a/code/controllers/subsystem/lobby.dm b/code/controllers/subsystem/lobby.dm index e0c8f1b8d8d5..c45269304bd4 100644 --- a/code/controllers/subsystem/lobby.dm +++ b/code/controllers/subsystem/lobby.dm @@ -5,13 +5,14 @@ SUBSYSTEM_DEF(lobby) name = "Lobby Manager" subsystem_flags = SS_NO_FIRE init_order = INIT_ORDER_LOBBY + init_stage = INIT_STAGE_EARLY /// our titlescreen var/datum/cutscene/titlescreen /datum/controller/subsystem/lobby/Initialize() initialize_title_scene() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/lobby/proc/initialize_title_scene() refresh_title_scene() diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index d0fa25883a7f..d6dfcb2c2973 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -45,7 +45,7 @@ SUBSYSTEM_DEF(machines) report_progress("Initializing atmos machinery...") setup_atmos_machinery(GLOB.machines) fire() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/machines/fire(resumed = 0) var/timer = TICK_USAGE diff --git a/code/controllers/subsystem/mapping/_mapping.dm b/code/controllers/subsystem/mapping/_mapping.dm index eabccb9b489d..b51368f09eb9 100644 --- a/code/controllers/subsystem/mapping/_mapping.dm +++ b/code/controllers/subsystem/mapping/_mapping.dm @@ -43,8 +43,7 @@ SUBSYSTEM_DEF(mapping) // finalize // todo: refactor repopulate_sorted_areas() - - return ..() + return SS_INIT_SUCCESS // // Mapping subsystem handles initialization of random map elements at server start diff --git a/code/controllers/subsystem/materials.dm b/code/controllers/subsystem/materials.dm index 4b53be2b5be1..7311cbbc0976 100644 --- a/code/controllers/subsystem/materials.dm +++ b/code/controllers/subsystem/materials.dm @@ -25,7 +25,7 @@ SUBSYSTEM_DEF(materials) /datum/controller/subsystem/materials/Initialize() initialize_material_recipes() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/materials/Recover() initialize_material_recipes() diff --git a/code/controllers/subsystem/media_tracks.dm b/code/controllers/subsystem/media_tracks.dm index 960e1be5b261..968dea1f9a5d 100644 --- a/code/controllers/subsystem/media_tracks.dm +++ b/code/controllers/subsystem/media_tracks.dm @@ -13,7 +13,7 @@ SUBSYSTEM_DEF(media_tracks) /datum/controller/subsystem/media_tracks/Initialize(timeofday) load_tracks() sort_tracks() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/media_tracks/proc/load_tracks() var/jukebox_track_file = "config_static/jukebox.json" diff --git a/code/controllers/subsystem/minimaps.dm b/code/controllers/subsystem/minimaps.dm index 893554388f5d..63a4d3bb9a13 100644 --- a/code/controllers/subsystem/minimaps.dm +++ b/code/controllers/subsystem/minimaps.dm @@ -9,7 +9,7 @@ SUBSYSTEM_DEF(minimaps) to_chat(world, "Minimaps disabled! Skipping init.") return ..() build_minimaps() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/minimaps/proc/build_minimaps() station_minimaps = list() diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm index 7321979537bb..f0fa4f7388b1 100644 --- a/code/controllers/subsystem/nightshift.dm +++ b/code/controllers/subsystem/nightshift.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(nightshift) name = "Night Shift" init_order = INIT_ORDER_NIGHTSHIFT + init_stage = INIT_STAGE_LATE priority = FIRE_PRIORITY_NIGHTSHIFT wait = 60 SECONDS subsystem_flags = SS_NO_TICK_CHECK @@ -22,9 +23,7 @@ SUBSYSTEM_DEF(nightshift) /datum/controller/subsystem/nightshift/Initialize() if (!CONFIG_GET(flag/nightshifts_enabled)) can_fire = FALSE - //if(CONFIG_GET(flag/randomized_start_time_enabled)) - //GLOB.gametime_offset = rand(0, 23) HOURS - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/nightshift/fire(resumed = FALSE) if(world.time - round_duration_in_ds < nightshift_first_check) diff --git a/code/controllers/subsystem/overlays.dm b/code/controllers/subsystem/overlays.dm index 1545a91280b0..5ed931866d9b 100644 --- a/code/controllers/subsystem/overlays.dm +++ b/code/controllers/subsystem/overlays.dm @@ -3,6 +3,7 @@ SUBSYSTEM_DEF(overlays) wait = 1 priority = FIRE_PRIORITY_OVERLAYS init_order = INIT_ORDER_OVERLAY + init_stage = INIT_STAGE_LATE runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY var/list/processing = list() @@ -23,7 +24,7 @@ SUBSYSTEM_DEF(overlays) /datum/controller/subsystem/overlays/Initialize() overlays_initialized = TRUE Flush() - ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/overlays/Shutdown() text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log") diff --git a/code/controllers/subsystem/overmap.dm b/code/controllers/subsystem/overmap.dm index bc994f2c4e2b..224e460db3c8 100644 --- a/code/controllers/subsystem/overmap.dm +++ b/code/controllers/subsystem/overmap.dm @@ -19,7 +19,7 @@ SUBSYSTEM_DEF(overmaps) /datum/controller/subsystem/overmaps/Initialize() make_default_overmap() rebuild_helm_computers() - return ..() + return SS_INIT_SUCCESS //! legacy code below diff --git a/code/controllers/subsystem/persistence/persistence.dm b/code/controllers/subsystem/persistence/persistence.dm index 806071a997be..e5965a985a0c 100644 --- a/code/controllers/subsystem/persistence/persistence.dm +++ b/code/controllers/subsystem/persistence/persistence.dm @@ -24,14 +24,11 @@ SUBSYSTEM_DEF(persistence) /datum/controller/subsystem/persistence/Initialize() /// build prototype lookup list build_prototype_id_lookup() - LoadPersistence() - // todo: should this be here? save_the_world is in ticker. if(CONFIG_GET(flag/persistence)) load_the_world() - - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/persistence/Shutdown() SavePersistence() diff --git a/code/controllers/subsystem/planets.dm b/code/controllers/subsystem/planets.dm index b01d171eff85..74e2b069c440 100644 --- a/code/controllers/subsystem/planets.dm +++ b/code/controllers/subsystem/planets.dm @@ -21,7 +21,7 @@ SUBSYSTEM_DEF(planets) report_progress("Initializing planetary weather.") allocateTurfs(TRUE) fire() // Fire once to preemptively set up weather and planetary ambient lighting. - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/planets/on_max_z_changed(old_z_count, new_z_count) . = ..() diff --git a/code/controllers/subsystem/plants.dm b/code/controllers/subsystem/plants.dm index 0ed5ca68eb1f..d32669e0be5f 100644 --- a/code/controllers/subsystem/plants.dm +++ b/code/controllers/subsystem/plants.dm @@ -34,7 +34,7 @@ SUBSYSTEM_DEF(plants) /datum/controller/subsystem/plants/Initialize(timeofday) setup() - return ..() + return SS_INIT_SUCCESS /** * Predefined/roundstart varieties use a string key to make it diff --git a/code/controllers/subsystem/preferences.dm b/code/controllers/subsystem/preferences.dm index cf35c223cf47..f9090109db7d 100644 --- a/code/controllers/subsystem/preferences.dm +++ b/code/controllers/subsystem/preferences.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(preferences) name = "Preferences" init_order = INIT_ORDER_PREFERENCES + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE var/list/datum/game_preference_entry/entries_by_key @@ -13,7 +14,7 @@ SUBSYSTEM_DEF(preferences) for(var/key in preferences_by_key) var/datum/game_preferences/prefs = preferences_by_key[key] prefs.initialize() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/preferences/proc/resolve_preference_entry(datum/game_preference_entry/entrylike) if(ispath(entrylike)) diff --git a/code/controllers/subsystem/processing/circuits.dm b/code/controllers/subsystem/processing/circuits.dm index cbf0adcf1b8b..52ded6c5325c 100644 --- a/code/controllers/subsystem/processing/circuits.dm +++ b/code/controllers/subsystem/processing/circuits.dm @@ -23,7 +23,7 @@ PROCESSING_SUBSYSTEM_DEF(circuit) /datum/controller/subsystem/processing/circuit/Initialize(timeofday) SScircuit.cipherkey = uppertext(random_string(2000+rand(0,10), GLOB.alphabet)) circuits_init() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/processing/circuit/proc/circuits_init() //Cached lists for free performance diff --git a/code/controllers/subsystem/processing/instruments.dm b/code/controllers/subsystem/processing/instruments.dm index 730f89d4b8ea..129506f2c5fe 100644 --- a/code/controllers/subsystem/processing/instruments.dm +++ b/code/controllers/subsystem/processing/instruments.dm @@ -2,6 +2,7 @@ PROCESSING_SUBSYSTEM_DEF(instruments) name = "Instruments" wait = 0.5 init_order = INIT_ORDER_INSTRUMENTS + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_KEEP_TIMING priority = FIRE_PRIORITY_INSTRUMENTS var/static/list/datum/instrument/instrument_data = list() //id = datum @@ -14,7 +15,7 @@ PROCESSING_SUBSYSTEM_DEF(instruments) /datum/controller/subsystem/processing/instruments/Initialize() initialize_instrument_data() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S) songs += S diff --git a/code/controllers/subsystem/repository.dm b/code/controllers/subsystem/repository.dm index 17a553eaa18d..04db037a4973 100644 --- a/code/controllers/subsystem/repository.dm +++ b/code/controllers/subsystem/repository.dm @@ -4,9 +4,10 @@ SUBSYSTEM_DEF(repository) name = "Repository System" init_order = INIT_ORDER_REPOSITORY + init_stage = INIT_STAGE_BACKEND subsystem_flags = SS_NO_FIRE /datum/controller/subsystem/repository/Initialize() __create_repositories() __init_repositories() - return ..() + return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index 2f1019cfa039..4bb02c2c0eca 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -49,7 +49,6 @@ SUBSYSTEM_DEF(shuttle) /// Shuttles remaining to process this fire() tick var/tmp/list/current_run - /** *! I made these shitty vars so we don't search for these in GOD DAMN WORLD *! If I find these are still here in 2023 I'll be very upset. @@ -63,7 +62,7 @@ SUBSYSTEM_DEF(shuttle) last_landmark_registration_time = world.time block_init_queue = FALSE process_init_queues() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/shuttle/fire(resumed = 0) if (!resumed) diff --git a/code/controllers/subsystem/sound/_sound.dm b/code/controllers/subsystem/sound/_sound.dm index 477ff2965771..3a2121a2fe78 100644 --- a/code/controllers/subsystem/sound/_sound.dm +++ b/code/controllers/subsystem/sound/_sound.dm @@ -3,8 +3,9 @@ SUBSYSTEM_DEF(sounds) name = "Sounds" subsystem_flags = SS_NO_FIRE init_order = INIT_ORDER_SOUNDS + init_stage = INIT_STAGE_BACKEND /datum/controller/subsystem/sounds/Initialize() setup_available_channels() setup_soundbytes() - return ..() + return SS_INIT_SUCCESS diff --git a/code/controllers/subsystem/spatial_grids.dm b/code/controllers/subsystem/spatial_grids.dm index 5f782bbc4ddd..a7d5d7cc7984 100644 --- a/code/controllers/subsystem/spatial_grids.dm +++ b/code/controllers/subsystem/spatial_grids.dm @@ -8,6 +8,7 @@ SUBSYSTEM_DEF(spatial_grids) name = "Spatial Grids" init_order = INIT_ORDER_SPATIAL_GRIDS + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE /// /living mobs. they don't have to be alive, just a subtype of /living. diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 5c5ea3e4dfaf..b4f981b4a5fb 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -2,7 +2,9 @@ SUBSYSTEM_DEF(statpanels) name = "Stat Panels" wait = 4 init_order = INIT_ORDER_STATPANELS + init_stage = INIT_STAGE_BACKEND priority = FIRE_PRIORITY_STATPANELS + subsystem_flags = SS_NO_INIT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY //! ticking @@ -19,11 +21,6 @@ SUBSYSTEM_DEF(statpanels) /// cached sdql2 data var/cache_sdql_data -/datum/controller/subsystem/statpanels/Initialize() - spawn() - manual_ticking() - return ..() - /datum/controller/subsystem/statpanels/fire(resumed = FALSE, no_tick_check) if(!resumed) // dispose / rebuild caches diff --git a/code/controllers/subsystem/supply.dm b/code/controllers/subsystem/supply.dm index c830a7159281..00b3f3856b9b 100644 --- a/code/controllers/subsystem/supply.dm +++ b/code/controllers/subsystem/supply.dm @@ -59,7 +59,7 @@ SUBSYSTEM_DEF(supply) for(var/key in legacy_supply_categories) flattened += key legacy_supply_categories = flattened - return ..() + return SS_INIT_SUCCESS // Supply shuttle SSticker - handles supply point regeneration // This is called by the process scheduler every thirty seconds diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 85c11386e172..a997765b4bd5 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -75,8 +75,7 @@ SUBSYSTEM_DEF(ticker) syndicate_code_response = generate_code_phrase() start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10) - - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/ticker/fire() switch(current_state) diff --git a/code/controllers/subsystem/transfer.dm b/code/controllers/subsystem/transfer.dm index 0040f5b42ce9..9820da271ec1 100644 --- a/code/controllers/subsystem/transfer.dm +++ b/code/controllers/subsystem/transfer.dm @@ -13,7 +13,7 @@ SUBSYSTEM_DEF(transfer) timerbuffer = config_legacy.vote_autotransfer_initial shift_hard_end = config_legacy.vote_autotransfer_initial + (config_legacy.vote_autotransfer_interval * NUMBER_OF_VOTE_EXTENSIONS) //Change this "1" to how many extend votes you want there to be. shift_last_vote = shift_hard_end - config_legacy.vote_autotransfer_interval - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/transfer/fire(resumed) currenttick = currenttick + 1 diff --git a/code/controllers/subsystem/vis_overlays.dm b/code/controllers/subsystem/vis_overlays.dm index b0e5d6c6896d..e8f0c71e7ba9 100644 --- a/code/controllers/subsystem/vis_overlays.dm +++ b/code/controllers/subsystem/vis_overlays.dm @@ -11,7 +11,7 @@ SUBSYSTEM_DEF(vis_overlays) /datum/controller/subsystem/vis_overlays/Initialize() vis_overlay_cache = list() unique_vis_overlays = list() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/vis_overlays/fire(resumed = FALSE) if(!resumed) diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 32a95513032c..3b57bd293c9a 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -35,9 +35,9 @@ SUBSYSTEM_DEF(vote) /datum/config_entry/string/default_on_transfer_tie default = "Extend the Shift" -/datum/controller/subsystem/vote/Initialize(start_timeofday) - . = ..() +/datum/controller/subsystem/vote/Initialize() ghost_weight_percent = CONFIG_GET(number/ghost_weight) + return SS_INIT_SUCCESS /datum/controller/subsystem/vote/fire(resumed) if(mode) diff --git a/code/controllers/subsystem/world.dm b/code/controllers/subsystem/world.dm index 36f95e492e2a..ff43eb9d9be5 100644 --- a/code/controllers/subsystem/world.dm +++ b/code/controllers/subsystem/world.dm @@ -41,7 +41,7 @@ SUBSYSTEM_DEF(game_world) initialize_locations() initialize_factions() initialize_map() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/game_world/proc/initialize_locations() location_lookup = list() diff --git a/code/controllers/subsystem/xenoarch.dm b/code/controllers/subsystem/xenoarch.dm index 0c0574a95f2b..e833522e1910 100644 --- a/code/controllers/subsystem/xenoarch.dm +++ b/code/controllers/subsystem/xenoarch.dm @@ -16,7 +16,7 @@ SUBSYSTEM_DEF(xenoarch) /datum/controller/subsystem/xenoarch/Initialize(timeofday) SetupXenoarch() - ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/xenoarch/Recover() if (istype(SSxenoarch.artifact_spawning_turfs)) diff --git a/code/controllers/subsystem/zcopy.dm b/code/controllers/subsystem/zcopy.dm index c745fdca6731..9adf8a3e0cf4 100644 --- a/code/controllers/subsystem/zcopy.dm +++ b/code/controllers/subsystem/zcopy.dm @@ -180,7 +180,7 @@ SUBSYSTEM_DEF(zcopy) calculate_zstack_limits() // Flush the queue. fire(FALSE, TRUE) - return ..() + return SS_INIT_SUCCESS // If you add a new Zlevel or change Z-connections, call this. /datum/controller/subsystem/zcopy/proc/calculate_zstack_limits() From 8f9d79c60f2c77bbe84d314d506b9cfe44d7939e Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:18:42 -0500 Subject: [PATCH 04/28] ssatoms --- citadel.dme | 1 + code/__DEFINES/controllers/_subsystems.dm | 38 ---------------- code/__DEFINES/controllers/atoms.dm | 37 ++++++++++++++++ code/controllers/subsystem/ai_movement.dm | 1 + code/controllers/subsystem/ai_scheduling.dm | 1 + code/controllers/subsystem/atoms.dm | 43 ++++++++++--------- .../atoms/atoms_initializing_EXPENSIVE.dm | 4 +- .../objects/structures/tables/materials.dm | 2 +- .../structures/tables/update_triggers.dm | 2 +- code/modules/mob/mob_helpers.dm | 2 +- 10 files changed, 67 insertions(+), 64 deletions(-) create mode 100644 code/__DEFINES/controllers/atoms.dm diff --git a/citadel.dme b/citadel.dme index 98411a41d277..601333c331cb 100644 --- a/citadel.dme +++ b/citadel.dme @@ -160,6 +160,7 @@ #include "code\__DEFINES\controllers\_repositories.dm" #include "code\__DEFINES\controllers\_subsystems.dm" #include "code\__DEFINES\controllers\assets.dm" +#include "code\__DEFINES\controllers\atoms.dm" #include "code\__DEFINES\controllers\dbcore.dm" #include "code\__DEFINES\controllers\grids.dm" #include "code\__DEFINES\controllers\persistence.dm" diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index e1ee97370702..f726e8be2e52 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -29,44 +29,6 @@ /// Succesfully initialized, BUT do not announce it to players (generally to hide game mechanics it would otherwise spoil) #define SS_INIT_NO_MESSAGE 4 -//! ## Initialization subsystem - -/// New should not call Initialize. -#define INITIALIZATION_INSSATOMS 0 -/// New should call Initialize(FALSE). -#define INITIALIZATION_INNEW_REGULAR 1 -/// New should call Initialize(TRUE). -#define INITIALIZATION_INNEW_MAPLOAD 2 - -//! ### Initialization hints - -/// Nothing happens -#define INITIALIZE_HINT_NORMAL 0 - -/** - * call LateInitialize at the end of all atom Initalization. - * - * The item will be added to the late_loaders list, this is iterated over after - * initalization of subsystems is complete and calls LateInitalize on the atom - * see [this file for the LateIntialize proc](atom.html#proc/LateInitialize) - */ -#define INITIALIZE_HINT_LATELOAD 1 - -/// Call qdel on the atom after intialization. -#define INITIALIZE_HINT_QDEL 2 - -/// type and all subtypes should always immediately call Initialize in New(). -#define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\ - ..();\ - if(!(atom_flags & ATOM_INITIALIZED)) {\ - var/previous_initialized_value = SSatoms.initialized;\ - SSatoms.initialized = INITIALIZATION_INNEW_MAPLOAD;\ - args[1] = TRUE;\ - SSatoms.InitAtom(src, FALSE, args);\ - SSatoms.initialized = previous_initialized_value;\ - }\ -} - //! ### SS runlevels //* Runlevels *// diff --git a/code/__DEFINES/controllers/atoms.dm b/code/__DEFINES/controllers/atoms.dm new file mode 100644 index 000000000000..851ab297914c --- /dev/null +++ b/code/__DEFINES/controllers/atoms.dm @@ -0,0 +1,37 @@ +//* Values for the `atom_init_status` variable on SSatoms. *// + +/// New should not call Initialize. +#define ATOM_INIT_IN_SUBSYSTEM 0 +/// New should call Initialize(FALSE) - not mapload +#define ATOM_INIT_IN_NEW_REGULAR 1 +/// New should call Initialize(TRUE) - is in a mapload +#define ATOM_INIT_IN_NEW_MAPLOAD 2 + +//! ### Initialization hints + +/// Nothing happens +#define INITIALIZE_HINT_NORMAL 0 + +/** + * call LateInitialize at the end of all atom Initalization. + * + * The item will be added to the late_loaders list, this is iterated over after + * initalization of subsystems is complete and calls LateInitalize on the atom + * see [this file for the LateIntialize proc](atom.html#proc/LateInitialize) + */ +#define INITIALIZE_HINT_LATELOAD 1 + +/// Call qdel on the atom after intialization. +#define INITIALIZE_HINT_QDEL 2 + +/// type and all subtypes should always immediately call Initialize in New(). +#define INITIALIZE_IMMEDIATE(X) ##X/New(loc, ...){\ + ..();\ + if(!(atom_flags & ATOM_INITIALIZED)) {\ + var/previous_initialized_value = SSatoms.initialized;\ + SSatoms.initialized = ATOM_INIT_IN_NEW_MAPLOAD;\ + args[1] = TRUE;\ + SSatoms.InitAtom(src, FALSE, args);\ + SSatoms.initialized = previous_initialized_value;\ + }\ +} diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index 92238d41b5af..0c909724f16d 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -18,6 +18,7 @@ SUBSYSTEM_DEF(ai_movement) subsystem_flags = NONE priority = FIRE_PRIORITY_AI_MOVEMENT init_order = INIT_ORDER_AI_MOVEMENT + init_stage = INIT_STAGE_EARLY wait = 0 /// ais that are moving using a movement handler right now diff --git a/code/controllers/subsystem/ai_scheduling.dm b/code/controllers/subsystem/ai_scheduling.dm index ec549ab98192..b9cefa12fb34 100644 --- a/code/controllers/subsystem/ai_scheduling.dm +++ b/code/controllers/subsystem/ai_scheduling.dm @@ -14,6 +14,7 @@ SUBSYSTEM_DEF(ai_scheduling) subsystem_flags = NONE priority = FIRE_PRIORITY_AI_SCHEDULING init_order = INIT_ORDER_AI_SCHEDULING + init_stage = INIT_STAGE_EARLY wait = 0 /// rolling bucket list; these hold the head node of linked ai_holders. diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 2844434b1203..b553f67d85df 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -27,28 +27,29 @@ SUBSYSTEM_DEF(atoms) var/list/mapload_init_times = list() #endif - initialized = INITIALIZATION_INSSATOMS + /// Status to use for atom New() / Initialize(). + var/atom_init_status = ATOM_INIT_IN_SUBSYSTEM /datum/controller/subsystem/atoms/Initialize(timeofday) init_start_time = world.time - initialized = INITIALIZATION_INNEW_MAPLOAD + atom_init_status = ATOM_INIT_IN_NEW_MAPLOAD InitializeAtoms() - initialized = INITIALIZATION_INNEW_REGULAR + atom_init_status = ATOM_INIT_IN_NEW_REGULAR return SS_INIT_SUCCESS /datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms, list/atoms_to_return) - if(initialized == INITIALIZATION_INSSATOMS) + if(atom_init_status == ATOM_INIT_IN_SUBSYSTEM) return // Generate a unique mapload source for this run of InitializeAtoms var/static/uid = 0 uid = (uid + 1) % (SHORT_REAL_LIMIT - 1) var/source = "subsystem init [uid]" - set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, source) + set_tracked_initalized(ATOM_INIT_IN_NEW_MAPLOAD, source) - // This may look a bit odd, but if the actual atom creation runtimes for some reason, we absolutely need to set initialized BACK + // This may look a bit odd, but if the actual atom creation runtimes for some reason, we absolutely need to set atom_init_status BACK CreateAtoms(atoms, atoms_to_return, source) clear_tracked_initalize(source) @@ -100,7 +101,7 @@ SUBSYSTEM_DEF(atoms) clear_tracked_initalize(mapload_source) stoplag() if(mapload_source) - set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, mapload_source) + set_tracked_initalized(ATOM_INIT_IN_NEW_MAPLOAD, mapload_source) PROFILE_INIT_ATOM_BEGIN() InitAtom(A, TRUE, mapload_arg) PROFILE_INIT_ATOM_END(A) @@ -121,7 +122,7 @@ SUBSYSTEM_DEF(atoms) clear_tracked_initalize(mapload_source) stoplag() if(mapload_source) - set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, mapload_source) + set_tracked_initalized(ATOM_INIT_IN_NEW_MAPLOAD, mapload_source) testing("Initialized [count] atoms") @@ -135,14 +136,14 @@ SUBSYSTEM_DEF(atoms) * * ... - rest of args are passed to new() / Initialize(). */ /datum/controller/subsystem/atoms/proc/instance_atom_immediate(path, mapload, atom/where, ...) - var/old_initialized = initialized - initialized = mapload? INITIALIZATION_INNEW_MAPLOAD : INITIALIZATION_INNEW_REGULAR + var/old_init_status = atom_init_status + atom_init_status = mapload? ATOM_INIT_IN_NEW_MAPLOAD : ATOM_INIT_IN_NEW_REGULAR var/atom/created = new path(arglist(args.Copy())) - initialized = old_initialized + atom_init_status = old_init_status return created /datum/controller/subsystem/atoms/proc/map_loader_begin(source) - set_tracked_initalized(INITIALIZATION_INSSATOMS, source) + set_tracked_initalized(ATOM_INIT_IN_SUBSYSTEM, source) /datum/controller/subsystem/atoms/proc/map_loader_stop(source) clear_tracked_initalize(source) @@ -158,9 +159,9 @@ SUBSYSTEM_DEF(atoms) /// Accepts a state and a source, the most recent state is used, sources exist to prevent overriding old values accidentally /datum/controller/subsystem/atoms/proc/set_tracked_initalized(state, source) if(!length(initialized_state)) - base_initialized = initialized + base_initialized = atom_init_status initialized_state += list(list(source, state)) - initialized = state + atom_init_status = state /datum/controller/subsystem/atoms/proc/clear_tracked_initalize(source) if(!length(initialized_state)) @@ -171,17 +172,17 @@ SUBSYSTEM_DEF(atoms) break if(!length(initialized_state)) - initialized = base_initialized - base_initialized = INITIALIZATION_INNEW_REGULAR + atom_init_status = base_initialized + base_initialized = ATOM_INIT_IN_NEW_REGULAR return - initialized = initialized_state[length(initialized_state)][2] + atom_init_status = initialized_state[length(initialized_state)][2] /// Returns TRUE if anything is currently being initialized /datum/controller/subsystem/atoms/proc/initializing_something() return length(initialized_state) > 1 /datum/controller/subsystem/atoms/proc/init_map_bounds(list/bounds) - if (initialized == INITIALIZATION_INSSATOMS) + if (atom_init_status == ATOM_INIT_IN_SUBSYSTEM) return // Let proper initialisation handle it later var/prev_shuttle_queue_state = SSshuttle.block_init_queue @@ -231,8 +232,8 @@ SUBSYSTEM_DEF(atoms) /datum/controller/subsystem/atoms/Recover() - initialized = SSatoms.initialized - if(initialized == INITIALIZATION_INNEW_MAPLOAD) + atom_init_status = SSatoms.atom_init_status + if(atom_init_status == ATOM_INIT_IN_NEW_MAPLOAD) InitializeAtoms() initialized_state = SSatoms.initialized_state BadInitializeCalls = SSatoms.BadInitializeCalls @@ -253,7 +254,7 @@ SUBSYSTEM_DEF(atoms) /// Prepares an atom to be deleted once the atoms SS is initialized. /datum/controller/subsystem/atoms/proc/prepare_deletion(atom/target) - if (initialized == INITIALIZATION_INNEW_REGULAR) + if (atom_init_status == ATOM_INIT_IN_NEW_REGULAR) // Atoms SS has already completed, just kill it now. qdel(target) else diff --git a/code/game/atoms/atoms_initializing_EXPENSIVE.dm b/code/game/atoms/atoms_initializing_EXPENSIVE.dm index 3c1dbacb0149..f46c03c53104 100644 --- a/code/game/atoms/atoms_initializing_EXPENSIVE.dm +++ b/code/game/atoms/atoms_initializing_EXPENSIVE.dm @@ -71,8 +71,8 @@ generate_tag() var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(do_initialize != ATOM_INIT_IN_SUBSYSTEM) + args[1] = do_initialize == ATOM_INIT_IN_NEW_MAPLOAD if(SSatoms.InitAtom(src, FALSE, args)) //we were deleted return diff --git a/code/game/objects/structures/tables/materials.dm b/code/game/objects/structures/tables/materials.dm index 6fdf575888dd..63bbcffae7b9 100644 --- a/code/game/objects/structures/tables/materials.dm +++ b/code/game/objects/structures/tables/materials.dm @@ -13,7 +13,7 @@ (reinforcing) = MATERIAL_SIGNIFICANCE_TABLE_REINFORCEMENT, ))) // sigh - if(SSatoms.initialized == INITIALIZATION_INNEW_REGULAR) + if(SSatoms.atom_init_status == ATOM_INIT_IN_NEW_REGULAR) update_connections(TRUE) update_appearance() diff --git a/code/game/objects/structures/tables/update_triggers.dm b/code/game/objects/structures/tables/update_triggers.dm index 237842124f91..cb7e785e52d4 100644 --- a/code/game/objects/structures/tables/update_triggers.dm +++ b/code/game/objects/structures/tables/update_triggers.dm @@ -1,6 +1,6 @@ /obj/structure/window/Initialize(mapload) . = ..() - if(SSatoms.initialized == INITIALIZATION_INNEW_REGULAR) + if(SSatoms.atom_init_status == ATOM_INIT_IN_NEW_REGULAR) for(var/obj/structure/table/T in view(src, 1)) T.update_connections() T.update_icon() diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index b81b51c2bd56..8107d87fc978 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -389,7 +389,7 @@ var/list/intents = list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM) */ /proc/notify_ghosts(message, ghost_sound, enter_link, atom/source, mutable_appearance/alert_overlay, action = NOTIFY_JUMP, flashwindow = TRUE, ignore_mapload = TRUE, ignore_key, ignore_dnr_observers = FALSE, header) //Easy notification of ghosts. // Don't notify for objects created during a mapload. - if(ignore_mapload && SSatoms.initialized != INITIALIZATION_INNEW_REGULAR) + if(ignore_mapload && SSatoms.atom_init_status != ATOM_INIT_IN_NEW_REGULAR) return for(var/mob/observer/dead/O in GLOB.player_list) if(!O.client) From a49981d9886970a0f85e67cf2a4b221a5e83d680 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:20:15 -0500 Subject: [PATCH 05/28] fix typos --- code/controllers/master.dm | 32 ++++++++++++++--------------- code/controllers/subsystem/atoms.dm | 2 -- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index d1054e5e0f45..ed9543f937bd 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -271,12 +271,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new stage_sorted_subsystems[subsystem_init_stage] += subsystem // Sort subsystems by display setting for easy access. - sortTim(subsystems, GLOBAL_PROC_REF(cmp_subsystem_display)) + tim_sort(subsystems, GLOBAL_PROC_REF(cmp_subsystem_display)) // Initialize subsystems. The ticker loop will be started immediately upon the first stage being done. var/rtod_start = REALTIMEOFDAY - for(var/current_init_stage in 1 to INITSTAGE_MAX) + for(var/current_init_stage in 1 to INIT_STAGE_MAX) for(var/datum/controller/subsystem/subsystem in stage_sorted_subsystems[current_init_stage]) initialize_subsystem(subsystem) CHECK_TICK @@ -289,8 +289,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new SetRunLevel(1) Master.StartProcessing(0) - var/end_rtod = REALTIMEOFDAY - var/took_seconds = round((end_rtod - start_rtod) / 10, 0.01) + var/rtod_end = REALTIMEOFDAY + var/took_seconds = round((rtod_end - rtod_start) / 10, 0.01) // Announce, log, and record end var/msg = "Initializations complete within [took_seconds] second[took_seconds == 1 ? "" : "s"]!" @@ -327,7 +327,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new */ /datum/controller/master/proc/initialize_subsystem(datum/controller/subsystem/subsystem) // Do not re-init already initialized subsystems if it's somehow called again. - if (subsystem.flags & SS_NO_INIT || subsystem.initialized) + if (subsystem.subsystem_flags & SS_NO_INIT || subsystem.initialized) return // todo: dylib high-precision timers @@ -369,10 +369,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new subsystem.initialized = TRUE if(SS_INIT_NO_NEED) else - warning("[subsystem.name] subsystem initialized, returning invalid result [result]. This is a bug.") + warning("[subsystem.name] subsystem initialized, returning invalid result [initialize_result]. This is a bug.") var/message = "[message_prefix] [took_seconds] second[took_seconds == 1 ? "" : "s"]." - var/chat_message = chat_warning ? SPAN_BOLDWARNING(message) : SPAN_BOLDANNOUNCE(MESSAGE) + var/chat_message = chat_warning ? SPAN_BOLDWARNING(message) : SPAN_BOLDANNOUNCE(message) if(tell_everyone && message_prefix) to_chat(world, chat_message) @@ -427,7 +427,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because local vars rock // All this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed) - var/list/SStickersubsystems = list() + var/list/ticker_subsystems = list() var/list/runlevel_sorted_subsystems = list(list()) // Ensure we always have at least one runlevel. var/timer = world.time for (var/thing in subsystems) @@ -448,7 +448,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new SS.recompute_wait_dt() if (SS.subsystem_flags & SS_TICKER) - SStickersubsystems += SS + ticker_subsystems += SS // Timer subsystems aren't allowed to bunch up, so we offset them a bit. timer += world.tick_lag * rand(0, 1) SS.next_fire = timer @@ -474,10 +474,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new * These sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue. * (higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add) */ - tim_sort(SStickersubsystems, GLOBAL_PROC_REF(cmp_subsystem_priority)) + tim_sort(ticker_subsystems, GLOBAL_PROC_REF(cmp_subsystem_priority)) for(var/I in runlevel_sorted_subsystems) tim_sort(I, GLOBAL_PROC_REF(cmp_subsystem_priority)) - I += SStickersubsystems + I += ticker_subsystems var/cached_runlevel = current_runlevel var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel] @@ -504,7 +504,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new continue // If we're fully initialized, run normal tick heuristics. Otherwise, always run every tick. - if (init_stage == INITSTAGE_MAX) + if (init_stage == INIT_STAGE_MAX) /** * Anti-tick-contention heuristics: * If there are mutiple sleeping procs running before us hogging the cpu, we have to run later. @@ -560,8 +560,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new SS.next_fire = world.time + world.tick_lag * rand(0, DS2TICKS(min(SS.wait, 2 SECONDS))) //* **Experimental**: Check every queue, every tick. - if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(SStickersubsystems) <= 0) - if (!SoftReset(SStickersubsystems, runlevel_sorted_subsystems)) + if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(ticker_subsystems) <= 0) + if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) log_world("MC: SoftReset() failed, crashing") return @@ -577,7 +577,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if (RunQueue() <= 0) //error running queue stack_trace("MC: RunQueue failed. Current error_level is [round(error_level, 0.25)]") if (error_level > 1) //skip the first error, - if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) + if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) CRASH("MC: SoftReset() failed, exiting loop()") if (error_level <= 2) //after 3 strikes stop incrmenting our iteration so failsafe enters defcon @@ -600,7 +600,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta) // We're about to go to sleep. Set the tick budget for other sleeping procs. - if (init_stage != INITSTAGE_MAX) + if (init_stage != INIT_STAGE_MAX) // Still initializing, allow up to 100% dilation (50% of normal FPS). current_ticklimit = TICK_LIMIT_RUNNING * 2 else diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index b553f67d85df..0d1ba5316e85 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -4,8 +4,6 @@ SUBSYSTEM_DEF(atoms) init_stage = INIT_STAGE_WORLD subsystem_flags = SS_NO_FIRE - #warn time to stop using `initialized` variable! - /// A stack of list(source, desired initialized state) /// We read the source of init changes from the last entry, and assert that all changes will come with a reset var/list/initialized_state = list() From 85789945a6a2934e653c321be9ec014faf05b7b4 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:00:13 -0500 Subject: [PATCH 06/28] more init changes --- citadel.dme | 3 +- code/__DEFINES/MC-init.dm | 94 +++++++++++++++++++ code/__DEFINES/MC.dm | 24 ++--- code/__DEFINES/controllers/_subsystems.dm | 79 ---------------- code/__HELPERS/sorts/comparators.dm | 19 ---- code/controllers/master.dm | 19 ++-- code/controllers/subsystem.dm | 53 ++++++++--- code/controllers/subsystem/early_init.dm | 2 +- code/controllers/subsystem/spatial_grids.dm | 2 +- code/controllers/subsystem/timer.dm | 1 + .../subsystem/{lobby.dm => titlescreen.dm} | 22 ++--- .../atoms/atoms_initializing_EXPENSIVE.dm | 2 +- code/modules/mob/new_player/login.dm | 2 +- 13 files changed, 173 insertions(+), 149 deletions(-) create mode 100644 code/__DEFINES/MC-init.dm rename code/controllers/subsystem/{lobby.dm => titlescreen.dm} (64%) diff --git a/citadel.dme b/citadel.dme index 601333c331cb..de40196dd17a 100644 --- a/citadel.dme +++ b/citadel.dme @@ -64,6 +64,7 @@ #include "code\__DEFINES\maps.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\matrices.dm" +#include "code\__DEFINES\MC-init.dm" #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\metrics.dm" #include "code\__DEFINES\misc.dm" @@ -572,7 +573,6 @@ #include "code\controllers\subsystem\legacy_atc.dm" #include "code\controllers\subsystem\legacy_lore.dm" #include "code\controllers\subsystem\lighting.dm" -#include "code\controllers\subsystem\lobby.dm" #include "code\controllers\subsystem\machines.dm" #include "code\controllers\subsystem\materials.dm" #include "code\controllers\subsystem\media_tracks.dm" @@ -608,6 +608,7 @@ #include "code\controllers\subsystem\ticker.dm" #include "code\controllers\subsystem\time_track.dm" #include "code\controllers\subsystem\timer.dm" +#include "code\controllers\subsystem\titlescreen.dm" #include "code\controllers\subsystem\transcore_vr.dm" #include "code\controllers\subsystem\transfer.dm" #include "code\controllers\subsystem\turbolift.dm" diff --git a/code/__DEFINES/MC-init.dm b/code/__DEFINES/MC-init.dm new file mode 100644 index 000000000000..762f46046d52 --- /dev/null +++ b/code/__DEFINES/MC-init.dm @@ -0,0 +1,94 @@ +//* Initialization Stages *// +//* After each stage, the MC starts ticking that stage while later stages are still waiting to init. *// +//* MC init stages must be a positive number, and init stages must all be consequetive! *// + +/// Early initializations required for server function; database, timers, tgui, etc +#define INIT_STAGE_BACKEND 1 +/// Pre-mapload initializations +#define INIT_STAGE_EARLY 2 +/// Mapload +#define INIT_STAGE_WORLD 3 +/// Late +#define INIT_STAGE_LATE 4 + +/// Last init stage we need to do. +/// +/// * This must be set to the maximum INIT_STAGE. +#define INIT_STAGE_MAX 4 + +//* Initialization Orders *// + +/** + *! Subsystem init_order, from highest priority to lowest priority. + *? Subsystems shutdown in the reverse of the order they initialize in. + *? Subsystems should always have init_order defined, even if they don't initialize, if they use init. + *? The numbers just define the ordering, they are meaningless otherwise. + */ + +//* Backend *// + +#define INIT_ORDER_FAIL2TOPIC 100 +#define INIT_ORDER_DBCORE 50 +#define INIT_ORDER_REPOSITORY 25 +#define INIT_ORDER_TIMER 10 +#define INIT_ORDER_STATPANELS 0 + +//* Early *// + +#define INIT_ORDER_EARLY_INIT 200 +#define INIT_ORDER_PREFERENCES 150 +#define INIT_ORDER_JOBS 125 +#define INIT_ORDER_ASSETS 100 +#define INIT_ORDER_INSTRUMENTS 50 +#define INIT_ORDER_AI_SCHEDULING 25 +#define INIT_ORDER_AI_MOVEMENT 25 +#define INIT_ORDER_AI_HOLDERS 25 + +//* World *// + +#define INIT_ORDER_IPINTEL 197 + +#define INIT_ORDER_INPUT 160 +#define INIT_ORDER_CHARACTERS 140 +#define INIT_ORDER_SOUNDS 130 +#define INIT_ORDER_GARBAGE 120 +#define INIT_ORDER_VIS 90 +#define INIT_ORDER_SERVER_MAINT 75 +#define INIT_ORDER_MEDIA_TRACKS 65 +#define INIT_ORDER_CHEMISTRY 60 +#define INIT_ORDER_MATERIALS 55 +#define INIT_ORDER_PHOTOGRAPHY 50 +#define INIT_ORDER_MAPPING 45 +#define INIT_ORDER_SPATIAL_GRIDS 43 //! must be after SSmapping so we know world.maxx and world.maxy +#define INIT_ORDER_GAME_WORLD 40 +#define INIT_ORDER_LEGACY_ATC 37 +#define INIT_ORDER_LEGACY_LORE 35 +#define INIT_ORDER_PLANTS 25 +#define INIT_ORDER_ALARMS 20 +#define INIT_ORDER_RESEARCH 17 +#define INIT_ORDER_ATOMS 15 +#define INIT_ORDER_MACHINES 10 +#define INIT_ORDER_SHUTTLES 3 +#define INIT_ORDER_DEFAULT 0 +#define INIT_ORDER_AIR -1 +#define INIT_ORDER_PLANETS -2 +#define INIT_ORDER_PERSISTENCE -3 +#define INIT_ORDER_AMBIENT_OCCLUSION -5 +#define INIT_ORDER_HOLOMAPS -5 +#define INIT_ORDER_ICON_SMOOTHING -6 +#define INIT_ORDER_EVENTS -10 +#define INIT_ORDER_OVERMAPS -20 +#define INIT_ORDER_TICKER -30 +#define INIT_ORDER_LIGHTING -40 +#define INIT_ORDER_ZMIMIC -45 +#define INIT_ORDER_AMBIENT_LIGHT -46 +#define INIT_ORDER_XENOARCH -50 +#define INIT_ORDER_CIRCUIT -60 +#define INIT_ORDER_AI -70 +#define INIT_ORDER_CHAT -100 //! Should be last to ensure chat remains smooth during init. + +//* Late *// + +#define INIT_ORDER_OVERLAY 200 +#define INIT_ORDER_TITLESCREEN 150 +#define INIT_ORDER_NIGHTSHIFT 75 diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 80fa63e3e1e0..b97bcfb7a69d 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -1,3 +1,8 @@ +/** + * This file (and its -dash files) is called MC, but actually holds quite a lot of logic including init orders + * and subsystems as the MC and subsystems make up the global orchestration system of the codebase. + */ + #define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 ) #define MC_TICK_CHECK_USAGE ( ( TICK_USAGE > Master.current_ticklimit ) ? pause() : 0 ) @@ -38,24 +43,6 @@ /// We want the MC to exit. #define MC_LOOP_RTN_GRACEFUL_EXIT 2 -//* Initialization Stages *// -//* After each stage, the MC starts ticking that stage while later stages are still waiting to init. *// -//* MC init stages must be a positive number, and init stages must all be consequetive! *// - -/// Early initializations required for server function; database, timers, tgui, etc -#define INIT_STAGE_BACKEND 1 -/// Pre-mapload initializations -#define INIT_STAGE_EARLY 2 -/// Mapload -#define INIT_STAGE_WORLD 3 -/// Late -#define INIT_STAGE_LATE 4 - -/// Last init stage we need to do. -/// -/// * This must be set to the maximum INIT_STAGE. -#define INIT_STAGE_MAX 4 - //! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier) /** @@ -63,6 +50,7 @@ * * * The subsystem will still fire when its init stage is completed, unless it is * marked with [SS_NO_FIRE] or its `can_fire` is set to FALSE. + * * The subsystem will not have its `initialized` variable set to TRUE. */ #define SS_NO_INIT (1<<0) diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm index f726e8be2e52..0b7faf696f95 100644 --- a/code/__DEFINES/controllers/_subsystems.dm +++ b/code/__DEFINES/controllers/_subsystems.dm @@ -64,85 +64,6 @@ DEFINE_BITFIELD(runlevels, list( BITFIELD(RUNLEVEL_POSTGAME), )) -/** - *! 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. - */ - -// todo: tg init brackets - -// core security system, used by client/New() -#define INIT_ORDER_FAIL2TOPIC 200 -// core security system, used by client/New() -#define INIT_ORDER_IPINTEL 197 - -// core timing system, used by almost everything -#define INIT_ORDER_TIMER 195 -// just about every feature on the server requires the database backend -// for storage and durability of permeance. -#define INIT_ORDER_DBCORE 190 -// repository is just struct storage. its things depend on database, -// but should depend on nothing else. -// -// for the rare occasion when a prototype requires asset registration, -// it should be able to recognize if SSassets is ready, -// and only queue an udpate if its asset is already loaded. -#define INIT_ORDER_REPOSITORY 187 -// early init initializes what is basically expensive global variables. it needs to go before assets. -#define INIT_ORDER_EARLY_INIT 185 -// assets is loaded early because things hook into this to register *their* assets -#define INIT_ORDER_ASSETS 180 - -#define INIT_ORDER_STATPANELS 170 -#define INIT_ORDER_PREFERENCES 165 -#define INIT_ORDER_INPUT 160 -#define INIT_ORDER_JOBS 150 -#define INIT_ORDER_CHARACTERS 140 -#define INIT_ORDER_SOUNDS 130 -#define INIT_ORDER_GARBAGE 120 -#define INIT_ORDER_VIS 90 -#define INIT_ORDER_SERVER_MAINT 75 -#define INIT_ORDER_INSTRUMENTS 70 -#define INIT_ORDER_MEDIA_TRACKS 65 -#define INIT_ORDER_CHEMISTRY 60 -#define INIT_ORDER_MATERIALS 55 -#define INIT_ORDER_PHOTOGRAPHY 50 -#define INIT_ORDER_AI_SCHEDULING 48 -#define INIT_ORDER_AI_MOVEMENT 48 -#define INIT_ORDER_AI_HOLDERS 48 -#define INIT_ORDER_MAPPING 45 -#define INIT_ORDER_SPATIAL_GRIDS 43 // must be after SSmapping so we know world.maxx and world.maxy -#define INIT_ORDER_GAME_WORLD 40 -#define INIT_ORDER_LEGACY_ATC 37 -#define INIT_ORDER_LEGACY_LORE 35 -#define INIT_ORDER_LOBBY 30 -#define INIT_ORDER_PLANTS 25 -#define INIT_ORDER_ALARMS 20 -#define INIT_ORDER_RESEARCH 17 -#define INIT_ORDER_ATOMS 15 -#define INIT_ORDER_MACHINES 10 -#define INIT_ORDER_SHUTTLES 3 -#define INIT_ORDER_DEFAULT 0 -#define INIT_ORDER_AIR -1 -#define INIT_ORDER_PLANETS -2 -#define INIT_ORDER_PERSISTENCE -3 -#define INIT_ORDER_AMBIENT_OCCLUSION -5 -#define INIT_ORDER_HOLOMAPS -5 -#define INIT_ORDER_NIGHTSHIFT -5 -#define INIT_ORDER_ICON_SMOOTHING -6 -#define INIT_ORDER_OVERLAY -7 -#define INIT_ORDER_EVENTS -10 -#define INIT_ORDER_OVERMAPS -20 -#define INIT_ORDER_TICKER -30 -#define INIT_ORDER_LIGHTING -40 -#define INIT_ORDER_ZMIMIC -45 -#define INIT_ORDER_AMBIENT_LIGHT -46 -#define INIT_ORDER_XENOARCH -50 -#define INIT_ORDER_CIRCUIT -60 -#define INIT_ORDER_AI -70 -#define INIT_ORDER_CHAT -100 //! Should be last to ensure chat remains smooth during init. - /** *! Subsystem fire priority, from lowest to highest priority diff --git a/code/__HELPERS/sorts/comparators.dm b/code/__HELPERS/sorts/comparators.dm index ace8dd066988..5049291233e3 100644 --- a/code/__HELPERS/sorts/comparators.dm +++ b/code/__HELPERS/sorts/comparators.dm @@ -92,25 +92,6 @@ GLOBAL_VAR_INIT(cmp_field, "name") /proc/cmp_ckey_dsc(client/a, client/b) return sorttext(a.ckey, b.ckey) -/** - * Sorts subsystems alphabetically. - */ -/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b) - return sorttext(b.name, a.name) - -/** - * Sorts subsystems by init_order. - */ -/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b) - // Uses initial() so it can be used on types. - return initial(b.init_order) - initial(a.init_order) - -/** - * Sorts subsystems by priority. - */ -/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) - return a.priority - b.priority - /proc/cmp_filter_data_priority(list/A, list/B) return A["priority"] - B["priority"] diff --git a/code/controllers/master.dm b/code/controllers/master.dm index ed9543f937bd..ebd09875c3de 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -27,9 +27,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// How many times have we ran? var/iteration = 0 - /// Are we initialized? - var/initialized = FALSE - /// world.time of last fire, for tracking lag outside of the mc. var/last_run @@ -47,15 +44,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// Makes the mc main loop runtime. var/make_runtime = FALSE - var/initializations_finished_with_no_players_logged_in // I wonder what this could be? - /// The type of the last subsystem to be process()'d. var/last_type_processed /// For scheduling different subsystems for different stages of the round. var/current_runlevel - var/sleep_offline_after_initializations = TRUE var/static/restart_clear = 0 var/static/restart_timeout = 0 @@ -63,6 +57,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/random_seed + //* Iniitialization *// + + /// The subsystem currently being initialized. + var/datum/controller/subsystem/current_initializing_subsystem + /// Are we initialized? This means all subsystems have been initialized. + var/initialized = FALSE + /// Set if it is specified to pause the world while no one is logged in after initializations, and we did pause. + var/initializations_finished_with_no_players_logged_in + /// Set to determine if we should sleep offline after initializations if no one is connected. + /// + /// * This is turned off by unit tests automatically. + var/sleep_offline_after_initializations = TRUE + //* Global State *// //* These are tracked through MC restarts. *// diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 6e4f5e8f6111..a84817b2d3d4 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -1,3 +1,24 @@ +/** + * Sorts subsystems alphabetically. + */ +/proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b) + return sorttext(b.name, a.name) + +/** + * Sorts subsystems by init_order. + */ +/proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b) + // Uses initial() so it can be used on types. + if(a.init_stage != b.init_stage) + return initial(a.init_stage) - initial(b.init_stage) + return initial(b.init_order) - initial(a.init_order) + +/** + * Sorts subsystems by priority. + */ +/proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) + return a.priority - b.priority + /** * # Subsystem base class * @@ -351,17 +372,27 @@ return can_fire? "\[[state_letter()]\][name]" : name /datum/controller/subsystem/proc/state_letter() - switch (state) - if (SS_RUNNING) - . = "R" - if (SS_QUEUED) - . = "Q" - if (SS_PAUSED, SS_PAUSING) - . = "P" - if (SS_SLEEPING) - . = "S" - if (SS_IDLE) - . = " " + if(Master.initialized) + switch (state) + if (SS_RUNNING) + . = "R" + if (SS_QUEUED) + . = "Q" + if (SS_PAUSED, SS_PAUSING) + . = "P" + if (SS_SLEEPING) + . = "S" + if (SS_IDLE) + . = " " + else + if(subsystem_flags & SS_NO_INIT) + . = "-" + if(src == Master.current_initializing_subsystem) + . = "I" + else if(initialized) + . = "D" + else + . = "W" /** * Could be used to postpone a costly subsystem for (default one) var/cycles, cycles. diff --git a/code/controllers/subsystem/early_init.dm b/code/controllers/subsystem/early_init.dm index 6beebb4771f6..0460bc5da4dd 100644 --- a/code/controllers/subsystem/early_init.dm +++ b/code/controllers/subsystem/early_init.dm @@ -1,7 +1,7 @@ SUBSYSTEM_DEF(early_init) name = "Early Init" init_order = INIT_ORDER_EARLY_INIT - init_stage= INIT_STAGE_EARLY + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_FIRE /datum/controller/subsystem/early_init/Initialize() diff --git a/code/controllers/subsystem/spatial_grids.dm b/code/controllers/subsystem/spatial_grids.dm index a7d5d7cc7984..043e7fffdf6d 100644 --- a/code/controllers/subsystem/spatial_grids.dm +++ b/code/controllers/subsystem/spatial_grids.dm @@ -8,7 +8,7 @@ SUBSYSTEM_DEF(spatial_grids) name = "Spatial Grids" init_order = INIT_ORDER_SPATIAL_GRIDS - init_stage = INIT_STAGE_EARLY + init_stage = INIT_STAGE_WORLD subsystem_flags = SS_NO_FIRE /// /living mobs. they don't have to be alive, just a subtype of /living. diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 865e1d7c30b4..f812db13b9f3 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -18,6 +18,7 @@ SUBSYSTEM_DEF(timer) name = "Timer" wait = 1 // SS_TICKER subsystem, so wait is in ticks init_order = INIT_ORDER_TIMER + init_stage = INIT_STAGE_BACKEND runlevels = RUNLEVELS_ALL priority = FIRE_PRIORITY_TIMER subsystem_flags = SS_TICKER|SS_NO_INIT diff --git a/code/controllers/subsystem/lobby.dm b/code/controllers/subsystem/titlescreen.dm similarity index 64% rename from code/controllers/subsystem/lobby.dm rename to code/controllers/subsystem/titlescreen.dm index c45269304bd4..b2f834a97b57 100644 --- a/code/controllers/subsystem/lobby.dm +++ b/code/controllers/subsystem/titlescreen.dm @@ -1,30 +1,30 @@ /** - * manager for everything involving the lobby, including the title screen + * Manages the lobby's titlescreen. */ -SUBSYSTEM_DEF(lobby) - name = "Lobby Manager" +SUBSYSTEM_DEF(titlescreen) + name = "Titlescreens" subsystem_flags = SS_NO_FIRE - init_order = INIT_ORDER_LOBBY - init_stage = INIT_STAGE_EARLY + init_order = INIT_ORDER_TITLESCREEN + init_stage = INIT_STAGE_LATE /// our titlescreen var/datum/cutscene/titlescreen -/datum/controller/subsystem/lobby/Initialize() +/datum/controller/subsystem/titlescreen/Initialize() initialize_title_scene() - return SS_INIT_SUCCESS + return SS_INIT_NO_MESSAGE -/datum/controller/subsystem/lobby/proc/initialize_title_scene() +/datum/controller/subsystem/titlescreen/proc/initialize_title_scene() refresh_title_scene() for(var/client/C as anything in GLOB.clients) if(!isnewplayer(C.mob)) continue C.start_cutscene(titlescreen) -/datum/controller/subsystem/lobby/proc/refresh_title_scene() +/datum/controller/subsystem/titlescreen/proc/refresh_title_scene() set_title_scene(make_title_scene()) -/datum/controller/subsystem/lobby/proc/make_title_scene() +/datum/controller/subsystem/titlescreen/proc/make_title_scene() var/picked = pickweight((LEGACY_MAP_DATUM).titlescreens.Copy()) if(isnull(picked)) return @@ -42,7 +42,7 @@ SUBSYSTEM_DEF(lobby) built.init() return built -/datum/controller/subsystem/lobby/proc/set_title_scene(datum/cutscene/scene) +/datum/controller/subsystem/titlescreen/proc/set_title_scene(datum/cutscene/scene) var/list/client/old_viewing if(!isnull(titlescreen)) old_viewing = titlescreen.viewing?.Copy() diff --git a/code/game/atoms/atoms_initializing_EXPENSIVE.dm b/code/game/atoms/atoms_initializing_EXPENSIVE.dm index f46c03c53104..1cb28418a64e 100644 --- a/code/game/atoms/atoms_initializing_EXPENSIVE.dm +++ b/code/game/atoms/atoms_initializing_EXPENSIVE.dm @@ -70,7 +70,7 @@ if(datum_flags & DF_USE_TAG) generate_tag() - var/do_initialize = SSatoms.initialized + var/do_initialize = SSatoms.atom_init_status if(do_initialize != ATOM_INIT_IN_SUBSYSTEM) args[1] = do_initialize == ATOM_INIT_IN_NEW_MAPLOAD if(SSatoms.InitAtom(src, FALSE, args)) diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index 0aa83c75b444..4644f71fa8e9 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -22,4 +22,4 @@ return ..() /mob/new_player/login_cutscene() - client.start_cutscene(SSlobby.titlescreen) + client.start_cutscene(SStitlescreen.titlescreen) From b36043af5d7be3a4af4ae70c528782d678882224 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:25:05 -0500 Subject: [PATCH 07/28] organize defines in a way that will make lohikar hate me --- citadel.dme | 10 +- .../__DEFINES/controllers/_master-runlevel.dm | 32 ++++ code/__DEFINES/controllers/_master.dm | 42 +++++ .../{_repositories.dm => _repository.dm} | 0 .../_subsystem-init.dm} | 0 .../controllers/_subsystem-priority.dm | 68 +++++++++ .../{MC.dm => controllers/_subsystem.dm} | 107 +++++++------ code/__DEFINES/controllers/_subsystems.dm | 143 ------------------ code/__DEFINES/controllers/timer.dm | 10 ++ code/controllers/master.dm | 5 +- code/controllers/subsystem.dm | 13 +- code/controllers/subsystem/air.dm | 10 +- code/controllers/subsystem/ipintel.dm | 3 +- code/controllers/subsystem/machines.dm | 8 +- .../subsystem/processing/processing.dm | 4 +- code/controllers/subsystem/spatial_grids.dm | 2 +- code/controllers/subsystem/transcore_vr.dm | 4 +- 17 files changed, 239 insertions(+), 222 deletions(-) create mode 100644 code/__DEFINES/controllers/_master-runlevel.dm create mode 100644 code/__DEFINES/controllers/_master.dm rename code/__DEFINES/controllers/{_repositories.dm => _repository.dm} (100%) rename code/__DEFINES/{MC-init.dm => controllers/_subsystem-init.dm} (100%) create mode 100644 code/__DEFINES/controllers/_subsystem-priority.dm rename code/__DEFINES/{MC.dm => controllers/_subsystem.dm} (57%) delete mode 100644 code/__DEFINES/controllers/_subsystems.dm diff --git a/citadel.dme b/citadel.dme index de40196dd17a..77dc70d24882 100644 --- a/citadel.dme +++ b/citadel.dme @@ -64,8 +64,6 @@ #include "code\__DEFINES\maps.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\matrices.dm" -#include "code\__DEFINES\MC-init.dm" -#include "code\__DEFINES\MC.dm" #include "code\__DEFINES\metrics.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\move_force.dm" @@ -158,8 +156,12 @@ #include "code\__DEFINES\combat\damage.dm" #include "code\__DEFINES\combat\explosions.dm" #include "code\__DEFINES\combat\shieldcall.dm" -#include "code\__DEFINES\controllers\_repositories.dm" -#include "code\__DEFINES\controllers\_subsystems.dm" +#include "code\__DEFINES\controllers\_master-runlevel.dm" +#include "code\__DEFINES\controllers\_master.dm" +#include "code\__DEFINES\controllers\_repository.dm" +#include "code\__DEFINES\controllers\_subsystem-init.dm" +#include "code\__DEFINES\controllers\_subsystem-priority.dm" +#include "code\__DEFINES\controllers\_subsystem.dm" #include "code\__DEFINES\controllers\assets.dm" #include "code\__DEFINES\controllers\atoms.dm" #include "code\__DEFINES\controllers\dbcore.dm" diff --git a/code/__DEFINES/controllers/_master-runlevel.dm b/code/__DEFINES/controllers/_master-runlevel.dm new file mode 100644 index 000000000000..6f505ef2e8c7 --- /dev/null +++ b/code/__DEFINES/controllers/_master-runlevel.dm @@ -0,0 +1,32 @@ +//* Runlevels *// +//* Must be powers of 2. Runlevels should be in order of progression. *// +//* Only subsystem with a runlevel matching the MC's will be ticked. *// +//* The first runlevel (value '1') will be the default runlevel when the MC is first initialized. *// + +/// "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set). +#define RUNLEVEL_INIT 0 +/// Initial runlevel before setup. Returns to here if setup fails. +#define RUNLEVEL_LOBBY 1 +/// While the gamemode setup is running. I.E gameticker.setup() +#define RUNLEVEL_SETUP 2 +/// After successful game ticker setup, while the round is running. +#define RUNLEVEL_GAME 4 +/// When round completes but before reboot. +#define RUNLEVEL_POSTGAME 8 + +/// default runlevels for most subsystems +#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) +/// all valid runlevels - subsystems with this will run all the time after their MC init stage. +#define RUNLEVELS_ALL (RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) + +var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME) +/// Convert from the runlevel bitfield constants to index in runlevel_flags list. +#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) + +DEFINE_BITFIELD(runlevels, list( + BITFIELD(RUNLEVEL_INIT), + BITFIELD(RUNLEVEL_LOBBY), + BITFIELD(RUNLEVEL_SETUP), + BITFIELD(RUNLEVEL_GAME), + BITFIELD(RUNLEVEL_POSTGAME), +)) diff --git a/code/__DEFINES/controllers/_master.dm b/code/__DEFINES/controllers/_master.dm new file mode 100644 index 000000000000..d9be9f1f05ae --- /dev/null +++ b/code/__DEFINES/controllers/_master.dm @@ -0,0 +1,42 @@ +/** + * This file (and its -dash files) is called MC, but actually holds quite a lot of logic including init orders + * and subsystems as the MC and subsystems make up the global orchestration system of the codebase. + */ + +#define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 ) +#define MC_TICK_CHECK_USAGE ( ( TICK_USAGE > Master.current_ticklimit ) ? pause() : 0 ) + +#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count +#define MC_SPLIT_TICK \ + if(split_tick_phases > 1){\ + Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + TICK_USAGE;\ + --split_tick_phases;\ + } else {\ + Master.current_ticklimit = original_tick_limit;\ + } + +// Used to smooth out costs to try and avoid oscillation. +#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current)) +#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current)) +#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current)) + +#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current)) +#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current)) + +#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum} +#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum + +//* Recreate_MC() return values *// + +#define MC_RESTART_RTN_FAILED -1 +#define MC_RESTART_RTN_COOLDOWN 0 +#define MC_RESTART_RTN_SUCCESS 1 + +//* Master Controller Loop() return values *// + +/// Unknown or error +#define MC_LOOP_RTN_UNKNOWN 0 +/// New initialize stage happened +#define MC_LOOP_RTN_NEWSTAGES 1 +/// We want the MC to exit. +#define MC_LOOP_RTN_GRACEFUL_EXIT 2 diff --git a/code/__DEFINES/controllers/_repositories.dm b/code/__DEFINES/controllers/_repository.dm similarity index 100% rename from code/__DEFINES/controllers/_repositories.dm rename to code/__DEFINES/controllers/_repository.dm diff --git a/code/__DEFINES/MC-init.dm b/code/__DEFINES/controllers/_subsystem-init.dm similarity index 100% rename from code/__DEFINES/MC-init.dm rename to code/__DEFINES/controllers/_subsystem-init.dm diff --git a/code/__DEFINES/controllers/_subsystem-priority.dm b/code/__DEFINES/controllers/_subsystem-priority.dm new file mode 100644 index 000000000000..cd006fa47cdc --- /dev/null +++ b/code/__DEFINES/controllers/_subsystem-priority.dm @@ -0,0 +1,68 @@ + +/** + *! Subsystem fire priority, from lowest to highest priority + *? If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) + */ + +//? Background Subsystems - Below normal +// Any ../subsystem/.. is here unless it doesn't have SS_BACKGROUND in subsystem_flags! +// This means by default, ../subsystem/processing/.. is here! + +#define FIRE_PRIORITY_RADIATION 10 //! laggy as hell, bottom barrel until optimizations are done. +#define FIRE_PRIORITY_GARBAGE 15 +#define FIRE_PRIORITY_CHARACTERS 20 +#define FIRE_PRIORITY_PARALLAX 20 +#define FIRE_PRIORITY_AIR 25 +#define FIRE_PRIORITY_ASSET_LOADING 25 +#define FIRE_PRIORITY_PLANETS 25 +#define FIRE_PRIORITY_PROCESS 50 +// DEFAULT PRIORITY IS HERE (50) + +//? Normal Subsystems - Above background, below ticker +// Any ../subsystem/.. without SS_TICKER or SS_BACKGROUND in subsystem_flags is here! + +#define FIRE_PRIORITY_PING 5 +#define FIRE_PRIORITY_SHUTTLES 5 +#define FIRE_PRIORITY_PLANTS 5 +#define FIRE_PRIORITY_NIGHTSHIFT 6 +#define FIRE_PRIORITY_VOTE 9 +#define FIRE_PRIORITY_VIS 10 +#define FIRE_PRIORITY_SERVER_MAINT 10 +#define FIRE_PRIORITY_ZMIMIC 10 +#define FIRE_PRIORITY_ALARMS 20 +#define FIRE_PRIORITY_AIRFLOW 20 +#define FIRE_PRIORITY_SPACEDRIFT 25 +#define FIRE_PRIORITY_OBJ 40 +// DEFAULT PRIORITY IS HERE (50) +#define FIRE_PRIORITY_LIGHTING 50 +#define FIRE_PRIORITY_INSTRUMENTS 50 +#define FIRE_PRIORITY_MACHINES 50 +#define FIRE_PRIORITY_AI 65 +#define FIRE_PRIORITY_AI_HOLDERS 65 +#define FIRE_PRIORITY_AI_MOVEMENT 75 +#define FIRE_PRIORITY_AI_SCHEDULING 75 +#define FIRE_PRIORITY_NANO 80 +#define FIRE_PRIORITY_TGUI 80 +#define FIRE_PRIORITY_OVERMAP_PHYSICS 90 +#define FIRE_PRIORITY_PROJECTILES 90 +#define FIRE_PRIORITY_THROWING 90 +#define FIRE_PRIORITY_STATPANELS 100 +#define FIRE_PRIORITY_OVERLAYS 100 +#define FIRE_PRIORITY_SMOOTHING 100 +#define FIRE_PRIORITY_CHAT 100 +#define FIRE_PRIORITY_INPUT 100 + +//? Ticker Subsystems - Highest priority +// Any subsystem flagged with SS_TICKER is here! +// Do not unnecessarily set your subsystem as TICKER. +// Is your feature as important as movement, chat, or timers? +// Probably not! Go to normal bracket instead! + +// DEFAULT PRIORITY IS HERE (50) +#define FIRE_PRIORITY_DPC 100 +#define FIRE_PRIORITY_TIMER 100 + +//? Special + +/// This is used as the default regardless of bucket. Check above. +#define FIRE_PRIORITY_DEFAULT 50 diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/controllers/_subsystem.dm similarity index 57% rename from code/__DEFINES/MC.dm rename to code/__DEFINES/controllers/_subsystem.dm index b97bcfb7a69d..75cb623230fd 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/controllers/_subsystem.dm @@ -1,56 +1,43 @@ /** - * This file (and its -dash files) is called MC, but actually holds quite a lot of logic including init orders - * and subsystems as the MC and subsystems make up the global orchestration system of the codebase. + *! Defines for subsystems + * + *? Lots of important stuff in here, make sure you have your brain switched on when editing this file! */ -#define MC_TICK_CHECK ( ( TICK_USAGE > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 ) -#define MC_TICK_CHECK_USAGE ( ( TICK_USAGE > Master.current_ticklimit ) ? pause() : 0 ) - -#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count -#define MC_SPLIT_TICK \ - if(split_tick_phases > 1){\ - Master.current_ticklimit = ((original_tick_limit - TICK_USAGE) / split_tick_phases) + TICK_USAGE;\ - --split_tick_phases;\ - } else {\ - Master.current_ticklimit = original_tick_limit;\ - } - -// Used to smooth out costs to try and avoid oscillation. -#define MC_AVERAGE_FAST(average, current) (0.7 * (average) + 0.3 * (current)) -#define MC_AVERAGE(average, current) (0.8 * (average) + 0.2 * (current)) -#define MC_AVERAGE_SLOW(average, current) (0.9 * (average) + 0.1 * (current)) - -#define MC_AVG_FAST_UP_SLOW_DOWN(average, current) (average > current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current)) -#define MC_AVG_SLOW_UP_FAST_DOWN(average, current) (average < current ? MC_AVERAGE_SLOW(average, current) : MC_AVERAGE_FAST(average, current)) +//* Subsystem Definition Macros *// +/** + * Macro to fire off all logic when a subsystem is created - this is done immediately on New(). + */ #define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){PreInit(TRUE);Preload(TRUE);Recover();qdel(varname);}varname = src;} -#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum} -#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum - -//* Recreate_MC() return values *// - -#define MC_RESTART_RTN_FAILED -1 -#define MC_RESTART_RTN_COOLDOWN 0 -#define MC_RESTART_RTN_SUCCESS 1 - -//* Master Controller Loop() return values *// +/** + * Defines a normal subsystem. + */ +#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\ +/datum/controller/subsystem/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ +}\ +/datum/controller/subsystem/##X -/// Unknown or error -#define MC_LOOP_RTN_UNKNOWN 0 -/// New initialize stage happened -#define MC_LOOP_RTN_NEWSTAGES 1 -/// We want the MC to exit. -#define MC_LOOP_RTN_GRACEFUL_EXIT 2 +/** + * Defines a processing subsystem. + */ +#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\ +/datum/controller/subsystem/processing/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ +}\ +/datum/controller/subsystem/processing/##X -//! SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier) +//* Subsystem flags *// +//* Please design any new flags so that the default is off, to make adding flags to subsystems easier. *// /** * Subsystem does not need Initialize() called. * * * The subsystem will still fire when its init stage is completed, unless it is * marked with [SS_NO_FIRE] or its `can_fire` is set to FALSE. - * * The subsystem will not have its `initialized` variable set to TRUE. + * * The subsystem will still have its `initialized` variable set to TRUE. */ #define SS_NO_INIT (1<<0) @@ -62,11 +49,15 @@ */ #define SS_NO_FIRE (1<<1) -/** subsystem only runs on spare cpu (after all non-background subsystems have ran that tick) */ -/// SS_BACKGROUND has its own priority bracket +/** + * Subsystem will try its best to only run on spare CPU, after all non-background subsystems have ran. + * + * * This pushes a subsystem into the background fire_priority bracket. + */ #define SS_BACKGROUND (1<<2) /// subsystem does not tick check, and should not run unless there is enough time (or its running behind (unless background)) +// todo: this should be deprecated; please do not use this on new subsystems without good reason. #define SS_NO_TICK_CHECK (1<<3) /** Treat wait as a tick count, not DS, run every wait ticks. */ @@ -107,8 +98,25 @@ DEFINE_BITFIELD(subsystem_flags, list( BITFIELD(SS_OK_TO_FAIL_INIT), )) +//* Subsystem `Initialize()` returns *// +/** + * Negative values incidate a failure or warning of some kind, positive are good. + * 0 and 1 are unused so that TRUE and FALSE are guarenteed to be invalid values. + */ + +/// Subsystem failed to initialize entirely. Print a warning, log, and disable firing. +#define SS_INIT_FAILURE -2 +/// The default return value which must be overriden. Will succeed with a warning. +#define SS_INIT_NONE -1 +/// Subsystem initialized sucessfully. +#define SS_INIT_SUCCESS 2 +/// If your system doesn't need to be initialized (by being disabled or something) +#define SS_INIT_NO_NEED 3 +/// Succesfully initialized, BUT do not announce it to players (generally to hide game mechanics it would otherwise spoil) +#define SS_INIT_NO_MESSAGE 4 + +//* Subsystem 'state' variable *// -//! SUBSYSTEM STATES /// aint doing shit. #define SS_IDLE 0 /// queued to run @@ -121,20 +129,11 @@ DEFINE_BITFIELD(subsystem_flags, list( #define SS_SLEEPING 4 /// in the middle of pausing #define SS_PAUSING 5 -#define SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/##X);\ -/datum/controller/subsystem/##X/New(){\ - NEW_SS_GLOBAL(SS##X);\ -}\ -/datum/controller/subsystem/##X -#define PROCESSING_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/processing/##X);\ -/datum/controller/subsystem/processing/##X/New(){\ - NEW_SS_GLOBAL(SS##X);\ -}\ -/datum/controller/subsystem/processing/##X +//* Misc *// -// Boilerplate code for multi-step processors. See machines.dm for example use. -#define INTERNAL_PROCESS_STEP(this_step, initial_step, proc_to_call, cost_var, next_step)\ +/// Boilerplate code for multi-step processors. See machines.dm for example use. +#define INTERNAL_SUBSYSTEM_PROCESS_STEP(this_step, initial_step, proc_to_call, cost_var, next_step)\ if(current_step == this_step || (initial_step && !resumed)) /* So we start at step 1 if not resumed.*/ {\ timer = TICK_USAGE;\ proc_to_call(resumed);\ diff --git a/code/__DEFINES/controllers/_subsystems.dm b/code/__DEFINES/controllers/_subsystems.dm deleted file mode 100644 index 0b7faf696f95..000000000000 --- a/code/__DEFINES/controllers/_subsystems.dm +++ /dev/null @@ -1,143 +0,0 @@ -/** - *! Defines for subsystems and overlays - * - *? Lots of important stuff in here, make sure you have your brain switched on when editing this file! - */ - -//* Subsystem `initialized` variable *// - -// todo: implement these, separate out SSatoms initialization state to its own variable -// #define SUBSYSTEM_INITIALIZED_NOT_STARTED 0 -// #define SUBSYSTEM_INITIALIZED_INITIALIZING 1 -// #define SUBSYSTEM_INITIALIZED_DONE 2 - -//* Subsystem `Initialize()` returns *// - -/** - * Negative values incidate a failure or warning of some kind, positive are good. - * 0 and 1 are unused so that TRUE and FALSE are guarenteed to be invalid values. - */ - -/// Subsystem failed to initialize entirely. Print a warning, log, and disable firing. -#define SS_INIT_FAILURE -2 -/// The default return value which must be overriden. Will succeed with a warning. -#define SS_INIT_NONE -1 -/// Subsystem initialized sucessfully. -#define SS_INIT_SUCCESS 2 -/// If your system doesn't need to be initialized (by being disabled or something) -#define SS_INIT_NO_NEED 3 -/// Succesfully initialized, BUT do not announce it to players (generally to hide game mechanics it would otherwise spoil) -#define SS_INIT_NO_MESSAGE 4 - -//! ### SS runlevels - -//* Runlevels *// -//* Must be powers of 2. Runlevels should be in order of progression. *// -//* Only subsystem with a runlevel matching the MC's will be ticked. *// -//* The first runlevel (value '1') will be the default runlevel when the MC is first initialized. *// - -/// "Initialize Only" - Used for subsystems that should never be fired (Should also have SS_NO_FIRE set). -#define RUNLEVEL_INIT 0 -/// Initial runlevel before setup. Returns to here if setup fails. -#define RUNLEVEL_LOBBY 1 -/// While the gamemode setup is running. I.E gameticker.setup() -#define RUNLEVEL_SETUP 2 -/// After successful game ticker setup, while the round is running. -#define RUNLEVEL_GAME 4 -/// When round completes but before reboot. -#define RUNLEVEL_POSTGAME 8 - -/// default runlevels for most subsystems -#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) -/// all valid runlevels - subsystems with this will run all the time after their MC init stage. -#define RUNLEVELS_ALL (RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME) - -var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME) -/// Convert from the runlevel bitfield constants to index in runlevel_flags list. -#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) - -DEFINE_BITFIELD(runlevels, list( - BITFIELD(RUNLEVEL_INIT), - BITFIELD(RUNLEVEL_LOBBY), - BITFIELD(RUNLEVEL_SETUP), - BITFIELD(RUNLEVEL_GAME), - BITFIELD(RUNLEVEL_POSTGAME), -)) - - -/** - *! Subsystem fire priority, from lowest to highest priority - *? If the subsystem isn't listed here it's either DEFAULT or PROCESS (if it's a processing subsystem child) - */ - -//? Background Subsystems - Below normal -// Any ../subsystem/.. is here unless it doesn't have SS_BACKGROUND in subsystem_flags! -// This means by default, ../subsystem/processing/.. is here! - -#define FIRE_PRIORITY_RADIATION 10 //! laggy as hell, bottom barrel until optimizations are done. -#define FIRE_PRIORITY_GARBAGE 15 -#define FIRE_PRIORITY_CHARACTERS 20 -#define FIRE_PRIORITY_PARALLAX 20 -#define FIRE_PRIORITY_AIR 25 -#define FIRE_PRIORITY_ASSET_LOADING 25 -#define FIRE_PRIORITY_PLANETS 25 -#define FIRE_PRIORITY_PROCESS 50 -// DEFAULT PRIORITY IS HERE (50) - -//? Normal Subsystems - Above background, below ticker -// Any ../subsystem/.. without SS_TICKER or SS_BACKGROUND in subsystem_flags is here! - -#define FIRE_PRIORITY_PING 5 -#define FIRE_PRIORITY_SHUTTLES 5 -#define FIRE_PRIORITY_PLANTS 5 -#define FIRE_PRIORITY_NIGHTSHIFT 6 -#define FIRE_PRIORITY_VOTE 9 -#define FIRE_PRIORITY_VIS 10 -#define FIRE_PRIORITY_SERVER_MAINT 10 -#define FIRE_PRIORITY_ZMIMIC 10 -#define FIRE_PRIORITY_ALARMS 20 -#define FIRE_PRIORITY_AIRFLOW 20 -#define FIRE_PRIORITY_SPACEDRIFT 25 -#define FIRE_PRIORITY_OBJ 40 -// DEFAULT PRIORITY IS HERE (50) -#define FIRE_PRIORITY_LIGHTING 50 -#define FIRE_PRIORITY_INSTRUMENTS 50 -#define FIRE_PRIORITY_MACHINES 50 -#define FIRE_PRIORITY_AI 65 -#define FIRE_PRIORITY_AI_HOLDERS 65 -#define FIRE_PRIORITY_AI_MOVEMENT 75 -#define FIRE_PRIORITY_AI_SCHEDULING 75 -#define FIRE_PRIORITY_NANO 80 -#define FIRE_PRIORITY_TGUI 80 -#define FIRE_PRIORITY_OVERMAP_PHYSICS 90 -#define FIRE_PRIORITY_PROJECTILES 90 -#define FIRE_PRIORITY_THROWING 90 -#define FIRE_PRIORITY_STATPANELS 100 -#define FIRE_PRIORITY_OVERLAYS 100 -#define FIRE_PRIORITY_SMOOTHING 100 -#define FIRE_PRIORITY_CHAT 100 -#define FIRE_PRIORITY_INPUT 100 - -//? Ticker Subsystems - Highest priority -// Any subsystem flagged with SS_TICKER is here! -// Do not unnecessarily set your subsystem as TICKER. -// Is your feature as important as movement, chat, or timers? -// Probably not! Go to normal bracket instead! - -// DEFAULT PRIORITY IS HERE (50) -#define FIRE_PRIORITY_DPC 100 -#define FIRE_PRIORITY_TIMER 100 - -//? Special - -/// This is used as the default regardless of bucket. Check above. -#define FIRE_PRIORITY_DEFAULT 50 - -/** - * Create a new timer and add it to the queue. - * Arguments: - * * callback the callback to call on timer finish - * * wait deciseconds to run the timer for - * * atom_flags atom_flags for this timer, see: code\__DEFINES\subsystems.dm - */ -#define addtimer(args...) _addtimer(args, file = __FILE__, line = __LINE__) diff --git a/code/__DEFINES/controllers/timer.dm b/code/__DEFINES/controllers/timer.dm index 500019080619..2d6de7928b1b 100644 --- a/code/__DEFINES/controllers/timer.dm +++ b/code/__DEFINES/controllers/timer.dm @@ -1,4 +1,14 @@ //! ## Timing subsystem + +/** + * Create a new timer and add it to the queue. + * Arguments: + * * callback the callback to call on timer finish + * * wait deciseconds to run the timer for + * * atom_flags atom_flags for this timer, see: code\__DEFINES\subsystems.dm + */ +#define addtimer(args...) _addtimer(args, file = __FILE__, line = __LINE__) + /** * Don't run if there is an identical unique timer active * diff --git a/code/controllers/master.dm b/code/controllers/master.dm index ebd09875c3de..2ef9748a401e 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -334,7 +334,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new */ /datum/controller/master/proc/initialize_subsystem(datum/controller/subsystem/subsystem) // Do not re-init already initialized subsystems if it's somehow called again. - if (subsystem.subsystem_flags & SS_NO_INIT || subsystem.initialized) + if(subsystem.subsystem_flags & SS_NO_INIT) + subsystem.initialized = TRUE + return + if(subsystem.initialized) return // todo: dylib high-precision timers diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index a84817b2d3d4..3f34539d3e10 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -50,9 +50,14 @@ * Which stage does this subsystem init at. Earlier stages can fire while later stages init. * * * This is higher in precedence than [init_order]. + * * This determines when the subsystem starts firing; besure to set this if you need ticking even if you are using SS_NO_INIT! */ var/init_stage = INIT_STAGE_WORLD - /// This var is set to TRUE after the subsystem has been initialized. + /** + * This variable is set to TRUE after the subsystem has been initialized. + * + * * If this subsystem is marked as SS_NO_FIRE, this still will be set to TRUE. We just won't call Initialize(). + */ var/initialized = FALSE /** @@ -372,7 +377,7 @@ return can_fire? "\[[state_letter()]\][name]" : name /datum/controller/subsystem/proc/state_letter() - if(Master.initialized) + if(Master.init_stage_completed >= init_stage) switch (state) if (SS_RUNNING) . = "R" @@ -383,7 +388,7 @@ if (SS_SLEEPING) . = "S" if (SS_IDLE) - . = " " + . = "  " else if(subsystem_flags & SS_NO_INIT) . = "-" @@ -392,7 +397,7 @@ else if(initialized) . = "D" else - . = "W" + . = "-" /** * Could be used to postpone a costly subsystem for (default one) var/cycles, cycles. diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index ed3d01c9f284..83c7386ad3e7 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -145,11 +145,11 @@ SUBSYSTEM_DEF(air) current_step = SSAIR_TURFS current_cycle++ - INTERNAL_PROCESS_STEP(SSAIR_TURFS, TRUE, process_tiles_to_update, cost_turfs, SSAIR_EDGES) - INTERNAL_PROCESS_STEP(SSAIR_EDGES, FALSE, process_active_edges, cost_edges, SSAIR_FIREZONES) - INTERNAL_PROCESS_STEP(SSAIR_FIREZONES, FALSE, process_active_fire_zones, cost_firezones, SSAIR_HOTSPOTS) - INTERNAL_PROCESS_STEP(SSAIR_HOTSPOTS, FALSE, process_active_hotspots, cost_hotspots, SSAIR_ZONES) - INTERNAL_PROCESS_STEP(SSAIR_ZONES, FALSE, process_zones_to_update, cost_zones, SSAIR_DONE) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSAIR_TURFS, TRUE, process_tiles_to_update, cost_turfs, SSAIR_EDGES) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSAIR_EDGES, FALSE, process_active_edges, cost_edges, SSAIR_FIREZONES) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSAIR_FIREZONES, FALSE, process_active_fire_zones, cost_firezones, SSAIR_HOTSPOTS) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSAIR_HOTSPOTS, FALSE, process_active_hotspots, cost_hotspots, SSAIR_ZONES) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSAIR_ZONES, FALSE, process_zones_to_update, cost_zones, SSAIR_DONE) // Okay, we're done! Woo! Got thru a whole SSair cycle! if(LAZYLEN(currentrun) != 0) diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm index e35e0c9e3941..170e520e4cfe 100644 --- a/code/controllers/subsystem/ipintel.dm +++ b/code/controllers/subsystem/ipintel.dm @@ -3,8 +3,7 @@ */ SUBSYSTEM_DEF(ipintel) name = "IPIntel" - init_order = INIT_ORDER_IPINTEL - subsystem_flags = SS_NO_FIRE + subsystem_flags = SS_NO_FIRE | SS_NO_INIT /// is ipintel enabled? var/enabled = FALSE diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index d6dfcb2c2973..a04b9b4d696f 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -50,10 +50,10 @@ SUBSYSTEM_DEF(machines) /datum/controller/subsystem/machines/fire(resumed = 0) var/timer = TICK_USAGE - INTERNAL_PROCESS_STEP(SSMACHINES_POWER_OBJECTS,FALSE,process_power_objects,cost_power_objects,SSMACHINES_PIPENETS) // Higher priority, damnit - INTERNAL_PROCESS_STEP(SSMACHINES_PIPENETS,TRUE,process_pipenets,cost_pipenets,SSMACHINES_MACHINERY) - INTERNAL_PROCESS_STEP(SSMACHINES_MACHINERY,FALSE,process_machinery,cost_machinery,SSMACHINES_POWERNETS) - INTERNAL_PROCESS_STEP(SSMACHINES_POWERNETS,FALSE,process_powernets,cost_powernets,SSMACHINES_POWER_OBJECTS) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSMACHINES_POWER_OBJECTS,FALSE,process_power_objects,cost_power_objects,SSMACHINES_PIPENETS) // Higher priority, damnit + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSMACHINES_PIPENETS,TRUE,process_pipenets,cost_pipenets,SSMACHINES_MACHINERY) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSMACHINES_MACHINERY,FALSE,process_machinery,cost_machinery,SSMACHINES_POWERNETS) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSMACHINES_POWERNETS,FALSE,process_powernets,cost_powernets,SSMACHINES_POWER_OBJECTS) // rebuild all power networks from scratch - only called at world creation or by the admin verb // The above is a lie. Turbolifts also call this proc. diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index 3e4829857a2a..1b98510c602f 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -1,12 +1,12 @@ //Used to process objects. SUBSYSTEM_DEF(processing) - name = "Processing" + name = "Processing - 1 FPS" priority = FIRE_PRIORITY_PROCESS subsystem_flags = SS_BACKGROUND|SS_POST_FIRE_TIMING|SS_NO_INIT wait = 1 SECONDS - var/stat_tag = "P" //Used for logging + var/stat_tag = "P1" //Used for logging var/list/processing = list() var/list/currentrun = list() diff --git a/code/controllers/subsystem/spatial_grids.dm b/code/controllers/subsystem/spatial_grids.dm index 043e7fffdf6d..b19dfcced2a1 100644 --- a/code/controllers/subsystem/spatial_grids.dm +++ b/code/controllers/subsystem/spatial_grids.dm @@ -20,7 +20,7 @@ SUBSYSTEM_DEF(spatial_grids) /datum/controller/subsystem/spatial_grids/Initialize() make_grids() - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/spatial_grids/proc/make_grids() living = new /datum/spatial_grid(/mob/living) diff --git a/code/controllers/subsystem/transcore_vr.dm b/code/controllers/subsystem/transcore_vr.dm index 70b1e3fa8fed..fe550f4f95ca 100644 --- a/code/controllers/subsystem/transcore_vr.dm +++ b/code/controllers/subsystem/transcore_vr.dm @@ -32,8 +32,8 @@ SUBSYSTEM_DEF(transcore) /datum/controller/subsystem/transcore/fire(resumed = 0) var/timer = TICK_USAGE - INTERNAL_PROCESS_STEP(SSTRANSCORE_IMPLANTS,TRUE,process_implants,cost_implants,SSTRANSCORE_BACKUPS) -// INTERNAL_PROCESS_STEP(SSTRANSCORE_BACKUPS,FALSE,process_backups,cost_backups,SSTRANSCORE_IMPLANTS) + INTERNAL_SUBSYSTEM_PROCESS_STEP(SSTRANSCORE_IMPLANTS,TRUE,process_implants,cost_implants,SSTRANSCORE_BACKUPS) +// INTERNAL_SUBSYSTEM_PROCESS_STEP(SSTRANSCORE_BACKUPS,FALSE,process_backups,cost_backups,SSTRANSCORE_IMPLANTS) /datum/controller/subsystem/transcore/proc/process_implants(resumed = 0) if (!resumed) From 1fe89f00436f07faf2b09e701d5cb5fe5630f997 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:33:50 -0500 Subject: [PATCH 08/28] update --- code/__DEFINES/controllers/_master.dm | 6 ++++++ code/controllers/master.dm | 2 ++ code/controllers/subsystem.dm | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/code/__DEFINES/controllers/_master.dm b/code/__DEFINES/controllers/_master.dm index d9be9f1f05ae..f75a92e31331 100644 --- a/code/__DEFINES/controllers/_master.dm +++ b/code/__DEFINES/controllers/_master.dm @@ -26,6 +26,12 @@ #define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum} #define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum +/// Returns true if the MC is initialized and running. +/// Optional argument init_stage controls what stage the mc must have initializted to count as initialized. Defaults to INITSTAGE_MAX if not specified. +#define MC_RUNNING(INIT_STAGE...) (Master && Master.processing > 0 && Master.current_runlevel && Master.init_stage_completed == (max(min(INIT_STAGE_MAX, ##INIT_STAGE), 1))) +/// Returns true if the MC is at atleast a given init stage. Defaults to fully initialized. +#define MC_INITIALIZED(INIT_STAGE...) (Master?.init_stage_completed >= max(INIT_STAGE_MAX, ##INIT_STAGE)) + //* Recreate_MC() return values *// #define MC_RESTART_RTN_FAILED -1 diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 2ef9748a401e..3d5156ab2d05 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -285,7 +285,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new for(var/current_init_stage in 1 to INIT_STAGE_MAX) for(var/datum/controller/subsystem/subsystem in stage_sorted_subsystems[current_init_stage]) + current_initializing_subsystem = subsystem initialize_subsystem(subsystem) + current_initializing_subsystem = null CHECK_TICK init_stage_completed = current_init_stage if(!mc_started) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 3f34539d3e10..6fceb0f7e393 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -374,9 +374,19 @@ . = "OFFLINE " /datum/controller/subsystem/stat_key() - return can_fire? "\[[state_letter()]\][name]" : name + return can_fire? "\[[state_letter()]\] [name]" : name +/** + * Returns our status symbol. + */ /datum/controller/subsystem/proc/state_letter() + // R: running + // Q: queued + // P: pausing / paused + // S: sleeping + // I: initializing + // D: done initializing, waiting for init stage to finish + // blank: idle if(Master.init_stage_completed >= init_stage) switch (state) if (SS_RUNNING) @@ -391,13 +401,13 @@ . = "  " else if(subsystem_flags & SS_NO_INIT) - . = "-" + . = "D" if(src == Master.current_initializing_subsystem) . = "I" else if(initialized) . = "D" else - . = "-" + . = "W" /** * Could be used to postpone a costly subsystem for (default one) var/cycles, cycles. From 9fdbe436dea7a7a4ee86a553088fa7feeebe31b2 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 11:46:37 -0500 Subject: [PATCH 09/28] adjust --- code/__DEFINES/controllers/_master.dm | 2 ++ code/__DEFINES/controllers/_subsystem-init.dm | 2 +- code/controllers/failsafe.dm | 4 ++++ code/controllers/master.dm | 9 +++++---- code/controllers/subsystem.dm | 1 + code/controllers/subsystem/chat.dm | 1 + code/controllers/subsystem/nanoui.dm | 15 +-------------- code/controllers/subsystem/parallax.dm | 2 +- code/controllers/subsystem/playtime.dm | 2 +- code/controllers/subsystem/status_effects.dm | 2 +- code/controllers/subsystem/sun.dm | 1 + code/controllers/subsystem/turbolift.dm | 2 +- 12 files changed, 20 insertions(+), 23 deletions(-) diff --git a/code/__DEFINES/controllers/_master.dm b/code/__DEFINES/controllers/_master.dm index f75a92e31331..d67baa915d99 100644 --- a/code/__DEFINES/controllers/_master.dm +++ b/code/__DEFINES/controllers/_master.dm @@ -30,6 +30,8 @@ /// Optional argument init_stage controls what stage the mc must have initializted to count as initialized. Defaults to INITSTAGE_MAX if not specified. #define MC_RUNNING(INIT_STAGE...) (Master && Master.processing > 0 && Master.current_runlevel && Master.init_stage_completed == (max(min(INIT_STAGE_MAX, ##INIT_STAGE), 1))) /// Returns true if the MC is at atleast a given init stage. Defaults to fully initialized. +/// +/// * This does not check anything else about the MC, including if it's actually running. #define MC_INITIALIZED(INIT_STAGE...) (Master?.init_stage_completed >= max(INIT_STAGE_MAX, ##INIT_STAGE)) //* Recreate_MC() return values *// diff --git a/code/__DEFINES/controllers/_subsystem-init.dm b/code/__DEFINES/controllers/_subsystem-init.dm index 762f46046d52..c00ad66e13ac 100644 --- a/code/__DEFINES/controllers/_subsystem-init.dm +++ b/code/__DEFINES/controllers/_subsystem-init.dm @@ -85,10 +85,10 @@ #define INIT_ORDER_XENOARCH -50 #define INIT_ORDER_CIRCUIT -60 #define INIT_ORDER_AI -70 -#define INIT_ORDER_CHAT -100 //! Should be last to ensure chat remains smooth during init. //* Late *// #define INIT_ORDER_OVERLAY 200 #define INIT_ORDER_TITLESCREEN 150 #define INIT_ORDER_NIGHTSHIFT 75 +#define INIT_ORDER_CHAT -100 //! Should be last to ensure chat remains smooth during init. diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 3384590cfb3c..657bf083ffb5 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -36,6 +36,10 @@ var/datum/controller/failsafe/Failsafe /datum/controller/failsafe/New() + // Do not contaminate `usr`; if this is set, the MC main loop will have the usr of whoever called it, + // which results in all procs called by the MC inheriting that usr. + usr = null + // Highlander-style: there can only be one! Kill off the old and replace it with the new. if(Failsafe != src) if(istype(Failsafe)) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 3d5156ab2d05..ec65a57b6fd2 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -99,6 +99,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/current_ticklimit = TICK_LIMIT_RUNNING /datum/controller/master/New() + // Do not contaminate `usr`; if this is set, the MC main loop will have the usr of whoever called it, + // which results in all procs called by the MC inheriting that usr. + usr = null + //# 1. load configs if(!config_legacy) load_configuration() @@ -170,9 +174,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new * - -1 If we encountered a runtime trying to recreate it. */ /proc/Recreate_MC() - // Do not contaminate `usr`; if this is set, the MC main loop will have the usr of whoever called it, - // which results in all procs called by the MC inheriting that usr. - usr = null . = MC_RESTART_RTN_FAILED // So if we runtime, things know we failed. if (world.time < Master.restart_timeout) return MC_RESTART_RTN_COOLDOWN @@ -364,7 +365,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new message_prefix = "Failed to initialize [subsystem.name] subsystem after" tell_everyone = TRUE chat_warning = TRUE - // Since this is an explicit failure, shut its ticking off. + // Since this is an explicit failure, shut its ticking off. We also will not set its initialized variable. subsystem.subsystem_flags |= SS_NO_FIRE if(SS_INIT_NONE) message_prefix = "Initialized [subsystem.name] subsystem with errors within" diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 6fceb0f7e393..840fc21f0896 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -57,6 +57,7 @@ * This variable is set to TRUE after the subsystem has been initialized. * * * If this subsystem is marked as SS_NO_FIRE, this still will be set to TRUE. We just won't call Initialize(). + * * This will remain FALSE if initialization is an explicit failure. */ var/initialized = FALSE diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 3e20925ad97c..05e72cd7d0b6 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -10,6 +10,7 @@ SUBSYSTEM_DEF(chat) runlevels = RUNLEVELS_ALL priority = FIRE_PRIORITY_CHAT init_order = INIT_ORDER_CHAT + init_stage = INIT_STAGE_LATE var/list/payload_by_client = list() diff --git a/code/controllers/subsystem/nanoui.dm b/code/controllers/subsystem/nanoui.dm index e5bc5d6314f7..301ca96e7ced 100644 --- a/code/controllers/subsystem/nanoui.dm +++ b/code/controllers/subsystem/nanoui.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(nanoui) name = "NanoUI" priority = FIRE_PRIORITY_NANO + subsystem_flags = SS_NO_INIT wait = 7 /// A list of current open /nanoui UIs, grouped by src_object and ui_key. @@ -9,25 +10,20 @@ SUBSYSTEM_DEF(nanoui) /// A list of current open /nanoui UIs, not grouped, for use in processing. var/list/processing_uis = list() - - /datum/controller/subsystem/nanoui/fire(resumed) for(var/thing in processing_uis) var/datum/nanoui/UI = thing UI.process() - /datum/controller/subsystem/nanoui/Recover() if(SSnanoui.open_uis) open_uis |= SSnanoui.open_uis if(SSnanoui.processing_uis) processing_uis |= SSnanoui.processing_uis - /datum/controller/subsystem/nanoui/stat_entry() return ..() + " [processing_uis.len] UIs" - /** * Get an open /nanoui ui for the current user, src_object and ui_key and try to update it with data * @@ -56,7 +52,6 @@ SUBSYSTEM_DEF(nanoui) return ui - /** * Get an open /nanoui ui for the current user, src_object and ui_key * @@ -75,7 +70,6 @@ SUBSYSTEM_DEF(nanoui) if (ui.user == user) return ui - /** * Update all /nanoui uis attached to src_object * @@ -98,7 +92,6 @@ SUBSYSTEM_DEF(nanoui) else ui.close() - /** * Close all /nanoui uis attached to src_object * @@ -117,7 +110,6 @@ SUBSYSTEM_DEF(nanoui) ui.close() // If it's missing src_object or user, we want to close it even more. .++ - /** * Update /nanoui uis belonging to user * @@ -137,7 +129,6 @@ SUBSYSTEM_DEF(nanoui) ui.try_update(1) .++ - /** * Close /nanoui uis belonging to user * @@ -157,7 +148,6 @@ SUBSYSTEM_DEF(nanoui) ui.close() .++ - /** * Add a /nanoui ui to the list of open uis * This is called by the /nanoui open() proc @@ -173,7 +163,6 @@ SUBSYSTEM_DEF(nanoui) LAZYDISTINCTADD(ui.user.open_uis, ui) processing_uis += ui - /** * Remove a /nanoui ui from the list of open uis * This is called by the /nanoui close() proc @@ -199,7 +188,6 @@ SUBSYSTEM_DEF(nanoui) return TRUE - /** * This is called on user logout * Closes/clears all uis attached to the user's /mob @@ -211,7 +199,6 @@ SUBSYSTEM_DEF(nanoui) /datum/controller/subsystem/nanoui/proc/user_logout(mob/user) return close_user_uis(user) - /** * This is called when a player transfers from one mob to another * Transfers all open UIs to the new mob diff --git a/code/controllers/subsystem/parallax.dm b/code/controllers/subsystem/parallax.dm index 5c1b72de730e..0d5874546188 100644 --- a/code/controllers/subsystem/parallax.dm +++ b/code/controllers/subsystem/parallax.dm @@ -1,7 +1,7 @@ SUBSYSTEM_DEF(parallax) name = "Parallax" wait = 2 - subsystem_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND + subsystem_flags = SS_POST_FIRE_TIMING | SS_BACKGROUND | SS_NO_INIT priority = FIRE_PRIORITY_PARALLAX runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT var/list/currentrun diff --git a/code/controllers/subsystem/playtime.dm b/code/controllers/subsystem/playtime.dm index 4dbfd544fc68..617cdbf03350 100644 --- a/code/controllers/subsystem/playtime.dm +++ b/code/controllers/subsystem/playtime.dm @@ -9,7 +9,7 @@ SUBSYSTEM_DEF(playtime) name = "Playtime" wait = 10 MINUTES - subsystem_flags = SS_NO_TICK_CHECK + subsystem_flags = SS_NO_TICK_CHECK | SS_NO_INIT /datum/controller/subsystem/playtime/Shutdown() flush_playtimes() diff --git a/code/controllers/subsystem/status_effects.dm b/code/controllers/subsystem/status_effects.dm index cceedbafb1b4..03ec92e68bfa 100644 --- a/code/controllers/subsystem/status_effects.dm +++ b/code/controllers/subsystem/status_effects.dm @@ -1,6 +1,6 @@ SUBSYSTEM_DEF(status_effects) wait = 0.5 - subsystem_flags = NONE + subsystem_flags = SS_NO_INIT name = "Status Effects" /// ticking effects diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 59ef967b6069..c93852ef30fa 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(sun) name = "Sun" wait = 1 MINUTE + subsystem_flags = SS_NO_INIT var/static/datum/sun/sun = new /datum/controller/subsystem/sun/fire() diff --git a/code/controllers/subsystem/turbolift.dm b/code/controllers/subsystem/turbolift.dm index fd506f39f669..0afd8de21b43 100644 --- a/code/controllers/subsystem/turbolift.dm +++ b/code/controllers/subsystem/turbolift.dm @@ -1,6 +1,6 @@ SUBSYSTEM_DEF(turbolifts) name = "Turbolifts" - subsystem_flags = SS_NO_TICK_CHECK + subsystem_flags = SS_NO_TICK_CHECK | SS_NO_INIT wait = 10 var/static/list/moving_lifts = list() From 7cea2180e398fa49c5512d5e72dfc3a11027837a Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:01:21 -0500 Subject: [PATCH 10/28] update --- code/controllers/subsystem.dm | 1 + code/controllers/subsystem/ai_movement.dm | 4 +++- code/controllers/subsystem/automata.dm | 2 +- code/controllers/subsystem/chemistry.dm | 1 + code/controllers/subsystem/inactivity.dm | 4 ++-- code/controllers/subsystem/input.dm | 3 ++- code/modules/ai/holders/ai_holder-movement.dm | 4 ++++ 7 files changed, 14 insertions(+), 5 deletions(-) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 840fc21f0896..7c1a274dd973 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -246,6 +246,7 @@ * Sleeping in here prevents future fires until returned. */ /datum/controller/subsystem/proc/fire(resumed = FALSE) + SHOULD_NOT_SLEEP(TRUE) subsystem_flags |= SS_NO_FIRE CRASH("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.") diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index 0c909724f16d..af95444d4620 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -87,8 +87,10 @@ SUBSYSTEM_DEF(ai_movement) if(reschedule_delay) // eject; we don't change being_processed.ticking_(next|previous) if(being_processed.movement_bucket_next == being_processed) + // this was the only holder in the bucket buckets[bucket_offset] = null else + // this was not the only holder in the bucket, stitch it back together after the ejection. buckets[bucket_offset] = being_processed.movement_bucket_next being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next @@ -106,7 +108,7 @@ SUBSYSTEM_DEF(ai_movement) being_processed.movement_bucket_next = being_processed.movement_bucket_prev = being_processed being_processed.movement_bucket_position = inject_offset else - // get out + // get out if not rescheduling unregister_moving(being_processed) if(MC_TICK_CHECK) break diff --git a/code/controllers/subsystem/automata.dm b/code/controllers/subsystem/automata.dm index 6e7777980137..e1940555a400 100644 --- a/code/controllers/subsystem/automata.dm +++ b/code/controllers/subsystem/automata.dm @@ -4,7 +4,7 @@ SUBSYSTEM_DEF(automata) name = "Automata" wait = 1 - subsystem_flags = SS_TICKER + subsystem_flags = SS_TICKER | SS_NO_INIT /// all automata in world var/static/list/datum/automata/automatons = list() diff --git a/code/controllers/subsystem/chemistry.dm b/code/controllers/subsystem/chemistry.dm index ec025f09b03e..602a248c0e92 100644 --- a/code/controllers/subsystem/chemistry.dm +++ b/code/controllers/subsystem/chemistry.dm @@ -2,6 +2,7 @@ SUBSYSTEM_DEF(chemistry) name = "Chemistry" wait = 10 init_order = INIT_ORDER_CHEMISTRY + subsystem_flags = SS_NO_INIT /// id to instance dict of reagents var/list/reagent_lookup = list() diff --git a/code/controllers/subsystem/inactivity.dm b/code/controllers/subsystem/inactivity.dm index 53e15c3bc179..70d04751155b 100644 --- a/code/controllers/subsystem/inactivity.dm +++ b/code/controllers/subsystem/inactivity.dm @@ -1,7 +1,7 @@ SUBSYSTEM_DEF(inactivity) - name = "AFK Kick" + name = "Inactivity" wait = 600 - subsystem_flags = SS_BACKGROUND | SS_NO_TICK_CHECK + subsystem_flags = SS_BACKGROUND | SS_NO_TICK_CHECK | SS_NO_INIT /datum/controller/subsystem/inactivity/fire() if(config_legacy.kick_inactive) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 31d921504741..10d8c2fb1dd4 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -2,7 +2,8 @@ SUBSYSTEM_DEF(input) name = "Input" wait = 0.25 // scale to 40 fps init_order = INIT_ORDER_INPUT - subsystem_flags = NONE + init_stage = INIT_STAGE_WORLD + subsystem_flags = SS_NO_INIT priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY diff --git a/code/modules/ai/holders/ai_holder-movement.dm b/code/modules/ai/holders/ai_holder-movement.dm index ce2b6e76d3de..81087b4ae0a4 100644 --- a/code/modules/ai/holders/ai_holder-movement.dm +++ b/code/modules/ai/holders/ai_holder-movement.dm @@ -11,8 +11,12 @@ /// bucket position var/movement_bucket_position /// last datum in bucket + /// + /// * We are a circularly double-linked list. If we are the only one in the bucket, this is ourselves. var/datum/ai_holder/movement_bucket_prev /// next datum in bucket + /// + /// * We are a circularly double-linked list. If we are the only one in the bucket, this is ourselves. var/datum/ai_holder/movement_bucket_next /// movement cycle var/movement_cycle From 46fe65edcefbfc69451341dea44051b9bceafe9f Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:10:03 -0500 Subject: [PATCH 11/28] patch loop to use better logging / be more forgiving of errorlevel --- code/controllers/master.dm | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index ec65a57b6fd2..84c8ab414a13 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -574,16 +574,18 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //* **Experimental**: Check every queue, every tick. if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(ticker_subsystems) <= 0) - if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) - log_world("MC: SoftReset() failed, crashing") - return + stack_trace("MC: CheckQueue failed. Current error_level is [round(error_level, 0.25)]") + if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) + error_level++ + CRASH("MC: SoftReset() failed, exiting loop()") - if (!error_level) + if (error_level < 2) //except for the first strike, stop incrmenting our iteration so failsafe enters defcon iteration++ - - error_level++ + else + cached_runlevel = null //3 strikes, Lets reset the runlevel lists current_ticklimit = TICK_LIMIT_RUNNING - sleep(10) + sleep((1 SECONDS) * error_level) + error_level++ continue if (queue_head) From 67540a038fc77b12b0558218e3d14db29e8f495a Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:15:56 -0500 Subject: [PATCH 12/28] handle sleeping offline / reorg --- code/controllers/master.dm | 51 +++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 28 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 84c8ab414a13..60d58d1fb79a 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -193,20 +193,22 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /datum/controller/master/Recover() var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n" - for (var/varname in Master.vars) - switch (varname) - if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk. - continue - - else - var/varval = Master.vars[varname] - if (istype(varval, /datum)) // Check if it has a type var. - var/datum/D = varval - msg += "\t [varname] = [D]([D.type])\n" - - else - msg += "\t [varname] = [varval]\n" - + var/list/master_attributes = Master.vars + var/list/filtered_variables = list( + NAMEOF(src, name), + NAMEOF(src, parent_type), + NAMEOF(src, statclick), + NAMEOF(src, tag), + NAMEOF(src, type), + NAMEOF(src, vars), + ) + for (var/varname in master_attributes - filtered_variables) + var/varval = master_attributes[varname] + if (isdatum(varval)) // Check if it has a type var. + var/datum/D = varval + msg += "\t [varname] = [D]([D.type])\n" + else + msg += "\t [varname] = [varval]\n" log_world(msg) var/datum/controller/subsystem/BadBoy = Master.last_type_processed @@ -218,27 +220,22 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(2) msg = "The [BadBoy.name] subsystem was the last to fire for 2 controller restarts. It will be recovered now and disabled if it happens again." FireHim = TRUE - if(3) - msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined." + msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be put offline." BadBoy.subsystem_flags |= SS_NO_FIRE - if(msg) - to_chat(GLOB.admins, SPAN_BOLDANNOUNCE("[msg]")) + to_chat(GLOB.admins, span_boldannounce("[msg]")) log_world(msg) if (istype(Master.subsystems)) if(FireHim) - Master.subsystems += new BadBoy.type // NEW_SS_GLOBAL will remove the old one. - + Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one subsystems = Master.subsystems current_runlevel = Master.current_runlevel - initialized = TRUE StartProcessing(10) - else - to_chat(world, SPAN_BOLDANNOUNCE("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) - Initialize(20, TRUE) + to_chat(world, span_boldannounce("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) + Initialize(20, TRUE, FALSE) /** * Please don't stuff random bullshit here, @@ -307,9 +304,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new to_chat(world, SPAN_BOLDANNOUNCE("[msg]")) log_world(msg) - // Record initialization finish. - var/initialized_tod = REALTIMEOFDAY - // Set world options. world.set_fps(config_legacy.fps) @@ -322,12 +316,13 @@ GLOBAL_REAL(Master, /datum/controller/master) = new world.TgsInitializationComplete() // Handle sleeping offline after initializations. + var/rtod_sleep_offline_check = REALTIMEOFDAY if(sleep_offline_after_initializations) world.sleep_offline = TRUE sleep(1 TICK) if(sleep_offline_after_initializations) // && CONFIG_GET(flag/resume_after_initializations)) world.sleep_offline = FALSE - initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10 + initializations_finished_with_no_players_logged_in = rtod_sleep_offline_check < REALTIMEOFDAY - 10 /** * Initialize a given subsystem and handle the results. From 6bb83ed1fb7cf0d4948f3c4dff449ce282006f0a Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 14:13:53 -0500 Subject: [PATCH 13/28] sigh --- citadel.dme | 1 + code/__DEFINES/controllers/_master.dm | 9 ++ code/__DEFINES/controllers/_subsystem.dm | 28 +++- code/controllers/master.dm | 120 +++++++++++------- code/controllers/subsystem.dm | 16 ++- .../subsystem/__test_bad_subsystem_sleeps.dm | 40 ++++++ 6 files changed, 159 insertions(+), 55 deletions(-) create mode 100644 code/controllers/subsystem/__test_bad_subsystem_sleeps.dm diff --git a/citadel.dme b/citadel.dme index 77dc70d24882..d315eac0880f 100644 --- a/citadel.dme +++ b/citadel.dme @@ -542,6 +542,7 @@ #include "code\controllers\repository\material_traits.dm" #include "code\controllers\repository\materials.dm" #include "code\controllers\repository\structs.dm" +#include "code\controllers\subsystem\__test_bad_subsystem_sleeps.dm" #include "code\controllers\subsystem\ai_holders.dm" #include "code\controllers\subsystem\ai_legacy.dm" #include "code\controllers\subsystem\ai_movement.dm" diff --git a/code/__DEFINES/controllers/_master.dm b/code/__DEFINES/controllers/_master.dm index d67baa915d99..26502007cf94 100644 --- a/code/__DEFINES/controllers/_master.dm +++ b/code/__DEFINES/controllers/_master.dm @@ -48,3 +48,12 @@ #define MC_LOOP_RTN_NEWSTAGES 1 /// We want the MC to exit. #define MC_LOOP_RTN_GRACEFUL_EXIT 2 + +//* Master Controller RunQueue() return values *// + +/// Unknown or error +#define MC_RUN_RTN_UNKNOWN 0 +/// Success; full completion +#define MC_RUN_RTN_FULL_COMPLETION 1 +/// Atleast one subsystem was sleeping or pausing +#define MC_RUN_RTN_PARTIAL_COMPLETION 2 diff --git a/code/__DEFINES/controllers/_subsystem.dm b/code/__DEFINES/controllers/_subsystem.dm index 75cb623230fd..6ba5a915c4b9 100644 --- a/code/__DEFINES/controllers/_subsystem.dm +++ b/code/__DEFINES/controllers/_subsystem.dm @@ -117,17 +117,33 @@ DEFINE_BITFIELD(subsystem_flags, list( //* Subsystem 'state' variable *// -/// aint doing shit. +/** + * Not doing anything right now. + */ #define SS_IDLE 0 -/// queued to run +/** + * In the MC's run-queue + */ #define SS_QUEUED 1 -/// actively running +/** + * Set before the MC ignites a subsystem. This is the state while it's currently running. + */ #define SS_RUNNING 2 -/// paused by mc_tick_check +/** + * Paused by MC_TICK_CHECK + * + * * Should not be set by anything other than ignite(). + */ #define SS_PAUSED 3 -/// fire() slept. +/** + * fire() is currently sleeping. + */ #define SS_SLEEPING 4 -/// in the middle of pausing +/** + * In the middle of pausing by MC_TICK_CHECK. + * + * * Set by the macro, and changed to `SS_PAUSED` by ignite() + */ #define SS_PAUSING 5 //* Misc *// diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 60d58d1fb79a..456707d5c413 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -75,6 +75,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// The current initialization stage we're at. var/static/init_stage_completed = 0 + /// An init stage change was queued. + var/init_stage_change_pending = FALSE //* Processing Variables *// //* These are set during a Loop(). *// @@ -224,7 +226,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be put offline." BadBoy.subsystem_flags |= SS_NO_FIRE if(msg) - to_chat(GLOB.admins, span_boldannounce("[msg]")) + to_chat(GLOB.admins, SPAN_BOLDANNOUNCE("[msg]")) log_world(msg) if (istype(Master.subsystems)) @@ -234,7 +236,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new current_runlevel = Master.current_runlevel StartProcessing(10) else - to_chat(world, span_boldannounce("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) + to_chat(world, SPAN_BOLDANNOUNCE("The Master Controller is having some issues, we will need to re-initialize EVERYTHING")) Initialize(20, TRUE, FALSE) /** @@ -502,9 +504,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag))) var/starting_tick_usage = TICK_USAGE - // If another stage of init was completed, restart. + // check if we need to queue an init stage change if (init_stage != init_stage_completed) - return MC_LOOP_RTN_NEWSTAGES + // set stage change pending; this'll stop new (but not paused / sleeping) subsystems from being queued to run, + // including ticker subsystems! + init_stage_change_pending = TRUE + // warning: here be dragons! + // no subsystem must be running for init stage change to happen + // checking for running and paused subsystems is easy; we just check that there's nothing in the queue to run. + // checking for sleeping subsystems is harder as they're not re-queued until they stop sleeping + // thus, we have to loop through everything. + if(!queue_head && !laggy_sleeping_subsystem_check()) + return MC_LOOP_RTN_NEWSTAGES + // If we're paused for some reason, well, pause. if (processing <= 0) current_ticklimit = TICK_LIMIT_RUNNING @@ -568,37 +580,43 @@ GLOBAL_REAL(Master, /datum/controller/master) = new SS.next_fire = world.time + world.tick_lag * rand(0, DS2TICKS(min(SS.wait, 2 SECONDS))) //* **Experimental**: Check every queue, every tick. - if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(ticker_subsystems) <= 0) - stack_trace("MC: CheckQueue failed. Current error_level is [round(error_level, 0.25)]") - if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems)) + //* If init stage change is pending, we will not re-queue subsystems that are not currently queued to fire + if(!init_stage_change_pending) + if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(ticker_subsystems) <= 0) + stack_trace("MC: CheckQueue failed. Current error_level is [round(error_level, 0.25)]") + if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) + error_level++ + CRASH("MC: SoftReset() failed, exiting loop()") + + if (error_level < 2) //except for the first strike, stop incrmenting our iteration so failsafe enters defcon + iteration++ + else + cached_runlevel = null //3 strikes, Lets reset the runlevel lists + current_ticklimit = TICK_LIMIT_RUNNING + sleep((1 SECONDS) * error_level) error_level++ - CRASH("MC: SoftReset() failed, exiting loop()") - - if (error_level < 2) //except for the first strike, stop incrmenting our iteration so failsafe enters defcon - iteration++ - else - cached_runlevel = null //3 strikes, Lets reset the runlevel lists - current_ticklimit = TICK_LIMIT_RUNNING - sleep((1 SECONDS) * error_level) - error_level++ - continue + continue if (queue_head) - if (RunQueue() <= 0) //error running queue - stack_trace("MC: RunQueue failed. Current error_level is [round(error_level, 0.25)]") - if (error_level > 1) //skip the first error, - if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) - CRASH("MC: SoftReset() failed, exiting loop()") - - if (error_level <= 2) //after 3 strikes stop incrmenting our iteration so failsafe enters defcon - iteration++ - else - cached_runlevel = null //3 strikes, Lets also reset the runlevel lists - current_ticklimit = TICK_LIMIT_RUNNING - sleep((1 SECONDS) * error_level) + var/run_result = RunQueue() + switch(run_result) + if(MC_RUN_RTN_UNKNOWN) + // Error running queue + stack_trace("MC: RunQueue failed. Current error_level is [round(error_level, 0.25)]") + if (error_level > 1) //skip the first error, + if (!SoftReset(ticker_subsystems, runlevel_sorted_subsystems)) + CRASH("MC: SoftReset() failed, exiting loop()") + + if (error_level <= 2) //after 3 strikes stop incrmenting our iteration so failsafe enters defcon + iteration++ + else + cached_runlevel = null //3 strikes, Lets also reset the runlevel lists + current_ticklimit = TICK_LIMIT_RUNNING + sleep((1 SECONDS) * error_level) + error_level++ + continue error_level++ - continue - error_level++ + if (error_level > 0) error_level = max(MC_AVERAGE_SLOW(error_level-1, error_level), 0) if (!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync @@ -662,7 +680,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage. /datum/controller/master/proc/RunQueue() - . = FALSE + . = MC_RUN_RTN_UNKNOWN var/datum/controller/subsystem/queue_node var/queue_node_flags var/queue_node_priority @@ -677,6 +695,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // the % of tick used by the current running subsystem var/queue_node_tick_usage + // is a subsystem stopping mid-cycle? this means either pausing or sleeping + var/something_is_mid_cycle /** * Keep running while we have stuff to run and we haven't gone over a tick @@ -736,12 +756,22 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.state = SS_RUNNING // ignite / fire the head node + // ignite() will return immediately even if fire() sleeps. + // the return value will be SS_SLEEPING if fire() is sleeping. queue_node_tick_usage = TICK_USAGE var/state = queue_node.ignite(queue_node_paused) queue_node_tick_usage = TICK_USAGE - queue_node_tick_usage - if (state == SS_RUNNING) - state = SS_IDLE + switch(state) + if(SS_RUNNING) + // successful, full run + state = SS_IDLE + if(SS_PAUSING, SS_SLEEPING) + // partial run or is not done. + something_is_mid_cycle = TRUE + else + stack_trace("subsystem had unexpected state: [state]") + state = SS_IDLE current_tick_budget -= queue_node_priority @@ -799,7 +829,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // move to next queue_node = queue_node.queue_next - . = TRUE + . = something_is_mid_cycle ? MC_RUN_RTN_PARTIAL_COMPLETION : MC_RUN_RTN_FULL_COMPLETION /** * Resets the queue, and all subsystems, while filtering out the subsystem lists called if any mc's queue procs runtime or exit improperly. @@ -872,18 +902,16 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/datum/controller/subsystem/SS = S SS.StopLoadingMap() -/* -/datum/controller/master/proc/UpdateTickRate() - if (!processing) - return - var/client_count = length(GLOB.clients) - if (client_count < CONFIG_GET(number/mc_tick_rate/disable_high_pop_mc_mode_amount)) - processing = CONFIG_GET(number/mc_tick_rate/base_mc_tick_rate) - else if (client_count > CONFIG_GET(number/mc_tick_rate/high_pop_mc_mode_amount)) - processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate) -*/ - /datum/controller/master/proc/OnConfigLoad() for (var/thing in subsystems) var/datum/controller/subsystem/SS = thing SS.OnConfigLoad() + +/** + * CitRP snowflake special: Check if any subsystems are sleeping. + */ +/datum/controller/master/proc/laggy_sleeping_subsystem_check() + for(var/datum/controller/subsystem/ss in subsystems) + if(ss.state == SS_SLEEPING) + return TRUE + return FALSE diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7c1a274dd973..377316a3da88 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -213,21 +213,33 @@ /** * This is used so the mc knows when the subsystem sleeps. * DO NOT OVERRIDE THIS. + * + * * If fire() sleeps, the return value will be SS_SLEEPING. + * * If fire() does not sleep, the return value will be SS_PAUSING or SS_RUNNING. + * + * @return the state we're now in. This return value is only used if fire() does not sleep. */ /datum/controller/subsystem/proc/ignite(resumed = FALSE) SHOULD_NOT_OVERRIDE(TRUE) + // This makes us return the last return value when we sleep set waitfor = FALSE + // Paranoid set. . = SS_IDLE + // Record tick allocation tick_allocation_last = Master.current_ticklimit-(TICK_USAGE) tick_allocation_avg = MC_AVERAGE(tick_allocation_avg, tick_allocation_last) + // Fire; the default return value will return the fact we're sleeping if fire() sleeps. . = SS_SLEEPING fire(resumed) + // Set the default return value to either RUNNING or PAUSING. . = state + // If the MC detected we are sleeping, set back to idle now that we're done sleeping. if (state == SS_SLEEPING) state = SS_IDLE - + // If we're pausing, re-queue us for the next cycle. + // This can interact weirdly with sleeps. if (state == SS_PAUSING) var/QT = queued_time enqueue() @@ -246,7 +258,6 @@ * Sleeping in here prevents future fires until returned. */ /datum/controller/subsystem/proc/fire(resumed = FALSE) - SHOULD_NOT_SLEEP(TRUE) subsystem_flags |= SS_NO_FIRE CRASH("Subsystem [src]([type]) does not fire() but did not set the SS_NO_FIRE flag. Please add the SS_NO_FIRE flag to any subsystem that doesn't fire so it doesn't get added to the processing list and waste cpu.") @@ -256,7 +267,6 @@ subsystem_flags |= SS_NO_FIRE if (Master) Master.subsystems -= src - return ..() /** diff --git a/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm b/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm new file mode 100644 index 000000000000..7867c34533d8 --- /dev/null +++ b/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm @@ -0,0 +1,40 @@ +//* This file is explicitly licensed under the MIT license. *// +//* Copyright (c) 2024 Citadel Station Developers *// + +/** + * Used to intentionally fuck up the MC fire() by sleeping ridiculous + * amounts of time. + * + * Used to test sleep handling and stage change behavior. + * + * **This file should not be ticked outside of special development testing.** + */ +#warn Bad subsystem sleep tester is ticked. + +/datum/controller/subsystem/__test_bad_subsystem_sleeps + init_stage = INIT_STAGE_BACKEND + init_order = 100 // this doesn't matter tbh; what matters is it runs as soon as possible so the test goes faster + subsystem_flags = SS_NO_INIT + + var/is_currently_sleeping = FALSE + var/should_resume = FALSE + var/first_fire = TRUE + var/mc_init_stage_at_start_of_sleep + +/datum/controller/subsystem/__test_bad_subsystem_sleeps/fire(resumed) + if(first_fire) + is_currently_sleeping = TRUE + mc_init_stage_at_start_of_sleep = Master.init_stage_completed + sleep(10 SECONDS) + is_currently_sleeping = FALSE + should_resume = TRUE + pause() + if(mc_init_stage_at_start_of_sleep != Master.init_stage_completed) + CRASH("master controller moved on when we were trying to block init") + return + if(is_currently_sleeping) + CRASH("re-fired while sleeping") + if(!resumed && should_resume) + CRASH("wasn't resumed when we should resume") + if(resumed) + should_resume = FALSE From af52c723b173e490e347893623da1b2d3e1c3261 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:29:16 -0500 Subject: [PATCH 14/28] yikes --- code/__DEFINES/controllers/_subsystem-init.dm | 4 +- code/controllers/master.dm | 103 +++++++----------- code/controllers/subsystem.dm | 59 ++++++++-- .../subsystem/__test_bad_subsystem_sleeps.dm | 12 +- code/controllers/subsystem/input.dm | 9 +- .../middleware/keybindings.dm | 2 +- 6 files changed, 104 insertions(+), 85 deletions(-) diff --git a/code/__DEFINES/controllers/_subsystem-init.dm b/code/__DEFINES/controllers/_subsystem-init.dm index c00ad66e13ac..12aaff4db93b 100644 --- a/code/__DEFINES/controllers/_subsystem-init.dm +++ b/code/__DEFINES/controllers/_subsystem-init.dm @@ -36,6 +36,7 @@ //* Early *// #define INIT_ORDER_EARLY_INIT 200 +#define INIT_ORDER_INPUT 170 #define INIT_ORDER_PREFERENCES 150 #define INIT_ORDER_JOBS 125 #define INIT_ORDER_ASSETS 100 @@ -46,9 +47,6 @@ //* World *// -#define INIT_ORDER_IPINTEL 197 - -#define INIT_ORDER_INPUT 160 #define INIT_ORDER_CHARACTERS 140 #define INIT_ORDER_SOUNDS 130 #define INIT_ORDER_GARBAGE 120 diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 456707d5c413..7c0cbd5a96c7 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -34,8 +34,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/list/subsystems //# Vars for keeping track of tick drift. - var/init_timeofday - var/init_time + var/loop_start_timeofday + var/loop_start_time var/tickdrift = 0 /// How long is the MC sleeping between runs, read only (set by Loop() based off of anti-tick-contention heuristics). @@ -77,6 +77,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/static/init_stage_completed = 0 /// An init stage change was queued. var/init_stage_change_pending = FALSE + /// The init stage currently being ran by the main ticker loop + var/init_stage_ticking //* Processing Variables *// //* These are set during a Loop(). *// @@ -492,8 +494,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/cached_runlevel = current_runlevel var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel] - init_timeofday = REALTIMEOFDAY - init_time = world.time + loop_start_timeofday = REALTIMEOFDAY + loop_start_time = world.time + init_stage_ticking = init_stage iteration = 1 var/error_level = 0 @@ -501,7 +504,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //# The actual loop. while (1) - tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag))) + tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - loop_start_timeofday) - (world.time - loop_start_time)) / world.tick_lag))) var/starting_tick_usage = TICK_USAGE // check if we need to queue an init stage change @@ -564,23 +567,18 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //# Now do the actual stuff. - //* **Experimental**: Check every tick. + // Check if runlevel changed if(cached_runlevel != current_runlevel) - // Resechedule subsystems. - var/list/old_subsystems = current_runlevel_subsystems + // Resechedule subsystems that are not already part of the runlevel, and are running behind. + var/list/old_runlevel_subsystems = current_runlevel_subsystems cached_runlevel = current_runlevel current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel] - - // Now we'll go through all the subsystems we want to offset and give them a next_fire. - for(var/datum/controller/subsystem/SS as anything in current_runlevel_subsystems) - // We only want to offset it if it's new and also behind. - if(SS.next_fire > world.time || (SS in old_subsystems)) + for(var/datum/controller/subsystem/adding_to_runlevel as anything in (current_runlevel_subsystems - old_runlevel_subsystems)) + if(adding_to_runlevel.next_fire > world.time) continue + adding_to_runlevel.next_fire = world.time + world.tick_lag * rand(0, DS2TICKS(min(adding_to_runlevel.wait, 2 SECONDS))) - SS.next_fire = world.time + world.tick_lag * rand(0, DS2TICKS(min(SS.wait, 2 SECONDS))) - - //* **Experimental**: Check every queue, every tick. - //* If init stage change is pending, we will not re-queue subsystems that are not currently queued to fire + // If no init stage change is pending, re-queue any subsystems that are idle and are ready to fire. if(!init_stage_change_pending) if (CheckQueue(current_runlevel_subsystems) <= 0 || CheckQueue(ticker_subsystems) <= 0) stack_trace("MC: CheckQueue failed. Current error_level is [round(error_level, 0.25)]") @@ -640,40 +638,31 @@ GLOBAL_REAL(Master, /datum/controller/master) = new sleep(world.tick_lag * (processing * sleep_delta)) /** - * This is what decides if something should run. + * Checks a list of subsystems and enqueues anything that is idle and is ready to run. * - * Arguments: + * @params * * subsystemstocheck - List of systems to check. */ -/datum/controller/master/proc/CheckQueue(list/subsystemstocheck) +/datum/controller/master/proc/CheckQueue(list/datum/controller/subsystem/subsystemstocheck) . = FALSE // So the mc knows if we runtimed. - // We create our variables outside of the loops to save on overhead. - var/datum/controller/subsystem/SS - var/SS_flags - - for (var/thing in subsystemstocheck) - if (!thing) - subsystemstocheck -= thing + for (var/datum/controller/subsystem/SS as anything in subsystemstocheck) + if(!SS) + subsystemstocheck -= SS - SS = thing if (SS.state != SS_IDLE) continue - if (SS.can_fire <= 0) continue - if (SS.next_fire > world.time) continue - SS_flags = SS.subsystem_flags + var/SS_flags = SS.subsystem_flags + if (SS_flags & SS_NO_FIRE) subsystemstocheck -= SS continue - if ((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time) - continue - SS.enqueue() . = TRUE @@ -690,7 +679,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/tick_precentage var/tick_remaining var/ran = TRUE // This is right. - var/ran_non_SSticker = FALSE + var/ran_non_ticker = FALSE var/bg_calc // Have we swtiched current_tick_budget to background mode yet? // the % of tick used by the current running subsystem @@ -720,7 +709,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new * (unless we haven't even ran anything this tick, since its unlikely they will ever be able run in those cases, so we just let them run) */ if (queue_node_flags & SS_NO_TICK_CHECK) - if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_SSticker) + if (queue_node.tick_usage > TICK_LIMIT_RUNNING - TICK_USAGE && ran_non_ticker) queue_node.queued_priority += queue_priority_count * 0.1 queue_priority_count -= queue_node_priority queue_priority_count += queue_node.queued_priority @@ -746,11 +735,11 @@ GLOBAL_REAL(Master, /datum/controller/master) = new current_ticklimit = round(TICK_USAGE + tick_precentage) if (!(queue_node_flags & SS_TICKER)) - ran_non_SSticker = TRUE + ran_non_ticker = TRUE ran = TRUE - queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING) + queue_node_paused = (queue_node.state == SS_PAUSED) last_type_processed = queue_node queue_node.state = SS_RUNNING @@ -758,16 +747,21 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // ignite / fire the head node // ignite() will return immediately even if fire() sleeps. // the return value will be SS_SLEEPING if fire() is sleeping. + // the return value will be SS_RUNNING if it did not sleep and ran to completion. + // the return value will be SS_PAUSING if it did not sleep and yielded. queue_node_tick_usage = TICK_USAGE - var/state = queue_node.ignite(queue_node_paused) + var/state = queue_node.ignite(queue_node.state == SS_PAUSED) queue_node_tick_usage = TICK_USAGE - queue_node_tick_usage switch(state) if(SS_RUNNING) // successful, full run state = SS_IDLE - if(SS_PAUSING, SS_SLEEPING) - // partial run or is not done. + if(SS_PAUSING) + // partial run + something_is_mid_cycle = TRUE + if(SS_SLEEPING) + // it slept. this is very bad. something_is_mid_cycle = TRUE else stack_trace("subsystem had unexpected state: [state]") @@ -782,20 +776,20 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.state = state // if it paused mid-run, track that - if (state == SS_PAUSED) + if (state == SS_PAUSING) queue_node.paused_ticks++ queue_node.paused_tick_usage += queue_node_tick_usage queue_node = queue_node.queue_next continue // it did not pause; this is a complete run - queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks) - queue_node_tick_usage += queue_node.paused_tick_usage + queue_node_tick_usage += queue_node.paused_tick_usage queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, queue_node_tick_usage) queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(queue_node_tick_usage)) + queue_node.paused_ticks = 0 queue_node.paused_tick_usage = 0 @@ -807,25 +801,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.last_fire = world.time queue_node.times_fired++ - // schedule next run - if (queue_node_flags & SS_TICKER) - // ticker: run this many ticks after always - queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait) - else if (queue_node_flags & SS_POST_FIRE_TIMING) - // post fire timing: fire this much wait after current time, with tick overrun punishment - queue_node.next_fire = world.time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100)) - else if (queue_node_flags & SS_KEEP_TIMING) - // keep timing: fire this much wait after *the last time we should have fired*, without tick overrun punishment - // **experimental**: do not keep timing past last 10 seconds, if something is running behind that much don't permanently accelerate it. - queue_node.next_fire = max(world.time - 10 SECONDS, queue_node.next_fire + queue_node.wait) - else - // normal: fire this much wait after when we were queued, with tick overrun punishment - queue_node.next_fire = queue_node.queued_time + queue_node.wait + (world.tick_lag * (queue_node.tick_overrun/100)) - - queue_node.queued_time = 0 - - // Remove from queue. + // remove from queue queue_node.dequeue() + // update the next time it should be available to queue + queue_node.update_nextfire() // move to next queue_node = queue_node.queue_next diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 377316a3da88..070b16809286 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -1,11 +1,11 @@ /** - * Sorts subsystems alphabetically. + * Sorts subsystems for display (alphabetically). */ /proc/cmp_subsystem_display(datum/controller/subsystem/a, datum/controller/subsystem/b) return sorttext(b.name, a.name) /** - * Sorts subsystems by init_order. + * Sorts subsystems by init_order and init_stage. */ /proc/cmp_subsystem_init(datum/controller/subsystem/a, datum/controller/subsystem/b) // Uses initial() so it can be used on types. @@ -14,7 +14,9 @@ return initial(b.init_order) - initial(a.init_order) /** - * Sorts subsystems by priority. + * Sorts subsystems by priority, from lowest to highest. + * + * * This does not take into account SS_BACKGROUND and SS_TICKER flags! */ /proc/cmp_subsystem_priority(datum/controller/subsystem/a, datum/controller/subsystem/b) return a.priority - b.priority @@ -26,6 +28,15 @@ * * Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration. * Changing the name is required. + * + * ## Sleeping + * + * If a subsystem sleeps during a tick, it is very, very bad. + * + * * Sleeping orphans the subsystem's call stack from the MC's. The MC is no longer able to control the subsystem's tick usage. + * * Sleeping is handled, but not supported. This means the MC won't crash / do anything nasty, but normal timing will nonetheless + * be problematic. + * * Sleeping causes things like paused tick tracking to go out of wack. */ /datum/controller/subsystem //# Metadata; you should define these. @@ -133,6 +144,9 @@ var/times_fired = 0 /// Time the subsystem entered the queue, (for timing and priority reasons). + /// + /// * This doesn't take into account pauses and sleeps. queued_time is the time it was initially put into queue + /// for a full firing cycle. var/queued_time = 0 /** @@ -238,13 +252,14 @@ // If the MC detected we are sleeping, set back to idle now that we're done sleeping. if (state == SS_SLEEPING) state = SS_IDLE - // If we're pausing, re-queue us for the next cycle. - // This can interact weirdly with sleeps. if (state == SS_PAUSING) - var/QT = queued_time - enqueue() state = SS_PAUSED - queued_time = QT + //! HACK: if we are not queued, queue us + // this is to handle pausing during sleep. + // normally, the MC does not eject us if we are pausing. + // a sleep, however, is not considered a pause. + if(!queued_time) + enqueue() else // track time between runs var/full_run_took = world.time - last_fire @@ -269,6 +284,33 @@ Master.subsystems -= src return ..() +/** + * Updates `next_fire` for the next run. + * + * @params + * * reset_time - Entirely reset the subsystem's stateful time tracking including tick-overrun, post fire timing, etc. + */ +/datum/controller/subsystem/proc/update_nextfire(reset_time) + if(reset_time) + next_fire = (subsystem_flags & SS_TICKER) ? (world.time + (world.tick_lag * wait)) : (world.time + wait) + return + + var/queue_node_flags = subsystem_flags + + if (queue_node_flags & SS_TICKER) + // ticker: run this many ticks after always + next_fire = world.time + (world.tick_lag * wait) + else if (queue_node_flags & SS_POST_FIRE_TIMING) + // post fire timing: fire this much wait after current time, with tick overrun punishment + next_fire = world.time + wait + (world.tick_lag * (tick_overrun / 100)) + else if (queue_node_flags & SS_KEEP_TIMING) + // keep timing: fire this much wait after *the last time we should have fired*, without tick overrun punishment + // **experimental**: do not keep timing past last 10 seconds, if something is running behind that much don't permanently accelerate it. + next_fire = max(world.time - 10 SECONDS, next_fire + wait) + else + // normal: fire this much wait after when we were queued, with tick overrun punishment + next_fire = queued_time + wait + (world.tick_lag * (tick_overrun / 100)) + /** * Queue it to run. * (we loop thru a linked list until we get to the end or find the right point) @@ -361,7 +403,6 @@ switch(state) if(SS_RUNNING) state = SS_PAUSED - if(SS_SLEEPING) state = SS_PAUSING diff --git a/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm b/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm index 7867c34533d8..0b9f7ddd8833 100644 --- a/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm +++ b/code/controllers/subsystem/__test_bad_subsystem_sleeps.dm @@ -11,10 +11,12 @@ */ #warn Bad subsystem sleep tester is ticked. -/datum/controller/subsystem/__test_bad_subsystem_sleeps +SUBSYSTEM_DEF(__test_bad_subsystem_sleeps) + name = "-- TEST BAD SUBSYSTEM SLEEPS --" init_stage = INIT_STAGE_BACKEND init_order = 100 // this doesn't matter tbh; what matters is it runs as soon as possible so the test goes faster subsystem_flags = SS_NO_INIT + runlevels = RUNLEVELS_ALL var/is_currently_sleeping = FALSE var/should_resume = FALSE @@ -24,13 +26,13 @@ /datum/controller/subsystem/__test_bad_subsystem_sleeps/fire(resumed) if(first_fire) is_currently_sleeping = TRUE - mc_init_stage_at_start_of_sleep = Master.init_stage_completed + mc_init_stage_at_start_of_sleep = Master.init_stage_ticking sleep(10 SECONDS) is_currently_sleeping = FALSE + if(mc_init_stage_at_start_of_sleep != Master.init_stage_ticking) + CRASH("master controller moved on when we were trying to block init") should_resume = TRUE pause() - if(mc_init_stage_at_start_of_sleep != Master.init_stage_completed) - CRASH("master controller moved on when we were trying to block init") return if(is_currently_sleeping) CRASH("re-fired while sleeping") @@ -38,3 +40,5 @@ CRASH("wasn't resumed when we should resume") if(resumed) should_resume = FALSE + if(mc_init_stage_at_start_of_sleep != Master.init_stage_ticking) + CRASH("master controller moved on when we were trying to block init") diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index 10d8c2fb1dd4..e13c15858a1b 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -2,7 +2,7 @@ SUBSYSTEM_DEF(input) name = "Input" wait = 0.25 // scale to 40 fps init_order = INIT_ORDER_INPUT - init_stage = INIT_STAGE_WORLD + init_stage = INIT_STAGE_EARLY subsystem_flags = SS_NO_INIT priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY @@ -24,16 +24,13 @@ SUBSYSTEM_DEF(input) /datum/controller/subsystem/input/Initialize() setup_macrosets() - // set init early so refresh macrosets works - initialized = TRUE refresh_client_macro_sets() - - return ..() + return SS_INIT_SUCCESS /datum/controller/subsystem/input/Recover() - initialized = SSinput.initialized setup_macrosets() refresh_client_macro_sets() + initialized = SSinput.initialized /// Sets up the key list for classic mode for when badmins screw up vv's. /datum/controller/subsystem/input/proc/setup_macrosets() diff --git a/code/modules/client/game_preferences/middleware/keybindings.dm b/code/modules/client/game_preferences/middleware/keybindings.dm index 006a156a7a5d..607b9e040db2 100644 --- a/code/modules/client/game_preferences/middleware/keybindings.dm +++ b/code/modules/client/game_preferences/middleware/keybindings.dm @@ -6,8 +6,8 @@ key = "keybindings" /datum/game_preference_middleware/keybindings/on_initial_load(datum/game_preferences/prefs) - prefs.active?.update_movement_keys(src) prefs.active?.set_macros() + prefs.active?.update_movement_keys(src) return ..() /datum/game_preference_middleware/keybindings/handle_reset(datum/game_preferences/prefs) From d01242bd59927bffe635f108368f788a0a5c0916 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:29:25 -0500 Subject: [PATCH 15/28] no need to keep ethat included --- citadel.dme | 1 - 1 file changed, 1 deletion(-) diff --git a/citadel.dme b/citadel.dme index d315eac0880f..77dc70d24882 100644 --- a/citadel.dme +++ b/citadel.dme @@ -542,7 +542,6 @@ #include "code\controllers\repository\material_traits.dm" #include "code\controllers\repository\materials.dm" #include "code\controllers\repository\structs.dm" -#include "code\controllers\subsystem\__test_bad_subsystem_sleeps.dm" #include "code\controllers\subsystem\ai_holders.dm" #include "code\controllers\subsystem\ai_legacy.dm" #include "code\controllers\subsystem\ai_movement.dm" From 8464c4e1539a6cc6038668f5a56c41e877a54404 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:30:04 -0500 Subject: [PATCH 16/28] well i'm smart lmao --- code/controllers/subsystem/input.dm | 1 - 1 file changed, 1 deletion(-) diff --git a/code/controllers/subsystem/input.dm b/code/controllers/subsystem/input.dm index e13c15858a1b..b1c113aa2d62 100644 --- a/code/controllers/subsystem/input.dm +++ b/code/controllers/subsystem/input.dm @@ -3,7 +3,6 @@ SUBSYSTEM_DEF(input) wait = 0.25 // scale to 40 fps init_order = INIT_ORDER_INPUT init_stage = INIT_STAGE_EARLY - subsystem_flags = SS_NO_INIT priority = FIRE_PRIORITY_INPUT runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY From 06169f549ad912b6a20fa1f1ba65d6ad91a8b03e Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:31:57 -0500 Subject: [PATCH 17/28] more descriptive --- code/controllers/master.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 7c0cbd5a96c7..e146c474f201 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -354,9 +354,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // "[message_prefix] [seconds] seconds." var/message_prefix - // tell everyone? + // run to_chat(world) var/tell_everyone - // is this a warning? + // use a warning spans var/chat_warning switch(initialize_result) From 3c47a26979c40c27ba0a035175a4c9472fc32237 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 17:25:22 -0500 Subject: [PATCH 18/28] fix something --- code/controllers/master.dm | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index e146c474f201..f15652df456d 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -34,7 +34,14 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/list/subsystems //# Vars for keeping track of tick drift. + + /** + * The world.timeofday that the current Loop() started at. + */ var/loop_start_timeofday + /** + * The world.time that the current Loop() started at. + */ var/loop_start_time var/tickdrift = 0 @@ -75,8 +82,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new /// The current initialization stage we're at. var/static/init_stage_completed = 0 - /// An init stage change was queued. - var/init_stage_change_pending = FALSE /// The init stage currently being ran by the main ticker loop var/init_stage_ticking @@ -499,6 +504,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new init_stage_ticking = init_stage iteration = 1 + + var/init_stage_change_pending = FALSE var/error_level = 0 var/sleep_delta = 1 @@ -508,15 +515,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/starting_tick_usage = TICK_USAGE // check if we need to queue an init stage change - if (init_stage != init_stage_completed) - // set stage change pending; this'll stop new (but not paused / sleeping) subsystems from being queued to run, - // including ticker subsystems! + if(init_stage != init_stage_completed) + // set stage change pending; this'll stop new (but not paused / sleeping) subsystems from being queued to run. init_stage_change_pending = TRUE - // warning: here be dragons! - // no subsystem must be running for init stage change to happen - // checking for running and paused subsystems is easy; we just check that there's nothing in the queue to run. - // checking for sleeping subsystems is harder as they're not re-queued until they stop sleeping - // thus, we have to loop through everything. + // ensure that 1. queue is empty and 2. no sleeping subsystems (as those don't stay in queue) exist if(!queue_head && !laggy_sleeping_subsystem_check()) return MC_LOOP_RTN_NEWSTAGES @@ -744,24 +746,20 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.state = SS_RUNNING - // ignite / fire the head node // ignite() will return immediately even if fire() sleeps. - // the return value will be SS_SLEEPING if fire() is sleeping. - // the return value will be SS_RUNNING if it did not sleep and ran to completion. - // the return value will be SS_PAUSING if it did not sleep and yielded. queue_node_tick_usage = TICK_USAGE var/state = queue_node.ignite(queue_node.state == SS_PAUSED) queue_node_tick_usage = TICK_USAGE - queue_node_tick_usage switch(state) if(SS_RUNNING) - // successful, full run + // fire() ran to completion state = SS_IDLE if(SS_PAUSING) - // partial run + // fire() ran and then pause()'d something_is_mid_cycle = TRUE if(SS_SLEEPING) - // it slept. this is very bad. + // fire() slept; the subsystem may or may not pause later something_is_mid_cycle = TRUE else stack_trace("subsystem had unexpected state: [state]") From 280da6f2ef91f3061077d974ed191c7505dbdc66 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:03:58 -0500 Subject: [PATCH 19/28] timing changes --- code/__DEFINES/controllers/_subsystem.dm | 10 +- code/controllers/master.dm | 9 +- code/controllers/subsystem.dm | 118 +++++++++++------------ 3 files changed, 67 insertions(+), 70 deletions(-) diff --git a/code/__DEFINES/controllers/_subsystem.dm b/code/__DEFINES/controllers/_subsystem.dm index 6ba5a915c4b9..f9fce73f1a02 100644 --- a/code/__DEFINES/controllers/_subsystem.dm +++ b/code/__DEFINES/controllers/_subsystem.dm @@ -130,9 +130,9 @@ DEFINE_BITFIELD(subsystem_flags, list( */ #define SS_RUNNING 2 /** - * Paused by MC_TICK_CHECK + * We are requesting a pause. * - * * Should not be set by anything other than ignite(). + * * Set by the pause() proc if we did not sleep yet during our fire(). */ #define SS_PAUSED 3 /** @@ -140,9 +140,11 @@ DEFINE_BITFIELD(subsystem_flags, list( */ #define SS_SLEEPING 4 /** - * In the middle of pausing by MC_TICK_CHECK. + * We slept, and now we are requesting a pause. * - * * Set by the macro, and changed to `SS_PAUSED` by ignite() + * * Set by the pause() proc if we have slept since fire() was invoked. + * * Converted to SS_PAUSED by ignite() once we finally return from fire(), as we cannot immediately pause if + * we are sleeping. */ #define SS_PAUSING 5 diff --git a/code/controllers/master.dm b/code/controllers/master.dm index f15652df456d..b27971864c27 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -755,7 +755,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(SS_RUNNING) // fire() ran to completion state = SS_IDLE - if(SS_PAUSING) + if(SS_PAUSED) // fire() ran and then pause()'d something_is_mid_cycle = TRUE if(SS_SLEEPING) @@ -773,14 +773,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.tick_overrun = max(0, MC_AVG_FAST_UP_SLOW_DOWN(queue_node.tick_overrun, queue_node_tick_usage-tick_precentage)) queue_node.state = state - // if it paused mid-run, track that - if (state == SS_PAUSING) + // if it paused mid-run, track that ; do not eject it from the queue + if (state == SS_PAUSED) queue_node.paused_ticks++ queue_node.paused_tick_usage += queue_node_tick_usage queue_node = queue_node.queue_next continue - // it did not pause; this is a complete run + // it did not pause. either this is a complete run, or the subsystem is sleeping. + // in either case, we will track what we can and eject it; if it's sleeping, we can no longer manage the fire() call. queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks) queue_node_tick_usage += queue_node.paused_tick_usage diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 070b16809286..1376b6549988 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -34,9 +34,9 @@ * If a subsystem sleeps during a tick, it is very, very bad. * * * Sleeping orphans the subsystem's call stack from the MC's. The MC is no longer able to control the subsystem's tick usage. - * * Sleeping is handled, but not supported. This means the MC won't crash / do anything nasty, but normal timing will nonetheless - * be problematic. - * * Sleeping causes things like paused tick tracking to go out of wack. + * * Sleeping is handled, but not perfect. This means the MC won't crash / do anything nasty, but normal timing will nonetheless + * be affected. + * * Sleeping causes things like paused tick tracking to be inaccurate. */ /datum/controller/subsystem //# Metadata; you should define these. @@ -108,16 +108,20 @@ //# The following variables are managed by the MC and should not be modified directly. - /// Last world.time we did a full ignite()/fire() without pausing + /// Last time ignite() was called and fire() ran to completion. /// /// * this is set by the MC's processing loop - /// * this is a heuristic; subsystems that have weird pausing behaviors won't work right with this. - /// * this is why it's crucial subsystems call pause() if they didn't finish a run! + /// * sleeping will count as a fire(), despite potentially not finishing a cycle. var/last_fire = 0 /// Scheduled world.time for next ignite(). /// /// * this is set by the MC's processing loop var/next_fire = 0 + /// Tracks the number of times fire() was ran to completion after an ignite(). + /// + /// * this is set by the MC's processing loop + /// * sleeping will count as a time fired, despite potentially not finishing a cycle. + var/times_fired = 0 /// Running average of the amount of milliseconds it takes the subsystem to complete a run (including all resumes but not the time spent paused). var/cost = 0 @@ -140,9 +144,6 @@ /// Tracks how many fires the subsystem takes to complete a run on average. var/ticks = 1 - /// Tracks the amount of completed runs for the subsystem. - var/times_fired = 0 - /// Time the subsystem entered the queue, (for timing and priority reasons). /// /// * This doesn't take into account pauses and sleeps. queued_time is the time it was initially put into queue @@ -183,21 +184,19 @@ /// /// * this is pretty much time dilation for this subsystem /// * this is based on wait time; e.g. 100% means we're running twice as slow, etc - var/tick_dilation_avg = 0 - /// How much of a tick (in percents of a tick) were we allocated last fire. - var/tick_allocation_last = 0 - /// How much of a tick (in percents of a tick) do we get allocated by the mc on avg. - var/tick_allocation_avg = 0 + var/tracked_average_dilation = 0 + /// Last world.time we did a full ignite()/fire() without pausing + /// + /// * this is set when fire() finishes, whether normally or by sleeping, without pausing. + /// * this is set by ignite() + var/tracked_last_completion = 0 + /** * # Do not blindly add vars here to the bottom, put it where it goes above. * # If your var only has two values, put it in as a flag. */ -// Do not override -// /datum/controller/subsystem/New() -// return - /** * Called before global vars are initialized * Called before Recover() @@ -225,47 +224,57 @@ return /** - * This is used so the mc knows when the subsystem sleeps. - * DO NOT OVERRIDE THIS. + * Used to initialize the subsystem AFTER the map has loaded. + * This is expected to be overriden by subtypes. + */ +/datum/controller/subsystem/Initialize() + return SS_INIT_NONE + +/** + * Usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash). + * Should attempt to salvage what it can from the old instance of subsystem. + */ +/datum/controller/subsystem/Recover() + return TRUE + +/** + * Handles logic used to track fire() and sleeps. * * * If fire() sleeps, the return value will be SS_SLEEPING. - * * If fire() does not sleep, the return value will be SS_PAUSING or SS_RUNNING. + * * If fire() does not sleep, the return value will be SS_PAUSED or SS_RUNNING. * * @return the state we're now in. This return value is only used if fire() does not sleep. */ /datum/controller/subsystem/proc/ignite(resumed = FALSE) SHOULD_NOT_OVERRIDE(TRUE) - // This makes us return the last return value when we sleep + // This makes us return the last return value when we (or anything we call; e.g. fire()) sleeps. set waitfor = FALSE // Paranoid set. . = SS_IDLE - - // Record tick allocation - tick_allocation_last = Master.current_ticklimit-(TICK_USAGE) - tick_allocation_avg = MC_AVERAGE(tick_allocation_avg, tick_allocation_last) - - // Fire; the default return value will return the fact we're sleeping if fire() sleeps. + // Set to SLEEPING so the MC knows if anything below this sleeps. . = SS_SLEEPING + // Fire. This can potentially sleep. If it does, the rest of the proc will be disregarded by the MC. fire(resumed) - // Set the default return value to either RUNNING or PAUSING. + // If fire() does not sleep, this will set our return value to RUNNING or PAUSED, depending on if we hit pause(). + // If fire() does sleep, 'state' will have already been overwritten by the MC to be SLEEPING, + // and if pause() is hit after the sleep, it will be changed to PAUSING. . = state - // If the MC detected we are sleeping, set back to idle now that we're done sleeping. - if (state == SS_SLEEPING) - state = SS_IDLE - if (state == SS_PAUSING) - state = SS_PAUSED - //! HACK: if we are not queued, queue us - // this is to handle pausing during sleep. - // normally, the MC does not eject us if we are pausing. - // a sleep, however, is not considered a pause. - if(!queued_time) + + switch(state) + if(SS_PAUSING) + // sleeping & did pause; MC already moved on, and we've been ejected from queue. Re-insert into queue. + var/was_queued_at = queued_time enqueue() - else - // track time between runs - var/full_run_took = world.time - last_fire - var/new_tick_dilation = (full_run_took / nominal_dt_ds) * 100 - 100 - tick_dilation_avg = max(0, MC_AVERAGE_SLOW(tick_dilation_avg, new_tick_dilation)) - last_fire = world.time + state = SS_PAUSED + queued_time = was_queued_at + if(SS_RUNNING, SS_SLEEPING) + // full run finished ; track tick dilation average, last fire, and prepare to re-insert into queue. + var/full_run_took = world.time - tracked_last_completion + var/new_tick_dilation = (full_run_took / nominal_dt_ds) * 100 - 100 + tracked_average_dilation = max(0, MC_AVERAGE_SLOW(tracked_average_dilation, new_tick_dilation)) + state = SS_IDLE + else + CRASH("unexpected state in [src] ([type]): [state]") /** * previously, this would have been named 'process()' but that name is used everywhere for different things! @@ -278,7 +287,7 @@ /datum/controller/subsystem/Destroy() dequeue() - can_fire = 0 + can_fire = FALSE subsystem_flags |= SS_NO_FIRE if (Master) Master.subsystems -= src @@ -410,19 +419,12 @@ /datum/controller/subsystem/proc/OnConfigLoad() return -/** - * Used to initialize the subsystem AFTER the map has loaded. - * This is expected to be overriden by subtypes. - */ -/datum/controller/subsystem/Initialize() - return SS_INIT_NONE - /** * Hook for printing stats to the "MC" statuspanel for admins to see performance and related stats etc. */ /datum/controller/subsystem/stat_entry() if(can_fire && !(SS_NO_FIRE & subsystem_flags)) - . = "[round(cost,1)]ms | D:[round(tick_dilation_avg,1)]% | U:[round(tick_usage,1)]% | O:[round(tick_overrun,1)]% | T:[round(ticks,0.1)] " + . = "[round(cost,1)]ms | D:[round(tracked_average_dilation,1)]% | U:[round(tick_usage,1)]% | O:[round(tick_overrun,1)]% | T:[round(ticks,0.1)] " else . = "OFFLINE " @@ -470,14 +472,6 @@ if(next_fire - world.time < wait) next_fire += (wait*cycles) -/** - * Usually called via datum/controller/subsystem/New() when replacing a subsystem (i.e. due to a recurring crash). - * Should attempt to salvage what it can from the old instance of subsystem. - */ -/datum/controller/subsystem/Recover() - return TRUE - - /datum/controller/subsystem/vv_edit_var(var_name, var_value) switch (var_name) if (NAMEOF(src, can_fire)) From 3e85ba735574512cfa03aa0b9923944e2bbc036c Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:40:03 -0500 Subject: [PATCH 20/28] attitude correction --- code/controllers/master.dm | 6 +++--- code/controllers/subsystem.dm | 24 +++++++++++---------- code/controllers/subsystem/statpanel.dm | 13 ----------- code/modules/keybindings/bindings_client.dm | 4 ++-- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index b27971864c27..3e3e92535695 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -748,7 +748,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // ignite() will return immediately even if fire() sleeps. queue_node_tick_usage = TICK_USAGE - var/state = queue_node.ignite(queue_node.state == SS_PAUSED) + var/state = queue_node.ignite(queue_node_paused) queue_node_tick_usage = TICK_USAGE - queue_node_tick_usage switch(state) @@ -800,10 +800,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new queue_node.last_fire = world.time queue_node.times_fired++ + // update the next time it should be available to queue + queue_node.update_next_fire() // remove from queue queue_node.dequeue() - // update the next time it should be available to queue - queue_node.update_nextfire() // move to next queue_node = queue_node.queue_next diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 1376b6549988..c376eb933323 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -273,6 +273,8 @@ var/new_tick_dilation = (full_run_took / nominal_dt_ds) * 100 - 100 tracked_average_dilation = max(0, MC_AVERAGE_SLOW(tracked_average_dilation, new_tick_dilation)) state = SS_IDLE + if(SS_PAUSED) + // we paused; nothing special, move on. the MC will handle it. else CRASH("unexpected state in [src] ([type]): [state]") @@ -299,7 +301,7 @@ * @params * * reset_time - Entirely reset the subsystem's stateful time tracking including tick-overrun, post fire timing, etc. */ -/datum/controller/subsystem/proc/update_nextfire(reset_time) +/datum/controller/subsystem/proc/update_next_fire(reset_time) if(reset_time) next_fire = (subsystem_flags & SS_TICKER) ? (world.time + (world.tick_lag * wait)) : (world.time + wait) return @@ -429,7 +431,7 @@ . = "OFFLINE " /datum/controller/subsystem/stat_key() - return can_fire? "\[[state_letter()]\] [name]" : name + return "\[[state_letter()]\] [name]" /** * Returns our status symbol. @@ -445,24 +447,24 @@ if(Master.init_stage_completed >= init_stage) switch (state) if (SS_RUNNING) - . = "R" + . = "R" if (SS_QUEUED) - . = "Q" + . = "Q" if (SS_PAUSED, SS_PAUSING) - . = "P" + . = "P" if (SS_SLEEPING) - . = "S" + . = "S" if (SS_IDLE) - . = "  " + . = "   " else if(subsystem_flags & SS_NO_INIT) - . = "D" + . = "D" if(src == Master.current_initializing_subsystem) - . = "I" + . = "I" else if(initialized) - . = "D" + . = "D" else - . = "W" + . = "W" /** * Could be used to postpone a costly subsystem for (default one) var/cycles, cycles. diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index b4f981b4a5fb..a7fbbe9abaa5 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -141,16 +141,3 @@ SUBSYSTEM_DEF(statpanels) . += Q.generate_stat() . = url_encode(json_encode(.)) cache_sdql_data = . - -/** - * is this shitcode? - * yes it is - * if you wanna do better, do better; i'm not at the point of janking up our MC with my own - * fuckery. - * - * tl;dr this ensures we push data while MC is initializing. - */ -/datum/controller/subsystem/statpanels/proc/manual_ticking() - while(!Master.initialized) - fire(null, TRUE) - sleep(10) diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index 71775b65f6a2..517cbb2fcbdd 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -75,7 +75,7 @@ else full_key = "[AltMod][CtrlMod][ShiftMod][_key]" var/keycount = 0 - for(var/kb_name in preferences.keybindings[full_key]) + for(var/kb_name in preferences?.keybindings[full_key]) keycount++ var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] if(kb.can_use(src) && kb.down(src) && keycount >= MAX_COMMANDS_PER_KEY) @@ -137,7 +137,7 @@ // We don't do full key for release, because for mod keys you // can hold different keys and releasing any should be handled by the key binding specifically - for (var/kb_name in preferences.keybindings[_key]) + for (var/kb_name in preferences?.keybindings[_key]) var/datum/keybinding/kb = GLOB.keybindings_by_name[kb_name] if(kb.can_use(src) && kb.up(src)) break From 984bd54b7f5fbfa51d8fb22b89974e2abd8fd10b Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 18:57:12 -0500 Subject: [PATCH 21/28] check tick the lateinitialize --- code/controllers/subsystem/atoms.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 0d1ba5316e85..33e4e1156c9b 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -58,6 +58,7 @@ SUBSYSTEM_DEF(atoms) if(QDELETED(A)) continue A.LateInitialize() + CHECK_TICK testing("Late initialized [late_loaders.len] atoms") late_loaders.Cut() From 714c05a1b701de8e007222173739e03ce0078207 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:15:11 -0500 Subject: [PATCH 22/28] Fix --- code/___compile_options.dm | 9 +-------- code/controllers/master.dm | 4 +--- code/controllers/subsystem/air.dm | 4 +--- code/controllers/subsystem/chat.dm | 2 +- code/controllers/subsystem/emergency_shuttle.dm | 1 + 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/code/___compile_options.dm b/code/___compile_options.dm index ed656eb5a738..4ea838ae6163 100644 --- a/code/___compile_options.dm +++ b/code/___compile_options.dm @@ -79,17 +79,10 @@ */ // #define USE_BYOND_TRACY -/** - * If this is uncommented, Autowiki will generate edits and shut down the server. - * Prefer the autowiki build target instead. - */ -// #define AUTOWIKI - - /** * If this is uncommented, will profile mapload atom initializations. */ -// #define PROFILE_MAPLOAD_INIT_ATOM +#define PROFILE_MAPLOAD_INIT_ATOM /** diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 3e3e92535695..1614d6fdc7f1 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -349,9 +349,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // todo: dylib high-precision timers var/rtod_start = REALTIMEOFDAY - var/initialize_result = subsystem.Initialize() - var/rtod_end = REALTIMEOFDAY var/took_seconds = round((rtod_end - rtod_start) / 10, 0.01) @@ -359,7 +357,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new // "[message_prefix] [seconds] seconds." var/message_prefix - // run to_chat(world) + // should to_chat the message to world rather than just log var/tell_everyone // use a warning spans var/chat_warning diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 83c7386ad3e7..4d5637252898 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -89,7 +89,7 @@ SUBSYSTEM_DEF(air) continue generated_atmospheres[id] = SSair.generated_atmospheres[id] -/datum/controller/subsystem/air/Initialize(timeofday) +/datum/controller/subsystem/air/Initialize() #ifndef FASTBOOT_DISABLE_ZONES report_progress("Initializing [name] subsystem...") @@ -114,9 +114,7 @@ SUBSYSTEM_DEF(air) startup_active_edge_log = edge_log.Copy() //! Fancy blockquote of data. - var/time = (REALTIMEOFDAY - timeofday) / 10 var/list/blockquote_data = list( - SPAN_BOLDANNOUNCE("Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!
"), SPAN_DEBUGINFO("Total Zones: [zones.len]"), SPAN_DEBUGINFO("\nTotal Edges: [edges.len]"), SPAN_DEBUGINFO("\nTotal Active Edges: [active_edges.len ? SPAN_DANGER("[active_edges.len]") : "None"]"), diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index 05e72cd7d0b6..67b7f81a6ca8 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -5,7 +5,7 @@ SUBSYSTEM_DEF(chat) name = "Chat" - subsystem_flags = NONE + subsystem_flags = SS_NO_INIT wait = 0.25 // scale up to 40 fps runlevels = RUNLEVELS_ALL priority = FIRE_PRIORITY_CHAT diff --git a/code/controllers/subsystem/emergency_shuttle.dm b/code/controllers/subsystem/emergency_shuttle.dm index b1c07d9cc338..767db15201d7 100644 --- a/code/controllers/subsystem/emergency_shuttle.dm +++ b/code/controllers/subsystem/emergency_shuttle.dm @@ -1,6 +1,7 @@ SUBSYSTEM_DEF(emergencyshuttle) name = "Emergency Shuttle" wait = 20 + subsystem_flags = SS_NO_INIT /datum/controller/subsystem/emergencyshuttle var/datum/shuttle/autodock/ferry/emergency/shuttle // Set in shuttle_emergency.dm TODO - is it really? From ce02a07b0b63012828f1155ab54ec5fe1e72a810 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:17:15 -0500 Subject: [PATCH 23/28] sleep enforcement --- code/controllers/subsystem/ai_scheduling.dm | 5 +++++ code/modules/ai/holders/ai_holder-movement.dm | 3 +++ code/modules/ai/holders/ai_holder-scheduling.dm | 1 + code/modules/ai/holders/ai_holder-ticking.dm | 3 +++ 4 files changed, 12 insertions(+) diff --git a/code/controllers/subsystem/ai_scheduling.dm b/code/controllers/subsystem/ai_scheduling.dm index b9cefa12fb34..13d7042b2d9b 100644 --- a/code/controllers/subsystem/ai_scheduling.dm +++ b/code/controllers/subsystem/ai_scheduling.dm @@ -117,6 +117,10 @@ SUBSYSTEM_DEF(ai_scheduling) * List of things allowed to use this: * * /datum/ai_holder * * /datum/ai_network + * + * Quirks: + * * This will never sleep on invocation. If the called proc sleeps, we blow right past. + * * Try to not have the called proc be ridiculously expensive as we are on a very fast-firing subsystem. */ /datum/ai_callback var/proc_ref @@ -144,4 +148,5 @@ SUBSYSTEM_DEF(ai_scheduling) /datum/ai_callback/proc/invoke() SHOULD_NOT_SLEEP(TRUE) + set waitfor = FALSE call(parent, proc_ref)(arglist(arguments)) diff --git a/code/modules/ai/holders/ai_holder-movement.dm b/code/modules/ai/holders/ai_holder-movement.dm index 81087b4ae0a4..c0a2f1d62a2b 100644 --- a/code/modules/ai/holders/ai_holder-movement.dm +++ b/code/modules/ai/holders/ai_holder-movement.dm @@ -27,6 +27,7 @@ * @return amount of time to next move; 0 to stop moving */ /datum/ai_holder/proc/move(cycles) + SHOULD_NOT_SLEEP(TRUE) . = 0 CRASH("unimplemented move proc called; what happened here?") @@ -34,10 +35,12 @@ * register on movement subsystem to move */ /datum/ai_holder/proc/start_moving(initial_delay) + SHOULD_NOT_SLEEP(TRUE) return SSai_movement.register_moving(src) /** * stop moving */ /datum/ai_holder/proc/stop_moving() + SHOULD_NOT_SLEEP(TRUE) return SSai_movement.unregister_moving(src) diff --git a/code/modules/ai/holders/ai_holder-scheduling.dm b/code/modules/ai/holders/ai_holder-scheduling.dm index 80d29a46a6ce..59454eee8754 100644 --- a/code/modules/ai/holders/ai_holder-scheduling.dm +++ b/code/modules/ai/holders/ai_holder-scheduling.dm @@ -19,5 +19,6 @@ * * arguments - a list of arguments to use with the call */ /datum/ai_holder/proc/schedule(time, proc_ref, list/arguments) + SHOULD_NOT_SLEEP(TRUE) var/datum/ai_callback/ai_callback = new(proc_ref, arguments, src) SSai_scheduling.schedule_callback(ai_callback, time) diff --git a/code/modules/ai/holders/ai_holder-ticking.dm b/code/modules/ai/holders/ai_holder-ticking.dm index 872bf0e306d7..08db4bc01b37 100644 --- a/code/modules/ai/holders/ai_holder-ticking.dm +++ b/code/modules/ai/holders/ai_holder-ticking.dm @@ -23,6 +23,7 @@ * correct bucket for our target cycle. */ /datum/ai_holder/proc/set_ticking(delay) + SHOULD_NOT_SLEEP(TRUE) ASSERT(delay > 0) ASSERT(delay <= AI_SCHEDULING_LIMIT) if(ticking > 0) @@ -39,6 +40,7 @@ * Unregister us from the doubly linked list we're in and removes us from the ai_holders subsystem. */ /datum/ai_holder/proc/stop_ticking() + SHOULD_NOT_SLEEP(TRUE) if(!ticking) return SSai_holders.bucket_evict(src) @@ -50,6 +52,7 @@ * Called by subsystem to tick this holder. */ /datum/ai_holder/proc/tick(cycles) + SHOULD_NOT_SLEEP(TRUE) return /** From e87c0c0a7a14716a200b7606e439d2f6e0052836 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:39:55 -0500 Subject: [PATCH 24/28] pov i'm stupid --- code/___compile_options.dm | 3 +-- code/controllers/subsystem/ai_movement.dm | 4 ++-- code/modules/ai/holders/polaris/ai_holder_targeting.dm | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/___compile_options.dm b/code/___compile_options.dm index 4ea838ae6163..456b5cabb66a 100644 --- a/code/___compile_options.dm +++ b/code/___compile_options.dm @@ -82,8 +82,7 @@ /** * If this is uncommented, will profile mapload atom initializations. */ -#define PROFILE_MAPLOAD_INIT_ATOM - +// #define PROFILE_MAPLOAD_INIT_ATOM /** * If this is uncommented, force our verb processing into just the 2% of a tick. diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index af95444d4620..1f1f4097b47f 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -92,7 +92,7 @@ SUBSYSTEM_DEF(ai_movement) else // this was not the only holder in the bucket, stitch it back together after the ejection. buckets[bucket_offset] = being_processed.movement_bucket_next - being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev + being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next // insert; we now set its ticking_(next|previous) // note that we don't do catchup @@ -159,7 +159,7 @@ SUBSYSTEM_DEF(ai_movement) buckets.len = BUCKET_AMOUNT for(var/i in 1 to length(moving_ais)) var/datum/ai_holder/holder = buckets[i] - holder.movement_bucket_next = holder.movement_bucket_prev = null + holder.movement_bucket_next = holder.movement_bucket_prev = holder holder.movement_bucket_position = i /** diff --git a/code/modules/ai/holders/polaris/ai_holder_targeting.dm b/code/modules/ai/holders/polaris/ai_holder_targeting.dm index 9f2f90656264..b6028156a45e 100644 --- a/code/modules/ai/holders/polaris/ai_holder_targeting.dm +++ b/code/modules/ai/holders/polaris/ai_holder_targeting.dm @@ -235,7 +235,7 @@ if(!hostile && !retaliate) // Not allowed to defend ourselves. ai_log("react_to_attack_polaris() : Was attacked by [attacker], but we are not allowed to attack back.", AI_LOG_TRACE) return FALSE - if(holder.IIsAlly(attacker)) // I'll overlook it THIS time... + if(ismob(attacker) && holder.IIsAlly(attacker)) // I'll overlook it THIS time... ai_log("react_to_attack_polaris() : Was attacked by [attacker], but they were an ally.", AI_LOG_TRACE) return FALSE if(target) // Already fighting someone. Switching every time we get hit would impact our combat performance. From 32be9db13bac4adfc3dfc99da41b0b0d6403a439 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:41:00 -0500 Subject: [PATCH 25/28] pov i'm stupid --- code/controllers/subsystem/ai_movement.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/controllers/subsystem/ai_movement.dm b/code/controllers/subsystem/ai_movement.dm index 1f1f4097b47f..0c228e285c4e 100644 --- a/code/controllers/subsystem/ai_movement.dm +++ b/code/controllers/subsystem/ai_movement.dm @@ -92,7 +92,7 @@ SUBSYSTEM_DEF(ai_movement) else // this was not the only holder in the bucket, stitch it back together after the ejection. buckets[bucket_offset] = being_processed.movement_bucket_next - being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev + being_processed.movement_bucket_next.movement_bucket_prev = being_processed.movement_bucket_prev being_processed.movement_bucket_prev.movement_bucket_next = being_processed.movement_bucket_next // insert; we now set its ticking_(next|previous) // note that we don't do catchup @@ -139,7 +139,7 @@ SUBSYSTEM_DEF(ai_movement) moving_ais -= holder stack_trace("bad holder found") continue - // doubly linked list inject + // circular double-linked list inject if(!isnull(buckets[position])) var/datum/ai_holder/existing = buckets[position] holder.movement_bucket_next = existing.movement_bucket_next From 063160ce947ffc9da4d3392374260b638bc58ca4 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 19:55:17 -0500 Subject: [PATCH 26/28] fix subsystem tracking --- code/controllers/master.dm | 3 ++- code/controllers/subsystem.dm | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 1614d6fdc7f1..8fc5bfd176d1 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -509,7 +509,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new //# The actual loop. while (1) - tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - loop_start_timeofday) - (world.time - loop_start_time)) / world.tick_lag))) + var/new_tickdrift = (((REALTIMEOFDAY - loop_start_timeofday) - (world.time - loop_start_time)) / world.tick_lag) + tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, new_tickdrift)) var/starting_tick_usage = TICK_USAGE // check if we need to queue an init stage change diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index c376eb933323..bf1333d28b4e 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -184,6 +184,7 @@ /// /// * this is pretty much time dilation for this subsystem /// * this is based on wait time; e.g. 100% means we're running twice as slow, etc + /// * this is also reset by update_next_fire() if 'reset timing' arg is specified var/tracked_average_dilation = 0 /// Last world.time we did a full ignite()/fire() without pausing /// @@ -272,6 +273,7 @@ var/full_run_took = world.time - tracked_last_completion var/new_tick_dilation = (full_run_took / nominal_dt_ds) * 100 - 100 tracked_average_dilation = max(0, MC_AVERAGE_SLOW(tracked_average_dilation, new_tick_dilation)) + tracked_last_completion = world.time state = SS_IDLE if(SS_PAUSED) // we paused; nothing special, move on. the MC will handle it. @@ -304,6 +306,7 @@ /datum/controller/subsystem/proc/update_next_fire(reset_time) if(reset_time) next_fire = (subsystem_flags & SS_TICKER) ? (world.time + (world.tick_lag * wait)) : (world.time + wait) + tracked_last_completion = world.time return var/queue_node_flags = subsystem_flags From eac93658c7c4719b0cec6b4e7809cf9b7dc140d7 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:31:54 -0500 Subject: [PATCH 27/28] return the keep timing check --- code/controllers/master.dm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 8fc5bfd176d1..7d0c002dad77 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -660,9 +660,13 @@ GLOBAL_REAL(Master, /datum/controller/master) = new var/SS_flags = SS.subsystem_flags + // if it's flagged as NO_FIRE for some reason (probably because something faulted and shut it off), completely boot it if (SS_flags & SS_NO_FIRE) subsystemstocheck -= SS continue + // keep timing: do not run faster than 1.33x of base speed, to prevent catch-up from going too fast + if ((SS_flags & (SS_TICKER|SS_KEEP_TIMING)) == SS_KEEP_TIMING && SS.last_fire + (SS.wait * 0.75) > world.time) + continue SS.enqueue() From 0a430962d78f7b575e304f497c4640081ed7fc72 Mon Sep 17 00:00:00 2001 From: silicons <2003111+silicons@users.noreply.github.com> Date: Wed, 13 Nov 2024 21:50:46 -0500 Subject: [PATCH 28/28] do that in machines --- code/controllers/subsystem/machines.dm | 5 +++++ code/modules/power/apc.dm | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/code/controllers/subsystem/machines.dm b/code/controllers/subsystem/machines.dm index a04b9b4d696f..d377218545d0 100644 --- a/code/controllers/subsystem/machines.dm +++ b/code/controllers/subsystem/machines.dm @@ -44,6 +44,7 @@ SUBSYSTEM_DEF(machines) makepowernets() report_progress("Initializing atmos machinery...") setup_atmos_machinery(GLOB.machines) + update_all_apcs() fire() return SS_INIT_SUCCESS @@ -89,6 +90,10 @@ SUBSYSTEM_DEF(machines) T.broadcast_status() CHECK_TICK +/datum/controller/subsystem/machines/proc/update_all_apcs() + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + apc.update() + /datum/controller/subsystem/machines/proc/process_pipenets(resumed = 0) if (!resumed) src.current_run = global.pipe_networks.Copy() diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index e42290cc3c60..69230f569620 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -321,8 +321,6 @@ CREATE_WALL_MOUNTING_TYPES_SHIFTED(/obj/machinery/power/apc, 22) make_terminal() - addtimer(CALLBACK(src, PROC_REF(update)), 5) - /obj/machinery/power/apc/examine(mob/user, dist) . = ..() if(Adjacent(user))