Changes job config format to use TOML, and add tools for migrating existing format (#70199)

This commit is contained in:
san7890
2022-10-18 15:51:24 -06:00
committed by GitHub
parent 468809e38f
commit 901662f59b
49 changed files with 574 additions and 61 deletions

View File

@@ -160,6 +160,15 @@
else
CRASH(output["content"])
#define rustg_raw_toml_encode(value) json_decode(call(RUST_G, "toml_encode")(json_encode(value)))
/proc/rustg_toml_encode(value)
var/list/output = rustg_raw_toml_encode(value)
if (output["success"])
return output["content"]
else
CRASH(output["content"])
#define rustg_url_encode(text) call(RUST_G, "url_encode")("[text]")
#define rustg_url_decode(text) call(RUST_G, "url_decode")(text)

View File

@@ -88,6 +88,8 @@
LoadMOTD()
LoadPolicy()
LoadChatFilter()
if(CONFIG_GET(flag/load_jobs_from_txt))
validate_job_config()
loaded = TRUE
@@ -468,6 +470,31 @@ Example config:
var/regex_filter = whitespace_split != "" ? "([whitespace_split]|[word_bounds])" : word_bounds
return regex(regex_filter, "i")
/// Check to ensure that the jobconfig is valid/in-date.
/datum/controller/configuration/proc/validate_job_config()
var/config_toml = "[directory]/jobconfig.toml"
var/config_txt = "[directory]/jobs.txt"
var/message = "Notify Server Operators: "
log_config("Validating config file jobconfig.toml...")
if(!fexists(file(config_toml)))
SSjob.legacy_mode = TRUE
message += "jobconfig.toml not found, falling back to legacy mode (using jobs.txt). To surpress this warning, generate a jobconfig.toml by running the verb 'Generate Job Configuration' in the Server tab.\n\
From there, you can then add it to the /config folder of your server to have it take effect for future rounds."
if(!fexists(file(config_txt)))
message += "\n\nFailed to set up legacy mode, jobs.txt not found! Codebase defaults will be used. If you do not wish to use this system, please disable it by commenting out the LOAD_JOBS_FROM_TXT config flag."
log_config(message)
DelayedMessageAdmins(span_notice(message))
return
var/list/result = rustg_raw_read_toml_file(config_toml)
if(!result["success"])
message += "The job config (jobconfig.toml) is not configured correctly! [result["content"]]"
log_config(message)
DelayedMessageAdmins(span_notice(message))
//Message admins when you can.
/datum/controller/configuration/proc/DelayedMessageAdmins(text)
addtimer(CALLBACK(GLOBAL_PROC, /proc/message_admins, text), 0)

View File

@@ -62,12 +62,26 @@ SUBSYSTEM_DEF(job)
/// Dictionary that maps job priorities to low/medium/high. Keys have to be number-strings as assoc lists cannot be indexed by integers. Set in setup_job_lists.
var/list/job_priorities_to_strings
/// Are we using the old job config system (txt) or the new job config system (TOML)? IF we are going to use the txt file, then we are in "legacy mode", and this will flip to TRUE.
var/legacy_mode = FALSE
/// This is just the message we prepen and put into all of the config files to ensure documentation. We use this in more than one place, so let's put it in the SS to make life a bit easier.
var/config_documentation = "## This is the configuration file for the job system.\n## This will only be enabled when the config flag LOAD_JOBS_FROM_TXT is enabled.\n\
## We use a system of keys here that directly correlate to the job, just to ensure they don't desync if we choose to change the name of a job.\n## You are able to change (as of now) four different variables in this file.\n\
## Total Positions are how many job slots you get in a shift, Spawn Positions are how many you get that load in at spawn. If you set this to -1, it is unrestricted.\n## Playtime Requirements is in minutes, and the job will unlock when a player reaches that amount of time.\n\
## However, that can be superseded by Required Account Age, which is a time in days that you need to have had an account on the server for.\n## As time goes on, more config options may be added to this file.\n\
## You can use the admin verb 'Generate Job Configuration' in-game to auto-regenerate this config as a downloadable file without having to manually edit this file if we add more jobs or more things you can edit here.\n\
## It will always respect prior-existing values in the config, but will appropriately add more fields when they generate.\n## It's strongly advised you create your own version of this file rather than use the one provisioned on the codebase.\n\n\
## The game will not read any line that is commented out with a '#', as to allow you to defer to codebase defaults.\n## If you want to override the codebase values, add the value and then uncomment that line by removing the # from the job key's name.\n\
## Ensure that the key is flush, do not introduce any whitespaces when you uncomment a key. For example:\n## \"# Total Positions\" should always be changed to \"Total Positions\", no additional spacing. \n\
## Best of luck editing!\n"
/datum/controller/subsystem/job/Initialize()
setup_job_lists()
if(!length(all_occupations))
SetupOccupations()
if(CONFIG_GET(flag/load_jobs_from_txt))
LoadJobs()
load_jobs_from_config()
set_overflow_role(CONFIG_GET(string/overflow_job))
return SS_INIT_SUCCESS
@@ -590,14 +604,203 @@ SUBSYSTEM_DEF(job)
else //We ran out of spare locker spawns!
break
#define TOTAL_POSITIONS "Total Positions"
#define SPAWN_POSITIONS "Spawn Positions"
#define PLAYTIME_REQUIREMENTS "Playtime Requirements"
#define REQUIRED_ACCOUNT_AGE "Required Account Age"
/datum/controller/subsystem/job/proc/LoadJobs()
var/jobstext = file2text("[global.config.directory]/jobs.txt")
for(var/datum/job/job as anything in joinable_occupations)
var/regex/jobs = new("[job.title]=(-1|\\d+),(-1|\\d+)")
jobs.Find(jobstext)
job.total_positions = text2num(jobs.group[1])
job.spawn_positions = text2num(jobs.group[2])
/// Called in jobs subsystem initialize if LOAD_JOBS_FROM_TXT config flag is set: reads jobconfig.toml (or if in legacy mode, jobs.txt) to set all of the datum's values to what the server operator wants.
/datum/controller/subsystem/job/proc/load_jobs_from_config()
var/toml_file = "[global.config.directory]/jobconfig.toml"
if(!legacy_mode) // this flag is set during the setup of SSconfig, and all warnings were handled there.
var/job_config = rustg_read_toml_file(toml_file)
for(var/datum/job/occupation as anything in joinable_occupations)
var/job_title = occupation.title
var/job_key = occupation.config_tag
if(!job_config[job_key]) // Job isn't listed, skip it.
message_admins(span_notice("[job_title] (with config key [job_key]) is missing from jobconfig.toml! Using codebase defaults.")) // List both job_title and job_key in case they de-sync over time.
continue
// If the value is commented out, we assume that the server operate did not want to override the codebase default values, so we skip it.
var/default_positions = job_config[job_key][TOTAL_POSITIONS]
var/starting_positions = job_config[job_key][SPAWN_POSITIONS]
var/playtime_requirements = job_config[job_key][PLAYTIME_REQUIREMENTS]
var/required_account_age = job_config[job_key][REQUIRED_ACCOUNT_AGE]
if(default_positions)
occupation.total_positions = default_positions
if(starting_positions)
occupation.spawn_positions = starting_positions
if(playtime_requirements)
occupation.exp_requirements = playtime_requirements
if(required_account_age)
occupation.minimal_player_age = required_account_age
return
else // legacy mode, so just run the old parser.
var/jobsfile = file("[global.config.directory]/jobs.txt")
if(!fexists(jobsfile)) // sanity with a trace
stack_trace("Despite SSconfig setting SSjob.legacy_mode to TRUE, jobs.txt was not found in the config directory! Something has gone terribly wrong!")
return
var/jobstext = file2text(jobsfile)
for(var/datum/job/occupation as anything in joinable_occupations)
var/regex/parser = new("[occupation.title]=(-1|\\d+),(-1|\\d+)")
parser.Find(jobstext)
occupation.total_positions = text2num(parser.group[1])
occupation.spawn_positions = text2num(parser.group[2])
/// Called from an admin debug verb that generates the jobconfig.toml file and then allows the end user to download it to their machine. Returns TRUE if a file is successfully generated, FALSE otherwise.
/datum/controller/subsystem/job/proc/generate_config(mob/user)
var/toml_file = "[global.config.directory]/jobconfig.toml"
var/jobstext = "[global.config.directory]/jobs.txt"
var/list/file_data = list()
config_documentation = initial(config_documentation) // Reset to default juuuuust in case.
if(fexists(file(toml_file)))
to_chat(src, span_notice("Generating new jobconfig.toml, pulling from the old config settings."))
if(!regenerate_job_config(user))
return FALSE
return TRUE
if(fexists(file(jobstext))) // Generate the new TOML format, migrating from the text format.
to_chat(user, span_notice("Found jobs.txt in config directory! Generating jobconfig.toml from it."))
jobstext = file2text(file(jobstext)) // walter i'm dying (get the file from the string, then parse it into a larger text string)
config_documentation += "\n\n## This TOML was migrated from jobs.txt. All variables are COMMENTED and will not load by default! Please verify to ensure that they are correct, and uncomment the key as you want, comparing it to the old config.\n\n" // small warning
for(var/datum/job/occupation as anything in joinable_occupations)
var/job_key = occupation.config_tag
var/regex/parser = new("[occupation.title]=(-1|\\d+),(-1|\\d+)") // TXT system used the occupation's name, we convert it to the new config_key system here.
parser.Find(jobstext)
var/default_positions = text2num(parser.group[1])
var/starting_positions = text2num(parser.group[2])
// Playtime Requirements and Required Account Age are new and we want to see it migrated, so we will just pull codebase defaults for them.
// Remember, every time we write the TOML from scratch, we want to have it commented out by default to ensure that the server operator is knows that they codebase defaults when they remove the comment.
file_data["[job_key]"] = list(
"# [PLAYTIME_REQUIREMENTS]" = occupation.exp_requirements,
"# [REQUIRED_ACCOUNT_AGE]" = occupation.minimal_player_age,
"# [TOTAL_POSITIONS]" = default_positions,
"# [SPAWN_POSITIONS]" = starting_positions,
)
if(!export_toml(user, file_data))
return FALSE
return TRUE
else // Generate the new TOML format, using codebase defaults.
to_chat(user, span_notice("Generating new jobconfig.toml, using codebase defaults."))
for(var/datum/job/occupation as anything in joinable_occupations)
var/job_key = occupation.config_tag
// Remember, every time we write the TOML from scratch, we want to have it commented out by default to ensure that the server operator is knows that they override codebase defaults when they remove the comment.
// Having comments mean that we allow server operators to defer to codebase standards when they deem acceptable. They must uncomment to override the codebase default.
if(is_assistant_job(occupation)) // there's a concession made in jobs.txt that we should just rapidly account for here I KNOW I KNOW.
file_data["[job_key]"] = list(
"# [TOTAL_POSITIONS]" = -1,
"# [SPAWN_POSITIONS]" = -1,
"# [PLAYTIME_REQUIREMENTS]" = occupation.exp_requirements,
"# [REQUIRED_ACCOUNT_AGE]" = occupation.minimal_player_age,
)
continue
// Generate new config from codebase defaults.
file_data["[job_key]"] = list(
"# [TOTAL_POSITIONS]" = occupation.total_positions,
"# [SPAWN_POSITIONS]" = occupation.spawn_positions,
"# [PLAYTIME_REQUIREMENTS]" = occupation.exp_requirements,
"# [REQUIRED_ACCOUNT_AGE]" = occupation.minimal_player_age,
)
if(!export_toml(user, file_data))
return FALSE
return TRUE
/// If we add a new job or more fields to config a job with, quickly spin up a brand new config that inherits all of your old settings, but adds the new job with codebase defaults. Returns TRUE if a file is successfully generated, FALSE otherwise.
/datum/controller/subsystem/job/proc/regenerate_job_config(mob/user)
var/toml_file = "[global.config.directory]/jobconfig.toml"
var/list/file_data = list()
if(!fexists(file(toml_file))) // You need an existing (valid) TOML for this to work. Sanity check if someone calls this directly instead of through 'Generate Job Configuration' verb.
to_chat(user, span_notice("No jobconfig.toml found in the config folder! If this is not expected, please notify a server operator or coders. You may need to generate a new config file by running 'Generate Job Configuration' from the Server tab."))
return FALSE
var/job_config = rustg_read_toml_file(toml_file)
for(var/datum/job/occupation as anything in joinable_occupations)
var/job_name = occupation.title
var/job_key = occupation.config_tag
var/default_positions = job_config[job_key][TOTAL_POSITIONS]
var/starting_positions = job_config[job_key][SPAWN_POSITIONS]
var/playtime_requirements = job_config[job_key][PLAYTIME_REQUIREMENTS]
var/required_account_age = job_config[job_key][REQUIRED_ACCOUNT_AGE]
// When we regenerate, we want to make sure commented stuff stays commented, but we also want to migrate information that remains uncommented. So, let's make sure we keep that pattern.
if(job_config["[job_key]"]) // Let's see if any data for this job exists.
if(file_data["[job_key]"]) // Sanity, let's just make sure we don't overwrite anything or add any dupe keys. We also unit test for this, but eh, you never know sometimes.
stack_trace("We were about to over-write a job key that already exists in file_data while generating a new jobconfig.toml! This should not happen! Verify you do not have any duplicate job keys in your codebase!")
continue
if(default_positions) // If the variable exists, we want to ensure it migrated into the new TOML uncommented, to allow for flush migration.
file_data["[job_key]"] += list(
TOTAL_POSITIONS = default_positions,
)
else // If we can't find anything for this variable, then we just throw in the codebase default with it commented out.
file_data["[job_key]"] += list(
"# [TOTAL_POSITIONS]" = occupation.total_positions,
)
if(starting_positions) // Same pattern as above.
file_data["[job_key]"] += list(
SPAWN_POSITIONS = starting_positions,
)
else
file_data["[job_key]"] += list(
"# [SPAWN_POSITIONS]" = occupation.spawn_positions,
)
if(playtime_requirements) // Same pattern as above.
file_data["[job_key]"] += list(
PLAYTIME_REQUIREMENTS = playtime_requirements,
)
else
file_data["[job_key]"] += list(
"# [PLAYTIME_REQUIREMENTS]" = occupation.exp_requirements,
)
if(required_account_age) // Same pattern as above.
file_data["[job_key]"] += list(
REQUIRED_ACCOUNT_AGE = required_account_age,
)
else
file_data["[job_key]"] += list(
"# [REQUIRED_ACCOUNT_AGE]" = occupation.minimal_player_age,
)
continue
else
to_chat(user, span_notice("New job [job_name] (using key [job_key]) detected! Adding to jobconfig.toml using default codebase values..."))
// Commented out keys here in case server operators wish to defer to codebase defaults.
file_data["[job_key]"] = list(
"# [TOTAL_POSITIONS]" = occupation.total_positions,
"# [SPAWN_POSITIONS]" = occupation.spawn_positions,
"# [PLAYTIME_REQUIREMENTS]" = occupation.exp_requirements,
"# [REQUIRED_ACCOUNT_AGE]" = occupation.minimal_player_age,
)
if(!export_toml(user, file_data))
return FALSE
return TRUE
/// Proc that we call to generate a new jobconfig.toml file and send it to the requesting client. Returns TRUE if a file is successfully generated.
/datum/controller/subsystem/job/proc/export_toml(mob/user, data)
var/file_location = "data/jobconfig.toml" // store it in the data folder server-side so we can FTP it to the client.
var/payload = "[config_documentation]\n[rustg_toml_encode(data)]"
rustg_file_write(payload, file_location)
DIRECT_OUTPUT(user, ftp(file(file_location), "jobconfig.toml"))
return TRUE
#undef TOTAL_POSITIONS
#undef SPAWN_POSITIONS
#undef PLAYTIME_REQUIREMENTS
#undef REQUIRED_ACCOUNT_AGE
/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
for(var/datum/job/job as anything in joinable_occupations)

View File

@@ -142,7 +142,8 @@ GLOBAL_PROTECT(admin_verbs_server)
/client/proc/panicbunker,
/client/proc/toggle_interviews,
/client/proc/toggle_hub,
/client/proc/toggle_cdn
/client/proc/toggle_cdn,
/client/proc/generate_job_config,
)
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
GLOBAL_PROTECT(admin_verbs_debug)

