From ded0ee63622efe82de3c3dc118797e1fd9a866f1 Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Wed, 19 Mar 2025 12:54:03 -0700 Subject: [PATCH] Bitflag Metainfo, Usable (Happy 90000th) (#90000) ## About The Pull Request Makes wrapper procs to support working with bitflags as defined "things" rather then EXCLUSIVELY numbers, mostly for getting random flags. I've ~~also added a unit test to prevent bitfields from being double defined~~ stolen some code form moth to make double definitions here a compile error. This does rely on people actually using the bitfield lists, essentially upgrading them from breaking just admin debug to POTENTIALLY breaking game logic (slightly). Would like input on this @tgstation/commit-access ## Why It's Good For The Game More tools for feature coders to play with, more sane code --- code/__DEFINES/_bitfields.dm | 6 +++- code/_globalvars/bitfields.dm | 11 +++++++ code/datums/browser.dm | 30 +++++++++---------- code/modules/admin/permissionedit.dm | 2 +- .../admin/view_variables/debug_variables.dm | 24 +++++++-------- .../admin/view_variables/get_variables.dm | 2 +- .../admin/view_variables/modify_variables.dm | 1 - 7 files changed, 45 insertions(+), 31 deletions(-) diff --git a/code/__DEFINES/_bitfields.dm b/code/__DEFINES/_bitfields.dm index 19a962fbffe..4bf587c7101 100644 --- a/code/__DEFINES/_bitfields.dm +++ b/code/__DEFINES/_bitfields.dm @@ -1,4 +1,8 @@ -#define DEFINE_BITFIELD(_variable, _flags) /datum/bitfield/##_variable { \ +#define DEFINE_BITFIELD(_variable, _flags) \ +/* Important note: This exists to throw a compile time warning if more then one bitfield with the same name is defined */ \ +/* This is required to avoid dupes in vv, and any consumers of our bitfield metainfo procs */ \ +GLOBAL_REAL_VAR(_bitfield_##_variable); \ +/datum/bitfield/##_variable { \ flags = ##_flags; \ variable = #_variable; \ } diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 1a813cb1aad..b159fc201ee 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -16,6 +16,17 @@ GLOBAL_LIST_INIT(bitfields, generate_bitfields()) bitfields[bitfield.variable] = bitfield.flags return bitfields +/// Returns an associative list of bitflag name -> number for all valid bitflags in the passed in field +/proc/get_valid_bitflags(var_name) + return GLOB.bitfields[var_name] || list() + +/proc/get_random_bitflag(var_name) + var/list/flags = get_valid_bitflags(var_name) + if(!length(flags)) + return + var/name = pick(flags) + return flags[name] + DEFINE_BITFIELD(admin_flags, list( "ADMIN" = R_ADMIN, "AUTOLOGIN" = R_AUTOADMIN, diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 8027a2438f2..bff7b9f3cb7 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -312,25 +312,25 @@ if (A.selectedbutton) return list("button" = A.selectedbutton, "values" = A.valueslist) -/proc/input_bitfield(mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_list = null) - if (!User || !(bitfield in GLOB.bitfields)) +/proc/input_bitfield(mob/User, title, bitfield, current_value, nwidth = 350, nheight = 350, nslidecolor, allowed_edit_flags = ALL) + var/list/bitflags = get_valid_bitflags(bitfield) + if (!User || !length(bitflags)) return - var/list/pickerlist = list() - for (var/i in GLOB.bitfields[bitfield]) - var/can_edit = 1 - if(!isnull(allowed_edit_list) && !(allowed_edit_list & GLOB.bitfields[bitfield][i])) - can_edit = 0 - if (current_value & GLOB.bitfields[bitfield][i]) - pickerlist += list(list("checked" = 1, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit)) - else - pickerlist += list(list("checked" = 0, "value" = GLOB.bitfields[bitfield][i], "name" = i, "allowed_edit" = can_edit)) - var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = pickerlist, width = nwidth, height = nheight, slidecolor = nslidecolor) + var/list/picker_list = list() + for (var/bit_name in bitflags) + var/bit_value = bitflags[bit_name] + // TRUE/FALSE cast, sorry :) + var/can_edit = !!(allowed_edit_flags & bit_value) + var/flag_set = !!(current_value & bit_value) + picker_list += list(list("checked" = flag_set, "value" = bit_value, "name" = bit_name, "allowed_edit" = can_edit)) + + var/list/result = presentpicker(User, "", title, Button1="Save", Button2 = "Cancel", Timeout=FALSE, values = picker_list, width = nwidth, height = nheight, slidecolor = nslidecolor) if (islist(result)) if (result["button"] == 2) // If the user pressed the cancel button return - . = 0 - for (var/flag in result["values"]) - . |= GLOB.bitfields[bitfield][flag] + . = NONE + for (var/flag_name in result["values"]) + . |= bitflags[flag_name] else return diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index a4a4c525167..f0ffe85aa2d 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -471,7 +471,7 @@ ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit adm admin_holder.rank_flags(), 350, 590, - allowed_edit_list = usr.client.holder.can_edit_rights_flags(), + allowed_edit_flags = usr.client.holder.can_edit_rights_flags(), ) admin_holder.disassociate() diff --git a/code/modules/admin/view_variables/debug_variables.dm b/code/modules/admin/view_variables/debug_variables.dm index b776f1b5d79..34b34369299 100644 --- a/code/modules/admin/view_variables/debug_variables.dm +++ b/code/modules/admin/view_variables/debug_variables.dm @@ -86,21 +86,21 @@ items += debug_variable(key, val, level + 1, sanitize = sanitize) return "/list ([list_value.len])" - else - return "/list ([list_value.len])" + return "/list ([list_value.len])" - 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)) - return "[VV_HTML_ENCODE(jointext(flags, ", "))]" - else - return "NONE" - else + // if it's a number, is it a bitflag? + var/list/valid_bitflags = get_valid_bitflags(name) + if(!length(valid_bitflags)) return "[VV_HTML_ENCODE(value)]" + var/list/flags = list() + for (var/bit_name in valid_bitflags) + if (value & valid_bitflags[bit_name]) + flags += bit_name + if(length(flags)) + return "[VV_HTML_ENCODE(flags.Join(", "))]" + return "NONE" + /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)]" diff --git a/code/modules/admin/view_variables/get_variables.dm b/code/modules/admin/view_variables/get_variables.dm index d493831da67..34bcc07b5c2 100644 --- a/code/modules/admin/view_variables/get_variables.dm +++ b/code/modules/admin/view_variables/get_variables.dm @@ -3,7 +3,7 @@ . = VV_NULL else if(isnum(var_value)) - if(var_name in GLOB.bitfields) + if(length(get_valid_bitflags(var_name))) . = VV_BITFIELD else . = VV_NUM diff --git a/code/modules/admin/view_variables/modify_variables.dm b/code/modules/admin/view_variables/modify_variables.dm index d8255f8b397..13b3a2aa398 100644 --- a/code/modules/admin/view_variables/modify_variables.dm +++ b/code/modules/admin/view_variables/modify_variables.dm @@ -65,7 +65,6 @@ GLOBAL_PROTECT(VVpixelmovement) .["[D]([shorttype])[REF(D)]#[i]"] = D /client/proc/mod_list_add_ass(atom/O) //hehe - var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) var/class = L["class"] if (!class)