Files
Bubberstation/code/controllers/configuration/config_entry.dm
AnturK 4d6a8bc537 515 Compatibility (#71161)
Makes the code compatible with 515.1594+

Few simple changes and one very painful one.
Let's start with the easy:
* puts call behind `LIBCALL` define, so call_ext is properly used in 515
* Adds `NAMEOF_STATIC(_,X)` macro for nameof in static definitions since
src is now invalid there.
* Fixes tgui and devserver. From 515 onward the tmp3333{procid} cache
directory is not appened to base path in browser controls so we don't
check for it in base js and put the dev server dummy window file in
actual directory not the byond root.
* Renames the few things that had /final/ in typepath to ultimate since
final is a new keyword

And the very painful change:
`.proc/whatever` format is no longer valid, so we're replacing it with
new nameof() function. All this wrapped in three new macros.
`PROC_REF(X)`,`TYPE_PROC_REF(TYPE,X)`,`GLOBAL_PROC_REF(X)`. Global is
not actually necessary but if we get nameof that does not allow globals
it would be nice validation.
This is pretty unwieldy but there's no real alternative.
If you notice anything weird in the commits let me know because majority
was done with regex replace.

@tgstation/commit-access Since the .proc/stuff is pretty big change.

Co-authored-by: san7890 <the@san7890.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2022-11-15 03:50:11 +00:00

306 lines
10 KiB
Plaintext

#define VALUE_MODE_NUM 0
#define VALUE_MODE_TEXT 1
#define VALUE_MODE_FLAG 2
#define KEY_MODE_TEXT 0
#define KEY_MODE_TYPE 1
/datum/config_entry
/// Read-only, this is determined by the last portion of the derived entry type
var/name
/// The configured value for this entry. This shouldn't be initialized in code, instead set default
var/config_entry_value
/// Read-only default value for this config entry, used for resetting value to defaults when necessary. This is what config_entry_value is initially set to
var/default
/// The file which this was loaded from, if any
var/resident_file
/// Set to TRUE if the default has been overridden by a config entry
var/modified = FALSE
/// The config name of a configuration type that depricates this, if it exists
var/deprecated_by
/// The /datum/config_entry type that supercedes this one
var/protection = NONE
/// Do not instantiate if type matches this
var/abstract_type = /datum/config_entry
/// Force validate and set on VV. VAS proccall guard will run regardless.
var/vv_VAS = TRUE
/// Controls if error is thrown when duplicate configuration values for this entry type are encountered
var/dupes_allowed = FALSE
/// Stores the original protection configuration, used for set_default()
var/default_protection
/datum/config_entry/New()
if(type == abstract_type)
CRASH("Abstract config entry [type] instatiated!")
name = lowertext(type2top(type))
default_protection = protection
set_default()
/datum/config_entry/Destroy()
config.RemoveEntry(src)
return ..()
/**
* Returns the value of the configuration datum to its default, used for resetting a config value. Note this also sets the protection back to default.
*/
/datum/config_entry/proc/set_default()
if ((protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall())
log_admin_private("[key_name(usr)] attempted to reset locked config entry [type] to its default")
return
if (islist(default))
var/list/L = default
config_entry_value = L.Copy()
else
config_entry_value = default
protection = default_protection
resident_file = null
modified = FALSE
/datum/config_entry/can_vv_get(var_name)
. = ..()
if(var_name == NAMEOF(src, config_entry_value) || var_name == NAMEOF(src, default))
. &= !(protection & CONFIG_ENTRY_HIDDEN)
/datum/config_entry/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list(NAMEOF_STATIC(src, name), NAMEOF_STATIC(src, vv_VAS), NAMEOF_STATIC(src, default), NAMEOF_STATIC(src, resident_file), NAMEOF_STATIC(src, protection), NAMEOF_STATIC(src, abstract_type), NAMEOF_STATIC(src, modified), NAMEOF_STATIC(src, dupes_allowed))
if(var_name == NAMEOF(src, config_entry_value))
if(protection & CONFIG_ENTRY_LOCKED)
return FALSE
if(vv_VAS)
. = ValidateAndSet("[var_value]")
if(.)
datum_flags |= DF_VAR_EDITED
return
else
return ..()
if(var_name in banned_edits)
return FALSE
return ..()
/datum/config_entry/proc/VASProcCallGuard(str_val)
. = !((protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall())
if(!.)
log_admin_private("[key_name(usr)] attempted to set locked config entry [type] to '[str_val]'")
/datum/config_entry/proc/ValidateAndSet(str_val)
VASProcCallGuard(str_val)
CRASH("Invalid config entry type!")
/datum/config_entry/proc/ValidateListEntry(key_name, key_value)
return TRUE
/datum/config_entry/proc/DeprecationUpdate(value)
return
/datum/config_entry/string
default = ""
abstract_type = /datum/config_entry/string
var/auto_trim = TRUE
/// whether the string will be lowercased on ValidateAndSet or not.
var/lowercase = FALSE
/datum/config_entry/string/vv_edit_var(var_name, var_value)
return var_name != NAMEOF(src, auto_trim) && ..()
/datum/config_entry/string/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
config_entry_value = auto_trim ? trim(str_val) : str_val
if(lowercase)
config_entry_value = lowertext(config_entry_value)
return TRUE
/datum/config_entry/number
default = 0
abstract_type = /datum/config_entry/number
var/integer = TRUE
var/max_val = INFINITY
var/min_val = -INFINITY
/datum/config_entry/number/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
var/temp = text2num(trim(str_val))
if(!isnull(temp))
config_entry_value = clamp(integer ? round(temp) : temp, min_val, max_val)
if(config_entry_value != temp && !(datum_flags & DF_VAR_EDITED))
log_config("Changing [name] from [temp] to [config_entry_value]!")
return TRUE
return FALSE
/datum/config_entry/number/vv_edit_var(var_name, var_value)
var/static/list/banned_edits = list(NAMEOF_STATIC(src, max_val), NAMEOF_STATIC(src, min_val), NAMEOF_STATIC(src, integer))
return !(var_name in banned_edits) && ..()
/datum/config_entry/flag
default = FALSE
abstract_type = /datum/config_entry/flag
/datum/config_entry/flag/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
config_entry_value = text2num(trim(str_val)) != 0
return TRUE
/// List config entry, used for configuring a list of strings
/datum/config_entry/str_list
abstract_type = /datum/config_entry/str_list
default = list()
dupes_allowed = TRUE
/// whether the string elements will be lowercased on ValidateAndSet or not.
var/lowercase = FALSE
/datum/config_entry/str_list/ValidateAndSet(str_val)
if (!VASProcCallGuard(str_val))
return FALSE
str_val = trim(str_val)
if (str_val != "")
config_entry_value += lowercase ? lowertext(str_val) : str_val
return TRUE
/datum/config_entry/number_list
abstract_type = /datum/config_entry/number_list
default = list()
/datum/config_entry/number_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
str_val = trim(str_val)
var/list/new_list = list()
var/list/values = splittext(str_val," ")
for(var/I in values)
var/temp = text2num(I)
if(isnull(temp))
return FALSE
new_list += temp
if(!new_list.len)
return FALSE
config_entry_value = new_list
return TRUE
/datum/config_entry/keyed_list
abstract_type = /datum/config_entry/keyed_list
default = list()
dupes_allowed = TRUE
vv_VAS = FALSE //VAS will not allow things like deleting from lists, it'll just bug horribly.
var/key_mode
var/value_mode
var/splitter = " "
/// whether the key names will be lowercased on ValidateAndSet or not.
var/lowercase_key = TRUE
/datum/config_entry/keyed_list/New()
. = ..()
if(isnull(key_mode) || isnull(value_mode))
CRASH("Keyed list of type [type] created with null key or value mode!")
/datum/config_entry/keyed_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
str_val = trim(str_val)
var/list/new_entry = parse_key_and_value(str_val)
var/new_key = new_entry["config_key"]
var/new_value = new_entry["config_value"]
if(!isnull(new_value) && !isnull(new_key) && ValidateListEntry(new_key, new_value))
config_entry_value[new_key] = new_value
return TRUE
return FALSE
/datum/config_entry/keyed_list/proc/parse_key_and_value(option_string)
// Blank or null option string? Bad mojo!
if(!option_string)
log_config("ERROR: Keyed list config tried to parse with no key or value data.")
return null
var/list/config_entry_words = splittext(option_string, splitter)
var/config_value
var/config_key
var/is_ambiguous = FALSE
// If this config entry's value mode is flag, the value can either be TRUE or FALSE.
// However, the config supports implicitly setting a config entry to TRUE by omitting the value.
// This value mode should also support config overrides disabling it too.
// The following code supports config entries as such:
// Implicitly enable the config entry: CONFIG_ENTRY config key goes here
// Explicitly enable the config entry: CONFIG_ENTRY config key goes here 1
// Explicitly disable the config entry: CONFIG_ENTRY config key goes here 0
if(value_mode == VALUE_MODE_FLAG)
var/value = peek(config_entry_words)
config_value = TRUE
if(value == "0")
config_key = jointext(config_entry_words, splitter, length(config_entry_words) - 1)
config_value = FALSE
is_ambiguous = (length(config_entry_words) > 2)
else if(value == "1")
config_key = jointext(config_entry_words, splitter, length(config_entry_words) - 1)
is_ambiguous = (length(config_entry_words) > 2)
else
config_key = option_string
is_ambiguous = (length(config_entry_words) > 1)
// Else it has to be a key value pair and we parse it under that assumption.
else
// If config_entry_words only has 1 or 0 words in it and isn't value_mode == VALUE_MODE_FLAG then it's an invalid config entry.
if(length(config_entry_words) <= 1)
log_config("ERROR: Could not parse value from config entry string: [option_string]")
return null
config_value = pop(config_entry_words)
config_key = jointext(config_entry_words, splitter)
if(lowercase_key)
config_key = lowertext(config_key)
is_ambiguous = (length(config_entry_words) > 2)
config_key = validate_config_key(config_key)
config_value = validate_config_value(config_value)
// If there are multiple splitters, it's definitely ambiguous and we'll warn about how we parsed it. Helps with debugging config issues.
if(is_ambiguous)
log_config("WARNING: Multiple splitter characters (\"[splitter]\") found. Using \"[config_key]\" as config key and \"[config_value]\" as config value.")
return list("config_key" = config_key, "config_value" = config_value)
/// Takes a given config key and validates it. If successful, returns the formatted key. If unsuccessful, returns null.
/datum/config_entry/keyed_list/proc/validate_config_key(key)
switch(key_mode)
if(KEY_MODE_TEXT)
return key
if(KEY_MODE_TYPE)
if(ispath(key))
return key
var/key_path = text2path(key)
if(isnull(key_path))
log_config("ERROR: Invalid KEY_MODE_TYPE typepath. Is not a valid typepath: [key]")
return
return key_path
/// Takes a given config value and validates it. If successful, returns the formatted key. If unsuccessful, returns null.
/datum/config_entry/keyed_list/proc/validate_config_value(value)
switch(value_mode)
if(VALUE_MODE_FLAG)
return value
if(VALUE_MODE_NUM)
if(isnum(value))
return value
var/value_num = text2num(value)
if(isnull(value_num))
log_config("ERROR: Invalid VALUE_MODE_NUM number. Could not parse a valid number: [value]")
return
return value_num
if(VALUE_MODE_TEXT)
return value
/datum/config_entry/keyed_list/vv_edit_var(var_name, var_value)
return var_name != NAMEOF(src, splitter) && ..()