View File

@@ -0,0 +1,19 @@
/// Verbs created to help server operators with generating certain config files.
/client/proc/generate_job_config()
set name = "Generate Job Configuration"
set category = "Server"
set desc = "Generate a job configuration (jobconfig.toml) file for the server. If TOML file already exists, will re-generate it based off the already existing config values. Will migrate from the old jobs.txt format if necessary."
if(!check_rights(R_SERVER))
return
if(tgui_alert(usr, "This verb is not at all useful if you are not a server operator with access to the configuration folder. Do you wish to proceed?", "Generate jobconfig.toml for download", list("Yes", "No")) != "Yes")
return
if(SSjob.generate_config(usr))
to_chat(usr, span_notice("Job configuration file generated. Download prompt should appear now."))
else
to_chat(usr, span_warning("Job configuration file could not be generated. Check the server logs / runtimes / above warning messages for more information."))
SSblackbox.record_feedback("tally", "admin_verb", 1, "Generate Job Configuration") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!

View File

@@ -123,6 +123,10 @@
/// Does this job ignore human authority?
var/ignore_human_authority = FALSE
/// String key to track any variables we want to tie to this job in config, so we can avoid using the job title. We CAPITALIZE it in order to ensure it's unique and resistant to trivial formatting changes.
/// You'll probably break someone's config if you change this, so it's best to not to.
var/config_tag = ""
/datum/job/New()
. = ..()

