From f1928357e3f6793fe71211ce10b25ef10a85bc86 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Mon, 19 Feb 2024 01:23:33 +0100 Subject: [PATCH] [MIRROR] Fixes smoothing breaking if a map is loaded post init (#26553) * Fixes smoothing breaking if a map is loaded post init (#81526) ## About The Pull Request We'd finish a set of atom creation, then try and smooth those atoms The problem is they might try and smooth with an uninitialized neighbor, which wouldn't have its smoothing vars parsed. This fixes that by pooling "to be smoothed" things into a list based off the source of the init stoppage, which we then release when we're done. Also fixes things staying in mapload, even during a sleep. This can cause massive headaches so it's good to avoid. This has a cost but it's minuscule (on the order of like 0.006s (6ms over all of init), so I'm happy with it. ## Why It's Good For The Game Closes #77040 ## Changelog :cl: fix: Maps loaded in after roundstart will no longer have broken smoothing /:cl: * Fixes smoothing breaking if a map is loaded post init --------- Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> --- code/controllers/subsystem/atoms.dm | 36 +++++++++++++++------ code/controllers/subsystem/icon_smooth.dm | 15 +++++++++ code/controllers/subsystem/timer.dm | 3 -- code/modules/client/preferences_savefile.dm | 2 +- 4 files changed, 43 insertions(+), 13 deletions(-) diff --git a/code/controllers/subsystem/atoms.dm b/code/controllers/subsystem/atoms.dm index 916bbed6e4e..71eaed14ea2 100644 --- a/code/controllers/subsystem/atoms.dm +++ b/code/controllers/subsystem/atoms.dm @@ -1,4 +1,3 @@ -#define SUBSYSTEM_INIT_SOURCE "subsystem init" SUBSYSTEM_DEF(atoms) name = "Atoms" init_order = INIT_ORDER_ATOMS @@ -41,11 +40,16 @@ SUBSYSTEM_DEF(atoms) if(initialized == INITIALIZATION_INSSATOMS) return - set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, SUBSYSTEM_INIT_SOURCE) + // 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) // This may look a bit odd, but if the actual atom creation runtimes for some reason, we absolutely need to set initialized BACK - CreateAtoms(atoms, atoms_to_return) - clear_tracked_initalize(SUBSYSTEM_INIT_SOURCE) + CreateAtoms(atoms, atoms_to_return, source) + clear_tracked_initalize(source) + SSicon_smooth.free_deferred(source) if(late_loaders.len) for(var/I in 1 to late_loaders.len) @@ -72,7 +76,7 @@ SUBSYSTEM_DEF(atoms) #endif /// Actually creates the list of atoms. Exists soley so a runtime in the creation logic doesn't cause initalized to totally break -/datum/controller/subsystem/atoms/proc/CreateAtoms(list/atoms, list/atoms_to_return = null) +/datum/controller/subsystem/atoms/proc/CreateAtoms(list/atoms, list/atoms_to_return = null, mapload_source = null) if (atoms_to_return) LAZYINITLIST(created_atoms) @@ -90,7 +94,12 @@ SUBSYSTEM_DEF(atoms) for(var/I in 1 to atoms.len) var/atom/A = atoms[I] if(!(A.flags_1 & INITIALIZED_1)) - CHECK_TICK + // Unrolled CHECK_TICK setup to let us enable/disable mapload based off source + if(TICK_CHECK) + clear_tracked_initalize(mapload_source) + stoplag() + if(mapload_source) + set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, mapload_source) PROFILE_INIT_ATOM_BEGIN() InitAtom(A, TRUE, mapload_arg) PROFILE_INIT_ATOM_END(A) @@ -107,7 +116,11 @@ SUBSYSTEM_DEF(atoms) #ifdef TESTING ++count #endif - CHECK_TICK + if(TICK_CHECK) + clear_tracked_initalize(mapload_source) + stoplag() + if(mapload_source) + set_tracked_initalized(INITIALIZATION_INNEW_MAPLOAD, mapload_source) testing("Initialized [count] atoms") @@ -117,6 +130,13 @@ SUBSYSTEM_DEF(atoms) /datum/controller/subsystem/atoms/proc/map_loader_stop(source) clear_tracked_initalize(source) +/// Returns the source currently modifying SSatom's init behavior +/datum/controller/subsystem/atoms/proc/get_initialized_source() + var/state_length = length(initialized_state) + if(!state_length) + return null + return initialized_state[state_length][1] + /// Use this to set initialized to prevent error states where the old initialized is overriden, and we end up losing all context /// Accepts a state and a source, the most recent state is used, sources exist to prevent overriding old values accidentially /datum/controller/subsystem/atoms/proc/set_tracked_initalized(state, source) @@ -199,5 +219,3 @@ SUBSYSTEM_DEF(atoms) var/initlog = InitLog() if(initlog) text2file(initlog, "[GLOB.log_directory]/initialize.log") - -#undef SUBSYSTEM_INIT_SOURCE diff --git a/code/controllers/subsystem/icon_smooth.dm b/code/controllers/subsystem/icon_smooth.dm index 4d5f3069eca..9e5e9ffa99d 100644 --- a/code/controllers/subsystem/icon_smooth.dm +++ b/code/controllers/subsystem/icon_smooth.dm @@ -9,6 +9,7 @@ SUBSYSTEM_DEF(icon_smooth) var/list/blueprint_queue = list() var/list/smooth_queue = list() var/list/deferred = list() + var/list/deferred_by_source = list() /datum/controller/subsystem/icon_smooth/fire() // We do not want to smooth icons of atoms whose neighbors are not initialized yet, @@ -61,16 +62,30 @@ SUBSYSTEM_DEF(icon_smooth) return SS_INIT_SUCCESS +/// Releases a pool of delayed smooth attempts from a particular source +/datum/controller/subsystem/icon_smooth/proc/free_deferred(source_to_free) + smooth_queue += deferred_by_source[source_to_free] + deferred_by_source -= source_to_free + if(!can_fire) + can_fire = TRUE /datum/controller/subsystem/icon_smooth/proc/add_to_queue(atom/thing) if(thing.smoothing_flags & SMOOTH_QUEUED) return thing.smoothing_flags |= SMOOTH_QUEUED + // If we're currently locked into mapload BY something + // Then put us in a deferred list that we release when this mapload run is finished + if(initialized && length(SSatoms.initialized_state) && SSatoms.initialized == INITIALIZATION_INNEW_MAPLOAD) + var/source = SSatoms.get_initialized_source() + LAZYADD(deferred_by_source[source], thing) + return smooth_queue += thing if(!can_fire) can_fire = TRUE /datum/controller/subsystem/icon_smooth/proc/remove_from_queues(atom/thing) + // Lack of removal from deferred_by_source is safe because the lack of SMOOTH_QUEUED will just free it anyway + // Hopefully this'll never cause a harddel (dies) thing.smoothing_flags &= ~SMOOTH_QUEUED smooth_queue -= thing if(blueprint_queue) diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 80ee843bf8b..bc894de9bee 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -4,8 +4,6 @@ #define BUCKET_POS(timer) (((ROUND_UP((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN) || BUCKET_LEN) /// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue #define TIMER_MAX(timer_ss) (timer_ss.head_offset + TICKS2DS(BUCKET_LEN + timer_ss.practical_offset - 1)) -/// Max float with integer precision -#define TIMER_ID_MAX (2**24) /** * # Timer Subsystem @@ -731,4 +729,3 @@ SUBSYSTEM_DEF(timer) #undef BUCKET_LEN #undef BUCKET_POS #undef TIMER_MAX -#undef TIMER_ID_MAX diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index eb324419cb2..0fa517cd248 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -214,7 +214,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Sanitize lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) - toggles = sanitize_integer(toggles, 0, (2**24)-1, initial(toggles)) + toggles = sanitize_integer(toggles, 0, SHORT_REAL_LIMIT-1, initial(toggles)) be_special = sanitize_be_special(SANITIZE_LIST(be_special)) key_bindings = sanitize_keybindings(key_bindings) favorite_outfits = SANITIZE_LIST(favorite_outfits)