diff --git a/code/__DEFINES/_helpers.dm b/code/__DEFINES/_helpers.dm index fdbfde60822..c6a5b860e9a 100644 --- a/code/__DEFINES/_helpers.dm +++ b/code/__DEFINES/_helpers.dm @@ -36,3 +36,8 @@ /// : because of the embedded typecheck #define text_ref(datum) (isdatum(datum) ? (datum:cached_ref ||= "\ref[datum]") : ("\ref[datum]")) #endif + +// Refs contain a type id within their string that can be used to identify byond types. +// Custom types that we define don't get a unique id, but this is useful for identifying +// types that don't normally have a way to run istype() on them. +#define TYPEID(thing) copytext(REF(thing), 4, 6) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 901e3c4d90f..d7f69d2646a 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -11,8 +11,14 @@ #define isweakref(D) (istype(D, /datum/weakref)) +#define isimage(thing) (istype(thing, /image)) + GLOBAL_VAR_INIT(magic_appearance_detecting_image, new /image) // appearances are awful to detect safely, but this seems to be the best way ~ninjanomnom -#define isappearance(thing) (!istype(thing, /image) && !ispath(thing) && istype(GLOB.magic_appearance_detecting_image, thing)) +#define isappearance(thing) (!isimage(thing) && !ispath(thing) && istype(GLOB.magic_appearance_detecting_image, thing)) + +// The filters list has the same ref type id as a filter, but isnt one and also isnt a list, so we have to check if the thing has Cut() instead +GLOBAL_VAR_INIT(refid_filter, TYPEID(filter(type="angular_blur"))) +#define isfilter(thing) (!hascall(thing, "Cut") && TYPEID(thing) == GLOB.refid_filter) #define isgenerator(A) (istype(A, /generator)) diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index 5196d83b124..ea119597ffa 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -1,107 +1,120 @@ #define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) /// Get displayed variable in VV variable list -/proc/debug_variable(name, value, level, datum/D, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value. - var/header - if(D) - if(islist(D)) +/proc/debug_variable(name, value, level, datum/owner, sanitize = TRUE, display_flags = NONE) //if D is a list, name will be index, and value will be assoc value. + if(owner) + if(islist(owner)) var/index = name if (value) - name = D[name] //name is really the index until this line + name = owner[name] //name is really the index until this line else - value = D[name] - header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(D, VV_HK_LIST_REMOVE, "-", index)]) " + value = owner[name] + . = "
  • ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_EDIT, "E", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_CHANGE, "C", index)]) ([VV_HREF_TARGET_1V(owner, VV_HK_LIST_REMOVE, "-", index)]) " else - header = "
  • ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(D, VV_HK_BASIC_MASSEDIT, "M", name)]) " + . = "
  • ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_EDIT, "E", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_CHANGE, "C", name)]) ([VV_HREF_TARGET_1V(owner, VV_HK_BASIC_MASSEDIT, "M", name)]) " else - header = "
  • " + . = "
  • " - var/item var/name_part = VV_HTML_ENCODE(name) - if(level > 0 || islist(D)) //handling keys in assoc lists + if(level > 0 || islist(owner)) //handling keys in assoc lists if(istype(name,/datum)) name_part = "[VV_HTML_ENCODE(name)] [REF(name)]" else if(islist(name)) - var/list/L = name - name_part = " /list ([length(L)]) [REF(name)]" + var/list/list_value = name + name_part = " /list ([length(list_value)]) [REF(name)]" - if (isnull(value)) - item = "[name_part] = null" + . = "[.][name_part] = " - else if (istext(value)) - item = "[name_part] = \"[VV_HTML_ENCODE(value)]\"" + var/item = _debug_variable_value(name, value, level, owner, sanitize, display_flags) - else if (isicon(value)) + return "[.][item]
  • " + +// This is split into a seperate proc mostly to make errors that happen not break things too much +/proc/_debug_variable_value(name, value, level, datum/owner, sanitize, display_flags) + . = "DISPLAY_ERROR" + + if(isnull(value)) + return "null" + + if(istext(value)) + return "\"[VV_HTML_ENCODE(value)]\"" + + if(isicon(value)) #ifdef VARSICON - var/icon/I = icon(value) + var/icon/icon_value = icon(value) var/rnd = rand(1,10000) - var/rname = "tmp[REF(I)][rnd].png" - usr << browse_rsc(I, rname) - item = "[name_part] = ([value]) " + var/rname = "tmp[REF(icon_value)][rnd].png" + usr << browse_rsc(icon_value, rname) + return "([value]) " #else - item = "[name_part] = /icon ([value])" + return "/icon ([value])" #endif - else if(isappearance(value)) + if(isappearance(value)) var/image/actually_an_appearance = value - item = "[name_part] = /appearance ([actually_an_appearance.icon])" + return "/appearance ([actually_an_appearance.icon])" - else if (isfile(value)) - item = "[name_part] = '[value]'" + if(isfilter(value)) + var/datum/filter_value = value + return "/filter ([filter_value.type] [REF(filter_value)])" - else if(istype(value,/matrix)) // Needs to be before datum - var/matrix/M = value - item = {"[name_part] = -
      - - - - - - -
    [M.a][M.d]0
    [M.b][M.e]0
    [M.c][M.f]1
     
    "} //TODO link to modify_transform wrapper for all matrices + if(isfile(value)) + return "'[value]'" - else if (isdatum(value)) - var/datum/DV = value - if ("[DV]" != "[DV.type]") //if the thing as a name var, lets use it. - item = "[name_part] = [DV] [DV.type] [REF(value)]" - else - item = "[name_part] = [DV.type] [REF(value)]" - if(istype(value,/datum/weakref)) - var/datum/weakref/weakref = value - item += " (Resolve)" + if(isdatum(value)) + var/datum/datum_value = value + return datum_value.debug_variable_value(name, level, owner, sanitize, display_flags) - else if (islist(value)) - var/list/L = value + if(islist(value) || hascall(value, "Cut")) // Some special lists arent detectable as a list through istype, so we check if it has a list proc instead + var/list/list_value = value var/list/items = list() - if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && L.len > 0 && !(name == "underlays" || name == "overlays" || L.len > (IS_NORMAL_LIST(L) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD))) - for (var/i in 1 to L.len) - var/key = L[i] + if (!(display_flags & VV_ALWAYS_CONTRACT_LIST) && list_value.len > 0 && list_value.len <= (IS_NORMAL_LIST(list_value) ? VV_NORMAL_LIST_NO_EXPAND_THRESHOLD : VV_SPECIAL_LIST_NO_EXPAND_THRESHOLD)) + for (var/i in 1 to list_value.len) + var/key = list_value[i] var/val - if (IS_NORMAL_LIST(L) && !isnum(key)) - val = L[key] + if (IS_NORMAL_LIST(list_value) && !isnum(key)) + val = list_value[key] if (isnull(val)) // we still want to display non-null false values, such as 0 or "" val = key key = i items += debug_variable(key, val, level + 1, sanitize = sanitize) - item = "[name_part] = /list ([L.len])" + return "/list ([list_value.len])" else - item = "[name_part] = /list ([L.len])" + return "/list ([list_value.len])" - else if (name in GLOB.bitfields) + if(name in GLOB.bitfields) var/list/flags = list() for (var/i in GLOB.bitfields[name]) if (value & GLOB.bitfields[name][i]) flags += i if(length(flags)) - item = "[name_part] = [VV_HTML_ENCODE(jointext(flags, ", "))]" + return "[VV_HTML_ENCODE(jointext(flags, ", "))]" else - item = "[name_part] = NONE" + return "NONE" + else + return "[VV_HTML_ENCODE(value)]" + +/datum/proc/debug_variable_value(name, level, datum/owner, sanitize, display_flags) + if("[src]" != "[type]") // If we have a name var, let's use it. + return "[src] [type] [REF(src)]" else - item = "[name_part] = [VV_HTML_ENCODE(value)]" + return "[type] [REF(src)]" - return "[header][item]" +/datum/weakref/debug_variable_value(name, level, datum/owner, sanitize, display_flags) + . = ..() + return "[.] (Resolve)" + +/matrix/debug_variable_value(name, level, datum/owner, sanitize, display_flags) + return {" +
      + + + + + + +
    [a][d]0
    [b][e]0
    [c][f]1
     
    "} //TODO link to modify_transform wrapper for all matrices #undef VV_HTML_ENCODE diff --git a/code/modules/admin/view_variables/topic.dm b/code/modules/admin/view_variables/topic.dm index e805d55a8af..6edc9cf2e6a 100644 --- a/code/modules/admin/view_variables/topic.dm +++ b/code/modules/admin/view_variables/topic.dm @@ -11,7 +11,10 @@ else if(islist(target)) vv_do_list(target, href_list) if(href_list["Vars"]) - debug_variables(locate(href_list["Vars"])) + var/datum/vars_target = locate(href_list["Vars"]) + if(href_list["special_varname"]) // Some special vars can't be located even if you have their ref, you have to use this instead + vars_target = vars_target.vars[href_list["special_varname"]] + debug_variables(vars_target) //Stuff below aren't in dropdowns/etc. diff --git a/code/modules/admin/view_variables/view_variables.dm b/code/modules/admin/view_variables/view_variables.dm index 2db59be5b38..9f0d86e04f6 100644 --- a/code/modules/admin/view_variables/view_variables.dm +++ b/code/modules/admin/view_variables/view_variables.dm @@ -1,4 +1,4 @@ -/client/proc/debug_variables(datum/D in world) +/client/proc/debug_variables(datum/thing in world) set category = "Debug" set name = "View Variables" //set src in world @@ -8,54 +8,58 @@ to_chat(usr, span_danger("You need to be an administrator to access this."), confidential = TRUE) return - if(!D) + if(!thing) return var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/simple/vv) asset_cache_datum.send(usr) - var/islist = islist(D) - if(!islist && !istype(D)) + var/islist = islist(thing) || (!isdatum(thing) && hascall(thing, "Cut")) // Some special lists dont count as lists, but can be detected by if they have list procs + if(!islist && !isdatum(thing)) return var/title = "" - var/refid = REF(D) + var/refid = REF(thing) var/icon/sprite var/hash - var/type = islist? /list : D.type + var/type = islist? /list : thing.type var/no_icon = FALSE - if(istype(D, /atom)) - sprite = getFlatIcon(D) - if(sprite) - hash = md5(sprite) - src << browse_rsc(sprite, "vv[hash].png") - else + if(isatom(thing)) + sprite = getFlatIcon(thing) + if(!sprite) no_icon = TRUE - title = "[D] ([REF(D)]) = [type]" - var/formatted_type = replacetext("[type]", "/", "/") + else if(isimage(thing)) + var/image/image_object = thing + sprite = icon(image_object.icon, image_object.icon_state) var/sprite_text if(sprite) - sprite_text = no_icon? "\[NO ICON\]" : "" - var/list/header = islist(D)? list("/list") : D.vv_get_header() + hash = md5(sprite) + src << browse_rsc(sprite, "vv[hash].png") + sprite_text = no_icon ? "\[NO ICON\]" : "" + + title = "[thing] ([REF(thing)]) = [type]" + var/formatted_type = replacetext("[type]", "/", "/") + + var/list/header = islist ? list("/list") : thing.vv_get_header() var/ref_line = "@[copytext(refid, 2, -1)]" // get rid of the brackets, add a @ prefix for copy pasting in asay var/marked_line - if(holder && holder.marked_datum && holder.marked_datum == D) + if(holder && holder.marked_datum && holder.marked_datum == thing) marked_line = VV_MSG_MARKED var/tagged_line - if(holder && LAZYFIND(holder.tagged_datums, D)) - var/tag_index = LAZYFIND(holder.tagged_datums, D) + if(holder && LAZYFIND(holder.tagged_datums, thing)) + var/tag_index = LAZYFIND(holder.tagged_datums, thing) tagged_line = VV_MSG_TAGGED(tag_index) var/varedited_line - if(!islist && (D.datum_flags & DF_VAR_EDITED)) + if(!islist && (thing.datum_flags & DF_VAR_EDITED)) varedited_line = VV_MSG_EDITED var/deleted_line - if(!islist && D.gc_destroyed) + if(!islist && thing.gc_destroyed) deleted_line = VV_MSG_DELETED var/list/dropdownoptions @@ -75,28 +79,29 @@ var/link = dropdownoptions[name] dropdownoptions[i] = "" else - dropdownoptions = D.vv_get_dropdown() + dropdownoptions = thing.vv_get_dropdown() var/list/names = list() if(!islist) - for(var/V in D.vars) - names += V + for(var/varname in thing.vars) + names += varname + sleep(1 TICKS) var/list/variable_html = list() if(islist) - var/list/L = D - for(var/i in 1 to L.len) - var/key = L[i] + var/list/list_value = thing + for(var/i in 1 to list_value.len) + var/key = list_value[i] var/value - if(IS_NORMAL_LIST(L) && IS_VALID_ASSOC_KEY(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, L) + if(IS_NORMAL_LIST(list_value) && IS_VALID_ASSOC_KEY(key)) + value = list_value[key] + variable_html += debug_variable(i, value, 0, list_value) else names = sort_list(names) - for(var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) + for(var/varname in names) + if(thing.can_vv_get(varname)) + variable_html += thing.vv_get_var(varname) var/html = {" @@ -274,5 +279,5 @@ datumrefresh=[refid];[HrefToken()]'>Refresh "} src << browse(html, "window=variables[refid];size=475x650") -/client/proc/vv_update_display(datum/D, span, content) - src << output("[span]:[content]", "variables[REF(D)].browser:replace_span") +/client/proc/vv_update_display(datum/thing, span, content) + src << output("[span]:[content]", "variables[REF(thing)].browser:replace_span")