View File

@@ -22,6 +22,7 @@
random_spawns_possible = FALSE
job_flags = JOB_NEW_PLAYER_JOINABLE | JOB_EQUIP_RANK | JOB_BOLD_SELECT_TEXT
var/do_special_check = TRUE
config_tag = "AI"
/datum/job/ai/after_spawn(mob/living/spawned, client/player_client)

View File

@@ -34,6 +34,7 @@ Assistant
job_flags = JOB_ANNOUNCE_ARRIVAL | JOB_CREW_MANIFEST | JOB_EQUIP_RANK | JOB_CREW_MEMBER | JOB_NEW_PLAYER_JOINABLE | JOB_REOPEN_ON_ROUNDSTART_LOSS | JOB_ASSIGN_QUIRKS | JOB_CAN_BE_INTERN
rpg_title = "Lout"
config_tag = "ASSISTANT"
/datum/outfit/job/assistant
name = JOB_ASSISTANT

View File

@@ -10,6 +10,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "ATMOSPHERIC_TECHNICIAN"
outfit = /datum/outfit/job/atmos
plasmaman_outfit = /datum/outfit/plasmaman/atmospherics

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "BARTENDER"
outfit = /datum/outfit/job/bartender
plasmaman_outfit = /datum/outfit/plasmaman/bar

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "BOTANIST"
outfit = /datum/outfit/job/botanist
plasmaman_outfit = /datum/outfit/plasmaman/botany

