Merge branch 'master' into ghost-rad-hud

This commit is contained in:
Matt
2021-08-10 11:14:35 -07:00
committed by GitHub
593 changed files with 28984 additions and 43984 deletions
-968
View File
@@ -1,968 +0,0 @@
/datum/configuration
var/server_name = null // server name (for world name / status)
var/server_tag_line = null // server tagline (for showing on hub entry)
var/server_extra_features = null // server-specific extra features (for hub entry)
var/server_suffix = 0 // generate numeric suffix based on server port
var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit
var/nudge_script_path = "nudge.py" // where the nudge.py script is located
var/log_ooc = 0 // log OOC channel
var/log_access = 0 // log login/logout
var/log_say = 0 // log client say
var/log_admin = 0 // log admin actions
var/log_debug = 1 // log debug output
var/log_game = 0 // log game events
var/log_vote = 0 // log voting
var/log_whisper = 0 // log client whisper
var/log_emote = 0 // log emotes
var/log_attack = 0 // log attack messages
var/log_adminchat = 0 // log admin chat messages
var/log_adminwarn = 0 // log warnings admins get about bomb construction and such
var/log_pda = 0 // log pda messages
var/log_world_output = 0 // log world.log << messages
var/log_runtimes = 0 // logs world.log to a file
var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits
var/sql_enabled = 0 // for sql switching
var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour
var/pregame_timestart = 240 // Time it takes for the server to start the game
var/allow_vote_restart = 0 // allow votes to restart
var/allow_vote_mode = 0 // allow votes to change mode
var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default)
var/vote_period = 600 // length of voting period (deciseconds, default 1 minute)
var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called
var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote
var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi)
var/vote_no_dead = 0 // dead people can't vote (tbi)
// var/enable_authentication = 0 // goon authentication
var/del_new_on_log = 1 // qdel's new players if they log before they spawn in
var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard
var/traitor_scaling = 0 //if amount of traitors scales based on amount of players
var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other
var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke.
var/allow_Metadata = 0 // Metadata is supported.
var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1.
var/Ticklag = 0.5
var/socket_talk = 0 // use socket_talk to communicate with other processes
var/list/resource_urls = null
var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round.
var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round.
var/list/mode_names = list()
var/list/modes = list() // allowed modes
var/list/votable_modes = list() // votable modes
var/list/probabilities = list() // relative probability of each mode
var/humans_need_surnames = 0
var/allow_random_events = 0 // enables random events mid-round when set to 1
var/allow_ai = 1 // allow ai job
var/respawn = 0
var/guest_jobban = 1
var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting
var/usewhitelist = 0
var/mods_are_mentors = 0
var/load_jobs_from_txt = 0
var/automute_on = 0 //enables automuting/spam prevention
var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access.
var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized
var/medal_hub_address = null
var/medal_hub_password = null
var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors
var/assistantlimit = 0 //enables assistant limiting
var/assistantratio = 2 //how many assistants to security members
// The AFK subsystem will not be activated if any of the below config values are equal or less than 0
var/warn_afk_minimum = 0 // How long till you get a warning while being AFK
var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK
var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic)
var/auto_cryo_ssd_mins = 0
var/ssd_warning = 0
var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb
var/traitor_objectives_amount = 2
var/shadowling_max_age = 0
var/max_maint_drones = 5 //This many drones can spawn,
var/allow_drone_spawn = 1 //assuming the admin allow them to.
var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes.
var/usealienwhitelist = 0
var/limitalienplayers = 0
var/alien_to_human_ratio = 0.5
var/server
var/banappeals
var/wikiurl = "http://example.org"
var/forumurl = "http://example.org"
var/rulesurl = "http://example.org"
var/githuburl = "http://example.org"
var/donationsurl = "http://example.org"
var/repositoryurl = "http://example.org"
var/discordurl = "http://example.org"
var/discordforumurl = "http://example.org"
var/overflow_server_url
var/forbid_singulo_possession = 0
var/check_randomizer = 0
//game_options.txt configs
var/bones_can_break = 1
var/revival_pod_plants = 1
var/revival_cloning = 1
var/revival_brain_life = -1
var/auto_toggle_ooc_during_round = 0
var/shuttle_refuel_delay = 12000
//Used for modifying movement speed for mobs.
//Unversal modifiers
var/run_speed = 0
var/walk_speed = 0
//Mob specific modifiers. NOTE: These will affect different mob types in different ways
var/human_delay = 0
var/robot_delay = 0
var/monkey_delay = 0
var/alien_delay = 0
var/slime_delay = 0
var/animal_delay = 0
//IP Intel vars
var/ipintel_email
var/ipintel_rating_bad = 1
var/ipintel_save_good = 12
var/ipintel_save_bad = 1
var/ipintel_domain = "check.getipintel.net"
var/ipintel_maxplaytime = 0
var/ipintel_whitelist = 0
var/ipintel_detailsurl = "https://iphub.info/?ip="
var/forum_link_url
var/forum_playerinfo_url
var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database
var/use_exp_tracking = 0
var/use_exp_restrictions = 0
var/use_exp_restrictions_admin_bypass = 0
var/simultaneous_pm_warning_timeout = 100
var/assistant_maint = 0 //Do assistants get maint access?
var/gateway_delay = 6000
var/ghost_interaction = 0
var/comms_password = ""
var/default_laws = 0 //Controls what laws the AI spawns with.
var/const/minutes_to_ticks = 60 * 10
// Event settings
var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours
// If the first delay has a custom start time
// No custom time, no custom time, between 80 to 100 minutes respectively.
var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000))
// The lowest delay until next event
// 10, 30, 50 minutes respectively
var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000)
// The upper delay until next event
// 15, 45, 70 minutes respectively
var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000)
var/starlight = 0 // Whether space turfs have ambient light or not
var/allow_holidays = 0
var/player_overflow_cap = 0 //number of players before the server starts rerouting
var/list/overflow_whitelist = list() //whitelist for overflow
var/disable_away_missions = 0 // disable away missions
var/disable_space_ruins = 0 //disable space ruins
var/extra_space_ruin_levels_min = 4
var/extra_space_ruin_levels_max = 8
var/ooc_allowed = 1
var/looc_allowed = 1
var/dooc_allowed = 1
var/dsay_allowed = 1
var/disable_lobby_music = 0 // Disables the lobby music
var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing.
var/max_loadout_points = 5 // How many points can be spent on extra items in character setup
var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC
var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it
var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default
// StonedMC
var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling
// Highpop tickrates
var/base_mc_tick_rate = 1
var/high_pop_mc_tick_rate = 1.1
var/high_pop_mc_mode_amount = 65
var/disable_high_pop_mc_mode_amount = 60
// Nightshift
var/randomize_shift_time = FALSE
var/enable_night_shifts = FALSE
// Developer
var/developer_express_start = 0
// Automatic localhost admin disable
var/disable_localhost_admin = 0
//Start now warning
var/start_now_confirmation = 0
// Lavaland
var/lavaland_budget = 60
//cube monkey limit
var/cubemonkeycap = 20
// Makes gamemodes respect player limits
var/enable_gamemode_player_limit = 0
/// BYOND account age limit for notifcations of new accounts (Any accounts older than this value will not send notifications on first join)
var/byond_account_age_threshold = 7
/// Are discord webhooks enabled?
var/discord_webhooks_enabled = FALSE
/// Role ID to be pinged for administrative events
var/discord_admin_role_id = null // Intentional null usage
/// Webhook URLs for the main public webhook
var/list/discord_main_webhook_urls = list()
/// Webhook URLs for the admin webhook
var/list/discord_admin_webhook_urls = list()
/// Webhook URLs for the mentor webhook
var/list/discord_mentor_webhook_urls = list()
/// Do we want to forward all adminhelps to the discord or just ahelps when admins are offline.
/// (This does not mean all ahelps are pinged, only ahelps sent when staff are offline get the ping, regardless of this setting)
var/discord_forward_all_ahelps = FALSE
/// URL for the CentCom Ban DB API
var/centcom_ban_db_url = null
/// Timeout (seconds) for async SQL queries
var/async_sql_query_timeout = 10 SECONDS
/// Limit of how many SQL threads can run at once
var/rust_sql_thread_limit = 50
/// Max amount of CIDs that one ckey can have attached to them before they trip a warning
var/max_client_cid_history = 3
/// Enable auto profiler of rounds
var/auto_profile = FALSE
// Enable map voting
var/map_voting_enabled = FALSE
// 2FA auth host
var/_2fa_auth_host = null
/datum/configuration/New()
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = T
if(initial(M.config_tag))
if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once
src.modes += initial(M.config_tag)
src.mode_names[initial(M.config_tag)] = initial(M.name)
src.probabilities[initial(M.config_tag)] = initial(M.probability)
if(initial(M.votable))
src.votable_modes += initial(M.config_tag)
src.votable_modes += "secret"
/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='boldannounce'>Config reload blocked: Advanced ProcCall detected.</span>")
message_admins("[key_name(usr)] attempted to reload configuration via advanced proc-call")
log_admin("[key_name(usr)] attempted to reload configuration via advanced proc-call")
return
var/list/Lines = file2list(filename)
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/name = null
var/value = null
if(pos)
name = lowertext(copytext(t, 1, pos))
value = copytext(t, pos + 1)
else
name = lowertext(t)
if(!name)
continue
if(type == "config")
switch(name)
if("resource_urls")
config.resource_urls = splittext(value, " ")
if("admin_legacy_system")
config.admin_legacy_system = 1
if("ban_legacy_system")
config.ban_legacy_system = 1
if("use_age_restriction_for_jobs")
config.use_age_restriction_for_jobs = 1
if("use_age_restriction_for_antags")
config.use_age_restriction_for_antags = 1
if("use_exp_tracking")
config.use_exp_tracking = 1
if("use_exp_restrictions")
config.use_exp_restrictions = 1
if("use_exp_restrictions_admin_bypass")
config.use_exp_restrictions_admin_bypass = 1
if("jobs_have_minimal_access")
config.jobs_have_minimal_access = 1
if("shadowling_max_age")
config.shadowling_max_age = text2num(value)
if("warn_afk_minimum")
config.warn_afk_minimum = text2num(value)
if("auto_cryo_afk")
config.auto_cryo_afk = text2num(value)
if("auto_despawn_afk")
config.auto_despawn_afk = text2num(value)
if("auto_cryo_ssd_mins")
config.auto_cryo_ssd_mins = text2num(value)
if("ssd_warning")
config.ssd_warning = 1
if("list_afk_minimum")
config.list_afk_minimum = text2num(value)
if("ipintel_email")
if(value != "ch@nge.me")
config.ipintel_email = value
if("ipintel_rating_bad")
config.ipintel_rating_bad = text2num(value)
if("ipintel_domain")
config.ipintel_domain = value
if("ipintel_save_good")
config.ipintel_save_good = text2num(value)
if("ipintel_save_bad")
config.ipintel_save_bad = text2num(value)
if("ipintel_maxplaytime")
config.ipintel_maxplaytime = text2num(value)
if("ipintel_whitelist")
config.ipintel_whitelist = 1
if("ipintel_detailsurl")
config.ipintel_detailsurl = value
if("forum_link_url")
config.forum_link_url = value
if("forum_playerinfo_url")
config.forum_playerinfo_url = value
if("log_ooc")
config.log_ooc = 1
if("log_access")
config.log_access = 1
if("log_say")
config.log_say = 1
if("log_admin")
config.log_admin = 1
if("log_debug")
config.log_debug = 1
if("log_game")
config.log_game = 1
if("log_vote")
config.log_vote = 1
if("log_whisper")
config.log_whisper = 1
if("log_attack")
config.log_attack = 1
if("log_emote")
config.log_emote = 1
if("log_adminchat")
config.log_adminchat = 1
if("log_adminwarn")
config.log_adminwarn = 1
if("log_pda")
config.log_pda = 1
if("log_world_output")
config.log_world_output = 1
if("log_hrefs")
config.log_hrefs = 1
if("log_runtime")
config.log_runtimes = 1
if("mentors")
config.mods_are_mentors = 1
if("allow_admin_ooccolor")
config.allow_admin_ooccolor = 1
if("pregame_timestart")
config.pregame_timestart = text2num(value)
if("allow_vote_restart")
config.allow_vote_restart = 1
if("allow_vote_mode")
config.allow_vote_mode = 1
if("no_dead_vote")
config.vote_no_dead = 1
if("vote_autotransfer_initial")
config.vote_autotransfer_initial = text2num(value)
if("vote_autotransfer_interval")
config.vote_autotransfer_interval = text2num(value)
if("default_no_vote")
config.vote_no_default = 1
if("vote_delay")
config.vote_delay = text2num(value)
if("vote_period")
config.vote_period = text2num(value)
if("allow_ai")
config.allow_ai = 1
// if("authentication")
// config.enable_authentication = 1
if("norespawn")
config.respawn = 0
if("servername")
config.server_name = value
if("server_tag_line")
config.server_tag_line = value
if("server_extra_features")
config.server_extra_features = value
if("serversuffix")
config.server_suffix = 1
if("minimum_client_build")
config.minimum_client_build = text2num(value)
if("nudge_script_path")
config.nudge_script_path = value
if("server")
config.server = value
if("banappeals")
config.banappeals = value
if("wikiurl")
config.wikiurl = value
if("forumurl")
config.forumurl = value
if("rulesurl")
config.rulesurl = value
if("githuburl")
config.githuburl = value
if("discordurl")
config.discordurl = value
if("discordforumurl")
config.discordforumurl = value
if("donationsurl")
config.donationsurl = value
if("repositoryurl")
config.repositoryurl = value
if("guest_jobban")
config.guest_jobban = 1
if("guest_ban")
GLOB.guests_allowed = 0
if("panic_bunker_threshold")
config.panic_bunker_threshold = text2num(value)
if("usewhitelist")
config.usewhitelist = 1
if("feature_object_spell_system")
config.feature_object_spell_system = 1
if("allow_metadata")
config.allow_Metadata = 1
if("traitor_scaling")
config.traitor_scaling = 1
if("protect_roles_from_antagonist")
config.protect_roles_from_antagonist = 1
if("probability")
var/prob_pos = findtext(value, " ")
var/prob_name = null
var/prob_value = null
if(prob_pos)
prob_name = lowertext(copytext(value, 1, prob_pos))
prob_value = copytext(value, prob_pos + 1)
if(prob_name in config.modes)
config.probabilities[prob_name] = text2num(prob_value)
else
log_config("Unknown game mode probability configuration definition: [prob_name].")
else
log_config("Incorrect probability configuration definition: [prob_name] [prob_value].")
if("allow_random_events")
config.allow_random_events = 1
if("load_jobs_from_txt")
load_jobs_from_txt = 1
if("forbid_singulo_possession")
forbid_singulo_possession = 1
if("check_randomizer")
check_randomizer = 1
if("popup_admin_pm")
config.popup_admin_pm = 1
if("allow_holidays")
config.allow_holidays = 1
if("ticklag")
Ticklag = text2num(value)
if("socket_talk")
socket_talk = text2num(value)
if("allow_antag_hud")
config.antag_hud_allowed = 1
if("antag_hud_restricted")
config.antag_hud_restricted = 1
if("humans_need_surnames")
humans_need_surnames = 1
if("automute_on")
automute_on = 1
if("usealienwhitelist")
usealienwhitelist = 1
if("alien_player_ratio")
limitalienplayers = 1
alien_to_human_ratio = text2num(value)
if("assistant_maint")
config.assistant_maint = 1
if("gateway_delay")
config.gateway_delay = text2num(value)
if("continuous_rounds")
config.continuous_rounds = 1
if("ghost_interaction")
config.ghost_interaction = 1
if("comms_password")
config.comms_password = value
if("python_path")
if(value)
GLOB.python_path = value
else
if(world.system_type == UNIX)
GLOB.python_path = "/usr/bin/env python2"
else //probably windows, if not this should work anyway
GLOB.python_path = "pythonw"
if("assistant_limit")
config.assistantlimit = 1
if("assistant_ratio")
config.assistantratio = text2num(value)
if("allow_drone_spawn")
config.allow_drone_spawn = text2num(value)
if("drone_build_time")
config.drone_build_time = text2num(value)
if("max_maint_drones")
config.max_maint_drones = text2num(value)
if("expected_round_length")
config.expected_round_length = text2num(value) MINUTES
if("event_custom_start_mundane")
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_custom_start_moderate")
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_custom_start_major")
var/values = text2numlist(value, ";")
config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = values[1] MINUTES, "upper" = values[2] MINUTES)
if("event_delay_lower")
var/values = text2numlist(value, ";")
config.event_delay_lower[EVENT_LEVEL_MUNDANE] = values[1] MINUTES
config.event_delay_lower[EVENT_LEVEL_MODERATE] = values[2] MINUTES
config.event_delay_lower[EVENT_LEVEL_MAJOR] = values[3] MINUTES
if("event_delay_upper")
var/values = text2numlist(value, ";")
config.event_delay_upper[EVENT_LEVEL_MUNDANE] = values[1] MINUTES
config.event_delay_upper[EVENT_LEVEL_MODERATE] = values[2] MINUTES
config.event_delay_upper[EVENT_LEVEL_MAJOR] = values[3] MINUTES
if("starlight")
config.starlight = 1
if("player_reroute_cap")
var/vvalue = text2num(value)
config.player_overflow_cap = vvalue >= 0 ? vvalue : 0
if("overflow_server_url")
config.overflow_server_url = value
if("disable_away_missions")
config.disable_away_missions = 1
if("disable_space_ruins")
config.disable_space_ruins = 1
if("disable_lobby_music")
config.disable_lobby_music = 1
if("disable_cid_warn_popup")
config.disable_cid_warn_popup = 1
if("extra_space_ruin_levels_min")
var/vvalue = text2num(value)
config.extra_space_ruin_levels_min = max(vvalue, 0)
if("extra_space_ruin_levels_max")
var/vvalue = text2num(value)
config.extra_space_ruin_levels_max = max(vvalue, 0)
if("max_loadout_points")
config.max_loadout_points = text2num(value)
if("round_abandon_penalty_period")
config.round_abandon_penalty_period = text2num(value) MINUTES
if("medal_hub_address")
config.medal_hub_address = value
if("medal_hub_password")
config.medal_hub_password = value
if("disable_ooc_emoji")
config.disable_ooc_emoji = 1
if("shutdown_on_reboot")
config.shutdown_on_reboot = 1
if("shutdown_shell_command")
GLOB.shutdown_shell_command = value
if("disable_karma")
config.disable_karma = 1
if("start_now_confirmation")
config.start_now_confirmation = 1
if("tick_limit_mc_init")
config.tick_limit_mc_init = text2num(value)
if("base_mc_tick_rate")
config.base_mc_tick_rate = text2num(value)
if("high_pop_mc_tick_rate")
config.high_pop_mc_tick_rate = text2num(value)
if("high_pop_mc_mode_amount")
config.high_pop_mc_mode_amount = text2num(value)
if("disable_high_pop_mc_mode_amount")
config.disable_high_pop_mc_mode_amount = text2num(value)
if("developer_express_start")
config.developer_express_start = 1
if("disable_localhost_admin")
config.disable_localhost_admin = 1
if("enable_gamemode_player_limit")
config.enable_gamemode_player_limit = 1
if("byond_account_age_threshold")
config.byond_account_age_threshold = text2num(value)
// Discord stuff
if("enable_discord_webhooks")
discord_webhooks_enabled = TRUE
if("discord_webhooks_admin_role_id")
discord_admin_role_id = "[value]" // This MUST be a string because BYOND doesnt like massive integers
if("discord_webhooks_main_url")
discord_main_webhook_urls = splittext(value, "|")
if("discord_webhooks_admin_url")
discord_admin_webhook_urls = splittext(value, "|")
if("discord_webhooks_mentor_url")
discord_mentor_webhook_urls = splittext(value, "|")
if("discord_forward_all_ahelps")
discord_forward_all_ahelps = TRUE
// End discord stuff
if("centcom_ban_db_url")
centcom_ban_db_url = value
if("max_client_cid_history")
max_client_cid_history = text2num(value)
if("enable_auto_profiler")
auto_profile = TRUE
if("enable_map_voting")
map_voting_enabled = TRUE
if("2fa_host")
_2fa_auth_host = value
else
log_config("Unknown setting in configuration: '[name]'")
else if(type == "game_options")
value = text2num(value)
switch(name)
if("revival_pod_plants")
config.revival_pod_plants = value
if("revival_cloning")
config.revival_cloning = value
if("revival_brain_life")
config.revival_brain_life = value
if("auto_toggle_ooc_during_round")
config.auto_toggle_ooc_during_round = 1
if("run_speed")
config.run_speed = value
if("walk_speed")
config.walk_speed = value
if("human_delay")
config.human_delay = value
if("robot_delay")
config.robot_delay = value
if("monkey_delay")
config.monkey_delay = value
if("alien_delay")
config.alien_delay = value
if("slime_delay")
config.slime_delay = value
if("animal_delay")
config.animal_delay = value
if("bones_can_break")
config.bones_can_break = value
if("shuttle_refuel_delay")
config.shuttle_refuel_delay = text2num(value)
if("traitor_objectives_amount")
config.traitor_objectives_amount = text2num(value)
if("reactionary_explosions")
config.reactionary_explosions = 1
if("bombcap")
var/BombCap = text2num(value)
if(!BombCap)
continue
if(BombCap < 4)
BombCap = 4
if(BombCap > 128)
BombCap = 128
GLOB.max_ex_devastation_range = round(BombCap/4)
GLOB.max_ex_heavy_range = round(BombCap/2)
GLOB.max_ex_light_range = BombCap
GLOB.max_ex_flash_range = BombCap
GLOB.max_ex_flame_range = BombCap
if("default_laws")
config.default_laws = text2num(value)
if("randomize_shift_time")
config.randomize_shift_time = TRUE
if("enable_night_shifts")
config.enable_night_shifts = TRUE
if("lavaland_budget")
config.lavaland_budget = text2num(value)
if("cubemonkey_cap")
config.cubemonkeycap = text2num(value)
else
log_config("Unknown setting in configuration: '[name]'")
/datum/configuration/proc/loadsql(filename) // -- TLE
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='boldannounce'>SQL configuration reload blocked: Advanced ProcCall detected.</span>")
message_admins("[key_name(usr)] attempted to reload SQL configuration via advanced proc-call")
log_admin("[key_name(usr)] attempted to reload SQL configuration via advanced proc-call")
return
var/list/Lines = file2list(filename)
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/name = null
var/value = null
if(pos)
name = lowertext(copytext(t, 1, pos))
value = copytext(t, pos + 1)
else
name = lowertext(t)
if(!name)
continue
switch(name)
if("sql_enabled")
config.sql_enabled = 1
if("address")
sqladdress = value
if("port")
sqlport = value
if("feedback_database")
sqlfdbkdb = value
if("feedback_login")
sqlfdbklogin = value
if("feedback_password")
sqlfdbkpass = value
if("feedback_tableprefix")
sqlfdbktableprefix = value
if("db_version")
sql_version = text2num(value)
if("async_query_timeout")
async_sql_query_timeout = text2num(value)
if("rust_sql_thread_limit")
config.rust_sql_thread_limit = text2num(value)
else
log_config("Unknown setting in configuration: '[name]'")
/datum/configuration/proc/loadoverflowwhitelist(filename)
var/list/Lines = file2list(filename)
for(var/t in Lines)
if(!t) continue
t = trim(t)
if(length(t) == 0)
continue
else if(copytext(t, 1, 2) == "#")
continue
config.overflow_whitelist += t
/datum/configuration/proc/pick_mode(mode_name)
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = T
if(initial(M.config_tag) && initial(M.config_tag) == mode_name)
return new T()
return new /datum/game_mode/extended()
/datum/configuration/proc/get_runnable_modes()
var/list/datum/game_mode/runnable_modes = new
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = new T()
// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]")
if(!(M.config_tag in modes))
qdel(M)
continue
if(probabilities[M.config_tag]<=0)
qdel(M)
continue
if(M.can_start())
runnable_modes[M] = probabilities[M.config_tag]
// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]")
return runnable_modes
/datum/configuration/proc/load_rank_colour_map()
var/list/lines = file2list("config/rank_colours.txt")
for(var/line in lines)
// Skip newlines
if(!length(line))
continue
// Skip comments
if(line[1] == "#")
continue
//Split the line at every " - "
var/list/split_holder = splittext(line, " - ")
if(!length(split_holder))
continue
// Rank is before the " - "
var/rank = split_holder[1]
if(!rank)
continue
// Color is after the " - "
var/colour = ""
if(length(split_holder) >= 2)
colour = split_holder[2]
if(rank && colour)
GLOB.rank_colour_map[rank] = colour
else
stack_trace("Invalid colour for rank '[rank]' in config/rank_colours.txt")
@@ -0,0 +1,40 @@
// Config protection states
#define PROTECTION_PRIVATE "PRIVATE"
#define PROTECTION_READONLY "READONLY"
#define PROTECTION_NONE "NONE"
/// Wrapper to not overwrite a variable if a list key doesnt exist. Auto casts to bools.
#define CONFIG_LOAD_BOOL(target, input) \
if(!isnull(input)) {\
target = ((input == 1) ? TRUE : FALSE)\
}
/// Wrapper to not overwrite a variable if a list key doesnt exist. Auto casts to number.
#define CONFIG_LOAD_NUM(target, input) \
if(!isnull(input)) {\
target = text2num(input)\
}
/// Wrapper to not overwrite a variable if a list key doesnt exist. Auto casts to number, and accepts a macro argument for number maths (ds to min for example)
#define CONFIG_LOAD_NUM_MULT(target, input, multiplier) \
if(!isnull(input)) {\
target = text2num(input) multiplier\
}
/// Wrapper to not overwrite a variable if a list key doesnt exist. Auto casts to string.
#define CONFIG_LOAD_STR(target, input) \
if(!isnull(input)) {\
target = "[input]"\
}
/// Wrapper to not overwrite a variable if a list key doesnt exist. No casting done.
#define CONFIG_LOAD_RAW(target, input) \
if(!isnull(input)) {\
target = input\
}
/// Wrapper to not overwrite a variable if a list key doesnt exist. Ensures target is a list.
#define CONFIG_LOAD_LIST(target, input) \
if(islist(input)) {\
target = input\
}
@@ -0,0 +1,142 @@
// Paradise SS13 Configuration System
// Refactored to use config sections as part of a single TOML file, since thats much better to deal with
/// Global configuration datum holder for all the config sections
GLOBAL_DATUM_INIT(configuration, /datum/server_configuration, new())
/// Represents a base configuration datum. Has everything else bundled into it
/datum/server_configuration
/// Holder for the admin configuration datum
var/datum/configuration_section/admin_configuration/admin
/// Holder for the AFK configuration datum
var/datum/configuration_section/afk_configuration/afk
/// Holder for the custom sprites configuration datum
var/datum/configuration_section/custom_sprites_configuration/custom_sprites
/// Holder for the DB configuration datum
var/datum/configuration_section/database_configuration/database
/// Holder for the Discord configuration datum
var/datum/configuration_section/discord_configuration/discord
/// Holder for the Event configuration datum
var/datum/configuration_section/event_configuration/event
/// Holder for the gamemode configuration datum
var/datum/configuration_section/gamemode_configuration/gamemode
/// Holder for the gateway configuration datum
var/datum/configuration_section/gateway_configuration/gateway
/// Holder for the general configuration datum
var/datum/configuration_section/general_configuration/general
/// Holder for the IPIntel configuration datum
var/datum/configuration_section/ipintel_configuration/ipintel
/// Holder for the job configuration datum
var/datum/configuration_section/job_configuration/jobs
/// Holder for the logging configuration datum
var/datum/configuration_section/logging_configuration/logging
/// Holder for the MC configuration datum
var/datum/configuration_section/mc_configuration/mc
/// Holder for the MC configuration datum
var/datum/configuration_section/movement_configuration/movement
/// Holder for the overflow configuration datum
var/datum/configuration_section/overflow_configuration/overflow
/// Holder for the ruins configuration datum
var/datum/configuration_section/ruin_configuration/ruins
/// Holder for the system configuration datum
var/datum/configuration_section/system_configuration/system
/// Holder for the URL configuration datum
var/datum/configuration_section/url_configuration/url
/// Holder for the voting configuration datum
var/datum/configuration_section/vote_configuration/vote
/datum/server_configuration/Destroy(force)
SHOULD_CALL_PARENT(FALSE)
// This is going to stay existing. I dont care.
return QDEL_HINT_LETMELIVE
/datum/server_configuration/CanProcCall(procname)
return FALSE // No thanks
/datum/server_configuration/proc/load_configuration()
var/start = start_watch() // Time tracking
// Initialize all our holders
admin = new()
afk = new()
custom_sprites = new()
database = new()
discord = new()
event = new()
gamemode = new()
gateway = new()
general = new()
ipintel = new()
jobs = new()
logging = new()
mc = new()
movement = new()
overflow = new()
ruins = new()
system = new()
url = new()
vote = new()
// Load our stuff up
var/config_file = "config/config.toml"
if(!fexists(config_file))
config_file = "config/example/config.toml" // Fallback to example if user hasnt setup config properly
var/raw_json = rustg_toml2json(config_file)
var/list/raw_config_data = json_decode(raw_json)
// Now pass through all our stuff
admin.load_data(raw_config_data["admin_configuration"])
afk.load_data(raw_config_data["afk_configuration"])
custom_sprites.load_data(raw_config_data["custom_sprites_configuration"])
database.load_data(raw_config_data["database_configuration"])
discord.load_data(raw_config_data["discord_configuration"])
event.load_data(raw_config_data["event_configuration"])
gamemode.load_data(raw_config_data["gamemode_configuration"])
gateway.load_data(raw_config_data["gateway_configuration"])
general.load_data(raw_config_data["general_configuration"])
ipintel.load_data(raw_config_data["ipintel_configuration"])
jobs.load_data(raw_config_data["job_configuration"])
logging.load_data(raw_config_data["logging_configuration"])
mc.load_data(raw_config_data["mc_configuration"])
movement.load_data(raw_config_data["movement_configuration"])
overflow.load_data(raw_config_data["overflow_configuration"])
ruins.load_data(raw_config_data["ruin_configuration"])
system.load_data(raw_config_data["system_configuration"])
url.load_data(raw_config_data["url_configuration"])
vote.load_data(raw_config_data["voting_configuration"])
// And report the load
DIRECT_OUTPUT(world.log, "Config loaded in [stop_watch(start)]s")
/datum/configuration_section
/// See __config_defines.dm
var/protection_state = PROTECTION_NONE
/datum/configuration_section/proc/load_data(list/data)
CRASH("load() not overriden for [type]!")
// Maximum protection
/datum/configuration_section/can_vv_get(var_name)
if(protection_state == PROTECTION_PRIVATE)
return FALSE
return ..()
/datum/configuration_section/vv_edit_var(var_name, var_value)
if(protection_state in list(PROTECTION_PRIVATE, PROTECTION_READONLY))
return FALSE
return ..()
/datum/configuration_section/vv_get_var(var_name)
if(protection_state == PROTECTION_PRIVATE)
return FALSE
return ..()
/datum/configuration_section/Destroy(force)
SHOULD_CALL_PARENT(FALSE)
// This is going to stay existing. I dont care.
return QDEL_HINT_LETMELIVE
/datum/configuration_section/CanProcCall(procname)
return FALSE // No thanks
@@ -0,0 +1,43 @@
/// Config holder for all admin related things
/datum/configuration_section/admin_configuration
protection_state = PROTECTION_READONLY // Dont even think about it
/// Do we want to load admins from the database?
var/use_database_admins = FALSE
/// Do we want to auto enable admin rights if you connect from localhost?
var/enable_localhost_autoadmin = TRUE
/// Do we want to allow admins to set their own OOC colour?
var/allow_admin_ooc_colour = TRUE
/// Assoc list of admin ranks and their stuff. key: rank name string | value: list of rights
var/list/rank_rights_map = list()
/// Assoc list of admin ckeys and their ranks. key: ckey | value: rank name
var/list/ckey_rank_map = list()
/// Assoc list of admin ranks and their colours. key: rank | value: rank colour
var/list/rank_colour_map = list()
/datum/configuration_section/admin_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(use_database_admins, data["use_database_admins"])
CONFIG_LOAD_BOOL(enable_localhost_autoadmin, data["enable_localhost_autoadmin"])
CONFIG_LOAD_BOOL(allow_admin_ooc_colour, data["allow_admin_ooc_colour"])
// Load admin rank tokens
if(islist(data["admin_ranks"]))
rank_rights_map.Cut()
for(var/list/kvset in data["admin_ranks"])
rank_rights_map[kvset["name"]] = kvset["rights"]
// Load admin assignments
if(islist(data["admin_assignments"]))
ckey_rank_map.Cut()
for(var/list/kvset in data["admin_assignments"])
ckey_rank_map[kvset["ckey"]] = kvset["rank"]
// Load admin colours
if(islist(data["admin_rank_colour_map"]))
rank_colour_map.Cut()
for(var/list/kvset in data["admin_rank_colour_map"])
rank_colour_map[kvset["name"]] = kvset["colour"]
// For the person who asks "Why not put admin datum generation in this step?", well I will tell you why
// Admins can be reloaded at runtime when DB edits are made and such, and I dont want an entire config reload to be part of this
// Separation makes sense. That and in prod we use the DB anyways.
@@ -0,0 +1,17 @@
/// Config holder for all AFK related things
/datum/configuration_section/afk_configuration
/// Minutes before someone gets an AFK warning
var/warning_minutes = 0
/// Minutes before someone is auto moved to cryo
var/auto_cryo_minutes = 0
/// Minutes before someone is auto despawned
var/auto_despawn_minutes = 0
/// Time before SSD people are auto cryo'd
var/ssd_auto_cryo_minutes = 0
/datum/configuration_section/afk_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_NUM(warning_minutes, data["afk_warning_minutes"])
CONFIG_LOAD_NUM(auto_cryo_minutes, data["afk_auto_cryo_minutes"])
CONFIG_LOAD_NUM(auto_despawn_minutes, data["afk_auto_despawn_minutes"])
CONFIG_LOAD_NUM(ssd_auto_cryo_minutes, data["ssd_auto_cryo_minutes"])
@@ -0,0 +1,25 @@
/// Config holder for all things regarding custom sprites
/datum/configuration_section/custom_sprites_configuration
/// List of ckeys that have custom cyborg skins
var/list/cyborg_ckeys = list()
/// List of ckeys that have custom AI core skins
var/list/ai_core_ckeys = list()
/// List of ckeys that have custom AI hologram skins
var/list/ai_hologram_ckeys = list()
/// List of ckeys that have custom pAI holoforms
var/list/pai_holoform_ckeys = list()
/// Assoc of ckeys that have custom pAI screens. Key: ckey | value: list of icon states
var/list/ipc_screen_map = list()
/datum/configuration_section/custom_sprites_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_LIST(cyborg_ckeys, data["cyborgs"])
CONFIG_LOAD_LIST(ai_core_ckeys, data["ai_core"])
CONFIG_LOAD_LIST(ai_hologram_ckeys, data["ai_hologram"])
CONFIG_LOAD_LIST(pai_holoform_ckeys, data["pai_holoform"])
// Load the ipc screens
if(islist(data["ipc_screens"]))
ipc_screen_map.Cut()
for(var/kvp in data["ipc_screens"])
ipc_screen_map[kvp["ckey"]] = kvp["screens"]
@@ -0,0 +1,43 @@
/// Config holder for all database related things
/datum/configuration_section/database_configuration
protection_state = PROTECTION_PRIVATE // NO! BAD!
/// SQL enabled or not
var/enabled = FALSE
/// What SQL version are we on
var/version = 0
/// Address of the SQL server
var/address = "127.0.0.1"
/// Port of the SQL server
var/port = 3306
/// SQL usename
var/username = "root"
/// SQL password
var/password = "root" // Dont do this in prod. Please......
/// Database name
var/db = "paradise_gamedb"
/// Time in seconds for async queries to time out
var/async_query_timeout = 10
/// Thread limit for async queries
var/async_thread_limit = 50
/datum/configuration_section/database_configuration/load_data(list/data)
// UNIT TESTS ARE DEFINED - USE CUSTOM CI VALUES
#ifdef UNIT_TESTS
enabled = TRUE
// This needs to happen in the CI environment to ensure the example SQL version gets updated.
CONFIG_LOAD_NUM(version, data["sql_version"])
#else
// Load the normal config. Were not in CI mode
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enabled, data["sql_enabled"])
CONFIG_LOAD_NUM(version, data["sql_version"])
CONFIG_LOAD_STR(address, data["sql_address"])
CONFIG_LOAD_NUM(port, data["sql_port"])
CONFIG_LOAD_STR(username, data["sql_username"])
CONFIG_LOAD_STR(password, data["sql_password"])
CONFIG_LOAD_STR(db, data["sql_database"])
CONFIG_LOAD_NUM(async_query_timeout, data["async_query_timeout"])
CONFIG_LOAD_NUM(async_thread_limit, data["async_thread_limit"])
#endif
@@ -0,0 +1,26 @@
/// Config holder for all things relating to discord webhooks
/datum/configuration_section/discord_configuration
protection_state = PROTECTION_PRIVATE // No hook reading
/// Are webhooks enabled at all
var/webhooks_enabled = FALSE
/// Do we want to forward all ahelps or just ones sent with no active admins
var/forward_all_ahelps = TRUE
/// Admin role to ping if no admins are online. Disables if empty string
var/admin_role_id = ""
/// List of all URLs for the main webhooks
var/list/main_webhook_urls = list()
/// List of all URLs for the admin webhooks
var/list/mentor_webhook_urls = list()
/// List of all URLs for the mentor webhooks
var/list/admin_webhook_urls = list()
/datum/configuration_section/discord_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(webhooks_enabled, data["enable_discord_webhooks"])
CONFIG_LOAD_BOOL(forward_all_ahelps, data["forward_all_ahelps"])
CONFIG_LOAD_STR(admin_role_id, data["admin_role_id"])
CONFIG_LOAD_LIST(main_webhook_urls, data["main_webhook_urls"])
CONFIG_LOAD_LIST(mentor_webhook_urls, data["mentor_webhook_urls"])
CONFIG_LOAD_LIST(admin_webhook_urls, data["admin_webhook_urls"])
@@ -0,0 +1,61 @@
/// Config holder for all stuff relating to ingame random events
/datum/configuration_section/event_configuration
/// Do we want to enable random events at all
var/enable_random_events = TRUE
/// Assoc list of when the first event in a group can run. key: severity | value: assoc list with upper and low bounds (key: "upper"/"lower" | value: time in deciseconds)
var/list/first_run_times = list(
EVENT_LEVEL_MUNDANE = null,
EVENT_LEVEL_MODERATE = null,
EVENT_LEVEL_MAJOR = list("lower" = 40 MINUTES, "upper" = 50 MINUTES)
) // <---- Whoever designed this needs to be shot
/// Assoc list of lower bounds of event delays. key: severity | value: delay (deciseconds)
var/list/delay_lower_bound = list(
EVENT_LEVEL_MUNDANE = 5 MINUTES,
EVENT_LEVEL_MODERATE = 15 MINUTES,
EVENT_LEVEL_MAJOR = 25 MINUTES
)
/// Assoc list of lower bounds of event delays. key: severity | value: delay (deciseconds)
var/list/delay_upper_bound = list(
EVENT_LEVEL_MUNDANE = 7.5 MINUTES,
EVENT_LEVEL_MODERATE = 22.5 MINUTES,
EVENT_LEVEL_MAJOR = 35 MINUTES
)
/// Expected time of a round in deciseconds
var/expected_round_length = 120 MINUTES // This macro is equivilent to 72,000 deciseconds
/datum/configuration_section/event_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enable_random_events, data["allow_random_events"])
// Wrapper cant be used here due to it being multiplied
if(isnum(data["expected_round_length"]))
expected_round_length = data["expected_round_length"] MINUTES // Convert from minutes to deciseconds
// Load event severities. This is quite awful but needs to be done so we can account for config mistakes. This event system is awful
if(islist(data["event_delay_lower_bounds"]))
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MUNDANE], data["event_delay_lower_bounds"]["mundane"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MODERATE], data["event_delay_lower_bounds"]["moderate"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_lower_bound[EVENT_LEVEL_MAJOR], data["event_delay_lower_bounds"]["major"], MINUTES)
// Same here. I hate this.
if(islist(data["event_delay_upper_bounds"]))
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MUNDANE], data["event_delay_upper_bounds"]["mundane"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MODERATE], data["event_delay_upper_bounds"]["moderate"], MINUTES)
CONFIG_LOAD_NUM_MULT(delay_upper_bound[EVENT_LEVEL_MAJOR], data["event_delay_upper_bounds"]["major"], MINUTES)
// And for the worst, the first run delays. I hate this so much -aa07
if(islist(data["event_initial_delays"]))
for(var/list/assoclist in data["event_initial_delays"])
var/target = null
switch(assoclist["severity"])
if("mundane")
target = EVENT_LEVEL_MUNDANE
if("moderate")
target = EVENT_LEVEL_MODERATE
if("major")
target = EVENT_LEVEL_MAJOR
ASSERT(target in list(EVENT_LEVEL_MUNDANE, EVENT_LEVEL_MODERATE, EVENT_LEVEL_MAJOR))
first_run_times[target] = list("lower" = assoclist["lower_bound"] MINUTES, "upper" = assoclist["upper_bound"] MINUTES)
@@ -0,0 +1,96 @@
/// Config holder for everything regarding gamemodes
/datum/configuration_section/gamemode_configuration
/// List of all gamemodes (value: config-tag)
var/list/gamemodes = list()
/// Assoc list of gamemode names (key: config-tag | value: mode name)
var/list/gamemode_names = list()
/// Assoc list of gamemode probabilities (key: config-tag | value: probability)
var/list/probabilities = list()
/// List of all gamemodes that can be voted for (value: config-tag)
var/list/votable_modes = list()
/// Should antags be restricted based on account age?
var/antag_account_age_restriction = FALSE
/// Max age (in SSmobs cycles, [2 seconds]) before a shadowling starts to take damage if they have not hatched
var/shadowling_max_age = 600 // 20 mins
/// Scale amount of traitors with population
var/traitor_scaling = TRUE
/// Prevent mindshield roles getting antagonist status
var/prevent_mindshield_antags = TRUE
/// Rounds such as rev, wizard and malf end instantly when the antag has won. Enable the setting below to not do that.
var/disable_certain_round_early_end = FALSE
/// Amount of objectives traitors should get. Does not include escape or hijack.
var/traitor_objectives_amount = 2
/// Enable player limits on gamemodes? Disabling can be useful for testing
var/enable_gamemode_player_limit = TRUE
// Dynamically setup a list of all gamemodes
/datum/configuration_section/gamemode_configuration/New()
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = T
// Dont bother if theres no tag
if(!initial(M.config_tag))
continue
// Ensure each mode is added only once
if(initial(M.config_tag) in gamemodes)
continue
// Add it in
gamemodes += initial(M.config_tag)
gamemode_names[initial(M.config_tag)] = initial(M.name)
probabilities[initial(M.config_tag)] = initial(M.probability)
if(initial(M.votable))
votable_modes += initial(M.config_tag)
// Add secret to the votable pool
votable_modes += "secret"
/datum/configuration_section/gamemode_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(antag_account_age_restriction, data["antag_account_age_restrictions"])
CONFIG_LOAD_BOOL(traitor_scaling, data["traitor_scaling"])
CONFIG_LOAD_BOOL(prevent_mindshield_antags, data["prevent_mindshield_antag"])
CONFIG_LOAD_BOOL(disable_certain_round_early_end, data["disable_certain_round_early_end"])
CONFIG_LOAD_BOOL(enable_gamemode_player_limit, data["enable_gamemode_player_limit"])
CONFIG_LOAD_NUM(traitor_objectives_amount, data["traitor_objective_amount"])
CONFIG_LOAD_NUM(shadowling_max_age, data["shadowling_max_age"])
// Load gamemode probabilities
if(islist(data["gamemode_probabilities"]))
for(var/list/assocset in data["gamemode_probabilities"])
// Make sure it exists
if(assocset["gamemode"] in gamemodes)
probabilities[assocset["gamemode"]] = assocset["probability"]
else
stack_trace("Gamemode [assocset["gamemode"]] has a probability in config, but does not exist!")
/datum/configuration_section/gamemode_configuration/proc/pick_mode(mode_name)
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = T
// If the tag exists, and its the same as the mode
if(initial(M.config_tag) && (initial(M.config_tag) == mode_name))
return new T()
// Default to extended if it didnt work
stack_trace("Could not pick a gamemode. Defaulting to extended. (Attempted mode: [mode_name])")
return new /datum/game_mode/extended()
/datum/configuration_section/gamemode_configuration/proc/get_runnable_modes()
var/list/datum/game_mode/runnable_modes = new
for(var/T in subtypesof(/datum/game_mode))
var/datum/game_mode/M = new T()
if(!(M.config_tag in gamemodes))
qdel(M)
continue
if(probabilities[M.config_tag] <= 0)
qdel(M)
continue
if(M.can_start())
runnable_modes[M] = probabilities[M.config_tag]
return runnable_modes
@@ -0,0 +1,14 @@
/// Config holder for all gateway related things
/datum/configuration_section/gateway_configuration
/// Do we want to enable away missions or not
var/enable_away_mission = TRUE
/// Delay (in deciseconds) before the gateway is usable
var/away_mission_delay = 6000
/// List of all available away missions
var/list/enabled_away_missions = list()
/datum/configuration_section/gateway_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enable_away_mission, data["enable_away_mission"])
CONFIG_LOAD_NUM(away_mission_delay, data["away_mission_delay"])
CONFIG_LOAD_LIST(enabled_away_missions, data["enabled_away_missions"])
@@ -0,0 +1,137 @@
/// Config holder for all general/misc things
/datum/configuration_section/general_configuration
/// Server name for the BYOND hub
var/server_name = "Paradise Station"
/// Tagline for the hub entry
var/server_tag_line = "The perfect mix of RP & action"
/// Server features in a newline
var/server_features = "Medium RP, varied species/jobs"
/// Should bans be stored in the DB
var/use_database_bans = FALSE
/// Allow character OOC notes
var/allow_character_metadata = TRUE
/// Time in seconds for the pregame lobby
var/lobby_time = 240
/// Ban all Guest BYOND accounts
var/guest_ban = TRUE
/// Player threshold to automatically enable panic bunker
var/panic_bunker_threshold = 150
/// Allow players to use AntagHUD?
var/allow_antag_hud = TRUE
/// Forbid players from rejoining if they use AntagHUD?
var/restrict_antag_hud_rejoin = TRUE
/// Enable respanws by default?
var/respawn_enabled = FALSE
/// Enable karma? Disable to lockout awarding and unlock everything
var/enable_karma = TRUE
/// Enable CID randomiser buster?
var/enabled_cid_randomiser_buster = FALSE
/// Forbid admins from posessing and flying the singulo round
var/forbid_singulo_possession = FALSE
/// Force open a PM window when replied to? This is very annoying
var/popup_admin_pm = FALSE
/// Announce holidays (christmas, halloween, etc etc)
var/allow_holidays = TRUE
/// Enable auto muting in all chat channels
var/enable_auto_mute = FALSE
/// Show a warning to players to make them accept touching an SSD
var/ssd_warning = TRUE
/// Allow ghosts to spin chairs round
var/ghost_interaction = FALSE
/// Enable/disable starlight to light up space
var/starlight = TRUE
/// Disable lobby music?
var/disable_lobby_music = FALSE
/// Disable a popup if 2 users are on the same CID?
var/disable_cid_warning_popup = FALSE
/// Amount of loadout points non-donors should get
var/base_loadout_points = 5
/// Respawnability loss penalty for eary cryoing (minutes)
var/cryo_penalty_period = 30
/// Enable OOC emojis?
var/enable_ooc_emoji = TRUE
/// Auto start the game if on a local test server
var/developer_express_start = FALSE
/// Minimum client build. Keep above 1421 due to exploits
var/minimum_client_build = 1421
/// Give a confirm button for the "Start Now" verb
var/start_now_confirmation = TRUE
/// BYOND account age threshold for first join alerts
var/byond_account_age_threshold = 3
/// Max CIDs a client can have history of before a warning is thrown
var/max_client_cid_history = 20
/// Enable automatic profiling to profile.json
var/enable_auto_profiler = TRUE
/// Auto disable OOC on roundstart?
var/auto_disable_ooc = TRUE
/// Do we want to allow bones to break?
var/breakable_bones = TRUE
/// Enable/disable revival pod plants
var/enable_revival_pod_plants = TRUE
/// Enable/disable cloning
var/enable_cloning = TRUE
/// Randomise shift time instead of it always being 12:00?
var/randomise_shift_time = TRUE
/// Enable night-shift lighting?
var/enable_night_shifts = TRUE
/// Cap for monkey cube monkey spawns
var/monkey_cube_cap = 32
/// Enable to make explosions react to obstacles instead of ignoring them
var/reactionary_explosions = TRUE
/// Bomb cap (Devastation) Other values will be calculated around this
var/bomb_cap = 20
/// Time for a brain to keep its spark of life (deciseconds)
var/revival_brain_life = 10 MINUTES
/// Enable random AI lawsets from the default=TRUE pool
var/random_ai_lawset = TRUE
/datum/configuration_section/general_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
// A lot of bools
CONFIG_LOAD_BOOL(use_database_bans, data["use_database_bans"])
CONFIG_LOAD_BOOL(allow_character_metadata, data["allow_character_metadata"])
CONFIG_LOAD_BOOL(guest_ban, data["guest_ban"])
CONFIG_LOAD_BOOL(allow_antag_hud, data["allow_antag_hud"])
CONFIG_LOAD_BOOL(restrict_antag_hud_rejoin, data["restrict_antag_hud_rejoin"])
CONFIG_LOAD_BOOL(respawn_enabled, data["respawn_enabled"])
CONFIG_LOAD_BOOL(enable_karma, data["enable_karma"])
CONFIG_LOAD_BOOL(enabled_cid_randomiser_buster, data["enable_cid_randomiser_buster"])
CONFIG_LOAD_BOOL(forbid_singulo_possession, data["prevent_admin_singlo_possession"])
CONFIG_LOAD_BOOL(popup_admin_pm, data["popup_admin_pm"])
CONFIG_LOAD_BOOL(allow_holidays, data["allow_holidays"])
CONFIG_LOAD_BOOL(enable_auto_mute, data["enable_auto_mute"])
CONFIG_LOAD_BOOL(ssd_warning, data["ssd_warning"])
CONFIG_LOAD_BOOL(ghost_interaction, data["ghost_interaction"])
CONFIG_LOAD_BOOL(starlight, data["starlight"])
CONFIG_LOAD_BOOL(disable_lobby_music, data["disable_lobby_music"])
CONFIG_LOAD_BOOL(disable_cid_warning_popup, data["disable_cid_warning_popup"])
CONFIG_LOAD_BOOL(enable_ooc_emoji, data["enable_ooc_emoji"])
CONFIG_LOAD_BOOL(developer_express_start, data["developer_express_start"])
CONFIG_LOAD_BOOL(start_now_confirmation, data["start_now_confirmation"])
CONFIG_LOAD_BOOL(enable_auto_profiler, data["enable_auto_profiler"])
CONFIG_LOAD_BOOL(auto_disable_ooc, data["auto_disable_ooc"])
CONFIG_LOAD_BOOL(breakable_bones, data["breakable_bones"])
CONFIG_LOAD_BOOL(enable_revival_pod_plants, data["enable_revival_pod_plants"])
CONFIG_LOAD_BOOL(enable_cloning, data["enable_cloning"])
CONFIG_LOAD_BOOL(randomise_shift_time, data["randomise_shift_time"])
CONFIG_LOAD_BOOL(enable_night_shifts, data["enable_night_shifts"])
CONFIG_LOAD_BOOL(reactionary_explosions, data["reactionary_explosions"])
CONFIG_LOAD_BOOL(random_ai_lawset, data["random_ai_lawset"])
// Numbers
CONFIG_LOAD_NUM(lobby_time, data["lobby_time"])
CONFIG_LOAD_NUM(panic_bunker_threshold, data["panic_bunker_threshold"])
CONFIG_LOAD_NUM(base_loadout_points, data["base_loadout_points"])
CONFIG_LOAD_NUM(cryo_penalty_period, data["cryo_penalty_period"])
CONFIG_LOAD_NUM(minimum_client_build, data["minimum_client_build"])
CONFIG_LOAD_NUM(byond_account_age_threshold, data["byond_account_age_threshold"])
CONFIG_LOAD_NUM(max_client_cid_history, data["max_client_cid_history"])
CONFIG_LOAD_NUM(monkey_cube_cap, data["monkey_cube_cap"])
CONFIG_LOAD_NUM(bomb_cap, data["bomb_cap"])
CONFIG_LOAD_NUM(revival_brain_life, data["revival_brain_life"])
// Strings
CONFIG_LOAD_STR(server_name, data["server_name"])
CONFIG_LOAD_STR(server_tag_line, data["server_tag_line"])
CONFIG_LOAD_STR(server_features, data["server_features"])
@@ -0,0 +1,34 @@
/// Config holder for all things relating to IPIntel
/datum/configuration_section/ipintel_configuration
/// Is IPIntel enabled
var/enabled = FALSE
/// Are we in whitelist mode (Auto-kick people who are on proxies/VPNs)
var/whitelist_mode = TRUE
/// 0-1 float for percentage threshold to kick people out
var/bad_rating = 0.9
/// IPIntel contact email. Required.
var/contact_email = null
/// How many hours to save good matches for. Cached due to rate limits
var/hours_save_good = 72
/// How many hours to save bad matches for. Cached due to rate limits
var/hours_save_bad = 24
/// IPIntel Domain. Do not prefix with a protocol
var/ipintel_domain = "check.getipintel.net"
/// Do not proxy-check players with more hours than the below threshold
var/playtime_ignore_threshold = 10
/// Details URL for more info on an IP, including ASN. IP is tacked straight on the end.
var/details_url = "https://iphub.info/?ip="
/datum/configuration_section/ipintel_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enabled, data["ipintel_enabled"])
CONFIG_LOAD_BOOL(whitelist_mode, data["whitelist_mode"])
CONFIG_LOAD_NUM(bad_rating, data["bad_rating"])
CONFIG_LOAD_NUM(hours_save_good, data["hours_save_good"])
CONFIG_LOAD_NUM(hours_save_bad, data["hours_save_bad"])
CONFIG_LOAD_NUM(playtime_ignore_threshold, data["playtime_ignore_threshold"])
CONFIG_LOAD_STR(contact_email, data["contact_email"])
CONFIG_LOAD_STR(ipintel_domain, data["ipintel_domain"])
CONFIG_LOAD_STR(details_url, data["details_url"])
@@ -0,0 +1,55 @@
/// Config holder for all job related things
/datum/configuration_section/job_configuration
/// Do we want jobs to have minimal access or extra access (IE: Scientists having robotics access)
var/jobs_have_minimal_access = TRUE
/// Do we want to restrict jobs based on account age
var/restrict_jobs_on_account_age = FALSE
/// Allow admins to bypass age-based job restrictions
var/restrict_jobs_on_account_age_admin_bypass = TRUE
/// Enable EXP logging and tracking
var/enable_exp_tracking = FALSE
/// Lockout jobs based on EXP
var/enable_exp_restrictions = FALSE
/// Allow admins to bypass EXP restrictions
var/enable_exp_admin_bypass = TRUE
/// Allow non-admins to play as AI
var/allow_ai = TRUE
/// Prevent guests from playing high profile roles
var/guest_job_ban = TRUE
/// Grant assistants maint access
var/assistant_maint_access = TRUE
/// Limit amount of assistants?
var/assistant_limit = FALSE
/// If yes to above, ratio of assistants per security officer (IE: 4:1)
var/assistant_security_ratio = 2
/// Enable loading of job overrides from the config
var/enable_job_amount_overrides = TRUE
/// Map of job:amount for lowpop. key: Job | value: amount
var/list/lowpop_job_map = list()
/// Map of job:amount for highpop. key: Job | value: amount
var/list/highpop_job_map = list()
/datum/configuration_section/job_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(jobs_have_minimal_access, data["jobs_have_minimal_access"])
CONFIG_LOAD_BOOL(restrict_jobs_on_account_age, data["restrict_jobs_on_account_age"])
CONFIG_LOAD_BOOL(restrict_jobs_on_account_age_admin_bypass, data["restrict_jobs_on_account_age_admin_bypass"])
CONFIG_LOAD_BOOL(enable_exp_tracking, data["enable_exp_tracking"])
CONFIG_LOAD_BOOL(enable_exp_restrictions, data["enable_exp_restrictions"])
CONFIG_LOAD_BOOL(enable_exp_admin_bypass, data["enable_exp_admin_bypass"])
CONFIG_LOAD_BOOL(allow_ai, data["allow_ai"])
CONFIG_LOAD_BOOL(guest_job_ban, data["guest_job_ban"])
CONFIG_LOAD_BOOL(assistant_maint_access, data["assistant_maint_access"])
CONFIG_LOAD_BOOL(assistant_limit, data["assistant_limit"])
CONFIG_LOAD_NUM(assistant_security_ratio, data["assistant_security_ratio"])
CONFIG_LOAD_BOOL(enable_job_amount_overrides, data["enable_job_amount_overrides"])
if(enable_job_amount_overrides && islist(data["job_slot_amounts"]))
lowpop_job_map.Cut()
highpop_job_map.Cut()
for(var/kvp in data["job_slot_amounts"])
if(!isnull(kvp["name"]))
if(!isnull(kvp["lowpop"]))
lowpop_job_map[kvp["name"]] = kvp["lowpop"]
if(!isnull(kvp["highpop"]))
highpop_job_map[kvp["name"]] = kvp["highpop"]
@@ -0,0 +1,53 @@
/// Config holder for all things regarding logging
/datum/configuration_section/logging_configuration
/// Log OOC messages
var/ooc_logging = TRUE
/// Log ingame say messages
var/say_logging = TRUE
/// Log admin actions
var/admin_logging = TRUE
/// Log client access (login/logout)
var/access_logging = TRUE
/// Log game events (roundstart, results, a lot of other things)
var/game_logging = TRUE
/// Enable logging of votes and their results
var/vote_logging = TRUE
/// Enable logging of whipers
var/whisper_logging = TRUE
/// Enable logging of emotes
var/emote_logging = TRUE
/// Enable logging of attacks between players
var/attack_logging = TRUE
/// Enable logging of PDA messages
var/pda_logging = TRUE
/// Enable runtime logging
var/runtime_logging = TRUE
/// Enable world.log output logging
var/world_logging = TRUE
/// Log hrefs
var/href_logging = TRUE
/// Log admin warning messages
var/admin_warning_logging = TRUE
/// Log asay messages
var/adminchat_logging = TRUE
/// Log debug messages
var/debug_logging = TRUE
/datum/configuration_section/logging_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(ooc_logging, data["enable_ooc_logging"])
CONFIG_LOAD_BOOL(say_logging, data["enable_say_logging"])
CONFIG_LOAD_BOOL(admin_logging, data["enable_admin_logging"])
CONFIG_LOAD_BOOL(access_logging, data["enable_access_logging"])
CONFIG_LOAD_BOOL(game_logging, data["enable_game_logging"])
CONFIG_LOAD_BOOL(vote_logging, data["enable_vote_logging"])
CONFIG_LOAD_BOOL(whisper_logging, data["enable_whisper_logging"])
CONFIG_LOAD_BOOL(emote_logging, data["enable_emote_logging"])
CONFIG_LOAD_BOOL(attack_logging, data["enable_attack_logging"])
CONFIG_LOAD_BOOL(pda_logging, data["enable_pda_logging"])
CONFIG_LOAD_BOOL(runtime_logging, data["enable_runtime_logging"])
CONFIG_LOAD_BOOL(world_logging, data["enable_world_logging"])
CONFIG_LOAD_BOOL(href_logging, data["enable_href_logging"])
CONFIG_LOAD_BOOL(admin_warning_logging, data["enable_admin_warning_logging"])
CONFIG_LOAD_BOOL(adminchat_logging, data["enable_adminchat_logging"])
CONFIG_LOAD_BOOL(debug_logging, data["enable_debug_logging"])
@@ -0,0 +1,23 @@
/// Config holder for all things regarding the MC
/datum/configuration_section/mc_configuration
/// Server ticklag
var/ticklag = 0.5
/// Tick limit % during world Init
var/world_init_tick_limit = TICK_LIMIT_MC_INIT_DEFAULT
/// Base MC tick rate
var/base_tickrate = 1
/// Highpop MC tickrate
var/highpop_tickrate = 1.1
/// MC Highpop enable threshold
var/highpop_enable_threshold = 65
/// MC Highpop disable threshold
var/highpop_disable_threshold = 60
/datum/configuration_section/mc_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_NUM(ticklag, data["ticklag"])
CONFIG_LOAD_NUM(world_init_tick_limit, data["world_init_mc_tick_limit"])
CONFIG_LOAD_NUM(base_tickrate, data["base_mc_tick_rate"])
CONFIG_LOAD_NUM(highpop_tickrate, data["highpop_mc_tick_rate"])
CONFIG_LOAD_NUM(highpop_enable_threshold, data["mc_highpop_threshold_enable"])
CONFIG_LOAD_NUM(highpop_disable_threshold, data["mc_highpop_threshold_disable"])
@@ -0,0 +1,26 @@
/// Config holder for values relating to mob movement speeds
/datum/configuration_section/movement_configuration
/// Base run speed before modifiers
var/base_run_speed = 1
/// Base walk speed before modifiers
var/base_walk_speed = 4
/// Move delay for humanoids
var/human_delay = 1.5
/// Move delay for cyborgs
var/robot_delay = 2.5
/// Move delay for xenomorphs
var/alien_delay = 1.5
/// Move delay for slimes (xenobio, not slimepeople)
var/slime_delay = 1.5
/// Move delay for other simple animals
var/animal_delay = 2.5
/datum/configuration_section/movement_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_NUM(base_run_speed, data["base_run_speed"])
CONFIG_LOAD_NUM(base_walk_speed, data["base_walk_speed"])
CONFIG_LOAD_NUM(human_delay, data["human_delay"])
CONFIG_LOAD_NUM(robot_delay, data["robot_delay"])
CONFIG_LOAD_NUM(alien_delay, data["alien_delay"])
CONFIG_LOAD_NUM(slime_delay, data["slime_delay"])
CONFIG_LOAD_NUM(animal_delay, data["animal_delay"])
@@ -0,0 +1,14 @@
/// Config holder for all overflow-server related things
/datum/configuration_section/overflow_configuration
/// Amount of players before reroute server is used. 0 to disable.
var/reroute_cap = 0
/// Location of the overflow server
var/overflow_server_location = null
/// List of ckeys who will never be routed to the overflow server
var/list/overflow_whitelist = list()
/datum/configuration_section/overflow_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_NUM(reroute_cap, data["player_reroute_cap"])
CONFIG_LOAD_STR(overflow_server_location, data["overflow_server"])
CONFIG_LOAD_LIST(overflow_whitelist, data["overflow_whitelist"])
@@ -0,0 +1,23 @@
/// Config holder for all things regarding space ruins and lavaland ruins
/datum/configuration_section/ruin_configuration
/// Enable or disable space ruins
var/enable_space_ruins = TRUE
/// Minimum number of extra zlevels to fill with ruins
var/extra_levels_min = 2
/// Maximum number of extra zlevels to fill with ruins
var/extra_levels_max = 4
/// List of all active space ruins
var/list/active_space_ruins = list()
/// List of all active lavaland ruins
var/list/active_lava_ruins = list()
/// Budget for lavaland ruins
var/lavaland_ruin_budget = 60
/datum/configuration_section/ruin_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(enable_space_ruins, data["enable_space_ruins"])
CONFIG_LOAD_NUM(extra_levels_min, data["minimum_zlevels"])
CONFIG_LOAD_NUM(extra_levels_max, data["maximum_zlevels"])
CONFIG_LOAD_LIST(active_space_ruins, data["active_space_ruins"])
CONFIG_LOAD_LIST(active_lava_ruins, data["active_lava_ruins"])
CONFIG_LOAD_NUM(lavaland_ruin_budget, data["lavaland_ruin_budget"])
@@ -0,0 +1,26 @@
/// Config holder for stuff relating to server backend management and secrets
/datum/configuration_section/system_configuration
// NO EDITS OR READS TO THIS WHAT SOEVER
protection_state = PROTECTION_PRIVATE
/// Password for authorising world/Topic requests
var/topic_key = null
/// Medal hub address for lavaland stats
var/medal_hub_address = null
/// Medal hub password for lavaland stats
var/medal_hub_password = null
/// Do we want the server to kill on reboot instead of keeping the same DD session
var/shutdown_on_reboot = FALSE
/// If above is true, you can run a shell command
var/shutdown_shell_command = null
/// 2FA backend server host
var/_2fa_auth_host = null
/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(shutdown_on_reboot, data["shutdown_on_reboot"])
CONFIG_LOAD_STR(topic_key, data["communications_password"])
CONFIG_LOAD_STR(medal_hub_address, data["medal_hub_address"])
CONFIG_LOAD_STR(medal_hub_password, data["medal_hub_password"])
CONFIG_LOAD_STR(shutdown_shell_command, data["shutdown_shell_command"])
CONFIG_LOAD_STR(_2fa_auth_host, data["_2fa_auth_host"])
@@ -0,0 +1,47 @@
/// Config holder for all the server URLs
/datum/configuration_section/url_configuration
// Dont tweak these. You can read them though.
protection_state = PROTECTION_READONLY
/// List of URLs for the server RSC data
var/list/rsc_urls = list()
/// Server URL for auto-reconnecting people at end round
var/server_url
/// URL for the server ban appeals forum
var/banappeals_url
/// URL for the server wiki
var/wiki_url
/// URL for the server forums
var/forum_url
/// URL for the server rules
var/rules_url
/// URL for the server github repository
var/github_url
/// URL for server donations
var/donations_url
/// URL for a direct discord invite
var/discord_url
/// URL for a discord invite going via the forums
var/discord_forum_url
/// URL for linking ingame accounts and forum accounts. Token is appended to end
var/forum_link_url
/// URL for pulling player info on webtools
var/forum_playerinfo_url
/// URL for the CentCom Ban DB API
var/centcom_ban_db_url
/datum/configuration_section/url_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_LIST(rsc_urls, data["rsc_urls"])
CONFIG_LOAD_STR(server_url, data["reboot_url"])
CONFIG_LOAD_STR(banappeals_url, data["ban_appeals_url"])
CONFIG_LOAD_STR(wiki_url, data["wiki_url"])
CONFIG_LOAD_STR(forum_url, data["forum_url"])
CONFIG_LOAD_STR(rules_url, data["rules_url"])
CONFIG_LOAD_STR(github_url, data["github_url"])
CONFIG_LOAD_STR(donations_url, data["donations_url"])
CONFIG_LOAD_STR(discord_url, data["discord_url"])
CONFIG_LOAD_STR(discord_forum_url, data["discord_forum_url"])
CONFIG_LOAD_STR(forum_link_url, data["forum_link_url"])
CONFIG_LOAD_STR(forum_playerinfo_url, data["forum_playerinfo_url"])
CONFIG_LOAD_STR(centcom_ban_db_url, data["centcomm_ban_db_url"])
@@ -0,0 +1,33 @@
/// Config holder for stuff relating to the ingame vote system
/datum/configuration_section/vote_configuration
/// Allow players to start restart votes?
var/allow_restart_votes = FALSE
/// Allow players to start gamemode votes?
var/allow_mode_votes = FALSE
/// Minimum delay between each vote (deciseconds)
var/vote_delay = 18000 // 30 mins
/// How long will a vote last for (deciseconds)
var/vote_time = 600 // 60 seconds
/// Time before the first shuttle vote (deciseconds)
var/autotransfer_initial_time = 72000 // 2 hours
/// Time between subsequent shuttle votes if the first one is not successful (deciseconds)
var/autotransfer_interval_time = 18000 // 30 mins
/// Prevent dead players from voting
var/prevent_dead_voting = FALSE
/// Default to players not voting
var/disable_default_vote = TRUE
/// Enable map voting?
var/enable_map_voting = FALSE
/datum/configuration_section/vote_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(allow_restart_votes, data["allow_vote_restart"])
CONFIG_LOAD_BOOL(allow_mode_votes, data["allow_vote_mode"])
CONFIG_LOAD_BOOL(prevent_dead_voting, data["prevent_dead_voting"])
CONFIG_LOAD_BOOL(disable_default_vote, data["disable_default_vote"])
CONFIG_LOAD_BOOL(enable_map_voting, data["enable_map_voting"])
CONFIG_LOAD_NUM(vote_delay, data["vote_delay"])
CONFIG_LOAD_NUM(vote_time, data["vote_time"])
CONFIG_LOAD_NUM(autotransfer_initial_time, data["autotransfer_initial_time"])
CONFIG_LOAD_NUM(autotransfer_interval_time, data["autotransfer_interval_time"])
+8 -8
View File
@@ -194,7 +194,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/start_timeofday = REALTIMEOFDAY
// Initialize subsystems.
current_ticklimit = config.tick_limit_mc_init
current_ticklimit = GLOB.configuration.mc.world_init_tick_limit
for(var/datum/controller/subsystem/SS in subsystems)
if(SS.flags & SS_NO_INIT)
continue
@@ -206,8 +206,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
log_startup_progress("Initializations complete within [time] second[time == 1 ? "" : "s"]!")
if(config.developer_express_start & SSticker.current_state == GAME_STATE_PREGAME)
SSticker.current_state = GAME_STATE_SETTING_UP
if(GLOB.configuration.general.developer_express_start)
SSticker.force_start = TRUE
if(!current_runlevel)
SetRunLevel(1)
@@ -216,7 +216,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
sortTim(subsystems, /proc/cmp_subsystem_display)
// Set world options.
// world.fps = CONFIG_GET(number/fps) // TIGER TODO
world.tick_lag = config.Ticklag
world.tick_lag = GLOB.configuration.mc.ticklag
var/initialized_tod = REALTIMEOFDAY
if(sleep_offline_after_initializations)
@@ -626,10 +626,10 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
if(!processing)
return
var/client_count = length(GLOB.clients)
if(client_count < config.disable_high_pop_mc_mode_amount)
processing = config.base_mc_tick_rate
else if(client_count > config.high_pop_mc_mode_amount)
processing = config.high_pop_mc_tick_rate
if(client_count < GLOB.configuration.mc.highpop_disable_threshold)
processing = GLOB.configuration.mc.base_tickrate
else if(client_count > GLOB.configuration.mc.highpop_enable_threshold)
processing = GLOB.configuration.mc.highpop_tickrate
/datum/controller/master/proc/formatcpu()
switch(world.cpu)
+8 -6
View File
@@ -12,7 +12,8 @@ SUBSYSTEM_DEF(afk)
/datum/controller/subsystem/afk/Initialize()
if(config.warn_afk_minimum <= 0 || config.auto_cryo_afk <= 0 || config.auto_despawn_afk <= 0)
if(GLOB.configuration.afk.warning_minutes <= 0 || GLOB.configuration.afk.auto_cryo_minutes <= 0 || GLOB.configuration.afk.auto_despawn_minutes <= 0)
flags |= SS_NO_FIRE
else
non_cryo_antags = list(SPECIAL_ROLE_ABDUCTOR_AGENT, SPECIAL_ROLE_ABDUCTOR_SCIENTIST, \
@@ -35,19 +36,20 @@ SUBSYSTEM_DEF(afk)
toRemove += H.ckey
continue
var/mins_afk = round(H.client.inactivity / 600)
if(mins_afk < config.warn_afk_minimum)
if(mins_afk < GLOB.configuration.afk.warning_minutes)
if(afk_players[H.ckey])
toRemove += H.ckey
continue
if(!afk_players[H.ckey])
afk_players[H.ckey] = AFK_WARNED
warn(H, "<span class='danger'>You are AFK for [mins_afk] minutes. You will be cryod after [config.auto_cryo_afk] total minutes and fully despawned after [config.auto_despawn_afk] total minutes. Please move or click in game if you want to avoid being despawned.</span>")
warn(H, "<span class='danger'>You are AFK for [mins_afk] minutes. You will be cryod after [GLOB.configuration.afk.auto_cryo_minutes] total minutes and fully despawned after [GLOB.configuration.afk.auto_despawn_minutes] total minutes. Please move or click in game if you want to avoid being despawned.</span>")
else
var/area/A = T.loc // Turfs loc is the area
if(afk_players[H.ckey] == AFK_WARNED)
if(mins_afk >= config.auto_cryo_afk && A.can_get_auto_cryod)
if(mins_afk >= GLOB.configuration.afk.auto_cryo_minutes && A.can_get_auto_cryod)
if(A.fast_despawn)
toRemove += H.ckey
warn(H, "<span class='danger'>You have been despawned after being AFK for [mins_afk] minutes. You have been despawned instantly due to you being in a secure area.</span>")
@@ -60,13 +62,13 @@ SUBSYSTEM_DEF(afk)
afk_players[H.ckey] = AFK_CRYOD
log_afk_action(H, mins_afk, T, "put into cryostorage")
warn(H, "<span class='danger'>You are AFK for [mins_afk] minutes and have been moved to cryostorage. \
After being AFK for another [config.auto_despawn_afk] minutes you will be fully despawned. \
After being AFK for another [GLOB.configuration.afk.auto_despawn_minutes] minutes you will be fully despawned. \
Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.</span>")
else
message_admins("[key_name_admin(H)] at ([get_area(T).name] [ADMIN_JMP(T)]) is AFK for [mins_afk] and can't be automatically cryod due to it's antag status: ([H.mind.special_role]).")
afk_players[H.ckey] = AFK_ADMINS_WARNED
else if(afk_players[H.ckey] != AFK_ADMINS_WARNED && mins_afk >= config.auto_despawn_afk)
else if(afk_players[H.ckey] != AFK_ADMINS_WARNED && mins_afk >= GLOB.configuration.afk.auto_despawn_minutes)
log_afk_action(H, mins_afk, T, "despawned")
warn(H, "<span class='danger'>You have been despawned after being AFK for [mins_afk] minutes.</span>")
toRemove += H.ckey
+1 -1
View File
@@ -1,7 +1,7 @@
SUBSYSTEM_DEF(alarm)
name = "Alarm"
flags = SS_NO_INIT | SS_NO_FIRE
var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list(), "Burglar" = list())
var/list/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Burglar" = list())
/datum/controller/subsystem/alarm/proc/triggerAlarm(class, area/A, list/O, obj/alarmsource)
var/list/L = alarms[class]
+2 -2
View File
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(blackbox)
sqlversion = versions[FV.key]
var/datum/db_query/query_feedback_save = SSdbcore.NewQuery({"
INSERT IGNORE INTO [format_table_name("feedback")] (datetime, round_id, key_name, key_type, version, json)
INSERT IGNORE INTO feedback (datetime, round_id, key_name, key_type, version, json)
VALUES (NOW(), :rid, :keyname, :keytype, :version, :json)"}, list(
"rid" = text2num(GLOB.round_id),
"keyname" = FV.key,
@@ -287,7 +287,7 @@ SUBSYSTEM_DEF(blackbox)
lakey = L.lastattackerckey
var/datum/db_query/deathquery = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord)
INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord)
VALUES (:name, :key, :job, :special, :pod, NOW(), :laname, :lakey, :gender, :bruteloss, :fireloss, :brainloss, :oxyloss, :coord)"},
list(
"name" = L.real_name,
+4 -4
View File
@@ -24,7 +24,7 @@ SUBSYSTEM_DEF(changelog)
if(!SSdbcore.IsConnected())
return ..()
var/datum/db_query/latest_cl_date = SSdbcore.NewQuery("SELECT CAST(UNIX_TIMESTAMP(date_merged) AS CHAR) AS ut FROM [format_table_name("changelog")] ORDER BY date_merged DESC LIMIT 1")
var/datum/db_query/latest_cl_date = SSdbcore.NewQuery("SELECT CAST(UNIX_TIMESTAMP(date_merged) AS CHAR) AS ut FROM changelog ORDER BY date_merged DESC LIMIT 1")
if(!latest_cl_date.warn_execute())
qdel(latest_cl_date)
// Abort if we cant do this
@@ -63,7 +63,7 @@ SUBSYSTEM_DEF(changelog)
C.prefs.lastchangelog = current_cl_timestamp
var/datum/db_query/updatePlayerCLTime = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET lastchangelog=:lastchangelog WHERE ckey=:ckey",
"UPDATE player SET lastchangelog=:lastchangelog WHERE ckey=:ckey",
list(
"lastchangelog" = current_cl_timestamp,
"ckey" = C.ckey
@@ -266,11 +266,11 @@ SUBSYSTEM_DEF(changelog)
usr.client.github()
// Takes a PR number as argument
if(href_list["openPR"])
if(config.githuburl)
if(GLOB.configuration.url.github_url)
if(alert("This will open PR #[href_list["openPR"]] in your browser. Are you sure?",,"Yes","No")=="No")
return
// If the github URL in the config has a trailing slash, it doesnt matter here, thankfully github accepts having a double slash: https://github.com/org/repo//pull/1
var/url = "[config.githuburl]/pull/[href_list["openPR"]]"
var/url = "[GLOB.configuration.url.github_url]/pull/[href_list["openPR"]]"
usr << link(url)
else
to_chat(usr, "<span class='danger'>The GitHub URL is not set in the server configuration. PRs cannot be opened from changelog view. Please inform the server host.</span>")
+20 -20
View File
@@ -30,7 +30,7 @@ SUBSYSTEM_DEF(dbcore)
// This is in Initialize() so that its actually seen in chat
/datum/controller/subsystem/dbcore/Initialize()
if(!schema_valid)
to_chat(world, "<span class='boldannounce'>Database schema ([sql_version]) doesn't match the latest schema version ([SQL_VERSION]). Roundstart has been delayed.</span>")
to_chat(world, "<span class='boldannounce'>Database schema ([GLOB.configuration.database.version]) doesn't match the latest schema version ([SQL_VERSION]). Roundstart has been delayed.</span>")
return ..()
@@ -66,7 +66,7 @@ SUBSYSTEM_DEF(dbcore)
if(IsConnected())
return TRUE
if(!config.sql_enabled)
if(!GLOB.configuration.database.enabled)
return FALSE
if(failed_connection_timeout <= world.time) //it's been more than 5 seconds since we failed to connect, reset the counter
@@ -77,14 +77,14 @@ SUBSYSTEM_DEF(dbcore)
return FALSE
var/result = json_decode(rustg_sql_connect_pool(json_encode(list(
"host" = sqladdress,
"port" = text2num(sqlport),
"user" = sqlfdbklogin,
"pass" = sqlfdbkpass,
"db_name" = sqlfdbkdb,
"read_timeout" = config.async_sql_query_timeout,
"write_timeout" = config.async_sql_query_timeout,
"max_threads" = config.rust_sql_thread_limit,
"host" = GLOB.configuration.database.address,
"port" = GLOB.configuration.database.port,
"user" = GLOB.configuration.database.username,
"pass" = GLOB.configuration.database.password,
"db_name" = GLOB.configuration.database.db,
"read_timeout" = GLOB.configuration.database.async_query_timeout,
"write_timeout" = GLOB.configuration.database.async_query_timeout,
"max_threads" = GLOB.configuration.database.async_thread_limit,
))))
. = (result["status"] == "ok")
if(.)
@@ -102,11 +102,11 @@ SUBSYSTEM_DEF(dbcore)
* If it is a valid version, the DB will then connect.
*/
/datum/controller/subsystem/dbcore/proc/CheckSchemaVersion()
if(config.sql_enabled)
if(GLOB.configuration.database.enabled)
// The unit tests have their own version of this check, which wont hold the server up infinitely, so this is disabled if we are running unit tests
#ifndef UNIT_TESTS
if(config.sql_enabled && sql_version != SQL_VERSION)
config.sql_enabled = FALSE
if(GLOB.configuration.database.enabled && GLOB.configuration.database.version != SQL_VERSION)
GLOB.configuration.database.enabled = FALSE
schema_valid = FALSE
SSticker.ticker_going = FALSE
SEND_TEXT(world.log, "Database connection failed: Invalid SQL Versions")
@@ -142,7 +142,7 @@ SUBSYSTEM_DEF(dbcore)
//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/db_query/query_round_shutdown = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET shutdown_datetime = Now(), end_state = :end_state WHERE id = :round_id",
"UPDATE round SET shutdown_datetime = Now(), end_state = :end_state WHERE id = :round_id",
list("end_state" = SSticker.end_state, "round_id" = GLOB.round_id)
)
query_round_shutdown.Execute()
@@ -160,7 +160,7 @@ SUBSYSTEM_DEF(dbcore)
if(!IsConnected())
return
var/datum/db_query/query_round_initialize = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("round")] (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)",
"INSERT INTO round (initialize_datetime, server_ip, server_port) VALUES (Now(), INET_ATON(:internet_address), :port)",
list("internet_address" = world.internet_address || "0", "port" = "[world.port]")
)
query_round_initialize.Execute(async = FALSE)
@@ -177,7 +177,7 @@ SUBSYSTEM_DEF(dbcore)
if(!IsConnected())
return
var/datum/db_query/query_round_start = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET start_datetime=NOW(), commit_hash=:hash WHERE id=:round_id",
"UPDATE round SET start_datetime=NOW(), commit_hash=:hash WHERE id=:round_id",
list("hash" = GLOB.revision_info.commit_hash, "round_id" = GLOB.round_id)
)
query_round_start.Execute(async = FALSE) // This happens during a time of intense server lag, so should be non-async
@@ -193,7 +193,7 @@ SUBSYSTEM_DEF(dbcore)
if(!IsConnected())
return
var/datum/db_query/query_round_end = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = :game_mode_result WHERE id = :round_id",
"UPDATE round SET end_datetime = Now(), game_mode_result = :game_mode_result WHERE id = :round_id",
list("game_mode_result" = SSticker.mode_result, "station_name" = station_name(), "round_id" = GLOB.round_id)
)
query_round_end.Execute()
@@ -206,7 +206,7 @@ SUBSYSTEM_DEF(dbcore)
* Does a few sanity checks, then asks the DLL if we are properly connected
*/
/datum/controller/subsystem/dbcore/proc/IsConnected()
if(!config.sql_enabled)
if(!GLOB.configuration.database.enabled)
return FALSE
if(!schema_valid)
return FALSE
@@ -222,7 +222,7 @@ SUBSYSTEM_DEF(dbcore)
* Will always report "Database disabled by configuration" if the DB is disabled.
*/
/datum/controller/subsystem/dbcore/proc/ErrorMsg()
if(!config.sql_enabled)
if(!GLOB.configuration.database.enabled)
return "Database disabled by configuration"
return last_error
@@ -492,7 +492,7 @@ SUBSYSTEM_DEF(dbcore)
/client/proc/reestablish_db_connection()
set category = "Debug"
set name = "Reestablish DB Connection"
if(!config.sql_enabled)
if(!GLOB.configuration.database.enabled)
to_chat(usr, "<span class='warning'>The Database is not enabled in the server configuration!</span>")
return
+14 -10
View File
@@ -7,7 +7,7 @@ SUBSYSTEM_DEF(discord)
var/last_administration_ping = 0
/datum/controller/subsystem/discord/Initialize(start_timeofday)
if(config.discord_webhooks_enabled)
if(GLOB.configuration.discord.webhooks_enabled)
enabled = TRUE
return ..()
@@ -18,11 +18,11 @@ SUBSYSTEM_DEF(discord)
var/list/webhook_urls
switch(destination)
if(DISCORD_WEBHOOK_ADMIN)
webhook_urls = config.discord_admin_webhook_urls
webhook_urls = GLOB.configuration.discord.admin_webhook_urls
if(DISCORD_WEBHOOK_PRIMARY)
webhook_urls = config.discord_main_webhook_urls
webhook_urls = GLOB.configuration.discord.main_webhook_urls
if(DISCORD_WEBHOOK_MENTOR)
webhook_urls = config.discord_mentor_webhook_urls
webhook_urls = GLOB.configuration.discord.mentor_webhook_urls
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = content
@@ -36,14 +36,18 @@ SUBSYSTEM_DEF(discord)
var/list/webhook_urls
switch(destination)
if(DISCORD_WEBHOOK_ADMIN)
webhook_urls = config.discord_admin_webhook_urls
webhook_urls = GLOB.configuration.discord.admin_webhook_urls
if(DISCORD_WEBHOOK_PRIMARY)
webhook_urls = config.discord_main_webhook_urls
webhook_urls = GLOB.configuration.discord.main_webhook_urls
if(DISCORD_WEBHOOK_MENTOR)
webhook_urls = GLOB.configuration.discord.mentor_webhook_urls
for(var/url in webhook_urls)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json"))
// This one is for sending messages to the admin channel if no admins are active, complete with a ping to the game admins role
/datum/controller/subsystem/discord/proc/send2discord_simple_noadmins(content, check_send_always = FALSE)
if(!enabled)
return
// Setup some stuff
var/alerttext
var/list/admincounter = staff_countup(R_BAN)
@@ -57,7 +61,7 @@ SUBSYSTEM_DEF(discord)
else
alerttext = " | **NO ADMINS ONLINE**"
else
if(check_send_always && config.discord_forward_all_ahelps)
if(check_send_always && GLOB.configuration.discord.forward_all_ahelps)
// If we are here, there are admins online. We want to forward everything, but obviously dont want to add a ping, so we do this
add_ping = FALSE
else
@@ -68,17 +72,17 @@ SUBSYSTEM_DEF(discord)
var/datum/discord_webhook_payload/dwp = new()
dwp.webhook_content = message
for(var/url in config.discord_admin_webhook_urls)
for(var/url in GLOB.configuration.discord.admin_webhook_urls)
SShttp.create_async_request(RUSTG_HTTP_METHOD_POST, url, dwp.serialize2json(), list("content-type" = "application/json"))
// Helper to make administrator ping easier
/datum/controller/subsystem/discord/proc/handle_administrator_ping()
// Check if a role is even set
if(config.discord_admin_role_id)
if(GLOB.configuration.discord.admin_role_id)
if(last_administration_ping > world.time)
return "*(Role pinged recently)*"
last_administration_ping = world.time + 60 SECONDS
return "<@&[config.discord_admin_role_id]>"
return "<@&[GLOB.configuration.discord.admin_role_id]>"
return ""
+1 -1
View File
@@ -166,7 +166,7 @@ SUBSYSTEM_DEF(ghost_spawns)
if(role_text)
if(jobban_isbanned(M, role_text) || jobban_isbanned(M, ROLE_SYNDICATE))
return
if(config.use_exp_restrictions && min_hours)
if(GLOB.configuration.jobs.enable_exp_restrictions && min_hours)
if(M.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60)
return
if(check_antaghud && cannotPossess(M))
+1 -1
View File
@@ -5,7 +5,7 @@ SUBSYSTEM_DEF(holiday)
var/list/holidays
/datum/controller/subsystem/holiday/Initialize(start_timeofday)
if(!config.allow_holidays)
if(!GLOB.configuration.general.allow_holidays)
return ..() //Holiday stuff was not enabled in the config!
var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year
+365 -4
View File
@@ -3,13 +3,374 @@ SUBSYSTEM_DEF(ipintel)
wait = 1
flags = SS_NO_FIRE
init_order = INIT_ORDER_XKEYSCORE // 10
var/enabled = 0 //disable at round start to avoid checking reconnects
// Are we enabled? Auto disable at world init to avoid checking reconnects
var/enabled = FALSE
var/throttle = 0
var/errors = 0
var/list/cache = list()
/datum/controller/subsystem/ipintel/Initialize(timeofday, zlevel)
enabled = 1
. = ..()
/datum/controller/subsystem/ipintel/Initialize(timeofday)
enabled = TRUE
return ..()
// Represents an IP intel holder datum
/datum/ipintel
/// The IP being checked
var/ip
/// The current rating, 0-1 float.
var/intel = 0
/// Whether this was loaded from the cache or not
var/cache = FALSE
/// How many minutes ago it was cached
var/cacheminutesago = 0
/// The date it was cached
var/cachedate = ""
/// The real time it was cached
var/cacherealtime = 0
/datum/ipintel/New()
cachedate = SQLtime()
cacherealtime = world.realtime
/datum/ipintel/proc/is_valid()
. = FALSE
if(intel < 0)
return
if(intel <= GLOB.configuration.ipintel.bad_rating)
if(world.realtime < cacherealtime + (GLOB.configuration.ipintel.hours_save_good HOURS))
return TRUE
else
if(world.realtime < cacherealtime + (GLOB.configuration.ipintel.hours_save_bad HOURS))
return TRUE
/**
* Get IP intel
*
* Performs a lookup of the rating for an IP provided
*
* Arguments:
* * ip - The IP to lookup
* * bypasscache - Do we want to bypass the DB cache?
* * updatecache - Do we want to update the DB cache?
*/
/datum/controller/subsystem/ipintel/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
var/datum/ipintel/res = new()
res.ip = ip
. = res
if(!ip || !GLOB.configuration.ipintel.contact_email || !GLOB.configuration.ipintel.enabled || !enabled)
return
if(!bypasscache)
var/datum/ipintel/cachedintel = cache[ip]
if(cachedintel && cachedintel.is_valid())
cachedintel.cache = TRUE
return cachedintel
if(SSdbcore.IsConnected())
var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
FROM ipintel
WHERE
ip = INET_ATON(:ip)
AND ((
intel < :rating_bad
AND
date + INTERVAL :save_good HOUR > NOW()
) OR (
intel >= :rating_bad
AND
date + INTERVAL :save_bad HOUR > NOW()
))
"}, list(
"ip" = ip,
"rating_bad" = GLOB.configuration.ipintel.bad_rating,
"save_good" = GLOB.configuration.ipintel.hours_save_good,
"save_bad" = GLOB.configuration.ipintel.hours_save_bad,
))
if(!query_get_ip_intel.warn_execute())
qdel(query_get_ip_intel)
return
if(query_get_ip_intel.NextRow())
res.cache = TRUE
res.cachedate = query_get_ip_intel.item[1]
res.intel = text2num(query_get_ip_intel.item[2])
res.cacheminutesago = text2num(query_get_ip_intel.item[3])
res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60)
cache[ip] = res
qdel(query_get_ip_intel)
return
qdel(query_get_ip_intel)
res.intel = ip_intel_query(ip)
if(updatecache && res.intel >= 0)
cache[ip] = res
if(SSdbcore.IsConnected())
var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery({"
INSERT INTO ipintel (ip, intel) VALUES (INET_ATON(:ip), :intel)
ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()"},
list(
"ip" = ip,
"intel" = res.intel
)
)
query_add_ip_intel.warn_execute()
qdel(query_add_ip_intel)
/**
* Performs the remote IPintel lookup
*
*
*
* Arguments:
* * ip - The IP to lookup
* * retried - Was this attempt retried?
*/
/datum/controller/subsystem/ipintel/proc/ip_intel_query(ip, retried = FALSE)
. = -1 //default
if(!ip)
return
if(throttle > world.timeofday)
return
if(!enabled)
return
// Do not refactor this to use SShttp, because that requires the subsystem to be firing for requests to be made, and this will be triggered before the MC has finished loading
var/list/http[] = world.Export("http://[GLOB.configuration.ipintel.ipintel_domain]/check.php?ip=[ip]&contact=[GLOB.configuration.ipintel.contact_email]&format=json&flags=b")
if(http)
var/status = text2num(http["STATUS"])
if(status == 200)
var/response = json_decode(file2text(http["CONTENT"]))
if(response)
if(response["status"] == "success")
var/intelnum = text2num(response["result"])
if(isnum(intelnum))
return text2num(response["result"])
else
ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retried)
if(!retried)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Bad response from server: [response["status"]].", ip, retried)
if(!retried)
sleep(25)
return .(ip, 1)
else if(status == 429)
ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1)
return
else
ipintel_handle_error("Unknown status code: [status].", ip, retried)
if(!retried)
sleep(25)
return .(ip, 1)
else
ipintel_handle_error("Unable to connect to API.", ip, retried)
if(!retried)
sleep(25)
return .(ip, 1)
/**
* Error handler
*
* Handles an IP intel error, also throttling the susbystem if required
*
* Arguments:
* * error - The error description
* * ip - The IP that was tried
* * retried - Was this on a retried attempt
*/
/datum/controller/subsystem/ipintel/proc/ipintel_handle_error(error, ip, retried)
if(retried)
errors++
error += " Could not check [ip]. Disabling IPINTEL for [errors] minute[(errors == 1 ? "" : "s")]"
throttle = world.timeofday + (2 * errors MINUTES)
else
error += " Attempting retry on [ip]."
log_ipintel(error)
/**
* Logs an IPintel error
*
* Pretty self explanatory. Logs errors regarding ipintel.
*
* Arguments:
* * text - Argument 1
*/
/datum/controller/subsystem/ipintel/proc/log_ipintel(text)
log_game("IPINTEL: [text]")
log_debug("IPINTEL: [text]")
/**
* IPIntel Ban Checker
*
* Checks if a user is banned due to IPintel. It will check configuration, DB, whitelist checks, and more
*
* Arguments:
* * t_ckey - The ckey to check
* * t_ip - The IP to check
*/
/datum/controller/subsystem/ipintel/proc/ipintel_is_banned(t_ckey, t_ip)
if(!GLOB.configuration.ipintel.contact_email)
return FALSE
if(!GLOB.configuration.ipintel.enabled)
return FALSE
if(!GLOB.configuration.ipintel.whitelist_mode)
return FALSE
if(!SSdbcore.IsConnected())
return FALSE
if(!ipintel_badip_check(t_ip))
return FALSE
if(vpn_whitelist_check(t_ckey))
return FALSE
return TRUE
/**
* IP Rating Checker
*
* Checks if a provided IP passes the config threshold for denial
*
* Arguments:
* * target_ip - The IP to check
*/
/datum/controller/subsystem/ipintel/proc/ipintel_badip_check(target_ip)
var/rating_bad = GLOB.configuration.ipintel.bad_rating
if(!rating_bad)
log_debug("ipintel_badip_check reports misconfigured rating_bad directive")
return FALSE
var/valid_hours = GLOB.configuration.ipintel.hours_save_bad
if(!valid_hours)
log_debug("ipintel_badip_check reports misconfigured ipintel_save_bad directive")
return FALSE
var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
SELECT * FROM ipintel WHERE ip = INET_ATON(:target_ip)
AND intel >= :rating_bad AND (date + INTERVAL :valid_hours HOUR) > NOW()"},
list(
"target_ip" = target_ip,
"rating_bad" = rating_bad,
"valid_hours" = valid_hours
)
)
if(!query_get_ip_intel.warn_execute())
log_debug("ipintel_badip_check reports failed query execution")
qdel(query_get_ip_intel)
return FALSE
if(!query_get_ip_intel.NextRow())
qdel(query_get_ip_intel)
return FALSE
qdel(query_get_ip_intel)
return TRUE
/**
* VPN whitelist checker
*
* Checks if a ckey is whitelisted to be using a VPN against the DB
*
* Arguments:
* * target_ckey - The ckey to check
*/
/datum/controller/subsystem/ipintel/proc/vpn_whitelist_check(target_ckey)
if(!GLOB.configuration.ipintel.whitelist_mode)
return FALSE
var/datum/db_query/query_whitelist_check = SSdbcore.NewQuery("SELECT * FROM vpn_whitelist WHERE ckey=:ckey", list(
"ckey" = target_ckey
))
if(!query_whitelist_check.warn_execute())
qdel(query_whitelist_check)
return FALSE
if(query_whitelist_check.NextRow())
qdel(query_whitelist_check)
return TRUE // At least one row in the whitelist names their ckey. That means they are whitelisted.
qdel(query_whitelist_check)
return FALSE
/**
* VPN whitelist adder
*
* Adds a ckey to the VPN whitelist. Asks the admin to also provide a link to their request.
*
* Arguments:
* * target_ckey - The ckey to whitelist
*/
/datum/controller/subsystem/ipintel/proc/vpn_whitelist_add(target_ckey)
var/reason_string = input(usr, "Enter link to the URL of their whitelist request on the forum.","Reason required") as message|null
if(!reason_string)
return FALSE
var/datum/db_query/query_whitelist_add = SSdbcore.NewQuery("INSERT INTO vpn_whitelist (ckey,reason) VALUES (:targetckey, :reason)", list(
"targetckey" = target_ckey,
"reason" = reason_string
))
if(!query_whitelist_add.warn_execute())
qdel(query_whitelist_add)
return FALSE
qdel(query_whitelist_add)
return TRUE
/**
* VPN whitelist remover
*
* Removes a ckey from the VPN whitelist. Pretty simple.
*
* Arguments:
* * target_ckey - The ckey to remove
*/
/datum/controller/subsystem/ipintel/proc/vpn_whitelist_remove(target_ckey)
var/datum/db_query/query_whitelist_remove = SSdbcore.NewQuery("DELETE FROM vpn_whitelist WHERE ckey=:targetckey", list(
"targetckey" = target_ckey
))
if(!query_whitelist_remove.warn_execute())
qdel(query_whitelist_remove)
return FALSE
qdel(query_whitelist_remove)
return TRUE
/**
* VPN whitelist panel
*
* Doesnt actually open a panel, this is just a verb to handle the rest of the whitelist operations
*
* Arguments:
* * target_ckey - The ckey to add/remove
*/
/datum/controller/subsystem/ipintel/proc/vpn_whitelist_panel(target_ckey as text)
if(!check_rights(R_ADMIN))
return
if(!target_ckey)
return
var/is_already_whitelisted = vpn_whitelist_check(target_ckey)
if(is_already_whitelisted)
var/confirm = alert("[target_ckey] is already whitelisted. Remove them?", "Confirm Removal", "No", "Yes")
if(!confirm || confirm != "Yes")
to_chat(usr, "VPN whitelist alteration cancelled.")
return
else if(vpn_whitelist_remove(target_ckey))
to_chat(usr, "[target_ckey] was removed from the VPN whitelist.")
else
to_chat(usr, "VPN whitelist unchanged.")
else
if(vpn_whitelist_add(target_ckey))
to_chat(usr, "[target_ckey] was added to the VPN whitelist.")
else
to_chat(usr, "VPN whitelist unchanged.")
+25 -35
View File
@@ -20,12 +20,12 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/Initialize(timeofday)
if(!occupations.len)
SetupOccupations()
LoadJobs("config/jobs.txt")
LoadJobs(FALSE)
return ..()
// Only fires every 5 minutes
/datum/controller/subsystem/jobs/fire()
if(!SSdbcore.IsConnected() || !config.use_exp_tracking)
if(!SSdbcore.IsConnected() || !GLOB.configuration.jobs.enable_exp_tracking)
return
batch_update_player_exp(announce = FALSE) // Set this to true if you ever want to inform players about their EXP gains
@@ -242,8 +242,8 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/proc/FillAIPosition()
if(config && !config.allow_ai)
return 0
if(!GLOB.configuration.jobs.allow_ai)
return FALSE
var/ai_selected = 0
var/datum/job/job = GetJob("AI")
@@ -514,39 +514,29 @@ SUBSYSTEM_DEF(jobs)
/datum/controller/subsystem/jobs/proc/LoadJobs(jobsfile) //ran during round setup, reads info from jobs.txt -- Urist
if(!config.load_jobs_from_txt)
return 0
/datum/controller/subsystem/jobs/proc/LoadJobs(highpop = FALSE) //ran during round setup, reads info from jobs list
if(!GLOB.configuration.jobs.enable_job_amount_overrides)
return FALSE
var/list/jobEntries = file2list(jobsfile)
var/list/joblist = list()
for(var/job in jobEntries)
if(!job)
if(highpop)
joblist = GLOB.configuration.jobs.highpop_job_map.Copy()
else
joblist = GLOB.configuration.jobs.lowpop_job_map.Copy()
for(var/job in joblist)
// Key: name | Value: Amount
var/datum/job/J = GetJob(job)
if(!J)
continue
J.total_positions = text2num(joblist[job])
J.spawn_positions = text2num(joblist[job])
job = trim(job)
if(!length(job))
continue
if(job == "AI" || job == "Cyborg") //I dont like this here but it will do for now
J.total_positions = 0
var/pos = findtext(job, "=")
var/name = null
var/value = null
if(pos)
name = copytext(job, 1, pos)
value = copytext(job, pos + 1)
else
continue
if(name && value)
var/datum/job/J = GetJob(name)
if(!J) continue
J.total_positions = text2num(value)
J.spawn_positions = text2num(value)
if(name == "AI" || name == "Cyborg")//I dont like this here but it will do for now
J.total_positions = 0
return 1
return TRUE
/datum/controller/subsystem/jobs/proc/HandleFeedbackGathering()
@@ -750,7 +740,7 @@ SUBSYSTEM_DEF(jobs)
continue // If a client logs out in the middle of this
var/datum/db_query/exp_read = SSdbcore.NewQuery(
"SELECT exp FROM [format_table_name("player")] WHERE ckey=:ckey",
"SELECT exp FROM player WHERE ckey=:ckey",
list("ckey" = C.ckey)
)
@@ -834,7 +824,7 @@ SUBSYSTEM_DEF(jobs)
C.prefs.exp = new_exp
var/datum/db_query/update_query = SSdbcore.NewQuery(
"UPDATE [format_table_name("player")] SET exp =:newexp, lastseen=NOW() WHERE ckey=:ckey",
"UPDATE player SET exp =:newexp, lastseen=NOW() WHERE ckey=:ckey",
list(
"newexp" = new_exp,
"ckey" = C.ckey
@@ -844,7 +834,7 @@ SUBSYSTEM_DEF(jobs)
player_update_queries += update_query
var/datum/db_query/update_query_history = SSdbcore.NewQuery({"
INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost)
INSERT INTO playtime_history (ckey, date, time_living, time_ghost)
VALUES (:ckey, CURDATE(), :addedliving, :addedghost)
ON DUPLICATE KEY UPDATE time_living=time_living + VALUES(time_living), time_ghost=time_ghost + VALUES(time_ghost)"},
list(
+1 -1
View File
@@ -13,7 +13,7 @@ SUBSYSTEM_DEF(lighting)
/datum/controller/subsystem/lighting/Initialize(timeofday)
if(!initialized)
if(config.starlight)
if(GLOB.configuration.general.starlight)
for(var/I in GLOB.all_areas)
var/area/A = I
if(A.dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT)
+22 -16
View File
@@ -6,6 +6,10 @@ SUBSYSTEM_DEF(mapping)
var/datum/map/map_datum
/// What map will be used next round
var/datum/map/next_map
/// List of all areas that can be accessed via IC means
var/list/teleportlocs
/// List of all areas that can be accessed via IC and OOC means
var/list/ghostteleportlocs
// This has to be here because world/New() uses [station_name()], which looks this datum up
/datum/controller/subsystem/mapping/PreInit()
@@ -40,11 +44,11 @@ SUBSYSTEM_DEF(mapping)
loadLavaland()
// Pick a random away mission.
if(!config.disable_away_missions)
if(GLOB.configuration.gateway.enable_away_mission)
load_away_mission()
// Seed space ruins
if(!config.disable_space_ruins)
if(GLOB.configuration.ruins.enable_space_ruins)
handleRuins()
// Makes a blank space level for the sake of randomness
@@ -57,35 +61,37 @@ SUBSYSTEM_DEF(mapping)
// Spawn Lavaland ruins and rivers.
log_startup_progress("Populating lavaland...")
var/lavaland_setup_timer = start_watch()
seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, GLOB.lava_ruins_templates)
seedRuins(list(level_name_to_num(MINING)), GLOB.configuration.ruins.lavaland_ruin_budget, /area/lavaland/surface/outdoors/unexplored, GLOB.lava_ruins_templates)
spawn_rivers(list(level_name_to_num(MINING)))
log_startup_progress("Successfully populated lavaland in [stop_watch(lavaland_setup_timer)]s.")
// Now we make a list of areas for teleport locs
// TOOD: Make these locs into lists on the SS itself, not globs
teleportlocs = list()
for(var/area/AR in world)
if(AR.no_teleportlocs)
continue
if(GLOB.teleportlocs[AR.name])
if(teleportlocs[AR.name])
continue
var/turf/picked = safepick(get_area_turfs(AR.type))
if(picked && is_station_level(picked.z))
GLOB.teleportlocs[AR.name] = AR
teleportlocs[AR.name] = AR
GLOB.teleportlocs = sortAssoc(GLOB.teleportlocs)
teleportlocs = sortAssoc(teleportlocs)
ghostteleportlocs = list()
for(var/area/AR in world)
if(GLOB.ghostteleportlocs[AR.name])
if(ghostteleportlocs[AR.name])
continue
var/list/turfs = get_area_turfs(AR.type)
if(turfs.len)
GLOB.ghostteleportlocs[AR.name] = AR
ghostteleportlocs[AR.name] = AR
GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs)
ghostteleportlocs = sortAssoc(ghostteleportlocs)
// World name
if(config && config.server_name)
world.name = "[config.server_name]: [station_name()]"
if(GLOB.configuration.general.server_name)
world.name = "[GLOB.configuration.general.server_name]: [station_name()]"
else
world.name = station_name()
@@ -96,7 +102,7 @@ SUBSYSTEM_DEF(mapping)
// load in extra levels of space ruins
var/load_zlevels_timer = start_watch()
log_startup_progress("Creating random space levels...")
var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max)
var/num_extra_space = rand(GLOB.configuration.ruins.extra_levels_min, GLOB.configuration.ruins.extra_levels_max)
for(var/i in 1 to num_extra_space)
GLOB.space_manager.add_new_zlevel("Ruin Area #[i]", linkage = CROSSLINKED, traits = list(REACHABLE, SPAWN_RUINS))
log_startup_progress("Loaded random space levels in [stop_watch(load_zlevels_timer)]s.")
@@ -128,7 +134,7 @@ SUBSYSTEM_DEF(mapping)
if(!SSdbcore.IsConnected())
return
var/datum/db_query/query_set_map = SSdbcore.NewQuery(
"UPDATE [format_table_name("round")] SET start_datetime=NOW(), map_name=:mapname, station_name=:stationname WHERE id=:round_id",
"UPDATE round SET start_datetime=NOW(), map_name=:mapname, station_name=:stationname WHERE id=:round_id",
list("mapname" = map_datum.technical_name, "stationname" = map_datum.fluff_name, "round_id" = GLOB.round_id)
)
query_set_map.Execute(async = FALSE) // This happens during a time of intense server lag, so should be non-async
@@ -230,11 +236,11 @@ SUBSYSTEM_DEF(mapping)
if(length(GLOB.awaydestinations))
return
if(GLOB.potentialRandomZlevels && length(GLOB.potentialRandomZlevels))
if(length(GLOB.configuration.gateway.enabled_away_missions))
var/watch = start_watch()
log_startup_progress("Loading away mission...")
var/map = pick(GLOB.potentialRandomZlevels)
var/map = pick(GLOB.configuration.gateway.enabled_away_missions)
var/file = file(map)
if(isfile(file))
var/zlev = GLOB.space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL,BLOCK_TELEPORT))
+8 -8
View File
@@ -4,15 +4,15 @@ SUBSYSTEM_DEF(medals)
var/hub_enabled = FALSE
/datum/controller/subsystem/medals/Initialize(timeofday)
if(config.medal_hub_address && config.medal_hub_password)
if(GLOB.configuration.system.medal_hub_address && GLOB.configuration.system.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.medal_hub_address, config.medal_hub_password)))
if(isnull(world.SetMedal(medal, player, GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)))
hub_enabled = FALSE
log_game("MEDAL ERROR: Could not contact hub to award medal [medal] to player [player.ckey].")
message_admins("Error! Failed to contact hub to award [medal] medal to [player.ckey]!")
@@ -35,7 +35,7 @@ SUBSYSTEM_DEF(medals)
var/newscoreparam = list2params(oldscore)
if(isnull(world.SetScores(player.ckey, newscoreparam, config.medal_hub_address, config.medal_hub_password)))
if(isnull(world.SetScores(player.ckey, newscoreparam, GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)))
hub_enabled = FALSE
log_game("SCORE ERROR: Could not contact hub to set score. Score [score] for player [player.ckey].")
message_admins("Error! Failed to contact hub to set [score] score for [player.ckey]!")
@@ -44,7 +44,7 @@ SUBSYSTEM_DEF(medals)
if(!score || !hub_enabled)
return
var/scoreget = world.GetScores(player.ckey, score, config.medal_hub_address, config.medal_hub_password)
var/scoreget = world.GetScores(player.ckey, score, GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)
if(isnull(scoreget))
hub_enabled = FALSE
log_game("SCORE ERROR: Could not contact hub to get score. Score [score] for player [player.ckey].")
@@ -58,7 +58,7 @@ SUBSYSTEM_DEF(medals)
if(!medal || !hub_enabled)
return
if(isnull(world.GetMedal(medal, player, config.medal_hub_address, config.medal_hub_password)))
if(isnull(world.GetMedal(medal, player, GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)))
hub_enabled = FALSE
log_game("MEDAL ERROR: Could not contact hub to get medal [medal] for player [player.ckey]")
message_admins("Error! Failed to contact hub to get [medal] medal for [player.ckey]!")
@@ -68,7 +68,7 @@ SUBSYSTEM_DEF(medals)
/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player)
if(!player || !medal || !hub_enabled)
return
var/result = world.ClearMedal(medal, player, config.medal_hub_address, config.medal_hub_password)
var/result = world.ClearMedal(medal, player, GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)
switch(result)
if(null)
hub_enabled = FALSE
@@ -81,6 +81,6 @@ SUBSYSTEM_DEF(medals)
/datum/controller/subsystem/medals/proc/ClearScore(client/player)
if(isnull(world.SetScores(player.ckey, "", config.medal_hub_address, config.medal_hub_password)))
if(isnull(world.SetScores(player.ckey, "", GLOB.configuration.system.medal_hub_address, GLOB.configuration.system.medal_hub_password)))
log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.ckey].")
message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!")
+2
View File
@@ -9,6 +9,8 @@ SUBSYSTEM_DEF(mobs)
var/static/list/clients_by_zlevel[][]
var/static/list/dead_players_by_zlevel[][] = list(list()) // Needs to support zlevel 1 here, MaxZChanged only happens when CC is created and new_players can login before that.
var/static/list/cubemonkeys = list()
/// The amount of giant spiders that exist in the world. Used for mob capping.
var/giant_spiders = 0
/datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_living_list.len]")
+2 -2
View File
@@ -14,9 +14,9 @@ SUBSYSTEM_DEF(nightshift)
var/high_security_mode = FALSE
/datum/controller/subsystem/nightshift/Initialize()
if(!config.enable_night_shifts)
if(!GLOB.configuration.general.enable_night_shifts)
can_fire = FALSE
if(config.randomize_shift_time)
if(GLOB.configuration.general.randomise_shift_time)
GLOB.gametime_offset = rand(0, 23) HOURS
return ..()
+2 -2
View File
@@ -13,7 +13,7 @@ SUBSYSTEM_DEF(profiler)
..("F:[round(fetch_cost, 1)]ms | W:[round(write_cost, 1)]ms")
/datum/controller/subsystem/profiler/Initialize()
if(!config.auto_profile)
if(!GLOB.configuration.general.enable_auto_profiler)
StopProfiling() //Stop the early start profiler if we dont want it on in the config
flags |= SS_NO_FIRE
return ..()
@@ -22,7 +22,7 @@ SUBSYSTEM_DEF(profiler)
DumpFile()
/datum/controller/subsystem/profiler/Shutdown()
if(config.auto_profile)
if(GLOB.configuration.general.enable_auto_profiler)
DumpFile()
return ..()
+4 -3
View File
@@ -1,5 +1,4 @@
#define CALL_SHUTTLE_REASON_LENGTH 12
SUBSYSTEM_DEF(shuttle)
name = "Shuttle"
wait = 10
@@ -42,6 +41,8 @@ SUBSYSTEM_DEF(shuttle)
var/sold_atoms = ""
var/list/hidden_shuttle_turfs = list() //all turfs hidden from navigation computers associated with a list containing the image hiding them and the type of the turf they are pretending to be
var/list/hidden_shuttle_turf_images = list() //only the images from the above list
/// Default refuel delay
var/refuel_delay = 20 MINUTES
/datum/controller/subsystem/shuttle/Initialize(start_timeofday)
ordernum = rand(1,9000)
@@ -97,7 +98,7 @@ SUBSYSTEM_DEF(shuttle)
/datum/controller/subsystem/shuttle/proc/secondsToRefuel()
var/elapsed = world.time - SSticker.round_start_time
var/remaining = round((config.shuttle_refuel_delay - elapsed) / 10)
var/remaining = round((refuel_delay - elapsed) / 10)
return remaining > 0 ? remaining : 0
/datum/controller/subsystem/shuttle/proc/requestEvac(mob/user, call_reason)
@@ -115,7 +116,7 @@ SUBSYSTEM_DEF(shuttle)
emergency = backup_shuttle
if(secondsToRefuel())
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - config.shuttle_refuel_delay)/600))] minutes before trying again.")
to_chat(user, "The emergency shuttle is refueling. Please wait another [abs(round(((world.time - SSticker.round_start_time) - refuel_delay)/600))] minutes before trying again.")
return
switch(emergency.mode)
+2 -2
View File
@@ -6,7 +6,7 @@ SUBSYSTEM_DEF(statistics)
/datum/controller/subsystem/statistics/Initialize(start_timeofday)
if(!config.sql_enabled)
if(!SSdbcore.IsConnected())
flags |= SS_NO_FIRE // Disable firing if SQL is disabled
return ..()
@@ -18,7 +18,7 @@ SUBSYSTEM_DEF(statistics)
return
else
var/datum/db_query/statquery = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time) VALUES (:playercount, :admincount, NOW())",
"INSERT INTO legacy_population (playercount, admincount, time) VALUES (:playercount, :admincount, NOW())",
list(
"playercount" = length(GLOB.clients),
"admincount" = length(GLOB.admins)
+19 -13
View File
@@ -7,10 +7,12 @@ SUBSYSTEM_DEF(ticker)
runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME
offline_implications = "The game is no longer aware of when the round ends. Immediate server restart recommended."
/// Time the world started, relative to world.time
/// Time the game should start, relative to world.time
var/round_start_time = 0
/// Time that the round started
var/time_game_started = 0
/// Default timeout for if world.Reboot() doesnt have a time specified
var/const/restart_timeout = 600
var/const/restart_timeout = 75 SECONDS
/// Current status of the game. See code\__DEFINES\game.dm
var/current_state = GAME_STATE_STARTUP
/// Do we want to force-start as soon as we can
@@ -79,9 +81,9 @@ SUBSYSTEM_DEF(ticker)
switch(current_state)
if(GAME_STATE_STARTUP)
// This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails
round_start_time = world.time + (config.pregame_timestart * 10)
round_start_time = world.time + (GLOB.configuration.general.lobby_time SECONDS)
to_chat(world, "<B><span class='darkmblue'>Welcome to the pre-game lobby!</span></B>")
to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds")
to_chat(world, "Please, setup your character and select ready. Game will start in [GLOB.configuration.general.lobby_time] seconds")
current_state = GAME_STATE_PREGAME
fire() // TG says this is a good idea
for(var/mob/new_player/N in GLOB.player_list)
@@ -114,10 +116,10 @@ SUBSYSTEM_DEF(ticker)
if(world.time > next_autotransfer)
SSvote.autotransfer()
next_autotransfer = world.time + config.vote_autotransfer_interval
next_autotransfer = world.time + GLOB.configuration.vote.autotransfer_interval_time
var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked
if(config.continuous_rounds)
if(GLOB.configuration.gamemode.disable_certain_round_early_end)
mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result
else
game_finished |= mode.check_finished()
@@ -129,6 +131,8 @@ SUBSYSTEM_DEF(ticker)
auto_toggle_ooc(TRUE) // Turn it on
declare_completion()
addtimer(CALLBACK(src, .proc/call_reboot), 5 SECONDS)
if(GLOB.configuration.vote.enable_map_voting)
SSvote.initiate_vote("map", "the server", TRUE) // Start a map vote. Timing is a little tight here but we should be good.
/datum/controller/subsystem/ticker/proc/call_reboot()
if(mode.station_was_nuked)
@@ -147,7 +151,7 @@ SUBSYSTEM_DEF(ticker)
var/list/datum/game_mode/runnable_modes
if(GLOB.master_mode == "random" || GLOB.master_mode == "secret")
runnable_modes = config.get_runnable_modes()
runnable_modes = GLOB.configuration.gamemode.get_runnable_modes()
if(!length(runnable_modes))
to_chat(world, "<B>Unable to choose playable game mode.</B> Reverting to pre-game lobby.")
force_start = FALSE
@@ -155,9 +159,9 @@ SUBSYSTEM_DEF(ticker)
Master.SetRunLevel(RUNLEVEL_LOBBY)
return FALSE
if(GLOB.secret_force_mode != "secret")
var/datum/game_mode/M = config.pick_mode(GLOB.secret_force_mode)
var/datum/game_mode/M = GLOB.configuration.gamemode.pick_mode(GLOB.secret_force_mode)
if(M.can_start())
mode = config.pick_mode(GLOB.secret_force_mode)
mode = GLOB.configuration.gamemode.pick_mode(GLOB.secret_force_mode)
SSjobs.ResetOccupations()
if(!mode)
mode = pickweight(runnable_modes)
@@ -165,7 +169,7 @@ SUBSYSTEM_DEF(ticker)
var/mtype = mode.type
mode = new mtype
else
mode = config.pick_mode(GLOB.master_mode)
mode = GLOB.configuration.gamemode.pick_mode(GLOB.master_mode)
if(!mode.can_start())
to_chat(world, "<B>Unable to start [mode.name].</B> Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.")
@@ -264,10 +268,10 @@ SUBSYSTEM_DEF(ticker)
SSdiscord.send2discord_simple_noadmins("**\[Info]** Round has started")
auto_toggle_ooc(FALSE) // Turn it off
round_start_time = world.time
time_game_started = world.time
// Sets the auto shuttle vote to happen after the config duration
next_autotransfer = world.time + config.vote_autotransfer_initial
next_autotransfer = world.time + GLOB.configuration.vote.autotransfer_initial_time
for(var/mob/new_player/N in GLOB.mob_list)
if(N.client)
@@ -279,10 +283,12 @@ SUBSYSTEM_DEF(ticker)
if(playercount >= highpop_trigger)
log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - loading highpop job config")
SSjobs.LoadJobs("config/jobs_highpop.txt")
SSjobs.LoadJobs(TRUE)
else
log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config")
SSnightshift.check_nightshift()
#ifdef UNIT_TESTS
RunUnitTests()
#endif
@@ -214,14 +214,18 @@ SUBSYSTEM_DEF(tickets)
"Already Resolved" = "The problem has been resolved already.",
"Mentorhelp" = "Please redirect your question to Mentorhelp, as they are better experienced with these types of questions.",
"Happens Again" = "Thanks, let us know if it continues to happen.",
"Github Issue Report" = "To report a bug, please go to our <a href='[config.githuburl]'>Github page</a>. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends.",
"Clear Cache" = "To fix a blank screen, go to the 'Special Verbs' tab and press 'Reload UI Resources'. If that fails, clear your BYOND cache (instructions provided with 'Reload UI Resources'). If that still fails, please adminhelp again, stating you have already done the following." ,
"IC Issue" = "This is an In Character (IC) issue and will not be handled by admins. You could speak to Security, Internal Affairs, a Departmental Head, Nanotrasen Representetive, or any other relevant authority currently on station.",
"Reject" = "Reject",
"Man Up" = "Man Up",
"Appeal on the Forums" = "Appealing a ban must occur on the forums. Privately messaging, or adminhelping about your ban will not resolve it. To appeal your ban, please head to <a href='[config.banappeals]'>[config.banappeals]</a>"
)
if(GLOB.configuration.url.banappeals_url)
response_phrases["Appeal on the Forums"] = "Appealing a ban must occur on the forums. Privately messaging, or adminhelping about your ban will not resolve it. To appeal your ban, please head to <a href='[GLOB.configuration.url.banappeals_url]'>[GLOB.configuration.url.banappeals_url]</a>"
if(GLOB.configuration.url.github_url)
response_phrases["Github Issue Report"] = "To report a bug, please go to our <a href='[GLOB.configuration.url.github_url]'>Github page</a>. Then go to 'Issues'. Then 'New Issue'. Then fill out the report form. If the report would reveal current-round information, file it after the round ends."
var/sorted_responses = list()
for(var/key in response_phrases) //build a new list based on the short descriptive keys of the master list so we can send this as the input instead of the full paragraphs to the admin choosing which autoresponse
sorted_responses += key
+28 -28
View File
@@ -27,7 +27,7 @@ SUBSYSTEM_DEF(vote)
// Calculate how much time is remaining by comparing current time, to time of vote start,
// plus vote duration
time_remaining = round((started_time + config.vote_period - world.time)/10)
time_remaining = round((started_time + GLOB.configuration.vote.vote_time - world.time)/10)
if(time_remaining < 0)
result()
@@ -53,9 +53,9 @@ SUBSYSTEM_DEF(vote)
voting.Cut()
current_votes.Cut()
if(auto_muted && !config.ooc_allowed && !(config.auto_toggle_ooc_during_round && SSticker.current_state == GAME_STATE_PLAYING))
if(auto_muted && !GLOB.ooc_enabled && !(GLOB.configuration.general.auto_disable_ooc && SSticker.current_state == GAME_STATE_PLAYING))
auto_muted = 0
config.ooc_allowed = !( config.ooc_allowed )
GLOB.ooc_enabled = TRUE
to_chat(world, "<b>The OOC channel has been automatically enabled due to vote end.</b>")
log_admin("OOC was toggled automatically due to vote end.")
message_admins("OOC has been toggled on automatically.")
@@ -83,7 +83,7 @@ SUBSYSTEM_DEF(vote)
choices -= sorted_highest
choices = sorted_choices
//default-vote for everyone who didn't vote
if(!config.vote_no_default && choices.len)
if(!GLOB.configuration.vote.disable_default_vote && choices.len)
var/non_voters = (GLOB.clients.len - total_votes)
if(non_voters > 0)
if(mode == "restart")
@@ -196,7 +196,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/submit_vote(ckey, vote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
if(GLOB.configuration.vote.prevent_dead_voting && usr.stat == DEAD && !usr.client.holder)
return 0
if(current_votes[ckey])
choices[choices[current_votes[ckey]]]--
@@ -210,7 +210,7 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, code_invoked = FALSE)
if(!mode)
if(started_time != null && !check_rights(R_ADMIN))
var/next_allowed_time = (started_time + config.vote_delay)
var/next_allowed_time = (started_time + GLOB.configuration.vote.vote_delay)
if(next_allowed_time > world.time)
return 0
@@ -221,7 +221,7 @@ SUBSYSTEM_DEF(vote)
if("gamemode")
if(SSticker.current_state >= 2)
return 0
choices.Add(config.votable_modes)
choices.Add(GLOB.configuration.gamemode.votable_modes)
if("crew_transfer")
if(check_rights(R_ADMIN|R_MOD))
if(SSticker.current_state <= 2)
@@ -264,33 +264,33 @@ SUBSYSTEM_DEF(vote)
log_vote(text)
to_chat(world, {"<font color='purple'><b>[text]</b>
<a href='?src=[UID()];vote=open'>Click here or type vote to place your vote.</a>
You have [config.vote_period/10] seconds to vote.</font>"})
You have [GLOB.configuration.vote.vote_time / 10] seconds to vote.</font>"})
switch(vote_type)
if("crew_transfer", "gamemode", "custom", "map")
SEND_SOUND(world, sound('sound/ambience/alarm4.ogg'))
if(mode == "gamemode" && SSticker.ticker_going)
SSticker.ticker_going = FALSE
to_chat(world, "<font color='red'><b>Round start has been delayed.</b></font>")
if(mode == "crew_transfer" && config.ooc_allowed)
auto_muted = 1
config.ooc_allowed = !( config.ooc_allowed )
if(mode == "crew_transfer" && GLOB.ooc_enabled)
auto_muted = TRUE
GLOB.ooc_enabled = FALSE
to_chat(world, "<b>The OOC channel has been automatically disabled due to a crew transfer vote.</b>")
log_admin("OOC was toggled automatically due to crew_transfer vote.")
message_admins("OOC has been toggled off automatically.")
if(mode == "gamemode" && config.ooc_allowed)
auto_muted = 1
config.ooc_allowed = !( config.ooc_allowed )
if(mode == "gamemode" && GLOB.ooc_enabled)
auto_muted = TRUE
GLOB.ooc_enabled = FALSE
to_chat(world, "<b>The OOC channel has been automatically disabled due to the gamemode vote.</b>")
log_admin("OOC was toggled automatically due to gamemode vote.")
message_admins("OOC has been toggled off automatically.")
if(mode == "custom" && config.ooc_allowed)
auto_muted = 1
config.ooc_allowed = !( config.ooc_allowed )
if(mode == "custom" && GLOB.ooc_enabled)
auto_muted = TRUE
GLOB.ooc_enabled = FALSE
to_chat(world, "<b>The OOC channel has been automatically disabled due to a custom vote.</b>")
log_admin("OOC was toggled automatically due to custom vote.")
message_admins("OOC has been toggled off automatically.")
time_remaining = round(config.vote_period/10)
time_remaining = round(GLOB.configuration.vote.vote_time / 10)
return 1
return 0
@@ -315,25 +315,25 @@ SUBSYSTEM_DEF(vote)
else
dat += "<div id='vote_div'><h2>Start a vote:</h2><hr><ul><li>"
//restart
if(admin || config.allow_vote_restart)
if(admin || GLOB.configuration.vote.allow_restart_votes)
dat += "<a href='?src=[UID()];vote=restart'>Restart</a>"
else
dat += "<font color='grey'>Restart (Disallowed)</font>"
dat += "</li><li>"
if(admin || config.allow_vote_restart)
if(admin || GLOB.configuration.vote.allow_restart_votes)
dat += "<a href='?src=[UID()];vote=crew_transfer'>Crew Transfer</a>"
else
dat += "<font color='grey'>Crew Transfer (Disallowed)</font>"
if(admin)
dat += "\t(<a href='?src=[UID()];vote=toggle_restart'>[config.allow_vote_restart?"Allowed":"Disallowed"]</a>)"
dat += "\t(<a href='?src=[UID()];vote=toggle_restart'>[GLOB.configuration.vote.allow_restart_votes ? "Allowed" : "Disallowed"]</a>)"
dat += "</li><li>"
//gamemode
if(admin || config.allow_vote_mode)
if(admin || GLOB.configuration.vote.allow_mode_votes)
dat += "<a href='?src=[UID()];vote=gamemode'>GameMode</a>"
else
dat += "<font color='grey'>GameMode (Disallowed)</font>"
if(admin)
dat += "\t(<a href='?src=[UID()];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
dat += "\t(<a href='?src=[UID()];vote=toggle_gamemode'>[GLOB.configuration.vote.allow_mode_votes ? "Allowed" : "Disallowed"]</a>)"
dat += "</li><li>"
if(admin)
@@ -391,21 +391,21 @@ SUBSYSTEM_DEF(vote)
reset()
if("toggle_restart")
if(admin)
config.allow_vote_restart = !config.allow_vote_restart
GLOB.configuration.vote.allow_restart_votes = !GLOB.configuration.vote.allow_restart_votes
if("toggle_gamemode")
if(admin)
config.allow_vote_mode = !config.allow_vote_mode
GLOB.configuration.vote.allow_mode_votes = !GLOB.configuration.vote.allow_mode_votes
if("restart")
if(config.allow_vote_restart || admin)
if(GLOB.configuration.vote.allow_restart_votes || admin)
initiate_vote("restart",usr.key)
if("gamemode")
if(config.allow_vote_mode || admin)
if(GLOB.configuration.vote.allow_mode_votes || admin)
initiate_vote("gamemode",usr.key)
if("map")
if(admin)
initiate_vote("map", usr.key)
if("crew_transfer")
if(config.allow_vote_restart || admin)
if(GLOB.configuration.vote.allow_restart_votes || admin)
initiate_vote("crew_transfer",usr.key)
if("custom")
if(admin)
+1 -1
View File
@@ -28,7 +28,7 @@
return
switch(controller)
if("Configuration")
debug_variables(config)
debug_variables(GLOB.configuration)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Debug Config")
if("pAI")
debug_variables(GLOB.paiController)