Merge branch 'master' of https://github.com/Citadel-Station-13/Citadel-Station-13 into Seed-to-points
This commit is contained in:
@@ -1,240 +1,240 @@
|
||||
#define VALUE_MODE_NUM 0
|
||||
#define VALUE_MODE_TEXT 1
|
||||
#define VALUE_MODE_FLAG 2
|
||||
#define VALUE_MODE_NUM_LIST 3
|
||||
|
||||
#define KEY_MODE_TEXT 0
|
||||
#define KEY_MODE_TYPE 1
|
||||
|
||||
/datum/config_entry
|
||||
var/name //read-only, this is determined by the last portion of the derived entry type
|
||||
var/config_entry_value
|
||||
var/default //read-only, just set value directly
|
||||
|
||||
var/resident_file //the file which this was loaded from, if any
|
||||
var/modified = FALSE //set to TRUE if the default has been overridden by a config entry
|
||||
|
||||
var/deprecated_by //the /datum/config_entry type that supercedes this one
|
||||
|
||||
var/protection = NONE
|
||||
var/abstract_type = /datum/config_entry //do not instantiate if type matches this
|
||||
|
||||
var/vv_VAS = TRUE //Force validate and set on VV. VAS proccall guard will run regardless.
|
||||
var/postload_required = FALSE //requires running OnPostload()
|
||||
|
||||
var/dupes_allowed = FALSE
|
||||
|
||||
/datum/config_entry/New()
|
||||
if(type == abstract_type)
|
||||
CRASH("Abstract config entry [type] instatiated!")
|
||||
name = lowertext(type2top(type))
|
||||
if(islist(config_entry_value))
|
||||
var/list/L = config_entry_value
|
||||
default = L.Copy()
|
||||
else
|
||||
default = config_entry_value
|
||||
|
||||
/datum/config_entry/Destroy()
|
||||
config.RemoveEntry(src)
|
||||
return ..()
|
||||
|
||||
/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(src, name), NAMEOF(src, vv_VAS), NAMEOF(src, default), NAMEOF(src, resident_file), NAMEOF(src, protection), NAMEOF(src, abstract_type), NAMEOF(src, modified), NAMEOF(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() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
if(!.)
|
||||
log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]")
|
||||
|
||||
/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/proc/OnPostload()
|
||||
return
|
||||
|
||||
/datum/config_entry/string
|
||||
config_entry_value = ""
|
||||
abstract_type = /datum/config_entry/string
|
||||
var/auto_trim = TRUE
|
||||
|
||||
/datum/config_entry/string/vv_edit_var(var_name, var_value)
|
||||
return var_name != "auto_trim" && ..()
|
||||
|
||||
/datum/config_entry/string/ValidateAndSet(str_val, during_load)
|
||||
if(!VASProcCallGuard(str_val))
|
||||
return FALSE
|
||||
config_entry_value = auto_trim ? trim(str_val) : str_val
|
||||
return TRUE
|
||||
|
||||
/datum/config_entry/number
|
||||
config_entry_value = 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("max_val", "min_val", "integer")
|
||||
return !(var_name in banned_edits) && ..()
|
||||
|
||||
/datum/config_entry/flag
|
||||
config_entry_value = 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
|
||||
|
||||
/datum/config_entry/number_list
|
||||
abstract_type = /datum/config_entry/number_list
|
||||
config_entry_value = 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
|
||||
config_entry_value = 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 = " "
|
||||
var/lowercase = 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/key_pos = findtext(str_val, splitter)
|
||||
var/key_name = null
|
||||
var/key_value = null
|
||||
|
||||
if(key_pos || value_mode == VALUE_MODE_FLAG)
|
||||
key_name = copytext(str_val, 1, key_pos)
|
||||
if(lowercase)
|
||||
key_name = lowertext(key_name)
|
||||
key_value = copytext(str_val, key_pos + 1)
|
||||
var/new_key
|
||||
var/new_value
|
||||
var/continue_check_value
|
||||
var/continue_check_key
|
||||
switch(key_mode)
|
||||
if(KEY_MODE_TEXT)
|
||||
new_key = key_name
|
||||
continue_check_key = new_key
|
||||
if(KEY_MODE_TYPE)
|
||||
new_key = key_name
|
||||
if(!ispath(new_key))
|
||||
new_key = text2path(new_key)
|
||||
continue_check_key = ispath(new_key)
|
||||
switch(value_mode)
|
||||
if(VALUE_MODE_FLAG)
|
||||
new_value = TRUE
|
||||
continue_check_value = TRUE
|
||||
if(VALUE_MODE_NUM)
|
||||
new_value = text2num(key_value)
|
||||
continue_check_value = !isnull(new_value)
|
||||
if(VALUE_MODE_TEXT)
|
||||
new_value = key_value
|
||||
continue_check_value = new_value
|
||||
if(VALUE_MODE_NUM_LIST)
|
||||
// this is all copy+pasted from number list up there, but it's super basic so I don't see it being changed soon
|
||||
var/list/new_list = list()
|
||||
var/list/values = splittext(key_value," ")
|
||||
for(var/I in values)
|
||||
var/temp = text2num(I)
|
||||
if(isnull(temp))
|
||||
log_admin("invalid number list entry in [key_name]: [I]")
|
||||
continue_check_value = FALSE
|
||||
new_list += temp
|
||||
new_value = new_list
|
||||
continue_check_value = new_list.len
|
||||
if(continue_check_value && continue_check_key && ValidateListEntry(new_key, new_value))
|
||||
config_entry_value[new_key] = new_value
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/config_entry/keyed_list/vv_edit_var(var_name, var_value)
|
||||
return var_name != "splitter" && ..()
|
||||
|
||||
//snowflake for donator things being on one line smh
|
||||
/datum/config_entry/multi_keyed_flag
|
||||
vv_VAS = FALSE
|
||||
abstract_type = /datum/config_entry/multi_keyed_flag
|
||||
config_entry_value = list()
|
||||
var/delimiter = "|"
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/vv_edit_var(var_name, var_value)
|
||||
if(var_name == NAMEOF(src, delimiter))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/ValidateAndSet(str_val)
|
||||
if(!VASProcCallGuard(str_val))
|
||||
return FALSE
|
||||
str_val = trim(str_val)
|
||||
var/list/keys = splittext(str_val, delimiter)
|
||||
for(var/i in keys)
|
||||
config_entry_value[process_key(i)] = TRUE
|
||||
return length(keys)? TRUE : FALSE
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/proc/process_key(key)
|
||||
return trim(key)
|
||||
#define VALUE_MODE_NUM 0
|
||||
#define VALUE_MODE_TEXT 1
|
||||
#define VALUE_MODE_FLAG 2
|
||||
#define VALUE_MODE_NUM_LIST 3
|
||||
|
||||
#define KEY_MODE_TEXT 0
|
||||
#define KEY_MODE_TYPE 1
|
||||
|
||||
/datum/config_entry
|
||||
var/name //read-only, this is determined by the last portion of the derived entry type
|
||||
var/config_entry_value
|
||||
var/default //read-only, just set value directly
|
||||
|
||||
var/resident_file //the file which this was loaded from, if any
|
||||
var/modified = FALSE //set to TRUE if the default has been overridden by a config entry
|
||||
|
||||
var/deprecated_by //the /datum/config_entry type that supercedes this one
|
||||
|
||||
var/protection = NONE
|
||||
var/abstract_type = /datum/config_entry //do not instantiate if type matches this
|
||||
|
||||
var/vv_VAS = TRUE //Force validate and set on VV. VAS proccall guard will run regardless.
|
||||
var/postload_required = FALSE //requires running OnPostload()
|
||||
|
||||
var/dupes_allowed = FALSE
|
||||
|
||||
/datum/config_entry/New()
|
||||
if(type == abstract_type)
|
||||
CRASH("Abstract config entry [type] instatiated!")
|
||||
name = lowertext(type2top(type))
|
||||
if(islist(config_entry_value))
|
||||
var/list/L = config_entry_value
|
||||
default = L.Copy()
|
||||
else
|
||||
default = config_entry_value
|
||||
|
||||
/datum/config_entry/Destroy()
|
||||
config.RemoveEntry(src)
|
||||
return ..()
|
||||
|
||||
/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(src, name), NAMEOF(src, vv_VAS), NAMEOF(src, default), NAMEOF(src, resident_file), NAMEOF(src, protection), NAMEOF(src, abstract_type), NAMEOF(src, modified), NAMEOF(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() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
if(!.)
|
||||
log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]")
|
||||
|
||||
/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/proc/OnPostload()
|
||||
return
|
||||
|
||||
/datum/config_entry/string
|
||||
config_entry_value = ""
|
||||
abstract_type = /datum/config_entry/string
|
||||
var/auto_trim = TRUE
|
||||
|
||||
/datum/config_entry/string/vv_edit_var(var_name, var_value)
|
||||
return var_name != "auto_trim" && ..()
|
||||
|
||||
/datum/config_entry/string/ValidateAndSet(str_val, during_load)
|
||||
if(!VASProcCallGuard(str_val))
|
||||
return FALSE
|
||||
config_entry_value = auto_trim ? trim(str_val) : str_val
|
||||
return TRUE
|
||||
|
||||
/datum/config_entry/number
|
||||
config_entry_value = 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("max_val", "min_val", "integer")
|
||||
return !(var_name in banned_edits) && ..()
|
||||
|
||||
/datum/config_entry/flag
|
||||
config_entry_value = 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
|
||||
|
||||
/datum/config_entry/number_list
|
||||
abstract_type = /datum/config_entry/number_list
|
||||
config_entry_value = 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
|
||||
config_entry_value = 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 = " "
|
||||
var/lowercase = 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/key_pos = findtext(str_val, splitter)
|
||||
var/key_name = null
|
||||
var/key_value = null
|
||||
|
||||
if(key_pos || value_mode == VALUE_MODE_FLAG)
|
||||
key_name = copytext(str_val, 1, key_pos)
|
||||
if(lowercase)
|
||||
key_name = lowertext(key_name)
|
||||
key_value = copytext(str_val, key_pos + 1)
|
||||
var/new_key
|
||||
var/new_value
|
||||
var/continue_check_value
|
||||
var/continue_check_key
|
||||
switch(key_mode)
|
||||
if(KEY_MODE_TEXT)
|
||||
new_key = key_name
|
||||
continue_check_key = new_key
|
||||
if(KEY_MODE_TYPE)
|
||||
new_key = key_name
|
||||
if(!ispath(new_key))
|
||||
new_key = text2path(new_key)
|
||||
continue_check_key = ispath(new_key)
|
||||
switch(value_mode)
|
||||
if(VALUE_MODE_FLAG)
|
||||
new_value = TRUE
|
||||
continue_check_value = TRUE
|
||||
if(VALUE_MODE_NUM)
|
||||
new_value = text2num(key_value)
|
||||
continue_check_value = !isnull(new_value)
|
||||
if(VALUE_MODE_TEXT)
|
||||
new_value = key_value
|
||||
continue_check_value = new_value
|
||||
if(VALUE_MODE_NUM_LIST)
|
||||
// this is all copy+pasted from number list up there, but it's super basic so I don't see it being changed soon
|
||||
var/list/new_list = list()
|
||||
var/list/values = splittext(key_value," ")
|
||||
for(var/I in values)
|
||||
var/temp = text2num(I)
|
||||
if(isnull(temp))
|
||||
log_admin("invalid number list entry in [key_name]: [I]")
|
||||
continue_check_value = FALSE
|
||||
new_list += temp
|
||||
new_value = new_list
|
||||
continue_check_value = new_list.len
|
||||
if(continue_check_value && continue_check_key && ValidateListEntry(new_key, new_value))
|
||||
config_entry_value[new_key] = new_value
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/config_entry/keyed_list/vv_edit_var(var_name, var_value)
|
||||
return var_name != "splitter" && ..()
|
||||
|
||||
//snowflake for donator things being on one line smh
|
||||
/datum/config_entry/multi_keyed_flag
|
||||
vv_VAS = FALSE
|
||||
abstract_type = /datum/config_entry/multi_keyed_flag
|
||||
config_entry_value = list()
|
||||
var/delimiter = "|"
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/vv_edit_var(var_name, var_value)
|
||||
if(var_name == NAMEOF(src, delimiter))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/ValidateAndSet(str_val)
|
||||
if(!VASProcCallGuard(str_val))
|
||||
return FALSE
|
||||
str_val = trim(str_val)
|
||||
var/list/keys = splittext(str_val, delimiter)
|
||||
for(var/i in keys)
|
||||
config_entry_value[process_key(i)] = TRUE
|
||||
return length(keys)? TRUE : FALSE
|
||||
|
||||
/datum/config_entry/multi_keyed_flag/proc/process_key(key)
|
||||
return trim(key)
|
||||
|
||||
@@ -1,384 +1,384 @@
|
||||
/datum/controller/configuration
|
||||
name = "Configuration"
|
||||
|
||||
var/directory = "config"
|
||||
|
||||
var/warned_deprecated_configs = FALSE
|
||||
var/hiding_entries_by_type = TRUE //Set for readability, admins can set this to FALSE if they want to debug it
|
||||
var/list/entries
|
||||
var/list/entries_by_type
|
||||
|
||||
var/list/maplist
|
||||
var/datum/map_config/defaultmap
|
||||
|
||||
var/list/modes // allowed modes
|
||||
var/list/gamemode_cache
|
||||
var/list/votable_modes // votable modes
|
||||
var/list/storyteller_cache
|
||||
var/list/mode_names
|
||||
var/list/mode_reports
|
||||
var/list/mode_false_report_weight
|
||||
|
||||
var/motd
|
||||
|
||||
/datum/controller/configuration/proc/admin_reload()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
log_admin("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
|
||||
message_admins("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
|
||||
full_wipe()
|
||||
Load(world.params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
|
||||
|
||||
/datum/controller/configuration/proc/Load(_directory)
|
||||
if(IsAdminAdvancedProcCall()) //If admin proccall is detected down the line it will horribly break everything.
|
||||
return
|
||||
if(_directory)
|
||||
directory = _directory
|
||||
if(entries)
|
||||
CRASH("/datum/controller/configuration/Load() called more than once!")
|
||||
InitEntries()
|
||||
LoadModes()
|
||||
storyteller_cache = typecacheof(/datum/dynamic_storyteller, TRUE)
|
||||
if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1)
|
||||
var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt")
|
||||
for(var/I in legacy_configs)
|
||||
if(fexists("[directory]/[I]"))
|
||||
log_config("No $include directives found in config.txt! Loading legacy [legacy_configs.Join("/")] files...")
|
||||
for(var/J in legacy_configs)
|
||||
LoadEntries(J)
|
||||
break
|
||||
loadmaplist(CONFIG_MAPS_FILE)
|
||||
LoadMOTD()
|
||||
|
||||
/datum/controller/configuration/proc/full_wipe()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
entries_by_type.Cut()
|
||||
QDEL_LIST_ASSOC_VAL(entries)
|
||||
entries = null
|
||||
QDEL_LIST_ASSOC_VAL(maplist)
|
||||
maplist = null
|
||||
QDEL_NULL(defaultmap)
|
||||
|
||||
/datum/controller/configuration/Destroy()
|
||||
full_wipe()
|
||||
config = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/configuration/proc/InitEntries()
|
||||
var/list/_entries = list()
|
||||
entries = _entries
|
||||
var/list/_entries_by_type = list()
|
||||
entries_by_type = _entries_by_type
|
||||
|
||||
for(var/I in typesof(/datum/config_entry)) //typesof is faster in this case
|
||||
var/datum/config_entry/E = I
|
||||
if(initial(E.abstract_type) == I)
|
||||
continue
|
||||
E = new I
|
||||
var/esname = E.name
|
||||
var/datum/config_entry/test = _entries[esname]
|
||||
if(test)
|
||||
log_config("Error: [test.type] has the same name as [E.type]: [esname]! Not initializing [E.type]!")
|
||||
qdel(E)
|
||||
continue
|
||||
_entries[esname] = E
|
||||
_entries_by_type[I] = E
|
||||
|
||||
/datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE)
|
||||
entries -= CE.name
|
||||
entries_by_type -= CE.type
|
||||
|
||||
/datum/controller/configuration/proc/LoadEntries(filename, list/stack = list())
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
|
||||
var/filename_to_test = world.system_type == MS_WINDOWS ? lowertext(filename) : filename
|
||||
if(filename_to_test in stack)
|
||||
log_config("Warning: Config recursion detected ([english_list(stack)]), breaking!")
|
||||
return
|
||||
stack = stack + filename_to_test
|
||||
|
||||
log_config("Loading config file [filename]...")
|
||||
var/list/lines = world.file2list("[directory]/[filename]")
|
||||
var/list/_entries = entries
|
||||
var/list/postload_required = list()
|
||||
for(var/L in lines)
|
||||
L = trim(L)
|
||||
if(!L)
|
||||
continue
|
||||
|
||||
var/firstchar = copytext(L, 1, 2)
|
||||
if(firstchar == "#")
|
||||
continue
|
||||
|
||||
var/lockthis = firstchar == "@"
|
||||
if(lockthis)
|
||||
L = copytext(L, 2)
|
||||
|
||||
var/pos = findtext(L, " ")
|
||||
var/entry = null
|
||||
var/value = null
|
||||
|
||||
if(pos)
|
||||
entry = lowertext(copytext(L, 1, pos))
|
||||
value = copytext(L, pos + 1)
|
||||
else
|
||||
entry = lowertext(L)
|
||||
|
||||
if(!entry)
|
||||
continue
|
||||
|
||||
if(entry == "$include")
|
||||
if(!value)
|
||||
log_config("Warning: Invalid $include directive: [value]")
|
||||
else
|
||||
LoadEntries(value, stack)
|
||||
++.
|
||||
continue
|
||||
|
||||
var/datum/config_entry/E = _entries[entry]
|
||||
if(!E)
|
||||
log_config("Unknown setting in configuration: '[entry]'")
|
||||
continue
|
||||
|
||||
if(lockthis)
|
||||
E.protection |= CONFIG_ENTRY_LOCKED
|
||||
|
||||
if(E.deprecated_by)
|
||||
var/datum/config_entry/new_ver = entries_by_type[E.deprecated_by]
|
||||
var/new_value = E.DeprecationUpdate(value)
|
||||
var/good_update = istext(new_value)
|
||||
log_config("Entry [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]")
|
||||
if(!warned_deprecated_configs)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, "This server is using deprecated configuration settings. Please check the logs and update accordingly."), 0)
|
||||
warned_deprecated_configs = TRUE
|
||||
if(good_update)
|
||||
value = new_value
|
||||
E = new_ver
|
||||
else
|
||||
warning("[new_ver.type] is deprecated but gave no proper return for DeprecationUpdate()")
|
||||
|
||||
var/validated = E.ValidateAndSet(value, TRUE)
|
||||
if(!validated)
|
||||
log_config("Failed to validate setting \"[value]\" for [entry]")
|
||||
else
|
||||
if(E.modified && !E.dupes_allowed)
|
||||
log_config("Duplicate setting for [entry] ([value], [E.resident_file]) detected! Using latest.")
|
||||
if(E.postload_required)
|
||||
postload_required[E] = TRUE
|
||||
|
||||
E.resident_file = filename
|
||||
|
||||
if(validated)
|
||||
E.modified = TRUE
|
||||
|
||||
for(var/i in postload_required)
|
||||
var/datum/config_entry/E = i
|
||||
E.OnPostload()
|
||||
|
||||
++.
|
||||
|
||||
/datum/controller/configuration/can_vv_get(var_name)
|
||||
return (var_name != NAMEOF(src, entries_by_type) || !hiding_entries_by_type) && ..()
|
||||
|
||||
/datum/controller/configuration/vv_edit_var(var_name, var_value)
|
||||
var/list/banned_edits = list(NAMEOF(src, entries_by_type), NAMEOF(src, entries), NAMEOF(src, directory))
|
||||
return !(var_name in banned_edits) && ..()
|
||||
|
||||
/datum/controller/configuration/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Edit", src)
|
||||
stat("[name]:", statclick)
|
||||
|
||||
/datum/controller/configuration/proc/Get(entry_type)
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
|
||||
return
|
||||
return E.config_entry_value
|
||||
|
||||
/datum/controller/configuration/proc/Set(entry_type, new_val)
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to set an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
if((E.protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
|
||||
return
|
||||
return E.ValidateAndSet("[new_val]")
|
||||
|
||||
/datum/controller/configuration/proc/LoadModes()
|
||||
gamemode_cache = typecacheof(/datum/game_mode, TRUE)
|
||||
modes = list()
|
||||
mode_names = list()
|
||||
mode_reports = list()
|
||||
mode_false_report_weight = list()
|
||||
votable_modes = list()
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
for(var/T in gamemode_cache)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// for future reference: just use initial() lol
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if(M.config_tag)
|
||||
if(!(M.config_tag in modes)) // ensure each mode is added only once
|
||||
modes += M.config_tag
|
||||
mode_names[M.config_tag] = M.name
|
||||
mode_reports[M.config_tag] = M.generate_report()
|
||||
if(probabilities[M.config_tag]>0)
|
||||
mode_false_report_weight[M.config_tag] = M.false_report_weight
|
||||
else
|
||||
mode_false_report_weight[M.config_tag] = 1
|
||||
if(M.votable)
|
||||
votable_modes += M.config_tag
|
||||
qdel(M)
|
||||
votable_modes += "secret"
|
||||
|
||||
/datum/controller/configuration/proc/LoadMOTD()
|
||||
motd = file2text("[directory]/motd.txt")
|
||||
var/tm_info = GLOB.revdata.GetTestMergeInfo()
|
||||
if(motd || tm_info)
|
||||
motd = motd ? "[motd]<br>[tm_info]" : tm_info
|
||||
|
||||
/datum/controller/configuration/proc/loadmaplist(filename)
|
||||
log_config("Loading config file [filename]...")
|
||||
filename = "[directory]/[filename]"
|
||||
var/list/Lines = world.file2list(filename)
|
||||
|
||||
var/datum/map_config/currentmap = null
|
||||
for(var/t in Lines)
|
||||
if(!t)
|
||||
continue
|
||||
|
||||
t = trim(t)
|
||||
if(length(t) == 0)
|
||||
continue
|
||||
else if(copytext(t, 1, 2) == "#")
|
||||
continue
|
||||
|
||||
var/pos = findtext(t, " ")
|
||||
var/command = null
|
||||
var/data = null
|
||||
|
||||
if(pos)
|
||||
command = lowertext(copytext(t, 1, pos))
|
||||
data = copytext(t, pos + 1)
|
||||
else
|
||||
command = lowertext(t)
|
||||
|
||||
if(!command)
|
||||
continue
|
||||
|
||||
if (!currentmap && command != "map")
|
||||
continue
|
||||
|
||||
switch (command)
|
||||
if ("map")
|
||||
currentmap = load_map_config("_maps/[data].json")
|
||||
if(currentmap.defaulted)
|
||||
log_config("Failed to load map config for [data]!")
|
||||
currentmap = null
|
||||
if ("minplayers","minplayer")
|
||||
currentmap.config_min_users = text2num(data)
|
||||
if ("maxplayers","maxplayer")
|
||||
currentmap.config_max_users = text2num(data)
|
||||
if ("weight","voteweight")
|
||||
currentmap.voteweight = text2num(data)
|
||||
if ("default","defaultmap")
|
||||
defaultmap = currentmap
|
||||
if ("endmap")
|
||||
LAZYINITLIST(maplist)
|
||||
maplist[currentmap.map_name] = currentmap
|
||||
currentmap = null
|
||||
if ("disabled")
|
||||
currentmap = null
|
||||
else
|
||||
log_config("Unknown command in map vote config: '[command]'")
|
||||
|
||||
|
||||
/datum/controller/configuration/proc/pick_mode(mode_name)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// ^ This guy didn't try hard enough
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = T
|
||||
var/ct = initial(M.config_tag)
|
||||
if(ct && ct == mode_name)
|
||||
return new T
|
||||
return new /datum/game_mode/extended()
|
||||
|
||||
/datum/controller/configuration/proc/pick_storyteller(storyteller_name)
|
||||
for(var/T in storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/name = initial(S.name)
|
||||
if(name && name == storyteller_name)
|
||||
return T
|
||||
return /datum/dynamic_storyteller/classic
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
|
||||
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(M.config_tag in SSvote.stored_modetier_results && SSvote.stored_modetier_results[M.config_tag] < Get(/datum/config_entry/number/dropped_modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.can_start())
|
||||
var/final_weight = probabilities[M.config_tag]
|
||||
if(SSpersistence.saved_modes.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/recent_round = min(SSpersistence.saved_modes.Find(M.config_tag),3)
|
||||
var/adjustment = 0
|
||||
while(recent_round)
|
||||
adjustment += repeated_mode_adjust[recent_round]
|
||||
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
|
||||
final_weight *= ((100-adjustment)/100)
|
||||
runnable_modes[M] = final_weight
|
||||
return runnable_modes
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
|
||||
for(var/T in (gamemode_cache - SSticker.mode.type))
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.required_players <= crew)
|
||||
if(M.maximum_players >= 0 && M.maximum_players < crew)
|
||||
continue
|
||||
runnable_modes[M] = probabilities[M.config_tag]
|
||||
return runnable_modes
|
||||
/datum/controller/configuration
|
||||
name = "Configuration"
|
||||
|
||||
var/directory = "config"
|
||||
|
||||
var/warned_deprecated_configs = FALSE
|
||||
var/hiding_entries_by_type = TRUE //Set for readability, admins can set this to FALSE if they want to debug it
|
||||
var/list/entries
|
||||
var/list/entries_by_type
|
||||
|
||||
var/list/maplist
|
||||
var/datum/map_config/defaultmap
|
||||
|
||||
var/list/modes // allowed modes
|
||||
var/list/gamemode_cache
|
||||
var/list/votable_modes // votable modes
|
||||
var/list/storyteller_cache
|
||||
var/list/mode_names
|
||||
var/list/mode_reports
|
||||
var/list/mode_false_report_weight
|
||||
|
||||
var/motd
|
||||
|
||||
/datum/controller/configuration/proc/admin_reload()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
log_admin("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
|
||||
message_admins("[key_name_admin(usr)] has forcefully reloaded the configuration from disk.")
|
||||
full_wipe()
|
||||
Load(world.params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER])
|
||||
|
||||
/datum/controller/configuration/proc/Load(_directory)
|
||||
if(IsAdminAdvancedProcCall()) //If admin proccall is detected down the line it will horribly break everything.
|
||||
return
|
||||
if(_directory)
|
||||
directory = _directory
|
||||
if(entries)
|
||||
CRASH("/datum/controller/configuration/Load() called more than once!")
|
||||
InitEntries()
|
||||
LoadModes()
|
||||
storyteller_cache = typecacheof(/datum/dynamic_storyteller, TRUE)
|
||||
if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1)
|
||||
var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt")
|
||||
for(var/I in legacy_configs)
|
||||
if(fexists("[directory]/[I]"))
|
||||
log_config("No $include directives found in config.txt! Loading legacy [legacy_configs.Join("/")] files...")
|
||||
for(var/J in legacy_configs)
|
||||
LoadEntries(J)
|
||||
break
|
||||
loadmaplist(CONFIG_MAPS_FILE)
|
||||
LoadMOTD()
|
||||
|
||||
/datum/controller/configuration/proc/full_wipe()
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
entries_by_type.Cut()
|
||||
QDEL_LIST_ASSOC_VAL(entries)
|
||||
entries = null
|
||||
QDEL_LIST_ASSOC_VAL(maplist)
|
||||
maplist = null
|
||||
QDEL_NULL(defaultmap)
|
||||
|
||||
/datum/controller/configuration/Destroy()
|
||||
full_wipe()
|
||||
config = null
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/configuration/proc/InitEntries()
|
||||
var/list/_entries = list()
|
||||
entries = _entries
|
||||
var/list/_entries_by_type = list()
|
||||
entries_by_type = _entries_by_type
|
||||
|
||||
for(var/I in typesof(/datum/config_entry)) //typesof is faster in this case
|
||||
var/datum/config_entry/E = I
|
||||
if(initial(E.abstract_type) == I)
|
||||
continue
|
||||
E = new I
|
||||
var/esname = E.name
|
||||
var/datum/config_entry/test = _entries[esname]
|
||||
if(test)
|
||||
log_config("Error: [test.type] has the same name as [E.type]: [esname]! Not initializing [E.type]!")
|
||||
qdel(E)
|
||||
continue
|
||||
_entries[esname] = E
|
||||
_entries_by_type[I] = E
|
||||
|
||||
/datum/controller/configuration/proc/RemoveEntry(datum/config_entry/CE)
|
||||
entries -= CE.name
|
||||
entries_by_type -= CE.type
|
||||
|
||||
/datum/controller/configuration/proc/LoadEntries(filename, list/stack = list())
|
||||
if(IsAdminAdvancedProcCall())
|
||||
return
|
||||
|
||||
var/filename_to_test = world.system_type == MS_WINDOWS ? lowertext(filename) : filename
|
||||
if(filename_to_test in stack)
|
||||
log_config("Warning: Config recursion detected ([english_list(stack)]), breaking!")
|
||||
return
|
||||
stack = stack + filename_to_test
|
||||
|
||||
log_config("Loading config file [filename]...")
|
||||
var/list/lines = world.file2list("[directory]/[filename]")
|
||||
var/list/_entries = entries
|
||||
var/list/postload_required = list()
|
||||
for(var/L in lines)
|
||||
L = trim(L)
|
||||
if(!L)
|
||||
continue
|
||||
|
||||
var/firstchar = copytext(L, 1, 2)
|
||||
if(firstchar == "#")
|
||||
continue
|
||||
|
||||
var/lockthis = firstchar == "@"
|
||||
if(lockthis)
|
||||
L = copytext(L, 2)
|
||||
|
||||
var/pos = findtext(L, " ")
|
||||
var/entry = null
|
||||
var/value = null
|
||||
|
||||
if(pos)
|
||||
entry = lowertext(copytext(L, 1, pos))
|
||||
value = copytext(L, pos + 1)
|
||||
else
|
||||
entry = lowertext(L)
|
||||
|
||||
if(!entry)
|
||||
continue
|
||||
|
||||
if(entry == "$include")
|
||||
if(!value)
|
||||
log_config("Warning: Invalid $include directive: [value]")
|
||||
else
|
||||
LoadEntries(value, stack)
|
||||
++.
|
||||
continue
|
||||
|
||||
var/datum/config_entry/E = _entries[entry]
|
||||
if(!E)
|
||||
log_config("Unknown setting in configuration: '[entry]'")
|
||||
continue
|
||||
|
||||
if(lockthis)
|
||||
E.protection |= CONFIG_ENTRY_LOCKED
|
||||
|
||||
if(E.deprecated_by)
|
||||
var/datum/config_entry/new_ver = entries_by_type[E.deprecated_by]
|
||||
var/new_value = E.DeprecationUpdate(value)
|
||||
var/good_update = istext(new_value)
|
||||
log_config("Entry [entry] is deprecated and will be removed soon. Migrate to [new_ver.name]![good_update ? " Suggested new value is: [new_value]" : ""]")
|
||||
if(!warned_deprecated_configs)
|
||||
addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, "This server is using deprecated configuration settings. Please check the logs and update accordingly."), 0)
|
||||
warned_deprecated_configs = TRUE
|
||||
if(good_update)
|
||||
value = new_value
|
||||
E = new_ver
|
||||
else
|
||||
warning("[new_ver.type] is deprecated but gave no proper return for DeprecationUpdate()")
|
||||
|
||||
var/validated = E.ValidateAndSet(value, TRUE)
|
||||
if(!validated)
|
||||
log_config("Failed to validate setting \"[value]\" for [entry]")
|
||||
else
|
||||
if(E.modified && !E.dupes_allowed)
|
||||
log_config("Duplicate setting for [entry] ([value], [E.resident_file]) detected! Using latest.")
|
||||
if(E.postload_required)
|
||||
postload_required[E] = TRUE
|
||||
|
||||
E.resident_file = filename
|
||||
|
||||
if(validated)
|
||||
E.modified = TRUE
|
||||
|
||||
for(var/i in postload_required)
|
||||
var/datum/config_entry/E = i
|
||||
E.OnPostload()
|
||||
|
||||
++.
|
||||
|
||||
/datum/controller/configuration/can_vv_get(var_name)
|
||||
return (var_name != NAMEOF(src, entries_by_type) || !hiding_entries_by_type) && ..()
|
||||
|
||||
/datum/controller/configuration/vv_edit_var(var_name, var_value)
|
||||
var/list/banned_edits = list(NAMEOF(src, entries_by_type), NAMEOF(src, entries), NAMEOF(src, directory))
|
||||
return !(var_name in banned_edits) && ..()
|
||||
|
||||
/datum/controller/configuration/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Edit", src)
|
||||
stat("[name]:", statclick)
|
||||
|
||||
/datum/controller/configuration/proc/Get(entry_type)
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to retrieve an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
if((E.protection & CONFIG_ENTRY_HIDDEN) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Get" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
log_admin_private("Config access of [entry_type] attempted by [key_name(usr)]")
|
||||
return
|
||||
return E.config_entry_value
|
||||
|
||||
/datum/controller/configuration/proc/Set(entry_type, new_val)
|
||||
var/datum/config_entry/E = entry_type
|
||||
var/entry_is_abstract = initial(E.abstract_type) == entry_type
|
||||
if(entry_is_abstract)
|
||||
CRASH("Tried to set an abstract config_entry: [entry_type]")
|
||||
E = entries_by_type[entry_type]
|
||||
if(!E)
|
||||
CRASH("Missing config entry for [entry_type]!")
|
||||
if((E.protection & CONFIG_ENTRY_LOCKED) && IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "Set" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
|
||||
log_admin_private("Config rewrite of [entry_type] to [new_val] attempted by [key_name(usr)]")
|
||||
return
|
||||
return E.ValidateAndSet("[new_val]")
|
||||
|
||||
/datum/controller/configuration/proc/LoadModes()
|
||||
gamemode_cache = typecacheof(/datum/game_mode, TRUE)
|
||||
modes = list()
|
||||
mode_names = list()
|
||||
mode_reports = list()
|
||||
mode_false_report_weight = list()
|
||||
votable_modes = list()
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
for(var/T in gamemode_cache)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// for future reference: just use initial() lol
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if(M.config_tag)
|
||||
if(!(M.config_tag in modes)) // ensure each mode is added only once
|
||||
modes += M.config_tag
|
||||
mode_names[M.config_tag] = M.name
|
||||
mode_reports[M.config_tag] = M.generate_report()
|
||||
if(probabilities[M.config_tag]>0)
|
||||
mode_false_report_weight[M.config_tag] = M.false_report_weight
|
||||
else
|
||||
mode_false_report_weight[M.config_tag] = 1
|
||||
if(M.votable)
|
||||
votable_modes += M.config_tag
|
||||
qdel(M)
|
||||
votable_modes += "secret"
|
||||
|
||||
/datum/controller/configuration/proc/LoadMOTD()
|
||||
motd = file2text("[directory]/motd.txt")
|
||||
var/tm_info = GLOB.revdata.GetTestMergeInfo()
|
||||
if(motd || tm_info)
|
||||
motd = motd ? "[motd]<br>[tm_info]" : tm_info
|
||||
|
||||
/datum/controller/configuration/proc/loadmaplist(filename)
|
||||
log_config("Loading config file [filename]...")
|
||||
filename = "[directory]/[filename]"
|
||||
var/list/Lines = world.file2list(filename)
|
||||
|
||||
var/datum/map_config/currentmap = null
|
||||
for(var/t in Lines)
|
||||
if(!t)
|
||||
continue
|
||||
|
||||
t = trim(t)
|
||||
if(length(t) == 0)
|
||||
continue
|
||||
else if(copytext(t, 1, 2) == "#")
|
||||
continue
|
||||
|
||||
var/pos = findtext(t, " ")
|
||||
var/command = null
|
||||
var/data = null
|
||||
|
||||
if(pos)
|
||||
command = lowertext(copytext(t, 1, pos))
|
||||
data = copytext(t, pos + 1)
|
||||
else
|
||||
command = lowertext(t)
|
||||
|
||||
if(!command)
|
||||
continue
|
||||
|
||||
if (!currentmap && command != "map")
|
||||
continue
|
||||
|
||||
switch (command)
|
||||
if ("map")
|
||||
currentmap = load_map_config("_maps/[data].json")
|
||||
if(currentmap.defaulted)
|
||||
log_config("Failed to load map config for [data]!")
|
||||
currentmap = null
|
||||
if ("minplayers","minplayer")
|
||||
currentmap.config_min_users = text2num(data)
|
||||
if ("maxplayers","maxplayer")
|
||||
currentmap.config_max_users = text2num(data)
|
||||
if ("weight","voteweight")
|
||||
currentmap.voteweight = text2num(data)
|
||||
if ("default","defaultmap")
|
||||
defaultmap = currentmap
|
||||
if ("endmap")
|
||||
LAZYINITLIST(maplist)
|
||||
maplist[currentmap.map_name] = currentmap
|
||||
currentmap = null
|
||||
if ("disabled")
|
||||
currentmap = null
|
||||
else
|
||||
log_config("Unknown command in map vote config: '[command]'")
|
||||
|
||||
|
||||
/datum/controller/configuration/proc/pick_mode(mode_name)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// ^ This guy didn't try hard enough
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = T
|
||||
var/ct = initial(M.config_tag)
|
||||
if(ct && ct == mode_name)
|
||||
return new T
|
||||
return new /datum/game_mode/extended()
|
||||
|
||||
/datum/controller/configuration/proc/pick_storyteller(storyteller_name)
|
||||
for(var/T in storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/name = initial(S.name)
|
||||
if(name && name == storyteller_name)
|
||||
return T
|
||||
return /datum/dynamic_storyteller/classic
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
|
||||
var/list/repeated_mode_adjust = Get(/datum/config_entry/number_list/repeated_mode_adjust)
|
||||
for(var/T in gamemode_cache)
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(M.config_tag in SSvote.stored_modetier_results && SSvote.stored_modetier_results[M.config_tag] < Get(/datum/config_entry/number/dropped_modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.can_start())
|
||||
var/final_weight = probabilities[M.config_tag]
|
||||
if(SSpersistence.saved_modes.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/recent_round = min(SSpersistence.saved_modes.Find(M.config_tag),3)
|
||||
var/adjustment = 0
|
||||
while(recent_round)
|
||||
adjustment += repeated_mode_adjust[recent_round]
|
||||
recent_round = SSpersistence.saved_modes.Find(M.config_tag,recent_round+1,0)
|
||||
final_weight *= ((100-adjustment)/100)
|
||||
runnable_modes[M] = final_weight
|
||||
return runnable_modes
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_midround_modes(crew)
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
var/list/min_pop = Get(/datum/config_entry/keyed_list/min_pop)
|
||||
var/list/max_pop = Get(/datum/config_entry/keyed_list/max_pop)
|
||||
for(var/T in (gamemode_cache - SSticker.mode.type))
|
||||
var/datum/game_mode/M = new T()
|
||||
if(!(M.config_tag in modes))
|
||||
qdel(M)
|
||||
continue
|
||||
if(probabilities[M.config_tag]<=0)
|
||||
qdel(M)
|
||||
continue
|
||||
if(min_pop[M.config_tag])
|
||||
M.required_players = min_pop[M.config_tag]
|
||||
if(max_pop[M.config_tag])
|
||||
M.maximum_players = max_pop[M.config_tag]
|
||||
if(M.required_players <= crew)
|
||||
if(M.maximum_players >= 0 && M.maximum_players < crew)
|
||||
continue
|
||||
runnable_modes[M] = probabilities[M.config_tag]
|
||||
return runnable_modes
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
/datum/config_entry/string/comms_key
|
||||
protection = CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/comms_key/ValidateAndSet(str_val)
|
||||
return str_val != "default_pwd" && length(str_val) > 6 && ..()
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_TEXT
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/list/newv = list()
|
||||
for(var/I in config_entry_value)
|
||||
newv[replacetext(I, "+", " ")] = config_entry_value[I]
|
||||
config_entry_value = newv
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server/ValidateListEntry(key_name, key_value)
|
||||
return key_value != "byond:\\address:port" && ..()
|
||||
|
||||
/datum/config_entry/string/cross_comms_name
|
||||
|
||||
/datum/config_entry/string/medal_hub_address
|
||||
|
||||
/datum/config_entry/string/medal_hub_password
|
||||
/datum/config_entry/string/comms_key
|
||||
protection = CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/comms_key/ValidateAndSet(str_val)
|
||||
return str_val != "default_pwd" && length(str_val) > 6 && ..()
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_TEXT
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/list/newv = list()
|
||||
for(var/I in config_entry_value)
|
||||
newv[replacetext(I, "+", " ")] = config_entry_value[I]
|
||||
config_entry_value = newv
|
||||
|
||||
/datum/config_entry/keyed_list/cross_server/ValidateListEntry(key_name, key_value)
|
||||
return key_value != "byond:\\address:port" && ..()
|
||||
|
||||
/datum/config_entry/string/cross_comms_name
|
||||
|
||||
/datum/config_entry/string/medal_hub_address
|
||||
|
||||
/datum/config_entry/string/medal_hub_password
|
||||
protection = CONFIG_ENTRY_HIDDEN
|
||||
@@ -1,51 +1,51 @@
|
||||
/datum/config_entry/flag/sql_enabled // for sql switching
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/address
|
||||
config_entry_value = "localhost"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/number/port
|
||||
config_entry_value = 3306
|
||||
min_val = 0
|
||||
max_val = 65535
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_database
|
||||
config_entry_value = "test"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_login
|
||||
config_entry_value = "root"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_password
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_tableprefix
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/number/query_debug_log_timeout
|
||||
config_entry_value = 70
|
||||
min_val = 1
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
deprecated_by = /datum/config_entry/number/blocking_query_timeout
|
||||
|
||||
/datum/config_entry/number/query_debug_log_timeout/DeprecationUpdate(value)
|
||||
return value
|
||||
|
||||
/datum/config_entry/number/async_query_timeout
|
||||
config_entry_value = 10
|
||||
min_val = 0
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/number/blocking_query_timeout
|
||||
config_entry_value = 5
|
||||
min_val = 0
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/number/bsql_thread_limit
|
||||
config_entry_value = 50
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/flag/bsql_debug
|
||||
/datum/config_entry/flag/sql_enabled // for sql switching
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/address
|
||||
config_entry_value = "localhost"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/number/port
|
||||
config_entry_value = 3306
|
||||
min_val = 0
|
||||
max_val = 65535
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_database
|
||||
config_entry_value = "test"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_login
|
||||
config_entry_value = "root"
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_password
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/string/feedback_tableprefix
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/number/query_debug_log_timeout
|
||||
config_entry_value = 70
|
||||
min_val = 1
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
deprecated_by = /datum/config_entry/number/blocking_query_timeout
|
||||
|
||||
/datum/config_entry/number/query_debug_log_timeout/DeprecationUpdate(value)
|
||||
return value
|
||||
|
||||
/datum/config_entry/number/async_query_timeout
|
||||
config_entry_value = 10
|
||||
min_val = 0
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/number/blocking_query_timeout
|
||||
config_entry_value = 5
|
||||
min_val = 0
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/number/bsql_thread_limit
|
||||
config_entry_value = 50
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/flag/bsql_debug
|
||||
|
||||
@@ -1,402 +1,402 @@
|
||||
/datum/config_entry/number_list/repeated_mode_adjust
|
||||
|
||||
/datum/config_entry/keyed_list/probability
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/probability/ValidateListEntry(key_name)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/max_pop
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/max_pop/ValidateListEntry(key_name)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/min_pop
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/min_pop/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/continuous // which roundtypes continue if all antagonists die
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/continuous/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/midround_antag // which roundtypes use the midround antagonist system
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/midround_antag/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/policy
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_TEXT
|
||||
|
||||
/datum/config_entry/number/damage_multiplier
|
||||
config_entry_value = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/minimal_access_threshold //If the number of players is larger than this threshold, minimal access will be turned on.
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/jobs_have_minimal_access //determines whether jobs use minimal access or expanded access.
|
||||
|
||||
/datum/config_entry/flag/assistants_have_maint_access
|
||||
|
||||
/datum/config_entry/flag/security_has_maint_access
|
||||
|
||||
/datum/config_entry/flag/everyone_has_maint_access
|
||||
|
||||
/datum/config_entry/flag/sec_start_brig //makes sec start in brig instead of dept sec posts
|
||||
|
||||
/datum/config_entry/flag/force_random_names
|
||||
|
||||
/datum/config_entry/flag/humans_need_surnames
|
||||
|
||||
/datum/config_entry/flag/allow_ai // allow ai job
|
||||
|
||||
/datum/config_entry/flag/allow_ai_multicam //whether the AI can use their multicam
|
||||
|
||||
/datum/config_entry/flag/disable_human_mood
|
||||
|
||||
/datum/config_entry/flag/disable_borg_flash_knockdown //Should borg flashes be capable of knocking humanoid entities down?
|
||||
|
||||
/datum/config_entry/flag/weaken_secborg //Brings secborgs and k9s back in-line with the other borg modules
|
||||
|
||||
/datum/config_entry/flag/disable_secborg // disallow secborg module to be chosen.
|
||||
|
||||
/datum/config_entry/flag/disable_peaceborg
|
||||
|
||||
/datum/config_entry/number/minimum_secborg_alert //Minimum alert level for secborgs to be chosen.
|
||||
config_entry_value = 3
|
||||
|
||||
/datum/config_entry/number/traitor_scaling_coeff //how much does the amount of players get divided by to determine traitors
|
||||
config_entry_value = 6
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/brother_scaling_coeff //how many players per brother team
|
||||
config_entry_value = 25
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/changeling_scaling_coeff //how much does the amount of players get divided by to determine changelings
|
||||
config_entry_value = 6
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/security_scaling_coeff //how much does the amount of players get divided by to determine open security officer positions
|
||||
config_entry_value = 8
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/abductor_scaling_coeff //how many players per abductor team
|
||||
config_entry_value = 15
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/traitor_objectives_amount
|
||||
config_entry_value = 2
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/brother_objectives_amount
|
||||
config_entry_value = 2
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/reactionary_explosions //If we use reactionary explosions, explosions that react to walls and doors
|
||||
|
||||
/datum/config_entry/flag/protect_roles_from_antagonist //If security and such can be traitor/cult/other
|
||||
|
||||
/datum/config_entry/flag/protect_assistant_from_antagonist //If assistants can be traitor/cult/other
|
||||
|
||||
/datum/config_entry/flag/enforce_human_authority //If non-human species are barred from joining as a head of staff
|
||||
|
||||
/datum/config_entry/flag/allow_latejoin_antagonists // If late-joining players can be traitor/changeling
|
||||
|
||||
/datum/config_entry/flag/use_antag_rep // see game_options.txt for details
|
||||
|
||||
/datum/config_entry/number/antag_rep_maximum
|
||||
config_entry_value = 200
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/default_antag_tickets
|
||||
config_entry_value = 100
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/max_tickets_per_roll
|
||||
config_entry_value = 100
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/midround_antag_time_check // How late (in minutes you want the midround antag system to stay on, setting this to 0 will disable the system)
|
||||
config_entry_value = 60
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/midround_antag_life_check // A ratio of how many people need to be alive in order for the round not to immediately end in midround antagonist
|
||||
config_entry_value = 0.7
|
||||
integer = FALSE
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
|
||||
/datum/config_entry/number/suicide_reenter_round_timer
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roundstart_suicide_time_limit
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/shuttle_refuel_delay
|
||||
config_entry_value = 12000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/show_game_type_odds //if set this allows players to see the odds of each roundtype on the get revision screen
|
||||
|
||||
/datum/config_entry/keyed_list/roundstart_races //races you can play as from the get go.
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/flag/join_with_mutant_humans //players can pick mutant bodyparts for humans before joining the game
|
||||
|
||||
/datum/config_entry/flag/no_summon_guns //No
|
||||
|
||||
/datum/config_entry/flag/no_summon_magic //Fun
|
||||
|
||||
/datum/config_entry/flag/no_summon_events //Allowed
|
||||
|
||||
/datum/config_entry/flag/no_intercept_report //Whether or not to send a communications intercept report roundstart. This may be overridden by gamemodes.
|
||||
|
||||
/datum/config_entry/number/arrivals_shuttle_dock_window //Time from when a player late joins on the arrivals shuttle to when the shuttle docks on the station
|
||||
config_entry_value = 55
|
||||
min_val = 30
|
||||
|
||||
/datum/config_entry/flag/arrivals_shuttle_require_undocked //Require the arrivals shuttle to be undocked before latejoiners can join
|
||||
|
||||
/datum/config_entry/flag/arrivals_shuttle_require_safe_latejoin //Require the arrivals shuttle to be operational in order for latejoiners to join
|
||||
|
||||
/datum/config_entry/string/alert_green
|
||||
config_entry_value = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced."
|
||||
|
||||
/datum/config_entry/string/alert_blue_upto
|
||||
config_entry_value = "The station has received reliable information about potential threats to the station. Security staff may have weapons visible, random searches are permitted."
|
||||
|
||||
/datum/config_entry/string/alert_blue_downto
|
||||
config_entry_value = "Significant confirmed threats have been neutralized. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still permitted."
|
||||
|
||||
/datum/config_entry/string/alert_amber_upto
|
||||
config_entry_value = "There are significant confirmed threats to the station. Security staff may have weapons unholstered at all times. Random searches are allowed and advised."
|
||||
|
||||
/datum/config_entry/string/alert_amber_downto
|
||||
config_entry_value = "The immediate threat has passed. Security is no longer authorized to use lethal force, but may continue to have weapons drawn. Access requirements have been restored."
|
||||
|
||||
/datum/config_entry/string/alert_red_upto
|
||||
config_entry_value = "There is an immediate serious threat to the station. Security is now authorized to use lethal force. Additionally, access requirements on some machines have been lifted."
|
||||
|
||||
/datum/config_entry/string/alert_red_downto
|
||||
config_entry_value = "The station's destruction has been averted. There is still however an immediate serious threat to the station. Security is still authorized to use lethal force."
|
||||
|
||||
/datum/config_entry/string/alert_delta
|
||||
config_entry_value = "Destruction of the station is imminent. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill."
|
||||
|
||||
/datum/config_entry/flag/revival_pod_plants
|
||||
|
||||
/datum/config_entry/flag/revival_cloning
|
||||
|
||||
/datum/config_entry/number/revival_brain_life
|
||||
config_entry_value = -1
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/flag/ooc_during_round
|
||||
|
||||
/datum/config_entry/flag/emojis
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
key_mode = KEY_MODE_TYPE
|
||||
value_mode = VALUE_MODE_NUM
|
||||
config_entry_value = list( //DEFAULTS
|
||||
/mob/living/simple_animal = 1,
|
||||
/mob/living/silicon/pai = 1,
|
||||
/mob/living/carbon/alien/humanoid/hunter = -1,
|
||||
/mob/living/carbon/alien/humanoid/royal/praetorian = 1,
|
||||
/mob/living/carbon/alien/humanoid/royal/queen = 3
|
||||
)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/ValidateAndSet()
|
||||
. = ..()
|
||||
if(.)
|
||||
update_config_movespeed_type_lookup(TRUE)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(. && (var_name == NAMEOF(src, config_entry_value)))
|
||||
update_config_movespeed_type_lookup(TRUE)
|
||||
|
||||
/datum/config_entry/number/movedelay //Used for modifying movement speed for mobs.
|
||||
abstract_type = /datum/config_entry/number/movedelay
|
||||
|
||||
/datum/config_entry/number/movedelay/ValidateAndSet()
|
||||
. = ..()
|
||||
if(.)
|
||||
update_mob_config_movespeeds()
|
||||
|
||||
/datum/config_entry/number/movedelay/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(. && (var_name == NAMEOF(src, config_entry_value)))
|
||||
update_mob_config_movespeeds()
|
||||
|
||||
/datum/config_entry/number/movedelay/run_delay
|
||||
|
||||
/datum/config_entry/number/movedelay/walk_delay
|
||||
|
||||
/////////////////////////////////////////////////Outdated move delay
|
||||
/datum/config_entry/number/outdated_movedelay
|
||||
deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
abstract_type = /datum/config_entry/number/outdated_movedelay
|
||||
|
||||
var/movedelay_type
|
||||
|
||||
/datum/config_entry/number/outdated_movedelay/DeprecationUpdate(value)
|
||||
return "[movedelay_type] [value]"
|
||||
|
||||
/datum/config_entry/number/outdated_movedelay/human_delay
|
||||
movedelay_type = /mob/living/carbon/human
|
||||
/datum/config_entry/number/outdated_movedelay/robot_delay
|
||||
movedelay_type = /mob/living/silicon/robot
|
||||
/datum/config_entry/number/outdated_movedelay/monkey_delay
|
||||
movedelay_type = /mob/living/carbon/monkey
|
||||
/datum/config_entry/number/outdated_movedelay/alien_delay
|
||||
movedelay_type = /mob/living/carbon/alien
|
||||
/datum/config_entry/number/outdated_movedelay/slime_delay
|
||||
movedelay_type = /mob/living/simple_animal/slime
|
||||
/datum/config_entry/number/outdated_movedelay/animal_delay
|
||||
movedelay_type = /mob/living/simple_animal
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/datum/config_entry/flag/roundstart_away //Will random away mission be loaded.
|
||||
|
||||
/datum/config_entry/number/gateway_delay //How long the gateway takes before it activates. Default is half an hour. Only matters if roundstart_away is enabled.
|
||||
config_entry_value = 18000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/ghost_interaction
|
||||
|
||||
/datum/config_entry/flag/silent_ai
|
||||
/datum/config_entry/flag/silent_borg
|
||||
|
||||
/datum/config_entry/flag/sandbox_autoclose // close the sandbox panel after spawning an item, potentially reducing griff
|
||||
|
||||
/datum/config_entry/number/default_laws //Controls what laws the AI spawns with.
|
||||
config_entry_value = 0
|
||||
min_val = 0
|
||||
max_val = 3
|
||||
|
||||
/datum/config_entry/number/silicon_max_law_amount
|
||||
config_entry_value = 12
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/keyed_list/random_laws
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/law_weight
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
splitter = ","
|
||||
|
||||
/datum/config_entry/number/overflow_cap
|
||||
config_entry_value = -1
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/string/overflow_job
|
||||
config_entry_value = "Assistant"
|
||||
|
||||
/datum/config_entry/flag/starlight
|
||||
/datum/config_entry/flag/grey_assistants
|
||||
|
||||
/datum/config_entry/number/lavaland_budget
|
||||
config_entry_value = 60
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/space_budget
|
||||
config_entry_value = 16
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/allow_random_events // Enables random events mid-round when set
|
||||
|
||||
/datum/config_entry/number/events_min_time_mul // Multipliers for random events minimal starting time and minimal players amounts
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/events_min_players_mul
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/mice_roundstart
|
||||
config_entry_value = 10
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/bombcap
|
||||
config_entry_value = 14
|
||||
min_val = 4
|
||||
|
||||
/datum/config_entry/number/bombcap/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.MAX_EX_DEVESTATION_RANGE = round(config_entry_value / 4)
|
||||
GLOB.MAX_EX_HEAVY_RANGE = round(config_entry_value / 2)
|
||||
GLOB.MAX_EX_LIGHT_RANGE = config_entry_value
|
||||
GLOB.MAX_EX_FLASH_RANGE = config_entry_value
|
||||
GLOB.MAX_EX_FLAME_RANGE = config_entry_value
|
||||
|
||||
/datum/config_entry/number/emergency_shuttle_autocall_threshold
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/ic_printing
|
||||
|
||||
/datum/config_entry/flag/roundstart_traits
|
||||
|
||||
/datum/config_entry/flag/enable_night_shifts
|
||||
|
||||
/datum/config_entry/flag/randomize_shift_time
|
||||
|
||||
/datum/config_entry/flag/shift_time_realtime
|
||||
|
||||
/datum/config_entry/keyed_list/antag_rep
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/number/monkeycap
|
||||
config_entry_value = 64
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/disable_stambuffer
|
||||
|
||||
/datum/config_entry/keyed_list/box_random_engine
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
lowercase = FALSE
|
||||
splitter = ","
|
||||
|
||||
/datum/config_entry/number/auto_transfer_delay
|
||||
config_entry_value = 72000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/pai_custom_holoforms
|
||||
|
||||
/datum/config_entry/number/marauder_delay_non_reebe
|
||||
config_entry_value = 1800
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/allow_clockwork_marauder_on_station
|
||||
config_entry_value = TRUE
|
||||
|
||||
/datum/config_entry/flag/modetier_voting
|
||||
config_entry_value = TRUE
|
||||
|
||||
/datum/config_entry/number/dropped_modes
|
||||
config_entry_value = 3
|
||||
/datum/config_entry/number_list/repeated_mode_adjust
|
||||
|
||||
/datum/config_entry/keyed_list/probability
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/probability/ValidateListEntry(key_name)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/max_pop
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/max_pop/ValidateListEntry(key_name)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/min_pop
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/keyed_list/min_pop/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/continuous // which roundtypes continue if all antagonists die
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/continuous/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/midround_antag // which roundtypes use the midround antagonist system
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/midround_antag/ValidateListEntry(key_name, key_value)
|
||||
return key_name in config.modes
|
||||
|
||||
/datum/config_entry/keyed_list/policy
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_TEXT
|
||||
|
||||
/datum/config_entry/number/damage_multiplier
|
||||
config_entry_value = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/minimal_access_threshold //If the number of players is larger than this threshold, minimal access will be turned on.
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/jobs_have_minimal_access //determines whether jobs use minimal access or expanded access.
|
||||
|
||||
/datum/config_entry/flag/assistants_have_maint_access
|
||||
|
||||
/datum/config_entry/flag/security_has_maint_access
|
||||
|
||||
/datum/config_entry/flag/everyone_has_maint_access
|
||||
|
||||
/datum/config_entry/flag/sec_start_brig //makes sec start in brig instead of dept sec posts
|
||||
|
||||
/datum/config_entry/flag/force_random_names
|
||||
|
||||
/datum/config_entry/flag/humans_need_surnames
|
||||
|
||||
/datum/config_entry/flag/allow_ai // allow ai job
|
||||
|
||||
/datum/config_entry/flag/allow_ai_multicam //whether the AI can use their multicam
|
||||
|
||||
/datum/config_entry/flag/disable_human_mood
|
||||
|
||||
/datum/config_entry/flag/disable_borg_flash_knockdown //Should borg flashes be capable of knocking humanoid entities down?
|
||||
|
||||
/datum/config_entry/flag/weaken_secborg //Brings secborgs and k9s back in-line with the other borg modules
|
||||
|
||||
/datum/config_entry/flag/disable_secborg // disallow secborg module to be chosen.
|
||||
|
||||
/datum/config_entry/flag/disable_peaceborg
|
||||
|
||||
/datum/config_entry/number/minimum_secborg_alert //Minimum alert level for secborgs to be chosen.
|
||||
config_entry_value = 3
|
||||
|
||||
/datum/config_entry/number/traitor_scaling_coeff //how much does the amount of players get divided by to determine traitors
|
||||
config_entry_value = 6
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/brother_scaling_coeff //how many players per brother team
|
||||
config_entry_value = 25
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/changeling_scaling_coeff //how much does the amount of players get divided by to determine changelings
|
||||
config_entry_value = 6
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/security_scaling_coeff //how much does the amount of players get divided by to determine open security officer positions
|
||||
config_entry_value = 8
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/abductor_scaling_coeff //how many players per abductor team
|
||||
config_entry_value = 15
|
||||
min_val = 1
|
||||
|
||||
/datum/config_entry/number/traitor_objectives_amount
|
||||
config_entry_value = 2
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/brother_objectives_amount
|
||||
config_entry_value = 2
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/reactionary_explosions //If we use reactionary explosions, explosions that react to walls and doors
|
||||
|
||||
/datum/config_entry/flag/protect_roles_from_antagonist //If security and such can be traitor/cult/other
|
||||
|
||||
/datum/config_entry/flag/protect_assistant_from_antagonist //If assistants can be traitor/cult/other
|
||||
|
||||
/datum/config_entry/flag/enforce_human_authority //If non-human species are barred from joining as a head of staff
|
||||
|
||||
/datum/config_entry/flag/allow_latejoin_antagonists // If late-joining players can be traitor/changeling
|
||||
|
||||
/datum/config_entry/flag/use_antag_rep // see game_options.txt for details
|
||||
|
||||
/datum/config_entry/number/antag_rep_maximum
|
||||
config_entry_value = 200
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/default_antag_tickets
|
||||
config_entry_value = 100
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/max_tickets_per_roll
|
||||
config_entry_value = 100
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/midround_antag_time_check // How late (in minutes you want the midround antag system to stay on, setting this to 0 will disable the system)
|
||||
config_entry_value = 60
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/midround_antag_life_check // A ratio of how many people need to be alive in order for the round not to immediately end in midround antagonist
|
||||
config_entry_value = 0.7
|
||||
integer = FALSE
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
|
||||
/datum/config_entry/number/suicide_reenter_round_timer
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/roundstart_suicide_time_limit
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/shuttle_refuel_delay
|
||||
config_entry_value = 12000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/show_game_type_odds //if set this allows players to see the odds of each roundtype on the get revision screen
|
||||
|
||||
/datum/config_entry/keyed_list/roundstart_races //races you can play as from the get go.
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/flag/join_with_mutant_humans //players can pick mutant bodyparts for humans before joining the game
|
||||
|
||||
/datum/config_entry/flag/no_summon_guns //No
|
||||
|
||||
/datum/config_entry/flag/no_summon_magic //Fun
|
||||
|
||||
/datum/config_entry/flag/no_summon_events //Allowed
|
||||
|
||||
/datum/config_entry/flag/no_intercept_report //Whether or not to send a communications intercept report roundstart. This may be overridden by gamemodes.
|
||||
|
||||
/datum/config_entry/number/arrivals_shuttle_dock_window //Time from when a player late joins on the arrivals shuttle to when the shuttle docks on the station
|
||||
config_entry_value = 55
|
||||
min_val = 30
|
||||
|
||||
/datum/config_entry/flag/arrivals_shuttle_require_undocked //Require the arrivals shuttle to be undocked before latejoiners can join
|
||||
|
||||
/datum/config_entry/flag/arrivals_shuttle_require_safe_latejoin //Require the arrivals shuttle to be operational in order for latejoiners to join
|
||||
|
||||
/datum/config_entry/string/alert_green
|
||||
config_entry_value = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced."
|
||||
|
||||
/datum/config_entry/string/alert_blue_upto
|
||||
config_entry_value = "The station has received reliable information about potential threats to the station. Security staff may have weapons visible, random searches are permitted."
|
||||
|
||||
/datum/config_entry/string/alert_blue_downto
|
||||
config_entry_value = "Significant confirmed threats have been neutralized. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still permitted."
|
||||
|
||||
/datum/config_entry/string/alert_amber_upto
|
||||
config_entry_value = "There are significant confirmed threats to the station. Security staff may have weapons unholstered at all times. Random searches are allowed and advised."
|
||||
|
||||
/datum/config_entry/string/alert_amber_downto
|
||||
config_entry_value = "The immediate threat has passed. Security is no longer authorized to use lethal force, but may continue to have weapons drawn. Access requirements have been restored."
|
||||
|
||||
/datum/config_entry/string/alert_red_upto
|
||||
config_entry_value = "There is an immediate serious threat to the station. Security is now authorized to use lethal force. Additionally, access requirements on some machines have been lifted."
|
||||
|
||||
/datum/config_entry/string/alert_red_downto
|
||||
config_entry_value = "The station's destruction has been averted. There is still however an immediate serious threat to the station. Security is still authorized to use lethal force."
|
||||
|
||||
/datum/config_entry/string/alert_delta
|
||||
config_entry_value = "Destruction of the station is imminent. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill."
|
||||
|
||||
/datum/config_entry/flag/revival_pod_plants
|
||||
|
||||
/datum/config_entry/flag/revival_cloning
|
||||
|
||||
/datum/config_entry/number/revival_brain_life
|
||||
config_entry_value = -1
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/flag/ooc_during_round
|
||||
|
||||
/datum/config_entry/flag/emojis
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
key_mode = KEY_MODE_TYPE
|
||||
value_mode = VALUE_MODE_NUM
|
||||
config_entry_value = list( //DEFAULTS
|
||||
/mob/living/simple_animal = 1,
|
||||
/mob/living/silicon/pai = 1,
|
||||
/mob/living/carbon/alien/humanoid/hunter = -1,
|
||||
/mob/living/carbon/alien/humanoid/royal/praetorian = 1,
|
||||
/mob/living/carbon/alien/humanoid/royal/queen = 3
|
||||
)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/ValidateAndSet()
|
||||
. = ..()
|
||||
if(.)
|
||||
update_config_movespeed_type_lookup(TRUE)
|
||||
|
||||
/datum/config_entry/keyed_list/multiplicative_movespeed/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(. && (var_name == NAMEOF(src, config_entry_value)))
|
||||
update_config_movespeed_type_lookup(TRUE)
|
||||
|
||||
/datum/config_entry/number/movedelay //Used for modifying movement speed for mobs.
|
||||
abstract_type = /datum/config_entry/number/movedelay
|
||||
|
||||
/datum/config_entry/number/movedelay/ValidateAndSet()
|
||||
. = ..()
|
||||
if(.)
|
||||
update_mob_config_movespeeds()
|
||||
|
||||
/datum/config_entry/number/movedelay/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(. && (var_name == NAMEOF(src, config_entry_value)))
|
||||
update_mob_config_movespeeds()
|
||||
|
||||
/datum/config_entry/number/movedelay/run_delay
|
||||
|
||||
/datum/config_entry/number/movedelay/walk_delay
|
||||
|
||||
/////////////////////////////////////////////////Outdated move delay
|
||||
/datum/config_entry/number/outdated_movedelay
|
||||
deprecated_by = /datum/config_entry/keyed_list/multiplicative_movespeed
|
||||
abstract_type = /datum/config_entry/number/outdated_movedelay
|
||||
|
||||
var/movedelay_type
|
||||
|
||||
/datum/config_entry/number/outdated_movedelay/DeprecationUpdate(value)
|
||||
return "[movedelay_type] [value]"
|
||||
|
||||
/datum/config_entry/number/outdated_movedelay/human_delay
|
||||
movedelay_type = /mob/living/carbon/human
|
||||
/datum/config_entry/number/outdated_movedelay/robot_delay
|
||||
movedelay_type = /mob/living/silicon/robot
|
||||
/datum/config_entry/number/outdated_movedelay/monkey_delay
|
||||
movedelay_type = /mob/living/carbon/monkey
|
||||
/datum/config_entry/number/outdated_movedelay/alien_delay
|
||||
movedelay_type = /mob/living/carbon/alien
|
||||
/datum/config_entry/number/outdated_movedelay/slime_delay
|
||||
movedelay_type = /mob/living/simple_animal/slime
|
||||
/datum/config_entry/number/outdated_movedelay/animal_delay
|
||||
movedelay_type = /mob/living/simple_animal
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
/datum/config_entry/flag/roundstart_away //Will random away mission be loaded.
|
||||
|
||||
/datum/config_entry/number/gateway_delay //How long the gateway takes before it activates. Default is half an hour. Only matters if roundstart_away is enabled.
|
||||
config_entry_value = 18000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/ghost_interaction
|
||||
|
||||
/datum/config_entry/flag/silent_ai
|
||||
/datum/config_entry/flag/silent_borg
|
||||
|
||||
/datum/config_entry/flag/sandbox_autoclose // close the sandbox panel after spawning an item, potentially reducing griff
|
||||
|
||||
/datum/config_entry/number/default_laws //Controls what laws the AI spawns with.
|
||||
config_entry_value = 0
|
||||
min_val = 0
|
||||
max_val = 3
|
||||
|
||||
/datum/config_entry/number/silicon_max_law_amount
|
||||
config_entry_value = 12
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/keyed_list/random_laws
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_FLAG
|
||||
|
||||
/datum/config_entry/keyed_list/law_weight
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
splitter = ","
|
||||
|
||||
/datum/config_entry/number/overflow_cap
|
||||
config_entry_value = -1
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/string/overflow_job
|
||||
config_entry_value = "Assistant"
|
||||
|
||||
/datum/config_entry/flag/starlight
|
||||
/datum/config_entry/flag/grey_assistants
|
||||
|
||||
/datum/config_entry/number/lavaland_budget
|
||||
config_entry_value = 60
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/space_budget
|
||||
config_entry_value = 16
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/allow_random_events // Enables random events mid-round when set
|
||||
|
||||
/datum/config_entry/number/events_min_time_mul // Multipliers for random events minimal starting time and minimal players amounts
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/events_min_players_mul
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/mice_roundstart
|
||||
config_entry_value = 10
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/bombcap
|
||||
config_entry_value = 14
|
||||
min_val = 4
|
||||
|
||||
/datum/config_entry/number/bombcap/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
GLOB.MAX_EX_DEVESTATION_RANGE = round(config_entry_value / 4)
|
||||
GLOB.MAX_EX_HEAVY_RANGE = round(config_entry_value / 2)
|
||||
GLOB.MAX_EX_LIGHT_RANGE = config_entry_value
|
||||
GLOB.MAX_EX_FLASH_RANGE = config_entry_value
|
||||
GLOB.MAX_EX_FLAME_RANGE = config_entry_value
|
||||
|
||||
/datum/config_entry/number/emergency_shuttle_autocall_threshold
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/ic_printing
|
||||
|
||||
/datum/config_entry/flag/roundstart_traits
|
||||
|
||||
/datum/config_entry/flag/enable_night_shifts
|
||||
|
||||
/datum/config_entry/flag/randomize_shift_time
|
||||
|
||||
/datum/config_entry/flag/shift_time_realtime
|
||||
|
||||
/datum/config_entry/keyed_list/antag_rep
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
|
||||
/datum/config_entry/number/monkeycap
|
||||
config_entry_value = 64
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/disable_stambuffer
|
||||
|
||||
/datum/config_entry/keyed_list/box_random_engine
|
||||
key_mode = KEY_MODE_TEXT
|
||||
value_mode = VALUE_MODE_NUM
|
||||
lowercase = FALSE
|
||||
splitter = ","
|
||||
|
||||
/datum/config_entry/number/auto_transfer_delay
|
||||
config_entry_value = 72000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/pai_custom_holoforms
|
||||
|
||||
/datum/config_entry/number/marauder_delay_non_reebe
|
||||
config_entry_value = 1800
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/allow_clockwork_marauder_on_station
|
||||
config_entry_value = TRUE
|
||||
|
||||
/datum/config_entry/flag/modetier_voting
|
||||
config_entry_value = TRUE
|
||||
|
||||
/datum/config_entry/number/dropped_modes
|
||||
config_entry_value = 3
|
||||
|
||||
@@ -1,431 +1,431 @@
|
||||
/datum/config_entry/flag/autoadmin // if autoadmin is enabled
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/autoadmin_rank // the rank for autoadmins
|
||||
config_entry_value = "Game Master"
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/servername // server name (the name of the game window)
|
||||
|
||||
/datum/config_entry/string/servertagline
|
||||
config_entry_value = "We forgot to set the server's tagline in config.txt"
|
||||
|
||||
/datum/config_entry/string/serversqlname // short form server name used for the DB
|
||||
|
||||
/datum/config_entry/string/stationname // station name (the name of the station in-game)
|
||||
|
||||
/datum/config_entry/number/lobby_countdown // In between round countdown.
|
||||
config_entry_value = 120
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/round_end_countdown // Post round murder death kill countdown
|
||||
config_entry_value = 25
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/hub // if the game appears on the hub or not
|
||||
|
||||
/datum/config_entry/flag/log_ooc // log OOC channel
|
||||
|
||||
/datum/config_entry/flag/log_access // log login/logout
|
||||
|
||||
/datum/config_entry/flag/log_say // log client say
|
||||
|
||||
/datum/config_entry/flag/log_admin // log admin actions
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/log_prayer // log prayers
|
||||
|
||||
/datum/config_entry/flag/log_law // log lawchanges
|
||||
|
||||
/datum/config_entry/flag/log_game // log game events
|
||||
|
||||
/datum/config_entry/flag/log_virus // log virology data
|
||||
|
||||
/datum/config_entry/flag/log_vote // log voting
|
||||
|
||||
/datum/config_entry/flag/log_whisper // log client whisper
|
||||
|
||||
/datum/config_entry/flag/log_attack // log attack messages
|
||||
|
||||
/datum/config_entry/flag/log_emote // log emotes
|
||||
|
||||
/datum/config_entry/flag/log_adminchat // log admin chat messages
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/log_pda // log pda messages
|
||||
|
||||
/datum/config_entry/flag/log_telecomms // log telecomms messages
|
||||
|
||||
/datum/config_entry/flag/log_twitter // log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
|
||||
|
||||
/datum/config_entry/flag/log_world_topic // log all world.Topic() calls
|
||||
|
||||
/datum/config_entry/flag/log_manifest // log crew manifest to seperate file
|
||||
|
||||
/datum/config_entry/flag/log_job_debug // log roundstart divide occupations debug information to a file
|
||||
|
||||
/datum/config_entry/flag/allow_admin_ooccolor // Allows admins with relevant permissions to have their own ooc colour
|
||||
|
||||
/datum/config_entry/flag/allow_vote_restart // allow votes to restart
|
||||
|
||||
/datum/config_entry/flag/allow_vote_mode // allow votes to change mode
|
||||
|
||||
/datum/config_entry/number/vote_delay // minimum time between voting sessions (deciseconds, 10 minute default)
|
||||
config_entry_value = 6000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/vote_period // length of voting period (deciseconds, default 1 minute)
|
||||
config_entry_value = 600
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/default_no_vote // vote does not default to nochange/norestart
|
||||
|
||||
/datum/config_entry/flag/no_dead_vote // dead people can't vote
|
||||
|
||||
/datum/config_entry/flag/allow_metadata // Metadata is supported.
|
||||
|
||||
/datum/config_entry/flag/popup_admin_pm // adminPMs to non-admins show in a pop-up 'reply' window when set
|
||||
|
||||
/datum/config_entry/number/fps
|
||||
config_entry_value = 20
|
||||
min_val = 1
|
||||
max_val = 100 //byond will start crapping out at 50, so this is just ridic
|
||||
var/sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/fps/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
sync_validate = TRUE
|
||||
var/datum/config_entry/number/ticklag/TL = config.entries_by_type[/datum/config_entry/number/ticklag]
|
||||
if(!TL.sync_validate)
|
||||
TL.ValidateAndSet(10 / config_entry_value)
|
||||
sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/ticklag
|
||||
integer = FALSE
|
||||
var/sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/ticklag/New() //ticklag weirdly just mirrors fps
|
||||
var/datum/config_entry/CE = /datum/config_entry/number/fps
|
||||
config_entry_value = 10 / initial(CE.config_entry_value)
|
||||
..()
|
||||
|
||||
/datum/config_entry/number/ticklag/ValidateAndSet(str_val)
|
||||
. = text2num(str_val) > 0 && ..()
|
||||
if(.)
|
||||
sync_validate = TRUE
|
||||
var/datum/config_entry/number/fps/FPS = config.entries_by_type[/datum/config_entry/number/fps]
|
||||
if(!FPS.sync_validate)
|
||||
FPS.ValidateAndSet(10 / config_entry_value)
|
||||
sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/flag/allow_holidays
|
||||
|
||||
/datum/config_entry/number/tick_limit_mc_init //SSinitialization throttling
|
||||
config_entry_value = TICK_LIMIT_MC_INIT_DEFAULT
|
||||
min_val = 0 //oranges warned us
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/protect_legacy_admins //Stops any admins loaded by the legacy system from having their rank edited by the permissions panel
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/protect_legacy_ranks //Stops any ranks loaded by the legacy system from having their flags edited by the permissions panel
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/enable_localhost_rank //Gives the !localhost! rank to any client connecting from 127.0.0.1 or ::1
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/mentors_mobname_only
|
||||
|
||||
/datum/config_entry/flag/mentor_legacy_system //Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/hostedby
|
||||
|
||||
/datum/config_entry/flag/norespawn
|
||||
|
||||
/datum/config_entry/flag/guest_jobban
|
||||
|
||||
/datum/config_entry/flag/usewhitelist
|
||||
|
||||
/datum/config_entry/flag/ban_legacy_system //Defines whether the server uses the legacy banning system with the files in /data or the SQL system.
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/use_age_restriction_for_jobs //Do jobs use account age restrictions? --requires database
|
||||
|
||||
/datum/config_entry/flag/use_account_age_for_jobs //Uses the time they made the account for the job restriction stuff. New player joining alerts should be unaffected.
|
||||
|
||||
/datum/config_entry/flag/use_exp_tracking
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_heads
|
||||
|
||||
/datum/config_entry/number/use_exp_restrictions_heads_hours
|
||||
config_entry_value = 0
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_heads_department
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_other
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_admin_bypass
|
||||
|
||||
/datum/config_entry/string/server
|
||||
|
||||
/datum/config_entry/string/banappeals
|
||||
|
||||
/datum/config_entry/string/wikiurl
|
||||
config_entry_value = "https://katlin.dog/citadel-wiki"
|
||||
|
||||
/datum/config_entry/string/wikiurltg
|
||||
config_entry_value = "http://www.tgstation13.org/wiki"
|
||||
|
||||
/datum/config_entry/string/forumurl
|
||||
config_entry_value = "http://tgstation13.org/phpBB/index.php"
|
||||
|
||||
/datum/config_entry/string/rulesurl
|
||||
config_entry_value = "http://www.tgstation13.org/wiki/Rules"
|
||||
|
||||
/datum/config_entry/string/githuburl
|
||||
config_entry_value = "https://www.github.com/tgstation/-tg-station"
|
||||
|
||||
/datum/config_entry/string/roundstatsurl
|
||||
|
||||
/datum/config_entry/string/gamelogurl
|
||||
|
||||
/datum/config_entry/number/githubrepoid
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/guest_ban
|
||||
|
||||
/datum/config_entry/number/id_console_jobslot_delay
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/inactivity_period //time in ds until a player is considered inactive
|
||||
config_entry_value = 3000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/inactivity_period/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
config_entry_value *= 10 //documented as seconds in config.txt
|
||||
|
||||
/datum/config_entry/number/afk_period //time in ds until a player is considered inactive
|
||||
config_entry_value = 3000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/afk_period/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
config_entry_value *= 10 //documented as seconds in config.txt
|
||||
|
||||
/datum/config_entry/flag/kick_inactive //force disconnect for inactive players
|
||||
|
||||
/datum/config_entry/flag/load_jobs_from_txt
|
||||
|
||||
/datum/config_entry/flag/forbid_singulo_possession
|
||||
|
||||
/datum/config_entry/flag/automute_on //enables automuting/spam prevention
|
||||
|
||||
/datum/config_entry/string/panic_server_name
|
||||
|
||||
/datum/config_entry/string/panic_server_name/ValidateAndSet(str_val)
|
||||
return str_val != "\[Put the name here\]" && ..()
|
||||
|
||||
/datum/config_entry/string/panic_server_address //Reconnect a player this linked server if this server isn't accepting new players
|
||||
|
||||
/datum/config_entry/string/panic_server_address/ValidateAndSet(str_val)
|
||||
return str_val != "byond://address:port" && ..()
|
||||
|
||||
/datum/config_entry/string/invoke_youtubedl
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/flag/show_irc_name
|
||||
|
||||
/datum/config_entry/flag/see_own_notes //Can players see their own admin notes
|
||||
|
||||
/datum/config_entry/number/note_fresh_days
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/note_stale_days
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/maprotation
|
||||
|
||||
/datum/config_entry/flag/tgstyle_maprotation
|
||||
|
||||
/datum/config_entry/number/maprotatechancedelta
|
||||
config_entry_value = 0.75
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/soft_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/hard_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/extreme_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/soft_popcap_message
|
||||
config_entry_value = "Be warned that the server is currently serving a high number of users, consider using alternative game servers."
|
||||
|
||||
/datum/config_entry/string/hard_popcap_message
|
||||
config_entry_value = "The server is currently serving a high number of users, You cannot currently join. You may wait for the number of living crew to decline, observe, or find alternative servers."
|
||||
|
||||
/datum/config_entry/string/extreme_popcap_message
|
||||
config_entry_value = "The server is currently serving a high number of users, find alternative servers."
|
||||
|
||||
/datum/config_entry/flag/panic_bunker // prevents people the server hasn't seen before from connecting
|
||||
|
||||
/datum/config_entry/number/notify_new_player_age // how long do we notify admins of a new player
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/number/notify_new_player_account_age // how long do we notify admins of a new byond account
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/irc_first_connection_alert // do we notify the irc channel when somebody is connecting for the first time?
|
||||
|
||||
/datum/config_entry/flag/check_randomizer
|
||||
|
||||
/datum/config_entry/string/ipintel_email
|
||||
|
||||
/datum/config_entry/string/ipintel_email/ValidateAndSet(str_val)
|
||||
return str_val != "ch@nge.me" && ..()
|
||||
|
||||
/datum/config_entry/number/ipintel_rating_bad
|
||||
config_entry_value = 1
|
||||
integer = FALSE
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
|
||||
/datum/config_entry/number/ipintel_save_good
|
||||
config_entry_value = 12
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/ipintel_save_bad
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/ipintel_domain
|
||||
config_entry_value = "check.getipintel.net"
|
||||
|
||||
/datum/config_entry/flag/aggressive_changelog
|
||||
|
||||
/datum/config_entry/flag/autoconvert_notes //if all connecting player's notes should attempt to be converted to the database
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/allow_webclient
|
||||
|
||||
/datum/config_entry/flag/webclient_only_byond_members
|
||||
|
||||
/datum/config_entry/flag/announce_admin_logout
|
||||
|
||||
/datum/config_entry/flag/announce_admin_login
|
||||
|
||||
/datum/config_entry/flag/allow_map_voting
|
||||
|
||||
/datum/config_entry/number/client_warn_version
|
||||
config_entry_value = null
|
||||
min_val = 500
|
||||
|
||||
/datum/config_entry/string/client_warn_message
|
||||
config_entry_value = "Your version of byond may have issues or be blocked from accessing this server in the future."
|
||||
|
||||
/datum/config_entry/flag/client_warn_popup
|
||||
|
||||
/datum/config_entry/number/client_error_version
|
||||
config_entry_value = null
|
||||
min_val = 500
|
||||
|
||||
/datum/config_entry/string/client_error_message
|
||||
config_entry_value = "Your version of byond is too old, may have issues, and is blocked from accessing this server."
|
||||
|
||||
/datum/config_entry/number/minute_topic_limit
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/second_topic_limit
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/minute_click_limit
|
||||
config_entry_value = 400
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/second_click_limit
|
||||
config_entry_value = 15
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/error_cooldown // The "cooldown" time for each occurrence of a unique error
|
||||
config_entry_value = 600
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/error_limit // How many occurrences before the next will silence them
|
||||
config_entry_value = 50
|
||||
|
||||
/datum/config_entry/number/error_silence_time // How long a unique error will be silenced for
|
||||
config_entry_value = 6000
|
||||
|
||||
/datum/config_entry/number/error_msg_delay // How long to wait between messaging admins about occurrences of a unique error
|
||||
config_entry_value = 50
|
||||
|
||||
/datum/config_entry/flag/irc_announce_new_game
|
||||
|
||||
/datum/config_entry/flag/debug_admin_hrefs
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/base_mc_tick_rate
|
||||
integer = FALSE
|
||||
config_entry_value = 1
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/high_pop_mc_tick_rate
|
||||
integer = FALSE
|
||||
config_entry_value = 1.1
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/high_pop_mc_mode_amount
|
||||
config_entry_value = 65
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/disable_high_pop_mc_mode_amount
|
||||
config_entry_value = 60
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate
|
||||
abstract_type = /datum/config_entry/number/mc_tick_rate
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if (.)
|
||||
Master.UpdateTickRate()
|
||||
|
||||
/datum/config_entry/flag/resume_after_initializations
|
||||
|
||||
/datum/config_entry/flag/resume_after_initializations/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(. && Master.current_runlevel)
|
||||
world.sleep_offline = !config_entry_value
|
||||
|
||||
/datum/config_entry/number/rounds_until_hard_restart
|
||||
config_entry_value = -1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/default_view
|
||||
config_entry_value = "15x15"
|
||||
|
||||
/datum/config_entry/flag/log_pictures
|
||||
|
||||
/datum/config_entry/flag/picture_logging_camera
|
||||
/datum/config_entry/flag/autoadmin // if autoadmin is enabled
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/autoadmin_rank // the rank for autoadmins
|
||||
config_entry_value = "Game Master"
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/servername // server name (the name of the game window)
|
||||
|
||||
/datum/config_entry/string/servertagline
|
||||
config_entry_value = "We forgot to set the server's tagline in config.txt"
|
||||
|
||||
/datum/config_entry/string/serversqlname // short form server name used for the DB
|
||||
|
||||
/datum/config_entry/string/stationname // station name (the name of the station in-game)
|
||||
|
||||
/datum/config_entry/number/lobby_countdown // In between round countdown.
|
||||
config_entry_value = 120
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/round_end_countdown // Post round murder death kill countdown
|
||||
config_entry_value = 25
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/hub // if the game appears on the hub or not
|
||||
|
||||
/datum/config_entry/flag/log_ooc // log OOC channel
|
||||
|
||||
/datum/config_entry/flag/log_access // log login/logout
|
||||
|
||||
/datum/config_entry/flag/log_say // log client say
|
||||
|
||||
/datum/config_entry/flag/log_admin // log admin actions
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/log_prayer // log prayers
|
||||
|
||||
/datum/config_entry/flag/log_law // log lawchanges
|
||||
|
||||
/datum/config_entry/flag/log_game // log game events
|
||||
|
||||
/datum/config_entry/flag/log_virus // log virology data
|
||||
|
||||
/datum/config_entry/flag/log_vote // log voting
|
||||
|
||||
/datum/config_entry/flag/log_whisper // log client whisper
|
||||
|
||||
/datum/config_entry/flag/log_attack // log attack messages
|
||||
|
||||
/datum/config_entry/flag/log_emote // log emotes
|
||||
|
||||
/datum/config_entry/flag/log_adminchat // log admin chat messages
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/log_pda // log pda messages
|
||||
|
||||
/datum/config_entry/flag/log_telecomms // log telecomms messages
|
||||
|
||||
/datum/config_entry/flag/log_twitter // log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
|
||||
|
||||
/datum/config_entry/flag/log_world_topic // log all world.Topic() calls
|
||||
|
||||
/datum/config_entry/flag/log_manifest // log crew manifest to seperate file
|
||||
|
||||
/datum/config_entry/flag/log_job_debug // log roundstart divide occupations debug information to a file
|
||||
|
||||
/datum/config_entry/flag/allow_admin_ooccolor // Allows admins with relevant permissions to have their own ooc colour
|
||||
|
||||
/datum/config_entry/flag/allow_vote_restart // allow votes to restart
|
||||
|
||||
/datum/config_entry/flag/allow_vote_mode // allow votes to change mode
|
||||
|
||||
/datum/config_entry/number/vote_delay // minimum time between voting sessions (deciseconds, 10 minute default)
|
||||
config_entry_value = 6000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/vote_period // length of voting period (deciseconds, default 1 minute)
|
||||
config_entry_value = 600
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/default_no_vote // vote does not default to nochange/norestart
|
||||
|
||||
/datum/config_entry/flag/no_dead_vote // dead people can't vote
|
||||
|
||||
/datum/config_entry/flag/allow_metadata // Metadata is supported.
|
||||
|
||||
/datum/config_entry/flag/popup_admin_pm // adminPMs to non-admins show in a pop-up 'reply' window when set
|
||||
|
||||
/datum/config_entry/number/fps
|
||||
config_entry_value = 20
|
||||
min_val = 1
|
||||
max_val = 100 //byond will start crapping out at 50, so this is just ridic
|
||||
var/sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/fps/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
sync_validate = TRUE
|
||||
var/datum/config_entry/number/ticklag/TL = config.entries_by_type[/datum/config_entry/number/ticklag]
|
||||
if(!TL.sync_validate)
|
||||
TL.ValidateAndSet(10 / config_entry_value)
|
||||
sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/ticklag
|
||||
integer = FALSE
|
||||
var/sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/number/ticklag/New() //ticklag weirdly just mirrors fps
|
||||
var/datum/config_entry/CE = /datum/config_entry/number/fps
|
||||
config_entry_value = 10 / initial(CE.config_entry_value)
|
||||
..()
|
||||
|
||||
/datum/config_entry/number/ticklag/ValidateAndSet(str_val)
|
||||
. = text2num(str_val) > 0 && ..()
|
||||
if(.)
|
||||
sync_validate = TRUE
|
||||
var/datum/config_entry/number/fps/FPS = config.entries_by_type[/datum/config_entry/number/fps]
|
||||
if(!FPS.sync_validate)
|
||||
FPS.ValidateAndSet(10 / config_entry_value)
|
||||
sync_validate = FALSE
|
||||
|
||||
/datum/config_entry/flag/allow_holidays
|
||||
|
||||
/datum/config_entry/number/tick_limit_mc_init //SSinitialization throttling
|
||||
config_entry_value = TICK_LIMIT_MC_INIT_DEFAULT
|
||||
min_val = 0 //oranges warned us
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/protect_legacy_admins //Stops any admins loaded by the legacy system from having their rank edited by the permissions panel
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/protect_legacy_ranks //Stops any ranks loaded by the legacy system from having their flags edited by the permissions panel
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/enable_localhost_rank //Gives the !localhost! rank to any client connecting from 127.0.0.1 or ::1
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/mentors_mobname_only
|
||||
|
||||
/datum/config_entry/flag/mentor_legacy_system //Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/string/hostedby
|
||||
|
||||
/datum/config_entry/flag/norespawn
|
||||
|
||||
/datum/config_entry/flag/guest_jobban
|
||||
|
||||
/datum/config_entry/flag/usewhitelist
|
||||
|
||||
/datum/config_entry/flag/ban_legacy_system //Defines whether the server uses the legacy banning system with the files in /data or the SQL system.
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/use_age_restriction_for_jobs //Do jobs use account age restrictions? --requires database
|
||||
|
||||
/datum/config_entry/flag/use_account_age_for_jobs //Uses the time they made the account for the job restriction stuff. New player joining alerts should be unaffected.
|
||||
|
||||
/datum/config_entry/flag/use_exp_tracking
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_heads
|
||||
|
||||
/datum/config_entry/number/use_exp_restrictions_heads_hours
|
||||
config_entry_value = 0
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_heads_department
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_other
|
||||
|
||||
/datum/config_entry/flag/use_exp_restrictions_admin_bypass
|
||||
|
||||
/datum/config_entry/string/server
|
||||
|
||||
/datum/config_entry/string/banappeals
|
||||
|
||||
/datum/config_entry/string/wikiurl
|
||||
config_entry_value = "https://katlin.dog/citadel-wiki"
|
||||
|
||||
/datum/config_entry/string/wikiurltg
|
||||
config_entry_value = "http://www.tgstation13.org/wiki"
|
||||
|
||||
/datum/config_entry/string/forumurl
|
||||
config_entry_value = "http://tgstation13.org/phpBB/index.php"
|
||||
|
||||
/datum/config_entry/string/rulesurl
|
||||
config_entry_value = "http://www.tgstation13.org/wiki/Rules"
|
||||
|
||||
/datum/config_entry/string/githuburl
|
||||
config_entry_value = "https://www.github.com/tgstation/-tg-station"
|
||||
|
||||
/datum/config_entry/string/roundstatsurl
|
||||
|
||||
/datum/config_entry/string/gamelogurl
|
||||
|
||||
/datum/config_entry/number/githubrepoid
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/guest_ban
|
||||
|
||||
/datum/config_entry/number/id_console_jobslot_delay
|
||||
config_entry_value = 30
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/inactivity_period //time in ds until a player is considered inactive
|
||||
config_entry_value = 3000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/inactivity_period/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
config_entry_value *= 10 //documented as seconds in config.txt
|
||||
|
||||
/datum/config_entry/number/afk_period //time in ds until a player is considered inactive
|
||||
config_entry_value = 3000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/afk_period/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(.)
|
||||
config_entry_value *= 10 //documented as seconds in config.txt
|
||||
|
||||
/datum/config_entry/flag/kick_inactive //force disconnect for inactive players
|
||||
|
||||
/datum/config_entry/flag/load_jobs_from_txt
|
||||
|
||||
/datum/config_entry/flag/forbid_singulo_possession
|
||||
|
||||
/datum/config_entry/flag/automute_on //enables automuting/spam prevention
|
||||
|
||||
/datum/config_entry/string/panic_server_name
|
||||
|
||||
/datum/config_entry/string/panic_server_name/ValidateAndSet(str_val)
|
||||
return str_val != "\[Put the name here\]" && ..()
|
||||
|
||||
/datum/config_entry/string/panic_server_address //Reconnect a player this linked server if this server isn't accepting new players
|
||||
|
||||
/datum/config_entry/string/panic_server_address/ValidateAndSet(str_val)
|
||||
return str_val != "byond://address:port" && ..()
|
||||
|
||||
/datum/config_entry/string/invoke_youtubedl
|
||||
protection = CONFIG_ENTRY_LOCKED | CONFIG_ENTRY_HIDDEN
|
||||
|
||||
/datum/config_entry/flag/show_irc_name
|
||||
|
||||
/datum/config_entry/flag/see_own_notes //Can players see their own admin notes
|
||||
|
||||
/datum/config_entry/number/note_fresh_days
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/note_stale_days
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/flag/maprotation
|
||||
|
||||
/datum/config_entry/flag/tgstyle_maprotation
|
||||
|
||||
/datum/config_entry/number/maprotatechancedelta
|
||||
config_entry_value = 0.75
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
integer = FALSE
|
||||
|
||||
/datum/config_entry/number/soft_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/hard_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/extreme_popcap
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/soft_popcap_message
|
||||
config_entry_value = "Be warned that the server is currently serving a high number of users, consider using alternative game servers."
|
||||
|
||||
/datum/config_entry/string/hard_popcap_message
|
||||
config_entry_value = "The server is currently serving a high number of users, You cannot currently join. You may wait for the number of living crew to decline, observe, or find alternative servers."
|
||||
|
||||
/datum/config_entry/string/extreme_popcap_message
|
||||
config_entry_value = "The server is currently serving a high number of users, find alternative servers."
|
||||
|
||||
/datum/config_entry/flag/panic_bunker // prevents people the server hasn't seen before from connecting
|
||||
|
||||
/datum/config_entry/number/notify_new_player_age // how long do we notify admins of a new player
|
||||
min_val = -1
|
||||
|
||||
/datum/config_entry/number/notify_new_player_account_age // how long do we notify admins of a new byond account
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/irc_first_connection_alert // do we notify the irc channel when somebody is connecting for the first time?
|
||||
|
||||
/datum/config_entry/flag/check_randomizer
|
||||
|
||||
/datum/config_entry/string/ipintel_email
|
||||
|
||||
/datum/config_entry/string/ipintel_email/ValidateAndSet(str_val)
|
||||
return str_val != "ch@nge.me" && ..()
|
||||
|
||||
/datum/config_entry/number/ipintel_rating_bad
|
||||
config_entry_value = 1
|
||||
integer = FALSE
|
||||
min_val = 0
|
||||
max_val = 1
|
||||
|
||||
/datum/config_entry/number/ipintel_save_good
|
||||
config_entry_value = 12
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/ipintel_save_bad
|
||||
config_entry_value = 1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/ipintel_domain
|
||||
config_entry_value = "check.getipintel.net"
|
||||
|
||||
/datum/config_entry/flag/aggressive_changelog
|
||||
|
||||
/datum/config_entry/flag/autoconvert_notes //if all connecting player's notes should attempt to be converted to the database
|
||||
protection = CONFIG_ENTRY_LOCKED
|
||||
|
||||
/datum/config_entry/flag/allow_webclient
|
||||
|
||||
/datum/config_entry/flag/webclient_only_byond_members
|
||||
|
||||
/datum/config_entry/flag/announce_admin_logout
|
||||
|
||||
/datum/config_entry/flag/announce_admin_login
|
||||
|
||||
/datum/config_entry/flag/allow_map_voting
|
||||
|
||||
/datum/config_entry/number/client_warn_version
|
||||
config_entry_value = null
|
||||
min_val = 500
|
||||
|
||||
/datum/config_entry/string/client_warn_message
|
||||
config_entry_value = "Your version of byond may have issues or be blocked from accessing this server in the future."
|
||||
|
||||
/datum/config_entry/flag/client_warn_popup
|
||||
|
||||
/datum/config_entry/number/client_error_version
|
||||
config_entry_value = null
|
||||
min_val = 500
|
||||
|
||||
/datum/config_entry/string/client_error_message
|
||||
config_entry_value = "Your version of byond is too old, may have issues, and is blocked from accessing this server."
|
||||
|
||||
/datum/config_entry/number/minute_topic_limit
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/second_topic_limit
|
||||
config_entry_value = null
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/minute_click_limit
|
||||
config_entry_value = 400
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/second_click_limit
|
||||
config_entry_value = 15
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/error_cooldown // The "cooldown" time for each occurrence of a unique error
|
||||
config_entry_value = 600
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/number/error_limit // How many occurrences before the next will silence them
|
||||
config_entry_value = 50
|
||||
|
||||
/datum/config_entry/number/error_silence_time // How long a unique error will be silenced for
|
||||
config_entry_value = 6000
|
||||
|
||||
/datum/config_entry/number/error_msg_delay // How long to wait between messaging admins about occurrences of a unique error
|
||||
config_entry_value = 50
|
||||
|
||||
/datum/config_entry/flag/irc_announce_new_game
|
||||
|
||||
/datum/config_entry/flag/debug_admin_hrefs
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/base_mc_tick_rate
|
||||
integer = FALSE
|
||||
config_entry_value = 1
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/high_pop_mc_tick_rate
|
||||
integer = FALSE
|
||||
config_entry_value = 1.1
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/high_pop_mc_mode_amount
|
||||
config_entry_value = 65
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/disable_high_pop_mc_mode_amount
|
||||
config_entry_value = 60
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate
|
||||
abstract_type = /datum/config_entry/number/mc_tick_rate
|
||||
|
||||
/datum/config_entry/number/mc_tick_rate/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if (.)
|
||||
Master.UpdateTickRate()
|
||||
|
||||
/datum/config_entry/flag/resume_after_initializations
|
||||
|
||||
/datum/config_entry/flag/resume_after_initializations/ValidateAndSet(str_val)
|
||||
. = ..()
|
||||
if(. && Master.current_runlevel)
|
||||
world.sleep_offline = !config_entry_value
|
||||
|
||||
/datum/config_entry/number/rounds_until_hard_restart
|
||||
config_entry_value = -1
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/string/default_view
|
||||
config_entry_value = "15x15"
|
||||
|
||||
/datum/config_entry/flag/log_pictures
|
||||
|
||||
/datum/config_entry/flag/picture_logging_camera
|
||||
|
||||
+102
-102
@@ -1,102 +1,102 @@
|
||||
/**
|
||||
* Failsafe
|
||||
*
|
||||
* Pretty much pokes the MC to make sure it's still alive.
|
||||
**/
|
||||
|
||||
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
|
||||
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
|
||||
name = "Failsafe"
|
||||
|
||||
// The length of time to check on the MC (in deciseconds).
|
||||
// Set to 0 to disable.
|
||||
var/processing_interval = 20
|
||||
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
|
||||
var/defcon = 5
|
||||
//the world.time of the last check, so the mc can restart US if we hang.
|
||||
// (Real friends look out for *eachother*)
|
||||
var/lasttick = 0
|
||||
|
||||
// Track the MC iteration to make sure its still on track.
|
||||
var/master_iteration = 0
|
||||
var/running = TRUE
|
||||
|
||||
/datum/controller/failsafe/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
if(Failsafe != src)
|
||||
if(istype(Failsafe))
|
||||
qdel(Failsafe)
|
||||
Failsafe = src
|
||||
Initialize()
|
||||
|
||||
/datum/controller/failsafe/Initialize()
|
||||
set waitfor = 0
|
||||
Failsafe.Loop()
|
||||
if(!QDELETED(src))
|
||||
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
|
||||
|
||||
/datum/controller/failsafe/Destroy()
|
||||
running = FALSE
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/controller/failsafe/proc/Loop()
|
||||
while(running)
|
||||
lasttick = world.time
|
||||
if(!Master)
|
||||
// Replace the missing Master! This should never, ever happen.
|
||||
new /datum/controller/master()
|
||||
// Only poke it if overrides are not in effect.
|
||||
if(processing_interval > 0)
|
||||
if(Master.processing && Master.iteration)
|
||||
// Check if processing is done yet.
|
||||
if(Master.iteration == master_iteration)
|
||||
switch(defcon)
|
||||
if(4,5)
|
||||
--defcon
|
||||
if(3)
|
||||
message_admins("<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(2)
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(1)
|
||||
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
|
||||
--defcon
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else if(rtn < 0)
|
||||
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
|
||||
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
|
||||
//no need to handle that specially when defcon 0 can handle it
|
||||
if(0) //DEFCON 0! (mc failed to restart)
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else
|
||||
defcon = min(defcon + 1,5)
|
||||
master_iteration = Master.iteration
|
||||
if (defcon <= 1)
|
||||
sleep(processing_interval*2)
|
||||
else
|
||||
sleep(processing_interval)
|
||||
else
|
||||
defcon = 5
|
||||
sleep(initial(processing_interval))
|
||||
|
||||
/datum/controller/failsafe/proc/defcon_pretty()
|
||||
return defcon
|
||||
|
||||
/datum/controller/failsafe/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
|
||||
/**
|
||||
* Failsafe
|
||||
*
|
||||
* Pretty much pokes the MC to make sure it's still alive.
|
||||
**/
|
||||
|
||||
GLOBAL_REAL(Failsafe, /datum/controller/failsafe)
|
||||
|
||||
/datum/controller/failsafe // This thing pretty much just keeps poking the master controller
|
||||
name = "Failsafe"
|
||||
|
||||
// The length of time to check on the MC (in deciseconds).
|
||||
// Set to 0 to disable.
|
||||
var/processing_interval = 20
|
||||
// The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC.
|
||||
var/defcon = 5
|
||||
//the world.time of the last check, so the mc can restart US if we hang.
|
||||
// (Real friends look out for *eachother*)
|
||||
var/lasttick = 0
|
||||
|
||||
// Track the MC iteration to make sure its still on track.
|
||||
var/master_iteration = 0
|
||||
var/running = TRUE
|
||||
|
||||
/datum/controller/failsafe/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
if(Failsafe != src)
|
||||
if(istype(Failsafe))
|
||||
qdel(Failsafe)
|
||||
Failsafe = src
|
||||
Initialize()
|
||||
|
||||
/datum/controller/failsafe/Initialize()
|
||||
set waitfor = 0
|
||||
Failsafe.Loop()
|
||||
if(!QDELETED(src))
|
||||
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
|
||||
|
||||
/datum/controller/failsafe/Destroy()
|
||||
running = FALSE
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/datum/controller/failsafe/proc/Loop()
|
||||
while(running)
|
||||
lasttick = world.time
|
||||
if(!Master)
|
||||
// Replace the missing Master! This should never, ever happen.
|
||||
new /datum/controller/master()
|
||||
// Only poke it if overrides are not in effect.
|
||||
if(processing_interval > 0)
|
||||
if(Master.processing && Master.iteration)
|
||||
// Check if processing is done yet.
|
||||
if(Master.iteration == master_iteration)
|
||||
switch(defcon)
|
||||
if(4,5)
|
||||
--defcon
|
||||
if(3)
|
||||
message_admins("<span class='adminnotice'>Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(2)
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.</span>")
|
||||
--defcon
|
||||
if(1)
|
||||
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...</span>")
|
||||
--defcon
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else if(rtn < 0)
|
||||
log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0")
|
||||
to_chat(GLOB.admins, "<span class='boldannounce'>ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.</span>")
|
||||
//if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again
|
||||
//no need to handle that specially when defcon 0 can handle it
|
||||
if(0) //DEFCON 0! (mc failed to restart)
|
||||
var/rtn = Recreate_MC()
|
||||
if(rtn > 0)
|
||||
defcon = 4
|
||||
master_iteration = 0
|
||||
to_chat(GLOB.admins, "<span class='adminnotice'>MC restarted successfully</span>")
|
||||
else
|
||||
defcon = min(defcon + 1,5)
|
||||
master_iteration = Master.iteration
|
||||
if (defcon <= 1)
|
||||
sleep(processing_interval*2)
|
||||
else
|
||||
sleep(processing_interval)
|
||||
else
|
||||
defcon = 5
|
||||
sleep(initial(processing_interval))
|
||||
|
||||
/datum/controller/failsafe/proc/defcon_pretty()
|
||||
return defcon
|
||||
|
||||
/datum/controller/failsafe/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])"))
|
||||
|
||||
+55
-55
@@ -1,55 +1,55 @@
|
||||
GLOBAL_REAL(GLOB, /datum/controller/global_vars)
|
||||
|
||||
/datum/controller/global_vars
|
||||
name = "Global Variables"
|
||||
|
||||
var/static/list/gvars_datum_protected_varlist
|
||||
var/list/gvars_datum_in_built_vars
|
||||
var/list/gvars_datum_init_order
|
||||
|
||||
/datum/controller/global_vars/New()
|
||||
if(GLOB)
|
||||
CRASH("Multiple instances of global variable controller created")
|
||||
GLOB = src
|
||||
|
||||
var/datum/controller/exclude_these = new
|
||||
gvars_datum_in_built_vars = exclude_these.vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
|
||||
QDEL_IN(exclude_these, 0) //signal logging isn't ready
|
||||
|
||||
log_world("[vars.len - gvars_datum_in_built_vars.len] global variables")
|
||||
|
||||
Initialize()
|
||||
|
||||
/datum/controller/global_vars/Destroy()
|
||||
//fuck off kevinz
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/datum/controller/global_vars/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Globals:", statclick.update("Edit"))
|
||||
|
||||
/datum/controller/global_vars/vv_edit_var(var_name, var_value)
|
||||
if(gvars_datum_protected_varlist[var_name])
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/global_vars/Initialize()
|
||||
gvars_datum_init_order = list()
|
||||
gvars_datum_protected_varlist = list(NAMEOF(src, gvars_datum_protected_varlist) = TRUE)
|
||||
var/list/global_procs = typesof(/datum/controller/global_vars/proc)
|
||||
var/expected_len = vars.len - gvars_datum_in_built_vars.len
|
||||
if(global_procs.len != expected_len)
|
||||
warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!")
|
||||
if(global_procs.len)
|
||||
var/list/expected_global_procs = vars - gvars_datum_in_built_vars
|
||||
for(var/I in global_procs)
|
||||
expected_global_procs -= replacetext("[I]", "InitGlobal", "")
|
||||
log_world("Missing procs: [expected_global_procs.Join(", ")]")
|
||||
for(var/I in global_procs)
|
||||
var/start_tick = world.time
|
||||
call(src, I)()
|
||||
var/end_tick = world.time
|
||||
if(end_tick - start_tick)
|
||||
warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!")
|
||||
GLOBAL_REAL(GLOB, /datum/controller/global_vars)
|
||||
|
||||
/datum/controller/global_vars
|
||||
name = "Global Variables"
|
||||
|
||||
var/static/list/gvars_datum_protected_varlist
|
||||
var/list/gvars_datum_in_built_vars
|
||||
var/list/gvars_datum_init_order
|
||||
|
||||
/datum/controller/global_vars/New()
|
||||
if(GLOB)
|
||||
CRASH("Multiple instances of global variable controller created")
|
||||
GLOB = src
|
||||
|
||||
var/datum/controller/exclude_these = new
|
||||
gvars_datum_in_built_vars = exclude_these.vars + list(NAMEOF(src, gvars_datum_protected_varlist), NAMEOF(src, gvars_datum_in_built_vars), NAMEOF(src, gvars_datum_init_order))
|
||||
QDEL_IN(exclude_these, 0) //signal logging isn't ready
|
||||
|
||||
log_world("[vars.len - gvars_datum_in_built_vars.len] global variables")
|
||||
|
||||
Initialize()
|
||||
|
||||
/datum/controller/global_vars/Destroy()
|
||||
//fuck off kevinz
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/datum/controller/global_vars/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Globals:", statclick.update("Edit"))
|
||||
|
||||
/datum/controller/global_vars/vv_edit_var(var_name, var_value)
|
||||
if(gvars_datum_protected_varlist[var_name])
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/global_vars/Initialize()
|
||||
gvars_datum_init_order = list()
|
||||
gvars_datum_protected_varlist = list(NAMEOF(src, gvars_datum_protected_varlist) = TRUE)
|
||||
var/list/global_procs = typesof(/datum/controller/global_vars/proc)
|
||||
var/expected_len = vars.len - gvars_datum_in_built_vars.len
|
||||
if(global_procs.len != expected_len)
|
||||
warning("Unable to detect all global initialization procs! Expected [expected_len] got [global_procs.len]!")
|
||||
if(global_procs.len)
|
||||
var/list/expected_global_procs = vars - gvars_datum_in_built_vars
|
||||
for(var/I in global_procs)
|
||||
expected_global_procs -= replacetext("[I]", "InitGlobal", "")
|
||||
log_world("Missing procs: [expected_global_procs.Join(", ")]")
|
||||
for(var/I in global_procs)
|
||||
var/start_tick = world.time
|
||||
call(src, I)()
|
||||
var/end_tick = world.time
|
||||
if(end_tick - start_tick)
|
||||
warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!")
|
||||
|
||||
+151
-151
@@ -1,151 +1,151 @@
|
||||
#define BAD_INIT_QDEL_BEFORE 1
|
||||
#define BAD_INIT_DIDNT_INIT 2
|
||||
#define BAD_INIT_SLEPT 4
|
||||
#define BAD_INIT_NO_HINT 8
|
||||
|
||||
SUBSYSTEM_DEF(atoms)
|
||||
name = "Atoms"
|
||||
init_order = INIT_ORDER_ATOMS
|
||||
flags = SS_NO_FIRE
|
||||
|
||||
var/old_initialized
|
||||
|
||||
var/list/late_loaders
|
||||
|
||||
var/list/BadInitializeCalls = list()
|
||||
|
||||
/datum/controller/subsystem/atoms/Initialize(timeofday)
|
||||
GLOB.fire_overlay.appearance_flags = RESET_COLOR
|
||||
setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
InitializeAtoms()
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms)
|
||||
if(initialized == INITIALIZATION_INSSATOMS)
|
||||
return
|
||||
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
|
||||
LAZYINITLIST(late_loaders)
|
||||
|
||||
var/count
|
||||
var/list/mapload_arg = list(TRUE)
|
||||
if(atoms)
|
||||
count = atoms.len
|
||||
for(var/I in atoms)
|
||||
var/atom/A = I
|
||||
if(!(A.flags_1 & INITIALIZED_1))
|
||||
InitAtom(I, mapload_arg)
|
||||
CHECK_TICK
|
||||
else
|
||||
count = 0
|
||||
for(var/atom/A in world)
|
||||
if(!(A.flags_1 & INITIALIZED_1))
|
||||
InitAtom(A, mapload_arg)
|
||||
++count
|
||||
CHECK_TICK
|
||||
|
||||
testing("Initialized [count] atoms")
|
||||
pass(count)
|
||||
|
||||
initialized = INITIALIZATION_INNEW_REGULAR
|
||||
|
||||
if(late_loaders.len)
|
||||
for(var/I in late_loaders)
|
||||
var/atom/A = I
|
||||
A.LateInitialize()
|
||||
testing("Late initialized [late_loaders.len] atoms")
|
||||
late_loaders.Cut()
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments)
|
||||
var/the_type = A.type
|
||||
if(QDELING(A))
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE
|
||||
return TRUE
|
||||
|
||||
var/start_tick = world.time
|
||||
|
||||
var/result = A.Initialize(arglist(arguments))
|
||||
|
||||
if(start_tick != world.time)
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_SLEPT
|
||||
|
||||
var/qdeleted = FALSE
|
||||
|
||||
if(result != INITIALIZE_HINT_NORMAL)
|
||||
switch(result)
|
||||
if(INITIALIZE_HINT_LATELOAD)
|
||||
if(arguments[1]) //mapload
|
||||
late_loaders += A
|
||||
else
|
||||
A.LateInitialize()
|
||||
if(INITIALIZE_HINT_QDEL)
|
||||
qdel(A)
|
||||
qdeleted = TRUE
|
||||
else
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT
|
||||
|
||||
if(!A) //possible harddel
|
||||
qdeleted = TRUE
|
||||
else if(!(A.flags_1 & INITIALIZED_1))
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT
|
||||
|
||||
return qdeleted || QDELING(A)
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_begin()
|
||||
old_initialized = initialized
|
||||
initialized = INITIALIZATION_INSSATOMS
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_stop()
|
||||
initialized = old_initialized
|
||||
|
||||
/datum/controller/subsystem/atoms/Recover()
|
||||
initialized = SSatoms.initialized
|
||||
if(initialized == INITIALIZATION_INNEW_MAPLOAD)
|
||||
InitializeAtoms()
|
||||
old_initialized = SSatoms.old_initialized
|
||||
BadInitializeCalls = SSatoms.BadInitializeCalls
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/setupGenetics()
|
||||
var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
|
||||
for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
|
||||
avnums[i] = i
|
||||
CHECK_TICK
|
||||
|
||||
for(var/A in subtypesof(/datum/mutation/human))
|
||||
var/datum/mutation/human/B = new A()
|
||||
if(B.dna_block == NON_SCANNABLE)
|
||||
continue
|
||||
B.dna_block = pick_n_take(avnums)
|
||||
if(B.quality == POSITIVE)
|
||||
GLOB.good_mutations |= B
|
||||
else if(B.quality == NEGATIVE)
|
||||
GLOB.bad_mutations |= B
|
||||
else if(B.quality == MINOR_NEGATIVE)
|
||||
GLOB.not_good_mutations |= B
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitLog()
|
||||
. = ""
|
||||
for(var/path in BadInitializeCalls)
|
||||
. += "Path : [path] \n"
|
||||
var/fails = BadInitializeCalls[path]
|
||||
if(fails & BAD_INIT_DIDNT_INIT)
|
||||
. += "- Didn't call atom/Initialize()\n"
|
||||
if(fails & BAD_INIT_NO_HINT)
|
||||
. += "- Didn't return an Initialize hint\n"
|
||||
if(fails & BAD_INIT_QDEL_BEFORE)
|
||||
. += "- Qdel'd in New()\n"
|
||||
if(fails & BAD_INIT_SLEPT)
|
||||
. += "- Slept during Initialize()\n"
|
||||
|
||||
/datum/controller/subsystem/atoms/Shutdown()
|
||||
var/initlog = InitLog()
|
||||
if(initlog)
|
||||
text2file(initlog, "[GLOB.log_directory]/initialize.log")
|
||||
|
||||
#undef BAD_INIT_QDEL_BEFORE
|
||||
#undef BAD_INIT_DIDNT_INIT
|
||||
#undef BAD_INIT_SLEPT
|
||||
#undef BAD_INIT_NO_HINT
|
||||
#define BAD_INIT_QDEL_BEFORE 1
|
||||
#define BAD_INIT_DIDNT_INIT 2
|
||||
#define BAD_INIT_SLEPT 4
|
||||
#define BAD_INIT_NO_HINT 8
|
||||
|
||||
SUBSYSTEM_DEF(atoms)
|
||||
name = "Atoms"
|
||||
init_order = INIT_ORDER_ATOMS
|
||||
flags = SS_NO_FIRE
|
||||
|
||||
var/old_initialized
|
||||
|
||||
var/list/late_loaders
|
||||
|
||||
var/list/BadInitializeCalls = list()
|
||||
|
||||
/datum/controller/subsystem/atoms/Initialize(timeofday)
|
||||
GLOB.fire_overlay.appearance_flags = RESET_COLOR
|
||||
setupGenetics() //to set the mutations' place in structural enzymes, so monkey.initialize() knows where to put the monkey mutation.
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
InitializeAtoms()
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitializeAtoms(list/atoms)
|
||||
if(initialized == INITIALIZATION_INSSATOMS)
|
||||
return
|
||||
|
||||
initialized = INITIALIZATION_INNEW_MAPLOAD
|
||||
|
||||
LAZYINITLIST(late_loaders)
|
||||
|
||||
var/count
|
||||
var/list/mapload_arg = list(TRUE)
|
||||
if(atoms)
|
||||
count = atoms.len
|
||||
for(var/I in atoms)
|
||||
var/atom/A = I
|
||||
if(!(A.flags_1 & INITIALIZED_1))
|
||||
InitAtom(I, mapload_arg)
|
||||
CHECK_TICK
|
||||
else
|
||||
count = 0
|
||||
for(var/atom/A in world)
|
||||
if(!(A.flags_1 & INITIALIZED_1))
|
||||
InitAtom(A, mapload_arg)
|
||||
++count
|
||||
CHECK_TICK
|
||||
|
||||
testing("Initialized [count] atoms")
|
||||
pass(count)
|
||||
|
||||
initialized = INITIALIZATION_INNEW_REGULAR
|
||||
|
||||
if(late_loaders.len)
|
||||
for(var/I in late_loaders)
|
||||
var/atom/A = I
|
||||
A.LateInitialize()
|
||||
testing("Late initialized [late_loaders.len] atoms")
|
||||
late_loaders.Cut()
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitAtom(atom/A, list/arguments)
|
||||
var/the_type = A.type
|
||||
if(QDELING(A))
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_QDEL_BEFORE
|
||||
return TRUE
|
||||
|
||||
var/start_tick = world.time
|
||||
|
||||
var/result = A.Initialize(arglist(arguments))
|
||||
|
||||
if(start_tick != world.time)
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_SLEPT
|
||||
|
||||
var/qdeleted = FALSE
|
||||
|
||||
if(result != INITIALIZE_HINT_NORMAL)
|
||||
switch(result)
|
||||
if(INITIALIZE_HINT_LATELOAD)
|
||||
if(arguments[1]) //mapload
|
||||
late_loaders += A
|
||||
else
|
||||
A.LateInitialize()
|
||||
if(INITIALIZE_HINT_QDEL)
|
||||
qdel(A)
|
||||
qdeleted = TRUE
|
||||
else
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_NO_HINT
|
||||
|
||||
if(!A) //possible harddel
|
||||
qdeleted = TRUE
|
||||
else if(!(A.flags_1 & INITIALIZED_1))
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT
|
||||
|
||||
return qdeleted || QDELING(A)
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_begin()
|
||||
old_initialized = initialized
|
||||
initialized = INITIALIZATION_INSSATOMS
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_stop()
|
||||
initialized = old_initialized
|
||||
|
||||
/datum/controller/subsystem/atoms/Recover()
|
||||
initialized = SSatoms.initialized
|
||||
if(initialized == INITIALIZATION_INNEW_MAPLOAD)
|
||||
InitializeAtoms()
|
||||
old_initialized = SSatoms.old_initialized
|
||||
BadInitializeCalls = SSatoms.BadInitializeCalls
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/setupGenetics()
|
||||
var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
|
||||
for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
|
||||
avnums[i] = i
|
||||
CHECK_TICK
|
||||
|
||||
for(var/A in subtypesof(/datum/mutation/human))
|
||||
var/datum/mutation/human/B = new A()
|
||||
if(B.dna_block == NON_SCANNABLE)
|
||||
continue
|
||||
B.dna_block = pick_n_take(avnums)
|
||||
if(B.quality == POSITIVE)
|
||||
GLOB.good_mutations |= B
|
||||
else if(B.quality == NEGATIVE)
|
||||
GLOB.bad_mutations |= B
|
||||
else if(B.quality == MINOR_NEGATIVE)
|
||||
GLOB.not_good_mutations |= B
|
||||
CHECK_TICK
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/InitLog()
|
||||
. = ""
|
||||
for(var/path in BadInitializeCalls)
|
||||
. += "Path : [path] \n"
|
||||
var/fails = BadInitializeCalls[path]
|
||||
if(fails & BAD_INIT_DIDNT_INIT)
|
||||
. += "- Didn't call atom/Initialize()\n"
|
||||
if(fails & BAD_INIT_NO_HINT)
|
||||
. += "- Didn't return an Initialize hint\n"
|
||||
if(fails & BAD_INIT_QDEL_BEFORE)
|
||||
. += "- Qdel'd in New()\n"
|
||||
if(fails & BAD_INIT_SLEPT)
|
||||
. += "- Slept during Initialize()\n"
|
||||
|
||||
/datum/controller/subsystem/atoms/Shutdown()
|
||||
var/initlog = InitLog()
|
||||
if(initlog)
|
||||
text2file(initlog, "[GLOB.log_directory]/initialize.log")
|
||||
|
||||
#undef BAD_INIT_QDEL_BEFORE
|
||||
#undef BAD_INIT_DIDNT_INIT
|
||||
#undef BAD_INIT_SLEPT
|
||||
#undef BAD_INIT_NO_HINT
|
||||
|
||||
@@ -1,335 +1,335 @@
|
||||
SUBSYSTEM_DEF(blackbox)
|
||||
name = "Blackbox"
|
||||
wait = 6000
|
||||
flags = SS_NO_TICK_CHECK
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
init_order = INIT_ORDER_BLACKBOX
|
||||
|
||||
var/list/feedback = list() //list of datum/feedback_variable
|
||||
var/list/first_death = list() //the first death of this round, assoc. vars keep track of different things
|
||||
var/triggertime = 0
|
||||
var/sealed = FALSE //time to stop tracking stats?
|
||||
var/list/versions = list("antagonists" = 3,
|
||||
"admin_secrets_fun_used" = 2,
|
||||
"explosion" = 2,
|
||||
"time_dilation_current" = 3,
|
||||
"science_techweb_unlock" = 2,
|
||||
"round_end_stats" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
|
||||
|
||||
/datum/controller/subsystem/blackbox/Initialize()
|
||||
triggertime = world.time
|
||||
record_feedback("amount", "random_seed", Master.random_seed)
|
||||
record_feedback("amount", "dm_version", DM_VERSION)
|
||||
record_feedback("amount", "byond_version", world.byond_version)
|
||||
record_feedback("amount", "byond_build", world.byond_build)
|
||||
. = ..()
|
||||
|
||||
//poll population
|
||||
/datum/controller/subsystem/blackbox/fire()
|
||||
set waitfor = FALSE //for population query
|
||||
|
||||
CheckPlayerCount()
|
||||
|
||||
if(CONFIG_GET(flag/use_exp_tracking))
|
||||
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
|
||||
update_exp(10,FALSE)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/CheckPlayerCount()
|
||||
set waitfor = FALSE
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
var/playercount = 0
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client)
|
||||
playercount += 1
|
||||
var/admincount = GLOB.admins.len
|
||||
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')")
|
||||
query_record_playercount.Execute()
|
||||
qdel(query_record_playercount)
|
||||
|
||||
/datum/controller/subsystem/blackbox/Recover()
|
||||
feedback = SSblackbox.feedback
|
||||
sealed = SSblackbox.sealed
|
||||
|
||||
//no touchie
|
||||
/datum/controller/subsystem/blackbox/vv_get_var(var_name)
|
||||
if(var_name == "feedback")
|
||||
return debug_variable(var_name, deepCopyList(feedback), 0, src)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if("feedback")
|
||||
return FALSE
|
||||
if("sealed")
|
||||
if(var_value)
|
||||
return Seal()
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
//Recorded on subsystem shutdown
|
||||
/datum/controller/subsystem/blackbox/proc/FinalFeedback()
|
||||
record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved")
|
||||
for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list)
|
||||
if (MS.pda_msgs.len)
|
||||
record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA")
|
||||
if (MS.rc_msgs.len)
|
||||
record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console")
|
||||
|
||||
for(var/player_key in GLOB.player_details)
|
||||
var/datum/player_details/PD = GLOB.player_details[player_key]
|
||||
record_feedback("tally", "client_byond_version", 1, PD.byond_version)
|
||||
|
||||
/datum/controller/subsystem/blackbox/Shutdown()
|
||||
sealed = FALSE
|
||||
FinalFeedback()
|
||||
|
||||
if (!SSdbcore.Connect())
|
||||
return
|
||||
|
||||
var/list/sqlrowlist = list()
|
||||
|
||||
for (var/datum/feedback_variable/FV in feedback)
|
||||
var/sqlversion = 1
|
||||
if(FV.key in versions)
|
||||
sqlversion = versions[FV.key]
|
||||
sqlrowlist += list(list("datetime" = "Now()", "round_id" = GLOB.round_id, "key_name" = "'[sanitizeSQL(FV.key)]'", "key_type" = "'[FV.key_type]'", "version" = "[sqlversion]", "json" = "'[sanitizeSQL(json_encode(FV.json))]'"))
|
||||
|
||||
if (!length(sqlrowlist))
|
||||
return
|
||||
|
||||
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/Seal()
|
||||
if(sealed)
|
||||
return FALSE
|
||||
if(IsAdminAdvancedProcCall())
|
||||
message_admins("[key_name_admin(usr)] sealed the blackbox!")
|
||||
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
|
||||
sealed = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/LogBroadcast(freq)
|
||||
if(sealed)
|
||||
return
|
||||
switch(freq)
|
||||
if(FREQ_COMMON)
|
||||
record_feedback("tally", "radio_usage", 1, "common")
|
||||
if(FREQ_SCIENCE)
|
||||
record_feedback("tally", "radio_usage", 1, "science")
|
||||
if(FREQ_COMMAND)
|
||||
record_feedback("tally", "radio_usage", 1, "command")
|
||||
if(FREQ_MEDICAL)
|
||||
record_feedback("tally", "radio_usage", 1, "medical")
|
||||
if(FREQ_ENGINEERING)
|
||||
record_feedback("tally", "radio_usage", 1, "engineering")
|
||||
if(FREQ_SECURITY)
|
||||
record_feedback("tally", "radio_usage", 1, "security")
|
||||
if(FREQ_SYNDICATE)
|
||||
record_feedback("tally", "radio_usage", 1, "syndicate")
|
||||
if(FREQ_SERVICE)
|
||||
record_feedback("tally", "radio_usage", 1, "service")
|
||||
if(FREQ_SUPPLY)
|
||||
record_feedback("tally", "radio_usage", 1, "supply")
|
||||
if(FREQ_CENTCOM)
|
||||
record_feedback("tally", "radio_usage", 1, "centcom")
|
||||
if(FREQ_AI_PRIVATE)
|
||||
record_feedback("tally", "radio_usage", 1, "ai private")
|
||||
if(FREQ_CTF_RED)
|
||||
record_feedback("tally", "radio_usage", 1, "CTF red team")
|
||||
if(FREQ_CTF_BLUE)
|
||||
record_feedback("tally", "radio_usage", 1, "CTF blue team")
|
||||
else
|
||||
record_feedback("tally", "radio_usage", 1, "other")
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(key, key_type)
|
||||
for(var/datum/feedback_variable/FV in feedback)
|
||||
if(FV.key == key)
|
||||
return FV
|
||||
|
||||
var/datum/feedback_variable/FV = new(key, key_type)
|
||||
feedback += FV
|
||||
return FV
|
||||
/*
|
||||
feedback data can be recorded in 5 formats:
|
||||
"text"
|
||||
used for simple single-string records i.e. the current map
|
||||
further calls to the same key will append saved data unless the overwrite argument is true or it already exists
|
||||
when encoded calls made with overwrite will lack square brackets
|
||||
calls: SSblackbox.record_feedback("text", "example", 1, "sample text")
|
||||
SSblackbox.record_feedback("text", "example", 1, "other text")
|
||||
json: {"data":["sample text","other text"]}
|
||||
"amount"
|
||||
used to record simple counts of data i.e. the number of ahelps received
|
||||
further calls to the same key will add or subtract (if increment argument is a negative) from the saved amount
|
||||
calls: SSblackbox.record_feedback("amount", "example", 8)
|
||||
SSblackbox.record_feedback("amount", "example", 2)
|
||||
json: {"data":10}
|
||||
"tally"
|
||||
used to track the number of occurances of multiple related values i.e. how many times each type of gun is fired
|
||||
further calls to the same key will:
|
||||
add or subtract from the saved value of the data key if it already exists
|
||||
append the key and it's value if it doesn't exist
|
||||
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
|
||||
SSblackbox.record_feedback("tally", "example", 4, "sample data")
|
||||
SSblackbox.record_feedback("tally", "example", 2, "other data")
|
||||
json: {"data":{"sample data":5,"other data":2}}
|
||||
"nested tally"
|
||||
used to track the number of occurances of structured semi-relational values i.e. the results of arcade machines
|
||||
similar to running total, but related values are nested in a multi-dimensional array built
|
||||
the final element in the data list is used as the tracking key, all prior elements are used for nesting
|
||||
all data list elements must be strings
|
||||
further calls to the same key will:
|
||||
add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
|
||||
append the key and it's value if it doesn't exist
|
||||
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange", "apricot"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 10, list("fruit", "red", "apple"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 1, list("vegetable", "orange", "carrot"))
|
||||
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10}},"vegetable":{"orange":{"carrot":1}}}}
|
||||
tracking values associated with a number can't merge with a nesting value, trying to do so will append the list
|
||||
call: SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
|
||||
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10},"orange":3},"vegetable":{"orange":{"carrot":1}}}}
|
||||
"associative"
|
||||
used to record text that's associated with a value i.e. coordinates
|
||||
further calls to the same key will append a new list to existing data
|
||||
calls: SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
|
||||
SSblackbox.record_feedback("associative", "example", 1, list("number" = 7, "text" = "example", "other text" = "sample"))
|
||||
json: {"data":{"1":{"text":"example","path":"/obj/item","number":"4"},"2":{"number":"7","text":"example","other text":"sample"}}}
|
||||
|
||||
Versioning
|
||||
If the format of a feedback variable is ever changed, i.e. how many levels of nesting are used or a new type of data is added to it, add it to the versions list
|
||||
When feedback is being saved if a key is in the versions list the value specified there will be used, otherwise all keys are assumed to be version = 1
|
||||
versions is an associative list, remember to use the same string in it as defined on a feedback variable, example:
|
||||
list/versions = list("round_end_stats" = 4,
|
||||
"admin_toggle" = 2,
|
||||
"gun_fired" = 2)
|
||||
*/
|
||||
/datum/controller/subsystem/blackbox/proc/record_feedback(key_type, key, increment, data, overwrite)
|
||||
if(sealed || !key_type || !istext(key) || !isnum(increment || !data))
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(key, key_type)
|
||||
switch(key_type)
|
||||
if("text")
|
||||
if(!istext(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
if(overwrite)
|
||||
FV.json["data"] = data
|
||||
else
|
||||
FV.json["data"] |= data
|
||||
if("amount")
|
||||
FV.json["data"] += increment
|
||||
if("tally")
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
FV.json["data"]["[data]"] += increment
|
||||
if("nested tally")
|
||||
if(!islist(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
FV.json["data"] = record_feedback_recurse_list(FV.json["data"], data, increment)
|
||||
if("associative")
|
||||
if(!islist(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
var/pos = length(FV.json["data"]) + 1
|
||||
FV.json["data"]["[pos]"] = list() //in 512 "pos" can be replaced with "[FV.json["data"].len+1]"
|
||||
for(var/i in data)
|
||||
if(islist(data[i]))
|
||||
FV.json["data"]["[pos]"]["[i]"] = data[i] //and here with "[FV.json["data"].len]"
|
||||
else
|
||||
FV.json["data"]["[pos]"]["[i]"] = "[data[i]]"
|
||||
else
|
||||
CRASH("Invalid feedback key_type: [key_type]")
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/record_feedback_recurse_list(list/L, list/key_list, increment, depth = 1)
|
||||
if(depth == key_list.len)
|
||||
if(L.Find(key_list[depth]))
|
||||
L["[key_list[depth]]"] += increment
|
||||
else
|
||||
var/list/LFI = list(key_list[depth] = increment)
|
||||
L += LFI
|
||||
else
|
||||
if(!L.Find(key_list[depth]))
|
||||
var/list/LGD = list(key_list[depth] = list())
|
||||
L += LGD
|
||||
L["[key_list[depth-1]]"] = .(L["[key_list[depth]]"], key_list, increment, ++depth)
|
||||
return L
|
||||
|
||||
/datum/feedback_variable
|
||||
var/key
|
||||
var/key_type
|
||||
var/list/json = list()
|
||||
|
||||
/datum/feedback_variable/New(new_key, new_key_type)
|
||||
key = new_key
|
||||
key_type = new_key_type
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
|
||||
set waitfor = FALSE
|
||||
if(sealed)
|
||||
return
|
||||
if(!L || !L.key || !L.mind)
|
||||
return
|
||||
if(!L.suiciding && !first_death.len)
|
||||
first_death["name"] = "[(L.real_name == L.name) ? L.real_name : "[L.real_name] as [L.name]"]"
|
||||
first_death["role"] = null
|
||||
if(L.mind.assigned_role)
|
||||
first_death["role"] = L.mind.assigned_role
|
||||
first_death["area"] = "[AREACOORD(L)]"
|
||||
first_death["damage"] = "<font color='#FF5555'>[L.getBruteLoss()]</font>/<font color='orange'>[L.getFireLoss()]</font>/<font color='lightgreen'>[L.getToxLoss()]</font>/<font color='lightblue'>[L.getOxyLoss()]</font>/<font color='pink'>[L.getCloneLoss()]</font>"
|
||||
first_death["last_words"] = L.last_words
|
||||
var/sqlname = L.real_name
|
||||
var/sqlkey = L.ckey
|
||||
var/sqljob = L.mind.assigned_role
|
||||
var/sqlspecial = L.mind.special_role
|
||||
var/sqlpod = get_area_name(L, TRUE)
|
||||
var/laname = L.lastattacker
|
||||
var/lakey = L.lastattackerckey
|
||||
var/sqlbrute = L.getBruteLoss()
|
||||
var/sqlfire = L.getFireLoss()
|
||||
var/sqlbrain = L.getOrganLoss(ORGAN_SLOT_BRAIN)
|
||||
var/sqloxy = L.getOxyLoss()
|
||||
var/sqltox = L.getToxLoss()
|
||||
var/sqlclone = L.getCloneLoss()
|
||||
var/sqlstamina = L.getStaminaLoss()
|
||||
var/x_coord = L.x
|
||||
var/y_coord = L.y
|
||||
var/z_coord = L.z
|
||||
var/last_words = L.last_words
|
||||
var/suicide = L.suiciding
|
||||
var/map = SSmapping.config.map_name
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
|
||||
sqlname = sanitizeSQL(sqlname)
|
||||
sqlkey = sanitizeSQL(sqlkey)
|
||||
sqljob = sanitizeSQL(sqljob)
|
||||
sqlspecial = sanitizeSQL(sqlspecial)
|
||||
sqlpod = sanitizeSQL(sqlpod)
|
||||
laname = sanitizeSQL(laname)
|
||||
lakey = sanitizeSQL(lakey)
|
||||
sqlbrute = sanitizeSQL(sqlbrute)
|
||||
sqlfire = sanitizeSQL(sqlfire)
|
||||
sqlbrain = sanitizeSQL(sqlbrain)
|
||||
sqloxy = sanitizeSQL(sqloxy)
|
||||
sqltox = sanitizeSQL(sqltox)
|
||||
sqlclone = sanitizeSQL(sqlclone)
|
||||
sqlstamina = sanitizeSQL(sqlstamina)
|
||||
x_coord = sanitizeSQL(x_coord)
|
||||
y_coord = sanitizeSQL(y_coord)
|
||||
z_coord = sanitizeSQL(z_coord)
|
||||
last_words = sanitizeSQL(last_words)
|
||||
suicide = sanitizeSQL(suicide)
|
||||
map = sanitizeSQL(map)
|
||||
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])")
|
||||
if(query_report_death)
|
||||
query_report_death.Execute(async = TRUE)
|
||||
qdel(query_report_death)
|
||||
SUBSYSTEM_DEF(blackbox)
|
||||
name = "Blackbox"
|
||||
wait = 6000
|
||||
flags = SS_NO_TICK_CHECK
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
init_order = INIT_ORDER_BLACKBOX
|
||||
|
||||
var/list/feedback = list() //list of datum/feedback_variable
|
||||
var/list/first_death = list() //the first death of this round, assoc. vars keep track of different things
|
||||
var/triggertime = 0
|
||||
var/sealed = FALSE //time to stop tracking stats?
|
||||
var/list/versions = list("antagonists" = 3,
|
||||
"admin_secrets_fun_used" = 2,
|
||||
"explosion" = 2,
|
||||
"time_dilation_current" = 3,
|
||||
"science_techweb_unlock" = 2,
|
||||
"round_end_stats" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
|
||||
|
||||
/datum/controller/subsystem/blackbox/Initialize()
|
||||
triggertime = world.time
|
||||
record_feedback("amount", "random_seed", Master.random_seed)
|
||||
record_feedback("amount", "dm_version", DM_VERSION)
|
||||
record_feedback("amount", "byond_version", world.byond_version)
|
||||
record_feedback("amount", "byond_build", world.byond_build)
|
||||
. = ..()
|
||||
|
||||
//poll population
|
||||
/datum/controller/subsystem/blackbox/fire()
|
||||
set waitfor = FALSE //for population query
|
||||
|
||||
CheckPlayerCount()
|
||||
|
||||
if(CONFIG_GET(flag/use_exp_tracking))
|
||||
if((triggertime < 0) || (world.time > (triggertime +3000))) //subsystem fires once at roundstart then once every 10 minutes. a 5 min check skips the first fire. The <0 is midnight rollover check
|
||||
update_exp(10,FALSE)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/CheckPlayerCount()
|
||||
set waitfor = FALSE
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
var/playercount = 0
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
if(M.client)
|
||||
playercount += 1
|
||||
var/admincount = GLOB.admins.len
|
||||
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port, round_id) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', '[GLOB.round_id]')")
|
||||
query_record_playercount.Execute()
|
||||
qdel(query_record_playercount)
|
||||
|
||||
/datum/controller/subsystem/blackbox/Recover()
|
||||
feedback = SSblackbox.feedback
|
||||
sealed = SSblackbox.sealed
|
||||
|
||||
//no touchie
|
||||
/datum/controller/subsystem/blackbox/vv_get_var(var_name)
|
||||
if(var_name == "feedback")
|
||||
return debug_variable(var_name, deepCopyList(feedback), 0, src)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/blackbox/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if("feedback")
|
||||
return FALSE
|
||||
if("sealed")
|
||||
if(var_value)
|
||||
return Seal()
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
//Recorded on subsystem shutdown
|
||||
/datum/controller/subsystem/blackbox/proc/FinalFeedback()
|
||||
record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved")
|
||||
for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list)
|
||||
if (MS.pda_msgs.len)
|
||||
record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA")
|
||||
if (MS.rc_msgs.len)
|
||||
record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console")
|
||||
|
||||
for(var/player_key in GLOB.player_details)
|
||||
var/datum/player_details/PD = GLOB.player_details[player_key]
|
||||
record_feedback("tally", "client_byond_version", 1, PD.byond_version)
|
||||
|
||||
/datum/controller/subsystem/blackbox/Shutdown()
|
||||
sealed = FALSE
|
||||
FinalFeedback()
|
||||
|
||||
if (!SSdbcore.Connect())
|
||||
return
|
||||
|
||||
var/list/sqlrowlist = list()
|
||||
|
||||
for (var/datum/feedback_variable/FV in feedback)
|
||||
var/sqlversion = 1
|
||||
if(FV.key in versions)
|
||||
sqlversion = versions[FV.key]
|
||||
sqlrowlist += list(list("datetime" = "Now()", "round_id" = GLOB.round_id, "key_name" = "'[sanitizeSQL(FV.key)]'", "key_type" = "'[FV.key_type]'", "version" = "[sqlversion]", "json" = "'[sanitizeSQL(json_encode(FV.json))]'"))
|
||||
|
||||
if (!length(sqlrowlist))
|
||||
return
|
||||
|
||||
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/Seal()
|
||||
if(sealed)
|
||||
return FALSE
|
||||
if(IsAdminAdvancedProcCall())
|
||||
message_admins("[key_name_admin(usr)] sealed the blackbox!")
|
||||
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
|
||||
sealed = TRUE
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/LogBroadcast(freq)
|
||||
if(sealed)
|
||||
return
|
||||
switch(freq)
|
||||
if(FREQ_COMMON)
|
||||
record_feedback("tally", "radio_usage", 1, "common")
|
||||
if(FREQ_SCIENCE)
|
||||
record_feedback("tally", "radio_usage", 1, "science")
|
||||
if(FREQ_COMMAND)
|
||||
record_feedback("tally", "radio_usage", 1, "command")
|
||||
if(FREQ_MEDICAL)
|
||||
record_feedback("tally", "radio_usage", 1, "medical")
|
||||
if(FREQ_ENGINEERING)
|
||||
record_feedback("tally", "radio_usage", 1, "engineering")
|
||||
if(FREQ_SECURITY)
|
||||
record_feedback("tally", "radio_usage", 1, "security")
|
||||
if(FREQ_SYNDICATE)
|
||||
record_feedback("tally", "radio_usage", 1, "syndicate")
|
||||
if(FREQ_SERVICE)
|
||||
record_feedback("tally", "radio_usage", 1, "service")
|
||||
if(FREQ_SUPPLY)
|
||||
record_feedback("tally", "radio_usage", 1, "supply")
|
||||
if(FREQ_CENTCOM)
|
||||
record_feedback("tally", "radio_usage", 1, "centcom")
|
||||
if(FREQ_AI_PRIVATE)
|
||||
record_feedback("tally", "radio_usage", 1, "ai private")
|
||||
if(FREQ_CTF_RED)
|
||||
record_feedback("tally", "radio_usage", 1, "CTF red team")
|
||||
if(FREQ_CTF_BLUE)
|
||||
record_feedback("tally", "radio_usage", 1, "CTF blue team")
|
||||
else
|
||||
record_feedback("tally", "radio_usage", 1, "other")
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(key, key_type)
|
||||
for(var/datum/feedback_variable/FV in feedback)
|
||||
if(FV.key == key)
|
||||
return FV
|
||||
|
||||
var/datum/feedback_variable/FV = new(key, key_type)
|
||||
feedback += FV
|
||||
return FV
|
||||
/*
|
||||
feedback data can be recorded in 5 formats:
|
||||
"text"
|
||||
used for simple single-string records i.e. the current map
|
||||
further calls to the same key will append saved data unless the overwrite argument is true or it already exists
|
||||
when encoded calls made with overwrite will lack square brackets
|
||||
calls: SSblackbox.record_feedback("text", "example", 1, "sample text")
|
||||
SSblackbox.record_feedback("text", "example", 1, "other text")
|
||||
json: {"data":["sample text","other text"]}
|
||||
"amount"
|
||||
used to record simple counts of data i.e. the number of ahelps received
|
||||
further calls to the same key will add or subtract (if increment argument is a negative) from the saved amount
|
||||
calls: SSblackbox.record_feedback("amount", "example", 8)
|
||||
SSblackbox.record_feedback("amount", "example", 2)
|
||||
json: {"data":10}
|
||||
"tally"
|
||||
used to track the number of occurances of multiple related values i.e. how many times each type of gun is fired
|
||||
further calls to the same key will:
|
||||
add or subtract from the saved value of the data key if it already exists
|
||||
append the key and it's value if it doesn't exist
|
||||
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
|
||||
SSblackbox.record_feedback("tally", "example", 4, "sample data")
|
||||
SSblackbox.record_feedback("tally", "example", 2, "other data")
|
||||
json: {"data":{"sample data":5,"other data":2}}
|
||||
"nested tally"
|
||||
used to track the number of occurances of structured semi-relational values i.e. the results of arcade machines
|
||||
similar to running total, but related values are nested in a multi-dimensional array built
|
||||
the final element in the data list is used as the tracking key, all prior elements are used for nesting
|
||||
all data list elements must be strings
|
||||
further calls to the same key will:
|
||||
add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
|
||||
append the key and it's value if it doesn't exist
|
||||
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange", "apricot"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 10, list("fruit", "red", "apple"))
|
||||
SSblackbox.record_feedback("nested tally", "example", 1, list("vegetable", "orange", "carrot"))
|
||||
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10}},"vegetable":{"orange":{"carrot":1}}}}
|
||||
tracking values associated with a number can't merge with a nesting value, trying to do so will append the list
|
||||
call: SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
|
||||
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10},"orange":3},"vegetable":{"orange":{"carrot":1}}}}
|
||||
"associative"
|
||||
used to record text that's associated with a value i.e. coordinates
|
||||
further calls to the same key will append a new list to existing data
|
||||
calls: SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
|
||||
SSblackbox.record_feedback("associative", "example", 1, list("number" = 7, "text" = "example", "other text" = "sample"))
|
||||
json: {"data":{"1":{"text":"example","path":"/obj/item","number":"4"},"2":{"number":"7","text":"example","other text":"sample"}}}
|
||||
|
||||
Versioning
|
||||
If the format of a feedback variable is ever changed, i.e. how many levels of nesting are used or a new type of data is added to it, add it to the versions list
|
||||
When feedback is being saved if a key is in the versions list the value specified there will be used, otherwise all keys are assumed to be version = 1
|
||||
versions is an associative list, remember to use the same string in it as defined on a feedback variable, example:
|
||||
list/versions = list("round_end_stats" = 4,
|
||||
"admin_toggle" = 2,
|
||||
"gun_fired" = 2)
|
||||
*/
|
||||
/datum/controller/subsystem/blackbox/proc/record_feedback(key_type, key, increment, data, overwrite)
|
||||
if(sealed || !key_type || !istext(key) || !isnum(increment || !data))
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(key, key_type)
|
||||
switch(key_type)
|
||||
if("text")
|
||||
if(!istext(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
if(overwrite)
|
||||
FV.json["data"] = data
|
||||
else
|
||||
FV.json["data"] |= data
|
||||
if("amount")
|
||||
FV.json["data"] += increment
|
||||
if("tally")
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
FV.json["data"]["[data]"] += increment
|
||||
if("nested tally")
|
||||
if(!islist(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
FV.json["data"] = record_feedback_recurse_list(FV.json["data"], data, increment)
|
||||
if("associative")
|
||||
if(!islist(data))
|
||||
return
|
||||
if(!islist(FV.json["data"]))
|
||||
FV.json["data"] = list()
|
||||
var/pos = length(FV.json["data"]) + 1
|
||||
FV.json["data"]["[pos]"] = list() //in 512 "pos" can be replaced with "[FV.json["data"].len+1]"
|
||||
for(var/i in data)
|
||||
if(islist(data[i]))
|
||||
FV.json["data"]["[pos]"]["[i]"] = data[i] //and here with "[FV.json["data"].len]"
|
||||
else
|
||||
FV.json["data"]["[pos]"]["[i]"] = "[data[i]]"
|
||||
else
|
||||
CRASH("Invalid feedback key_type: [key_type]")
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/record_feedback_recurse_list(list/L, list/key_list, increment, depth = 1)
|
||||
if(depth == key_list.len)
|
||||
if(L.Find(key_list[depth]))
|
||||
L["[key_list[depth]]"] += increment
|
||||
else
|
||||
var/list/LFI = list(key_list[depth] = increment)
|
||||
L += LFI
|
||||
else
|
||||
if(!L.Find(key_list[depth]))
|
||||
var/list/LGD = list(key_list[depth] = list())
|
||||
L += LGD
|
||||
L["[key_list[depth-1]]"] = .(L["[key_list[depth]]"], key_list, increment, ++depth)
|
||||
return L
|
||||
|
||||
/datum/feedback_variable
|
||||
var/key
|
||||
var/key_type
|
||||
var/list/json = list()
|
||||
|
||||
/datum/feedback_variable/New(new_key, new_key_type)
|
||||
key = new_key
|
||||
key_type = new_key_type
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
|
||||
set waitfor = FALSE
|
||||
if(sealed)
|
||||
return
|
||||
if(!L || !L.key || !L.mind)
|
||||
return
|
||||
if(!L.suiciding && !first_death.len)
|
||||
first_death["name"] = "[(L.real_name == L.name) ? L.real_name : "[L.real_name] as [L.name]"]"
|
||||
first_death["role"] = null
|
||||
if(L.mind.assigned_role)
|
||||
first_death["role"] = L.mind.assigned_role
|
||||
first_death["area"] = "[AREACOORD(L)]"
|
||||
first_death["damage"] = "<font color='#FF5555'>[L.getBruteLoss()]</font>/<font color='orange'>[L.getFireLoss()]</font>/<font color='lightgreen'>[L.getToxLoss()]</font>/<font color='lightblue'>[L.getOxyLoss()]</font>/<font color='pink'>[L.getCloneLoss()]</font>"
|
||||
first_death["last_words"] = L.last_words
|
||||
var/sqlname = L.real_name
|
||||
var/sqlkey = L.ckey
|
||||
var/sqljob = L.mind.assigned_role
|
||||
var/sqlspecial = L.mind.special_role
|
||||
var/sqlpod = get_area_name(L, TRUE)
|
||||
var/laname = L.lastattacker
|
||||
var/lakey = L.lastattackerckey
|
||||
var/sqlbrute = L.getBruteLoss()
|
||||
var/sqlfire = L.getFireLoss()
|
||||
var/sqlbrain = L.getOrganLoss(ORGAN_SLOT_BRAIN)
|
||||
var/sqloxy = L.getOxyLoss()
|
||||
var/sqltox = L.getToxLoss()
|
||||
var/sqlclone = L.getCloneLoss()
|
||||
var/sqlstamina = L.getStaminaLoss()
|
||||
var/x_coord = L.x
|
||||
var/y_coord = L.y
|
||||
var/z_coord = L.z
|
||||
var/last_words = L.last_words
|
||||
var/suicide = L.suiciding
|
||||
var/map = SSmapping.config.map_name
|
||||
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
|
||||
sqlname = sanitizeSQL(sqlname)
|
||||
sqlkey = sanitizeSQL(sqlkey)
|
||||
sqljob = sanitizeSQL(sqljob)
|
||||
sqlspecial = sanitizeSQL(sqlspecial)
|
||||
sqlpod = sanitizeSQL(sqlpod)
|
||||
laname = sanitizeSQL(laname)
|
||||
lakey = sanitizeSQL(lakey)
|
||||
sqlbrute = sanitizeSQL(sqlbrute)
|
||||
sqlfire = sanitizeSQL(sqlfire)
|
||||
sqlbrain = sanitizeSQL(sqlbrain)
|
||||
sqloxy = sanitizeSQL(sqloxy)
|
||||
sqltox = sanitizeSQL(sqltox)
|
||||
sqlclone = sanitizeSQL(sqlclone)
|
||||
sqlstamina = sanitizeSQL(sqlstamina)
|
||||
x_coord = sanitizeSQL(x_coord)
|
||||
y_coord = sanitizeSQL(y_coord)
|
||||
z_coord = sanitizeSQL(z_coord)
|
||||
last_words = sanitizeSQL(last_words)
|
||||
suicide = sanitizeSQL(suicide)
|
||||
map = sanitizeSQL(map)
|
||||
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])")
|
||||
if(query_report_death)
|
||||
query_report_death.Execute(async = TRUE)
|
||||
qdel(query_report_death)
|
||||
|
||||
@@ -1,365 +1,365 @@
|
||||
SUBSYSTEM_DEF(dbcore)
|
||||
name = "Database"
|
||||
flags = SS_BACKGROUND
|
||||
wait = 1 MINUTES
|
||||
init_order = INIT_ORDER_DBCORE
|
||||
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
|
||||
|
||||
var/schema_mismatch = 0
|
||||
var/db_minor = 0
|
||||
var/db_major = 0
|
||||
var/failed_connections = 0
|
||||
|
||||
var/last_error
|
||||
var/list/active_queries = list()
|
||||
|
||||
var/datum/BSQL_Connection/connection
|
||||
var/datum/BSQL_Operation/connectOperation
|
||||
|
||||
/datum/controller/subsystem/dbcore/Initialize()
|
||||
//We send warnings to the admins during subsystem init, as the clients will be New'd and messages
|
||||
//will queue properly with goonchat
|
||||
switch(schema_mismatch)
|
||||
if(1)
|
||||
message_admins("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
|
||||
if(2)
|
||||
message_admins("Could not get schema version from database")
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/fire()
|
||||
for(var/I in active_queries)
|
||||
var/datum/DBQuery/Q = I
|
||||
if(world.time - Q.last_activity_time > (5 MINUTES))
|
||||
message_admins("Found undeleted query, please check the server logs and notify coders.")
|
||||
log_sql("Undeleted query: \"[Q.sql]\" LA: [Q.last_activity] LAT: [Q.last_activity_time]")
|
||||
qdel(Q)
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/dbcore/Recover()
|
||||
connection = SSdbcore.connection
|
||||
connectOperation = SSdbcore.connectOperation
|
||||
|
||||
/datum/controller/subsystem/dbcore/Shutdown()
|
||||
//This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_round_shutdown = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = '[sanitizeSQL(SSticker.end_state)]' WHERE id = [GLOB.round_id]")
|
||||
query_round_shutdown.Execute()
|
||||
qdel(query_round_shutdown)
|
||||
if(IsConnected())
|
||||
Disconnect()
|
||||
world.BSQL_Shutdown()
|
||||
|
||||
//nu
|
||||
/datum/controller/subsystem/dbcore/can_vv_get(var_name)
|
||||
return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && var_name != NAMEOF(src, connectOperation) && ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/vv_edit_var(var_name, var_value)
|
||||
if(var_name == NAMEOF(src, connection) || var_name == NAMEOF(src, connectOperation))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Connect()
|
||||
if(IsConnected())
|
||||
return TRUE
|
||||
|
||||
if(failed_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect anymore.
|
||||
return FALSE
|
||||
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return FALSE
|
||||
|
||||
var/user = CONFIG_GET(string/feedback_login)
|
||||
var/pass = CONFIG_GET(string/feedback_password)
|
||||
var/db = CONFIG_GET(string/feedback_database)
|
||||
var/address = CONFIG_GET(string/address)
|
||||
var/port = CONFIG_GET(number/port)
|
||||
|
||||
connection = new /datum/BSQL_Connection(BSQL_CONNECTION_TYPE_MARIADB, CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout), CONFIG_GET(number/bsql_thread_limit))
|
||||
var/error
|
||||
if(QDELETED(connection))
|
||||
connection = null
|
||||
error = last_error
|
||||
else
|
||||
SSdbcore.last_error = null
|
||||
connectOperation = connection.BeginConnect(address, port, user, pass, db)
|
||||
if(SSdbcore.last_error)
|
||||
CRASH(SSdbcore.last_error)
|
||||
UNTIL(connectOperation.IsComplete())
|
||||
error = connectOperation.GetError()
|
||||
. = !error
|
||||
if (!.)
|
||||
last_error = error
|
||||
log_sql("Connect() failed | [error]")
|
||||
++failed_connections
|
||||
QDEL_NULL(connection)
|
||||
QDEL_NULL(connectOperation)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion()
|
||||
if(CONFIG_GET(flag/sql_enabled))
|
||||
if(Connect())
|
||||
log_world("Database connection established.")
|
||||
var/datum/DBQuery/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
|
||||
query_db_version.Execute()
|
||||
if(query_db_version.NextRow())
|
||||
db_major = text2num(query_db_version.item[1])
|
||||
db_minor = text2num(query_db_version.item[2])
|
||||
if(db_major != DB_MAJOR_VERSION || db_minor != DB_MINOR_VERSION)
|
||||
schema_mismatch = 1 // flag admin message about mismatch
|
||||
log_sql("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
|
||||
else
|
||||
schema_mismatch = 2 //flag admin message about no schema version
|
||||
log_sql("Could not get schema version from database")
|
||||
qdel(query_db_version)
|
||||
else
|
||||
log_sql("Your server failed to establish a connection with the database.")
|
||||
else
|
||||
log_sql("Database is not enabled in configuration.")
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundID()
|
||||
if(!Connect())
|
||||
return
|
||||
var/datum/DBQuery/query_round_initialize = SSdbcore.NewQuery("INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
|
||||
query_round_initialize.Execute()
|
||||
qdel(query_round_initialize)
|
||||
var/datum/DBQuery/query_round_last_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
|
||||
query_round_last_id.Execute()
|
||||
if(query_round_last_id.NextRow())
|
||||
GLOB.round_id = query_round_last_id.item[1]
|
||||
qdel(query_round_last_id)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundStart()
|
||||
if(!Connect())
|
||||
return
|
||||
var/datum/DBQuery/query_round_start = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = [GLOB.round_id]")
|
||||
query_round_start.Execute()
|
||||
qdel(query_round_start)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundEnd()
|
||||
if(!Connect())
|
||||
return
|
||||
var/sql_station_name = sanitizeSQL(station_name())
|
||||
var/datum/DBQuery/query_round_end = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = '[sanitizeSQL(SSticker.mode_result)]', station_name = '[sql_station_name]' WHERE id = [GLOB.round_id]")
|
||||
query_round_end.Execute()
|
||||
qdel(query_round_end)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Disconnect()
|
||||
failed_connections = 0
|
||||
QDEL_NULL(connectOperation)
|
||||
QDEL_NULL(connection)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/IsConnected()
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return FALSE
|
||||
//block until any connect operations finish
|
||||
var/datum/BSQL_Connection/_connection = connection
|
||||
var/datum/BSQL_Operation/op = connectOperation
|
||||
UNTIL(QDELETED(_connection) || op.IsComplete())
|
||||
return !QDELETED(connection) && !op.GetError()
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Quote(str)
|
||||
if(connection)
|
||||
return connection.Quote(str)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/ErrorMsg()
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return "Database disabled by configuration"
|
||||
return last_error
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/ReportError(error)
|
||||
last_error = error
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
log_admin_private("ERROR: Advanced admin proc call led to sql query: [sql_query]. Query has been blocked")
|
||||
message_admins("ERROR: Advanced admin proc call led to sql query. Query has been blocked")
|
||||
return FALSE
|
||||
return new /datum/DBQuery(sql_query, connection)
|
||||
|
||||
/*
|
||||
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
|
||||
Rows missing columns present in other rows will resolve to SQL NULL
|
||||
You are expected to do your own escaping of the data, and expected to provide your own quotes for strings.
|
||||
The duplicate_key arg can be true to automatically generate this part of the query
|
||||
or set to a string that is appended to the end of the query
|
||||
Ignore_errors instructes mysql to continue inserting rows if some of them have errors.
|
||||
the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
|
||||
Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables,
|
||||
It was included because it is still supported in mariadb.
|
||||
It does not work with duplicate_key and the mysql server ignores it in those cases
|
||||
*/
|
||||
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE)
|
||||
if (!table || !rows || !istype(rows))
|
||||
return
|
||||
var/list/columns = list()
|
||||
var/list/sorted_rows = list()
|
||||
|
||||
for (var/list/row in rows)
|
||||
var/list/sorted_row = list()
|
||||
sorted_row.len = columns.len
|
||||
for (var/column in row)
|
||||
var/idx = columns[column]
|
||||
if (!idx)
|
||||
idx = columns.len + 1
|
||||
columns[column] = idx
|
||||
sorted_row.len = columns.len
|
||||
|
||||
sorted_row[idx] = row[column]
|
||||
sorted_rows[++sorted_rows.len] = sorted_row
|
||||
|
||||
if (duplicate_key == TRUE)
|
||||
var/list/column_list = list()
|
||||
for (var/column in columns)
|
||||
column_list += "[column] = VALUES([column])"
|
||||
duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n"
|
||||
else if (duplicate_key == FALSE)
|
||||
duplicate_key = null
|
||||
|
||||
if (ignore_errors)
|
||||
ignore_errors = " IGNORE"
|
||||
else
|
||||
ignore_errors = null
|
||||
|
||||
if (delayed)
|
||||
delayed = " DELAYED"
|
||||
else
|
||||
delayed = null
|
||||
|
||||
var/list/sqlrowlist = list()
|
||||
var/len = columns.len
|
||||
for (var/list/row in sorted_rows)
|
||||
if (length(row) != len)
|
||||
row.len = len
|
||||
for (var/value in row)
|
||||
if (value == null)
|
||||
value = "NULL"
|
||||
sqlrowlist += "([row.Join(", ")])"
|
||||
|
||||
sqlrowlist = " [sqlrowlist.Join(",\n ")]"
|
||||
var/datum/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]")
|
||||
if (warn)
|
||||
. = Query.warn_execute(async)
|
||||
else
|
||||
. = Query.Execute(async)
|
||||
qdel(Query)
|
||||
|
||||
/datum/DBQuery
|
||||
var/sql // The sql query being executed.
|
||||
var/list/item //list of data values populated by NextRow()
|
||||
|
||||
var/last_activity
|
||||
var/last_activity_time
|
||||
|
||||
var/last_error
|
||||
var/skip_next_is_complete
|
||||
var/in_progress
|
||||
var/datum/BSQL_Connection/connection
|
||||
var/datum/BSQL_Operation/Query/query
|
||||
|
||||
/datum/DBQuery/New(sql_query, datum/BSQL_Connection/connection)
|
||||
SSdbcore.active_queries[src] = TRUE
|
||||
Activity("Created")
|
||||
item = list()
|
||||
src.connection = connection
|
||||
sql = sql_query
|
||||
|
||||
/datum/DBQuery/Destroy()
|
||||
Close()
|
||||
SSdbcore.active_queries -= src
|
||||
return ..()
|
||||
|
||||
/datum/DBQuery/CanProcCall(proc_name)
|
||||
//fuck off kevinz
|
||||
return FALSE
|
||||
|
||||
/datum/DBQuery/proc/SetQuery(new_sql)
|
||||
if(in_progress)
|
||||
CRASH("Attempted to set new sql while waiting on active query")
|
||||
Close()
|
||||
sql = new_sql
|
||||
|
||||
/datum/DBQuery/proc/Activity(activity)
|
||||
last_activity = activity
|
||||
last_activity_time = world.time
|
||||
|
||||
/datum/DBQuery/proc/warn_execute(async = FALSE)
|
||||
. = Execute(async)
|
||||
if(!.)
|
||||
to_chat(usr, "<span class='danger'>A SQL error occurred during this operation, check the server logs.</span>")
|
||||
|
||||
/datum/DBQuery/proc/Execute(async = FALSE, log_error = TRUE)
|
||||
Activity("Execute")
|
||||
if(in_progress)
|
||||
CRASH("Attempted to start a new query while waiting on the old one")
|
||||
|
||||
if(QDELETED(connection))
|
||||
last_error = "No connection!"
|
||||
return FALSE
|
||||
|
||||
var/start_time
|
||||
var/timed_out
|
||||
if(!async)
|
||||
start_time = REALTIMEOFDAY
|
||||
Close()
|
||||
query = connection.BeginQuery(sql)
|
||||
if(!async)
|
||||
timed_out = !query.WaitForCompletion()
|
||||
else
|
||||
in_progress = TRUE
|
||||
UNTIL(query.IsComplete())
|
||||
in_progress = FALSE
|
||||
skip_next_is_complete = TRUE
|
||||
var/error = QDELETED(query) ? "Query object deleted!" : query.GetError()
|
||||
last_error = error
|
||||
. = !error
|
||||
if(!. && log_error)
|
||||
log_sql("[error] | Query used: [sql]")
|
||||
if(!async && timed_out)
|
||||
log_query_debug("Query execution started at [start_time]")
|
||||
log_query_debug("Query execution ended at [REALTIMEOFDAY]")
|
||||
log_query_debug("Slow query timeout detected.")
|
||||
log_query_debug("Query used: [sql]")
|
||||
slow_query_check()
|
||||
|
||||
/datum/DBQuery/proc/slow_query_check()
|
||||
message_admins("HEY! A database query timed out. Did the server just hang? <a href='?_src_=holder;[HrefToken()];slowquery=yes'>\[YES\]</a>|<a href='?_src_=holder;[HrefToken()];slowquery=no'>\[NO\]</a>")
|
||||
|
||||
/datum/DBQuery/proc/NextRow(async)
|
||||
Activity("NextRow")
|
||||
UNTIL(!in_progress)
|
||||
if(!skip_next_is_complete)
|
||||
if(!async)
|
||||
query.WaitForCompletion()
|
||||
else
|
||||
in_progress = TRUE
|
||||
UNTIL(query.IsComplete())
|
||||
in_progress = FALSE
|
||||
else
|
||||
skip_next_is_complete = FALSE
|
||||
|
||||
last_error = query.GetError()
|
||||
var/list/results = query.CurrentRow()
|
||||
. = results != null
|
||||
|
||||
item.Cut()
|
||||
//populate item array
|
||||
for(var/I in results)
|
||||
item += results[I]
|
||||
|
||||
/datum/DBQuery/proc/ErrorMsg()
|
||||
return last_error
|
||||
|
||||
/datum/DBQuery/proc/Close()
|
||||
item.Cut()
|
||||
QDEL_NULL(query)
|
||||
|
||||
/world/BSQL_Debug(message)
|
||||
if(!CONFIG_GET(flag/bsql_debug))
|
||||
return
|
||||
|
||||
//strip sensitive stuff
|
||||
if(findtext(message, ": CreateConnection("))
|
||||
message = "CreateConnection CENSORED"
|
||||
|
||||
log_sql("BSQL_DEBUG: [message]")
|
||||
SUBSYSTEM_DEF(dbcore)
|
||||
name = "Database"
|
||||
flags = SS_BACKGROUND
|
||||
wait = 1 MINUTES
|
||||
init_order = INIT_ORDER_DBCORE
|
||||
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
|
||||
|
||||
var/schema_mismatch = 0
|
||||
var/db_minor = 0
|
||||
var/db_major = 0
|
||||
var/failed_connections = 0
|
||||
|
||||
var/last_error
|
||||
var/list/active_queries = list()
|
||||
|
||||
var/datum/BSQL_Connection/connection
|
||||
var/datum/BSQL_Operation/connectOperation
|
||||
|
||||
/datum/controller/subsystem/dbcore/Initialize()
|
||||
//We send warnings to the admins during subsystem init, as the clients will be New'd and messages
|
||||
//will queue properly with goonchat
|
||||
switch(schema_mismatch)
|
||||
if(1)
|
||||
message_admins("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
|
||||
if(2)
|
||||
message_admins("Could not get schema version from database")
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/fire()
|
||||
for(var/I in active_queries)
|
||||
var/datum/DBQuery/Q = I
|
||||
if(world.time - Q.last_activity_time > (5 MINUTES))
|
||||
message_admins("Found undeleted query, please check the server logs and notify coders.")
|
||||
log_sql("Undeleted query: \"[Q.sql]\" LA: [Q.last_activity] LAT: [Q.last_activity_time]")
|
||||
qdel(Q)
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/dbcore/Recover()
|
||||
connection = SSdbcore.connection
|
||||
connectOperation = SSdbcore.connectOperation
|
||||
|
||||
/datum/controller/subsystem/dbcore/Shutdown()
|
||||
//This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_round_shutdown = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = '[sanitizeSQL(SSticker.end_state)]' WHERE id = [GLOB.round_id]")
|
||||
query_round_shutdown.Execute()
|
||||
qdel(query_round_shutdown)
|
||||
if(IsConnected())
|
||||
Disconnect()
|
||||
world.BSQL_Shutdown()
|
||||
|
||||
//nu
|
||||
/datum/controller/subsystem/dbcore/can_vv_get(var_name)
|
||||
return var_name != NAMEOF(src, connection) && var_name != NAMEOF(src, active_queries) && var_name != NAMEOF(src, connectOperation) && ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/vv_edit_var(var_name, var_value)
|
||||
if(var_name == NAMEOF(src, connection) || var_name == NAMEOF(src, connectOperation))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Connect()
|
||||
if(IsConnected())
|
||||
return TRUE
|
||||
|
||||
if(failed_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect anymore.
|
||||
return FALSE
|
||||
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return FALSE
|
||||
|
||||
var/user = CONFIG_GET(string/feedback_login)
|
||||
var/pass = CONFIG_GET(string/feedback_password)
|
||||
var/db = CONFIG_GET(string/feedback_database)
|
||||
var/address = CONFIG_GET(string/address)
|
||||
var/port = CONFIG_GET(number/port)
|
||||
|
||||
connection = new /datum/BSQL_Connection(BSQL_CONNECTION_TYPE_MARIADB, CONFIG_GET(number/async_query_timeout), CONFIG_GET(number/blocking_query_timeout), CONFIG_GET(number/bsql_thread_limit))
|
||||
var/error
|
||||
if(QDELETED(connection))
|
||||
connection = null
|
||||
error = last_error
|
||||
else
|
||||
SSdbcore.last_error = null
|
||||
connectOperation = connection.BeginConnect(address, port, user, pass, db)
|
||||
if(SSdbcore.last_error)
|
||||
CRASH(SSdbcore.last_error)
|
||||
UNTIL(connectOperation.IsComplete())
|
||||
error = connectOperation.GetError()
|
||||
. = !error
|
||||
if (!.)
|
||||
last_error = error
|
||||
log_sql("Connect() failed | [error]")
|
||||
++failed_connections
|
||||
QDEL_NULL(connection)
|
||||
QDEL_NULL(connectOperation)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion()
|
||||
if(CONFIG_GET(flag/sql_enabled))
|
||||
if(Connect())
|
||||
log_world("Database connection established.")
|
||||
var/datum/DBQuery/query_db_version = NewQuery("SELECT major, minor FROM [format_table_name("schema_revision")] ORDER BY date DESC LIMIT 1")
|
||||
query_db_version.Execute()
|
||||
if(query_db_version.NextRow())
|
||||
db_major = text2num(query_db_version.item[1])
|
||||
db_minor = text2num(query_db_version.item[2])
|
||||
if(db_major != DB_MAJOR_VERSION || db_minor != DB_MINOR_VERSION)
|
||||
schema_mismatch = 1 // flag admin message about mismatch
|
||||
log_sql("Database schema ([db_major].[db_minor]) doesn't match the latest schema version ([DB_MAJOR_VERSION].[DB_MINOR_VERSION]), this may lead to undefined behaviour or errors")
|
||||
else
|
||||
schema_mismatch = 2 //flag admin message about no schema version
|
||||
log_sql("Could not get schema version from database")
|
||||
qdel(query_db_version)
|
||||
else
|
||||
log_sql("Your server failed to establish a connection with the database.")
|
||||
else
|
||||
log_sql("Database is not enabled in configuration.")
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundID()
|
||||
if(!Connect())
|
||||
return
|
||||
var/datum/DBQuery/query_round_initialize = SSdbcore.NewQuery("INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
|
||||
query_round_initialize.Execute()
|
||||
qdel(query_round_initialize)
|
||||
var/datum/DBQuery/query_round_last_id = SSdbcore.NewQuery("SELECT LAST_INSERT_ID()")
|
||||
query_round_last_id.Execute()
|
||||
if(query_round_last_id.NextRow())
|
||||
GLOB.round_id = query_round_last_id.item[1]
|
||||
qdel(query_round_last_id)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundStart()
|
||||
if(!Connect())
|
||||
return
|
||||
var/datum/DBQuery/query_round_start = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET start_datetime = Now() WHERE id = [GLOB.round_id]")
|
||||
query_round_start.Execute()
|
||||
qdel(query_round_start)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/SetRoundEnd()
|
||||
if(!Connect())
|
||||
return
|
||||
var/sql_station_name = sanitizeSQL(station_name())
|
||||
var/datum/DBQuery/query_round_end = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = '[sanitizeSQL(SSticker.mode_result)]', station_name = '[sql_station_name]' WHERE id = [GLOB.round_id]")
|
||||
query_round_end.Execute()
|
||||
qdel(query_round_end)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Disconnect()
|
||||
failed_connections = 0
|
||||
QDEL_NULL(connectOperation)
|
||||
QDEL_NULL(connection)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/IsConnected()
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return FALSE
|
||||
//block until any connect operations finish
|
||||
var/datum/BSQL_Connection/_connection = connection
|
||||
var/datum/BSQL_Operation/op = connectOperation
|
||||
UNTIL(QDELETED(_connection) || op.IsComplete())
|
||||
return !QDELETED(connection) && !op.GetError()
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/Quote(str)
|
||||
if(connection)
|
||||
return connection.Quote(str)
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/ErrorMsg()
|
||||
if(!CONFIG_GET(flag/sql_enabled))
|
||||
return "Database disabled by configuration"
|
||||
return last_error
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/ReportError(error)
|
||||
last_error = error
|
||||
|
||||
/datum/controller/subsystem/dbcore/proc/NewQuery(sql_query)
|
||||
if(IsAdminAdvancedProcCall())
|
||||
log_admin_private("ERROR: Advanced admin proc call led to sql query: [sql_query]. Query has been blocked")
|
||||
message_admins("ERROR: Advanced admin proc call led to sql query. Query has been blocked")
|
||||
return FALSE
|
||||
return new /datum/DBQuery(sql_query, connection)
|
||||
|
||||
/*
|
||||
Takes a list of rows (each row being an associated list of column => value) and inserts them via a single mass query.
|
||||
Rows missing columns present in other rows will resolve to SQL NULL
|
||||
You are expected to do your own escaping of the data, and expected to provide your own quotes for strings.
|
||||
The duplicate_key arg can be true to automatically generate this part of the query
|
||||
or set to a string that is appended to the end of the query
|
||||
Ignore_errors instructes mysql to continue inserting rows if some of them have errors.
|
||||
the erroneous row(s) aren't inserted and there isn't really any way to know why or why errored
|
||||
Delayed insert mode was removed in mysql 7 and only works with MyISAM type tables,
|
||||
It was included because it is still supported in mariadb.
|
||||
It does not work with duplicate_key and the mysql server ignores it in those cases
|
||||
*/
|
||||
/datum/controller/subsystem/dbcore/proc/MassInsert(table, list/rows, duplicate_key = FALSE, ignore_errors = FALSE, delayed = FALSE, warn = FALSE, async = TRUE)
|
||||
if (!table || !rows || !istype(rows))
|
||||
return
|
||||
var/list/columns = list()
|
||||
var/list/sorted_rows = list()
|
||||
|
||||
for (var/list/row in rows)
|
||||
var/list/sorted_row = list()
|
||||
sorted_row.len = columns.len
|
||||
for (var/column in row)
|
||||
var/idx = columns[column]
|
||||
if (!idx)
|
||||
idx = columns.len + 1
|
||||
columns[column] = idx
|
||||
sorted_row.len = columns.len
|
||||
|
||||
sorted_row[idx] = row[column]
|
||||
sorted_rows[++sorted_rows.len] = sorted_row
|
||||
|
||||
if (duplicate_key == TRUE)
|
||||
var/list/column_list = list()
|
||||
for (var/column in columns)
|
||||
column_list += "[column] = VALUES([column])"
|
||||
duplicate_key = "ON DUPLICATE KEY UPDATE [column_list.Join(", ")]\n"
|
||||
else if (duplicate_key == FALSE)
|
||||
duplicate_key = null
|
||||
|
||||
if (ignore_errors)
|
||||
ignore_errors = " IGNORE"
|
||||
else
|
||||
ignore_errors = null
|
||||
|
||||
if (delayed)
|
||||
delayed = " DELAYED"
|
||||
else
|
||||
delayed = null
|
||||
|
||||
var/list/sqlrowlist = list()
|
||||
var/len = columns.len
|
||||
for (var/list/row in sorted_rows)
|
||||
if (length(row) != len)
|
||||
row.len = len
|
||||
for (var/value in row)
|
||||
if (value == null)
|
||||
value = "NULL"
|
||||
sqlrowlist += "([row.Join(", ")])"
|
||||
|
||||
sqlrowlist = " [sqlrowlist.Join(",\n ")]"
|
||||
var/datum/DBQuery/Query = NewQuery("INSERT[delayed][ignore_errors] INTO [table]\n([columns.Join(", ")])\nVALUES\n[sqlrowlist]\n[duplicate_key]")
|
||||
if (warn)
|
||||
. = Query.warn_execute(async)
|
||||
else
|
||||
. = Query.Execute(async)
|
||||
qdel(Query)
|
||||
|
||||
/datum/DBQuery
|
||||
var/sql // The sql query being executed.
|
||||
var/list/item //list of data values populated by NextRow()
|
||||
|
||||
var/last_activity
|
||||
var/last_activity_time
|
||||
|
||||
var/last_error
|
||||
var/skip_next_is_complete
|
||||
var/in_progress
|
||||
var/datum/BSQL_Connection/connection
|
||||
var/datum/BSQL_Operation/Query/query
|
||||
|
||||
/datum/DBQuery/New(sql_query, datum/BSQL_Connection/connection)
|
||||
SSdbcore.active_queries[src] = TRUE
|
||||
Activity("Created")
|
||||
item = list()
|
||||
src.connection = connection
|
||||
sql = sql_query
|
||||
|
||||
/datum/DBQuery/Destroy()
|
||||
Close()
|
||||
SSdbcore.active_queries -= src
|
||||
return ..()
|
||||
|
||||
/datum/DBQuery/CanProcCall(proc_name)
|
||||
//fuck off kevinz
|
||||
return FALSE
|
||||
|
||||
/datum/DBQuery/proc/SetQuery(new_sql)
|
||||
if(in_progress)
|
||||
CRASH("Attempted to set new sql while waiting on active query")
|
||||
Close()
|
||||
sql = new_sql
|
||||
|
||||
/datum/DBQuery/proc/Activity(activity)
|
||||
last_activity = activity
|
||||
last_activity_time = world.time
|
||||
|
||||
/datum/DBQuery/proc/warn_execute(async = FALSE)
|
||||
. = Execute(async)
|
||||
if(!.)
|
||||
to_chat(usr, "<span class='danger'>A SQL error occurred during this operation, check the server logs.</span>")
|
||||
|
||||
/datum/DBQuery/proc/Execute(async = FALSE, log_error = TRUE)
|
||||
Activity("Execute")
|
||||
if(in_progress)
|
||||
CRASH("Attempted to start a new query while waiting on the old one")
|
||||
|
||||
if(QDELETED(connection))
|
||||
last_error = "No connection!"
|
||||
return FALSE
|
||||
|
||||
var/start_time
|
||||
var/timed_out
|
||||
if(!async)
|
||||
start_time = REALTIMEOFDAY
|
||||
Close()
|
||||
query = connection.BeginQuery(sql)
|
||||
if(!async)
|
||||
timed_out = !query.WaitForCompletion()
|
||||
else
|
||||
in_progress = TRUE
|
||||
UNTIL(query.IsComplete())
|
||||
in_progress = FALSE
|
||||
skip_next_is_complete = TRUE
|
||||
var/error = QDELETED(query) ? "Query object deleted!" : query.GetError()
|
||||
last_error = error
|
||||
. = !error
|
||||
if(!. && log_error)
|
||||
log_sql("[error] | Query used: [sql]")
|
||||
if(!async && timed_out)
|
||||
log_query_debug("Query execution started at [start_time]")
|
||||
log_query_debug("Query execution ended at [REALTIMEOFDAY]")
|
||||
log_query_debug("Slow query timeout detected.")
|
||||
log_query_debug("Query used: [sql]")
|
||||
slow_query_check()
|
||||
|
||||
/datum/DBQuery/proc/slow_query_check()
|
||||
message_admins("HEY! A database query timed out. Did the server just hang? <a href='?_src_=holder;[HrefToken()];slowquery=yes'>\[YES\]</a>|<a href='?_src_=holder;[HrefToken()];slowquery=no'>\[NO\]</a>")
|
||||
|
||||
/datum/DBQuery/proc/NextRow(async)
|
||||
Activity("NextRow")
|
||||
UNTIL(!in_progress)
|
||||
if(!skip_next_is_complete)
|
||||
if(!async)
|
||||
query.WaitForCompletion()
|
||||
else
|
||||
in_progress = TRUE
|
||||
UNTIL(query.IsComplete())
|
||||
in_progress = FALSE
|
||||
else
|
||||
skip_next_is_complete = FALSE
|
||||
|
||||
last_error = query.GetError()
|
||||
var/list/results = query.CurrentRow()
|
||||
. = results != null
|
||||
|
||||
item.Cut()
|
||||
//populate item array
|
||||
for(var/I in results)
|
||||
item += results[I]
|
||||
|
||||
/datum/DBQuery/proc/ErrorMsg()
|
||||
return last_error
|
||||
|
||||
/datum/DBQuery/proc/Close()
|
||||
item.Cut()
|
||||
QDEL_NULL(query)
|
||||
|
||||
/world/BSQL_Debug(message)
|
||||
if(!CONFIG_GET(flag/bsql_debug))
|
||||
return
|
||||
|
||||
//strip sensitive stuff
|
||||
if(findtext(message, ": CreateConnection("))
|
||||
message = "CreateConnection CENSORED"
|
||||
|
||||
log_sql("BSQL_DEBUG: [message]")
|
||||
|
||||
@@ -1,87 +1,87 @@
|
||||
SUBSYSTEM_DEF(medals)
|
||||
name = "Medals"
|
||||
flags = SS_NO_FIRE
|
||||
var/hub_enabled = FALSE
|
||||
|
||||
/datum/controller/subsystem/medals/Initialize(timeofday)
|
||||
if(CONFIG_GET(string/medal_hub_address) && CONFIG_GET(string/medal_hub_password))
|
||||
hub_enabled = TRUE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/medals/proc/UnlockMedal(medal, client/player)
|
||||
set waitfor = FALSE
|
||||
if(!medal || !hub_enabled)
|
||||
return
|
||||
if(isnull(world.SetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to award [medal] medal to [player.key]!")
|
||||
return
|
||||
to_chat(player, "<span class='greenannounce'><B>Achievement unlocked: [medal]!</B></span>")
|
||||
|
||||
|
||||
/datum/controller/subsystem/medals/proc/SetScore(score, client/player, increment, force)
|
||||
set waitfor = FALSE
|
||||
if(!score || !hub_enabled)
|
||||
return
|
||||
|
||||
var/list/oldscore = GetScore(score, player, TRUE)
|
||||
if(increment)
|
||||
if(!oldscore[score])
|
||||
oldscore[score] = 1
|
||||
else
|
||||
oldscore[score] = (text2num(oldscore[score]) + 1)
|
||||
else
|
||||
oldscore[score] = force
|
||||
|
||||
var/newscoreparam = list2params(oldscore)
|
||||
|
||||
if(isnull(world.SetScores(player.ckey, newscoreparam, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to set [score] score for [player.key]!")
|
||||
|
||||
/datum/controller/subsystem/medals/proc/GetScore(score, client/player, returnlist)
|
||||
if(!score || !hub_enabled)
|
||||
return
|
||||
|
||||
var/scoreget = world.GetScores(player.ckey, score, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
|
||||
if(isnull(scoreget))
|
||||
hub_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to get score: [score] for [player.key]!")
|
||||
return
|
||||
. = params2list(scoreget)
|
||||
if(!returnlist)
|
||||
return .[score]
|
||||
|
||||
/datum/controller/subsystem/medals/proc/CheckMedal(medal, client/player)
|
||||
if(!medal || !hub_enabled)
|
||||
return
|
||||
|
||||
if(isnull(world.GetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player: [player.key]")
|
||||
message_admins("Error! Failed to contact hub to get [medal] medal for [player.key]!")
|
||||
return
|
||||
to_chat(player, "[medal] is unlocked")
|
||||
|
||||
/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player)
|
||||
if(!player || !medal || !hub_enabled)
|
||||
return
|
||||
var/result = world.ClearMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
|
||||
switch(result)
|
||||
if(null)
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to clear [medal] medal for [player.key]!")
|
||||
if(TRUE)
|
||||
message_admins("Medal: [medal] removed for [player.key]")
|
||||
if(FALSE)
|
||||
message_admins("Medal: [medal] was not found for [player.key]. Unable to clear.")
|
||||
|
||||
|
||||
/datum/controller/subsystem/medals/proc/ClearScore(client/player)
|
||||
if(isnull(world.SetScores(player.ckey, "", CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.key]!")
|
||||
message_admins("Error! Failed to contact hub to clear scores for [player.key]!")
|
||||
SUBSYSTEM_DEF(medals)
|
||||
name = "Medals"
|
||||
flags = SS_NO_FIRE
|
||||
var/hub_enabled = FALSE
|
||||
|
||||
/datum/controller/subsystem/medals/Initialize(timeofday)
|
||||
if(CONFIG_GET(string/medal_hub_address) && CONFIG_GET(string/medal_hub_password))
|
||||
hub_enabled = TRUE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/medals/proc/UnlockMedal(medal, client/player)
|
||||
set waitfor = FALSE
|
||||
if(!medal || !hub_enabled)
|
||||
return
|
||||
if(isnull(world.SetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to award [medal] medal to [player.key]!")
|
||||
return
|
||||
to_chat(player, "<span class='greenannounce'><B>Achievement unlocked: [medal]!</B></span>")
|
||||
|
||||
|
||||
/datum/controller/subsystem/medals/proc/SetScore(score, client/player, increment, force)
|
||||
set waitfor = FALSE
|
||||
if(!score || !hub_enabled)
|
||||
return
|
||||
|
||||
var/list/oldscore = GetScore(score, player, TRUE)
|
||||
if(increment)
|
||||
if(!oldscore[score])
|
||||
oldscore[score] = 1
|
||||
else
|
||||
oldscore[score] = (text2num(oldscore[score]) + 1)
|
||||
else
|
||||
oldscore[score] = force
|
||||
|
||||
var/newscoreparam = list2params(oldscore)
|
||||
|
||||
if(isnull(world.SetScores(player.ckey, newscoreparam, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to set [score] score for [player.key]!")
|
||||
|
||||
/datum/controller/subsystem/medals/proc/GetScore(score, client/player, returnlist)
|
||||
if(!score || !hub_enabled)
|
||||
return
|
||||
|
||||
var/scoreget = world.GetScores(player.ckey, score, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
|
||||
if(isnull(scoreget))
|
||||
hub_enabled = FALSE
|
||||
log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to get score: [score] for [player.key]!")
|
||||
return
|
||||
. = params2list(scoreget)
|
||||
if(!returnlist)
|
||||
return .[score]
|
||||
|
||||
/datum/controller/subsystem/medals/proc/CheckMedal(medal, client/player)
|
||||
if(!medal || !hub_enabled)
|
||||
return
|
||||
|
||||
if(isnull(world.GetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player: [player.key]")
|
||||
message_admins("Error! Failed to contact hub to get [medal] medal for [player.key]!")
|
||||
return
|
||||
to_chat(player, "[medal] is unlocked")
|
||||
|
||||
/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player)
|
||||
if(!player || !medal || !hub_enabled)
|
||||
return
|
||||
var/result = world.ClearMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))
|
||||
switch(result)
|
||||
if(null)
|
||||
hub_enabled = FALSE
|
||||
log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.key]")
|
||||
message_admins("Error! Failed to contact hub to clear [medal] medal for [player.key]!")
|
||||
if(TRUE)
|
||||
message_admins("Medal: [medal] removed for [player.key]")
|
||||
if(FALSE)
|
||||
message_admins("Medal: [medal] was not found for [player.key]. Unable to clear.")
|
||||
|
||||
|
||||
/datum/controller/subsystem/medals/proc/ClearScore(client/player)
|
||||
if(isnull(world.SetScores(player.ckey, "", CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password))))
|
||||
log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.key]!")
|
||||
message_admins("Error! Failed to contact hub to clear scores for [player.key]!")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(mood)
|
||||
name = "Mood"
|
||||
flags = SS_NO_INIT | SS_BACKGROUND
|
||||
priority = 20
|
||||
PROCESSING_SUBSYSTEM_DEF(mood)
|
||||
name = "Mood"
|
||||
flags = SS_NO_INIT | SS_BACKGROUND
|
||||
priority = 20
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
SUBSYSTEM_DEF(nightshift)
|
||||
name = "Night Shift"
|
||||
wait = 600
|
||||
flags = SS_NO_TICK_CHECK
|
||||
|
||||
var/nightshift_active = FALSE
|
||||
var/nightshift_start_time = 702000 //7:30 PM, station time
|
||||
var/nightshift_end_time = 270000 //7:30 AM, station time
|
||||
var/nightshift_first_check = 30 SECONDS
|
||||
|
||||
var/high_security_mode = FALSE
|
||||
|
||||
/datum/controller/subsystem/nightshift/Initialize()
|
||||
if(!CONFIG_GET(flag/enable_night_shifts))
|
||||
can_fire = FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/nightshift/fire(resumed = FALSE)
|
||||
if(world.time - SSticker.round_start_time < nightshift_first_check)
|
||||
return
|
||||
check_nightshift()
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/announce(message)
|
||||
priority_announce(message, sound='sound/misc/notice2.ogg', sender_override="Automated Lighting System Announcement")
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/check_nightshift()
|
||||
var/emergency = GLOB.security_level >= SEC_LEVEL_RED
|
||||
var/announcing = TRUE
|
||||
var/time = STATION_TIME(FALSE)
|
||||
var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time)
|
||||
if(high_security_mode != emergency)
|
||||
high_security_mode = emergency
|
||||
if(night_time)
|
||||
announcing = FALSE
|
||||
if(!emergency)
|
||||
announce("Restoring night lighting configuration to normal operation.")
|
||||
else
|
||||
announce("Disabling night lighting: Station is in a state of emergency.")
|
||||
if(emergency)
|
||||
night_time = FALSE
|
||||
if(nightshift_active != night_time)
|
||||
update_nightshift(night_time, announcing)
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/update_nightshift(active, announce = TRUE)
|
||||
nightshift_active = active
|
||||
if(announce)
|
||||
if (active)
|
||||
announce("Good evening, crew. To reduce power consumption and stimulate the circadian rhythms of some species, all of the lights aboard the station have been dimmed for the night.")
|
||||
else
|
||||
announce("Good morning, crew. As it is now day time, all of the lights aboard the station have been restored to their former brightness.")
|
||||
for(var/A in GLOB.apcs_list)
|
||||
var/obj/machinery/power/apc/APC = A
|
||||
if (APC.area && (APC.area.type in GLOB.the_station_areas))
|
||||
APC.set_nightshift(active)
|
||||
CHECK_TICK
|
||||
SUBSYSTEM_DEF(nightshift)
|
||||
name = "Night Shift"
|
||||
wait = 600
|
||||
flags = SS_NO_TICK_CHECK
|
||||
|
||||
var/nightshift_active = FALSE
|
||||
var/nightshift_start_time = 702000 //7:30 PM, station time
|
||||
var/nightshift_end_time = 270000 //7:30 AM, station time
|
||||
var/nightshift_first_check = 30 SECONDS
|
||||
|
||||
var/high_security_mode = FALSE
|
||||
|
||||
/datum/controller/subsystem/nightshift/Initialize()
|
||||
if(!CONFIG_GET(flag/enable_night_shifts))
|
||||
can_fire = FALSE
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/nightshift/fire(resumed = FALSE)
|
||||
if(world.time - SSticker.round_start_time < nightshift_first_check)
|
||||
return
|
||||
check_nightshift()
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/announce(message)
|
||||
priority_announce(message, sound='sound/misc/notice2.ogg', sender_override="Automated Lighting System Announcement")
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/check_nightshift()
|
||||
var/emergency = GLOB.security_level >= SEC_LEVEL_RED
|
||||
var/announcing = TRUE
|
||||
var/time = STATION_TIME(FALSE)
|
||||
var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time)
|
||||
if(high_security_mode != emergency)
|
||||
high_security_mode = emergency
|
||||
if(night_time)
|
||||
announcing = FALSE
|
||||
if(!emergency)
|
||||
announce("Restoring night lighting configuration to normal operation.")
|
||||
else
|
||||
announce("Disabling night lighting: Station is in a state of emergency.")
|
||||
if(emergency)
|
||||
night_time = FALSE
|
||||
if(nightshift_active != night_time)
|
||||
update_nightshift(night_time, announcing)
|
||||
|
||||
/datum/controller/subsystem/nightshift/proc/update_nightshift(active, announce = TRUE)
|
||||
nightshift_active = active
|
||||
if(announce)
|
||||
if (active)
|
||||
announce("Good evening, crew. To reduce power consumption and stimulate the circadian rhythms of some species, all of the lights aboard the station have been dimmed for the night.")
|
||||
else
|
||||
announce("Good morning, crew. As it is now day time, all of the lights aboard the station have been restored to their former brightness.")
|
||||
for(var/A in GLOB.apcs_list)
|
||||
var/obj/machinery/power/apc/APC = A
|
||||
if (APC.area && (APC.area.type in GLOB.the_station_areas))
|
||||
APC.set_nightshift(active)
|
||||
CHECK_TICK
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
SUBSYSTEM_DEF(npcpool)
|
||||
name = "NPC Pool"
|
||||
flags = SS_KEEP_TIMING | SS_NO_INIT
|
||||
priority = FIRE_PRIORITY_NPC
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/npcpool/stat_entry()
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
..("NPCS:[activelist.len]")
|
||||
|
||||
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
|
||||
|
||||
if (!resumed)
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
src.currentrun = activelist.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while(currentrun.len)
|
||||
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
|
||||
if(!SA.ckey && !SA.notransform)
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_movement()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_action()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_speech()
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
SUBSYSTEM_DEF(npcpool)
|
||||
name = "NPC Pool"
|
||||
flags = SS_KEEP_TIMING | SS_NO_INIT
|
||||
priority = FIRE_PRIORITY_NPC
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/npcpool/stat_entry()
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
..("NPCS:[activelist.len]")
|
||||
|
||||
/datum/controller/subsystem/npcpool/fire(resumed = FALSE)
|
||||
|
||||
if (!resumed)
|
||||
var/list/activelist = GLOB.simple_animals[AI_ON]
|
||||
src.currentrun = activelist.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while(currentrun.len)
|
||||
var/mob/living/simple_animal/SA = currentrun[currentrun.len]
|
||||
--currentrun.len
|
||||
|
||||
if(!SA.ckey && !SA.notransform)
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_movement()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_action()
|
||||
if(SA.stat != DEAD)
|
||||
SA.handle_automated_speech()
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
@@ -8,7 +8,7 @@ SUBSYSTEM_DEF(pai)
|
||||
var/spam_delay = 100
|
||||
var/list/pai_card_list = list()
|
||||
|
||||
/datum/controller/subsystem/pai/Topic(href, href_list[])
|
||||
/datum/controller/subsystem/pai/Topic(href, href_list)
|
||||
if(href_list["download"])
|
||||
var/datum/paiCandidate/candidate = locate(href_list["candidate"]) in candidates
|
||||
var/obj/item/paicard/card = locate(href_list["device"]) in pai_card_list
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
SUBSYSTEM_DEF(pathfinder)
|
||||
name = "Pathfinder"
|
||||
init_order = INIT_ORDER_PATH
|
||||
flags = SS_NO_FIRE
|
||||
var/datum/flowcache/mobs
|
||||
var/datum/flowcache/circuits
|
||||
var/static/space_type_cache
|
||||
|
||||
/datum/controller/subsystem/pathfinder/Initialize()
|
||||
space_type_cache = typecacheof(/turf/open/space)
|
||||
mobs = new(10)
|
||||
circuits = new(3)
|
||||
return ..()
|
||||
|
||||
/datum/flowcache
|
||||
var/lcount
|
||||
var/run
|
||||
var/free
|
||||
var/list/flow
|
||||
|
||||
/datum/flowcache/New(var/n)
|
||||
. = ..()
|
||||
lcount = n
|
||||
run = 0
|
||||
free = 1
|
||||
flow = new/list(lcount)
|
||||
|
||||
/datum/flowcache/proc/getfree(atom/M)
|
||||
if(run < lcount)
|
||||
run += 1
|
||||
while(flow[free])
|
||||
CHECK_TICK
|
||||
free = (free % lcount) + 1
|
||||
var/t = addtimer(CALLBACK(src, /datum/flowcache.proc/toolong, free), 150, TIMER_STOPPABLE)
|
||||
flow[free] = t
|
||||
flow[t] = M
|
||||
return free
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/flowcache/proc/toolong(l)
|
||||
log_game("Pathfinder route took longer than 150 ticks, src bot [flow[flow[l]]]")
|
||||
found(l)
|
||||
|
||||
/datum/flowcache/proc/found(l)
|
||||
deltimer(flow[l])
|
||||
flow[l] = null
|
||||
run -= 1
|
||||
SUBSYSTEM_DEF(pathfinder)
|
||||
name = "Pathfinder"
|
||||
init_order = INIT_ORDER_PATH
|
||||
flags = SS_NO_FIRE
|
||||
var/datum/flowcache/mobs
|
||||
var/datum/flowcache/circuits
|
||||
var/static/space_type_cache
|
||||
|
||||
/datum/controller/subsystem/pathfinder/Initialize()
|
||||
space_type_cache = typecacheof(/turf/open/space)
|
||||
mobs = new(10)
|
||||
circuits = new(3)
|
||||
return ..()
|
||||
|
||||
/datum/flowcache
|
||||
var/lcount
|
||||
var/run
|
||||
var/free
|
||||
var/list/flow
|
||||
|
||||
/datum/flowcache/New(var/n)
|
||||
. = ..()
|
||||
lcount = n
|
||||
run = 0
|
||||
free = 1
|
||||
flow = new/list(lcount)
|
||||
|
||||
/datum/flowcache/proc/getfree(atom/M)
|
||||
if(run < lcount)
|
||||
run += 1
|
||||
while(flow[free])
|
||||
CHECK_TICK
|
||||
free = (free % lcount) + 1
|
||||
var/t = addtimer(CALLBACK(src, /datum/flowcache.proc/toolong, free), 150, TIMER_STOPPABLE)
|
||||
flow[free] = t
|
||||
flow[t] = M
|
||||
return free
|
||||
else
|
||||
return 0
|
||||
|
||||
/datum/flowcache/proc/toolong(l)
|
||||
log_game("Pathfinder route took longer than 150 ticks, src bot [flow[flow[l]]]")
|
||||
found(l)
|
||||
|
||||
/datum/flowcache/proc/found(l)
|
||||
deltimer(flow[l])
|
||||
flow[l] = null
|
||||
run -= 1
|
||||
|
||||
@@ -1,51 +1,51 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(networks)
|
||||
name = "Networks"
|
||||
priority = FIRE_PRIORITY_NETWORKS
|
||||
wait = 1
|
||||
stat_tag = "NET"
|
||||
flags = SS_KEEP_TIMING
|
||||
init_order = INIT_ORDER_NETWORKS
|
||||
var/datum/ntnet/station/station_network
|
||||
var/assignment_hardware_id = HID_RESTRICTED_END
|
||||
var/list/networks_by_id = list() //id = network
|
||||
var/list/interfaces_by_id = list() //hardware id = component interface
|
||||
var/resolve_collisions = TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/Initialize()
|
||||
station_network = new
|
||||
station_network.register_map_supremecy()
|
||||
. = ..()
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/register_network(datum/ntnet/network)
|
||||
if(!networks_by_id[network.network_id])
|
||||
networks_by_id[network.network_id] = network
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/unregister_network(datum/ntnet/network)
|
||||
networks_by_id -= network.network_id
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/register_interface(datum/component/ntnet_interface/D)
|
||||
if(!interfaces_by_id[D.hardware_id])
|
||||
interfaces_by_id[D.hardware_id] = D
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/unregister_interface(datum/component/ntnet_interface/D)
|
||||
interfaces_by_id -= D.hardware_id
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/get_next_HID()
|
||||
var/string = "[num2text(assignment_hardware_id++, 12)]"
|
||||
return make_address(string)
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/make_address(string)
|
||||
if(!string)
|
||||
return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null
|
||||
var/hex = md5(string)
|
||||
if(!hex)
|
||||
return //errored
|
||||
. = "[copytext(hex, 1, 9)]" //16 ^ 8 possibilities I think.
|
||||
if(interfaces_by_id[.])
|
||||
return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null
|
||||
PROCESSING_SUBSYSTEM_DEF(networks)
|
||||
name = "Networks"
|
||||
priority = FIRE_PRIORITY_NETWORKS
|
||||
wait = 1
|
||||
stat_tag = "NET"
|
||||
flags = SS_KEEP_TIMING
|
||||
init_order = INIT_ORDER_NETWORKS
|
||||
var/datum/ntnet/station/station_network
|
||||
var/assignment_hardware_id = HID_RESTRICTED_END
|
||||
var/list/networks_by_id = list() //id = network
|
||||
var/list/interfaces_by_id = list() //hardware id = component interface
|
||||
var/resolve_collisions = TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/Initialize()
|
||||
station_network = new
|
||||
station_network.register_map_supremecy()
|
||||
. = ..()
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/register_network(datum/ntnet/network)
|
||||
if(!networks_by_id[network.network_id])
|
||||
networks_by_id[network.network_id] = network
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/unregister_network(datum/ntnet/network)
|
||||
networks_by_id -= network.network_id
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/register_interface(datum/component/ntnet_interface/D)
|
||||
if(!interfaces_by_id[D.hardware_id])
|
||||
interfaces_by_id[D.hardware_id] = D
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/unregister_interface(datum/component/ntnet_interface/D)
|
||||
interfaces_by_id -= D.hardware_id
|
||||
return TRUE
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/get_next_HID()
|
||||
var/string = "[num2text(assignment_hardware_id++, 12)]"
|
||||
return make_address(string)
|
||||
|
||||
/datum/controller/subsystem/processing/networks/proc/make_address(string)
|
||||
if(!string)
|
||||
return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null
|
||||
var/hex = md5(string)
|
||||
if(!hex)
|
||||
return //errored
|
||||
. = "[copytext(hex, 1, 9)]" //16 ^ 8 possibilities I think.
|
||||
if(interfaces_by_id[.])
|
||||
return resolve_collisions? make_address("[num2text(rand(HID_RESTRICTED_END, 999999999), 12)]"):null
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(projectiles)
|
||||
name = "Projectiles"
|
||||
wait = 1
|
||||
stat_tag = "PP"
|
||||
flags = SS_NO_INIT|SS_TICKER
|
||||
var/global_max_tick_moves = 10
|
||||
var/global_pixel_speed = 2
|
||||
var/global_iterations_per_move = 16
|
||||
|
||||
/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed)
|
||||
global_pixel_speed = new_speed
|
||||
for(var/i in processing)
|
||||
var/obj/item/projectile/P = i
|
||||
if(istype(P)) //there's non projectiles on this too.
|
||||
P.set_pixel_speed(new_speed)
|
||||
|
||||
/datum/controller/subsystem/processing/projectiles/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if(NAMEOF(src, global_pixel_speed))
|
||||
set_pixel_speed(var_value)
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
PROCESSING_SUBSYSTEM_DEF(projectiles)
|
||||
name = "Projectiles"
|
||||
wait = 1
|
||||
stat_tag = "PP"
|
||||
flags = SS_NO_INIT|SS_TICKER
|
||||
var/global_max_tick_moves = 10
|
||||
var/global_pixel_speed = 2
|
||||
var/global_iterations_per_move = 16
|
||||
|
||||
/datum/controller/subsystem/processing/projectiles/proc/set_pixel_speed(new_speed)
|
||||
global_pixel_speed = new_speed
|
||||
for(var/i in processing)
|
||||
var/obj/item/projectile/P = i
|
||||
if(istype(P)) //there's non projectiles on this too.
|
||||
P.set_pixel_speed(new_speed)
|
||||
|
||||
/datum/controller/subsystem/processing/projectiles/vv_edit_var(var_name, var_value)
|
||||
switch(var_name)
|
||||
if(NAMEOF(src, global_pixel_speed))
|
||||
set_pixel_speed(var_value)
|
||||
return TRUE
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(wet_floors)
|
||||
name = "Wet floors"
|
||||
priority = FIRE_PRIORITY_WET_FLOORS
|
||||
wait = 10
|
||||
stat_tag = "WFP" //Used for logging
|
||||
var/temperature_coeff = 2
|
||||
var/time_ratio = 1.5 SECONDS
|
||||
PROCESSING_SUBSYSTEM_DEF(wet_floors)
|
||||
name = "Wet floors"
|
||||
priority = FIRE_PRIORITY_WET_FLOORS
|
||||
wait = 10
|
||||
stat_tag = "WFP" //Used for logging
|
||||
var/temperature_coeff = 2
|
||||
var/time_ratio = 1.5 SECONDS
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,59 +1,59 @@
|
||||
SUBSYSTEM_DEF(spacedrift)
|
||||
name = "Space Drift"
|
||||
priority = FIRE_PRIORITY_SPACEDRIFT
|
||||
wait = 5
|
||||
flags = SS_NO_INIT|SS_KEEP_TIMING
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/list/processing = list()
|
||||
|
||||
/datum/controller/subsystem/spacedrift/stat_entry()
|
||||
..("P:[processing.len]")
|
||||
|
||||
|
||||
/datum/controller/subsystem/spacedrift/fire(resumed = 0)
|
||||
if (!resumed)
|
||||
src.currentrun = processing.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while (currentrun.len)
|
||||
var/atom/movable/AM = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (!AM)
|
||||
processing -= AM
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if (AM.inertia_next_move > world.time)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if (!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0))
|
||||
AM.inertia_dir = 0
|
||||
|
||||
if (!AM.inertia_dir)
|
||||
AM.inertia_last_loc = null
|
||||
processing -= AM
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
var/old_dir = AM.dir
|
||||
var/old_loc = AM.loc
|
||||
AM.inertia_moving = TRUE
|
||||
step(AM, AM.inertia_dir)
|
||||
AM.inertia_moving = FALSE
|
||||
AM.inertia_next_move = world.time + AM.inertia_move_delay
|
||||
if (AM.loc == old_loc)
|
||||
AM.inertia_dir = 0
|
||||
|
||||
AM.setDir(old_dir)
|
||||
AM.inertia_last_loc = AM.loc
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
SUBSYSTEM_DEF(spacedrift)
|
||||
name = "Space Drift"
|
||||
priority = FIRE_PRIORITY_SPACEDRIFT
|
||||
wait = 5
|
||||
flags = SS_NO_INIT|SS_KEEP_TIMING
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/list/processing = list()
|
||||
|
||||
/datum/controller/subsystem/spacedrift/stat_entry()
|
||||
..("P:[processing.len]")
|
||||
|
||||
|
||||
/datum/controller/subsystem/spacedrift/fire(resumed = 0)
|
||||
if (!resumed)
|
||||
src.currentrun = processing.Copy()
|
||||
|
||||
//cache for sanic speed (lists are references anyways)
|
||||
var/list/currentrun = src.currentrun
|
||||
|
||||
while (currentrun.len)
|
||||
var/atom/movable/AM = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if (!AM)
|
||||
processing -= AM
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if (AM.inertia_next_move > world.time)
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
if (!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0))
|
||||
AM.inertia_dir = 0
|
||||
|
||||
if (!AM.inertia_dir)
|
||||
AM.inertia_last_loc = null
|
||||
processing -= AM
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
continue
|
||||
|
||||
var/old_dir = AM.dir
|
||||
var/old_loc = AM.loc
|
||||
AM.inertia_moving = TRUE
|
||||
step(AM, AM.inertia_dir)
|
||||
AM.inertia_moving = FALSE
|
||||
AM.inertia_next_move = world.time + AM.inertia_move_delay
|
||||
if (AM.loc == old_loc)
|
||||
AM.inertia_dir = 0
|
||||
|
||||
AM.setDir(old_dir)
|
||||
AM.inertia_last_loc = AM.loc
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
|
||||
+676
-676
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user