This commit is contained in:
kevinz000
2020-01-02 23:24:47 -08:00
parent 82db38d5d6
commit 031f485e4d
7 changed files with 213 additions and 78 deletions

View File

@@ -1,4 +1,5 @@
#define GLOBAL_PROC "some_magic_bullshit" #define GLOBAL_PROC "some_magic_bullshit"
/// A shorthand for the callback datum, [documented here](datum/callback.html)
#define CALLBACK new /datum/callback #define CALLBACK new /datum/callback
#define INVOKE_ASYNC world.ImmediateInvokeAsync #define INVOKE_ASYNC world.ImmediateInvokeAsync
#define CALLBACK_NEW(typepath, args) CALLBACK(GLOBAL_PROC, /proc/___callbacknew, typepath, args)

View File

@@ -11,14 +11,12 @@
#define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails. #define QDEL_HINT_IFFAIL_FINDREFERENCE 6 //Above but only if gc fails.
//defines for the gc_destroyed var //defines for the gc_destroyed var
#define GC_QUEUE_PREQUEUE 1 #define GC_QUEUE_CHECK 1
#define GC_QUEUE_CHECK 2 #define GC_QUEUE_HARDDELETE 2
#define GC_QUEUE_HARDDELETE 3 #define GC_QUEUE_COUNT 2 //increase this when adding more steps.
#define GC_QUEUE_COUNT 3 //increase this when adding more steps.
#define GC_QUEUED_FOR_QUEUING -1 #define GC_QUEUED_FOR_QUEUING -1
#define GC_QUEUED_FOR_HARD_DEL -2 #define GC_CURRENTLY_BEING_QDELETED -2
#define GC_CURRENTLY_BEING_QDELETED -3
#define QDELING(X) (X.gc_destroyed) #define QDELING(X) (X.gc_destroyed)
#define QDELETED(X) (!X || QDELING(X)) #define QDELETED(X) (!X || QDELING(X))

View File

