mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 18:22:39 +00:00
Merge pull request #3526 from VOREStation/stonedmc-update
StonedMC Update & Hookup
This commit is contained in:
@@ -34,5 +34,9 @@ var/global/datum/controller/process/ticker/tickerProcess
|
||||
/datum/controller/process/ticker/proc/getLastTickerTimeDuration()
|
||||
return lastTickerTimeDuration
|
||||
|
||||
/world/proc/has_round_started()
|
||||
// Use these preferentially to directly examining ticker.current_state to help prepare for transition to ticker as subsystem!
|
||||
/datum/controller/process/ticker/proc/HasRoundStarted()
|
||||
return (ticker && ticker.current_state >= GAME_STATE_PLAYING)
|
||||
|
||||
/datum/controller/process/ticker/proc/IsRoundInProgress()
|
||||
return (ticker && ticker.current_state == GAME_STATE_PLAYING)
|
||||
|
||||
@@ -7,14 +7,6 @@
|
||||
*
|
||||
**/
|
||||
var/datum/controller/master/Master = new()
|
||||
var/MC_restart_clear = 0
|
||||
var/MC_restart_timeout = 0
|
||||
var/MC_restart_count = 0
|
||||
|
||||
|
||||
//current tick limit, assigned by the queue controller before running a subsystem.
|
||||
//used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
|
||||
var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
|
||||
/datum/controller/master
|
||||
name = "Master"
|
||||
@@ -40,8 +32,6 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
var/make_runtime = 0
|
||||
|
||||
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
|
||||
// Has round started? (So we know what subsystems to run)
|
||||
var/round_started = 0
|
||||
|
||||
// The type of the last subsystem to be process()'d.
|
||||
var/last_type_processed
|
||||
@@ -52,6 +42,16 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
var/queue_priority_count_bg = 0 //Same, but for background subsystems
|
||||
var/map_loading = FALSE //Are we loading in a new map?
|
||||
|
||||
var/current_runlevel //for scheduling different subsystems for different stages of the round
|
||||
|
||||
var/static/restart_clear = 0
|
||||
var/static/restart_timeout = 0
|
||||
var/static/restart_count = 0
|
||||
|
||||
//current tick limit, assigned before running a subsystem.
|
||||
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
|
||||
var/static/current_ticklimit = TICK_LIMIT_RUNNING
|
||||
|
||||
/datum/controller/master/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
subsystems = list()
|
||||
@@ -70,6 +70,8 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
|
||||
/datum/controller/master/Shutdown()
|
||||
processing = FALSE
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
reverseRange(subsystems)
|
||||
for(var/datum/controller/subsystem/ss in subsystems)
|
||||
ss.Shutdown()
|
||||
|
||||
@@ -77,14 +79,14 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
/proc/Recreate_MC()
|
||||
. = -1 //so if we runtime, things know we failed
|
||||
if (world.time < MC_restart_timeout)
|
||||
if (world.time < Master.restart_timeout)
|
||||
return 0
|
||||
if (world.time < MC_restart_clear)
|
||||
MC_restart_count *= 0.5
|
||||
if (world.time < Master.restart_clear)
|
||||
Master.restart_count *= 0.5
|
||||
|
||||
var/delay = 50 * ++MC_restart_count
|
||||
MC_restart_timeout = world.time + delay
|
||||
MC_restart_clear = world.time + (delay * 2)
|
||||
var/delay = 50 * ++Master.restart_count
|
||||
Master.restart_timeout = world.time + delay
|
||||
Master.restart_clear = world.time + (delay * 2)
|
||||
Master.processing = 0 //stop ticking this one
|
||||
try
|
||||
new/datum/controller/master()
|
||||
@@ -107,8 +109,28 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
else
|
||||
msg += "\t [varname] = [varval]\n"
|
||||
log_world(msg)
|
||||
|
||||
var/datum/controller/subsystem/BadBoy = Master.last_type_processed
|
||||
var/FireHim = FALSE
|
||||
if(istype(BadBoy))
|
||||
msg = null
|
||||
switch(++BadBoy.failure_strikes)
|
||||
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
|
||||
if(3)
|
||||
msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined."
|
||||
BadBoy.flags |= SS_NO_FIRE
|
||||
if(msg)
|
||||
log_game(msg)
|
||||
message_admins("<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
|
||||
if (istype(Master.subsystems))
|
||||
if(FireHim)
|
||||
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
||||
subsystems = Master.subsystems
|
||||
current_runlevel = Master.current_runlevel
|
||||
StartProcessing(10)
|
||||
else
|
||||
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
|
||||
@@ -133,19 +155,22 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
|
||||
var/start_timeofday = REALTIMEOFDAY
|
||||
// Initialize subsystems.
|
||||
CURRENT_TICKLIMIT = config.tick_limit_mc_init
|
||||
current_ticklimit = config.tick_limit_mc_init
|
||||
for (var/datum/controller/subsystem/SS in subsystems)
|
||||
if (SS.flags & SS_NO_INIT)
|
||||
continue
|
||||
SS.Initialize(REALTIMEOFDAY)
|
||||
CHECK_TICK
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
|
||||
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
|
||||
if (!current_runlevel)
|
||||
SetRunLevel(RUNLEVEL_LOBBY)
|
||||
|
||||
// Sort subsystems by display setting for easy access.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_display)
|
||||
// Set world options.
|
||||
@@ -161,16 +186,12 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
// Loop.
|
||||
Master.StartProcessing(0)
|
||||
|
||||
// Notify the MC that the round has started.
|
||||
/datum/controller/master/proc/RoundStart()
|
||||
round_started = 1
|
||||
var/timer = world.time
|
||||
for (var/datum/controller/subsystem/SS in subsystems)
|
||||
if (SS.flags & SS_FIRE_IN_LOBBY || SS.flags & SS_TICKER)
|
||||
continue //already firing
|
||||
// Stagger subsystems.
|
||||
timer += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = timer
|
||||
/datum/controller/master/proc/SetRunLevel(new_runlevel)
|
||||
var/old_runlevel = isnull(current_runlevel) ? "NULL" : runlevel_flags[current_runlevel]
|
||||
testing("MC: Runlevel changed from [old_runlevel] to [new_runlevel]")
|
||||
current_runlevel = RUNLEVEL_FLAG_TO_INDEX(new_runlevel)
|
||||
if(current_runlevel < 1)
|
||||
CRASH("Attempted to set invalid runlevel: [new_runlevel]")
|
||||
|
||||
// Starts the mc, and sticks around to restart it if the loop ever ends.
|
||||
/datum/controller/master/proc/StartProcessing(delay)
|
||||
@@ -195,12 +216,9 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
//Prep the loop (most of this is because we want MC restarts to reset as much state as we can, and because
|
||||
// local vars rock
|
||||
|
||||
// Schedule the first run of the Subsystems.
|
||||
round_started = world.has_round_started()
|
||||
//all this shit is here so that flag edits can be refreshed by restarting the MC. (and for speed)
|
||||
var/list/tickersubsystems = list()
|
||||
var/list/normalsubsystems = list()
|
||||
var/list/lobbysubsystems = list()
|
||||
var/list/runlevel_sorted_subsystems = list(list()) //ensure we always have at least one runlevel
|
||||
var/timer = world.time
|
||||
for (var/thing in subsystems)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
@@ -215,25 +233,29 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
timer += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = timer
|
||||
continue
|
||||
if (SS.flags & SS_FIRE_IN_LOBBY)
|
||||
lobbysubsystems += SS
|
||||
timer += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = timer
|
||||
else if (round_started)
|
||||
timer += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = timer
|
||||
normalsubsystems += SS
|
||||
|
||||
var/ss_runlevels = SS.runlevels
|
||||
var/added_to_any = FALSE
|
||||
for(var/I in 1 to global.runlevel_flags.len)
|
||||
if(ss_runlevels & global.runlevel_flags[I])
|
||||
while(runlevel_sorted_subsystems.len < I)
|
||||
runlevel_sorted_subsystems += list(list())
|
||||
runlevel_sorted_subsystems[I] += SS
|
||||
added_to_any = TRUE
|
||||
if(!added_to_any)
|
||||
WARNING("[SS.name] subsystem is not SS_NO_FIRE but also does not have any runlevels set!")
|
||||
|
||||
queue_head = null
|
||||
queue_tail = null
|
||||
//these sort by lower priorities first to reduce the number of loops needed to add subsequent SS's to the queue
|
||||
//(higher subsystems will be sooner in the queue, adding them later in the loop means we don't have to loop thru them next queue add)
|
||||
sortTim(tickersubsystems, /proc/cmp_subsystem_priority)
|
||||
sortTim(normalsubsystems, /proc/cmp_subsystem_priority)
|
||||
sortTim(lobbysubsystems, /proc/cmp_subsystem_priority)
|
||||
for(var/I in runlevel_sorted_subsystems)
|
||||
sortTim(runlevel_sorted_subsystems, /proc/cmp_subsystem_priority)
|
||||
I += tickersubsystems
|
||||
|
||||
normalsubsystems += tickersubsystems
|
||||
lobbysubsystems += tickersubsystems
|
||||
var/cached_runlevel = current_runlevel
|
||||
var/list/current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
|
||||
|
||||
init_timeofday = REALTIMEOFDAY
|
||||
init_time = world.time
|
||||
@@ -246,7 +268,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
while (1)
|
||||
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
|
||||
if (processing <= 0)
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
@@ -254,7 +276,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
// because sleeps are processed in the order received, so longer sleeps are more likely to run first
|
||||
if (world.tick_usage > TICK_LIMIT_MC)
|
||||
sleep_delta += 2
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.5)
|
||||
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
continue
|
||||
|
||||
@@ -270,32 +292,41 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
if (!Failsafe || (Failsafe.processing_interval > 0 && (Failsafe.lasttick+(Failsafe.processing_interval*5)) < world.time))
|
||||
new/datum/controller/failsafe() // (re)Start the failsafe.
|
||||
if (!queue_head || !(iteration % 3))
|
||||
if (round_started)
|
||||
subsystems_to_check = normalsubsystems
|
||||
else
|
||||
subsystems_to_check = lobbysubsystems
|
||||
var/checking_runlevel = current_runlevel
|
||||
if(cached_runlevel != checking_runlevel)
|
||||
//resechedule subsystems
|
||||
cached_runlevel = checking_runlevel
|
||||
current_runlevel_subsystems = runlevel_sorted_subsystems[cached_runlevel]
|
||||
var/stagger = world.time
|
||||
for(var/I in current_runlevel_subsystems)
|
||||
var/datum/controller/subsystem/SS = I
|
||||
if(SS.next_fire <= world.time)
|
||||
stagger += world.tick_lag * rand(1, 5)
|
||||
SS.next_fire = stagger
|
||||
|
||||
subsystems_to_check = current_runlevel_subsystems
|
||||
else
|
||||
subsystems_to_check = tickersubsystems
|
||||
if (CheckQueue(subsystems_to_check) <= 0)
|
||||
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
|
||||
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
|
||||
log_world("MC: SoftReset() failed, crashing")
|
||||
return
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
if (queue_head)
|
||||
if (RunQueue() <= 0)
|
||||
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
|
||||
if (!SoftReset(tickersubsystems, runlevel_sorted_subsystems))
|
||||
log_world("MC: SoftReset() failed, crashing")
|
||||
return
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
error_level--
|
||||
@@ -306,7 +337,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
iteration++
|
||||
last_run = world.time
|
||||
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
|
||||
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
|
||||
current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
|
||||
|
||||
@@ -395,7 +426,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
else
|
||||
tick_precentage = tick_remaining
|
||||
|
||||
CURRENT_TICKLIMIT = world.tick_usage + tick_precentage
|
||||
current_ticklimit = world.tick_usage + tick_precentage
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER))
|
||||
ran_non_ticker = TRUE
|
||||
@@ -460,13 +491,15 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
|
||||
//resets the queue, and all subsystems, while filtering out the subsystem lists
|
||||
// called if any mc's queue procs runtime or exit improperly.
|
||||
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/normal_SS, list/lobby_SS)
|
||||
/datum/controller/master/proc/SoftReset(list/ticker_SS, list/runlevel_SS)
|
||||
. = 0
|
||||
log_world("MC: SoftReset called, resetting MC queue state.")
|
||||
if (!istype(subsystems) || !istype(ticker_SS) || !istype(normal_SS) || !istype(lobby_SS))
|
||||
log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[normal_SS]' '[lobby_SS]' Crashing!")
|
||||
if (!istype(subsystems) || !istype(ticker_SS) || !istype(runlevel_SS))
|
||||
log_world("MC: SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[runlevel_SS]'")
|
||||
return
|
||||
var/subsystemstocheck = subsystems + ticker_SS + normal_SS + lobby_SS
|
||||
var/subsystemstocheck = subsystems + ticker_SS
|
||||
for(var/I in runlevel_SS)
|
||||
subsystemstocheck |= I
|
||||
|
||||
for (var/thing in subsystemstocheck)
|
||||
var/datum/controller/subsystem/SS = thing
|
||||
@@ -474,8 +507,8 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
//list(SS) is so if a list makes it in the subsystem list, we remove the list, not the contents
|
||||
subsystems -= list(SS)
|
||||
ticker_SS -= list(SS)
|
||||
normal_SS -= list(SS)
|
||||
lobby_SS -= list(SS)
|
||||
for(var/I in runlevel_SS)
|
||||
I -= list(SS)
|
||||
log_world("MC: SoftReset: Found bad entry in subsystem list, '[SS]'")
|
||||
continue
|
||||
if (SS.queue_next && !istype(SS.queue_next))
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
var/priority = 50 //When mutiple subsystems need to run in the same tick, higher priority subsystems will run first and be given a higher share of the tick before MC_TICK_CHECK triggers a sleep
|
||||
|
||||
var/flags = 0 //see MC.dm in __DEFINES Most flags must be set on world start to take full effect. (You can also restart the mc to force them to process again)
|
||||
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
|
||||
|
||||
//set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
|
||||
// use the SS_NO_FIRE flag instead for systems that never fire to keep it from even being added to the list
|
||||
@@ -27,6 +28,8 @@
|
||||
var/datum/controller/subsystem/queue_next
|
||||
var/datum/controller/subsystem/queue_prev
|
||||
|
||||
var/static/failure_strikes = 0 //How many times we suspect this subsystem has crashed the MC, 3 strikes and you're out!
|
||||
|
||||
//Do not override
|
||||
/datum/controller/subsystem/New()
|
||||
return
|
||||
|
||||
@@ -5,7 +5,8 @@ SUBSYSTEM_DEF(garbage)
|
||||
name = "Garbage"
|
||||
priority = 15
|
||||
wait = 5
|
||||
flags = SS_FIRE_IN_LOBBY|SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
|
||||
flags = SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
|
||||
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
|
||||
|
||||
var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
|
||||
var/delslasttick = 0 // number of del()'s we've done this tick
|
||||
|
||||
Reference in New Issue
Block a user