From 42e390cdb916c88ecd4bb53e341d4636bf8f89af Mon Sep 17 00:00:00 2001 From: IQuant Date: Thu, 23 May 2024 21:02:39 +0300 Subject: [PATCH] Automatic mod install --- README.md | 1 + noita-proxy/Cargo.lock | 1001 +++++++++++++++++++++++++++++++- noita-proxy/Cargo.toml | 8 +- noita-proxy/src/lib.rs | 97 +++- noita-proxy/src/main.rs | 10 +- noita-proxy/src/mod_manager.rs | 298 ++++++++++ noita-proxy/src/releases.rs | 250 ++++++++ prepare_release.py | 9 + quant.ew/files/version.lua | 1 - quant.ew/init.lua | 2 +- 10 files changed, 1636 insertions(+), 41 deletions(-) create mode 100644 noita-proxy/src/mod_manager.rs create mode 100644 noita-proxy/src/releases.rs delete mode 100644 quant.ew/files/version.lua diff --git a/README.md b/README.md index 89132f49..228003c0 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ There is a video guide/showcase of the mod by Gudyni (in russian): https://www.y Discord server: https://discord.gg/RVmAzBNE Special thanks to: + - Contributors. - @EvaisaDev for allowing to use code from Noita Arena mod. - @dextered for NoitaPatcher. - Creators of other libraries used in this project. diff --git a/noita-proxy/Cargo.lock b/noita-proxy/Cargo.lock index 9bad2d1f..8c777e93 100644 --- a/noita-proxy/Cargo.lock +++ b/noita-proxy/Cargo.lock @@ -28,12 +28,32 @@ dependencies = [ "serde", ] +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + [[package]] name = "ahash" version = "0.8.11" @@ -84,6 +104,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "arboard" version = "3.4.0" @@ -117,12 +146,33 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bincode" version = "1.3.3" @@ -249,6 +299,27 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "calloop" version = "0.12.4" @@ -313,6 +384,16 @@ dependencies = [ "libc", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clipboard" version = "0.5.0" @@ -355,7 +436,7 @@ dependencies = [ "cocoa-foundation", "core-foundation", "core-graphics", - "foreign-types", + "foreign-types 0.5.0", "libc", "objc", ] @@ -399,6 +480,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + [[package]] name = "core-foundation" version = "0.9.4" @@ -424,7 +511,7 @@ dependencies = [ "bitflags 1.3.2", "core-foundation", "core-graphics-types", - "foreign-types", + "foreign-types 0.5.0", "libc", ] @@ -448,6 +535,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.4.0" @@ -548,6 +650,32 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "deflate64" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -556,6 +684,16 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", +] + +[[package]] +name = "directories" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" +dependencies = [ + "dirs-sys", ] [[package]] @@ -568,6 +706,18 @@ dependencies = [ "dirs-sys-next", ] +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + [[package]] name = "dirs-sys-next" version = "0.1.2" @@ -585,6 +735,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dlib" version = "0.5.2" @@ -670,6 +831,17 @@ dependencies = [ "serde", ] +[[package]] +name = "egui-file-dialog" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f42a712dafe93177d5d99e3dd451783439d68c971ae88873b526c20ce37ce094" +dependencies = [ + "directories", + "egui", + "sysinfo", +] + [[package]] name = "egui-winit" version = "0.27.2" @@ -712,6 +884,15 @@ dependencies = [ "serde", ] +[[package]] +name = "encoding_rs" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" +dependencies = [ + "cfg-if", +] + [[package]] name = "enumn" version = "0.1.13" @@ -762,6 +943,12 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +[[package]] +name = "fastrand" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" + [[package]] name = "fdeflate" version = "0.3.4" @@ -787,6 +974,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + [[package]] name = "foreign-types" version = "0.5.0" @@ -794,7 +990,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" dependencies = [ "foreign-types-macros", - "foreign-types-shared", + "foreign-types-shared 0.3.1", ] [[package]] @@ -808,6 +1004,12 @@ dependencies = [ "syn", ] +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "foreign-types-shared" version = "0.3.1" @@ -823,6 +1025,56 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -854,6 +1106,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "gl_generator" version = "0.14.0" @@ -942,6 +1200,25 @@ dependencies = [ "gl_generator", ] +[[package]] +name = "h2" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -954,6 +1231,15 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -974,12 +1260,91 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +[[package]] +name = "hyper" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "icrate" version = "0.0.4" @@ -1024,6 +1389,21 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" + [[package]] name = "itoa" version = "1.0.11" @@ -1141,6 +1521,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.21" @@ -1153,6 +1539,16 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + [[package]] name = "malloc_buf" version = "0.0.6" @@ -1195,6 +1591,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + [[package]] name = "miniz_oxide" version = "0.7.2" @@ -1205,6 +1607,35 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "ndk" version = "0.8.0" @@ -1250,15 +1681,30 @@ dependencies = [ "clipboard", "crossbeam", "eframe", + "egui-file-dialog", "lz4_flex", + "poll-promise", "rand", + "reqwest", "serde", + "serde_json", "socket2", "steamworks", "tangled", + "thiserror", "tracing", "tracing-subscriber", "tungstenite", + "zip", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", ] [[package]] @@ -1271,6 +1717,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.18" @@ -1280,6 +1732,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "num_enum" version = "0.7.2" @@ -1401,12 +1863,71 @@ dependencies = [ "objc", ] +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +dependencies = [ + "bitflags 2.5.0", + "cfg-if", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "orbclient" version = "0.3.47" @@ -1454,18 +1975,54 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + [[package]] name = "percent-encoding" version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "pin-project-lite" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.30" @@ -1485,6 +2042,16 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "poll-promise" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6a58fecbf9da8965bcdb20ce4fd29788d1acee68ddbb64f0ba1b81bccdb7df" +dependencies = [ + "document-features", + "static_assertions", +] + [[package]] name = "polling" version = "3.7.0" @@ -1500,6 +2067,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1666,18 +2239,67 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "ron" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64", + "base64 0.21.7", "bitflags 2.5.0", "serde", "serde_derive", ] +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + [[package]] name = "rustix" version = "0.38.34" @@ -1691,6 +2313,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.1", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -1700,6 +2344,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1712,6 +2365,29 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "security-framework" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" +dependencies = [ + "bitflags 2.5.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317936bbbd05227752583946b9e66d7ce3b489f84e11a94a510b4437fef407d7" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "serde" version = "1.0.199" @@ -1732,6 +2408,29 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1861,6 +2560,12 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef6b00f8fe8eaaaff22cb9b70822a48c1a5d772bc682c202a57c0b438175845" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "2.0.60" @@ -1872,6 +2577,47 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sysinfo" +version = "0.30.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "732ffa00f53e6b2af46208fba5718d9662a421049204e156328b66791ffa15ae" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "windows", +] + +[[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 = "tangled" version = "0.2.0" @@ -1885,6 +2631,18 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if", + "fastrand", + "rustix", + "windows-sys 0.52.0", +] + [[package]] name = "test-log" version = "0.2.16" @@ -1908,18 +2666,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.59" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", @@ -1936,6 +2694,25 @@ dependencies = [ "once_cell", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + [[package]] name = "tinyvec" version = "1.6.0" @@ -1951,6 +2728,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml_datetime" version = "0.6.5" @@ -1968,12 +2784,41 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2029,6 +2874,12 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "ttf-parser" version = "0.20.0" @@ -2110,6 +2961,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.4" @@ -2126,6 +2983,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -2375,6 +3241,25 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + [[package]] name = "windows-sys" version = "0.45.0" @@ -2637,6 +3522,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "x11-clipboard" version = "0.3.3" @@ -2738,3 +3633,93 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b7a5a9285bd4ee13bdeb3f8a4917eb46557e53f270c783849db8bef37b0ad00" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap", + "lzma-rs", + "pbkdf2", + "rand", + "sha1", + "thiserror", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.10+zstd.1.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/noita-proxy/Cargo.toml b/noita-proxy/Cargo.toml index c4cc9fb5..2c55d52a 100644 --- a/noita-proxy/Cargo.toml +++ b/noita-proxy/Cargo.toml @@ -14,7 +14,7 @@ eframe = { version="0.27.2", features = ["persistence", "glow", "default_fonts"] tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing = "0.1.40" tangled = { path = "tangled" } -serde = { version = "1.0.199", features = ["serde_derive"] } +serde = { version = "1.0.199", features = ["serde_derive", "derive"] } bitcode = "0.6.0" lz4_flex = { version = "0.11.3", default_features = false, features = ["std"]} rand = "0.8.5" @@ -22,6 +22,12 @@ steamworks = "0.11.0" crossbeam = { version = "0.8.4", features = ["crossbeam-channel"] } clipboard = "0.5.0" socket2 = { version = "0.5.7", features = ["all"] } +egui-file-dialog = "0.5.0" +reqwest = { version = "0.12.4", features = ["blocking", "json"] } +serde_json = "1.0.117" +thiserror = "1.0.61" +poll-promise = "0.3.0" +zip = "1.3.1" [profile.dev] opt-level = 1 diff --git a/noita-proxy/src/lib.rs b/noita-proxy/src/lib.rs index a962ddd7..1ceb4675 100644 --- a/noita-proxy/src/lib.rs +++ b/noita-proxy/src/lib.rs @@ -4,12 +4,16 @@ use std::{ use bitcode::{Decode, Encode}; use clipboard::{ClipboardContext, ClipboardProvider}; -use eframe::egui::{self, Color32, Layout}; +use eframe::egui::{self, Align2, Color32, Layout}; +use mod_manager::{Modmanager, ModmanagerSettings}; +use serde::{Deserialize, Serialize}; use steamworks::{LobbyId, SteamAPIInitError}; use tangled::Peer; use tracing::info; pub mod messages; +mod mod_manager; +pub mod releases; #[derive(Debug, Decode, Encode, Clone)] pub struct GameSettings { @@ -20,7 +24,8 @@ pub struct GameSettings { pub mod net; enum AppState { - Init, + Connect, + ModManager, Netman { netman: Arc }, Error { message: String }, } @@ -34,7 +39,8 @@ impl SteamState { if env::var_os("NP_DISABLE_STEAM").is_some() { return Err(SteamAPIInitError::FailedGeneric("Disabled by env variable".to_string())) } - let (client, single) = steamworks::Client::init_app(881100)?; + let app_id = env::var("NP_APPID").ok().and_then(|x| x.parse().ok()); + let (client, single) = steamworks::Client::init_app(app_id.unwrap_or(881100))?; thread::spawn(move || { info!("Spawned steam callback thread"); loop { @@ -46,15 +52,51 @@ impl SteamState { } } -pub struct App { - state: AppState, - steam_state: Result, +#[derive(Debug, Serialize, Deserialize)] +struct AppSavedState { addr: String, debug_mode: bool, use_constant_seed: bool, + + } +impl Default for AppSavedState { + fn default() -> Self { + Self { + addr: "127.0.0.1:5123".to_string(), + debug_mode: false, + use_constant_seed: false, + } + } +} + +pub struct App { + state: AppState, + modmanager: Modmanager, + steam_state: Result, + saved_state: AppSavedState, + modmanager_settings: ModmanagerSettings +} + +const MODMANAGER: &str = "modman"; + impl App { + pub fn new(cc: &eframe::CreationContext<'_>) -> Self { + let saved_state = cc.storage.and_then(|storage| eframe::get_value(storage, eframe::APP_KEY)) + .unwrap_or_default(); + let modmanager_settings = cc.storage.and_then(|storage| eframe::get_value(storage, MODMANAGER)) + .unwrap_or_default(); + + info!("Creating the app..."); + Self { + state: AppState::ModManager, + modmanager: Modmanager::default(), + steam_state: SteamState::new(),saved_state, + modmanager_settings, + } + } + fn start_server(&mut self) { let bind_addr = "0.0.0.0:5123".parse().unwrap(); let peer = Peer::host(bind_addr, None).unwrap(); @@ -66,8 +108,8 @@ impl App { fn set_netman_settings(&mut self, netman: &Arc) { let mut settings = netman.settings.lock().unwrap(); - settings.debug_mode = self.debug_mode; - if !self.use_constant_seed { + settings.debug_mode = self.saved_state.debug_mode; + if !self.saved_state.use_constant_seed { settings.seed = rand::random(); } netman.accept_local.store(true, Ordering::SeqCst); @@ -107,34 +149,21 @@ impl App { } } -impl Default for App { - fn default() -> Self { - info!("Creating the app..."); - Self { - state: AppState::Init, - addr: "127.0.0.1:5123".to_string(), - debug_mode: false, - use_constant_seed: false, - steam_state: SteamState::new(), - } - } -} - impl eframe::App for App { fn update(&mut self, ctx: &eframe::egui::Context, _frame: &mut eframe::Frame) { ctx.request_repaint_after(Duration::from_secs(1)); match &self.state { - AppState::Init => { + AppState::Connect => { egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Noita Entangled Worlds proxy"); - ui.checkbox(&mut self.debug_mode, "Debug mode"); - ui.checkbox(&mut self.use_constant_seed, "Use specified seed"); + ui.checkbox(&mut self.saved_state.debug_mode, "Debug mode"); + ui.checkbox(&mut self.saved_state.use_constant_seed, "Use specified seed"); if ui.button("Host").clicked() { self.start_server(); } ui.separator(); - ui.text_edit_singleline(&mut self.addr); - let addr = self.addr.parse(); + ui.text_edit_singleline(&mut self.saved_state.addr); + let addr = self.saved_state.addr.parse(); ui.add_enabled_ui(addr.is_ok(), |ui| { if ui.button("Connect").clicked() { if let Ok(addr) = addr { @@ -221,9 +250,23 @@ impl eframe::App for App { }) .inner { - self.state = AppState::Init; + self.state = AppState::Connect; } } + AppState::ModManager => { + egui::Window::new("Mod manager").auto_sized().anchor(Align2::CENTER_CENTER, [0.0, 0.0]) + .show(ctx, |ui| { + self.modmanager.update(ctx, ui, &mut self.modmanager_settings, self.steam_state.as_mut().ok()) + }); + if self.modmanager.is_done() { + self.state = AppState::Connect; + } + }, }; } + + fn save(&mut self, storage: &mut dyn eframe::Storage) { + eframe::set_value(storage, eframe::APP_KEY, &self.saved_state); + eframe::set_value(storage, MODMANAGER, &self.modmanager_settings); + } } diff --git a/noita-proxy/src/main.rs b/noita-proxy/src/main.rs index 3b2b39b0..03e46df9 100644 --- a/noita-proxy/src/main.rs +++ b/noita-proxy/src/main.rs @@ -1,4 +1,4 @@ -use eframe::NativeOptions; +use eframe::{egui::ViewportBuilder, NativeOptions}; use noita_proxy::App; use tracing::level_filters::LevelFilter; use tracing_subscriber::EnvFilter; @@ -14,7 +14,11 @@ fn main() -> Result<(), eframe::Error> { tracing::subscriber::set_global_default(my_subscriber).expect("setting tracing default failed"); eframe::run_native( "Noita Proxy", - NativeOptions::default(), - Box::new(|_cc| Box::new(App::default())), + NativeOptions { + viewport: ViewportBuilder::default().with_min_inner_size([800.0, 600.0]), + follow_system_theme: false, + ..Default::default() + }, + Box::new(|cc| Box::new(App::new(cc))), ) } diff --git a/noita-proxy/src/mod_manager.rs b/noita-proxy/src/mod_manager.rs new file mode 100644 index 00000000..b7062833 --- /dev/null +++ b/noita-proxy/src/mod_manager.rs @@ -0,0 +1,298 @@ +use std::{ + fs::{self, File}, + io, + path::{Path, PathBuf}, +}; + +use eframe::egui::{Align2, Context, Ui}; +use egui_file_dialog::{DialogState, FileDialog}; +use poll_promise::Promise; +use serde::{Deserialize, Serialize}; +use steamworks::AppId; +use tracing::{error, info}; + +use crate::{ + releases::{get_release_by_tag, Downloader, ReleasesError, Version}, + SteamState, +}; + +#[derive(Default)] +enum State { + #[default] + JustStarted, + IsAutomaticPathOk, + SelectPath, + PreCheckMod, + InvalidPath, + CheckMod, + Done, + DownloadMod(Promise>), + Error(io::Error), + ReleasesError(ReleasesError), + UnpackMod(Promise>), + ConfirmInstall, +} + +pub struct Modmanager { + state: State, + file_dialog: FileDialog, +} + +impl Default for Modmanager { + fn default() -> Self { + Self { + state: Default::default(), + file_dialog: FileDialog::default() + .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) + .title("Select path to noita.exe"), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct ModmanagerSettings { + game_path: PathBuf, +} + +impl ModmanagerSettings { + fn try_find_game_path(&mut self, steam_state: Option<&mut SteamState>) { + info!("Trying to find game path"); + if let Some(state) = steam_state { + let apps = state.client.apps(); + let app_id = AppId::from(881100); + if apps.is_app_installed(app_id) { + let app_install_dir = apps.app_install_dir(app_id); + self.game_path = PathBuf::from(app_install_dir).join("noita.exe"); + info!("Found game path with steam: {}", self.game_path.display()) + } else { + info!("App not installed"); + } + } + } + fn mod_path(&self) -> PathBuf { + let mut path = self.game_path.clone(); + path.pop(); + path.push("mods"); + path.push("quant.ew"); + path + } +} + +impl Modmanager { + pub fn update( + &mut self, + ctx: &Context, + ui: &mut Ui, + settings: &mut ModmanagerSettings, + steam_state: Option<&mut SteamState>, + ) { + if let State::JustStarted = self.state { + if check_path_valid(&settings.game_path) { + info!("Path is valid, checking mod now"); + self.state = State::PreCheckMod; + } else { + settings.try_find_game_path(steam_state); + let could_find_automatically = check_path_valid(&settings.game_path); + if could_find_automatically { + self.state = State::IsAutomaticPathOk; + } else { + self.select_noita_file(); + } + } + } + + match &self.state { + State::JustStarted => unreachable!(), + State::IsAutomaticPathOk => { + ui.heading("Found a path automatically:"); + ui.label(settings.game_path.display().to_string()); + if ui.button("Use this one").clicked() { + self.state = State::PreCheckMod; + ctx.request_repaint(); + } + if ui.button("Select manually").clicked() { + self.select_noita_file(); + } + } + State::SelectPath => { + if let Some(path) = self.file_dialog.update(ctx).selected() { + settings.game_path = path.to_path_buf(); + if !check_path_valid(&settings.game_path) { + self.state = State::InvalidPath; + } + } + if self.file_dialog.state() == DialogState::Cancelled { + // self.select_noita_file() + self.state = State::JustStarted + } + } + State::InvalidPath => { + ui.label("This path is not valid"); + if ui.button("Select again").clicked() { + self.select_noita_file(); + } + } + State::PreCheckMod => { + // settings.game_path = PathBuf::new(); + // self.state = State::JustStarted; + ui.label("Will check mod install now..."); + self.state = State::CheckMod; + ctx.request_repaint(); + } + State::CheckMod => { + ctx.request_repaint(); + let mod_path = settings.mod_path(); + info!("Mod path: {}", mod_path.display()); + + self.state = match is_mod_ok(&mod_path) { + Ok(true) => State::Done, + Ok(false) => State::ConfirmInstall, + Err(err) => { + error!("Could not check if mod is ok: {}", err); + State::Error(err) + } + } + } + State::ConfirmInstall => { + let mod_path = settings.mod_path(); + ui.label(format!( + "Proxy will install the mod to {}", + mod_path.display() + )); + ui.horizontal(|ui| { + if ui.button("Confirm").clicked() { + let download_path = PathBuf::from("mod.zip"); + let tag = Version::current().into(); + let promise = Promise::spawn_thread("release-request", move || { + mod_downloader_for(tag, download_path) + }); + // Make sure we are deleting the right thing + assert!(mod_path.ends_with("quant.ew")); + fs::remove_dir_all(mod_path).ok(); + info!("Current mod deleted"); + + self.state = State::DownloadMod(promise) + } + if ui.button("Select a different path").clicked() { + self.select_noita_file() + } + }); + } + State::DownloadMod(promise) => { + ui.label("Downloading mod..."); + match promise.ready() { + Some(Ok(downloader)) => { + downloader.show_progress(ui); + match downloader.ready() { + Some(Ok(_)) => { + let path = downloader.path().to_path_buf(); + let directory = settings.mod_path(); + let promise: Promise> = + Promise::spawn_thread("unpack", move || { + extract_and_remove_zip(path, directory) + }); + self.state = State::UnpackMod(promise); + } + Some(Err(err)) => self.state = State::ReleasesError(err.clone()), + None => {} + } + } + Some(Err(err)) => self.state = State::ReleasesError(err.clone()), + None => { + ui.label("Receiving release info..."); + ui.spinner(); + } + } + } + State::UnpackMod(promise) => match promise.ready() { + Some(Ok(_)) => { + ui.label("Mod has been installed!"); + if ui.button("Continue").clicked() { + self.state = State::Done; + }; + } + Some(Err(err)) => { + self.state = State::ReleasesError(err.clone()); + } + None => { + ui.label("Unpacking mod"); + } + }, + State::Error(err) => { + ui.label(format!("Encountered an error: {}", err)); + if ui.button("Retry").clicked() { + self.state = State::JustStarted; + } + } + State::ReleasesError(err) => { + ui.label(format!("Encountered an error: {}", err)); + if ui.button("Retry").clicked() { + self.state = State::JustStarted; + } + } + State::Done => {} + } + } + + fn select_noita_file(&mut self) { + self.state = State::SelectPath; + self.file_dialog.select_file(); + } + + pub fn is_done(&self) -> bool { + if let State::Done = self.state { + true + } else { + false + } + } +} + +fn mod_downloader_for( + tag: crate::releases::Tag, + download_path: PathBuf, +) -> Result { + let client = reqwest::blocking::Client::builder() + .timeout(None) + .build() + .unwrap(); + get_release_by_tag(&client, tag) + .and_then(|release| release.get_release_assets(&client)) + .and_then(|asset_list| asset_list.find_by_name("quant.ew.zip").cloned()) + .and_then(|asset| asset.download(&client, &download_path)) +} + +fn extract_and_remove_zip(zip_file: PathBuf, extact_to: PathBuf) -> Result<(), ReleasesError> { + let reader = File::open(&zip_file)?; + let mut zip = zip::ZipArchive::new(reader)?; + info!("Extracting zip file"); + zip.extract(extact_to)?; + info!("Zip file extracted"); + fs::remove_file(&zip_file).ok(); + Ok(()) +} + +fn is_mod_ok(mod_path: &Path) -> io::Result { + if !mod_path.try_exists()? { + return Ok(false); + } + let version_path = mod_path.join("files/version.lua"); + let version = fs::read_to_string(&version_path) + .ok() + .and_then(|v| Version::parse_from_mod(&v)); + + info!("Mod version: {:?}", version); + + if Some(Version::current()) != version { + info!("Mod version differs"); + return Ok(false); + } + + info!("Mod is ok"); + + Ok(true) +} + +fn check_path_valid(game_path: &Path) -> bool { + game_path.ends_with("noita.exe") && game_path.exists() +} diff --git a/noita-proxy/src/releases.rs b/noita-proxy/src/releases.rs new file mode 100644 index 00000000..584c500f --- /dev/null +++ b/noita-proxy/src/releases.rs @@ -0,0 +1,250 @@ +use std::{ + fs::File, + io::{self, Read, Write}, + path::{Path, PathBuf}, + sync::{atomic::AtomicU64, Arc}, + time::Duration, +}; + +use eframe::egui::{self, Ui}; +use poll_promise::Promise; +use reqwest::blocking::Client; +use serde::Deserialize; +use thiserror::Error; +use zip::result::ZipError; + +#[derive(Debug, Error, Clone)] +pub enum ReleasesError { + #[error("Could not complete request: {0}")] + Request(Arc), + #[error("Asset not found")] + AssetNotFound, + #[error("Io error: {0}")] + Io(Arc), + #[error("Zip error: {0}")] + Zip(Arc), +} + +impl From for ReleasesError { + fn from(value: reqwest::Error) -> Self { + Self::Request(value.into()) + } +} + +impl From for ReleasesError { + fn from(value: io::Error) -> Self { + Self::Io(value.into()) + } +} + +impl From for ReleasesError { + fn from(value: ZipError) -> Self { + Self::Zip(value.into()) + } +} + +#[derive(Debug, Deserialize)] +pub struct Release { + pub tag_name: String, + assets_url: String, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct Asset { + url: String, + pub name: String, + pub size: u64, +} + +impl Asset { + pub fn download(&self, client: &Client, path: &Path) -> Result { + let shared = Arc::new(DownloaderSharedState { + progress: AtomicU64::new(0), + }); + let client = client.clone(); + let url = self.url.clone(); + let file = File::create(path)?; + let handle = { + let shared = shared.clone(); + Promise::spawn_thread("downloader", move || { + download_thread(client, url, shared, file) + }) + }; + + let downloader = Ok(Downloader { + shared, + handle, + path: path.to_path_buf(), + size: self.size, + }); + downloader + } +} + +fn download_thread( + client: Client, + url: String, + shared: Arc, + mut file: File, +) -> Result<(), ReleasesError> { + let mut response = client + .get(&url) + .header("Accept", "application/octet-stream") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-agent", "noita proxy") + .send()?; + let mut buf = [0; 4096]; + + loop { + let len = response.read(&mut buf)?; + shared + .progress + .fetch_add(len as u64, std::sync::atomic::Ordering::Relaxed); + if len == 0 { + break; + } + file.write_all(&buf[..len])?; + } + + Ok(()) +} + +struct DownloaderSharedState { + progress: AtomicU64, +} + +pub struct Downloader { + shared: Arc, + size: u64, + handle: Promise>, + path: PathBuf, +} + +impl Downloader { + pub fn progress(&self) -> (u64, u64) { + let written = self + .shared + .progress + .load(std::sync::atomic::Ordering::Relaxed); + (written, self.size) + } + + pub fn show_progress(&self, ui: &mut Ui) { + let (current, max) = self.progress(); + ui.label(format!("{} out of {} bytes", current, max)); + ui.add(egui::ProgressBar::new(current as f32 / max as f32)); + ui.ctx().request_repaint_after(Duration::from_millis(200)); + } + + pub fn ready(&self) -> Option<&Result<(), ReleasesError>> { + self.handle.ready() + } + + pub fn path(&self) -> &Path { + &self.path + } +} + +#[derive(Debug, Deserialize)] +pub struct AssetList(Vec); + +impl AssetList { + pub fn find_by_name(&self, name: &str) -> Result<&Asset, ReleasesError> { + self.0 + .iter() + .find(|asset| asset.name == name) + .ok_or(ReleasesError::AssetNotFound) + } +} + +pub struct Tag(String); + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + pub major: u32, + pub minor: u32, + pub patch: u32, +} + +impl Version { + pub fn parse_from_mod(version: &str) -> Option { + let strip_suffix = version.strip_prefix("return \"")?.strip_suffix("\"")?; + Self::parse_from_string(strip_suffix) + } + fn parse_from_string(version: &str) -> Option { + let mut nums = version.split("."); + let major = nums.next()?.parse().ok()?; + let minor = nums.next()?.parse().ok()?; + let patch = nums.next()?.parse().ok()?; + Some(Self { + major, + minor, + patch, + }) + } + pub fn current() -> Self { + Self::parse_from_string(env!("CARGO_PKG_VERSION")).expect("can always parse crate version") + } +} + +impl From for Tag { + fn from(value: Version) -> Self { + Self(format!("v{}.{}.{}", value.major, value.minor, value.patch)) + } +} + +impl Release { + pub fn get_release_assets(&self, client: &Client) -> Result { + let response = client + .get(&self.assets_url) + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-agent", "noita proxy") + .send()?; + + Ok(response.json()?) + } +} + +pub fn get_latest_release(client: &Client) -> Result { + let response = client + .get("https://api.github.com/repos/IntQuant/noita_entangled_worlds/releases/latest") + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-agent", "noita proxy") + .send()?; + + Ok(response.json()?) +} + +pub fn get_release_by_tag(client: &Client, tag: Tag) -> Result { + let response = client + .get(format!( + "https://api.github.com/repos/IntQuant/noita_entangled_worlds/releases/tags/{}", + tag.0 + )) + .header("Accept", "application/vnd.github+json") + .header("X-GitHub-Api-Version", "2022-11-28") + .header("User-agent", "noita proxy") + .send()?; + + Ok(response.json()?) +} + +#[cfg(test)] +mod test { + use crate::releases::{get_release_by_tag, Tag}; + + #[test] + fn release_assets() { + let client = reqwest::blocking::Client::new(); + // let release = get_latest_release(&client).unwrap(); + let release = get_release_by_tag(&client, Tag("v0.4.1".to_string())).unwrap(); + let assets = release.get_release_assets(&client).unwrap(); + println!("{:?}", release); + println!("{:?}", assets); + let mod_asset = assets.find_by_name("quant.ew.zip").unwrap(); + println!("{:?}", mod_asset); + assert_eq!(mod_asset.name, "quant.ew.zip") + } +} diff --git a/prepare_release.py b/prepare_release.py index 62024ead..e520ae0b 100644 --- a/prepare_release.py +++ b/prepare_release.py @@ -2,6 +2,12 @@ import subprocess from zipfile import ZipFile, ZIP_DEFLATED as COMPRESS_TYPE import shutil import os +import tomllib + +cargo_manifest = tomllib.load(open("noita-proxy/Cargo.toml", "rb")) +version = cargo_manifest["package"]["version"] + +print("Current version: ", version) def try_remove(path): try: @@ -47,3 +53,6 @@ with ZipFile("target/noita-proxy-linux.zip", "w") as release: print("Writing mod release...") shutil.make_archive("target/quant.ew", "zip", "quant.ew") + +with ZipFile("target/quant.ew.zip", "a") as release: + release.writestr("files/version.lua", f'return "{version}"') diff --git a/quant.ew/files/version.lua b/quant.ew/files/version.lua deleted file mode 100644 index 89f60ace..00000000 --- a/quant.ew/files/version.lua +++ /dev/null @@ -1 +0,0 @@ -return "dev-build" diff --git a/quant.ew/init.lua b/quant.ew/init.lua index 3d51d77b..0e58ad38 100755 --- a/quant.ew/init.lua +++ b/quant.ew/init.lua @@ -21,7 +21,7 @@ local enemy_sync = ctx.dofile_and_add_hooks("mods/quant.ew/files/src/enemy_sync. local world_sync = ctx.dofile_and_add_hooks("mods/quant.ew/files/src/world_sync.lua") local item_sync = ctx.dofile_and_add_hooks("mods/quant.ew/files/src/item_sync.lua") -local version = dofile_once("mods/quant.ew/files/version.lua") +local version = dofile_once("mods/quant.ew/files/version.lua") or "unknown (dev build)" ModLuaFileAppend("data/scripts/gun/gun.lua", "mods/quant.ew/files/append/gun.lua") ModLuaFileAppend("data/scripts/gun/gun_actions.lua", "mods/quant.ew/files/append/action_fix.lua")