From 9bbac01dbc6ac42932c73867fd0916bb936e1763 Mon Sep 17 00:00:00 2001
From: Michael <12646562+mbround18@users.noreply.github.com>
Date: Tue, 10 Oct 2023 20:04:31 -0700
Subject: [PATCH] Add additional arguments for Valheim Server (#742)
* Initial pass for #741
* Fixed variables with parser
* upgrade deps
* Fix test data
Signed-off-by: MBRound18 <12646562+mbround18@users.noreply.github.com>
* formatted
* Fix test data
Signed-off-by: MBRound18 <12646562+mbround18@users.noreply.github.com>
* formatted
* rustfmt
---------
Signed-off-by: MBRound18 <12646562+mbround18@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
---
.dockerignore | 1 +
.idea/inspectionProfiles/Project_Default.xml | 6 +
Cargo.lock | 210 ++++++++++++++++---
Makefile | 36 ++++
README.md | 5 +
src/odin/Cargo.toml | 11 +-
src/odin/cli.rs | 26 ++-
src/odin/commands/configure.rs | 59 +++++-
src/odin/files/config.rs | 55 ++++-
src/odin/logger.rs | 21 +-
src/odin/main.rs | 26 ++-
src/odin/mods/bepinex.rs | 12 +-
src/odin/server/startup.rs | 119 +++++++----
src/odin/utils/mod.rs | 2 +
src/odin/utils/parse_truthy.rs | 22 ++
src/scripts/entrypoint.sh | 3 +
src/scripts/start_valheim.sh | 16 ++
test.sh | 11 +
18 files changed, 541 insertions(+), 100 deletions(-)
create mode 100644 .idea/inspectionProfiles/Project_Default.xml
create mode 100644 Makefile
create mode 100644 src/odin/utils/parse_truthy.rs
create mode 100644 test.sh
diff --git a/.dockerignore b/.dockerignore
index 330b0e5e..bdc368d2 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,3 +10,4 @@ package.json
yarn.lock
.yarn
.env*
+.vscode
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..89a103a9
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index d9337383..b5782d03 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -57,9 +57,9 @@ dependencies = [
[[package]]
name = "anstream"
-version = "0.5.0"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -71,9 +71,9 @@ dependencies = [
[[package]]
name = "anstyle"
-version = "1.0.3"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46"
+checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
@@ -95,9 +95,9 @@ dependencies = [
[[package]]
name = "anstyle-wincon"
-version = "2.1.0"
+version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd"
+checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
dependencies = [
"anstyle",
"windows-sys",
@@ -171,9 +171,9 @@ checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "byteorder"
-version = "1.4.3"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
@@ -234,6 +234,7 @@ dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
+ "serde",
"wasm-bindgen",
"windows-targets",
]
@@ -250,9 +251,9 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.5"
+version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824956d0dca8334758a5b7f7e50518d66ea319330cbceedcf76905c2f6ab30e3"
+checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
dependencies = [
"clap_builder",
"clap_derive",
@@ -260,9 +261,9 @@ dependencies = [
[[package]]
name = "clap_builder"
-version = "4.4.5"
+version = "4.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "122ec64120a49b4563ccaedcbea7818d069ed8e9aa6d829b82d8a4128936b2ab"
+checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
dependencies = [
"anstream",
"anstyle",
@@ -300,6 +301,16 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
+[[package]]
+name = "core-foundation"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "core-foundation-sys"
version = "0.8.4"
@@ -361,6 +372,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 +414,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 +431,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,11 +467,17 @@ 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"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
+checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480"
dependencies = [
"errno-dragonfly",
"libc",
@@ -616,7 +671,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
- "indexmap",
+ "indexmap 1.9.3",
"slab",
"tokio",
"tokio-util",
@@ -645,9 +700,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 +740,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 +870,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 +894,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]]
@@ -882,15 +961,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.148"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "linux-raw-sys"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
+checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db"
[[package]]
name = "lock_api"
@@ -916,9 +995,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "memchr"
-version = "2.6.3"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "mime"
@@ -985,9 +1064,9 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.16"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
@@ -1033,6 +1112,7 @@ dependencies = [
"reqwest",
"serde",
"serde_json",
+ "serde_with",
"serial_test",
"sysinfo",
"tar",
@@ -1189,9 +1269,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
-version = "1.0.67"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c"
dependencies = [
"unicode-ident",
]
@@ -1246,9 +1326,9 @@ dependencies = [
[[package]]
name = "reqwest"
-version = "0.11.20"
+version = "0.11.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
+checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
dependencies = [
"base64",
"bytes",
@@ -1272,6 +1352,7 @@ dependencies = [
"serde",
"serde_json",
"serde_urlencoded",
+ "system-configuration",
"tokio",
"tokio-rustls",
"tower-service",
@@ -1306,9 +1387,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustix"
-version = "0.38.14"
+version = "0.38.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f"
+checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7"
dependencies = [
"bitflags 2.4.0",
"errno",
@@ -1419,6 +1500,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"
@@ -1536,9 +1646,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
-version = "2.0.37"
+version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
@@ -1559,6 +1669,27 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "tar"
version = "0.4.40"
@@ -1597,8 +1728,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
dependencies = [
"deranged",
+ "itoa",
"serde",
"time-core",
+ "time-macros",
]
[[package]]
@@ -1607,6 +1740,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/Makefile b/Makefile
new file mode 100644
index 00000000..56445ff1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,36 @@
+# Following this tutorial: https://markentier.tech/posts/2022/01/speedy-rust-builds-under-wsl2/
+# This makes developing on windows significantly easier for rust projects!!
+
+SOURCE_DIR = $(PWD)
+# `notdir` returns the part after the last `/`
+# so if the source was "/some/nested/project", only "project" remains
+BUILD_DIR = ~/tmp/$(notdir $(SOURCE_DIR))
+
+wsl.build: wsl.sync
+ cd $(BUILD_DIR) && cargo build
+ rsync -av $(BUILD_DIR)/target/debug/ $(SOURCE_DIR)/target/debug/ \
+ --exclude .git \
+ --exclude target \
+ --exclude .fingerprint \
+ --exclude build \
+ --exclude incremental \
+ --exclude deps
+
+wsl.run: wsl.sync
+ cd $(BUILD_DIR) && cargo run
+
+wsl.test: wsl.sync
+ cd $(BUILD_DIR) && cargo test
+
+wsl.sync:
+ mkdir -p $(BUILD_DIR)
+ rsync -av $(SOURCE_DIR)/ $(BUILD_DIR)/ --exclude .git --exclude target --exclude tmp
+
+wsl.clean:
+ rm -rf $(BUILD_DIR)/target
+
+wsl.clean-all:
+ rm -rf $(BUILD_DIR)
+
+wsl.clippy: wsl.sync
+ cd $(BUILD_DIR) && cargo clippy
diff --git a/README.md b/README.md
index 2f9a0e7d..04fa5fa3 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
@@ -128,6 +130,9 @@ If you purely want to run this on a Linux based system, without docker, take a l
| PASSWORD | `` | TRUE | Set this to something unique! |
| ENABLE_CROSSPLAY | `0` | FALSE | Enable crossplay support as of `Valheim Version >0.211.8` |
| TYPE | `Vanilla` | FALSE | This can be set to `ValheimPlus`, `BepInEx`, `BepInExFull` or `Vanilla` |
+| PRESET | `` | FALSE | Normal, Casual, Easy, Hard, Hardcore, Immersive, Hammer |
+| MODIFIERS | `` | FALSE | Comma-separated array of modifiers. EX: `combat=easy,raids=muchmore` |
+| SET_KEY | `` | FALSE | Can be one of the following: nobuildcost, playerevents, passivemobs, nomap |
| MODS | `` | FALSE | This is an array of mods separated by comma and a new line. [Click Here for Examples](./docs/tutorials/getting_started_with_mods.md) Supported files are `zip`, `dll`, and `cfg`. |
| WEBHOOK_URL | `` | FALSE | Supply this to get information regarding your server's status in a webhook or Discord notification! [Click here to learn how to get a webhook url for Discord](https://help.dashe.io/en/articles/2521940-how-to-create-a-discord-webhook-url) |
| WEBHOOK_INCLUDE_PUBLIC_IP | `0` | FALSE | Optionally include your server's public IP in webhook notications, useful if not using a static IP address. NOTE: If your server is behind a NAT using PAT with more than one external IP address (very unlikely on a home network), this could be inaccurate if your NAT doesn't maintain your server to a single external IP. |
diff --git a/src/odin/Cargo.toml b/src/odin/Cargo.toml
index 074ac605..0d205c99 100644
--- a/src/odin/Cargo.toml
+++ b/src/odin/Cargo.toml
@@ -30,7 +30,7 @@ path = "lib.rs"
handlebars = "4"
dotenv = "0.15"
log = "0.4"
-clap = { version = "4.3", features = [ "derive", "env" ] }
+clap = { version = "4.4", features = [ "derive", "env" ] }
which = "4.4"
serde = { version = "1.0", features = ["derive"], default_features = false }
sysinfo = { version = "0.29", default_features = false }
@@ -42,10 +42,11 @@ inflections = "1.1.1"
md5 = "0.7"
reqwest = { version = "0.11", default_features = false, features = ["blocking", "json", "rustls-tls"] }
chrono = "0.4"
-zip = { version = "0.6.3" }
-fs_extra = "1.2.0"
-glob = "0.3.0"
-a2s = "0.5.2"
+zip = { version = "0.6" }
+fs_extra = "1.3"
+glob = "0.3"
+a2s = "0.5"
+serde_with = "3.3.0"
[dev-dependencies]
once_cell = "1"
diff --git a/src/odin/cli.rs b/src/odin/cli.rs
index e1e2b263..354a609d 100644
--- a/src/odin/cli.rs
+++ b/src/odin/cli.rs
@@ -1,19 +1,22 @@
use clap::{Parser, Subcommand};
+use crate::utils::parse_truthy::parse_truthy;
+
#[derive(Parser)]
#[command(author, version)]
#[command(propagate_version = true)]
pub struct Cli {
/// Allows you to run as root
- #[arg(long)]
+ #[arg(long, env = "I_ACCEPT_TO_RUN_THINGS_UNSAFELY", value_parser = parse_truthy)]
pub run_as_root: bool,
/// Make everything noisy but very helpful to identify issues.
- #[arg(long)]
+ /// This will enable debugging, you can use the env variable DEBUG_MODE to set this as well.
+ #[arg(long, env = "DEBUG_MODE", value_parser = parse_truthy)]
pub debug: bool,
/// Will spit out the commands as if it were to run them but not really.
- #[arg(short = 'r', long)]
+ #[arg(short = 'r', long, env = "DRY_RUN", value_parser = parse_truthy)]
pub dry_run: bool,
#[command(subcommand)]
@@ -52,6 +55,23 @@ 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,
+
+ /// Sets the save interval in seconds
+ #[arg(long, env = "SAVE_INTERVAL")]
+ save_interval: Option,
},
/// Installs Valheim with steamcmd
diff --git a/src/odin/commands/configure.rs b/src/odin/commands/configure.rs
index 274de022..0d7d94f7 100644
--- a/src/odin/commands/configure.rs
+++ b/src/odin/commands/configure.rs
@@ -1,18 +1,65 @@
+use log::debug;
+use serde::{Deserialize, Serialize};
+
use crate::files::config::{config_file, write_config};
use crate::files::discord::{discord_file, write_discord};
-use log::debug;
+/// See: https://user-images.githubusercontent.com/34519392/273088066-b9c94664-9eef-419d-999a-8b8798462dee.PNG
+/// for a list of modifiers
+#[derive(Deserialize, Serialize, Debug, Clone)]
+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,
+
+ /// Sets the save interval in seconds
+ pub save_interval: Option,
}
impl Configuration {
+ /// Creates a new configuration
+ #[allow(clippy::too_many_arguments)]
pub fn new(
name: String,
server_executable: String,
@@ -20,6 +67,10 @@ impl Configuration {
world: String,
password: String,
public: bool,
+ preset: Option,
+ modifiers: Option>,
+ set_key: Option,
+ save_interval: Option,
) -> Self {
Configuration {
name,
@@ -28,8 +79,14 @@ impl Configuration {
world,
password,
public,
+ preset,
+ modifiers,
+ set_key,
+ save_interval,
}
}
+
+ /// 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..62791c5d 100644
--- a/src/odin/files/config.rs
+++ b/src/odin/files/config.rs
@@ -1,25 +1,54 @@
-use crate::commands::configure::Configuration;
-use crate::files::{FileManager, ManagedFile};
-use crate::traits::AsOneOrZero;
-use crate::utils::environment::fetch_var;
+use std::{fs, path::PathBuf, process::exit};
use log::{debug, error};
use serde::{Deserialize, Serialize};
-use std::{fs, path::PathBuf, process::exit};
+
+use crate::commands::configure::{Configuration, Modifiers};
+use crate::files::{FileManager, ManagedFile};
+use crate::traits::AsOneOrZero;
+use crate::utils::environment::fetch_var;
const ODIN_CONFIG_FILE_VAR: &str = "ODIN_CONFIG_FILE";
-#[derive(Deserialize, Serialize)]
+#[derive(Deserialize, Serialize, Debug, Clone)]
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,
+
+ /// Sets the save interval in seconds
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub save_interval: 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 +65,15 @@ impl From for ValheimArguments {
public: value.public.as_string(),
password: value.password,
command,
+ preset: value.preset,
+ modifiers: value.modifiers,
+ set_key: value.set_key,
+ save_interval: value.save_interval,
}
}
}
+/// Loads the configuration from the config file
pub fn load_config() -> ValheimArguments {
let file = config_file();
let config = read_config(file);
@@ -52,12 +86,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 +102,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);
@@ -79,11 +116,13 @@ pub fn write_config(config: ManagedFile, args: Configuration) -> bool {
#[cfg(test)]
mod tests {
- use super::*;
- use rand::Rng;
use std::env;
use std::env::current_dir;
+ use rand::Rng;
+
+ use super::*;
+
#[test]
#[should_panic(
expected = "Please initialize odin with `odin configure`. See `odin configure --help`"
diff --git a/src/odin/logger.rs b/src/odin/logger.rs
index 5132e1ba..eaa905dc 100644
--- a/src/odin/logger.rs
+++ b/src/odin/logger.rs
@@ -1,5 +1,9 @@
+use std::env;
+
use log::{debug, Level, LevelFilter, Metadata, Record, SetLoggerError};
+use crate::utils::parse_truthy::parse_truthy;
+
pub struct OdinLogger;
impl log::Log for OdinLogger {
@@ -34,6 +38,19 @@ pub fn initialize_logger(debug: bool) -> Result<(), SetLoggerError> {
LevelFilter::Info
};
let result = log::set_logger(&LOGGER).map(|_| log::set_max_level(level));
- debug!("Debugging set to {}", debug.to_string());
- result
+ match result {
+ Err(err) => {
+ println!("Error setting logger: {:?}", err);
+ Err(err)
+ }
+ Ok(_) => {
+ debug!("Logger initialized");
+ Ok(())
+ }
+ }
+}
+
+pub fn debug_mode() -> bool {
+ let debug_mode = env::var("DEBUG_MODE").unwrap_or(String::new());
+ parse_truthy(&debug_mode).unwrap_or(false)
}
diff --git a/src/odin/main.rs b/src/odin/main.rs
index d2045aa6..8be3587e 100644
--- a/src/odin/main.rs
+++ b/src/odin/main.rs
@@ -2,7 +2,11 @@ use clap::Parser;
use dotenv::dotenv;
use log::debug;
+use commands::configure::Configuration;
+
+use crate::commands::configure::Modifiers;
use crate::executable::handle_exit_status;
+use crate::logger::debug_mode;
use crate::messages::about;
mod cli;
@@ -26,7 +30,7 @@ fn main() {
use cli::{Cli, Commands};
let cli = Cli::parse();
- logger::initialize_logger(cli.debug).unwrap();
+ logger::initialize_logger(cli.debug || debug_mode()).unwrap();
if cli.debug {
debug!("Debug mode enabled!");
@@ -40,13 +44,28 @@ fn main() {
server_executable,
world,
port,
- } => commands::configure::Configuration::new(
+ modifiers,
+ preset,
+ set_key,
+ save_interval,
+ } => 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,
+ save_interval,
)
.invoke(),
Commands::Install {} => handle_exit_status(
@@ -81,9 +100,10 @@ fn main() {
mod tests {
// use super::*;
- use crate::cli::Cli;
use clap::CommandFactory;
+ use crate::cli::Cli;
+
#[test]
fn asserts() {
Cli::command().debug_assert();
diff --git a/src/odin/mods/bepinex.rs b/src/odin/mods/bepinex.rs
index 2ed3542a..fcd174ec 100644
--- a/src/odin/mods/bepinex.rs
+++ b/src/odin/mods/bepinex.rs
@@ -1,10 +1,12 @@
+use std::ops::Add;
+use std::process::{Child, Command};
+
+use log::{debug, info};
+use serde::{Deserialize, Serialize};
+
use crate::constants;
use crate::utils::common_paths::{bepinex_directory, bepinex_plugin_directory, game_directory};
use crate::utils::{environment, path_exists};
-use log::{debug, info};
-use serde::{Deserialize, Serialize};
-use std::ops::Add;
-use std::process::{Child, Command};
const DYLD_LIBRARY_PATH_VAR: &str = "DYLD_LIBRARY_PATH";
const DYLD_INSERT_LIBRARIES_VAR: &str = "DYLD_INSERT_LIBRARIES";
@@ -103,7 +105,7 @@ impl BepInExEnvironment {
if output {
debug!("Yay! looks like we found all the required files for BepInEx to run! <3")
} else {
- debug!("Uhh ohh!!! Looks like you are missing something.")
+ debug!("We didn't find a modded instance! Launching a normal instance!")
}
output
}
diff --git a/src/odin/server/startup.rs b/src/odin/server/startup.rs
index 2ff7faaa..1dc27ad6 100644
--- a/src/odin/server/startup.rs
+++ b/src/odin/server/startup.rs
@@ -1,8 +1,9 @@
+use std::process::exit;
+use std::{io, process::Child};
+
use daemonize::{Daemonize, Error};
use log::{debug, error, info};
-use std::{io, process::Child};
-
use crate::mods::bepinex::BepInExEnvironment;
use crate::notifications::enums::event_status::EventStatus;
use crate::notifications::enums::notification_event::NotificationEvent;
@@ -15,13 +16,14 @@ use crate::{
messages,
utils::environment,
};
-use std::process::exit;
type CommandResult = io::Result;
pub fn start_daemonized(config: ValheimArguments) -> Result {
+ debug!("Starting server daemonized...");
let stdout = create_file(format!("{}/logs/valheim_server.log", game_directory()).as_str());
let stderr = create_file(format!("{}/logs/valheim_server.err", game_directory()).as_str());
+ let command = start(config);
Daemonize::new()
.working_directory(game_directory())
.user("steam")
@@ -31,81 +33,120 @@ pub fn start_daemonized(config: ValheimArguments) -> Result CommandResult {
+pub fn start(config: ValheimArguments) -> CommandResult {
let mut command = create_execution(&config.command);
- info!(target: "server_startup","--------------------------------------------------------------------------------------------------------------");
+
+ debug!("--------------------------------------------------------------------------------------------------------------");
+
let ld_library_path_value = environment::fetch_multiple_var(
constants::LD_LIBRARY_PATH_VAR,
format!("{}/linux64", game_directory()).as_str(),
);
- debug!(target: "server_startup","Setting up base command");
+ info!("Setting up base command");
+ info!("Launching With Args: \n{:#?}", &config);
+ // Sets the base command for the server
let mut base_command = command
- // Extra launch arguments
- .arg(fetch_var(
- "SERVER_EXTRA_LAUNCH_ARGS",
- "-nographics -batchmode",
- ))
- // Required vars
- .args([
- "-port",
- config.port.as_str(),
- "-name",
- config.name.as_str(),
- "-world",
- config.world.as_str(),
- "-public",
- config.public.as_str(),
- ])
- .env("SteamAppId", environment::fetch_var("APPID", "892970"))
+ .env("SteamAppId", fetch_var("APPID", "892970"))
.current_dir(game_directory());
+ // Sets the name of the server, (Can be set with ENV variable NAME)
+ let name = format!("-name {}", fetch_var("NAME", config.name.as_str()));
+ base_command.arg(name);
+
+ // Sets the port of the server, (Can be set with ENV variable PORT)
+ let port = format!("-port {}", fetch_var("PORT", config.port.as_str()));
+ base_command.arg(port);
+
+ // Sets the world of the server, (Can be set with ENV variable WORLD)
+ let world = format!("-world {}", fetch_var("WORLD", config.world.as_str()));
+ base_command.arg(world);
+
+ // Determines if the server is public or not
+ let public = format!("-public {}", fetch_var("PUBLIC", config.public.as_str()));
+ base_command.arg(public);
+
+ // Sets the save interval in seconds
+ if let Some(save_interval) = &config.save_interval {
+ base_command.arg(format!("-saveinterval {}", save_interval));
+ };
+
+ // Add set_key to the command
+ if let Some(set_key) = &config.set_key {
+ base_command.arg(format!("-setkey {}", set_key));
+ };
+
+ // Add preset to the command
+ if let Some(preset) = &config.preset {
+ base_command.arg(format!("-preset {}", preset));
+ };
+
+ // Add modifiers to the command
+ if let Some(modifiers) = &config.modifiers {
+ base_command.args(
+ modifiers
+ .iter()
+ .map(|modifier| format!("-modifier {} {}", modifier.name, modifier.value)),
+ );
+ };
+
+ // Extra args for the server
+ let extra_args = format!(
+ "-nographics -batchmode {}",
+ fetch_var("SERVER_EXTRA_LAUNCH_ARGS", "")
+ )
+ .trim()
+ .to_string();
+ base_command.arg(extra_args);
+
let is_public = config.public.eq("1");
let is_vanilla = fetch_var("TYPE", "vanilla").eq_ignore_ascii_case("vanilla");
let no_password = config.password.is_empty();
// If no password env variable
if !is_public && !is_vanilla && no_password {
- debug!(target: "server_startup","No password found, skipping password flag.")
+ info!("No password found, skipping password flag.")
} else if no_password && (is_public || is_vanilla) {
error!("Cannot run you server with no password! PUBLIC must be 0 and cannot be a Vanilla type server.");
exit(1)
} else {
- debug!(target: "server_startup","Password found, adding password flag.");
- base_command = base_command.args(["-password", config.password.as_str()]);
+ info!("Password found, adding password flag.");
+ base_command = base_command.arg(format!("-password {}", config.password));
}
if fetch_var("ENABLE_CROSSPLAY", "0").eq("1") {
info!("Launching with Crossplay! <3");
base_command = base_command.arg("-crossplay")
} else {
- debug!("No Crossplay Enabled!")
+ info!("No Crossplay Enabled!")
}
// Tack on save dir at the end.
- base_command = base_command.args(["-savedir", &saves_directory()]);
+ base_command = base_command.arg(format!("-savedir {}", &saves_directory()));
+
+ debug!("Base Command: {:#?}", base_command);
- info!(target: "server_startup","Executable: {}", &config.command);
- info!(target: "server_startup","Launching Command...");
+ debug!("Executable: {}", &config.command);
+ info!("Launching Command...");
let bepinex_env = BepInExEnvironment::new();
if bepinex_env.is_installed() {
- info!(target: "server_startup","BepInEx detected! Switching to run with BepInEx...");
- debug!(target: "server_startup","BepInEx Environment: \n{:#?}", bepinex_env);
+ info!("BepInEx detected! Switching to run with BepInEx...");
+ info!("BepInEx Environment: \n{:#?}", bepinex_env);
bepinex_env.launch(base_command)
} else {
- info!(target: "server_startup","Everything looks good! Running normally!");
+ info!("Everything looks good! Running normally!");
base_command
.env(constants::LD_LIBRARY_PATH_VAR, ld_library_path_value)
.spawn()
diff --git a/src/odin/utils/mod.rs b/src/odin/utils/mod.rs
index 6bbf54ea..a96f79e1 100644
--- a/src/odin/utils/mod.rs
+++ b/src/odin/utils/mod.rs
@@ -1,6 +1,8 @@
pub mod common_paths;
pub mod environment;
pub mod fetch_public_ip_address;
+pub mod parse_truthy;
+
pub use fetch_public_ip_address::fetch_public_address;
use log::debug;
diff --git a/src/odin/utils/parse_truthy.rs b/src/odin/utils/parse_truthy.rs
new file mode 100644
index 00000000..7299c642
--- /dev/null
+++ b/src/odin/utils/parse_truthy.rs
@@ -0,0 +1,22 @@
+use std::fmt::Error;
+
+pub fn parse_truthy(value: &str) -> Result {
+ Ok(match value.to_lowercase().as_str() {
+ "true" => true,
+ "false" => false,
+ "1" => true,
+ "0" => false,
+ _ => false,
+ })
+}
+
+// test the parse_truthy function
+#[test]
+fn test_parse_truthy() {
+ assert_eq!(parse_truthy("true"), Ok(true));
+ assert_eq!(parse_truthy("false"), Ok(false));
+ assert_eq!(parse_truthy("1"), Ok(true));
+ assert_eq!(parse_truthy("0"), Ok(false));
+ assert_eq!(parse_truthy(""), Ok(false));
+ assert_eq!(parse_truthy("qwdqwdqwd"), Ok(false));
+}
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