@@ -1452,6 +1452,37 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
else else
D.vars[var_name] = var_value D.vars[var_name] = var_value
#define TRAIT_CALLBACK_ADD(target, trait, source) CALLBACK(GLOBAL_PROC, /proc/___TraitAdd, ##target, ##trait, ##source)
#define TRAIT_CALLBACK_REMOVE(target, trait, source) CALLBACK(GLOBAL_PROC, /proc/___TraitRemove, ##target, ##trait, ##source)
///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
/proc/___TraitAdd(target,trait,source)
if(!target || !trait || !source)
return
if(islist(target))
for(var/i in target)
if(!isatom(i))
continue
var/atom/the_atom = i
ADD_TRAIT(the_atom,trait,source)
else if(isatom(target))
var/atom/the_atom2 = target
ADD_TRAIT(the_atom2,trait,source)
///DO NOT USE ___TraitAdd OR ___TraitRemove as a replacement for ADD_TRAIT / REMOVE_TRAIT defines. To be used explicitly for callback.
/proc/___TraitRemove(target,trait,source)
if(!target || !trait || !source)
return
if(islist(target))
for(var/i in target)
if(!isatom(i))
continue
var/atom/the_atom = i
REMOVE_TRAIT(the_atom,trait,source)
else if(isatom(target))
var/atom/the_atom2 = target
REMOVE_TRAIT(the_atom2,trait,source)
/proc/get_random_food() /proc/get_random_food()
var/list/blocked = list(/obj/item/reagent_containers/food/snacks, var/list/blocked = list(/obj/item/reagent_containers/food/snacks,
/obj/item/reagent_containers/food/snacks/store/bread, /obj/item/reagent_containers/food/snacks/store/bread,

View File

@@ -8,6 +8,7 @@
**/ **/
//This is the ABSOLUTE ONLY THING that should init globally like this //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 GLOBAL_REAL(Master, /datum/controller/master) = new
//THIS IS THE INIT ORDER //THIS IS THE INIT ORDER

View File

@@ -8,6 +8,10 @@ SUBSYSTEM_DEF(server_maint)
init_order = INIT_ORDER_SERVER_MAINT init_order = INIT_ORDER_SERVER_MAINT
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
var/list/currentrun 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) /datum/controller/subsystem/server_maint/Initialize(timeofday)
if (CONFIG_GET(flag/hub)) if (CONFIG_GET(flag/hub))
@@ -18,10 +22,30 @@ SUBSYSTEM_DEF(server_maint)
if(!resumed) if(!resumed)
if(listclearnulls(GLOB.clients)) if(listclearnulls(GLOB.clients))
log_world("Found a null in clients list!") 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() 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/list/currentrun = src.currentrun
var/round_started = SSticker.HasRoundStarted() var/round_started = SSticker.HasRoundStarted()

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_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_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 #define TIMER_ID_MAX (2**24) //max float with integer precision
@@ -38,15 +38,15 @@ SUBSYSTEM_DEF(timer)
/datum/controller/subsystem/timer/fire(resumed = FALSE) /datum/controller/subsystem/timer/fire(resumed = FALSE)
var/lit = last_invoke_tick 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 var/list/bucket_list = src.bucket_list
if(!bucket_count) if(!bucket_count)
last_invoke_tick = world.time 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 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) message_admins(msg)
WARNING(msg) WARNING(msg)
if(bucket_auto_reset) if(bucket_auto_reset)
@@ -121,7 +121,7 @@ SUBSYSTEM_DEF(timer)
if (!resumed) if (!resumed)
timer = null 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] var/datum/timedevent/head = bucket_list[practical_offset]
if (!timer || !head || timer == head) if (!timer || !head || timer == head)
head = bucket_list[practical_offset] head = bucket_list[practical_offset]
@@ -159,7 +159,7 @@ SUBSYSTEM_DEF(timer)
if (timer.timeToRun < head_offset) if (timer.timeToRun < head_offset)
bucket_resolution = null //force bucket recreation 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) if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync() timer.callBack.InvokeAsync()
@@ -169,9 +169,9 @@ SUBSYSTEM_DEF(timer)
qdel(timer) qdel(timer)
continue continue
if (timer.timeToRun < head_offset + TICKS2DS(practical_offset)) if (timer.timeToRun < head_offset + TICKS2DS(practical_offset-1))
bucket_resolution = null //force bucket recreation 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) if (timer.callBack && !timer.spent)
timer.callBack.InvokeAsync() timer.callBack.InvokeAsync()
spent += timer spent += timer
@@ -447,6 +447,7 @@ SUBSYSTEM_DEF(timer)
next.prev = src next.prev = src
prev.next = src prev.next = src
///Returns a string of the type of the callback for this timer
/datum/timedevent/proc/getcallingtype() /datum/timedevent/proc/getcallingtype()
. = "ERROR" . = "ERROR"
if (callBack.object == GLOBAL_PROC) if (callBack.object == GLOBAL_PROC)
@@ -454,6 +455,14 @@ SUBSYSTEM_DEF(timer)
else else
. = "[callBack.object.type]" . = "[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) /proc/addtimer(datum/callback/callback, wait = 0, flags = 0)
if (!callback) if (!callback)
CRASH("addtimer called without a callback") CRASH("addtimer called without a callback")
@@ -498,6 +507,12 @@ SUBSYSTEM_DEF(timer)
var/datum/timedevent/timer = new(callback, wait, flags, hash) var/datum/timedevent/timer = new(callback, wait, flags, hash)
return timer.id return timer.id
/**
* Delete a timer
*
* Arguments:
* * id a timerid or a /datum/timedevent
*/
/proc/deltimer(id) /proc/deltimer(id)
if (!id) if (!id)
return FALSE return FALSE
@@ -514,6 +529,26 @@ SUBSYSTEM_DEF(timer)
return TRUE return TRUE
return FALSE 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_LEN
#undef BUCKET_POS #undef BUCKET_POS

View File