View File

@@ -16,6 +16,7 @@
exp_required_type = EXP_TYPE_CREW
exp_required_type_department = EXP_TYPE_COMMAND
exp_granted_type = EXP_TYPE_CREW
config_tag = "CAPTAIN"
outfit = /datum/outfit/job/captain
plasmaman_outfit = /datum/outfit/plasmaman/captain

View File

@@ -10,6 +10,7 @@
supervisors = SUPERVISOR_QM
selection_color = "#dcba97"
exp_granted_type = EXP_TYPE_CREW
config_tag = "CARGO_TECHNICIAN"
outfit = /datum/outfit/job/cargo_tech
plasmaman_outfit = /datum/outfit/plasmaman/cargo

View File

@@ -9,6 +9,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "CHAPLAIN"
outfit = /datum/outfit/job/chaplain
plasmaman_outfit = /datum/outfit/plasmaman/chaplain

View File

@@ -11,6 +11,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "CHEMIST"
outfit = /datum/outfit/job/chemist
plasmaman_outfit = /datum/outfit/plasmaman/chemist

View File

@@ -16,6 +16,7 @@
exp_required_type = EXP_TYPE_CREW
exp_required_type_department = EXP_TYPE_ENGINEERING
exp_granted_type = EXP_TYPE_CREW
config_tag = "CHIEF_ENGINEER"
outfit = /datum/outfit/job/ce
plasmaman_outfit = /datum/outfit/plasmaman/chief_engineer

