diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index 3ddf29310..a0f701654 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -121,6 +121,7 @@
#define FIRE_PRIORITY_CHAT 400
#define FIRE_PRIORITY_RUNECHAT 410
#define FIRE_PRIORITY_OVERLAYS 500
+#define FIRE_PRIORITY_TIMER 900
#define FIRE_PRIORITY_INPUT 1000 // This must always always be the max highest priority. Player input must never be lost.
// SS runlevels
@@ -154,3 +155,12 @@
}\
A.flags_1 &= ~OVERLAY_QUEUED_1;\
}
+
+/*
+ * Creates a new timer and adds it to the queue.
+ * Arguments:
+ * * callback The callback to call once the timer finishes.
+ * * wait Deciseconds to run the timer for, or the delay before the callback gets invoked.
+ * * flags Flags for the timer, see code\__DEFINES\subsystems.dm
+ */
+#define addtimer(args...) _addtimer(args, file = __FILE__, line = __LINE__)
diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm
index 34fde8b9e..f30043a8f 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -7,6 +7,7 @@ SUBSYSTEM_DEF(timer)
name = "Timer"
wait = 1 //SS_TICKER subsystem, so wait is in ticks
init_order = INIT_ORDER_TIMER
+ priority = FIRE_PRIORITY_TIMER
flags = SS_TICKER|SS_NO_INIT
@@ -27,6 +28,7 @@ SUBSYSTEM_DEF(timer)
var/last_invoke_tick = 0
var/static/last_invoke_warning = 0
var/static/bucket_auto_reset = TRUE
+ var/bucket_total_resets = 0
/datum/controller/subsystem/timer/PreInit()
bucket_list.len = BUCKET_LEN
@@ -235,6 +237,7 @@ SUBSYSTEM_DEF(timer)
. += ", NO CALLBACK"
/datum/controller/subsystem/timer/proc/reset_buckets()
+ bucket_total_resets++
var/list/bucket_list = src.bucket_list
var/list/alltimers = list()
//collect the timers currently in the bucket
@@ -317,6 +320,7 @@ SUBSYSTEM_DEF(timer)
var/timeToRun
var/wait
var/hash
+ var/source
var/list/flags
var/spent = 0 //time we ran the timer.
var/name //for easy debugging.
@@ -324,13 +328,14 @@ SUBSYSTEM_DEF(timer)
var/datum/timedevent/next
var/datum/timedevent/prev
-/datum/timedevent/New(datum/callback/callBack, wait, flags, hash)
+/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source)
var/static/nextid = 1
id = TIMER_ID_NULL
src.callBack = callBack
src.wait = wait
src.flags = flags
src.hash = hash
+ src.source = source
if (flags & TIMER_CLIENT_TIME)
timeToRun = REALTIMEOFDAY + wait
@@ -455,7 +460,8 @@ SUBSYSTEM_DEF(timer)
else
. = "[callBack.object.type]"
-/proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
+/// Do not call this directly. Instead, use addtimer(callback, wait, flags), which includes source information
+/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, file, line)
if (!callback)
CRASH("addtimer called without a callback")
@@ -496,7 +502,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)
+ var/datum/timedevent/timer = new(callback, wait, flags, hash, file && "[file]:[line]")
return timer.id
/proc/deltimer(id)
diff --git a/code/game/objects/effects/spawners/xeno_egg_delivery.dm b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
index 9be52dab5..dd4a6ea47 100644
--- a/code/game/objects/effects/spawners/xeno_egg_delivery.dm
+++ b/code/game/objects/effects/spawners/xeno_egg_delivery.dm
@@ -15,5 +15,5 @@
message_admins("An alien egg has been delivered to [ADMIN_VERBOSEJMP(T)].")
log_game("An alien egg has been delivered to [AREACOORD(T)]")
var/message = "Attention [station_name()], we have entrusted you with a research specimen in [get_area_name(T, TRUE)]. Remember to follow all safety precautions when dealing with the specimen."
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, CALLBACK(GLOBAL_PROC, /proc/print_command_report, message), announcement_time))
return INITIALIZE_HINT_QDEL
diff --git a/code/game/world.dm b/code/game/world.dm
index 8ef4d3719..999704c8a 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -71,7 +71,7 @@ GLOBAL_VAR(restart_counter)
#else
cb = VARSET_CALLBACK(SSticker, force_ending, TRUE)
#endif
- SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/addtimer, cb, 10 SECONDS))
+ SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, /proc/_addtimer, cb, 10 SECONDS))
/world/proc/SetupExternalRSC()
#if (PRELOAD_RSC == 0)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 399823a2c..b62e5bb8e 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -177,7 +177,8 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
/client/proc/cmd_display_overlay_log,
/client/proc/reload_configuration,
/datum/admins/proc/create_or_modify_area,
- /client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG
+ /client/proc/generate_wikichem_list, //DO NOT PRESS UNLESS YOU WANT SUPERLAG
+ /client/proc/check_timer_sources,
)
GLOBAL_PROTECT(admin_verbs_possess)
GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release))
@@ -728,4 +729,4 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
set name = "Debug Stat Panel"
set category = "Debug"
- src << output("", "statbrowser:create_debug")
\ No newline at end of file
+ src << output("", "statbrowser:create_debug")
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index f6df2a5d0..d7f89f6e2 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -822,3 +822,82 @@
return
if(alert(usr, "Are you absolutely sure you want to reload the configuration from the default path on the disk, wiping any in-round modificatoins?", "Really reset?", "No", "Yes") == "Yes")
config.admin_reload()
+
+/// A debug verb to check the sources of currently running timers
+/client/proc/check_timer_sources()
+ set category = "Debug"
+ set name = "Check Timer Sources"
+ set desc = "Checks the sources of the running timers"
+ if(!check_rights(R_DEBUG))
+ return
+
+ var/bucket_list_output = generate_timer_source_output(SStimer.bucket_list)
+ var/second_queue = generate_timer_source_output(SStimer.second_queue)
+
+ usr << browse({"
+ Bucket: [SStimer.bucket_count] | Secondary: [length(SStimer.second_queue)] | Resets: [SStimer.bucket_total_resets]
+
+
| File Source && Line | +Min TimeToFire | +C | +
| [timer_data["source"]] | +[timer_data["time"]] | +[timer_data["count"]] | +