@@ -1,55 +1,72 @@
/* /**
USAGE: *# Callback Datums
*A datum that holds a proc to be called on another object, used to track proccalls to other objects
var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn) *
var/timerid = addtimer(C, time, timertype) * ## USAGE
OR *
var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype) * ```
* var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
Note: proc strings can only be given for datum proc calls, global procs must be proc paths * var/timerid = addtimer(C, time, timertype)
Also proc strings are strongly advised against because they don't compile error if the proc stops existing * you can also use the compiler define shorthand
See the note on proc typepath shortcuts * var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
* ```
INVOKING THE CALLBACK: *
var/result = C.Invoke(args, to, add) //additional args are added after the ones given when the callback was created * Note: proc strings can only be given for datum proc calls, global procs must be proc paths
OR *
var/result = C.InvokeAsync(args, to, add) //Sleeps will not block, returns . on the first sleep (then continues on in the "background" after the sleep/block ends), otherwise operates normally. * Also proc strings are strongly advised against because they don't compile error if the proc stops existing
OR *
INVOKE_ASYNC(<CALLBACK args>) to immediately create and call InvokeAsync * In some cases you can provide a shortform of the procname, see the proc typepath shortcuts documentation below
*
PROC TYPEPATH SHORTCUTS (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...) * ## INVOKING THE CALLBACK
*`var/result = C.Invoke(args, to, add)` additional args are added after the ones given when the callback was created
global proc while in another global proc: *
.procname * `var/result = C.InvokeAsync(args, to, add)` Asyncronous - returns . on the first sleep then continues on in the background
Example: * after the sleep/block ends, otherwise operates normally.
CALLBACK(GLOBAL_PROC, .some_proc_here) *
* ## PROC TYPEPATH SHORTCUTS
proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents: * (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
.procname *
Example: * ### global proc while in another global proc:
CALLBACK(src, .some_proc_here) * .procname
*
* `CALLBACK(GLOBAL_PROC, .some_proc_here)`
when the above doesn't apply: *
.proc/procname * ### proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
Example: * .procname
CALLBACK(src, .proc/some_proc_here) *
* `CALLBACK(src, .some_proc_here)`
proc defined on a parent of a some type: *
/some/type/.proc/some_proc_here * ### when the above doesn't apply:
*.proc/procname
*
* `CALLBACK(src, .proc/some_proc_here)`
Other wise you will have to do the full typepath of the proc (/type/of/thing/proc/procname) *
*
*/ * proc defined on a parent of a some type
*
* `/some/type/.proc/some_proc_here`
*
* Otherwise you must always provide the full typepath of the proc (/type/of/thing/proc/procname)
*/
/datum/callback /datum/callback
///The object we will be calling the proc on
var/datum/object = GLOBAL_PROC var/datum/object = GLOBAL_PROC
///The proc we will be calling on the object
var/delegate var/delegate
///A list of arguments to pass into the proc
var/list/arguments var/list/arguments
///A weak reference to the user who triggered this callback
var/datum/weakref/user var/datum/weakref/user
/**
* Create a new callback datum
*
* Arguments
* * thingtocall the object to call the proc on
* * proctocall the proc to call on the target object
* * ... an optional list of extra arguments to pass to the proc
*/
/datum/callback/New(thingtocall, proctocall, ...) /datum/callback/New(thingtocall, proctocall, ...)
if (thingtocall) if (thingtocall)
object = thingtocall object = thingtocall
@@ -58,7 +75,14 @@
arguments = args.Copy(3) arguments = args.Copy(3)
if(usr) if(usr)
user = WEAKREF(usr) user = WEAKREF(usr)
/**
* Immediately Invoke proctocall on thingtocall, with waitfor set to false
*
* Arguments:
* * thingtocall Object to call on
* * proctocall Proc to call on that object
* * ... optional list of arguments to pass as arguments to the proc being called
*/
/world/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...) /world/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...)
set waitfor = FALSE set waitfor = FALSE
@@ -72,6 +96,14 @@
else else
call(thingtocall, proctocall)(arglist(calling_arguments)) call(thingtocall, proctocall)(arglist(calling_arguments))
/**
* Invoke this callback
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
*/
/datum/callback/proc/Invoke(...) /datum/callback/proc/Invoke(...)
if(!usr) if(!usr)
var/datum/weakref/W = user var/datum/weakref/W = user
@@ -97,7 +129,14 @@
return call(delegate)(arglist(calling_arguments)) return call(delegate)(arglist(calling_arguments))
return call(object, delegate)(arglist(calling_arguments)) return call(object, delegate)(arglist(calling_arguments))
//copy and pasted because fuck proc overhead /**
* Invoke this callback async (waitfor=false)
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
*/
/datum/callback/proc/InvokeAsync(...) /datum/callback/proc/InvokeAsync(...)
set waitfor = FALSE set waitfor = FALSE
@@ -125,7 +164,9 @@
return call(delegate)(arglist(calling_arguments)) return call(delegate)(arglist(calling_arguments))
return call(object, delegate)(arglist(calling_arguments)) return call(object, delegate)(arglist(calling_arguments))
/**
Helper datum for the select callbacks proc
*/
/datum/callback_select /datum/callback_select
var/list/finished var/list/finished
var/pendingcount var/pendingcount
@@ -150,15 +191,17 @@
if (savereturn) if (savereturn)
finished[index] = rtn finished[index] = rtn
/**
* Runs a list of callbacks asyncronously, returning only when all have finished
*
//runs a list of callbacks asynchronously, returning once all of them return. * Callbacks can be repeated, to call it multiple times
//callbacks can be repeated. *
//callbacks-args is an optional list of argument lists, in the same order as the callbacks, * Arguments:
// the inner lists will be sent to the callbacks when invoked() as additional args. * * list/callbacks the list of callbacks to be called
//can optionly save and return a list of return values, in the same order as the original list of callbacks * * list/callback_args the list of lists of arguments to pass into each callback
//resolution is the number of byond ticks between checks. * * savereturns Optionally save and return the list of returned values from each of the callbacks
* * resolution The number of byond ticks between each time you check if all callbacks are complete
*/
/proc/callback_select(list/callbacks, list/callback_args, savereturns = TRUE, resolution = 1) /proc/callback_select(list/callbacks, list/callback_args, savereturns = TRUE, resolution = 1)
if (!callbacks) if (!callbacks)
return return
@@ -178,3 +221,5 @@
sleep(resolution*world.tick_lag) sleep(resolution*world.tick_lag)
return CS.finished return CS.finished
/proc/___callbacknew(typepath, arguments)
new typepath(arglist(arguments))