View File

@@ -16,6 +16,7 @@
exp_required_type = EXP_TYPE_CREW
exp_required_type_department = EXP_TYPE_MEDICAL
exp_granted_type = EXP_TYPE_CREW
config_tag = "CHIEF_MEDICAL_OFFICER"
outfit = /datum/outfit/job/cmo
plasmaman_outfit = /datum/outfit/plasmaman/chief_medical_officer

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "CLOWN"
outfit = /datum/outfit/job/clown
plasmaman_outfit = /datum/outfit/plasmaman/clown

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "COOK"
var/cooks = 0 //Counts cooks amount
outfit = /datum/outfit/job/cook

View File

@@ -8,6 +8,7 @@
spawn_positions = 1
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
config_tag = "CURATOR"
exp_granted_type = EXP_TYPE_CREW
outfit = /datum/outfit/job/curator

View File

@@ -12,6 +12,7 @@
exp_requirements = 120
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "CYBORG"
display_order = JOB_DISPLAY_ORDER_CYBORG

View File

@@ -13,6 +13,7 @@
exp_requirements = 300
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "DETECTIVE"
outfit = /datum/outfit/job/detective
plasmaman_outfit = /datum/outfit/plasmaman/detective

View File

@@ -10,6 +10,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "GENETICIST"
outfit = /datum/outfit/job/geneticist
plasmaman_outfit = /datum/outfit/plasmaman/genetics

View File

@@ -16,6 +16,7 @@
exp_required_type = EXP_TYPE_CREW
exp_required_type_department = EXP_TYPE_SERVICE
exp_granted_type = EXP_TYPE_CREW
config_tag = "HEAD_OF_PERSONNEL"
outfit = /datum/outfit/job/hop
plasmaman_outfit = /datum/outfit/plasmaman/head_of_personnel

View File

