|
|
|
|
@@ -1,9 +1,9 @@
|
|
|
|
|
/// Controls how many buckets should be kept, each representing a tick. (1 minutes worth)
|
|
|
|
|
#define BUCKET_LEN (world.fps*1*60)
|
|
|
|
|
/// Helper for getting the correct bucket for a given timer
|
|
|
|
|
#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
|
|
|
|
|
#define BUCKET_POS(timer) (((round((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
|
|
|
|
|
/// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue
|
|
|
|
|
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
|
|
|
|
|
#define TIMER_MAX(timer_ss) (world.time + TICKS2DS(min(BUCKET_LEN-(timer_ss.practical_offset-DS2TICKS(world.time - timer_ss.head_offset))-1, BUCKET_LEN-1)))
|
|
|
|
|
/// Max float with integer precision
|
|
|
|
|
#define TIMER_ID_MAX (2**24)
|
|
|
|
|
|
|
|
|
|
@@ -43,6 +43,8 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
var/list/clienttime_timers = list()
|
|
|
|
|
/// Contains the last time that a timer's callback was invoked, or the last tick the SS fired if no timers are being processed
|
|
|
|
|
var/last_invoke_tick = 0
|
|
|
|
|
/// Keeps track of the next index to work on for client timers
|
|
|
|
|
var/next_clienttime_timer_index = 0
|
|
|
|
|
/// Contains the last time that a warning was issued for not invoking callbacks
|
|
|
|
|
var/static/last_invoke_warning = 0
|
|
|
|
|
/// Boolean operator controlling if the timer SS will automatically reset buckets if it fails to invoke callbacks for an extended period of time
|
|
|
|
|
@@ -100,7 +102,6 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
log_world(to_log.Join("\n"))
|
|
|
|
|
|
|
|
|
|
// Process client-time timers
|
|
|
|
|
var/static/next_clienttime_timer_index = 0
|
|
|
|
|
if (next_clienttime_timer_index)
|
|
|
|
|
clienttime_timers.Cut(1, next_clienttime_timer_index+1)
|
|
|
|
|
next_clienttime_timer_index = 0
|
|
|
|
|
@@ -181,7 +182,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
var/i = 0
|
|
|
|
|
for (i in 1 to length(second_queue))
|
|
|
|
|
timer = second_queue[i]
|
|
|
|
|
if (timer.timeToRun >= TIMER_MAX)
|
|
|
|
|
if (timer.timeToRun >= TIMER_MAX(src))
|
|
|
|
|
i--
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
@@ -275,7 +276,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
// Check that the TTR is within the range covered by buckets, when exceeded we've finished
|
|
|
|
|
if (timer.timeToRun >= TIMER_MAX)
|
|
|
|
|
if (timer.timeToRun >= TIMER_MAX(src))
|
|
|
|
|
i--
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
@@ -348,8 +349,10 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
var/datum/timedevent/next
|
|
|
|
|
/// Previous timed event in the bucket
|
|
|
|
|
var/datum/timedevent/prev
|
|
|
|
|
/// The timer subsystem this event is associated with
|
|
|
|
|
var/datum/controller/subsystem/timer/timer_subsystem
|
|
|
|
|
|
|
|
|
|
/datum/timedevent/New(datum/callback/callBack, wait, flags, hash, source)
|
|
|
|
|
/datum/timedevent/New(datum/callback/callBack, wait, flags, datum/controller/subsystem/timer/timer_subsystem, hash, source)
|
|
|
|
|
var/static/nextid = 1
|
|
|
|
|
id = TIMER_ID_NULL
|
|
|
|
|
src.callBack = callBack
|
|
|
|
|
@@ -357,13 +360,14 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
src.flags = flags
|
|
|
|
|
src.hash = hash
|
|
|
|
|
src.source = source
|
|
|
|
|
src.timer_subsystem = timer_subsystem || SStimer
|
|
|
|
|
|
|
|
|
|
// Determine time at which the timer's callback should be invoked
|
|
|
|
|
timeToRun = (flags & TIMER_CLIENT_TIME ? REALTIMEOFDAY : world.time) + wait
|
|
|
|
|
|
|
|
|
|
// Include the timer in the hash table if the timer is unique
|
|
|
|
|
if (flags & TIMER_UNIQUE)
|
|
|
|
|
SStimer.hashes[hash] = src
|
|
|
|
|
timer_subsystem.hashes[hash] = src
|
|
|
|
|
|
|
|
|
|
// Generate ID for the timer if the timer is stoppable, include in the timer id dictionary
|
|
|
|
|
if (flags & TIMER_STOPPABLE)
|
|
|
|
|
@@ -372,9 +376,9 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
nextid += min(1, 2 ** round(nextid / SHORT_REAL_LIMIT))
|
|
|
|
|
else
|
|
|
|
|
nextid++
|
|
|
|
|
SStimer.timer_id_dict[id] = src
|
|
|
|
|
timer_subsystem.timer_id_dict[id] = src
|
|
|
|
|
|
|
|
|
|
if ((timeToRun < world.time || timeToRun < SStimer.head_offset) && !(flags & TIMER_CLIENT_TIME))
|
|
|
|
|
if ((timeToRun < world.time || timeToRun < timer_subsystem.head_offset) && !(flags & TIMER_CLIENT_TIME))
|
|
|
|
|
CRASH("Invalid timer state: Timer created that would require a backtrack to run (addtimer would never let this happen): [SStimer.get_timer_debug_string(src)]")
|
|
|
|
|
|
|
|
|
|
if (callBack.object != GLOBAL_PROC && !QDESTROYING(callBack.object))
|
|
|
|
|
@@ -385,7 +389,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
/datum/timedevent/Destroy()
|
|
|
|
|
..()
|
|
|
|
|
if (flags & TIMER_UNIQUE && hash)
|
|
|
|
|
SStimer.hashes -= hash
|
|
|
|
|
timer_subsystem.hashes -= hash
|
|
|
|
|
|
|
|
|
|
if (callBack && callBack.object && callBack.object != GLOBAL_PROC && callBack.object.active_timers)
|
|
|
|
|
callBack.object.active_timers -= src
|
|
|
|
|
@@ -394,12 +398,12 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
callBack = null
|
|
|
|
|
|
|
|
|
|
if (flags & TIMER_STOPPABLE)
|
|
|
|
|
SStimer.timer_id_dict -= id
|
|
|
|
|
timer_subsystem.timer_id_dict -= id
|
|
|
|
|
|
|
|
|
|
if (flags & TIMER_CLIENT_TIME)
|
|
|
|
|
if (!spent)
|
|
|
|
|
spent = world.time
|
|
|
|
|
SStimer.clienttime_timers -= src
|
|
|
|
|
timer_subsystem.clienttime_timers -= src
|
|
|
|
|
return QDEL_HINT_IWILLGC
|
|
|
|
|
|
|
|
|
|
if (!spent)
|
|
|
|
|
@@ -423,8 +427,8 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
|
|
|
|
|
// Store local references for the bucket list and secondary queue
|
|
|
|
|
// This is faster than referencing them from the datum itself
|
|
|
|
|
var/list/bucket_list = SStimer.bucket_list
|
|
|
|
|
var/list/second_queue = SStimer.second_queue
|
|
|
|
|
var/list/bucket_list = timer_subsystem.bucket_list
|
|
|
|
|
var/list/second_queue = timer_subsystem.second_queue
|
|
|
|
|
|
|
|
|
|
// Attempt to get the head of the bucket
|
|
|
|
|
var/datum/timedevent/buckethead
|
|
|
|
|
@@ -436,14 +440,14 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
// into an existing bucket, or is otherwise not present in the secondary queue
|
|
|
|
|
if(buckethead == src)
|
|
|
|
|
bucket_list[bucketpos] = next
|
|
|
|
|
SStimer.bucket_count--
|
|
|
|
|
else if(timeToRun < TIMER_MAX)
|
|
|
|
|
SStimer.bucket_count--
|
|
|
|
|
timer_subsystem.bucket_count--
|
|
|
|
|
else if(timeToRun < TIMER_MAX(timer_subsystem))
|
|
|
|
|
timer_subsystem.bucket_count--
|
|
|
|
|
else
|
|
|
|
|
var/l = length(second_queue)
|
|
|
|
|
second_queue -= src
|
|
|
|
|
if(l == length(second_queue))
|
|
|
|
|
SStimer.bucket_count--
|
|
|
|
|
timer_subsystem.bucket_count--
|
|
|
|
|
|
|
|
|
|
// Remove the timed event from the bucket, ensuring to maintain
|
|
|
|
|
// the integrity of the bucket's list if relevant
|
|
|
|
|
@@ -473,20 +477,20 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
// Check if this timed event should be diverted to the client time bucket, or the secondary queue
|
|
|
|
|
var/list/L
|
|
|
|
|
if (flags & TIMER_CLIENT_TIME)
|
|
|
|
|
L = SStimer.clienttime_timers
|
|
|
|
|
else if (timeToRun >= TIMER_MAX)
|
|
|
|
|
L = SStimer.second_queue
|
|
|
|
|
L = timer_subsystem.clienttime_timers
|
|
|
|
|
else if (timeToRun >= TIMER_MAX(timer_subsystem))
|
|
|
|
|
L = timer_subsystem.second_queue
|
|
|
|
|
if(L)
|
|
|
|
|
BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
// Get a local reference to the bucket list, this is faster than referencing the datum
|
|
|
|
|
var/list/bucket_list = SStimer.bucket_list
|
|
|
|
|
var/list/bucket_list = timer_subsystem.bucket_list
|
|
|
|
|
|
|
|
|
|
// Find the correct bucket for this timed event
|
|
|
|
|
var/bucket_pos = BUCKET_POS(src)
|
|
|
|
|
var/datum/timedevent/bucket_head = bucket_list[bucket_pos]
|
|
|
|
|
SStimer.bucket_count++
|
|
|
|
|
timer_subsystem.bucket_count++
|
|
|
|
|
|
|
|
|
|
// If there is no timed event at this position, then the bucket is 'empty'
|
|
|
|
|
// and we can just set this event to that position
|
|
|
|
|
@@ -522,7 +526,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
* * 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, file, line)
|
|
|
|
|
/proc/_addtimer(datum/callback/callback, wait = 0, flags = 0, datum/controller/subsystem/timer/timer_subsystem, file, line)
|
|
|
|
|
if (!callback)
|
|
|
|
|
CRASH("addtimer called without a callback")
|
|
|
|
|
|
|
|
|
|
@@ -538,6 +542,8 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
if(wait >= INFINITY)
|
|
|
|
|
CRASH("Attempted to create timer with INFINITY delay")
|
|
|
|
|
|
|
|
|
|
timer_subsystem = timer_subsystem || SStimer
|
|
|
|
|
|
|
|
|
|
// Generate hash if relevant for timed events with the TIMER_UNIQUE flag
|
|
|
|
|
var/hash
|
|
|
|
|
if (flags & TIMER_UNIQUE)
|
|
|
|
|
@@ -547,7 +553,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
hashlist += callback.arguments
|
|
|
|
|
hash = hashlist.Join("|||||||")
|
|
|
|
|
|
|
|
|
|
var/datum/timedevent/hash_timer = SStimer.hashes[hash]
|
|
|
|
|
var/datum/timedevent/hash_timer = timer_subsystem.hashes[hash]
|
|
|
|
|
if(hash_timer)
|
|
|
|
|
if (hash_timer.spent) // it's pending deletion, pretend it doesn't exist.
|
|
|
|
|
hash_timer.hash = null // but keep it from accidentally deleting us
|
|
|
|
|
@@ -562,7 +568,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
else if(flags & TIMER_OVERRIDE)
|
|
|
|
|
stack_trace("TIMER_OVERRIDE used without TIMER_UNIQUE")
|
|
|
|
|
|
|
|
|
|
var/datum/timedevent/timer = new(callback, wait, flags, hash, file && "[file]:[line]")
|
|
|
|
|
var/datum/timedevent/timer = new(callback, wait, flags, timer_subsystem, hash, file && "[file]:[line]")
|
|
|
|
|
return timer.id
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -571,7 +577,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
* Arguments:
|
|
|
|
|
* * id a timerid or a /datum/timedevent
|
|
|
|
|
*/
|
|
|
|
|
/proc/deltimer(id)
|
|
|
|
|
/proc/deltimer(id, datum/controller/subsystem/timer/timer_subsystem)
|
|
|
|
|
if (!id)
|
|
|
|
|
return FALSE
|
|
|
|
|
if (id == TIMER_ID_NULL)
|
|
|
|
|
@@ -579,8 +585,9 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
if (istype(id, /datum/timedevent))
|
|
|
|
|
qdel(id)
|
|
|
|
|
return TRUE
|
|
|
|
|
timer_subsystem = timer_subsystem || SStimer
|
|
|
|
|
//id is string
|
|
|
|
|
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
|
|
|
|
|
var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id]
|
|
|
|
|
if (timer && (!timer.spent || timer.flags & TIMER_DELETE_ME))
|
|
|
|
|
qdel(timer)
|
|
|
|
|
return TRUE
|
|
|
|
|
@@ -592,7 +599,7 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
* Arguments:
|
|
|
|
|
* * id a timerid or a /datum/timedevent
|
|
|
|
|
*/
|
|
|
|
|
/proc/timeleft(id)
|
|
|
|
|
/proc/timeleft(id, datum/controller/subsystem/timer/timer_subsystem)
|
|
|
|
|
if (!id)
|
|
|
|
|
return null
|
|
|
|
|
if (id == TIMER_ID_NULL)
|
|
|
|
|
@@ -600,8 +607,9 @@ SUBSYSTEM_DEF(timer)
|
|
|
|
|
if (istype(id, /datum/timedevent))
|
|
|
|
|
var/datum/timedevent/timer = id
|
|
|
|
|
return timer.timeToRun - world.time
|
|
|
|
|
timer_subsystem = timer_subsystem || SStimer
|
|
|
|
|
//id is string
|
|
|
|
|
var/datum/timedevent/timer = SStimer.timer_id_dict[id]
|
|
|
|
|
var/datum/timedevent/timer = timer_subsystem.timer_id_dict[id]
|
|
|
|
|
return (timer && !timer.spent) ? timer.timeToRun - world.time : null
|
|
|
|
|
|
|
|
|
|
#undef BUCKET_LEN
|
|
|
|
|
|