diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index 6a8e6482ec8..c8d798532cf 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -1663,3 +1663,21 @@ GLOBAL_REAL_VAR(list/stack_trace_storage) /proc/CallAsync(datum/source, proctype, list/arguments) set waitfor = FALSE return call(source, proctype)(arglist(arguments)) + +/proc/whatIsThis(var/datum/D) + if(istype(D)) + return "It is a datum of type [D.type] - [D]" + else if(isnull(D)) + return "It is a NULL" + else if(istext(D)) + return "It is TEXT: [D]" + else if(isnum(D)) + return "It is NUM: [D]" + else if(ispath(D)) + return "It is PATH: [D]" + else if(islist(D)) + return "It is a LIST: [D]" + else if(isclient(D)) + return "It is a CLIENT: [D]" + else + return "Unknown data type: [D]" diff --git a/code/controllers/master.dm b/code/controllers/master.dm index d714e8c0c1f..97778d9532d 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -134,9 +134,11 @@ 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 + BadBoy.fail() if(3) msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined." BadBoy.flags |= SS_NO_FIRE + BadBoy.critfail() if(msg) log_game(msg) message_admins("[msg]") diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7115c77a61b..189f9183bb0 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -215,3 +215,18 @@ // Wakes a suspended subsystem. /datum/controller/subsystem/proc/wake() can_fire = TRUE + +// This subsystem has destabilized the game and is being put on warning. At this point there may be +// an opportunity to clean up the subsystem or check it for errors in ways that would otherwise be too slow. +// You should log the errors/cleanup results, so you can fix the problem rather than using this as a crutch. +/datum/controller/subsystem/proc/fail() + var/msg = "[name] subsystem being blamed for MC failure, continuing operation." + log_world(msg) + log_game(msg) + +// DO NOT ATTEMPT RECOVERY. Only log debugging info. You should leave the subsystem as it is. +// Attempting recovery here could make things worse, create hard recursions with the MC disabling it every run, etc. +/datum/controller/subsystem/proc/critfail() + var/msg = "[name] subsystem received final blame for MC failure, considered beyond recovery, and no longer firing." + log_world(msg) + log_game(msg) diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm index 1ae1d647579..1f57eaa1e52 100644 --- a/code/controllers/subsystems/mobs.dm +++ b/code/controllers/subsystems/mobs.dm @@ -51,3 +51,46 @@ SUBSYSTEM_DEF(mobs) if (MC_TICK_CHECK) return + +/datum/controller/subsystem/mobs/proc/log_recent() + var/msg = "Debug output from the [name] subsystem:\n" + msg += "- This subsystem is processed tail-first -\n" + if(!currentrun || !mob_list) + msg += "ERROR: A critical list [currentrun ? 'mob_list' : 'currentrun'] is gone!" + log_game(msg) + log_server(msg) + return + msg += "Lists: current_run: [currentrun.len], mob_list: [mob_list.len]\n" + + if(!currentrun.len) + msg += "!!The subsystem just finished the mob_list list, and currentrun is empty (or has never run).\n" + msg += "!!The info below is the tail of mob_list instead of currentrun.\n" + + var/datum/D = currentrun.len ? currentrun[currentrun.len] : mob_list[mob_list.len] + msg += "Tail entry: [whatIsThis(D)] (this is likely the item AFTER the problem item)\n" + + var/position = mob_list.Find(D) + if(!position) + msg += "Unable to find context of tail entry in mob_list list.\n" + else + if(position != mob_list.len) + var/additional = mob_list.Find(D, position+1) + if(additional) + msg += "WARNING: Tail entry found more than once in mob_list list! Context is for the first found.\n" + var/start = clamp(position-2,1,mob_list.len) + var/end = clamp(position+2,1,mob_list.len) + msg += "2 previous elements, then tail, then 2 next elements of mob_list list for context:\n" + msg += "---\n" + for(var/i in start to finish) + msg += "[whatIsThis([mob_list[i])]\n" + msg += "---\n" + log_game(msg) + log_server(msg) + +/datum/controller/subsystem/processing/fail() + ..() + log_recent() + +/datum/controller/subsystem/processing/critfail() + ..() + log_recent() \ No newline at end of file diff --git a/code/controllers/subsystems/processing/processing.dm b/code/controllers/subsystems/processing/processing.dm index 3eeff489aa3..a155d357cf3 100644 --- a/code/controllers/subsystems/processing/processing.dm +++ b/code/controllers/subsystems/processing/processing.dm @@ -60,6 +60,49 @@ SUBSYSTEM_DEF(processing) to_chat(usr, "[name] - Debug mode [debug_original_process_proc ? "en" : "dis"]abled") +/datum/controller/subsystem/processing/proc/log_recent() + var/msg = "Debug output from the [name] subsystem:\n" + msg += "- Process subsystems are processed tail-first -\n" + if(!currentrun || !processing) + msg += "ERROR: A critical list [currentrun ? 'processing' : 'currentrun'] is gone!" + log_game(msg) + log_server(msg) + return + msg += "Lists: current_run: [currentrun.len], processing: [processing.len]\n" + + if(!currentrun.len) + msg += "!!The subsystem just finished the processing list, and currentrun is empty (or has never run).\n" + msg += "!!The info below is the tail of processing instead of currentrun.\n" + + var/datum/D = currentrun.len ? currentrun[currentrun.len] : processing[processing.len] + msg += "Tail entry: [whatIsThis(D)] (this is likely the item AFTER the problem item)\n" + + var/position = processing.Find(D) + if(!position) + msg += "Unable to find context of tail entry in processing list.\n" + else + if(position != processing.len) + var/additional = processing.Find(D, position+1) + if(additional) + msg += "WARNING: Tail entry found more than once in processing list! Context is for the first found.\n" + var/start = clamp(position-2,1,processing.len) + var/end = clamp(position+2,1,processing.len) + msg += "2 previous elements, then tail, then 2 next elements of processing list for context:\n" + msg += "---\n" + for(var/i in start to finish) + msg += "[whatIsThis([processing[i])]\n" + msg += "---\n" + log_game(msg) + log_server(msg) + +/datum/controller/subsystem/processing/fail() + ..() + log_recent() + +/datum/controller/subsystem/processing/critfail() + ..() + log_recent() + /datum/proc/DebugSubsystemProcess(var/wait, var/times_fired, var/datum/controller/subsystem/processing/subsystem) subsystem.debug_last_thing = src var/start_tick = world.time