@@ -16,6 +16,7 @@
exp_required_type = EXP_TYPE_CREW
exp_required_type_department = EXP_TYPE_SECURITY
exp_granted_type = EXP_TYPE_CREW
config_tag = "HEAD_OF_SECURITY"
outfit = /datum/outfit/job/hos
plasmaman_outfit = /datum/outfit/plasmaman/head_of_security

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "JANITOR"
outfit = /datum/outfit/job/janitor
plasmaman_outfit = /datum/outfit/plasmaman/janitor

View File

@@ -9,6 +9,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "LAWYER"
outfit = /datum/outfit/job/lawyer
plasmaman_outfit = /datum/outfit/plasmaman/bar

View File

@@ -9,6 +9,7 @@
supervisors = SUPERVISOR_CMO
selection_color = "#ffeef0"
exp_granted_type = EXP_TYPE_CREW
config_tag = "MEDICAL_DOCTOR"
outfit = /datum/outfit/job/doctor
plasmaman_outfit = /datum/outfit/plasmaman/medical

View File

@@ -8,6 +8,7 @@
supervisors = SUPERVISOR_HOP
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "MIME"
outfit = /datum/outfit/job/mime
plasmaman_outfit = /datum/outfit/plasmaman/mime

View File

@@ -9,6 +9,7 @@
supervisors = SUPERVISOR_CMO
selection_color = "#ffeef0"
exp_granted_type = EXP_TYPE_CREW
config_tag = "PARAMEDIC"
outfit = /datum/outfit/job/paramedic
plasmaman_outfit = /datum/outfit/plasmaman/paramedic

View File

@@ -9,6 +9,7 @@
selection_color = "#ffe1c3"
exp_granted_type = EXP_TYPE_CREW
paycheck = PAYCHECK_LOWER
config_tag = "PRISONER"
outfit = /datum/outfit/job/prisoner
plasmaman_outfit = /datum/outfit/plasmaman/prisoner

View File

@@ -9,6 +9,7 @@
supervisors = "the Head of Personnel and the Chief Medical Officer"
selection_color = "#bbe291"
exp_granted_type = EXP_TYPE_CREW
config_tag = "PSYCHOLOGIST"
outfit = /datum/outfit/job/psychologist
plasmaman_outfit = /datum/outfit/plasmaman/psychologist

View File

@@ -13,6 +13,7 @@
selection_color = "#d7b088"
exp_required_type_department = EXP_TYPE_SUPPLY
exp_granted_type = EXP_TYPE_CREW
config_tag = "QUARTERMASTER"
outfit = /datum/outfit/job/quartermaster
plasmaman_outfit = /datum/outfit/plasmaman/cargo

View File

@@ -17,6 +17,7 @@
exp_requirements = 180
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "RESEARCH_DIRECTOR"
outfit = /datum/outfit/job/rd
plasmaman_outfit = /datum/outfit/plasmaman/research_director

View File

@@ -11,6 +11,7 @@
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
bounty_types = CIV_JOB_ROBO
config_tag = "ROBOTICIST"
outfit = /datum/outfit/job/roboticist
plasmaman_outfit = /datum/outfit/plasmaman/robotics

View File

@@ -10,6 +10,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "SCIENTIST"
outfit = /datum/outfit/job/scientist
plasmaman_outfit = /datum/outfit/plasmaman/science

View File

@@ -13,6 +13,7 @@
exp_requirements = 300
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "SECURITY_OFFICER"
outfit = /datum/outfit/job/security
plasmaman_outfit = /datum/outfit/plasmaman/security

View File

@@ -9,6 +9,7 @@
supervisors = SUPERVISOR_QM
selection_color = "#dcba97"
exp_granted_type = EXP_TYPE_CREW
config_tag = "SHAFT_MINER"
outfit = /datum/outfit/job/miner
plasmaman_outfit = /datum/outfit/plasmaman/mining

View File

@@ -11,6 +11,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "STATION_ENGINEER"
outfit = /datum/outfit/job/engineer
plasmaman_outfit = /datum/outfit/plasmaman/engineering

View File

@@ -11,6 +11,7 @@
exp_requirements = 60
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "VIROLOGIST"
outfit = /datum/outfit/job/virologist
plasmaman_outfit = /datum/outfit/plasmaman/viro

View File

@@ -14,6 +14,7 @@
exp_requirements = 300
exp_required_type = EXP_TYPE_CREW
exp_granted_type = EXP_TYPE_CREW
config_tag = "WARDEN"
outfit = /datum/outfit/job/warden
plasmaman_outfit = /datum/outfit/plasmaman/warden

View File

