// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0)
/**
* Proc to check if a datum allows proc calls on it
*
* Returns TRUE if you can call a proc on the datum, FALSE if you cant
*
*/
/datum/proc/CanProcCall(procname)
return TRUE
/datum/proc/can_vv_get(var_name)
return TRUE
/mob/can_vv_get(var_name)
var/static/list/protected_vars = list(
"lastKnownIP", "computer_id", "attack_log_old"
)
if(!check_rights(R_ADMIN, FALSE, usr) && (var_name in protected_vars))
return FALSE
return TRUE
/client/can_vv_get(var_name)
var/static/list/protected_vars = list(
"address", "computer_id", "connection", "jbh", "pm_tracker", "related_accounts_cid", "related_accounts_ip", "watchlisted"
)
if(!check_rights(R_ADMIN, FALSE, usr) && (var_name in protected_vars))
return FALSE
return TRUE
/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited
switch(var_name)
if("vars")
return FALSE
if("var_edited")
return FALSE
var_edited = TRUE
vars[var_name] = var_value
. = TRUE
/client/vv_edit_var(var_name, var_value) //called whenever a var is edited
switch(var_name)
// I know we will never be in a world where admins are editing client vars to let people bypass TOS
// But guess what, if I have the ability to overengineer something, I am going to do it
if("tos_consent")
return FALSE
// Dont fuck with this
if("cui_entries")
return FALSE
// or this
if("jbh")
return FALSE
if("vars")
return FALSE
if("var_edited")
return FALSE
var_edited = TRUE
vars[var_name] = var_value
. = TRUE
/datum/proc/vv_get_var(var_name)
switch(var_name)
if("attack_log_old", "debug_log")
return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE)
if("vars")
return debug_variable(var_name, list(), 0, src)
return debug_variable(var_name, vars[var_name], 0, src)
/client/vv_get_var(var_name)
switch(var_name)
if("vars")
return debug_variable(var_name, list(), 0, src)
return debug_variable(var_name, vars[var_name], 0, src)
/datum/proc/can_vv_delete()
return TRUE
//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down
//add seperaters by doing . += "---"
/datum/proc/vv_get_dropdown()
SHOULD_CALL_PARENT(TRUE)
. = list()
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION(VV_HK_PROC_CALL, "Call Proc")
VV_DROPDOWN_OPTION(VV_HK_MARK_OBJECT, "Mark Object")
VV_DROPDOWN_OPTION(VV_HK_JUMP_TO, "Jump to Object")
VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
VV_DROPDOWN_OPTION(VV_HK_TRAITMOD, "Modify Traits")
VV_DROPDOWN_OPTION(VV_HK_ADDCOMPONENT, "Add Component/Element")
VV_DROPDOWN_OPTION(VV_HK_REMOVECOMPONENT, "Remove Component/Element")
VV_DROPDOWN_OPTION(VV_HK_MASSREMOVECOMPONENT, "Mass Remove Component/Element")
VV_DROPDOWN_OPTION("", "---")
/**
* This proc is only called if everything topic-wise is verified. The only verifications that should happen here is things like permission checks!
* href_list is a reference, modifying it in these procs WILL change the rest of the proc in topic.dm of admin/view_variables!
* This proc is for "high level" actions like admin heal/set species/etc/etc. The low level debugging things should go in admin/view_variables/topic_basic.dm in case this runtimes.
*/
/datum/proc/vv_do_topic(list/href_list)
if(!usr || !usr.client || !usr.client.holder || !check_rights(R_VAREDIT))
return FALSE //This is VV, not to be called by anything else.
if(href_list[VV_HK_MODIFY_TRAITS])
usr.client.holder.modify_traits(src)
return TRUE
/datum/proc/vv_get_header()
. = list()
/client/vv_get_dropdown()
. = ..()
VV_DROPDOWN_OPTION(VV_HK_MANIPULATE_COLOR_MATRIX, "Manipulate Color Matrix")
VV_DROPDOWN_OPTION("", "---")
VV_DROPDOWN_OPTION(VV_HK_PROC_CALL, "Call Proc")
VV_DROPDOWN_OPTION(VV_HK_MARK_OBJECT, "Mark Object")
VV_DROPDOWN_OPTION(VV_HK_DELETE, "Delete")
VV_DROPDOWN_OPTION(VV_HK_MODIFY_TRAITS, "Modify Traits")
VV_DROPDOWN_OPTION("", "---")
/client/vv_do_topic(list/href_list)
. = ..()
if(!.)
return
if(href_list[VV_HK_MANIPULATE_COLOR_MATRIX])
if(!check_rights(R_DEBUG))
return
message_admins("[key_name_admin(usr)] is manipulating the colour matrix for [src]")
var/datum/ui_module/colour_matrix_tester/CMT = new(target=src)
CMT.ui_interact(usr)
/client/proc/debug_variables(datum/D in world)
set name = "\[Admin\] View Variables"
var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round.
if(!check_rights(R_ADMIN|R_VIEWRUNTIMES))
to_chat(usr, "You need to be an administrator to access this.")
return
if(!D)
return
var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/simple/vv)
asset_cache_datum.send(usr)
var/islist = islist(D)
var/isclient = isclient(D)
if(!islist && !isclient && !istype(D))
return
var/title = ""
var/icon/sprite
var/hash
var/refid
if(!islist)
refid = "[D.UID()]"
else
refid = "\ref[D]"
var/type = /list
if(!islist)
type = D.type
if(isatom(D))
var/atom/A = D
if(A.icon && A.icon_state)
sprite = new /icon(A.icon, A.icon_state)
hash = md5(A.icon)
hash = md5(hash + A.icon_state)
usr << browse_rsc(sprite, "vv[hash].png")
var/sprite_text
if(sprite)
sprite_text = "
E - Edit, tries to determine the variable type by itself. C - Change, asks you for the var type first. M - Mass modify: changes this variable for all objects of this type.
Search:
[variable_html.Join()]
"}
if(istype(D, /datum))
log_admin("[key_name(usr)] opened VV for [D] ([D.UID()])")
var/size_string = window_scaling ? "size=[475 * window_scaling]x[650 * window_scaling]" : "size=[475]x[650]"
usr << browse(html, "window=variables[refid];[size_string]")
#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing )
/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE)
var/header
if(owner)
if(islist(owner))
var/list/debug_list = owner
var/index = name
if(value)
name = debug_list[name] // name is really the index until this line
else
value = debug_list[name]
header = "
"} //TODO link to modify_transform wrapper for all matrices
/client/proc/view_var_Topic(href, href_list, hsrc)
//This should all be moved over to datum/admins/Topic() or something ~Carn
if(!check_rights(R_ADMIN|R_MOD, FALSE) \
&& !((href_list["datumrefresh"] || href_list["Vars"] || href_list["VarsList"]) && check_rights(R_VIEWRUNTIMES, FALSE)) \
&& !((href_list["proc_call"]) && check_rights(R_PROCCALL, FALSE)) \
)
return // clients with R_VIEWRUNTIMES can still refresh the window/view references/view lists. they cannot edit anything else however.
if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings
return
// Correct and warn about any VV topic links that aren't using UIDs
for(var/paramname in href_list)
if(findtext(href_list[paramname], "]_"))
continue // Contains UID-specific formatting, skip it
var/datum/D = locate(href_list[paramname])
if(!D)
continue
var/datuminfo = "[D]"
if(istype(D))
datuminfo = datum_info_line(D)
href_list[paramname] = D.UID()
else if(isclient(D))
var/client/C = D
href_list[paramname] = C.UID()
stack_trace("Found \\ref-based '[paramname]' param in VV topic for [datuminfo], should be UID: [href]")
if(href_list["Vars"])
debug_variables(locateUID(href_list["Vars"]))
var/target = GET_VV_TARGET
vv_core_topics(target, href_list, href)
if(isdatum(target))
var/datum/D = target
D.vv_do_topic(href_list)
// Refresh the VV if something asked us to
if(href_list["datumrefresh"])
var/datum/DAT = locateUID(href_list["datumrefresh"])
if(!istype(DAT, /datum) && !isclient(DAT))
return
src.debug_variables(DAT)
/client/proc/view_var_Topic_list(href, href_list, hsrc)
if(href_list["VarsList"])
debug_variables(locate(href_list["VarsList"]))
return TRUE
if(href_list["listedit"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listedit"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
mod_list(L, null, "list", "contents", index, autodetect_class = TRUE)
return TRUE
if(href_list["listchange"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listchange"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
mod_list(L, null, "list", "contents", index, autodetect_class = FALSE)
return TRUE
if(href_list["listremove"] && href_list["index"])
var/index = text2num(href_list["index"])
if(!index)
return TRUE
var/list/L = locate(href_list["listremove"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return
var/variable = L[index]
var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No")
if(prompt != "Yes")
return
L.Cut(index, index+1)
log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]")
log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]")
message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]")
return TRUE
if(href_list["listadd"])
var/list/L = locate(href_list["listadd"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
mod_list_add(L, null, "list", "contents")
return TRUE
if(href_list["listdupes"])
var/list/L = locate(href_list["listdupes"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
uniqueList_inplace(L)
log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES")
log_admin("[key_name(src)] modified list's contents: CLEAR DUPES")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES")
return TRUE
if(href_list["listnulls"])
var/list/L = locate(href_list["listnulls"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
listclearnulls(L)
log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS")
log_admin("[key_name(src)] modified list's contents: CLEAR NULLS")
message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS")
return TRUE
if(href_list["listlen"])
var/list/L = locate(href_list["listlen"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
var/value = vv_get_value(VV_NUM)
if(value["class"] != VV_NUM)
return TRUE
L.len = value["value"]
log_world("### ListVarEdit by [src]: /list len: [length(L)]")
log_admin("[key_name(src)] modified list's len: [length(L)]")
message_admins("[key_name_admin(src)] modified list's len: [length(L)]")
return TRUE
if(href_list["listshuffle"])
var/list/L = locate(href_list["listshuffle"])
if(!istype(L))
to_chat(usr, "This can only be used on instances of type /list")
return TRUE
shuffle_inplace(L)
log_world("### ListVarEdit by [src]: /list contents: SHUFFLE")
log_admin("[key_name(src)] modified list's contents: SHUFFLE")
message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE")
return TRUE
if(href_list["listrefresh"])
debug_variables(locate(href_list["listrefresh"]))
return TRUE
/client/proc/debug_global_variables(var_search as text)
set category = "Debug"
set name = "Debug Global Variables"
if(!check_rights(R_ADMIN|R_VIEWRUNTIMES))
to_chat(usr, "You need to be an administrator to access this.")
return
var_search = trim(var_search)
if(!var_search)
return
if(!GLOB.can_vv_get(var_search))
return
switch(var_search)
if("vars")
return FALSE
if(!(var_search in GLOB.vars))
to_chat(src, "GLOB.[var_search] does not exist.")
return
log_and_message_admins("is debugging the Global Variables controller with the search term \"[var_search]\"")
var/result = GLOB.vars[var_search]
if(islist(result) || isclient(result) || istype(result, /datum))
to_chat(src, "Now showing GLOB.[var_search].")
return debug_variables(result)
to_chat(src, "GLOB.[var_search] returned [result].")
#undef VV_HTML_ENCODE