From 3cf19e34dd168174b66f0a3ebf2fb98968dd5506 Mon Sep 17 00:00:00 2001 From: Emmett Gaines Date: Sat, 15 May 2021 04:59:21 -0400 Subject: [PATCH] Makes timer subsystems available as a new subsystem type (#59073) --- code/__DEFINES/MC.dm | 7 ++ code/__DEFINES/subsystems.dm | 1 + code/controllers/subsystem/sound_loops.dm | 3 + code/controllers/subsystem/timer.dm | 68 +++++++++++--------- code/datums/looping_sounds/_looping_sound.dm | 6 +- tgstation.dme | 1 + 6 files changed, 53 insertions(+), 33 deletions(-) create mode 100644 code/controllers/subsystem/sound_loops.dm diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 0ba6b0c4225..edd49047280 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -66,6 +66,13 @@ }\ /datum/controller/subsystem/##X +#define TIMER_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/timer/##X);\ +/datum/controller/subsystem/timer/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ +}\ +/datum/controller/subsystem/timer/##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);\ diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index cdb165124b9..f1d79d4a335 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -189,6 +189,7 @@ #define FIRE_PRIORITY_OVERLAYS 500 #define FIRE_PRIORITY_EXPLOSIONS 666 #define FIRE_PRIORITY_TIMER 700 +#define FIRE_PRIORITY_SOUND_LOOPS 800 #define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost. diff --git a/code/controllers/subsystem/sound_loops.dm b/code/controllers/subsystem/sound_loops.dm new file mode 100644 index 00000000000..46b916603f9 --- /dev/null +++ b/code/controllers/subsystem/sound_loops.dm @@ -0,0 +1,3 @@ +TIMER_SUBSYSTEM_DEF(sound_loops) + name = "Sound Loops" + priority = FIRE_PRIORITY_SOUND_LOOPS diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index da283a67c7c..e57901cd783 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -1,9 +1,9 @@ /// Controls how many buckets should be kept, each representing a tick. (1 minutes worth) #define BUCKET_LEN (world.fps*1*60) /// Helper for getting the correct bucket for a given timer -#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) +#define BUCKET_POS(timer) (((round((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 (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1))) +#define TIMER_MAX(timer_ss) (world.time + TICKS2DS(min(BUCKET_LEN-(timer_ss.practical_offset-DS2TICKS(world.time - timer_ss.head_offset))-1, BUCKET_LEN-1))) /// Max float with integer precision #define TIMER_ID_MAX (2**24) @@ -43,6 +43,8 @@ SUBSYSTEM_DEF(timer) var/list/clienttime_timers = list() /// Contains the last time that a timer's callback was invoked, or the last tick the SS fired if no timers are being processed var/last_invoke_tick = 0 + /// Keeps track of the next index to work on for client timers + var/next_clienttime_timer_index = 0 /// Contains the last time that a warning was issued for not invoking callbacks var/static/last_invoke_warning = 0 /// Boolean operator controlling if the timer SS will automatically reset buckets if it fails to invoke callbacks for an extended period of time @@ -100,7 +102,6 @@ SUBSYSTEM_DEF(timer) log_world(to_log.Join("\n")) // Process client-time timers - var/static/next_clienttime_timer_index = 0 if (next_clienttime_timer_index) clienttime_timers.Cut(1, next_clienttime_timer_index+1) next_clienttime_timer_index = 0 @@ -181,7 +182,7 @@ SUBSYSTEM_DEF(timer) var/i = 0 for (i in 1 to length(second_queue)) timer = second_queue[i] - if (timer.timeToRun >= TIMER_MAX) + if (timer.timeToRun >= TIMER_MAX(src)) i-- break @@ -275,7 +276,7 @@ SUBSYSTEM_DEF(timer) continue // Check that the TTR is within the range covered by buckets, when exceeded we've finished - if (timer.timeToRun >= TIMER_MAX) + if (timer.timeToRun >= TIMER_MAX(src)) i-- break @@ -348,8 +349,10 @@ SUBSYSTEM_DEF(timer) var/datum/timedevent/next /// Previous timed event in the bucket var/datum/timedevent/prev + /// The timer subsystem this event is associated with + var/datum/controller/subsystem/timer/timer_subsystem -/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source) +/datum/timedevent/New(datum/callback/callBack, wait, flags, datum/controller/subsystem/timer/timer_subsystem, hash, source) var/static/nextid = 1 id = TIMER_ID_NULL src.callBack = callBack @@ -357,13 +360,14 @@ SUBSYSTEM_DEF(timer) src.flags = flags src.hash = hash src.source = source + src.timer_subsystem = timer_subsystem || SStimer // Determine time at which the timer's callback should be invoked timeToRun = (flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) + wait // Include the timer in the hash table if the timer is unique if (flags & TIMER_UNIQUE) - SStimer.hashes[hash] = src + timer_subsystem.hashes[hash] = src // Generate ID for the timer if the timer is stoppable, include in the timer id dictionary if (flags & TIMER_STOPPABLE) @@ -372,9 +376,9 @@ SUBSYSTEM_DEF(timer) nextid += min(1, 2 ** round(nextid / SHORT_REAL_LIMIT)) else nextid++ - SStimer.timer_id_dict[id] = src + timer_subsystem.timer_id_dict[id] = src - if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME)) + if ((timeToRun < world.time || timeToRun < timer_subsystem.head_offset) && !(flags & TIMER_CLIENT_TIME)) CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]") if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object)) @@ -385,7 +389,7 @@ SUBSYSTEM_DEF(timer) /datum/timedevent/Destroy() ..() if (flags & TIMER_UNIQUE && hash) - SStimer.hashes -= hash + timer_subsystem.hashes -= hash if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers) callBack.object.active_timers -= src @@ -394,12 +398,12 @@ SUBSYSTEM_DEF(timer) callBack = null if (flags & TIMER_STOPPABLE) - SStimer.timer_id_dict -= id + timer_subsystem.timer_id_dict -= id if (flags & TIMER_CLIENT_TIME) if (!spent) spent = world.time - SStimer.clienttime_timers -= src + timer_subsystem.clienttime_timers -= src return QDEL_HINT_IWILLGC if (!spent) @@ -423,8 +427,8 @@ SUBSYSTEM_DEF(timer) // Store local references for the bucket list and secondary queue // This is faster than referencing them from the datum itself - var/list/bucket_list = SStimer.bucket_list - var/list/second_queue = SStimer.second_queue + var/list/bucket_list = timer_subsystem.bucket_list + var/list/second_queue = timer_subsystem.second_queue // Attempt to get the head of the bucket var/datum/timedevent/buckethead @@ -436,14 +440,14 @@ SUBSYSTEM_DEF(timer) // into an existing bucket, or is otherwise not present in the secondary queue if(buckethead == src) bucket_list[bucketpos] = next - SStimer.bucket_count-- - else if(timeToRun < TIMER_MAX) - SStimer.bucket_count-- + timer_subsystem.bucket_count-- + else if(timeToRun < TIMER_MAX(timer_subsystem)) + timer_subsystem.bucket_count-- else var/l = length(second_queue) second_queue -= src if(l == length(second_queue)) - SStimer.bucket_count-- + timer_subsystem.bucket_count-- // Remove the timed event from the bucket, ensuring to maintain // the integrity of the bucket's list if relevant @@ -473,20 +477,20 @@ SUBSYSTEM_DEF(timer) // Check if this timed event should be diverted to the client time bucket, or the secondary queue var/list/L if (flags & TIMER_CLIENT_TIME) - L = SStimer.clienttime_timers - else if (timeToRun >= TIMER_MAX) - L = SStimer.second_queue + L = timer_subsystem.clienttime_timers + else if (timeToRun >= TIMER_MAX(timer_subsystem)) + L = timer_subsystem.second_queue if(L) BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY) return // Get a local reference to the bucket list, this is faster than referencing the datum - var/list/bucket_list = SStimer.bucket_list + var/list/bucket_list = timer_subsystem.bucket_list // Find the correct bucket for this timed event var/bucket_pos = BUCKET_POS(src) var/datum/timedevent/bucket_head = bucket_list[bucket_pos] - SStimer.bucket_count++ + timer_subsystem.bucket_count++ // If there is no timed event at this position, then the bucket is 'empty' // and we can just set this event to that position @@ -522,7 +526,7 @@ SUBSYSTEM_DEF(timer) * * wait deciseconds to run the timer for * * flags flags for this timer, see: code\__DEFINES\subsystems.dm */ -/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, file, line) +/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, datum/controller/subsystem/timer/timer_subsystem, file, line) if (!callback) CRASH("addtimer called without a callback") @@ -538,6 +542,8 @@ SUBSYSTEM_DEF(timer) if(wait >= INFINITY) CRASH("Attempted to create timer with INFINITY delay") + timer_subsystem = timer_subsystem || SStimer + // Generate hash if relevant for timed events with the TIMER_UNIQUE flag var/hash if (flags & TIMER_UNIQUE) @@ -547,7 +553,7 @@ SUBSYSTEM_DEF(timer) hashlist += callback.arguments hash = hashlist.Join("|||||||") - var/datum/timedevent/hash_timer = SStimer.hashes[hash] + var/datum/timedevent/hash_timer = timer_subsystem.hashes[hash] if(hash_timer) if (hash_timer.spent) // it's pending deletion, pretend it doesn't exist. hash_timer.hash = null // but keep it from accidentally deleting us @@ -562,7 +568,7 @@ SUBSYSTEM_DEF(timer) else if(flags & TIMER_OVERRIDE) stack_trace("TIMER_OVERRIDE used without TIMER_UNIQUE") - var/datum/timedevent/timer = new(callback, wait, flags, hash, file && "[file]:[line]") + var/datum/timedevent/timer = new(callback, wait, flags, timer_subsystem, hash, file && "[file]:[line]") return timer.id /** @@ -571,7 +577,7 @@ SUBSYSTEM_DEF(timer) * Arguments: * * id a timerid or a /datum/timedevent */ -/proc/deltimer(id) +/proc/deltimer(id, datum/controller/subsystem/timer/timer_subsystem) if (!id) return FALSE if (id == TIMER_ID_NULL) @@ -579,8 +585,9 @@ SUBSYSTEM_DEF(timer) if (istype(id, /datum/timedevent)) qdel(id) return TRUE + timer_subsystem = timer_subsystem || SStimer //id is string - var/datum/timedevent/timer = SStimer.timer_id_dict[id] + var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id] if (timer && (!timer.spent || timer.flags & TIMER_DELETE_ME)) qdel(timer) return TRUE @@ -592,7 +599,7 @@ SUBSYSTEM_DEF(timer) * Arguments: * * id a timerid or a /datum/timedevent */ -/proc/timeleft(id) +/proc/timeleft(id, datum/controller/subsystem/timer/timer_subsystem) if (!id) return null if (id == TIMER_ID_NULL) @@ -600,8 +607,9 @@ SUBSYSTEM_DEF(timer) if (istype(id, /datum/timedevent)) var/datum/timedevent/timer = id return timer.timeToRun - world.time + timer_subsystem = timer_subsystem || SStimer //id is string - var/datum/timedevent/timer = SStimer.timer_id_dict[id] + var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id] return (timer && !timer.spent) ? timer.timeToRun - world.time : null #undef BUCKET_LEN diff --git a/code/datums/looping_sounds/_looping_sound.dm b/code/datums/looping_sounds/_looping_sound.dm index 33093cd4901..fff6b69a071 100644 --- a/code/datums/looping_sounds/_looping_sound.dm +++ b/code/datums/looping_sounds/_looping_sound.dm @@ -64,7 +64,7 @@ if(!timerid) return on_stop() - deltimer(timerid) + deltimer(timerid, SStimer) timerid = null /datum/looping_sound/proc/sound_loop(starttime) @@ -74,7 +74,7 @@ if(!chance || prob(chance)) play(get_sound(starttime)) if(!timerid) - timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP) + timerid = addtimer(CALLBACK(src, .proc/sound_loop, world.time), mid_length, TIMER_CLIENT_TIME | TIMER_STOPPABLE | TIMER_LOOP, SSsound_loops) /datum/looping_sound/proc/play(soundfile, volume_override) var/list/atoms_cache = output_atoms @@ -99,7 +99,7 @@ if(start_sound) play(start_sound, start_volume) start_wait = start_length - addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME) + addtimer(CALLBACK(src, .proc/sound_loop), start_wait, TIMER_CLIENT_TIME, SSsound_loops) /datum/looping_sound/proc/on_stop() if(end_sound) diff --git a/tgstation.dme b/tgstation.dme index 482b0c7de46..4432a414ff6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -346,6 +346,7 @@ #include "code\controllers\subsystem\server_maint.dm" #include "code\controllers\subsystem\shuttle.dm" #include "code\controllers\subsystem\skills.dm" +#include "code\controllers\subsystem\sound_loops.dm" #include "code\controllers\subsystem\sounds.dm" #include "code\controllers\subsystem\spacedrift.dm" #include "code\controllers\subsystem\statpanel.dm"