@@ -182,6 +182,7 @@
#include "timer_sanity.dm"
#include "traitor.dm"
#include "unit_test.dm"
#include "verify_config_tags.dm"
#include "wizard_loadout.dm"
#ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
#include "find_reference_sanity.dm"

View File

@@ -0,0 +1,31 @@
/// Unit Test to ensure that all config tags that we associate to all jobs in SSjob.joinable_occupations are valid.
/datum/unit_test/verify_config_tags
/datum/unit_test/verify_config_tags/Run()
var/job_tag
var/list/collected_tags = list()
var/number_of_jobs = length(SSjob.joinable_occupations)
var/number_of_keys
// I CREATED THE NEW SYSTEM TO GET AWAY FROM REGEXING SHIT BUT I AM STUCK IN THIS FILTHY MEAT CAGE FUCK
// Check for any whitespace in a config tag.
var/regex/tag_regex_whitespace = new("\\s")
// Check to ensure that no config tag has lowercase characters (enforce SCREAMING_SNAKE_CASE).
var/regex/tag_regex_lowercase = new("\[a-z\]+")
for(var/datum/job/occupation as anything in SSjob.joinable_occupations)
job_tag = occupation.config_tag
TEST_ASSERT_NOTEQUAL(job_tag, "", "Job [occupation.title] has no config_tag!") // The base job datum has an empty string, so it's likely that we forgot to give it a unique config tag in the first place.
if(tag_regex_whitespace.Find(job_tag)) // regex Find() passes 0 and not null, so we can't do TEST_ASSERT_NOTNULL
TEST_FAIL("Error for [occupation]: Job [occupation.title] has a config_tag [job_tag] with whitespace in it! Please remove the whitespace (use SCREAMING_SNAKE_CASE rules).")
if(tag_regex_lowercase.Find(job_tag)) // Lint for anything undercased.
TEST_FAIL("Error for [occupation]: The config tag [job_tag], for the job [occupation.title] contains lowercase characters. Please change it to SCREAMING_SNAKE_CASE.")
if(job_tag in collected_tags)
TEST_FAIL("Error for [occupation]: The config tag [job_tag] is used by multiple jobs! Found as a duplicate on job [occupation.title]! Please ensure that each job has a unique config tag.")
else
collected_tags += job_tag
number_of_keys = length(collected_tags)
TEST_ASSERT_EQUAL(number_of_keys, number_of_jobs, "Mismatch between the number of joinable occupations: [number_of_jobs] against the number of unique config tags: [number_of_keys]!")

View File

@@ -296,7 +296,11 @@ CHECK_RANDOMIZER
# INVOKE_YOUTUBEDL youtube-dl
## In-game features
##Toggle for having jobs load up from the .txt
## Toggle for having jobs load up from the jobconfig.toml (or jobs.txt) file. Will use codebase defaults if commented out.
## Jobs have specific "keys" tied to their in-game datums, those should sync up otherwise it will fail to load.
## Setting Total/Spawn Positions to -1 will open unlimited join slots for it.
## Playtime Requirements is in minutes, Required Account Age is in days.
#LOAD_JOBS_FROM_TXT
## Uncomment this to forbid admins from possessing the singularity.

227
config/jobconfig.toml Normal file
View File

