Skip to content

Commit

Permalink
Port some admin proccall stuff and callback (#14008)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanmixo authored Sep 26, 2023
1 parent fbe42eb commit 11d1d1b
Show file tree
Hide file tree
Showing 6 changed files with 418 additions and 286 deletions.
163 changes: 104 additions & 59 deletions code/datums/callback.dm
Original file line number Diff line number Diff line change
@@ -1,53 +1,63 @@
/*
USAGE:
var/datum/callback/C = new(object|null, GLOBAL_PROC_REF(type/path|"procstring"), arg1, arg2, ... argn)
var/timerid = addtimer(C, time, timertype)
OR
var/timerid = addtimer(CALLBACK(object|null, GLOBAL_PROC_REF(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
See the note on proc typepath shortcuts
INVOKING THE CALLBACK:
var/result = C.Invoke(args, to, add) //additional args are added after the ones given when the callback was created
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.
OR
INVOKE_ASYNC(<CALLBACK args>) to immediately create and call InvokeAsync
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:
PROC_REF(procname)
Example:
CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(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:
PROC_REF(procname)
Example:
CALLBACK(src, PROC_REF(some_proc_here))
when the above doesn't apply:
PROC_REF(procname)
Example:
CALLBACK(src, PROC_REF(some_proc_here))
proc defined on a parent of a some type:
TYPE_PROC_REF(some_type, some_proc_here)
Other wise you will have to do the full typepath of the proc (/type/of/thing/proc/procname)
*/

/**
*# 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_REF(procname), arg1, arg2, ... argn)
* var/timerid = addtimer(C, time, timertype)
* you can also use the compiler define shorthand
* var/timerid = addtimer(CALLBACK(object|null, PROC_REF(procname), 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...)
*
* ### proc defined on current(src) object OR overridden at src or any of it's parents:
* PROC_REF(procname)
*
* `CALLBACK(src, PROC_REF(some_proc_here))`
*
* ### global proc
* GLOBAL_PROC_REF(procname)
*
* `CALLBACK(src, GLOBAL_PROC_REF(some_proc_here))`
*
*
* ### proc defined on some type
* TYPE_PROC_REF(/some/type/, some_proc_here)
*/
/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
Expand All @@ -72,6 +82,14 @@
stack_trace("Callbacks can not be qdeleted. If they are referenced, they must exist. ([object == GLOBAL_PROC ? GLOBAL_PROC : object.type] [delegate])")
return QDEL_HINT_LETMELIVE

/**
* 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
Expand All @@ -85,19 +103,34 @@
if (!object)
return

#if DM_VERSION <= 514
if(istext(object) && object != GLOBAL_PROC)
to_chat(usr, "[object] may be an external library. Calling external libraries is disallowed.", confidential = TRUE)
return
#endif

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(datum_flags & DF_VAR_EDITED)
if(usr != GLOB.AdminProcCallHandler && !usr?.client?.ckey) //This happens when a timer or the MC invokes a callback
return HandleUserlessProcCall(usr, object, delegate, calling_arguments)
return WrapAdminProcCall(object, delegate, calling_arguments)
if (object == GLOBAL_PROC)
return call(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(...)
set waitfor = FALSE

Expand All @@ -113,19 +146,29 @@
if (!object)
return

#if DM_VERSION <= 514
if(istext(object) && object != GLOBAL_PROC)
to_chat(usr, "[object] may be an external library. Calling external libraries is disallowed.", confidential = TRUE)
return
#endif

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(datum_flags & DF_VAR_EDITED)
if(usr != GLOB.AdminProcCallHandler && !usr?.client?.ckey) //This happens when a timer or the MC invokes a callback
return HandleUserlessProcCall(usr, object, delegate, calling_arguments)
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
Expand All @@ -150,15 +193,17 @@
if (savereturn)
finished[index] = rtn




//runs a list of callbacks asynchronously, returning once all of them return.
//callbacks can be repeated.
//callbacks-args is an optional list of argument lists, in the same order as the callbacks,
// the inner lists will be sent to the callbacks when invoked() as additional args.
//can optionly save and return a list of return values, in the same order as the original list of callbacks
//resolution is the number of byond ticks between checks.
/**
* 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
Expand Down
Loading

0 comments on commit 11d1d1b

Please sign in to comment.