mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-21 23:52:12 +00:00
* Replace SSingulo with SScalamity; processing cleanup
- SScalamity now handles blobs as well as singularity-types.
- Processing subtypes have been simplified to not require a stop_processing definition.
* this is probably important
* Remove cascade disabling SSgarbage
* Fixed a bug where dust() did not qdel the target mob
* Misc fixes
* Fix decals surviving break-to-plating
* Subsystem flag tweaks
* Apparently subsystems are new'd before config is.
* Fix paper icons
* Speculative fix for insane lag
* Better machinery stat
* Make organs not use SSoverlay
* Misc bugfixes & tweaks
* Nightmode fixes
* Changelog for SMC
* Port /tg/'s improved MC crash handling
* Add some more SS Recover() procs
* supply_controller -> SScargo
* More New() -> Initialize()
* pAI and robot construction overlays
* Fix cargo unit tests
* Merge the DMM Suite's atom/New() into atoms_init
* Lighting pre-baking
* Lighting initialization logging
* Fix some bad SS init orders
* Fix SSlighting logging; rename Processes to MC
* Speculative fix for insane GC lag
* Prebaked openturf/icon_smooth & fix lighting prebake
* SS init status; SSatoms LATEQDEL
* Fix bug with MC init stat panel
* Fix parallax
* Misc
* Ignore SS_NO_DISPLAY during init
* apparently this is important
* REEEEEE
* Image GC fixes; broadcaster radio-new sanity
* RCON Cleanup
* Move pAI recruiter into subsystem
* Move global solars list into sun subsystem
* Make chickens not use a global
* Demote SSdisposals to SS_BACKGROUND; garbage-debug cleanup
* Speed up space init a little
* Fix bad timer flags on floor drying
* Subsystem panic-restart verb for mins
* Explosion speedup
* Minor subsystem & MC logging tweaks
* SSopenturf improvements
* Make pipenet actually initialize (whoops)
* Minor tweaks
* Implement lighting rounding
* comments are hard okay
* Minor lattice tweaks
* Fix some timer issues & better closet init
* Timer sanity
* Request console tweaks + Storage init sanity
* Minor SSmachiner RCON improvements
* Further reduce world-start timer count
* Standardize subsystem logging
* Garbage hard delete profiling from /tg/
* Timer hang detection & recovery
* Log machines that sleep in process() and fuck up SSmachinery
* Fix an issue with external airlocks sleeping in process()
* Failsafe logging
* Minor tweaks
* Revert "Request console tweaks + Storage init sanity"
This reverts commit 98d3579e35.
* Re-implement RC changes
* Fix SQL FT saving
* Fix SSmachinery sleep in disposals
* Minor SS tweaks
* Paper fixes
* Blood drying fixes
* Merge gameticker and SSticker
* Minor global list init cleanup
* Lagcheck biogenerator & bags
* Tweak SScargo init order; RIG Initialize()
* Caching tweaks
* Remove rogue comma
* Initialize fixes
* Lighting destroy cleanup
* Fix emagging airlocks
* Initial SSicon implementation
* Tweaks & Fixes
* Fire + Air alarm queued icon updates
* Overlays + Queued icon cleanup
* Runtime & background fixes
* Kill some meaningless set statements
* Kill some image qdels
* Bump up lighting rounding val
* Fix adv. scanner destroy runtimes
* Remove unneeded icon update limiting
* Move icon smoothing into helpers
* Show a warning if DM 510 compiles without memory leak hack enabled
* Re-organize subsystems & MC defines a little
* Airlock SFX
* Log of Changes
* Make SSicon_update disable itself when not doing anything
* Fix respawn verb runtime when used early in server-init
* Add more information to MC's stat_entry()
* Replace direct refernces to gcDestroyed with QDEL* macros
* plant_controller -> SSplants
* More plant tweaks
* Add more humor to changelog
* Move parallax globals into SSparallax
* Lighting responsiveness tweaks
* Fix parallax init order & better MC init panel stat
* Make mobs GC
* More overlays + Remove intercom spawn()
* SSfast_process; make pinpointers not use spawn-recursion to process
Also made the SM Cascade beach process with SSprocessing instead of a spawn loop.
* Update changelog
* Mob GC tweaks
* Del() cleanup
* Fix insomniac ZAS connection edges
* Minor pAI cleanup
* Convert more things to SSoverlay; fix duplicated overlay in field gens
* SM Bluespace turf tweaks
* Update SSgarbage debug globals list
* Human-type qdel tweaks
* Subsystem suspension; stat_entry improvements
* SQL Statistics cleanup
* Fix runtimes with ambrosia
* More disable() -> suspend(); fix nightmode again
* Human qdel fix; minor tweaks
* Update turbolift to work with StonedMC
* Make lifts use timers instead of a subsystem
* Make SSassets start earlier
* Convert the radio controller into a subsystem
* Fix some missing CHECK_TICKs in asteroid generation
* MC stat tweaks; make shouldnt_see a typecache
* Kill some redundant debug-controller entries
* radio_controller -> SSradio
* Better SSgarbage hard-del logging from /tg/ upstream
* Logging tweaks + GELF
* Misc client caching improvements
* Slime SSoverlay
* Oven icon fixes
* Implant fixes
- Death implants will no longer spam Common on death of user.
- Death implants should handle a deleted user better.
* Holder tweaks + Welding tool Initialize()
* Fix some bad subsystem logging
* Fix suit cooling units spawning without cells
* Starlight tweaks
* Gibber infinite gib fix
* More SSoverlay stuff
* Make crates use CUT_OVERLAY_IN
* Make SSarrivals suspend instead of disable
* Make openturf use split/phased tick checks
* Speculative fix for unwet timer runtimes
* Blood overlay tweaks/fixes
* Update crusher to play nice with SMC + SSoverlay
* Openturf improvements and fixes
* Minor turbolift tweaks
* Lighting performance improvements + ChangeTurf tweaks
* this is probably important
* Fix wall weld noises on changeturf
* More ChangeTurf tweaks
* Explosion tweaks
* Pre-game lobby tweaks
* Openturf tweaks
* Prevent admins from starting the game before init finishes
* Fix Travis
* Kill an unused var
* Fix ChangeTurf runtimes on openturfs
* Fixes
* Browser datum fixes, asset caching
* Update changelog
* Changelog
* Lobby tweaks
* Ticker tweaks; kill ticker var
* Further lobby tweaks
* Cascade tweaks
* air_master -> SSair
* Reduce overhead from radio autosay
* alarm_manager -> SSalarm
* bomb_processor -> SSexplosives
* corp_regs -> SSlaw
* ZAS overlay fixes
* Small wall icon optimization
* Fix effects master
* Assembly tweaks
* Megavend fixes
* Shuttle fixes
* Camera alert performance improvements
* Fix some world.log spam from lighting overlays
* Fix some Initialize() procs
* Openspace responsiveness tweaks
* Make HE pipes animate through openturfs
* Kill a spawn
551 lines
18 KiB
Plaintext
551 lines
18 KiB
Plaintext
/**
|
|
* StonedMC
|
|
*
|
|
* Designed to properly split up a given tick among subsystems
|
|
* Note: if you read parts of this code and think "why is it doing it that way"
|
|
* Odds are, there is a reason
|
|
*
|
|
**/
|
|
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"
|
|
|
|
// Are we processing (higher values increase the processing delay by n ticks)
|
|
var/processing = 1
|
|
// How many times have we ran
|
|
var/iteration = 0
|
|
|
|
// world.time of last fire, for tracking lag outside of the mc
|
|
var/last_run
|
|
|
|
// List of subsystems to process().
|
|
var/list/subsystems
|
|
|
|
// Vars for keeping track of tick drift.
|
|
var/init_timeofday
|
|
var/init_time
|
|
var/tickdrift = 0
|
|
|
|
var/sleep_delta
|
|
|
|
var/make_runtime = 0
|
|
|
|
var/initialization_time_taken
|
|
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
|
|
|
|
var/initializing = FALSE
|
|
|
|
// The type of the last subsystem to be process()'d.
|
|
var/last_type_processed
|
|
|
|
var/datum/controller/subsystem/queue_head //Start of queue linked list
|
|
var/datum/controller/subsystem/queue_tail //End of queue linked list (used for appending to the list)
|
|
var/queue_priority_count = 0 //Running total so that we don't have to loop thru the queue each run to split up the tick
|
|
var/queue_priority_count_bg = 0 //Same, but for background subsystems
|
|
|
|
/datum/controller/master/New()
|
|
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
|
subsystems = list()
|
|
if (Master != src)
|
|
if (istype(Master))
|
|
Recover()
|
|
qdel(Master)
|
|
else
|
|
init_subtypes(/datum/controller/subsystem, subsystems)
|
|
Master = src
|
|
|
|
/datum/controller/master/Destroy()
|
|
..()
|
|
// Tell qdel() to Del() this object.
|
|
return QDEL_HINT_HARDDEL_NOW
|
|
|
|
/datum/controller/master/Shutdown()
|
|
processing = FALSE
|
|
for(var/datum/controller/subsystem/ss in subsystems)
|
|
ss.Shutdown()
|
|
|
|
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
|
|
// -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)
|
|
return 0
|
|
if (world.time < MC_restart_clear)
|
|
MC_restart_count *= 0.5
|
|
|
|
var/delay = 50 * ++MC_restart_count
|
|
MC_restart_timeout = world.time + delay
|
|
MC_restart_clear = world.time + (delay * 2)
|
|
Master.processing = 0 //stop ticking this one
|
|
try
|
|
new/datum/controller/master()
|
|
catch
|
|
return -1
|
|
return 1
|
|
|
|
|
|
/datum/controller/master/Recover()
|
|
var/msg = "## DEBUG: [time2text(world.timeofday)] MC restarted. Reports:\n"
|
|
for (var/varname in Master.vars)
|
|
switch (varname)
|
|
if("name", "tag", "bestF", "type", "parent_type", "vars", "statclick") // Built-in junk.
|
|
continue
|
|
else
|
|
var/varval = Master.vars[varname]
|
|
if (istype(varval, /datum)) // Check if it has a type var.
|
|
var/datum/D = varval
|
|
msg += "\t [varname] = [D]([D.type])\n"
|
|
else
|
|
msg += "\t [varname] = [varval]\n"
|
|
log_mc(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)
|
|
admin_notice("<span class='danger'>[msg]</span>", R_DEBUG | R_DEV)
|
|
log_mc(msg)
|
|
|
|
if (istype(Master.subsystems))
|
|
if(FireHim)
|
|
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
|
subsystems = Master.subsystems
|
|
StartProcessing(10)
|
|
else
|
|
to_chat(world, "<span class='danger'><big>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</big></span>")
|
|
Initialize(20, TRUE)
|
|
|
|
// Please don't stuff random bullshit here,
|
|
// Make a subsystem, give it the SS_NO_FIRE flag, and do your work in it's Initialize()
|
|
/datum/controller/master/Initialize(delay, init_sss)
|
|
set waitfor = 0
|
|
|
|
if(delay)
|
|
sleep(delay)
|
|
|
|
if(init_sss)
|
|
init_subtypes(/datum/controller/subsystem, subsystems)
|
|
|
|
world.log << "Initializing subsystems..."
|
|
log_mc("Initializing subsystems...")
|
|
|
|
initializing = TRUE
|
|
|
|
// Sort subsystems by init_order, so they initialize in the correct order.
|
|
sortTim(subsystems, /proc/cmp_subsystem_init)
|
|
|
|
var/start_timeofday = REALTIMEOFDAY
|
|
// Initialize subsystems.
|
|
CURRENT_TICKLIMIT = TICK_LIMIT_MC_INIT
|
|
for (var/datum/controller/subsystem/SS in subsystems)
|
|
if (SS.flags & SS_NO_INIT)
|
|
continue
|
|
SS.StartInitialize(REALTIMEOFDAY)
|
|
CHECK_TICK
|
|
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
|
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
|
|
|
initializing = FALSE
|
|
initialization_time_taken = time
|
|
|
|
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
|
|
log_mc(msg)
|
|
admin_notice(span("danger", msg), R_DEBUG)
|
|
world.log << msg
|
|
|
|
// Sort subsystems by display setting for easy access.
|
|
sortTim(subsystems, /proc/cmp_subsystem_display)
|
|
// Set world options.
|
|
#ifndef UNIT_TEST
|
|
world.sleep_offline = 1
|
|
#endif
|
|
world.tick_lag = config.Ticklag
|
|
var/initialized_tod = REALTIMEOFDAY
|
|
sleep(1)
|
|
initializations_finished_with_no_players_logged_in = initialized_tod < REALTIMEOFDAY - 10
|
|
// 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
|
|
|
|
// Starts the mc, and sticks around to restart it if the loop ever ends.
|
|
/datum/controller/master/proc/StartProcessing(delay)
|
|
set waitfor = 0
|
|
if(delay)
|
|
sleep(delay)
|
|
var/rtn = Loop()
|
|
if (rtn > 0 || processing < 0)
|
|
return //this was suppose to happen.
|
|
//loop ended, restart the mc
|
|
log_game("MC crashed or runtimed, restarting")
|
|
message_admins("MC crashed or runtimed, restarting")
|
|
var/rtn2 = Recreate_MC()
|
|
if (rtn2 <= 0)
|
|
log_game("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
|
|
message_admins("Failed to recreate MC (Error code: [rtn2]), it's up to the failsafe now")
|
|
Failsafe.defcon = 2
|
|
|
|
// Main loop.
|
|
/datum/controller/master/proc/Loop()
|
|
. = -1
|
|
//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/timer = world.time
|
|
for (var/thing in subsystems)
|
|
var/datum/controller/subsystem/SS = thing
|
|
if (SS.flags & SS_NO_FIRE)
|
|
continue
|
|
SS.queued_time = 0
|
|
SS.queue_next = null
|
|
SS.queue_prev = null
|
|
SS.state = SS_IDLE
|
|
if (SS.flags & SS_TICKER)
|
|
tickersubsystems += SS
|
|
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
|
|
|
|
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)
|
|
|
|
normalsubsystems += tickersubsystems
|
|
lobbysubsystems += tickersubsystems
|
|
|
|
init_timeofday = REALTIMEOFDAY
|
|
init_time = world.time
|
|
|
|
iteration = 1
|
|
var/error_level = 0
|
|
var/sleep_delta = 0
|
|
var/list/subsystems_to_check
|
|
//the actual loop.
|
|
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
|
|
sleep(10)
|
|
continue
|
|
|
|
//if there are mutiple sleeping procs running before us hogging the cpu, we have to run later
|
|
// 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)
|
|
sleep(world.tick_lag * (processing + sleep_delta))
|
|
continue
|
|
|
|
sleep_delta = MC_AVERAGE_FAST(sleep_delta, 0)
|
|
if (last_run + (world.tick_lag * processing) > world.time)
|
|
sleep_delta += 1
|
|
if (world.tick_usage > (TICK_LIMIT_MC*0.5))
|
|
sleep_delta += 1
|
|
|
|
if (make_runtime)
|
|
var/datum/controller/subsystem/SS
|
|
SS.can_fire = 0
|
|
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
|
|
else
|
|
subsystems_to_check = tickersubsystems
|
|
if (CheckQueue(subsystems_to_check) <= 0)
|
|
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
|
|
log_mc("SoftReset() failed, crashing")
|
|
return
|
|
if (!error_level)
|
|
iteration++
|
|
error_level++
|
|
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
|
sleep(10)
|
|
continue
|
|
|
|
if (queue_head)
|
|
if (RunQueue() <= 0)
|
|
if (!SoftReset(tickersubsystems, normalsubsystems, lobbysubsystems))
|
|
log_mc("SoftReset() failed, crashing")
|
|
return
|
|
if (!error_level)
|
|
iteration++
|
|
error_level++
|
|
CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
|
sleep(10)
|
|
continue
|
|
error_level--
|
|
if (!queue_head) //reset the counts if the queue is empty, in the off chance they get out of sync
|
|
queue_priority_count = 0
|
|
queue_priority_count_bg = 0
|
|
|
|
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.
|
|
sleep(world.tick_lag * (processing + sleep_delta))
|
|
|
|
|
|
|
|
|
|
// This is what decides if something should run.
|
|
/datum/controller/master/proc/CheckQueue(list/subsystemstocheck)
|
|
. = 0 //so the mc knows if we runtimed
|
|
|
|
//we create our variables outside of the loops to save on overhead
|
|
var/datum/controller/subsystem/SS
|
|
var/SS_flags
|
|
|
|
for (var/thing in subsystemstocheck)
|
|
if (!thing)
|
|
subsystemstocheck -= thing
|
|
SS = thing
|
|
if (SS.state != SS_IDLE)
|
|
continue
|
|
if (SS.can_fire <= 0)
|
|
continue
|
|
if (SS.suspended > 0)
|
|
continue
|
|
if (SS.next_fire > world.time)
|
|
continue
|
|
SS_flags = SS.flags
|
|
if (SS_flags & SS_NO_FIRE)
|
|
subsystemstocheck -= SS
|
|
continue
|
|
if (!(SS_flags & SS_TICKER) && (SS_flags & SS_KEEP_TIMING) && SS.last_fire + (SS.wait * 0.75) > world.time)
|
|
continue
|
|
SS.enqueue()
|
|
. = 1
|
|
|
|
|
|
// Run thru the queue of subsystems to run, running them while balancing out their allocated tick precentage
|
|
/datum/controller/master/proc/RunQueue()
|
|
. = 0
|
|
var/datum/controller/subsystem/queue_node
|
|
var/queue_node_flags
|
|
var/queue_node_priority
|
|
var/queue_node_paused
|
|
|
|
var/current_tick_budget
|
|
var/tick_precentage
|
|
var/tick_remaining
|
|
var/ran = TRUE //this is right
|
|
var/ran_non_ticker = FALSE
|
|
var/bg_calc //have we swtiched current_tick_budget to background mode yet?
|
|
var/tick_usage
|
|
|
|
//keep running while we have stuff to run and we haven't gone over a tick
|
|
// this is so subsystems paused eariler can use tick time that later subsystems never used
|
|
while (ran && queue_head && world.tick_usage < TICK_LIMIT_MC)
|
|
ran = FALSE
|
|
bg_calc = FALSE
|
|
current_tick_budget = queue_priority_count
|
|
queue_node = queue_head
|
|
while (queue_node)
|
|
if (ran && world.tick_usage > TICK_LIMIT_RUNNING)
|
|
break
|
|
|
|
queue_node_flags = queue_node.flags
|
|
queue_node_priority = queue_node.queued_priority
|
|
|
|
//super special case, subsystems where we can't make them pause mid way through
|
|
//if we can't run them this tick (without going over a tick)
|
|
//we bump up their priority and attempt to run them next tick
|
|
//(unless we haven't even ran anything this tick, since its unlikely they will ever be able run
|
|
// in those cases, so we just let them run)
|
|
if (queue_node_flags & SS_NO_TICK_CHECK)
|
|
if (queue_node.tick_usage > TICK_LIMIT_RUNNING - world.tick_usage && ran_non_ticker)
|
|
queue_node.queued_priority += queue_priority_count * 0.10
|
|
queue_priority_count -= queue_node_priority
|
|
queue_priority_count += queue_node.queued_priority
|
|
current_tick_budget -= queue_node_priority
|
|
queue_node = queue_node.queue_next
|
|
continue
|
|
|
|
if ((queue_node_flags & SS_BACKGROUND) && !bg_calc)
|
|
current_tick_budget = queue_priority_count_bg
|
|
bg_calc = TRUE
|
|
|
|
tick_remaining = TICK_LIMIT_RUNNING - world.tick_usage
|
|
|
|
if (current_tick_budget > 0 && queue_node_priority > 0)
|
|
tick_precentage = tick_remaining / (current_tick_budget / queue_node_priority)
|
|
else
|
|
tick_precentage = tick_remaining
|
|
|
|
CURRENT_TICKLIMIT = world.tick_usage + tick_precentage
|
|
|
|
if (!(queue_node_flags & SS_TICKER))
|
|
ran_non_ticker = TRUE
|
|
ran = TRUE
|
|
tick_usage = world.tick_usage
|
|
queue_node_paused = (queue_node.state == SS_PAUSED || queue_node.state == SS_PAUSING)
|
|
last_type_processed = queue_node
|
|
|
|
queue_node.state = SS_RUNNING
|
|
|
|
var/state = queue_node.ignite(queue_node_paused)
|
|
if (state == SS_RUNNING)
|
|
state = SS_IDLE
|
|
current_tick_budget -= queue_node_priority
|
|
tick_usage = world.tick_usage - tick_usage
|
|
|
|
if (tick_usage < 0)
|
|
tick_usage = 0
|
|
|
|
queue_node.state = state
|
|
|
|
if (state == SS_PAUSED)
|
|
queue_node.paused_ticks++
|
|
queue_node.paused_tick_usage += tick_usage
|
|
queue_node = queue_node.queue_next
|
|
continue
|
|
|
|
queue_node.ticks = MC_AVERAGE(queue_node.ticks, queue_node.paused_ticks)
|
|
tick_usage += queue_node.paused_tick_usage
|
|
|
|
queue_node.tick_usage = MC_AVERAGE_FAST(queue_node.tick_usage, tick_usage)
|
|
|
|
queue_node.cost = MC_AVERAGE_FAST(queue_node.cost, TICK_DELTA_TO_MS(tick_usage))
|
|
queue_node.paused_ticks = 0
|
|
queue_node.paused_tick_usage = 0
|
|
|
|
if (queue_node_flags & SS_BACKGROUND) //update our running total
|
|
queue_priority_count_bg -= queue_node_priority
|
|
else
|
|
queue_priority_count -= queue_node_priority
|
|
|
|
queue_node.last_fire = world.time
|
|
queue_node.times_fired++
|
|
|
|
if (queue_node_flags & SS_TICKER)
|
|
queue_node.next_fire = world.time + (world.tick_lag * queue_node.wait)
|
|
else if (queue_node_flags & SS_POST_FIRE_TIMING)
|
|
queue_node.next_fire = world.time + queue_node.wait
|
|
else if (queue_node_flags & SS_KEEP_TIMING)
|
|
queue_node.next_fire += queue_node.wait
|
|
else
|
|
queue_node.next_fire = queue_node.queued_time + queue_node.wait
|
|
|
|
queue_node.queued_time = 0
|
|
|
|
//remove from queue
|
|
queue_node.dequeue()
|
|
|
|
queue_node = queue_node.queue_next
|
|
|
|
. = 1
|
|
|
|
//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)
|
|
. = 0
|
|
log_mc("SoftReset called, resetting MC queue state.")
|
|
if (!istype(subsystems) || !istype(ticker_SS) || !istype(normal_SS) || !istype(lobby_SS))
|
|
log_mc("SoftReset: Bad list contents: '[subsystems]' '[ticker_SS]' '[normal_SS]' '[lobby_SS]' Crashing!")
|
|
return
|
|
var/subsystemstocheck = subsystems + ticker_SS + normal_SS + lobby_SS
|
|
|
|
for (var/thing in subsystemstocheck)
|
|
var/datum/controller/subsystem/SS = thing
|
|
if (!SS || !istype(SS))
|
|
//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)
|
|
log_mc("SoftReset: Found bad entry in subsystem list, '[SS]'")
|
|
continue
|
|
if (SS.queue_next && !istype(SS.queue_next))
|
|
log_mc("SoftReset: Found bad data in subsystem queue, queue_next = '[SS.queue_next]'")
|
|
SS.queue_next = null
|
|
if (SS.queue_prev && !istype(SS.queue_prev))
|
|
log_mc("SoftReset: Found bad data in subsystem queue, queue_prev = '[SS.queue_prev]'")
|
|
SS.queue_prev = null
|
|
SS.queued_priority = 0
|
|
SS.queued_time = 0
|
|
SS.state = SS_IDLE
|
|
if (queue_head && !istype(queue_head))
|
|
log_mc("SoftReset: Found bad data in subsystem queue, queue_head = '[queue_head]'")
|
|
queue_head = null
|
|
if (queue_tail && !istype(queue_tail))
|
|
log_mc("SoftReset: Found bad data in subsystem queue, queue_tail = '[queue_tail]'")
|
|
queue_tail = null
|
|
queue_priority_count = 0
|
|
queue_priority_count_bg = 0
|
|
log_mc("SoftReset: Finished.")
|
|
. = 1
|
|
|
|
|
|
|
|
/datum/controller/master/stat_entry()
|
|
if(!statclick)
|
|
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
|
|
|
stat("Byond:", "(TickLag:[world.tick_lag]|FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
|
|
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (SleepDelta:[round(sleep_delta, 0.1)])"))
|
|
|
|
|
|
/datum/controller/master/ExplosionStart()
|
|
for (var/thing in subsystems)
|
|
var/datum/controller/subsystem/SS = thing
|
|
SS.ExplosionStart()
|
|
|
|
/datum/controller/master/ExplosionEnd()
|
|
for (var/thing in subsystems)
|
|
var/datum/controller/subsystem/SS = thing
|
|
SS.ExplosionEnd()
|
|
|
|
/world/proc/has_round_started()
|
|
if (SSticker.current_state >= GAME_STATE_PLAYING)
|
|
return TRUE
|
|
return FALSE
|