@@ -0,0 +1,227 @@
## This is the configuration file for the job system.
## This will only be enabled when the config flag LOAD_JOBS_FROM_TXT is enabled.
## We use a system of keys here that directly correlate to the job, just to ensure they don't desync if we choose to change the name of a job.
## You are able to change (as of now) four different variables in this file.
## Total Positions are how many job slots you get in a shift, Spawn Positions are how many you get that load in at spawn. If you set this to -1, it is unrestricted.
## Playtime Requirements is in minutes, and the job will unlock when a player reaches that amount of time.
## However, that can be superseded by Required Account Age, which is a time in days that you need to have had an account on the server for.
## As time goes on, more config options may be added to this file.
## You can use the admin verb 'Generate Job Configuration' in-game to auto-regenerate this config as a downloadable file without having to manually edit this file if we add more jobs or more things you can edit here.
## It will always respect prior-existing values in the config, but will appropriately add more fields when they generate.
## It's strongly advised you create your own version of this file rather than use the one provisioned on the codebase.
## The game will not read any line that is commented out with a '#', as to allow you to defer to codebase defaults.
## If you want to override the codebase values, add the value and then uncomment that line by removing the # from the job key's name.
## Ensure that the key is flush, do not introduce any whitespaces when you uncomment a key. For example:
## "# Total Positions" should always be changed to "Total Positions", no additional spacing.
## Best of luck editing!
[AI]
"# Playtime Requirements" = 180
"# Required Account Age" = 30
"# Spawn Positions" = 1
"# Total Positions" = 1
[ASSISTANT]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = -1
"# Total Positions" = -1
[ATMOSPHERIC_TECHNICIAN]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 3
[BARTENDER]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[BOTANIST]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 3
[CAPTAIN]
"# Playtime Requirements" = 180
"# Required Account Age" = 14
"# Spawn Positions" = 1
"# Total Positions" = 1
[CARGO_TECHNICIAN]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 3
[CHAPLAIN]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[CHEMIST]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 2
[CHIEF_ENGINEER]
"# Playtime Requirements" = 180
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1
[CHIEF_MEDICAL_OFFICER]
"# Playtime Requirements" = 180
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1
[CLOWN]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[COOK]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 2
[CURATOR]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[CYBORG]
"# Playtime Requirements" = 120
"# Required Account Age" = 21
"# Spawn Positions" = 1
"# Total Positions" = 0
[DETECTIVE]
"# Playtime Requirements" = 300
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1
[GENETICIST]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 2
[HEAD_OF_PERSONNEL]
"# Playtime Requirements" = 180
"# Required Account Age" = 10
"# Spawn Positions" = 1
"# Total Positions" = 1
[HEAD_OF_SECURITY]
"# Playtime Requirements" = 300
"# Required Account Age" = 14
"# Spawn Positions" = 1
"# Total Positions" = 1
[JANITOR]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 2
[LAWYER]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 2
[MEDICAL_DOCTOR]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 3
"# Total Positions" = 5
[MIME]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[PARAMEDIC]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 2
[PRISONER]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 0
[PSYCHOLOGIST]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[QUARTERMASTER]
"# Playtime Requirements" = 0
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1
[RESEARCH_DIRECTOR]
"# Playtime Requirements" = 180
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1
[ROBOTICIST]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 2
"# Total Positions" = 2
[SCIENTIST]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 3
"# Total Positions" = 5
[SECURITY_OFFICER]
"# Playtime Requirements" = 300
"# Required Account Age" = 7
"# Spawn Positions" = 5
"# Total Positions" = 5
[SHAFT_MINER]
"# Playtime Requirements" = 0
"# Required Account Age" = 0
"# Spawn Positions" = 3
"# Total Positions" = 3
[STATION_ENGINEER]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 5
"# Total Positions" = 5
[VIROLOGIST]
"# Playtime Requirements" = 60
"# Required Account Age" = 0
"# Spawn Positions" = 1
"# Total Positions" = 1
[WARDEN]
"# Playtime Requirements" = 300
"# Required Account Age" = 7
"# Spawn Positions" = 1
"# Total Positions" = 1

View File

@@ -1,49 +0,0 @@
#This allows easy configuration of the number of positions allowed for each job
#Format is: [Job name]=[total positions],[spawn positions]
#Job names must be identical to the title var of each job datum
#Positions can be set to -1 to allow unlimited slots
Captain=1,1
Head of Personnel=1,1
Head of Security=1,1
Chief Engineer=1,1
Research Director=1,1
Chief Medical Officer=1,1
Assistant=-1,-1
Prisoner=0,2
Quartermaster=1,1
Cargo Technician=3,2
Shaft Miner=3,3
Bartender=1,1
Cook=2,1
Botanist=3,2
Janitor=2,1
Clown=1,1
Mime=1,1
Curator=1,1
Lawyer=2,2
Chaplain=1,1
Station Engineer=5,5
Atmospheric Technician=3,2
Medical Doctor=5,3
Paramedic=2,2
Chemist=2,2
Geneticist=2,2
Virologist=1,1
Psychologist=1,1
Scientist=5,3
Roboticist=2,2
Warden=1,1
Detective=1,1
Security Officer=5,5
AI=0,1
Cyborg=0,1

View File

@@ -8,7 +8,7 @@ export BYOND_MAJOR=514
export BYOND_MINOR=1588
#rust_g git tag
export RUST_G_VERSION=1.0.2
export RUST_G_VERSION=1.1.0
#node version
export NODE_VERSION=14

Binary file not shown.

View File

@@ -2113,6 +2113,7 @@
#include "code\modules\admin\verbs\cinematic.dm"
#include "code\modules\admin\verbs\color_blind_test.dm"
#include "code\modules\admin\verbs\commandreport.dm"
#include "code\modules\admin\verbs\config_helpers.dm"
#include "code\modules\admin\verbs\deadsay.dm"
#include "code\modules\admin\verbs\debug.dm"
#include "code\modules\admin\verbs\diagnostics.dm"