Merge pull request #1094 from ArchieBeepBoop/maptick

Backend Controller Updates + Maptick Lag minimization port (Experimental)
This commit is contained in:
QuoteFox
2021-05-02 08:30:40 +01:00
committed by GitHub
23 changed files with 1082 additions and 906 deletions
+23 -10
View File
@@ -8,6 +8,7 @@
**/
//This is the ABSOLUTE ONLY THING that should init globally like this
//2019 update: the failsafe,config and Global controllers also do it
GLOBAL_REAL(Master, /datum/controller/master) = new
//THIS IS THE INIT ORDER
@@ -35,6 +36,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/sleep_delta = 1
///Only run ticker subsystems for the next n ticks.
var/skip_ticks = 0
var/make_runtime = 0
var/initializations_finished_with_no_players_logged_in //I wonder what this could be?
@@ -334,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
new/datum/controller/failsafe() // (re)Start the failsafe.
//now do the actual stuff
if (!queue_head || !(iteration % 3))
if (!skip_ticks)
var/checking_runlevel = current_runlevel
if(cached_runlevel != checking_runlevel)
//resechedule subsystems
@@ -380,6 +384,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
iteration++
last_run = world.time
if (skip_ticks)
skip_ticks--
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
current_ticklimit = TICK_LIMIT_RUNNING
if (processing * sleep_delta <= world.tick_lag)
@@ -443,10 +449,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
while (queue_node)
if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
break
queue_node_flags = queue_node.flags
queue_node_priority = queue_node.queued_priority
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
queue_node = queue_node.queue_next
continue
//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
@@ -454,14 +462,15 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
// 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 - TICK_USAGE && ran_non_ticker)
queue_node.queued_priority += queue_priority_count * 0.1
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
if (!(queue_node_flags & SS_BACKGROUND))
queue_node.queued_priority += queue_priority_count * 0.1
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)
if (!bg_calc && (queue_node_flags & SS_BACKGROUND))
current_tick_budget = queue_priority_count_bg
bg_calc = TRUE
@@ -514,7 +523,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
queue_node.paused_ticks = 0
queue_node.paused_tick_usage = 0
if (queue_node_flags & SS_BACKGROUND) //update our running total
if (bg_calc) //update our running total
queue_priority_count_bg -= queue_node_priority
else
queue_priority_count -= queue_node_priority
@@ -582,6 +591,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
log_world("MC: SoftReset: Finished.")
. = 1
/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick.
/datum/controller/master/proc/laggy_byond_map_update_incoming()
if (!skip_ticks)
skip_ticks = 1
/datum/controller/master/stat_entry(msg)
@@ -611,4 +624,4 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
if (client_count < CONFIG_GET(number/mc_tick_rate/disable_high_pop_mc_mode_amount))
processing = CONFIG_GET(number/mc_tick_rate/base_mc_tick_rate)
else if (client_count > CONFIG_GET(number/mc_tick_rate/high_pop_mc_mode_amount))
processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate)
processing = CONFIG_GET(number/mc_tick_rate/high_pop_mc_tick_rate)
+20 -12
View File
@@ -1,11 +1,3 @@
#define SSAIR_PIPENETS 1
#define SSAIR_ATMOSMACHINERY 2
#define SSAIR_REACTQUEUE 3
#define SSAIR_EXCITEDGROUPS 4
#define SSAIR_HIGHPRESSURE 5
#define SSAIR_HOTSPOTS 6
#define SSAIR_SUPERCONDUCTIVITY 7
SUBSYSTEM_DEF(air)
name = "Atmospherics"
init_order = INIT_ORDER_AIR
@@ -20,6 +12,7 @@ SUBSYSTEM_DEF(air)
var/cost_hotspots = 0
var/cost_superconductivity = 0
var/cost_pipenets = 0
var/cost_rebuilds = 0
var/cost_atmos_machinery = 0
var/list/excited_groups = list()
@@ -27,6 +20,7 @@ SUBSYSTEM_DEF(air)
var/list/turf_react_queue = list()
var/list/hotspots = list()
var/list/networks = list()
var/list/pipenets_needing_rebuilt = list()
var/list/obj/machinery/atmos_machinery = list()
var/list/pipe_init_dirs_cache = list()
@@ -39,7 +33,7 @@ SUBSYSTEM_DEF(air)
var/list/currentrun = list()
var/currentpart = SSAIR_PIPENETS
var/currentpart = SSAIR_REBUILD_PIPENETS
var/map_loading = TRUE
var/list/queued_for_activation
@@ -52,6 +46,7 @@ SUBSYSTEM_DEF(air)
msg += "HS:[round(cost_hotspots,1)]|"
msg += "SC:[round(cost_superconductivity,1)]|"
msg += "PN:[round(cost_pipenets,1)]|"
msg += "RB:[round(cost_rebuilds,1)]|"
msg += "AM:[round(cost_atmos_machinery,1)]"
msg += "} "
msg += "AT:[active_turfs.len]|"
@@ -77,6 +72,18 @@ SUBSYSTEM_DEF(air)
/datum/controller/subsystem/air/fire(resumed = 0)
var/timer = TICK_USAGE_REAL
if(currentpart == SSAIR_REBUILD_PIPENETS)
var/list/pipenet_rebuilds = pipenets_needing_rebuilt
for(var/thing in pipenet_rebuilds)
var/obj/machinery/atmospherics/AT = thing
AT.build_network()
cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
pipenets_needing_rebuilt.Cut()
if(state != SS_RUNNING)
return
resumed = FALSE
currentpart = SSAIR_PIPENETS
if(currentpart == SSAIR_PIPENETS || !resumed)
process_pipenets(resumed)
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
@@ -137,9 +144,7 @@ SUBSYSTEM_DEF(air)
if(state != SS_RUNNING)
return
resumed = 0
currentpart = SSAIR_PIPENETS
currentpart = SSAIR_REBUILD_PIPENETS
/datum/controller/subsystem/air/proc/process_pipenets(resumed = 0)
if (!resumed)
@@ -156,6 +161,9 @@ SUBSYSTEM_DEF(air)
if(MC_TICK_CHECK)
return
/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine)
if(istype(atmos_machine, /obj/machinery/atmospherics))
pipenets_needing_rebuilt += atmos_machine
/datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0)
var/seconds = wait * 0.1
+26 -2
View File
@@ -8,6 +8,10 @@ SUBSYSTEM_DEF(server_maint)
init_order = INIT_ORDER_SERVER_MAINT
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun
var/cleanup_ticker = 0
/datum/controller/subsystem/server_maint/PreInit()
world.hub_password = "" //quickly! before the hubbies see us.
/datum/controller/subsystem/server_maint/Initialize(timeofday)
if (CONFIG_GET(flag/hub))
@@ -18,10 +22,30 @@ SUBSYSTEM_DEF(server_maint)
if(!resumed)
if(listclearnulls(GLOB.clients))
log_world("Found a null in clients list!")
if(listclearnulls(GLOB.player_list))
log_world("Found a null in player list!")
src.currentrun = GLOB.clients.Copy()
switch (cleanup_ticker) // do only one of these at a time, once per 5 fires
if (0)
if(listclearnulls(GLOB.player_list))
log_world("Found a null in player_list!")
cleanup_ticker++
if (5)
if(listclearnulls(GLOB.mob_list))
log_world("Found a null in mob_list!")
cleanup_ticker++
if (10)
if(listclearnulls(GLOB.alive_mob_list))
log_world("Found a null in alive_mob_list!")
cleanup_ticker++
if (15)
if(listclearnulls(GLOB.dead_mob_list))
log_world("Found a null in dead_mob_list!")
cleanup_ticker++
if (20)
cleanup_ticker = 0
else
cleanup_ticker++
var/list/currentrun = src.currentrun
var/round_started = SSticker.HasRoundStarted()
+43 -8
View File
@@ -1,5 +1,5 @@
#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN)||BUCKET_LEN)
#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
#define TIMER_ID_MAX (2**24) //max float with integer precision
@@ -39,15 +39,15 @@ SUBSYSTEM_DEF(timer)
/datum/controller/subsystem/timer/fire(resumed = FALSE)
var/lit = last_invoke_tick
var/last_check = world.time - TIMER_NO_INVOKE_WARNING
var/last_check = world.time - TICKS2DS(BUCKET_LEN*1.5)
var/list/bucket_list = src.bucket_list
if(!bucket_count)
last_invoke_tick = world.time
if(lit && lit < last_check && last_invoke_warning < last_check)
if(lit && lit < last_check && head_offset < last_check && last_invoke_warning < last_check)
last_invoke_warning = world.time
var/msg = "No regular timers processed in the last [TIMER_NO_INVOKE_WARNING] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
var/msg = "No regular timers processed in the last [BUCKET_LEN*1.5] ticks[bucket_auto_reset ? ", resetting buckets" : ""]!"
message_admins(msg)
WARNING(msg)
if(bucket_auto_reset)
@@ -122,7 +122,7 @@ SUBSYSTEM_DEF(timer)
if (!resumed)
timer = null
while (practical_offset <= BUCKET_LEN && head_offset + (practical_offset*world.tick_lag) <= world.time)
while (practical_offset <= BUCKET_LEN && head_offset + ((practical_offset-1)*world.tick_lag) <= world.time)
var/datum/timedevent/head = bucket_list[practical_offset]
if (!timer || !head || timer == head)
head = bucket_list[practical_offset]
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(timer)
if (timer.timeToRun < head_offset)
bucket_resolution = null //force bucket recreation
CRASH("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
stack_trace("[i] Invalid timer state: Timer in long run queue with a time to run less then head_offset. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
@@ -170,9 +170,9 @@ SUBSYSTEM_DEF(timer)
qdel(timer)
continue
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset))
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
bucket_resolution = null //force bucket recreation
CRASH("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
stack_trace("[i] Invalid timer state: Timer in long run queue that would require a backtrack to transfer to short run queue. [get_timer_debug_string(timer)] world.time: [world.time], head_offset: [head_offset], practical_offset: [practical_offset]")
if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync()
spent += timer
@@ -448,6 +448,7 @@ SUBSYSTEM_DEF(timer)
next.prev = src
prev.next = src
///Returns a string of the type of the callback for this timer
/datum/timedevent/proc/getcallingtype()
. = "ERROR"
if (callBack.object == GLOBAL_PROC)
@@ -455,6 +456,14 @@ SUBSYSTEM_DEF(timer)
else
. = "[callBack.object.type]"
/**
* Create a new timer and insert it in the queue
*
* Arguments:
* * callback the callback to call on timer finish
* * 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)
if (!callback)
CRASH("addtimer called without a callback")
@@ -499,6 +508,12 @@ SUBSYSTEM_DEF(timer)
var/datum/timedevent/timer = new(callback, wait, flags, hash)
return timer.id
/**
* Delete a timer
*
* Arguments:
* * id a timerid or a /datum/timedevent
*/
/proc/deltimer(id)
if (!id)
return FALSE
@@ -515,6 +530,26 @@ SUBSYSTEM_DEF(timer)
return TRUE
return FALSE
/**
* Get the remaining deciseconds on a timer
*
* Arguments:
* * id a timerid or a /datum/timedevent
*/
/proc/timeleft(id)
if (!id)
return null
if (id == TIMER_ID_NULL)
CRASH("Tried to get timeleft of a null timerid. Use TIMER_STOPPABLE flag")
if (!istext(id))
if (istype(id, /datum/timedevent))
var/datum/timedevent/timer = id
return timer.timeToRun - world.time
//id is string
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
if (timer && !timer.spent)
return timer.timeToRun - world.time
return null
#undef BUCKET_LEN
#undef BUCKET_POS