From 8bb0ecfcd3d2ac8f54ae268424e243b32eed01cb Mon Sep 17 00:00:00 2001 From: Fluffy <65877598+FluffyGhoster@users.noreply.github.com> Date: Thu, 12 Oct 2023 23:23:28 +0200 Subject: [PATCH] Icon smoothing and icon update refactor (#17569) * Atomization * asdfsadf * sdfas --------- Co-authored-by: FluffyGhost --- code/_helpers/icon_smoothing.dm | 12 ++ code/controllers/subsystems/icon_smooth.dm | 22 +++- code/controllers/subsystems/icon_updates.dm | 119 +++++++++++++----- code/game/atoms_init.dm | 18 ++- code/game/objects/items/weapons/syndie.dm | 22 +++- code/game/objects/structures.dm | 28 +++-- ...yGhost-icon_smoothing_updates_refactor.yml | 50 ++++++++ 7 files changed, 223 insertions(+), 48 deletions(-) create mode 100644 html/changelogs/FluffyGhost-icon_smoothing_updates_refactor.yml diff --git a/code/_helpers/icon_smoothing.dm b/code/_helpers/icon_smoothing.dm index 6dbfa27421f..763a693d693 100644 --- a/code/_helpers/icon_smoothing.dm +++ b/code/_helpers/icon_smoothing.dm @@ -536,14 +536,26 @@ for(var/V in Z_ALL_TURFS(zlevel)) var/turf/T = V + + //There's no use in smoothing turfs that have been deleted + if(QDELETED(T)) + continue + if(T.smoothing_flags) if(now) smooth_icon(T) else SSicon_smooth.add_to_queue(T) + for(var/R in T) var/atom/A = R + + //There's no use in smoothing deleted things + if(QDELETED(A)) + continue + if(A.smoothing_flags) + if(now) smooth_icon(A) else diff --git a/code/controllers/subsystems/icon_smooth.dm b/code/controllers/subsystems/icon_smooth.dm index 290411c72fb..65ae77cd671 100644 --- a/code/controllers/subsystems/icon_smooth.dm +++ b/code/controllers/subsystems/icon_smooth.dm @@ -24,12 +24,17 @@ SUBSYSTEM_DEF(icon_smooth) runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY var/list/smooth_queue = list() + + ///Like `smooth_queue`, but contains atoms that didn't initialize yet, to be reran + var/list/deferred = list() + var/list/typecachecache = list() var/explosion_in_progress = FALSE /datum/controller/subsystem/icon_smooth/Recover() smooth_queue = SSicon_smooth.smooth_queue + deferred = SSicon_smooth.deferred /datum/controller/subsystem/icon_smooth/stat_entry(msg) msg = "Q:[smooth_queue.len]" @@ -50,13 +55,20 @@ SUBSYSTEM_DEF(icon_smooth) if(QDELETED(smoothing_atom) || !(smoothing_atom.smoothing_flags & SMOOTH_QUEUED)) continue - smooth_icon(smoothing_atom) + if(smoothing_atom.initialized && !(smoothing_atom.icon_update_queued)) + smooth_icon(smoothing_atom) + else + deferred += smoothing_atom if (MC_TICK_CHECK) return if (!length(smooth_queue_cache)) - can_fire = FALSE + if(deferred.len) + smooth_queue = deferred + deferred = smooth_queue_cache + else + can_fire = FALSE /datum/controller/subsystem/icon_smooth/ExplosionStart() explosion_in_progress = TRUE @@ -91,8 +103,9 @@ SUBSYSTEM_DEF(icon_smooth) /datum/controller/subsystem/icon_smooth/proc/add_to_queue(atom/thing) SHOULD_NOT_SLEEP(TRUE) - if(thing.smoothing_flags & SMOOTH_QUEUED) + if(QDELETED(thing) || thing.smoothing_flags & SMOOTH_QUEUED) return + thing.smoothing_flags |= SMOOTH_QUEUED smooth_queue += thing @@ -103,7 +116,7 @@ SUBSYSTEM_DEF(icon_smooth) SHOULD_NOT_SLEEP(TRUE) for(var/atom/neighbor as anything in orange(1,thing)) - if(neighbor.smoothing_flags) + if(!QDELETED(neighbor) && neighbor.smoothing_flags) SSICONSMOOTH_ADD_TO_QUEUE(neighbor) /datum/controller/subsystem/icon_smooth/proc/remove_from_queues(atom/thing) @@ -111,5 +124,6 @@ SUBSYSTEM_DEF(icon_smooth) thing.smoothing_flags &= ~SMOOTH_QUEUED smooth_queue -= thing + deferred -= thing #undef SSICONSMOOTH_ADD_TO_QUEUE diff --git a/code/controllers/subsystems/icon_updates.dm b/code/controllers/subsystems/icon_updates.dm index 53be8269e45..c36e54262e9 100644 --- a/code/controllers/subsystems/icon_updates.dm +++ b/code/controllers/subsystems/icon_updates.dm @@ -5,13 +5,20 @@ SUBSYSTEM_DEF(icon_update) priority = SS_PRIORITY_ICON_UPDATE init_order = SS_INIT_ICON_UPDATE - var/list/queue = list() + /** + * Associative list of atoms -> callback params + * A list of `/atoms` that are waiting to have its icon updated + */ + VAR_PRIVATE/list/icon_update_queue = list() + + ///Deferred list, contains atoms that were not ready to be processed in the previous run + VAR_PRIVATE/list/deferred = list() /datum/controller/subsystem/icon_update/New() NEW_SS_GLOBAL(SSicon_update) /datum/controller/subsystem/icon_update/stat_entry(msg) - msg = "QU:[queue.len]" + msg = "QU:[icon_update_queue.len]" return ..() /datum/controller/subsystem/icon_update/Initialize() @@ -19,45 +26,101 @@ SUBSYSTEM_DEF(icon_update) ..() /datum/controller/subsystem/icon_update/fire(resumed = FALSE, no_mc_tick = FALSE) - var/list/curr = queue + var/list/icon_update_queue_cache = icon_update_queue - if (!curr.len) - suspend() - return + while (length(icon_update_queue_cache)) + var/atom/A = icon_update_queue_cache[length(icon_update_queue_cache)] + var/list/argv = icon_update_queue_cache[A] + icon_update_queue_cache.len-- - while (curr.len) - var/atom/A = curr[curr.len] - var/list/argv = curr[A] - curr.len-- + if(A.initialized) + A.icon_update_queued = FALSE - A.icon_update_queued = FALSE + //Do not target qdeleted atoms + if(QDELETED(A)) + continue - //Do not target qdeleted atoms - if(QDELETED(A)) - continue + if (islist(argv)) + A.update_icon(arglist(argv)) + else + A.update_icon() + A.update_above() + A.last_icon_update = world.time - if (islist(argv)) - A.update_icon(arglist(argv)) else - A.update_icon() - A.update_above() - A.last_icon_update = world.time + src.deferred[A] = argv if (no_mc_tick) CHECK_TICK else if (MC_TICK_CHECK) return + if (!length(icon_update_queue_cache)) + if(deferred.len) + icon_update_queue = deferred + deferred = icon_update_queue_cache + else + can_fire = FALSE +/** + * Adds an atom to the queue to have its icon updated + * + * * item - An `/atom` to add to the queue + * * parameters - Parameters that will be passed to `update_icon()` on callback + */ +/datum/controller/subsystem/icon_update/proc/add_to_queue(var/atom/item, ...) + SHOULD_NOT_SLEEP(TRUE) + + var/atom/item_cache = item + + //Skip atoms that are being deleted + if(QDELETED(item_cache)) + return + + if(!item_cache.icon_update_queued && (!item_cache.icon_update_delay || (item_cache.last_icon_update + item_cache.icon_update_delay < world.time))) + item_cache.icon_update_queued = TRUE + + //Determine if we got any parameter, and if we do set it in the associative list, otherwise just set it to TRUE + var/list/calling_arguments = length(args) > 1 ? args.Copy(2) : TRUE + src.icon_update_queue[item_cache] = calling_arguments + + if(!src.can_fire) + can_fire = TRUE + +/** + * Removes an atom from the queue of atoms waiting to have its icon updated + * + * * item - An `/atom` to add to the queue + * * VARIPARAM - Parameters that will be passed to `update_icon()` on callback + */ +/datum/controller/subsystem/icon_update/proc/remove_from_queue(var/atom/item) + SHOULD_NOT_SLEEP(TRUE) + var/atom/item_cache = item + + if(item_cache.icon_update_queued) + item_cache.icon_update_queued = FALSE + src.icon_update_queue -= item_cache + src.deferred -= item_cache + /atom - var/tmp/last_icon_update - var/tmp/icon_update_queued - var/icon_update_delay + ///When was the last time (in `world.time`) that the icon of this atom was updated via `SSicon_update` + var/tmp/last_icon_update = null + + ///If the atom is currently queued to have it's icon updated in `SSicon_update` + var/tmp/icon_update_queued = FALSE + + ///Delay to apply before updating the icon in `SSicon_update` + var/icon_update_delay = null /atom/proc/update_icon() -/atom/proc/queue_icon_update(...) - if (!icon_update_queued && (!icon_update_delay || (last_icon_update + icon_update_delay < world.time))) - icon_update_queued = TRUE - SSicon_update.queue[src] = args.len ? args : TRUE - if (SSicon_update.suspended) - SSicon_update.wake() +/** + * DO NOT USE + * + * Left for backward compatibility, use `SSicon_update.add_to_queue(thing)` + */ +/atom/proc/queue_icon_update() + //Skip if we're being deleted + if(QDELETED(src)) + return + + SSicon_update.add_to_queue(src) diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index da4e63ffb2d..6da4bb6230a 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -1,4 +1,5 @@ /atom + ///Whether /atom/Initialize() has already run for the object var/initialized = FALSE var/update_icon_on_init // Default to 'no'. @@ -48,7 +49,7 @@ #endif if (update_icon_on_init) - queue_icon_update() + SSicon_update.add_to_queue(src) return INITIALIZE_HINT_NORMAL @@ -79,6 +80,9 @@ if (reagents) QDEL_NULL(reagents) + //We're being destroyed, no need to update the icon + SSicon_update.remove_from_queue(src) + LAZYCLEARLIST(our_overlays) LAZYCLEARLIST(priority_overlays) @@ -90,4 +94,16 @@ if (O.orbiter) O.orbiter.stop_orbit() + if(length(overlays)) + overlays.Cut() + + if(light) + QDEL_NULL(light) + + if (length(light_sources)) + light_sources.Cut() + + if(smoothing_flags & SMOOTH_QUEUED) + SSicon_smooth.remove_from_queues(src) + return ..() diff --git a/code/game/objects/items/weapons/syndie.dm b/code/game/objects/items/weapons/syndie.dm index aa66db61e78..7b3ae16f752 100644 --- a/code/game/objects/items/weapons/syndie.dm +++ b/code/game/objects/items/weapons/syndie.dm @@ -13,9 +13,15 @@ desc = "A small wrapped package." w_class = ITEMSIZE_NORMAL - var/power = 1 /*Size of the explosion.*/ + ///Size of the explosion + var/power = 1 + + ///Used for the icon var/size = "small" /*Used for the icon, this one will make c-4small_0 for the off state.*/ + ///The detonator that makes this C4 charge explode + var/obj/item/syndie/c4detonator/detonator = null + /obj/item/syndie/c4explosive/heavy icon_state = "c-4large_0" item_state = "c-4large" @@ -23,15 +29,21 @@ power = 2 size = "large" -/obj/item/syndie/c4explosive/New() +/obj/item/syndie/c4explosive/Initialize() + . = ..() + var/K = rand(1,2000) K = md5(num2text(K)+name) K = copytext(K,1,7) src.desc += "\n You see [K] engraved on \the [src]." - var/obj/item/syndie/c4detonator/detonator = new(src.loc) + detonator = new(src.loc) detonator.desc += "\n You see [K] engraved on the lighter." detonator.bomb = src +/obj/item/syndie/c4explosive/Destroy() + . = ..() + QDEL_NULL(detonator) + /obj/item/syndie/c4explosive/proc/detonate() icon_state = "c-4[size]_1" spawn(50) @@ -60,6 +72,10 @@ var/obj/item/syndie/c4explosive/bomb var/pr_open = 0 /*Is the "What do you want to do?" prompt open?*/ +/obj/item/syndie/c4detonator/Destroy() + bomb = null + . = ..() + /obj/item/syndie/c4detonator/attack_self(mob/user as mob) switch(src.icon_state) if("c-4detonator_0") diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index a0f96285873..fb028b6c0f1 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -15,11 +15,27 @@ var/slowdown = 0 //amount that pulling mobs have their movement delayed by +/obj/structure/Initialize(mapload) + . = ..() + if(!isnull(material) && !istype(material)) + material = SSmaterials.get_material_by_name(material) + if (!mapload) + updateVisibility(src) // No point checking this before visualnet initializes. + if(climbable) + verbs += /obj/structure/proc/climb_on + if (smoothing_flags) + SSicon_smooth.add_to_queue(src) + SSicon_smooth.add_to_queue_neighbors(src) + /obj/structure/Destroy() if(parts) new parts(loc) if (smoothing_flags) + SSicon_smooth.remove_from_queues(src) SSicon_smooth.add_to_queue_neighbors(src) + + climbers = null + return ..() /obj/structure/attack_hand(mob/user) @@ -65,18 +81,6 @@ . = ..() bullet_ping(P) -/obj/structure/Initialize(mapload) - . = ..() - if(!isnull(material) && !istype(material)) - material = SSmaterials.get_material_by_name(material) - if (!mapload) - updateVisibility(src) // No point checking this before visualnet initializes. - if(climbable) - verbs += /obj/structure/proc/climb_on - if (smoothing_flags) - SSicon_smooth.add_to_queue(src) - SSicon_smooth.add_to_queue_neighbors(src) - /obj/structure/proc/climb_on() set name = "Climb structure" diff --git a/html/changelogs/FluffyGhost-icon_smoothing_updates_refactor.yml b/html/changelogs/FluffyGhost-icon_smoothing_updates_refactor.yml new file mode 100644 index 00000000000..7781b991006 --- /dev/null +++ b/html/changelogs/FluffyGhost-icon_smoothing_updates_refactor.yml @@ -0,0 +1,50 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +# balance +# admin +# backend +# security +# refactor +################################# + +# Your name. +author: FluffyGhost + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - refactor: "Refactored SSicon_update, now supports deferred queue and insertion/removal from said queue(s)." + - refactor: "Refactored SSicon_smoothing, now supports deferred queue and insertion/removal from said queue(s)." + - bugfix: "SSicon_smoothing now waits for the atom to be initialized before trying to smooth it." + - bugfix: "Atoms now dequeue themselves from icon smoothing and update upon deletion." + - bugfix: "Atoms now delete their overlays upon deletion." + - bugfix: "Atoms now delete their light and light sources upon deletion." + - bugfix: "Structures now delete themselves from the smoothing and enqueue the neighbors upon deletion." + - bugfix: "Structures now drop the reference to climbers upon deletion." + - bugfix: "Z_LEVEL smoothing now ignores deleted turfs." + - bugfix: "Syndie C4s now delete correctly."