diff --git a/Cargo.lock b/Cargo.lock index d9337383..7f799e98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -234,6 +234,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets", ] @@ -361,6 +362,41 @@ dependencies = [ "libc", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -368,7 +404,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ "cfg-if", - "hashbrown 0.14.0", + "hashbrown 0.14.1", "lock_api", "once_cell", "parking_lot_core", @@ -385,6 +421,9 @@ name = "deranged" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +dependencies = [ + "serde", +] [[package]] name = "digest" @@ -418,6 +457,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.3" @@ -616,7 +661,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -645,9 +690,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" [[package]] name = "headers" @@ -685,6 +730,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "hmac" version = "0.12.1" @@ -809,6 +860,12 @@ dependencies = [ "cc", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -827,6 +884,18 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" +dependencies = [ + "equivalent", + "hashbrown 0.14.1", + "serde", ] [[package]] @@ -1033,6 +1102,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "serde_with", "serial_test", "sysinfo", "tar", @@ -1419,6 +1489,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.0.2", + "serde", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "serial_test" version = "2.0.0" @@ -1597,8 +1696,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ "deranged", + "itoa", "serde", "time-core", + "time-macros", ] [[package]] @@ -1607,6 +1708,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + [[package]] name = "tinyvec" version = "1.6.0" diff --git a/README.md b/README.md index 2f9a0e7d..8b7b14b5 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ + [![All Contributors](https://img.shields.io/badge/all_contributors-13-orange.svg?style=flat-square)](#contributors-) + ## Table of Contents diff --git a/docker-compose.yml b/docker-compose.yml index 713ce4e1..dd1a13e2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,6 +19,12 @@ services: AUTO_UPDATE: 1 AUTO_UPDATE_SCHEDULE: "0 1 * * *" TYPE: Vanilla + DEBUG_MODE: 1 + PRESET: "Hammer" + MODIFIERS: | + raids=muchmore + combat=hard + deathpenalty=casual build: context: . dockerfile: ./Dockerfile.valheim diff --git a/src/odin/Cargo.toml b/src/odin/Cargo.toml index 074ac605..71760ea0 100644 --- a/src/odin/Cargo.toml +++ b/src/odin/Cargo.toml @@ -46,6 +46,7 @@ zip = { version = "0.6.3" } fs_extra = "1.2.0" glob = "0.3.0" a2s = "0.5.2" +serde_with = "3.3.0" [dev-dependencies] once_cell = "1" diff --git a/src/odin/cli.rs b/src/odin/cli.rs index e1e2b263..023b607d 100644 --- a/src/odin/cli.rs +++ b/src/odin/cli.rs @@ -9,8 +9,9 @@ pub struct Cli { pub run_as_root: bool, /// Make everything noisy but very helpful to identify issues. - #[arg(long)] - pub debug: bool, + /// This will enable debugging, you can use the env variable DEBUG_MODE to set this as well. + #[arg(long, env = "DEBUG_MODE", action = clap::ArgAction::Set)] + pub debug: String, /// Will spit out the commands as if it were to run them but not really. #[arg(short = 'r', long)] @@ -52,6 +53,19 @@ pub enum Commands { /// Sets the public state of the server, (Can be set with ENV variable PUBLIC) #[arg(short = 'o', long, env = "PUBLIC")] public: String, + + /// Sets flag modifiers for launching the server, (Can be set with ENV variable MODIFIERS) + /// This should be comma separated with equal variables, e.g. "raids=none,combat=hard" + #[arg(long, env = "MODIFIERS")] + modifiers: Option, + + /// Sets flag preset for launching the server, (Can be set with ENV variable PRESET) + #[arg(long, env = "PRESET")] + preset: Option, + + /// Sets flag set_key for launching the server, (Can be set with ENV variable SET_KEY) + #[arg(long, env = "SET_KEY")] + set_key: Option, }, /// Installs Valheim with steamcmd diff --git a/src/odin/commands/configure.rs b/src/odin/commands/configure.rs index 274de022..530d29c6 100644 --- a/src/odin/commands/configure.rs +++ b/src/odin/commands/configure.rs @@ -2,17 +2,61 @@ use crate::files::config::{config_file, write_config}; use crate::files::discord::{discord_file, write_discord}; use log::debug; +use serde::{Deserialize, Serialize}; + +/// See: https://user-images.githubusercontent.com/34519392/273088066-b9c94664-9eef-419d-999a-8b8798462dee.PNG +/// for a list of modifiers +#[derive(Deserialize, Serialize, Debug)] +pub struct Modifiers { + /// The name of the modifier + pub name: String, + + /// The value of the modifier + pub value: String, +} + +impl From for Modifiers { + /// Creates a new modifier from a string + fn from(value: String) -> Self { + let mut split = value.split('='); + let name = split.next().unwrap().to_string(); + let value = split.next().unwrap().to_string(); + Modifiers { name, value } + } +} pub struct Configuration { + /// Sets the name of the server, (Can be set with ENV variable NAME) pub name: String, + + /// Sets the servers executable path. pub server_executable: String, + + /// Sets the port of the server, (Can be set with ENV variable PORT) pub port: u16, + + /// Sets the world of the server, (Can be set with ENV variable WORLD) pub world: String, + + /// Sets the password of the server, (Can be set with ENV variable PASSWORD) pub password: String, + + /// Sets the public state of the server, (Can be set with ENV variable PUBLIC) pub public: bool, + + /// Sets flag preset for launching the server, (Can be set with ENV variable PRESET) + pub preset: Option, + + /// Sets flag modifiers for launching the server, (Can be set with ENV variable MODIFIERS) + pub modifiers: Option>, + + /// Sets flag set_key for launching the server, (Can be set with ENV variable SET_KEY) + pub set_key: Option, } impl Configuration { + /// Creates a new configuration + #[allow(clippy::too_many_arguments)] pub fn new( name: String, server_executable: String, @@ -20,6 +64,9 @@ impl Configuration { world: String, password: String, public: bool, + preset: Option, + modifiers: Option>, + set_key: Option, ) -> Self { Configuration { name, @@ -28,8 +75,13 @@ impl Configuration { world, password, public, + preset, + modifiers, + set_key, } } + + /// Invokes the configuration by writing the config file pub fn invoke(self) { debug!("Pulling config file..."); let config = config_file(); diff --git a/src/odin/files/config.rs b/src/odin/files/config.rs index e05848f9..2fb2382b 100644 --- a/src/odin/files/config.rs +++ b/src/odin/files/config.rs @@ -1,4 +1,4 @@ -use crate::commands::configure::Configuration; +use crate::commands::configure::{Configuration, Modifiers}; use crate::files::{FileManager, ManagedFile}; use crate::traits::AsOneOrZero; use crate::utils::environment::fetch_var; @@ -9,17 +9,41 @@ use std::{fs, path::PathBuf, process::exit}; const ODIN_CONFIG_FILE_VAR: &str = "ODIN_CONFIG_FILE"; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Debug)] pub struct ValheimArguments { + /// The port of the server, (Can be set with ENV variable PORT) pub(crate) port: String, + + /// The name of the server, (Can be set with ENV variable NAME) pub(crate) name: String, + + /// The world of the server, (Can be set with ENV variable WORLD) pub(crate) world: String, + + /// The public state of the server, (Can be set with ENV variable PUBLIC) pub(crate) public: String, + + /// The password of the server, (Can be set with ENV variable PASSWORD) pub(crate) password: String, + + /// The command to launch the server pub(crate) command: String, + + /// The preset for launching the server, (Can be set with ENV variable PRESET) + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) preset: Option, + + /// The modifiers for launching the server, (Can be set with ENV variable MODIFIERS) + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) modifiers: Option>, + + /// The set_key for launching the server, (Can be set with ENV variable SET_KEY) + #[serde(skip_serializing_if = "Option::is_none")] + pub(crate) set_key: Option, } impl From for ValheimArguments { + /// Creates a new ValheimArguments from a Configuration fn from(value: Configuration) -> Self { let command = match fs::canonicalize(PathBuf::from(value.server_executable)) { Ok(command_path) => command_path.to_str().unwrap().to_string(), @@ -36,10 +60,14 @@ impl From for ValheimArguments { public: value.public.as_string(), password: value.password, command, + preset: value.preset, + modifiers: value.modifiers, + set_key: value.set_key, } } } +/// Loads the configuration from the config file pub fn load_config() -> ValheimArguments { let file = config_file(); let config = read_config(file); @@ -52,12 +80,14 @@ pub fn load_config() -> ValheimArguments { config } +/// Creates a new config file pub fn config_file() -> ManagedFile { let name = fetch_var(ODIN_CONFIG_FILE_VAR, "config.json"); debug!("Config file set to: {}", name); ManagedFile { name } } +/// Reads the config file pub fn read_config(config: ManagedFile) -> ValheimArguments { let content = config.read(); if content.is_empty() { @@ -66,6 +96,7 @@ pub fn read_config(config: ManagedFile) -> ValheimArguments { serde_json::from_str(content.as_str()).unwrap() } +/// Writes the config file pub fn write_config(config: ManagedFile, args: Configuration) -> bool { let content = ValheimArguments::from(args); diff --git a/src/odin/main.rs b/src/odin/main.rs index d2045aa6..49f19fc3 100644 --- a/src/odin/main.rs +++ b/src/odin/main.rs @@ -1,4 +1,6 @@ +use crate::commands::configure::Modifiers; use clap::Parser; +use commands::configure::Configuration; use dotenv::dotenv; use log::debug; @@ -40,13 +42,26 @@ fn main() { server_executable, world, port, - } => commands::configure::Configuration::new( + modifiers, + preset, + set_key, + } => Configuration::new( name, server_executable, port, world, password, { public.eq("1") }.to_owned(), + preset, + { + modifiers.map(|modifiers| { + modifiers + .split(',') + .map(|modifier| Modifiers::from(modifier.to_string())) + .collect() + }) + }, + set_key, ) .invoke(), Commands::Install {} => handle_exit_status( diff --git a/src/odin/server/startup.rs b/src/odin/server/startup.rs index 2ff7faaa..bde1463b 100644 --- a/src/odin/server/startup.rs +++ b/src/odin/server/startup.rs @@ -35,7 +35,7 @@ pub fn start_daemonized(config: ValheimArguments) -> Result CommandResult { format!("{}/linux64", game_directory()).as_str(), ); debug!(target: "server_startup","Setting up base command"); + debug!(target: "server_startup","Launching With Args: \n{:#?}", &config); let mut base_command = command // Extra launch arguments .arg(fetch_var( @@ -69,7 +70,27 @@ pub fn start(config: &ValheimArguments) -> CommandResult { "-public", config.public.as_str(), ]) - .env("SteamAppId", environment::fetch_var("APPID", "892970")) + .arg(if let Some(set_key) = &config.set_key { + format!("-setkey {}", set_key) + } else { + String::new() + }) + .arg(if let Some(preset) = &config.preset { + format!("-preset {}", preset) + } else { + String::new() + }) + .arg(if let Some(modifiers) = &config.modifiers { + modifiers + .iter() + .map(|modifier| format!("-modifier {} {}", modifier.name, modifier.value)) + .collect::>() + .join(" ") + .to_string() + } else { + String::new() + }) + .env("SteamAppId", fetch_var("APPID", "892970")) .current_dir(game_directory()); let is_public = config.public.eq("1"); diff --git a/src/scripts/entrypoint.sh b/src/scripts/entrypoint.sh index f4547db5..f81d700f 100644 --- a/src/scripts/entrypoint.sh +++ b/src/scripts/entrypoint.sh @@ -68,6 +68,9 @@ setup_cron_env() { ENABLE_CROSSPLAY=${ENABLE_CROSSPLAY:-"0"} UPDATE_ON_STARTUP=${UPDATE_ON_STARTUP} SERVER_EXTRA_LAUNCH_ARGS=${SERVER_EXTRA_LAUNCH_ARGS} + PRESET=${PRESET} + MODIFIERS=$(echo "${MODIFIERS}" | xargs echo -n | tr ' ' ',' | sed 's/,,/,/g') + SET_KEY=${SET_KEY} WEBHOOK_URL=${WEBHOOK_URL:-""} WEBHOOK_STATUS_SUCCESSFUL=${WEBHOOK_STATUS_SUCCESSFUL:-"1"} diff --git a/src/scripts/start_valheim.sh b/src/scripts/start_valheim.sh index bb4fdba1..d183a7da 100644 --- a/src/scripts/start_valheim.sh +++ b/src/scripts/start_valheim.sh @@ -90,6 +90,22 @@ log "World: ${WORLD}" log "Public: ${PUBLIC}" log "With Crossplay: ${ENABLE_CROSSPLAY}" log "Password: (REDACTED)" +log "Preset: ${PRESET}" +log "Modifiers: ${MODIFIERS}" +log "Set Key: ${SET_KEY}" +log "Auto Update: ${AUTO_UPDATE}" +log "Auto Backup: ${AUTO_BACKUP}" +log "Auto Backup On Update: ${AUTO_BACKUP_ON_UPDATE}" +log "Auto Backup On Shutdown: ${AUTO_BACKUP_ON_SHUTDOWN}" +log "Auto Backup Pause With No Players: ${AUTO_BACKUP_PAUSE_WITH_NO_PLAYERS}" +log "Auto Backup Pause With Players: ${AUTO_BACKUP_PAUSE_WITH_PLAYERS}" +log "Auto Backup Remove Old: ${AUTO_BACKUP_REMOVE_OLD}" +log "Auto Backup Days To Live: ${AUTO_BACKUP_DAYS_TO_LIVE}" +log "Auto Backup Nice Level: ${AUTO_BACKUP_NICE_LEVEL}" +log "Update On Startup: ${UPDATE_ON_STARTUP}" +log "Mods: ${MODS}" +line + export SteamAppId=${APPID:-892970} diff --git a/test.sh b/test.sh new file mode 100644 index 00000000..07ef268b --- /dev/null +++ b/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + + + +export MODIFIERS=" +raids=muchmore +combat=hard +deathpenalty=casual +" + +echo "${MODIFIERS}" | xargs echo -n | tr ' ' ',' | sed 's/,,/,/g' \ No newline at end of file