// 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 = "" var/list/header = islist ? list("/list") : D.vv_get_header() var/formatted_type = "[type]" if(length(formatted_type) > 25) var/middle_point = length(formatted_type) / 2 var/splitpoint = findtext(formatted_type, "/", middle_point) if(splitpoint) formatted_type = "[copytext(formatted_type, 1, splitpoint)]
[copytext(formatted_type, splitpoint)]" else formatted_type = "Type too long" //No suitable splitpoint (/) found. var/marked if(holder.marked_datum && holder.marked_datum == D) marked = "
Marked Object" var/varedited_line = "" if(isatom(D)) var/atom/A = D if(A.admin_spawned) varedited_line += "
Admin Spawned" if(!islist && D.var_edited) varedited_line += "
Var Edited" var/list/dropdownoptions = list() if(islist) dropdownoptions = list( "", "", "", "", "", "" ) else dropdownoptions = D.vv_get_dropdown() var/list/names = list() if(!islist) for(var/V in D.vars) names += V sleep(1) // Without a sleep here, VV sometimes disconnects clients var/list/variable_html = list() if(islist) var/list/L = D for(var/i in 1 to length(L)) var/key = L[i] var/value if(IS_NORMAL_LIST(L) && !isnum(key)) value = L[key] variable_html += debug_variable(i, value, 0, D) else names = sortList(names) for(var/V in names) if(D.can_vv_get(V)) variable_html += D.vv_get_var(V) var/html = {" [title] [window_scaling ? "" : ""]
[sprite_text]
[header.Join()]
[formatted_type] [marked] [varedited_line]
Refresh

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 = "
  • (E) (C) (-) " else header = "
  • (E) (C) (M) " else header = "
  • " var/name_part = VV_HTML_ENCODE(name) if(level > 0 || islist(owner)) //handling keys in assoc lists if(isdatum(name)) var/datum/datum_key = name name_part = "[VV_HTML_ENCODE(name)] \ref[datum_key]" else if(isclient(name)) var/client/client_key = name name_part = "[VV_HTML_ENCODE(client_key)] \ref[client_key] ([client_key] [client_key.type])" else if(islist(name)) var/list/list_value = name name_part = " /list ([length(list_value)]) \ref[name]" var/item = _debug_variable_value(name, value, level, owner, sanitize) return "[header][name_part] = [item]
  • " /proc/_debug_variable_value(name, value, level, datum/owner, sanitize) . = "DISPLAY_ERROR: ([value] \ref[value]s)" if(isnull(value)) return "null" else if(is_color_text(value)) return "  \"[value]\"" else if(istext(value)) return "\"[VV_HTML_ENCODE(value)]\"" else if(isicon(value)) #ifdef VARSICON return "/icon ([value]) [bicon(value, use_class=0)]" #else return "/icon ([value])" #endif else if(istype(value, /image)) var/image/I = value #ifdef VARSICON return "[name] \ref[value] /image ([value]) [bicon(value, use_class=0)]" #else return "[name] \ref[value] /image ([value])" #endif else if(isfile(value)) return "'[value]'" else if(istype(value, /datum)) var/datum/D = value return D.debug_variable_value(sanitize) else if(islist(value)) var/list/L = value var/list/items = list() if(length(L) > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || length(L) > (IS_NORMAL_LIST(L) ? 250 : 300))) for(var/i in 1 to length(L)) var/key = L[i] var/val if(IS_NORMAL_LIST(L) && !isnum(key)) val = L[key] if(isnull(val)) val = key key = i items += debug_variable(key, val, level + 1, sanitize = sanitize) return "/list ([length(L)])" else return "/list ([length(L)])" else if(name in GLOB.bitfields) return "[VV_HTML_ENCODE(translate_bitfield(VV_BITFIELD, name, value))]" else return "[VV_HTML_ENCODE(value)]" /datum/proc/debug_variable_value(sanitize) return "[VV_HTML_ENCODE(src)] \ref[src] ([type])" /matrix/debug_variable_value(sanitize) return {"
     
    [a][d]0
    [b][e]0
    [c][f]1
     
    "} //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