Timer subsystem (#30906)

* timers

* amend comment
This commit is contained in:
DamianX
2021-10-02 00:37:00 +02:00
committed by GitHub
parent 6137d76dc5
commit 1b30afe9a1
10 changed files with 193 additions and 86 deletions

View File

@@ -30,6 +30,7 @@
#define SS_INIT_PERSISTENCE_MISC -99 #define SS_INIT_PERSISTENCE_MISC -99
#define SS_INIT_DAYNIGHT -200 #define SS_INIT_DAYNIGHT -200
#define SS_PRIORITY_TIMER 1000
#define SS_PRIORITY_WEATHER 210 #define SS_PRIORITY_WEATHER 210
#define SS_PRIORITY_TICKER 200 #define SS_PRIORITY_TICKER 200
#define SS_PRIORITY_MOB 150 #define SS_PRIORITY_MOB 150
@@ -67,6 +68,7 @@
#define SS_WAIT_TICKER 2 SECONDS #define SS_WAIT_TICKER 2 SECONDS
#define SS_WAIT_ENGINES 30 SECONDS #define SS_WAIT_ENGINES 30 SECONDS
#define SS_DISPLAY_TIMER -110
#define SS_DISPLAY_GARBAGE -100 #define SS_DISPLAY_GARBAGE -100
#define SS_DISPLAY_AIR -90 #define SS_DISPLAY_AIR -90
#define SS_DISPLAY_LIGHTING -80 #define SS_DISPLAY_LIGHTING -80

View File

@@ -43,13 +43,13 @@ var/cmp_field = "name"
var/atom/cmp_dist_origin=null var/atom/cmp_dist_origin=null
/proc/cmp_dist_asc(var/atom/a, var/atom/b) /proc/cmp_dist_asc(var/atom/a, var/atom/b)
return get_dist_squared(cmp_dist_origin, a) - get_dist_squared(cmp_dist_origin, b) return get_dist_squared(cmp_dist_origin, a) - get_dist_squared(cmp_dist_origin, b)
/proc/cmp_dist_desc(var/atom/a, var/atom/b) /proc/cmp_dist_desc(var/atom/a, var/atom/b)
return get_dist_squared(cmp_dist_origin, b) - get_dist_squared(cmp_dist_origin, a) return get_dist_squared(cmp_dist_origin, b) - get_dist_squared(cmp_dist_origin, a)
/proc/cmp_profile_avg_time_dsc(var/list/a, var/list/b) /proc/cmp_profile_avg_time_dsc(var/list/a, var/list/b)
return (b[PROFILE_ITEM_TIME]/(b[PROFILE_ITEM_COUNT] || 1)) - (a[PROFILE_ITEM_TIME]/(a[PROFILE_ITEM_COUNT] || 1)) return (b[PROFILE_ITEM_TIME]/(b[PROFILE_ITEM_COUNT] || 1)) - (a[PROFILE_ITEM_TIME]/(a[PROFILE_ITEM_COUNT] || 1))
/proc/cmp_profile_time_dsc(var/list/a, var/list/b) /proc/cmp_profile_time_dsc(var/list/a, var/list/b)
return b[PROFILE_ITEM_TIME] - a[PROFILE_ITEM_TIME] return b[PROFILE_ITEM_TIME] - a[PROFILE_ITEM_TIME]
@@ -60,4 +60,11 @@ var/atom/cmp_dist_origin=null
return a[cmp_field] - b[cmp_field] return a[cmp_field] - b[cmp_field]
/proc/cmp_list_by_element_asc(list/a, list/b) /proc/cmp_list_by_element_asc(list/a, list/b)
return b[cmp_field] - a[cmp_field] return b[cmp_field] - a[cmp_field]
/proc/cmp_timer(datum/timer/a, datum/timer/b)
var/a_when = a.when
var/b_when = b.when
if(a_when == b_when)
return b.id - a.id
return b_when - a_when

View File

@@ -1,5 +1,6 @@
/datum /datum
var/list/datum_components var/list/datum_components
var/list/active_timers
/datum/proc/initialize() /datum/proc/initialize()
return TRUE return TRUE

View File

@@ -0,0 +1,86 @@
//////////////////////
//PriorityQueue object
//////////////////////
//an ordered list, using the cmp proc to weight the list elements
/PriorityQueue
var/list/L //the actual queue
var/cmp //the weight function used to order the queue
/PriorityQueue/New(compare)
L = new()
cmp = compare
/PriorityQueue/proc/IsEmpty()
return !L.len
//add an element in the list,
//immediatly ordering it to its position
/PriorityQueue/proc/Enqueue(atom/A)
if(!L.len)
L += A
return
var/cmp = src.cmp
var/i = 1
var/j = L.len
var/mid
var/position
while(i < j)
mid = round((i+j)/2)
if(call(cmp)(L[mid], A) < 0)
i = mid + 1
else
j = mid
if(i == 1 || i == L.len)
position = call(cmp)(L[i], A) > 0 ? i : i + 1
else
position = i
L.Insert(position, A)
//removes and returns the first element in the queue
/PriorityQueue/proc/Dequeue()
ASSERT(L.len)
. = L[1]
Remove(.)
//removes an element
/PriorityQueue/proc/Remove(var/atom/A)
return L.Remove(A)
//returns a copy of the elements list
/PriorityQueue/proc/List()
RETURN_TYPE(/list)
return L.Copy()
//return the position of an element or 0 if not found
/PriorityQueue/proc/Seek(var/atom/A)
return L.Find(A)
//return the element at the i_th position
/PriorityQueue/proc/Get(var/i)
ASSERT(i < L.len && i > 1)
return L[i]
//replace the passed element at it's right position using the cmp proc
/PriorityQueue/proc/ReSort(var/atom/A)
var/i = Seek(A)
if (i == 0)
CRASH("[src] was seeking [A] but could not find it.")
while(i < L.len && call(cmp)(L[i],L[i+1]) > 0)
L.Swap(i,i+1)
i++
while(i > 1 && call(cmp)(L[i],L[i-1]) <= 0) //last inserted element being first in case of ties (optimization)
L.Swap(i,i-1)
i--
return 1
// uses Insertion sort
/PriorityQueue/reverse/Enqueue(atom/A)
var/i
L.Add(A)
i = L.len -1
while(i > 0 && call(cmp)(L[i],A) >= 0) //place the element at it's right position using the compare proc
L.Swap(i,i+1) //last inserted element being first in case of ties (optimization)
i--

View File

@@ -34,6 +34,8 @@
return FALSE return FALSE
else else
new_alert = new alert_type() new_alert = new alert_type()
new_alert.category = category
new_alert.owner = src
new_alert.override_alerts = override new_alert.override_alerts = override
if(override) if(override)
new_alert.timeout = null new_alert.timeout = null
@@ -56,19 +58,8 @@
hud_used.reorganize_alerts() hud_used.reorganize_alerts()
new_alert.transform = matrix(32, 6, MATRIX_TRANSLATE) new_alert.transform = matrix(32, 6, MATRIX_TRANSLATE)
animate(new_alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING) animate(new_alert, transform = matrix(), time = 2.5, easing = CUBIC_EASING)
if(new_alert.timeout)
alert_timeout(new_alert, category)
new_alert.timeout = world.time + new_alert.timeout - world.tick_lag
return new_alert return new_alert
/mob/proc/alert_timeout(var/obj/abstract/screen/alert/alert, category)
if(!istype(alert) || !category)
return
spawn(alert.timeout)
if(alert.timeout && alerts[category] == alert && world.time >= alert.timeout)
clear_alert(category)
// Proc to clear an existing alert. // Proc to clear an existing alert.
/mob/proc/clear_alert(category, clear_override = FALSE) /mob/proc/clear_alert(category, clear_override = FALSE)
var/obj/abstract/screen/alert/alert = alerts[category] var/obj/abstract/screen/alert/alert = alerts[category]
@@ -76,10 +67,6 @@
return FALSE return FALSE
if(alert.override_alerts && !clear_override) if(alert.override_alerts && !clear_override)
return FALSE return FALSE
alerts -= category
if(client && hud_used)
hud_used.reorganize_alerts()
client.screen -= alert
qdel(alert) qdel(alert)
/mob/proc/clear_all_alerts() /mob/proc/clear_all_alerts()
@@ -158,17 +145,33 @@ var/global/list/screen_alarms_locs = list(
icon = 'icons/mob/screen_alarms.dmi' icon = 'icons/mob/screen_alarms.dmi'
icon_state = "default" icon_state = "default"
mouse_opacity = TRUE mouse_opacity = TRUE
var/severity
var/mob/owner
var/category
var/timeout = null //If set to a number, this alert will clear itself after that many deciseconds var/timeout = null //If set to a number, this alert will clear itself after that many deciseconds
var/severity = null
var/override_alerts = FALSE //If it is overriding other alerts of the same type var/override_alerts = FALSE //If it is overriding other alerts of the same type
var/alerttooltipstyle = null var/alerttooltipstyle = null
var/emph = FALSE //Whether to have a flashy outline var/emph = FALSE //Whether to have a flashy outline
/obj/abstract/screen/alert/New() /obj/abstract/screen/alert/New()
..() ..()
if(timeout)
add_timer(new /callback(src, .proc/qdel_self), timeout)
if(emph) if(emph)
overlays.Add(image('icons/mob/screen_alarms.dmi', icon_state = "emph_outline")) overlays.Add(image('icons/mob/screen_alarms.dmi', icon_state = "emph_outline"))
/obj/abstract/screen/alert/proc/qdel_self()
qdel(src)
/obj/abstract/screen/alert/Destroy()
if(owner)
owner.alerts -= category
if(owner.client && owner.hud_used)
owner.hud_used.reorganize_alerts()
owner.client.screen -= src
owner = null
..()
/obj/abstract/screen/alert/Click(location, control, params) /obj/abstract/screen/alert/Click(location, control, params)
if(!usr || !usr.client) if(!usr || !usr.client)
return return

View File

@@ -262,6 +262,9 @@ List of hard deletions:"}
registered_events = null registered_events = null
gcDestroyed = "Bye, world!" gcDestroyed = "Bye, world!"
tag = null tag = null
for(var/timer in active_timers)
qdel(timer)
active_timers = null
/datum/var/gcDestroyed /datum/var/gcDestroyed

View File

@@ -74,7 +74,7 @@ var/global/list/pathmakers = list()
var/turf/start var/turf/start
var/turf/end var/turf/end
var/atom/target var/atom/target
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher var/PriorityQueue/open = new /PriorityQueue/reverse(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher
var/list/closed = new() //the closed list var/list/closed = new() //the closed list
var/list/path = null //the returned path, if any var/list/path = null //the returned path, if any
var/PathNode/cur //current processed turf var/PathNode/cur //current processed turf

View File

@@ -0,0 +1,68 @@
var/datum/subsystem/timer/SStimer
var/PriorityQueue/timers = new /PriorityQueue(/proc/cmp_timer)
var/list/timers_by_id = list()
var/timer_id = 1
/datum/subsystem/timer
name = "Timer"
display_order = SS_DISPLAY_TIMER
priority = SS_PRIORITY_TIMER
flags = SS_TICKER | SS_NO_INIT
wait = 1
/datum/subsystem/timer/New()
NEW_SS_GLOBAL(SStimer)
/datum/subsystem/timer/stat_entry()
..("T:[timers.L.len]")
/datum/subsystem/timer/fire(resumed = FALSE)
for(var/entry in timers.L)
var/datum/timer/timer = entry
if(timer.when > world.time)
return
timer.callback.invoke_async()
qdel(timer)
/proc/add_timer(callback/callback, wait)
var/when = world.time + wait
var/id = timer_id++
var/datum/timer/new_timer = new
new_timer.callback = callback
new_timer.when = when
new_timer.id = id
var/datum/thing_to_call = callback.thing_to_call
if(thing_to_call != GLOBAL_PROC)
if(!thing_to_call.active_timers)
thing_to_call.active_timers = list()
thing_to_call.active_timers += new_timer
timers.Enqueue(new_timer)
var/id_str = "[id]"
timers_by_id += id_str
timers_by_id[id_str] = new_timer
return id_str
#define del_timer(id) qdel(timers_by_id[id])
/datum/timer
var/callback/callback
var/when
var/id
/datum/timer/Destroy()
var/datum/thing_to_call = callback.thing_to_call
// Optimization or waste of time?
// If a datum has many timers it may be beneficial to check whether thing_to_call.gcDestroyed has been set,
// indicating there's no need to remove this timer from its active_timers list, as the list will be nulled right afterwards.
if(thing_to_call != GLOBAL_PROC && !thing_to_call.gcDestroyed)
thing_to_call.active_timers -= src
if(!thing_to_call.active_timers.len)
thing_to_call.active_timers = null
timers.L -= src
timers_by_id -= "[id]"
..()

View File

@@ -45,71 +45,6 @@ length to avoid portals or something i guess?? Not that they're counted right no
// 4) adjacent = "/turf/proc/AdjacentTurfsSpace" and distance = "/turf/proc/Distance" // 4) adjacent = "/turf/proc/AdjacentTurfsSpace" and distance = "/turf/proc/Distance"
// Same as 1), but check all turf, including unsimulated // Same as 1), but check all turf, including unsimulated
//////////////////////
//PriorityQueue object
//////////////////////
//an ordered list, using the cmp proc to weight the list elements
/PriorityQueue
var/list/L //the actual queue
var/cmp //the weight function used to order the queue
/PriorityQueue/New(compare)
L = new()
cmp = compare
/PriorityQueue/proc/IsEmpty()
return !L.len
//add an element in the list,
//immediatly ordering it to its position using Insertion sort
/PriorityQueue/proc/Enqueue(var/atom/A)
var/i
L.Add(A)
i = L.len -1
while(i > 0 && call(cmp)(L[i],A) >= 0) //place the element at it's right position using the compare proc
L.Swap(i,i+1) //last inserted element being first in case of ties (optimization)
i--
//removes and returns the first element in the queue
/PriorityQueue/proc/Dequeue()
ASSERT(L.len)
. = L[1]
Remove(.)
return .
//removes an element
/PriorityQueue/proc/Remove(var/atom/A)
return L.Remove(A)
//returns a copy of the elements list
/PriorityQueue/proc/List()
RETURN_TYPE(/list)
var/list/ret = L.Copy()
return ret
//return the position of an element or 0 if not found
/PriorityQueue/proc/Seek(var/atom/A)
return L.Find(A)
//return the element at the i_th position
/PriorityQueue/proc/Get(var/i)
ASSERT(i < L.len && i > 1)
return L[i]
//replace the passed element at it's right position using the cmp proc
/PriorityQueue/proc/ReSort(var/atom/A)
var/i = Seek(A)
if (i == 0)
CRASH("[src] was seeking [A] but could not find it.")
while(i < L.len && call(cmp)(L[i],L[i+1]) > 0)
L.Swap(i,i+1)
i++
while(i > 1 && call(cmp)(L[i],L[i-1]) <= 0) //last inserted element being first in case of ties (optimization)
L.Swap(i,i-1)
i--
return 1
////////////////////// //////////////////////
//PathNode object //PathNode object
////////////////////// //////////////////////
@@ -202,7 +137,7 @@ length to avoid portals or something i guess?? Not that they're counted right no
/proc/quick_AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null, var/reference) /proc/quick_AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minnodedist,id=null, var/turf/exclude=null, var/reference)
ASSERT(!istype(end,/area)) //Because yeah some things might be doing this and we want to know what ASSERT(!istype(end,/area)) //Because yeah some things might be doing this and we want to know what
. = list() // In case of failure/runtimes, we want to return a list. . = list() // In case of failure/runtimes, we want to return a list.
var/PriorityQueue/open = new /PriorityQueue(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher var/PriorityQueue/open = new /PriorityQueue/reverse(/proc/PathWeightCompare) //the open list, ordered using the PathWeightCompare proc, from lower f to higher
var/list/closed = new() //the closed list var/list/closed = new() //the closed list
var/list/path = list() //the returned path, if any var/list/path = list() //the returned path, if any
var/PathNode/cur //current processed turf var/PathNode/cur //current processed turf

View File

@@ -101,6 +101,7 @@
#include "code\__HELPERS\maths.dm" #include "code\__HELPERS\maths.dm"
#include "code\__HELPERS\mobs.dm" #include "code\__HELPERS\mobs.dm"
#include "code\__HELPERS\names.dm" #include "code\__HELPERS\names.dm"
#include "code\__HELPERS\priority_queue.dm"
#include "code\__HELPERS\sanitize_values.dm" #include "code\__HELPERS\sanitize_values.dm"
#include "code\__HELPERS\text.dm" #include "code\__HELPERS\text.dm"
#include "code\__HELPERS\time.dm" #include "code\__HELPERS\time.dm"
@@ -244,6 +245,7 @@
#include "code\controllers\subsystem\supply_shuttle.dm" #include "code\controllers\subsystem\supply_shuttle.dm"
#include "code\controllers\subsystem\tgui.dm" #include "code\controllers\subsystem\tgui.dm"
#include "code\controllers\subsystem\ticker.dm" #include "code\controllers\subsystem\ticker.dm"
#include "code\controllers\subsystem\timer.dm"
#include "code\controllers\subsystem\vote.dm" #include "code\controllers\subsystem\vote.dm"
#include "code\controllers\subsystem\weather.dm" #include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\init\assets.dm" #include "code\controllers\subsystem\init\assets.dm"