From 4a771b439436353e94d0f7aaa7d01a14c7211259 Mon Sep 17 00:00:00 2001 From: Maximilian Burszley Date: Mon, 15 Mar 2021 13:19:36 -0400 Subject: [PATCH] parity clone of pyproj --- .gitignore | 3 + Cargo.lock | 709 +++++++++++++++++++++++++++++++++ Cargo.toml | 23 ++ bindings/Cargo.toml | 13 + bindings/build.rs | 5 + bindings/src/lib.rs | 1 + src/control.rs | 51 +++ src/main.rs | 166 ++++++++ src/valheim.rs | 5 + src/valheim/path.rs | 43 ++ src/valheim/unreserved_port.rs | 44 ++ 11 files changed, 1063 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 bindings/Cargo.toml create mode 100644 bindings/build.rs create mode 100644 bindings/src/lib.rs create mode 100644 src/control.rs create mode 100644 src/main.rs create mode 100644 src/valheim.rs create mode 100644 src/valheim/path.rs create mode 100644 src/valheim/unreserved_port.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ddae3f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.env + +target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e9ea1dc --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,709 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bindings" +version = "0.1.0" +dependencies = [ + "windows", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "cassowary" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" + +[[package]] +name = "cc" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "const-sha1" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb58b6451e8c2a812ad979ed1d83378caa5e927eef2622017a45f251457c2c9d" + +[[package]] +name = "crossbeam-channel" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "crossterm" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e86d73f2a0b407b5768d10a8c720cf5d2df49a9efc10ca09176d201ead4b7fb" +dependencies = [ + "bitflags", + "crossterm_winapi 0.6.2", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi", +] + +[[package]] +name = "crossterm" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c" +dependencies = [ + "bitflags", + "crossterm_winapi 0.7.0", + "lazy_static", + "libc", + "mio", + "parking_lot", + "signal-hook", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2265c3f8e080075d9b6417aa72293fc71662f34b4af2612d8d1b074d29510db" +dependencies = [ + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9" +dependencies = [ + "winapi", +] + +[[package]] +name = "ctrlc" +version = "3.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c15b8ec3b5755a188c141c1f6a98e76de31b936209bf066b647979e2a84764a9" +dependencies = [ + "nix", + "winapi", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "heck" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +dependencies = [ + "libc", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b07a082330a35e43f63177cc01689da34fbffa0105e1246cf0311472cac73a" + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mio" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5dede4e2065b3842b8b0af444119f3aa331cc7cc2dd20388bfb0f5d5a38823a" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897" +dependencies = [ + "socket2", + "winapi", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581" + +[[package]] +name = "rpassword" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc936cf8a7ea60c58f030fd36a612a48f440610214dc54bc36431f9ea0c3efb" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "signal-hook" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729" +dependencies = [ + "libc", + "mio", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +dependencies = [ + "libc", +] + +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if", + "libc", + "winapi", +] + +[[package]] +name = "squote" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fccf17fd09e2455ea796d2ad267b64fa2c5cbd8701b2a93b555d2aa73449f7d" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thread_local" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" +dependencies = [ + "once_cell", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "tui" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ced152a8e9295a5b168adc254074525c17ac4a83c90b2716274cc38118bddc9" +dependencies = [ + "bitflags", + "cassowary", + "crossterm 0.18.2", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "unicode-segmentation" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "valheim" +version = "0.1.0" +dependencies = [ + "anyhow", + "bindings", + "chrono", + "crossbeam-channel", + "crossterm 0.19.0", + "ctrlc", + "dotenv", + "once_cell", + "regex", + "rpassword", + "structopt", + "tui", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426842497696b65fbfc575691d94ef65befb248ed1a8c4361e293c724e7ebe61" +dependencies = [ + "const-sha1", + "windows_gen", + "windows_macros", + "windows_winmd", +] + +[[package]] +name = "windows_gen" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ac8f0f06b647f42ee5459a8e1ffe41795647582c5926ec3fa363a91aad7d77" +dependencies = [ + "proc-macro2", + "quote", + "squote", + "syn", + "windows_gen_macros", + "windows_winmd", +] + +[[package]] +name = "windows_gen_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23eac2169a20173b890c496f9e0e1149a92ef29fe4ba96026b72eec363b993f9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edc57c944eec106c7823b425ab0fd9f90163489e50a4df747f65fcf9030e1fb" +dependencies = [ + "proc-macro2", + "quote", + "squote", + "syn", + "windows_gen", + "windows_winmd", +] + +[[package]] +name = "windows_winmd" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16d44527d04c9713312ed598f5d6ce3c453754dbfc03ddc376615be4415ffc88" +dependencies = [ + "windows_winmd_macros", +] + +[[package]] +name = "windows_winmd_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2353f43f512938450614a176abf2b6cb31ac3b84fd71c88470fee571303e3f36" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..023e479 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "valheim" +description = "An application to wrap valheim_server that provides better logging and status" +version = "0.1.0" +authors = ["Maximilian Burszley "] +edition = "2018" + +[build] +target = "stable-x86_64-pc-windows-msvc" + +[dependencies] +anyhow = "1.0.38" +bindings = { path = "./bindings" } +chrono = "0.4.19" +crossbeam-channel = "0.5.0" +crossterm = "0.19.0" +ctrlc = { version = "3.1.8", features = ["termination"] } +dotenv = "0.15.0" +once_cell = "1.7.2" +regex = "1.4.3" +rpassword = "5.0.1" +structopt = "0.3.21" +tui = { version = "0.14.0", features = ["crossterm"], default-features = false } diff --git a/bindings/Cargo.toml b/bindings/Cargo.toml new file mode 100644 index 0000000..5fb0a61 --- /dev/null +++ b/bindings/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "bindings" +version = "0.1.0" +authors = ["Maximilian Burszley "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +windows = "0.3.1" + +[build-dependencies] +windows = "0.3.1" diff --git a/bindings/build.rs b/bindings/build.rs new file mode 100644 index 0000000..e543292 --- /dev/null +++ b/bindings/build.rs @@ -0,0 +1,5 @@ +#![allow(clippy::ptr_arg)] + +fn main() { + windows::build!(windows::win32::system_services::GenerateConsoleCtrlEvent); +} diff --git a/bindings/src/lib.rs b/bindings/src/lib.rs new file mode 100644 index 0000000..7915760 --- /dev/null +++ b/bindings/src/lib.rs @@ -0,0 +1 @@ +::windows::include_bindings!(); diff --git a/src/control.rs b/src/control.rs new file mode 100644 index 0000000..3e355c5 --- /dev/null +++ b/src/control.rs @@ -0,0 +1,51 @@ +use bindings::windows::win32::system_services::GenerateConsoleCtrlEvent; +use chrono::{Utc, SecondsFormat}; +use crossbeam_channel::{Receiver, bounded}; +use once_cell::sync::Lazy; +use regex::Regex; + +const CTRL_BREAK_EVENT: u32 = 0x00000001; + +static RE: Lazy = Lazy::new(|| Regex::new(r"(?x) +(?P\d{2})/(?P\d{2})/(?P\d{4})\s+ +(?P\d{2}):(?P\d{2}):(?P\d{2}):\s+ +(?P.*)" +).unwrap()); + +pub fn ctrl_break(pid: u32) -> bool { + println!("Emitting CTRL_BREAK to '{}'", pid); + + #[allow(unsafe_code)] + unsafe { + // If the function succeeds, the return value is nonzero. + // https://docs.microsoft.com/en-us/windows/console/generateconsolectrlevent#return-value + GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid).0 != 0 + } +} + +pub fn ctrlc_channel() -> Result, ctrlc::Error> { + let (tx, rx) = bounded(1); + ctrlc::set_handler(move || { + let _ = tx.send(()); + })?; + + Ok(rx) +} + +pub fn process_line(s: &str) { + if s.contains("(Filename:") || s.is_empty() { + return; + } + + let dt = if let Some(caps) = RE.captures(s) { + (format!( + "{}-{}-{}T{}:{}:{}.000Z", + &caps["year"], &caps["month"], &caps["day"], + &caps["hour"], &caps["minute"], &caps["second"] + ), caps["text"].to_string()) + } else { + (Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true), s.into()) + }; + + println!("{} :: {}", dt.0, dt.1); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d779cce --- /dev/null +++ b/src/main.rs @@ -0,0 +1,166 @@ +//! Crate level docs! + +#![deny(unsafe_code)] +#![warn(rust_2018_idioms)] +#![warn(rustdoc)] +#![warn(missing_docs)] +#![warn(clippy::all)] + +mod control; +mod valheim; + +use anyhow::Result; +use crossbeam_channel::select; +use rpassword::prompt_password_stdout; +use std::{ + env, fs, + io::{self, BufRead, BufReader}, + os::windows::process::CommandExt, + process::{Command, Stdio}, + thread, + time::Duration, +}; +use structopt::StructOpt; +use tui::backend::CrosstermBackend; +use tui::Terminal; + +const CREATE_NEW_PROCESS_GROUP: u32 = 0x00000200; + +// TODO! parse logs and emit events to control UI elements + +fn main() -> Result<()> { + dotenv::dotenv().ok(); + + let opts = dbg!(Opts::from_args()); + + dbg!(opts.to_args("example")); + + let appid = fs::read_to_string(opts.path.to_string() + "\\steam_appid.txt")?; + let appid = appid.trim(); + + dbg!(&appid); + + let out = io::stdout(); + let backend = CrosstermBackend::new(out); + let mut _terminal = Terminal::new(backend)?; + + let pwd = if let Ok(pwd) = env::var("VALHEIM_PWD") { + pwd + } else { + prompt_password_stdout("password: ")? + }; + + assert!(pwd.len() >= 5); + assert!(!opts.name.contains(&pwd)); + + let ctrl_c = control::ctrlc_channel()?; + + let mut child = Command::new(opts.path.to_string() + "\\valheim_server.exe") + .env_clear() + .envs(env::vars_os()) + .env("SteamAppId", appid) + .creation_flags(CREATE_NEW_PROCESS_GROUP) + .stdout(Stdio::piped()) + .args(&opts.to_args(&pwd)) + .spawn()?; + + let pid = child.id(); + println!("PID: {}", pid); + + let stdout = BufReader::new(child.stdout.take().expect("failed to take stdout")); + + let t = thread::spawn(move || { + for line in stdout.lines() { + match line { + Ok(line) => control::process_line(line.trim()), + Err(err) => eprintln!("{:?}", err), + } + } + }); + + loop { + select! { + recv(ctrl_c) -> _ => { + if !control::ctrl_break(pid) { + eprintln!("Sending CTRL_BREAK failed!"); + } + + break; + } + default(Duration::from_millis(500)) => {} + } + } + + t.join().ok(); + + Ok(()) +} + +#[derive(StructOpt, Debug)] +#[structopt( + name = "valheim", + version = env!("CARGO_PKG_VERSION"), + author = env!("CARGO_PKG_AUTHORS"), + about = env!("CARGO_PKG_DESCRIPTION"), +)] +struct Opts { + /// Directory containing `valheim_server.exe` + #[structopt( + long, + short, + env = "VALHEIM_EXE_PATH", + parse(from_os_str), + default_value + )] + path: valheim::Path, + + /// Directory for `worlds/`, created if it doesn't exist + #[structopt( + long, + short, + env = "VALHEIM_SAVES_PATH", + parse(from_os_str), + default_value + )] + saves: valheim::Path, + + /// Name of the server as it appears to Steam + #[structopt(env = "VALHEIM_SERVER_NAME")] + name: String, + + /// Savefile name + #[structopt(long, short, env = "VALHEIM_WORLD_NAME", default_value = "Dedicated")] + world: String, + + /// Port to bind the game server to + #[structopt(long, short = "b", env = "VALHEIM_PORT", default_value)] + port: valheim::UnreservedPort, + + /// Controls whether to display the server in the community list + #[structopt(long, short)] + enable_community_server: bool, + + #[structopt(skip)] + password: Option, +} + +impl Opts { + pub fn to_args(&self, pwd: &str) -> Vec { + vec![ + "-nographics".into(), + "-batchmode".into(), + "-name".into(), + self.name.clone(), + "-port".into(), + self.port.to_string(), + "-world".into(), + self.world.clone(), + "-password".into(), + pwd.into(), + "-savedir".into(), + self.saves.to_string(), + "-public".into(), + (self.enable_community_server as u8).to_string(), + ] + } +} diff --git a/src/valheim.rs b/src/valheim.rs new file mode 100644 index 0000000..357fc46 --- /dev/null +++ b/src/valheim.rs @@ -0,0 +1,5 @@ +mod path; +mod unreserved_port; + +pub use path::{Path, Saves, Server}; +pub use unreserved_port::UnreservedPort; diff --git a/src/valheim/path.rs b/src/valheim/path.rs new file mode 100644 index 0000000..8ba1a4f --- /dev/null +++ b/src/valheim/path.rs @@ -0,0 +1,43 @@ +use std::{borrow::Borrow, env, ffi::OsStr, fmt, marker::PhantomData, path::PathBuf}; + +#[derive(Debug)] +pub struct Path(PathBuf, PhantomData); + +#[derive(Debug)] +pub enum Server {} +#[derive(Debug)] +pub enum Saves {} + +impl Default for Path { + fn default() -> Self { + let program_files = env::var("ProgramFiles(x86)").expect("!ProgramFiles(x86)"); + let mut path = PathBuf::from(program_files); + path.push(r"Steam\steamapps\common\Valheim dedicated server"); + + Self(path, PhantomData) + } +} + +impl Default for Path { + fn default() -> Self { + let mut local_appdata = env::var("LOCALAPPDATA").expect("!LOCALAPPDATA"); + local_appdata.push_str("Low"); + + let mut path = PathBuf::from(local_appdata); + path.push(r"IronGate\Valheim"); + + Self(path, PhantomData) + } +} + +impl fmt::Display for Path { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.0.to_string_lossy().borrow()) + } +} + +impl> From

for Path { + fn from(path: P) -> Self { + Self(PathBuf::from(path.as_ref()), PhantomData) + } +} diff --git a/src/valheim/unreserved_port.rs b/src/valheim/unreserved_port.rs new file mode 100644 index 0000000..beece67 --- /dev/null +++ b/src/valheim/unreserved_port.rs @@ -0,0 +1,44 @@ +use std::{error, fmt, str::FromStr}; + +#[derive(Debug)] +pub struct UnreservedPort(u16); + +impl FromStr for UnreservedPort { + type Err = ParsePortError; + + fn from_str(s: &str) -> Result { + match s.trim().parse::() { + Ok(num) => { + if num < 1 << 10 { + Err(ParsePortError(format!("Value '{}' is a reserved port.", num))) + } else { + Ok(Self(num)) + } + } + Err(err) => Err(ParsePortError(format!("Unable to parse value '{}': {}", s, err))) + } + } +} + +impl Default for UnreservedPort { + fn default() -> Self { + Self(2456) + } +} + +impl fmt::Display for UnreservedPort { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +#[derive(Debug)] +pub struct ParsePortError(String); + +impl fmt::Display for ParsePortError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.0) + } +} + +impl error::Error for ParsePortError {}