mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
226 lines
6.8 KiB
Plaintext
226 lines
6.8 KiB
Plaintext
/**
|
|
*# Callback Datums
|
|
*A datum that holds a proc to be called on another object, used to track proccalls to other objects
|
|
*
|
|
* ## USAGE
|
|
*
|
|
* ```
|
|
* var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
|
|
* var/timerid = addtimer(C, time, timertype)
|
|
* you can also use the compiler define shorthand
|
|
* var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
|
|
* ```
|
|
*
|
|
* Note: proc strings can only be given for datum proc calls, global procs must be proc paths
|
|
*
|
|
* Also proc strings are strongly advised against because they don't compile error if the proc stops existing
|
|
*
|
|
* In some cases you can provide a shortform of the procname, see the proc typepath shortcuts documentation below
|
|
*
|
|
* ## INVOKING THE CALLBACK
|
|
*`var/result = C.Invoke(args, to, add)` additional args are added after the ones given when the callback was created
|
|
*
|
|
* `var/result = C.InvokeAsync(args, to, add)` Asyncronous - returns . on the first sleep then continues on in the background
|
|
* after the sleep/block ends, otherwise operates normally.
|
|
*
|
|
* ## PROC TYPEPATH SHORTCUTS
|
|
* (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
|
|
*
|
|
* ### global proc while in another global proc:
|
|
* .procname
|
|
*
|
|
* `CALLBACK(GLOBAL_PROC, .some_proc_here)`
|
|
*
|
|
* ### proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
|
|
* .procname
|
|
*
|
|
* `CALLBACK(src, .some_proc_here)`
|
|
*
|
|
* ### when the above doesn't apply:
|
|
*.proc/procname
|
|
*
|
|
* `CALLBACK(src, .proc/some_proc_here)`
|
|
*
|
|
*
|
|
* 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
|
|
|
|
///The object we will be calling the proc on
|
|
var/datum/object = GLOBAL_PROC
|
|
///The proc we will be calling on the object
|
|
var/delegate
|
|
///A list of arguments to pass into the proc
|
|
var/list/arguments
|
|
///A weak reference to the user who triggered this callback
|
|
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, ...)
|
|
if (thingtocall)
|
|
object = thingtocall
|
|
delegate = proctocall
|
|
if (length(args) > 2)
|
|
arguments = args.Copy(3)
|
|
if(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, ...)
|
|
set waitfor = FALSE
|
|
|
|
if (!thingtocall)
|
|
return
|
|
|
|
var/list/calling_arguments = length(args) > 2 ? args.Copy(3) : null
|
|
|
|
if (thingtocall == GLOBAL_PROC)
|
|
call(proctocall)(arglist(calling_arguments))
|
|
else
|
|
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][/proc/WrapAdminProcCall]
|
|
*/
|
|
/datum/callback/proc/Invoke(...)
|
|
if(!usr)
|
|
var/datum/weakref/W = user
|
|
if(W)
|
|
var/mob/M = W.resolve()
|
|
if(M)
|
|
if (length(args))
|
|
return world.PushUsr(arglist(list(M, src) + args))
|
|
return world.PushUsr(M, src)
|
|
|
|
if (!object)
|
|
return
|
|
|
|
var/list/calling_arguments = arguments
|
|
if (length(args))
|
|
if (length(arguments))
|
|
calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean
|
|
else
|
|
calling_arguments = args
|
|
if(datum_flags & DF_VAR_EDITED)
|
|
return WrapAdminProcCall(object, delegate, calling_arguments)
|
|
if (object == GLOBAL_PROC)
|
|
return call(delegate)(arglist(calling_arguments))
|
|
return call(object, delegate)(arglist(calling_arguments))
|
|
|
|
/**
|
|
* 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(...)
|
|
set waitfor = FALSE
|
|
|
|
if(!usr)
|
|
var/datum/weakref/W = user
|
|
if(W)
|
|
var/mob/M = W.resolve()
|
|
if(M)
|
|
if (length(args))
|
|
return world.PushUsr(arglist(list(M, src) + args))
|
|
return world.PushUsr(M, src)
|
|
|
|
if (!object)
|
|
return
|
|
|
|
var/list/calling_arguments = arguments
|
|
if (length(args))
|
|
if (length(arguments))
|
|
calling_arguments = calling_arguments + args //not += so that it creates a new list so the arguments list stays clean
|
|
else
|
|
calling_arguments = args
|
|
if(datum_flags & DF_VAR_EDITED)
|
|
return WrapAdminProcCall(object, delegate, calling_arguments)
|
|
if (object == GLOBAL_PROC)
|
|
return call(delegate)(arglist(calling_arguments))
|
|
return call(object, delegate)(arglist(calling_arguments))
|
|
|
|
/**
|
|
Helper datum for the select callbacks proc
|
|
*/
|
|
/datum/callback_select
|
|
var/list/finished
|
|
var/pendingcount
|
|
var/total
|
|
|
|
/datum/callback_select/New(count, savereturns)
|
|
total = count
|
|
if (savereturns)
|
|
finished = new(count)
|
|
|
|
|
|
/datum/callback_select/proc/invoke_callback(index, datum/callback/callback, list/callback_args, savereturn = TRUE)
|
|
set waitfor = FALSE
|
|
if (!callback || !istype(callback))
|
|
//This check only exists because the alternative is callback_select would block forever if given invalid data
|
|
CRASH("invalid callback passed to invoke_callback")
|
|
if (!length(callback_args))
|
|
callback_args = list()
|
|
pendingcount++
|
|
var/rtn = callback.Invoke(arglist(callback_args))
|
|
pendingcount--
|
|
if (savereturn)
|
|
finished[index] = rtn
|
|
|
|
/**
|
|
* Runs a list of callbacks asyncronously, returning only when all have finished
|
|
*
|
|
* Callbacks can be repeated, to call it multiple times
|
|
*
|
|
* Arguments:
|
|
* * list/callbacks the list of callbacks to be called
|
|
* * list/callback_args the list of lists of arguments to pass into each callback
|
|
* * 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)
|
|
if (!callbacks)
|
|
return
|
|
var/count = length(callbacks)
|
|
if (!count)
|
|
return
|
|
if (!callback_args)
|
|
callback_args = list()
|
|
|
|
callback_args.len = count
|
|
|
|
var/datum/callback_select/CS = new(count, savereturns)
|
|
for (var/i in 1 to count)
|
|
CS.invoke_callback(i, callbacks[i], callback_args[i], savereturns)
|
|
|
|
while(CS.pendingcount)
|
|
sleep(resolution*world.tick_lag)
|
|
return CS.finished
|
|
|
|
/proc/___callbacknew(typepath, arguments)
|
|
new typepath(arglist(arguments))
|