diff --git a/code/datums/callback.dm b/code/datums/callback.dm index 5a45961284ca4..e1c5f1c535912 100644 --- a/code/datums/callback.dm +++ b/code/datums/callback.dm @@ -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() 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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/code/modules/admin/callproc.dm b/code/modules/admin/callproc.dm new file mode 100644 index 0000000000000..5b074744b884e --- /dev/null +++ b/code/modules/admin/callproc.dm @@ -0,0 +1,312 @@ +GLOBAL_DATUM_INIT(AdminProcCallHandler, /mob/proccall_handler, new()) +GLOBAL_PROTECT(AdminProcCallHandler) + +/// Used to handle proccalls called indirectly by an admin (e.g. tgs, circuits). +/// Has to be a mob because IsAdminAdvancedProcCall() checks usr, which is a mob variable. +/// So usr is set to this for any proccalls that don't have any usr mob/client to refer to. +/mob/proccall_handler + name = "ProcCall Handler" + desc = "If you are seeing this, tell a coder." + + var/list/callers = list() + + invisibility = INVISIBILITY_ABSTRACT + density = FALSE + +/// Adds a caller. +/mob/proccall_handler/proc/add_caller(caller_name) + callers += caller_name + name = "[initial(name)] ([callers.Join(") (")])" + +/// Removes a caller. +/mob/proccall_handler/proc/remove_caller(caller_name) + callers -= caller_name + name = "[initial(name)] ([callers.Join(") (")])" + +/mob/proccall_handler/Initialize(mapload) + . = ..() + if(GLOB.AdminProcCallHandler) + return INITIALIZE_HINT_QDEL + +/mob/proccall_handler/vv_edit_var(var_name, var_value) + if(GLOB.AdminProcCallHandler != src) + return ..() + return FALSE + +/mob/proccall_handler/CanProcCall(procname) + if(GLOB.AdminProcCallHandler != src) + return ..() + return FALSE + +// Shit will break if this is allowed to be deleted +/mob/proccall_handler/Destroy(force) + if(GLOB.AdminProcCallHandler != src) + return ..() + if(!force) + #ifndef UNIT_TESTS + stack_trace("Attempted deletion on [type] - [name], aborting.") + #endif + return QDEL_HINT_LETMELIVE + return ..() + +/** + * Handles a userless proccall, used by circuits. + * + * Arguments: + * * user - a string used to identify the user + * * target - the target to proccall on + * * proc - the proc to call + * * arguments - any arguments + */ +/proc/HandleUserlessProcCall(user, datum/target, procname, list/arguments) + if(IsAdminAdvancedProcCall()) + return + var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler + handler.add_caller(user) + var/lastusr = usr + usr = handler + . = WrapAdminProcCall(target, procname, arguments) + usr = lastusr + handler.remove_caller(user) + +/** + * Handles a userless sdql, used by circuits and tgs. + * + * Arguments: + * * user - a string used to identify the user + * * query_text - the query text + */ +/proc/HandleUserlessSDQL(user, query_text) + if(IsAdminAdvancedProcCall()) + return + var/mob/proccall_handler/handler = GLOB.AdminProcCallHandler + handler.add_caller(user) + var/lastusr = usr + usr = handler + . = world.SDQL2_query(query_text, user, user) + usr = lastusr + handler.remove_caller(user) + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) +GLOBAL_VAR_INIT(AdminProcCallCount, FALSE) +GLOBAL_PROTECT(AdminProcCallCount) +GLOBAL_VAR(LastAdminCalledTargetRef) +GLOBAL_PROTECT(LastAdminCalledTargetRef) +GLOBAL_VAR(LastAdminCalledTarget) +GLOBAL_PROTECT(LastAdminCalledTarget) +GLOBAL_VAR(LastAdminCalledProc) +GLOBAL_PROTECT(LastAdminCalledProc) +GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) +GLOBAL_PROTECT(AdminProcCallSpamPrevention) + + +/datum/admins/proc/proccall_atom(datum/A as null|area|mob|obj|turf) + set category = null + set name = "Atom ProcCall" + set waitfor = FALSE + + if(!check_rights(R_DEBUG)) + return + + /// Holds a reference to the client incase something happens to them + var/client/starting_client = usr.client + + var/procname = input("Proc name, eg: attack_hand", "Proc:", null) as text|null + if(!procname) + return + + if(!hascall(A, procname)) + to_chat(starting_client, "Error: callproc_datum(): type [A.type] has no proc named [procname].") + return + + var/list/lst = starting_client.holder.get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(starting_client, span_warning("Error: callproc_datum(): owner of proc no longer exists.")) + return + + log_admin("[key_name(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + message_admins("[ADMIN_TPMONTY(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + admin_ticket_log(A, "[key_name_admin(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + + var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc + . = starting_client.holder.get_callproc_returnval(returnval, procname) + if(.) + to_chat(usr, .) + + +/datum/admins/proc/proccall_advanced() + set category = "Debug" + set name = "Advanced ProcCall" + set waitfor = FALSE + + if(!check_rights(R_DEBUG)) + return + + var/datum/target = null + var/targetselected = 0 + var/returnval = null + + switch(alert("Proc owned by something?",, "Yes", "No")) + if("Yes") + targetselected = TRUE + var/list/value = usr.client.vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) + if(!value["class"] || !value["value"]) + return + target = value["value"] + if("No") + target = null + targetselected = FALSE + + var/procname = input("Proc path, eg: /proc/attack_hand(mob/living/user)") + if(!procname) + return + + //strip away everything but the proc name + var/list/proclist = splittext(procname, "/") + if(!length(proclist)) + return + + procname = proclist[length(proclist)] + + var/proctype = "proc" + if("verb" in proclist) + proctype = "verb" + + + var/procpath + if(targetselected && !hascall(target, procname)) + to_chat(usr, + type = MESSAGE_TYPE_DEBUG, + html = "Error: callproc(): type [target.type] has no [proctype] named [procname].",) + return + else if(!targetselected) + procpath = text2path("/[proctype]/[procname]") + if(!procpath) + to_chat(usr, + type = MESSAGE_TYPE_DEBUG, + html = "Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") + return + + var/list/lst = usr.client.holder.get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, + type = MESSAGE_TYPE_DEBUG, + html = "Error: callproc(): owner of proc no longer exists.") + return + log_admin("[key_name(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + message_admins("[ADMIN_TPMONTY(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + admin_ticket_log(target, "[key_name(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + log_admin("[key_name(usr)] called [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + message_admins("[ADMIN_TPMONTY(usr)] called [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") + returnval = WrapAdminProcCall(GLOBAL_PROC, procpath, lst) // Pass the lst as an argument list to the proc + + . = usr.client.holder.get_callproc_returnval(returnval, procname) + if(.) + to_chat(usr, .) + + +/datum/admins/proc/get_callproc_returnval(returnval, procname) + . = "" + if(islist(returnval)) + var/list/returnedlist = returnval + . = "" + if(length(returnedlist)) + var/assoc_check = returnedlist[1] + if(istext(assoc_check) && (returnedlist[assoc_check] != null)) + . += "[procname] returned an associative list:" + for(var/key in returnedlist) + . += "\n[key] = [returnedlist[key]]" + + else + . += "[procname] returned a list:" + for(var/elem in returnedlist) + . += "\n[elem]" + else + . = "[procname] returned an empty list" + . += "" + + else + . = "[procname] returned: [!isnull(returnval) ? returnval : "null"]" + + +/datum/admins/proc/get_callproc_args() + var/argnum = input("Number of arguments", "Number:", 0) as num|null + if(isnull(argnum)) + return + + . = list() + var/list/named_args = list() + while(argnum--) + var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null + var/value = usr.client.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + if (!value["class"]) + return + if(named_arg) + named_args[named_arg] = value["value"] + else + . += value["value"] + if(LAZYLEN(named_args)) + . += named_args + +/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target && procname == "Del") + to_chat(usr, "Calling Del() is not allowed") + return + + if(target != GLOBAL_PROC && !target.CanProcCall(procname)) + to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") + return + var/current_caller = GLOB.AdminProcCaller + var/user_identifier = usr ? usr.client?.ckey : GLOB.AdminProcCaller + var/is_remote_handler = usr == GLOB.AdminProcCallHandler + if(is_remote_handler) + user_identifier = GLOB.AdminProcCallHandler.name + + if(!user_identifier) + CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") + + if(!is_remote_handler && current_caller && current_caller != user_identifier) + to_chat(usr, span_adminnotice("Another set of admin called procs are still running. Try again later.")) + return + + GLOB.LastAdminCalledProc = procname + if(target != GLOBAL_PROC) + GLOB.LastAdminCalledTargetRef = REF(target) + + if(!is_remote_handler) + GLOB.AdminProcCaller = user_identifier //if this runtimes, too bad for you + ++GLOB.AdminProcCallCount + . = world.WrapAdminProcCall(target, procname, arguments) + GLOB.AdminProcCallCount-- + if(GLOB.AdminProcCallCount == 0) + GLOB.AdminProcCaller = null + else + . = world.WrapAdminProcCall(target, procname, arguments) + + +/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) + if(target == GLOBAL_PROC) + return call(procname)(arglist(arguments)) + else if(target != world) + return call(target, procname)(arglist(arguments)) + else + log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") + + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return (GLOB.AdminProcCaller && GLOB.AdminProcCaller == usr?.client?.ckey) || (GLOB.AdminProcCallHandler && usr == GLOB.AdminProcCallHandler) +#endif diff --git a/code/modules/admin/debug_verbs.dm b/code/modules/admin/debug_verbs.dm index 15bae5e6768e8..c6cb450121742 100644 --- a/code/modules/admin/debug_verbs.dm +++ b/code/modules/admin/debug_verbs.dm @@ -1,176 +1,3 @@ -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) -GLOBAL_VAR_INIT(AdminProcCallCount, FALSE) -GLOBAL_PROTECT(AdminProcCallCount) -GLOBAL_VAR(LastAdminCalledTargetRef) -GLOBAL_PROTECT(LastAdminCalledTargetRef) -GLOBAL_VAR(LastAdminCalledTarget) -GLOBAL_PROTECT(LastAdminCalledTarget) -GLOBAL_VAR(LastAdminCalledProc) -GLOBAL_PROTECT(LastAdminCalledProc) -GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention) -GLOBAL_PROTECT(AdminProcCallSpamPrevention) - - -/datum/admins/proc/proccall_atom(datum/A as null|area|mob|obj|turf) - set category = null - set name = "Atom ProcCall" - set waitfor = FALSE - - if(!check_rights(R_DEBUG)) - return - - /// Holds a reference to the client incase something happens to them - var/client/starting_client = usr.client - - var/procname = input("Proc name, eg: attack_hand", "Proc:", null) as text|null - if(!procname) - return - - if(!hascall(A, procname)) - to_chat(starting_client, "Error: callproc_datum(): type [A.type] has no proc named [procname].") - return - - var/list/lst = starting_client.holder.get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(starting_client, span_warning("Error: callproc_datum(): owner of proc no longer exists.")) - return - - log_admin("[key_name(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - message_admins("[ADMIN_TPMONTY(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - admin_ticket_log(A, "[key_name_admin(usr)] called [A]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - - var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc - . = starting_client.holder.get_callproc_returnval(returnval, procname) - if(.) - to_chat(usr, .) - - -/datum/admins/proc/proccall_advanced() - set category = "Debug" - set name = "Advanced ProcCall" - set waitfor = FALSE - - if(!check_rights(R_DEBUG)) - return - - var/datum/target = null - var/targetselected = 0 - var/returnval = null - - switch(alert("Proc owned by something?",, "Yes", "No")) - if("Yes") - targetselected = TRUE - var/list/value = usr.client.vv_get_value(default_class = VV_ATOM_REFERENCE, classes = list(VV_ATOM_REFERENCE, VV_DATUM_REFERENCE, VV_MOB_REFERENCE, VV_CLIENT)) - if(!value["class"] || !value["value"]) - return - target = value["value"] - if("No") - target = null - targetselected = FALSE - - var/procname = input("Proc path, eg: /proc/attack_hand(mob/living/user)") - if(!procname) - return - - //strip away everything but the proc name - var/list/proclist = splittext(procname, "/") - if(!length(proclist)) - return - - procname = proclist[length(proclist)] - - var/proctype = "proc" - if("verb" in proclist) - proctype = "verb" - - - var/procpath - if(targetselected && !hascall(target, procname)) - to_chat(usr, - type = MESSAGE_TYPE_DEBUG, - html = "Error: callproc(): type [target.type] has no [proctype] named [procname].",) - return - else if(!targetselected) - procpath = text2path("/[proctype]/[procname]") - if(!procpath) - to_chat(usr, - type = MESSAGE_TYPE_DEBUG, - html = "Error: callproc(): proc [procname] does not exist. (Did you forget the /proc/ part?)") - return - - var/list/lst = usr.client.holder.get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, - type = MESSAGE_TYPE_DEBUG, - html = "Error: callproc(): owner of proc no longer exists.") - return - log_admin("[key_name(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - message_admins("[ADMIN_TPMONTY(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - admin_ticket_log(target, "[key_name(usr)] called [target]'s [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - log_admin("[key_name(usr)] called [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - message_admins("[ADMIN_TPMONTY(usr)] called [procname]() with [length(lst) ? "the arguments [list2params(lst)]" : "no arguments"].") - returnval = WrapAdminProcCall(GLOBAL_PROC, procpath, lst) // Pass the lst as an argument list to the proc - - . = usr.client.holder.get_callproc_returnval(returnval, procname) - if(.) - to_chat(usr, .) - - -/datum/admins/proc/get_callproc_returnval(returnval, procname) - . = "" - if(islist(returnval)) - var/list/returnedlist = returnval - . = "" - if(length(returnedlist)) - var/assoc_check = returnedlist[1] - if(istext(assoc_check) && (returnedlist[assoc_check] != null)) - . += "[procname] returned an associative list:" - for(var/key in returnedlist) - . += "\n[key] = [returnedlist[key]]" - - else - . += "[procname] returned a list:" - for(var/elem in returnedlist) - . += "\n[elem]" - else - . = "[procname] returned an empty list" - . += "" - - else - . = "[procname] returned: [!isnull(returnval) ? returnval : "null"]" - - -/datum/admins/proc/get_callproc_args() - var/argnum = input("Number of arguments", "Number:", 0) as num|null - if(isnull(argnum)) - return - - . = list() - var/list/named_args = list() - while(argnum--) - var/named_arg = input("Leave blank for positional argument. Positional arguments will be considered as if they were added first.", "Named argument") as text|null - var/value = usr.client.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - if (!value["class"]) - return - if(named_arg) - named_args[named_arg] = value["value"] - else - . += value["value"] - if(LAZYLEN(named_args)) - . += named_args - - /datum/admins/proc/delete_all() set category = "Debug" set name = "Delete Instances" diff --git a/code/modules/admin/holder.dm b/code/modules/admin/holder.dm index b4088c7b985f2..71f796cc1dc67 100755 --- a/code/modules/admin/holder.dm +++ b/code/modules/admin/holder.dm @@ -614,55 +614,6 @@ GLOBAL_PROTECT(admin_verbs_log) i = 0 GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" - -/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target && procname == "Del") - to_chat(usr, "Calling Del() is not allowed") - return - - if(target != GLOBAL_PROC && !target.CanProcCall(procname)) - to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!") - return - - var/current_caller = GLOB.AdminProcCaller - var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller - if(!ckey) - CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]") - - if(current_caller && current_caller != ckey) - if(!GLOB.AdminProcCallSpamPrevention[ckey]) - to_chat(usr, span_adminnotice("Another set of admin called procs are still running, your proc will be run after theirs finish.")) - GLOB.AdminProcCallSpamPrevention[ckey] = TRUE - UNTIL(!GLOB.AdminProcCaller) - to_chat(usr, span_adminnotice("Running your proc")) - GLOB.AdminProcCallSpamPrevention -= ckey - else - UNTIL(!GLOB.AdminProcCaller) - - GLOB.LastAdminCalledProc = procname - if(target != GLOBAL_PROC) - GLOB.LastAdminCalledTargetRef = "[REF(target)]" - - GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you - ++GLOB.AdminProcCallCount - . = world.WrapAdminProcCall(target, procname, arguments) - if(--GLOB.AdminProcCallCount == 0) - GLOB.AdminProcCaller = null - - -/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments) - if(target == GLOBAL_PROC) - return call(procname)(arglist(arguments)) - else if(target != world) - return call(target, procname)(arglist(arguments)) - else - log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]") - - -/proc/IsAdminAdvancedProcCall() - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey - - /proc/GenTgsStealthKey() var/num = (rand(0,1000)) var/i = 0 diff --git a/code/modules/admin/verbs/chat_commands.dm b/code/modules/admin/verbs/chat_commands.dm index b95c53f049ee5..ad37443fae8f6 100644 --- a/code/modules/admin/verbs/chat_commands.dm +++ b/code/modules/admin/verbs/chat_commands.dm @@ -70,11 +70,7 @@ /datum/tgs_chat_command/sdql/Run(datum/tgs_chat_user/sender, params) - if(GLOB.AdminProcCaller) - return "Unable to run query, another admin proc call is in progress. Try again later." - GLOB.AdminProcCaller = "CHAT_[sender.friendly_name]" //_ won't show up in ckeys so it'll never match with a real admin - var/list/results = world.SDQL2_query(params, GLOB.AdminProcCaller, GLOB.AdminProcCaller, TRUE) - GLOB.AdminProcCaller = null + var/list/results = HandleUserlessSDQL(sender.friendly_name, params) if(!results) return "Query produced no output" var/list/text_res = results.Copy(1, 3) diff --git a/tgmc.dme b/tgmc.dme index 3c91dee52229d..8c2c502ed92e4 100755 --- a/tgmc.dme +++ b/tgmc.dme @@ -1105,6 +1105,7 @@ #include "code\game\verbs\who.dm" #include "code\modules\admin\admin_ranks.dm" #include "code\modules\admin\admin_verbs.dm" +#include "code\modules\admin\callproc.dm" #include "code\modules\admin\debug_verbs.dm" #include "code\modules\admin\fun_verbs.dm" #include "code\modules\admin\holder.dm"