diff --git a/Cargo.lock b/Cargo.lock index 52afa33c1..fd0a26814 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aes" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", + "opaque-debug", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -95,9 +107,9 @@ checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" [[package]] name = "async-compression" -version = "0.3.10" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00461f243d703f6999c8e7494f077799f1362720a55ae49a90ffe6214032fc0b" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "bzip2", "flate2", @@ -112,9 +124,9 @@ dependencies = [ [[package]] name = "async-tungstenite" -version = "0.20.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0388bb7a400072bbb41ceb75d65c3baefb2ea99672fa22e85278452cd9b58b" +checksum = "6a48bf42ab2178374a79853bceef600e279258c75049b20481b022d73c908882" dependencies = [ "futures-io", "futures-util", @@ -200,6 +212,12 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bincode" version = "2.0.0-rc.2" @@ -424,6 +442,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + [[package]] name = "cocoa" version = "0.24.1" @@ -521,6 +548,12 @@ dependencies = [ "windows-sys 0.42.0", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "convert_case" version = "0.4.0" @@ -698,9 +731,9 @@ dependencies = [ [[package]] name = "daedalus" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab1ff8f873475996ff3d755659e5e0fbe5a2d02d6fc84ff2b625874a8c446973" +checksum = "2a66b666b316919af243d8684f225b56aed72a83db52a93d61da89e2f588a7c7" dependencies = [ "bincode", "bytes", @@ -748,6 +781,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + [[package]] name = "derive_more" version = "0.99.17" @@ -790,6 +829,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer 0.10.4", "crypto-common", + "subtle", ] [[package]] @@ -798,7 +838,16 @@ version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" dependencies = [ - "dirs-sys", + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", ] [[package]] @@ -822,6 +871,18 @@ dependencies = [ "winapi", ] +[[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" @@ -883,13 +944,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.2.8" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "winapi", + "windows-sys 0.48.0", ] [[package]] @@ -939,7 +1000,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -983,6 +1044,15 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futf" version = "0.1.5" @@ -1429,6 +1499,15 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "html5ever" version = "0.25.2" @@ -1638,6 +1717,26 @@ dependencies = [ "cfb", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -1762,6 +1861,26 @@ dependencies = [ "treediff", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "kuchiki" version = "0.8.1" @@ -1806,9 +1925,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.1.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" [[package]] name = "lock_api" @@ -1841,7 +1960,7 @@ dependencies = [ "serde", "serde_json", "tracing", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -2036,6 +2155,33 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "notify" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +dependencies = [ + "bitflags", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys 0.42.0", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e23e9fa24f094b143c1eb61f90ac6457de87be6987bc70746e0179f7dbc9007b" +dependencies = [ + "notify", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -2228,6 +2374,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "os_info" version = "3.7.0" @@ -2309,11 +2461,22 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "paste" version = "1.0.12" @@ -2326,6 +2489,18 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", + "hmac", + "password-hash", + "sha2 0.10.6", +] + [[package]] name = "percent-encoding" version = "2.2.0" @@ -2675,6 +2850,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2682,7 +2866,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -2792,9 +2976,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.36.11" +version = "0.37.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4165c9963ab29e422d6c26fbc1d37f15bace6b2810221f9d925023480fcf0e" +checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d" dependencies = [ "bitflags", "errno", @@ -3207,6 +3391,12 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.109" @@ -3517,15 +3707,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "redox_syscall 0.3.5", "rustix", - "windows-sys 0.42.0", + "windows-sys 0.45.0", ] [[package]] @@ -3557,11 +3747,13 @@ dependencies = [ "bytes", "chrono", "daedalus", - "dirs", + "dirs 5.0.1", "dunce", "futures", "indicatif", "lazy_static", + "notify", + "notify-debouncer-mini", "paste", "regex", "reqwest", @@ -3571,16 +3763,17 @@ dependencies = [ "sha2 0.9.9", "sys-info", "tauri", + "tempfile", "thiserror", "tokio", "tokio-stream", "toml 0.7.3", "tracing", - "tracing-error 0.1.2", - "tracing-subscriber 0.2.25", + "tracing-error 0.2.0", + "tracing-subscriber 0.3.17", "url", "uuid 1.3.0", - "winreg 0.11.0", + "winreg 0.50.0", "zip", ] @@ -3592,7 +3785,7 @@ dependencies = [ "color-eyre", "daedalus", "dialoguer", - "dirs", + "dirs 4.0.0", "dunce", "eyre", "futures", @@ -3604,7 +3797,7 @@ dependencies = [ "tracing", "tracing-error 0.2.0", "tracing-futures", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", "url", "uuid 1.3.0", "webbrowser", @@ -3910,7 +4103,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" dependencies = [ "tracing", - "tracing-subscriber 0.3.16", + "tracing-subscriber 0.3.17", ] [[package]] @@ -3968,9 +4161,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers 0.1.0", "nu-ansi-term", @@ -4001,13 +4194,13 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788" +checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" dependencies = [ - "base64 0.13.1", "byteorder", "bytes", + "data-encoding", "http", "httparse", "log", @@ -4424,7 +4617,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -4433,7 +4626,7 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", ] [[package]] @@ -4468,12 +4661,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] @@ -4483,7 +4676,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -4492,15 +4694,30 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows-tokens" version = "0.39.0" @@ -4513,6 +4730,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.37.0" @@ -4531,6 +4754,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.37.0" @@ -4549,6 +4778,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.37.0" @@ -4567,6 +4802,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.37.0" @@ -4585,12 +4826,24 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.37.0" @@ -4609,6 +4862,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winnow" version = "0.4.1" @@ -4637,6 +4896,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "winres" version = "0.1.12" @@ -4731,32 +5000,38 @@ checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" [[package]] name = "zip" -version = "0.5.13" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +checksum = "7e92305c174683d78035cbf1b70e18db6329cc0f1b9cae0a52ca90bf5bfe7125" dependencies = [ + "aes", "byteorder", "bzip2", + "constant_time_eq", "crc32fast", + "crossbeam-utils", "flate2", - "thiserror", - "time 0.1.45", + "hmac", + "pbkdf2", + "sha1 0.10.5", + "time 0.3.20", + "zstd", ] [[package]] name = "zstd" -version = "0.9.2+zstd.1.5.1" +version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" +checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "4.1.3+zstd.1.5.1" +version = "5.0.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" +checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" dependencies = [ "libc", "zstd-sys", @@ -4764,10 +5039,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "1.6.2+zstd.1.5.1" +version = "2.0.8+zstd.1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" +checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/theseus/Cargo.toml b/theseus/Cargo.toml index c2cf42c2a..112769577 100644 --- a/theseus/Cargo.toml +++ b/theseus/Cargo.toml @@ -15,36 +15,40 @@ sha1 = { version = "0.6.1", features = ["std"]} sha2 = "0.9.9" url = "2.2" uuid = { version = "1.1", features = ["serde", "v4"] } -zip = "0.5" +zip = "0.6.5" async_zip = { version = "0.0.13", features = ["full"] } +tempfile = "3.5.0" chrono = { version = "0.4.19", features = ["serde"] } -daedalus = { version = "0.1.20" } -dirs = "4.0" +daedalus = { version = "0.1.21" } +dirs = "5.0.1" regex = "1.5" sys-info = "0.9.0" thiserror = "1.0" tracing = "0.1.37" -tracing-subscriber = "0.2" -tracing-error = "0.1" +tracing-subscriber = "0.3.17" +tracing-error = "0.2.0" paste = { version = "1.0"} tauri = { version = "1.2", optional = true} indicatif = { version = "0.17.3", optional = true } -async-tungstenite = { version = "0.20.0", features = ["tokio-runtime", "tokio-native-tls"] } +async-tungstenite = { version = "0.22.1", features = ["tokio-runtime", "tokio-native-tls"] } futures = "0.3" reqwest = { version = "0.11", features = ["json", "stream"] } tokio = { version = "1", features = ["full"] } tokio-stream = { version = "0.1", features = ["fs"] } +notify = { version = "5.1.0", default-features = false } +notify-debouncer-mini = { version = "0.2.1", default-features = false } + lazy_static = "1.4.0" dunce = "1.0.3" [target.'cfg(windows)'.dependencies] -winreg = "0.11.0" +winreg = "0.50.0" [features] tauri = ["dep:tauri"] diff --git a/theseus/library/JavaInfo.class b/theseus/library/JavaInfo.class new file mode 100644 index 000000000..69ffc97b4 Binary files /dev/null and b/theseus/library/JavaInfo.class differ diff --git a/theseus/library/JavaInfo.java b/theseus/library/JavaInfo.java new file mode 100644 index 000000000..542fc6078 --- /dev/null +++ b/theseus/library/JavaInfo.java @@ -0,0 +1,22 @@ +public final class JavaInfo { + private static final String[] CHECKED_PROPERTIES = new String[] { + "os.arch", + "java.version" + }; + + public static void main(String[] args) { + int returnCode = 0; + + for (String key : CHECKED_PROPERTIES) { + String property = System.getProperty(key); + + if (property != null) { + System.out.println(key + "=" + property); + } else { + returnCode = 1; + } + } + + System.exit(returnCode); + } +} \ No newline at end of file diff --git a/theseus/src/api/jre.rs b/theseus/src/api/jre.rs index 45844a84d..1d5316a7f 100644 --- a/theseus/src/api/jre.rs +++ b/theseus/src/api/jre.rs @@ -1,12 +1,16 @@ //! Authentication flow interface +use reqwest::Method; +use serde::Deserialize; use std::path::PathBuf; +use crate::event::emit::{emit_loading, init_loading}; +use crate::util::fetch::{fetch_advanced, fetch_json}; use crate::{ launcher::download, prelude::Profile, state::JavaGlobals, util::jre::{self, extract_java_majorminor_version, JavaVersion}, - State, + LoadingBarType, State, }; pub const JAVA_8_KEY: &str = "JAVA_8"; @@ -133,6 +137,87 @@ pub async fn find_java17_jres() -> crate::Result> { .collect()) } +pub async fn auto_install_java(java_version: u32) -> crate::Result { + let state = State::get().await?; + + let loading_bar = init_loading( + LoadingBarType::JavaDownload { + version: java_version, + }, + 100.0, + "Downloading java version", + ) + .await?; + + #[derive(Deserialize)] + struct Package { + pub download_url: String, + pub name: PathBuf, + } + + emit_loading(&loading_bar, 0.0, Some("Fetching java version")).await?; + let packages = fetch_json::>( + Method::GET, + &format!( + "https://api.azul.com/metadata/v1/zulu/packages?arch={}&java_version={}&os={}&archive_type=zip&javafx_bundled=false&java_package_type=jre&page_size=1", + std::env::consts::ARCH, java_version, std::env::consts::OS + ), + None, + None, + &state.fetch_semaphore, + ).await?; + emit_loading(&loading_bar, 10.0, Some("Downloading java version")).await?; + + if let Some(download) = packages.first() { + let file = fetch_advanced( + Method::GET, + &download.download_url, + None, + None, + None, + Some((&loading_bar, 80.0)), + &state.fetch_semaphore, + ) + .await?; + + let path = state.directories.java_versions_dir(); + + if path.exists() { + tokio::fs::remove_dir_all(&path).await?; + } + + let mut archive = zip::ZipArchive::new(std::io::Cursor::new(file)) + .map_err(|_| { + crate::Error::from(crate::ErrorKind::InputError( + "Failed to read java zip".to_string(), + )) + })?; + + emit_loading(&loading_bar, 0.0, Some("Extracting java")).await?; + archive.extract(&path).map_err(|_| { + crate::Error::from(crate::ErrorKind::InputError( + "Failed to extract java zip".to_string(), + )) + })?; + emit_loading(&loading_bar, 100.0, Some("Done extracting java")).await?; + Ok(path + .join( + download + .name + .file_stem() + .unwrap_or_default() + .to_string_lossy() + .to_string(), + ) + .join(format!("zulu-{}.jre/Contents/Home/bin/java", java_version))) + } else { + Err(crate::ErrorKind::LauncherError(format!( + "No Java Version found for Java version {}, OS {}, and Architecture {}", + java_version, std::env::consts::OS, std::env::consts::ARCH, + )).into()) + } +} + // Get all JREs that exist on the system pub async fn get_all_jre() -> crate::Result> { Ok(jre::get_all_jre().await?) @@ -148,3 +233,14 @@ pub async fn validate_globals() -> crate::Result { pub async fn check_jre(path: PathBuf) -> crate::Result> { Ok(jre::check_java_at_filepath(&path).await) } + +// Gets maximum memory in KiB. +pub async fn get_max_memory() -> crate::Result { + Ok(sys_info::mem_info() + .map_err(|_| { + crate::Error::from(crate::ErrorKind::LauncherError( + "Unable to get computer memory".to_string(), + )) + })? + .total) +} diff --git a/theseus/src/api/pack.rs b/theseus/src/api/pack.rs index afb5b969d..680ae5ed3 100644 --- a/theseus/src/api/pack.rs +++ b/theseus/src/api/pack.rs @@ -6,8 +6,7 @@ use crate::event::emit::{ }; use crate::event::{LoadingBarId, LoadingBarType}; use crate::state::{ - LinkedData, ModrinthProject, ModrinthVersion, Profile, ProfileInstallStage, - SideType, + LinkedData, ModrinthProject, ModrinthVersion, ProfileInstallStage, SideType, }; use crate::util::fetch::{ fetch, fetch_advanced, fetch_json, fetch_mirrors, write, write_cached_icon, @@ -478,7 +477,6 @@ async fn install_pack( if let Some(profile_val) = crate::api::profile::get(&profile, None).await? { - Profile::sync_projects_task(profile.clone()); crate::launcher::install_minecraft( &profile_val, Some(loading_bar), diff --git a/theseus/src/api/profile.rs b/theseus/src/api/profile.rs index 8954147e8..c1417ed8f 100644 --- a/theseus/src/api/profile.rs +++ b/theseus/src/api/profile.rs @@ -5,7 +5,6 @@ use crate::state::ProjectMetadata; use crate::{ auth::{self, refresh}, event::{emit::emit_profile, ProfilePayloadType}, - launcher::download, state::MinecraftChild, }; pub use crate::{ @@ -109,40 +108,10 @@ pub async fn list( .collect()) } -/// Query + sync profile's projects with the UI from the FS -#[tracing::instrument] -pub async fn sync(path: &Path) -> crate::Result<()> { - Box::pin({ - async move { - let state = State::get().await?; - let result = { - let mut profiles: tokio::sync::RwLockWriteGuard< - crate::state::Profiles, - > = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(path) { - profile.sync_projects().await?; - Ok(()) - } else { - Err(crate::ErrorKind::UnmanagedProfileError( - path.display().to_string(), - ) - .as_error()) - } - }; - State::sync().await?; - result - } - }) - .await -} - /// Installs/Repairs a profile #[tracing::instrument] pub async fn install(path: &Path) -> crate::Result<()> { - let profile = get(path, None).await?; - - if let Some(profile) = profile { + if let Some(profile) = get(path, None).await? { crate::launcher::install_minecraft(&profile, None).await?; } else { return Err(crate::ErrorKind::UnmanagedProfileError( @@ -155,11 +124,8 @@ pub async fn install(path: &Path) -> crate::Result<()> { } pub async fn update_all(profile_path: &Path) -> crate::Result<()> { - let state = State::get().await?; Box::pin(async move { - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile_path) { + if let Some(profile) = get(profile_path, None).await? { let loading_bar = init_loading( LoadingBarType::ProfileUpdate { profile_path: profile.path.clone(), @@ -187,8 +153,6 @@ pub async fn update_all(profile_path: &Path) -> crate::Result<()> { ) .await?; - profile.sync_projects().await?; - Ok(()) } else { Err(crate::ErrorKind::UnmanagedProfileError( @@ -204,10 +168,7 @@ pub async fn update_project( profile_path: &Path, project_path: &Path, ) -> crate::Result { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile_path) { + if let Some(profile) = get(profile_path, None).await? { if let Some(project) = profile.projects.get(project_path) { if let ProjectMetadata::Modrinth { update_version: Some(update_version), @@ -222,15 +183,20 @@ pub async fn update_project( profile.remove_project(project_path, Some(true)).await?; } - let value = profile.projects.remove(project_path); - if let Some(mut project) = value { - if let ProjectMetadata::Modrinth { - ref mut version, .. - } = project.metadata - { - *version = Box::new(new_version); + let state = State::get().await?; + let mut profiles = state.profiles.write().await; + if let Some(profile) = profiles.0.get_mut(project_path) { + let value = profile.projects.remove(project_path); + if let Some(mut project) = value { + if let ProjectMetadata::Modrinth { + ref mut version, + .. + } = project.metadata + { + *version = Box::new(new_version); + } + profile.projects.insert(path.clone(), project); } - profile.projects.insert(path.clone(), project); } return Ok(path); @@ -249,57 +215,15 @@ pub async fn update_project( } } -/// Replaces a project given a new version ID -pub async fn replace_project( - profile_path: &Path, - project: &Path, - version_id: String, -) -> crate::Result { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile_path) { - let (path, new_version) = - profile.add_project_version(version_id).await?; - - if path != project { - profile.remove_project(project, Some(true)).await?; - } - - let value = profile.projects.remove(project); - if let Some(mut project) = value { - if let ProjectMetadata::Modrinth { - ref mut version, .. - } = project.metadata - { - *version = Box::new(new_version); - } - profile.projects.insert(path.clone(), project); - } - - Ok(path) - } else { - Err(crate::ErrorKind::UnmanagedProfileError( - profile_path.display().to_string(), - ) - .as_error()) - } -} - /// Add a project from a version #[tracing::instrument] pub async fn add_project_from_version( profile_path: &Path, version_id: String, ) -> crate::Result { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile_path) { + if let Some(profile) = get(profile_path, None).await? { let (path, _) = profile.add_project_version(version_id).await?; - Profile::sync_projects_task(profile.path.clone()); - Ok(path) } else { Err(crate::ErrorKind::UnmanagedProfileError( @@ -316,10 +240,7 @@ pub async fn add_project_from_path( path: &Path, project_type: Option, ) -> crate::Result { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile_path) { + if let Some(profile) = get(profile_path, None).await? { let file = fs::read(path).await?; let file_name = path .file_name() @@ -335,8 +256,6 @@ pub async fn add_project_from_path( ) .await?; - Profile::sync_projects_task(profile.path.clone()); - Ok(path) } else { Err(crate::ErrorKind::UnmanagedProfileError( @@ -352,10 +271,7 @@ pub async fn toggle_disable_project( profile: &Path, project: &Path, ) -> crate::Result<()> { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile) { + if let Some(profile) = get(profile, None).await? { profile.toggle_disable_project(project).await?; Ok(()) @@ -373,10 +289,7 @@ pub async fn remove_project( profile: &Path, project: &Path, ) -> crate::Result<()> { - let state = State::get().await?; - let mut profiles = state.profiles.write().await; - - if let Some(profile) = profiles.0.get_mut(profile) { + if let Some(profile) = get(profile, None).await? { profile.remove_project(project, None).await?; Ok(()) @@ -423,7 +336,6 @@ pub async fn run_credentials( Box::pin(async move { let state = State::get().await?; let settings = state.settings.read().await; - let metadata = state.metadata.read().await; let profile = get(path, None).await?.ok_or_else(|| { crate::ErrorKind::OtherError(format!( "Tried to run a nonexistent or unloaded profile at path {}!", @@ -431,25 +343,6 @@ pub async fn run_credentials( )) })?; - let version = metadata - .minecraft - .versions - .iter() - .find(|it| it.id == profile.metadata.game_version) - .ok_or_else(|| { - crate::ErrorKind::LauncherError(format!( - "Invalid or unknown Minecraft version: {}", - profile.metadata.game_version - )) - })?; - let version_info = download::download_version_info( - &state, - version, - profile.metadata.loader_version.as_ref(), - None, - None, - ) - .await?; let pre_launch_hooks = &profile.hooks.as_ref().unwrap_or(&settings.hooks).pre_launch; if let Some(hook) = pre_launch_hooks { @@ -473,49 +366,6 @@ pub async fn run_credentials( } } - let java_version = match profile.java { - // Load profile-specific Java implementation choice - // (This defaults to Daedalus-decided key on init, but can be changed by the user) - Some(JavaSettings { - jre_key: Some(ref jre_key), - .. - }) => settings.java_globals.get(jre_key), - // Fall back to Daedalus-decided key if no profile-specific key is set - _ => { - match version_info - .java_version - .as_ref() - .map(|it| it.major_version) - .unwrap_or(0) - { - 0..=16 => settings - .java_globals - .get(&crate::jre::JAVA_8_KEY.to_string()), - 17 => settings - .java_globals - .get(&crate::jre::JAVA_17_KEY.to_string()), - _ => settings - .java_globals - .get(&crate::jre::JAVA_18PLUS_KEY.to_string()), - } - } - }; - let java_version = java_version.as_ref().ok_or_else(|| { - crate::ErrorKind::LauncherError(format!( - "No Java stored for version {}", - version_info.java_version.map_or(8, |it| it.major_version), - )) - })?; - - // Get the path to the Java executable from the chosen Java implementation key - let java_install: &Path = &PathBuf::from(&java_version.path); - if !java_install.exists() { - return Err(crate::ErrorKind::LauncherError(format!( - "Could not find Java install: {}", - java_install.display() - )) - .as_error()); - } let java_args = profile .java .as_ref() @@ -550,7 +400,6 @@ pub async fn run_credentials( }; let mc_process = crate::launcher::launch_minecraft( - java_install, java_args, env_args, wrapper, diff --git a/theseus/src/api/profile_create.rs b/theseus/src/api/profile_create.rs index 6784425b9..8daac110e 100644 --- a/theseus/src/api/profile_create.rs +++ b/theseus/src/api/profile_create.rs @@ -1,9 +1,7 @@ //! Theseus profile management interface -use crate::event::emit::emit_warning; use crate::state::LinkedData; use crate::{ event::{emit::emit_profile, ProfilePayloadType}, - jre, prelude::ModLoader, }; pub use crate::{ @@ -84,7 +82,12 @@ pub async fn profile_create( &canonicalize(&path)?.display() ); let loader = if modloader != ModLoader::Vanilla { - get_loader_version_from_loader(game_version.clone(), modloader, loader_version).await? + get_loader_version_from_loader( + game_version.clone(), + modloader, + loader_version, + ) + .await? } else { None }; @@ -112,19 +115,6 @@ pub async fn profile_create( profile.metadata.linked_data = linked_data; - // Attempts to find optimal JRE for the profile from the JavaGlobals - // Finds optimal key, and see if key has been set in JavaGlobals - let settings = state.settings.read().await; - let optimal_version_key = jre::get_optimal_jre_key(&profile).await?; - if settings.java_globals.get(&optimal_version_key).is_some() { - profile.java = Some(JavaSettings { - jre_key: Some(optimal_version_key), - extra_arguments: None, - }); - } else { - emit_warning(&format!("Could not detect optimal JRE: {optimal_version_key}, falling back to system default.")).await?; - } - emit_profile( uuid, path.clone(), @@ -144,7 +134,8 @@ pub async fn profile_create( State::sync().await?; Ok(path) - }).await + }) + .await } pub(crate) async fn get_loader_version_from_loader( diff --git a/theseus/src/error.rs b/theseus/src/error.rs index bf27b7eeb..cfd624750 100644 --- a/theseus/src/error.rs +++ b/theseus/src/error.rs @@ -84,6 +84,9 @@ pub enum ErrorKind { #[error("Error: {0}")] OtherError(String), + + #[error("File watching error: {0}")] + NotifyError(#[from] notify::Error), } #[derive(Debug)] diff --git a/theseus/src/event/mod.rs b/theseus/src/event/mod.rs index f77f2cdb3..997f29227 100644 --- a/theseus/src/event/mod.rs +++ b/theseus/src/event/mod.rs @@ -149,6 +149,9 @@ impl Drop for LoadingBar { #[serde(rename_all = "snake_case")] pub enum LoadingBarType { StateInit, + JavaDownload { + version: u32, + }, PackFileDownload { profile_path: PathBuf, pack_name: String, diff --git a/theseus/src/launcher/args.rs b/theseus/src/launcher/args.rs index f9fceac93..09721e1d6 100644 --- a/theseus/src/launcher/args.rs +++ b/theseus/src/launcher/args.rs @@ -22,12 +22,13 @@ pub fn get_class_paths( libraries_path: &Path, libraries: &[Library], client_path: &Path, + java_arch: &str, ) -> crate::Result { let mut cps = libraries .iter() .filter_map(|library| { if let Some(rules) = &library.rules { - if !rules.iter().all(parse_rule) { + if !rules.iter().any(|x| parse_rule(x, java_arch)) { return None; } } @@ -53,19 +54,20 @@ pub fn get_class_paths( .to_string(), ); - Ok(cps.join(classpath_separator())) + Ok(cps.join(classpath_separator(java_arch))) } pub fn get_class_paths_jar>( libraries_path: &Path, libraries: &[T], + java_arch: &str, ) -> crate::Result { let cps = libraries .iter() .map(|library| get_lib_path(libraries_path, library.as_ref(), false)) .collect::, _>>()?; - Ok(cps.join(classpath_separator())) + Ok(cps.join(classpath_separator(java_arch))) } pub fn get_lib_path( @@ -91,6 +93,8 @@ pub fn get_lib_path( Ok(path.to_string_lossy().to_string()) } + +#[allow(clippy::too_many_arguments)] pub fn get_jvm_arguments( arguments: Option<&[Argument]>, natives_path: &Path, @@ -99,19 +103,26 @@ pub fn get_jvm_arguments( version_name: &str, memory: MemorySettings, custom_args: Vec, + java_arch: &str, ) -> crate::Result> { let mut parsed_arguments = Vec::new(); if let Some(args) = arguments { - parse_arguments(args, &mut parsed_arguments, |arg| { - parse_jvm_argument( - arg.to_string(), - natives_path, - libraries_path, - class_paths, - version_name, - ) - })?; + parse_arguments( + args, + &mut parsed_arguments, + |arg| { + parse_jvm_argument( + arg.to_string(), + natives_path, + libraries_path, + class_paths, + version_name, + java_arch, + ) + }, + java_arch, + )?; } else { parsed_arguments.push(format!( "-Djava.library.path={}", @@ -147,6 +158,7 @@ fn parse_jvm_argument( libraries_path: &Path, class_paths: &str, version_name: &str, + java_arch: &str, ) -> crate::Result { argument.retain(|c| !c.is_whitespace()); Ok(argument @@ -174,7 +186,7 @@ fn parse_jvm_argument( })? .to_string_lossy(), ) - .replace("${classpath_separator}", classpath_separator()) + .replace("${classpath_separator}", classpath_separator(java_arch)) .replace("${launcher_name}", "theseus") .replace("${launcher_version}", env!("CARGO_PKG_VERSION")) .replace("${version_name}", version_name) @@ -192,13 +204,37 @@ pub fn get_minecraft_arguments( assets_directory: &Path, version_type: &VersionType, resolution: WindowSize, + java_arch: &str, ) -> crate::Result> { if let Some(arguments) = arguments { let mut parsed_arguments = Vec::new(); - parse_arguments(arguments, &mut parsed_arguments, |arg| { - parse_minecraft_argument( - arg, + parse_arguments( + arguments, + &mut parsed_arguments, + |arg| { + parse_minecraft_argument( + arg, + &credentials.access_token, + &credentials.username, + &credentials.id, + version, + asset_index_name, + game_directory, + assets_directory, + version_type, + resolution, + ) + }, + java_arch, + )?; + + Ok(parsed_arguments) + } else if let Some(legacy_arguments) = legacy_arguments { + let mut parsed_arguments = Vec::new(); + for x in legacy_arguments.split(' ') { + parsed_arguments.push(parse_minecraft_argument( + &x.replace(' ', TEMPORARY_REPLACE_CHAR), &credentials.access_token, &credentials.username, &credentials.id, @@ -208,26 +244,9 @@ pub fn get_minecraft_arguments( assets_directory, version_type, resolution, - ) - })?; - + )?); + } Ok(parsed_arguments) - } else if let Some(legacy_arguments) = legacy_arguments { - Ok(parse_minecraft_argument( - &legacy_arguments.replace(' ', TEMPORARY_REPLACE_CHAR), - &credentials.access_token, - &credentials.username, - &credentials.id, - version, - asset_index_name, - game_directory, - assets_directory, - version_type, - resolution, - )? - .split(' ') - .map(|x| x.to_string()) - .collect()) } else { Ok(Vec::new()) } @@ -300,6 +319,7 @@ fn parse_arguments( arguments: &[Argument], parsed_arguments: &mut Vec, parse_function: F, + java_arch: &str, ) -> crate::Result<()> where F: Fn(&str) -> crate::Result, @@ -314,7 +334,7 @@ where } } Argument::Ruled { rules, value } => { - if rules.iter().all(parse_rule) { + if rules.iter().any(|x| parse_rule(x, java_arch)) { match value { ArgumentValue::Single(arg) => { parsed_arguments.push(parse_function( diff --git a/theseus/src/launcher/download.rs b/theseus/src/launcher/download.rs index 174b4aff3..feb4366d5 100644 --- a/theseus/src/launcher/download.rs +++ b/theseus/src/launcher/download.rs @@ -24,6 +24,7 @@ pub async fn download_minecraft( st: &State, version: &GameVersionInfo, loading_bar: &LoadingBarId, + java_arch: &str, ) -> crate::Result<()> { tracing::info!("Downloading Minecraft version {}", version.id); // 5 @@ -45,7 +46,7 @@ pub async fn download_minecraft( // Total loading sums to 90/60 download_client(st, version, Some(loading_bar)), // 10 download_assets(st, version.assets == "legacy", &assets_index, Some(loading_bar), amount), // 40 - download_libraries(st, version.libraries.as_slice(), &version.id, Some(loading_bar), amount) // 40 + download_libraries(st, version.libraries.as_slice(), &version.id, Some(loading_bar), amount, java_arch) // 40 }?; tracing::info!("Done downloading Minecraft!"); @@ -247,6 +248,7 @@ pub async fn download_libraries( version: &str, loading_bar: Option<&LoadingBarId>, loading_amount: f64, + java_arch: &str, ) -> crate::Result<()> { Box::pin(async move { tracing::debug!("Loading libraries"); @@ -260,10 +262,12 @@ pub async fn download_libraries( stream::iter(libraries.iter()) .map(Ok::<&Library, crate::Error>), None, loading_bar,loading_amount,num_files, None,|library| async move { if let Some(rules) = &library.rules { - if !rules.iter().all(super::parse_rule) { + if !rules.iter().any(|x| super::parse_rule(x, java_arch)) { + tracing::trace!("Skipped library {}", &library.name); return Ok(()); } } + tokio::try_join! { async { let artifact_path = d::get_path_from_artifact(&library.name)?; @@ -278,24 +282,23 @@ pub async fn download_libraries( let bytes = fetch(&artifact.url, Some(&artifact.sha1), &st.fetch_semaphore) .await?; write(&path, &bytes, &st.io_semaphore).await?; - tracing::trace!("Fetched library {}", &library.name); + tracing::trace!("Fetched library {} to path {:?}", &library.name, &path); Ok::<_, crate::Error>(()) } - None => { + _ => { let url = [ library .url .as_deref() - .unwrap_or("https://libraries.minecraft.net"), + .unwrap_or("https://libraries.minecraft.net/"), &artifact_path ].concat(); let bytes = fetch(&url, None, &st.fetch_semaphore).await?; write(&path, &bytes, &st.io_semaphore).await?; - tracing::trace!("Fetched library {}", &library.name); + tracing::trace!("Fetched library {} to path {:?}", &library.name, &path); Ok::<_, crate::Error>(()) } - _ => Ok(()) } }, async { @@ -304,7 +307,7 @@ pub async fn download_libraries( library .natives .as_ref()? - .get(&Os::native_arch())?, + .get(&Os::native_arch(java_arch))?, library .downloads .as_ref()? diff --git a/theseus/src/launcher/mod.rs b/theseus/src/launcher/mod.rs index d69d513f5..c7e073570 100644 --- a/theseus/src/launcher/mod.rs +++ b/theseus/src/launcher/mod.rs @@ -1,6 +1,8 @@ //! Logic for launching Minecraft use crate::event::emit::{emit_loading, init_or_edit_loading}; use crate::event::{LoadingBarId, LoadingBarType}; +use crate::jre::{JAVA_17_KEY, JAVA_18PLUS_KEY, JAVA_8_KEY}; +use crate::prelude::JavaVersion; use crate::state::ProfileInstallStage; use crate::{ process, @@ -8,10 +10,11 @@ use crate::{ State, }; use daedalus as d; +use daedalus::minecraft::VersionInfo; use dunce::canonicalize; use st::Profile; use std::fs; -use std::{path::Path, process::Stdio, sync::Arc}; +use std::{process::Stdio, sync::Arc}; use tokio::process::Command; use uuid::Uuid; @@ -21,18 +24,18 @@ pub mod auth; pub mod download; #[tracing::instrument] -pub fn parse_rule(rule: &d::minecraft::Rule) -> bool { +pub fn parse_rule(rule: &d::minecraft::Rule, java_version: &str) -> bool { use d::minecraft::{Rule, RuleAction}; let res = match rule { Rule { os: Some(ref os), .. - } => crate::util::platform::os_rule(os), + } => crate::util::platform::os_rule(os, java_version), Rule { features: Some(ref features), .. } => features.has_demo_resolution.unwrap_or(false), - _ => true, + _ => false, }; match rule.action { @@ -54,6 +57,40 @@ macro_rules! processor_rules { } } +async fn get_java_version_from_profile( + profile: &Profile, + version_info: &VersionInfo, +) -> crate::Result { + if let Some(java) = profile.java.clone().and_then(|x| x.override_version) { + Ok(java) + } else { + let optimal_keys = match version_info + .java_version + .as_ref() + .map(|it| it.major_version) + .unwrap_or(8) + { + 0..=15 => vec![JAVA_8_KEY, JAVA_17_KEY, JAVA_18PLUS_KEY], + 16..=17 => vec![JAVA_17_KEY, JAVA_18PLUS_KEY], + _ => vec![JAVA_18PLUS_KEY], + }; + + let state = State::get().await?; + let settings = state.settings.read().await; + + for key in optimal_keys { + if let Some(java) = settings.java_globals.get(&key.to_string()) { + return Ok(java.clone()); + } + } + + Err(crate::ErrorKind::LauncherError( + "No available java installation".to_string(), + ) + .into()) + } +} + #[tracing::instrument(skip(profile))] pub async fn install_minecraft( profile: &Profile, @@ -112,8 +149,10 @@ pub async fn install_minecraft( ) .await?; + let java_version = get_java_version_from_profile(profile, &version_info).await?; + // Download minecraft (5-90) - download::download_minecraft(&state, &version_info, &loading_bar).await?; + download::download_minecraft(&state, &version_info, &loading_bar, &java_version.architecture).await?; if let Some(processors) = &version_info.processors { let client_path = state @@ -157,11 +196,12 @@ pub async fn install_minecraft( cp.push(processor.jar.clone()) }); - let child = Command::new("java") + let child = Command::new(&java_version.path) .arg("-cp") .arg(args::get_class_paths_jar( &state.directories.libraries_dir(), &cp, + &java_version.architecture )?) .arg( args::get_processor_main_class(args::get_lib_path( @@ -231,7 +271,6 @@ pub async fn install_minecraft( #[allow(clippy::too_many_arguments)] pub async fn launch_minecraft( - java_install: &Path, java_args: &[String], env_args: &[(String, String)], wrapper: &Option, @@ -286,6 +325,8 @@ pub async fn launch_minecraft( ) .await?; + let java_version = get_java_version_from_profile(profile, &version_info).await?; + let client_path = state .directories .version_dir(&version_jar) @@ -294,9 +335,9 @@ pub async fn launch_minecraft( let args = version_info.arguments.clone().unwrap_or_default(); let mut command = match wrapper { Some(hook) => { - wrap_ref_builder!(it = Command::new(hook) => {it.arg(java_install)}) + wrap_ref_builder!(it = Command::new(hook) => {it.arg(&java_version.path)}) } - None => Command::new(String::from(java_install.to_string_lossy())), + None => Command::new(&java_version.path), }; let env_args = Vec::from(env_args); @@ -324,10 +365,12 @@ pub async fn launch_minecraft( &state.directories.libraries_dir(), version_info.libraries.as_slice(), &client_path, + &java_version.architecture )?, &version_jar, *memory, Vec::from(java_args), + &java_version.architecture )? .into_iter() .collect::>(), @@ -345,6 +388,7 @@ pub async fn launch_minecraft( &state.directories.assets_dir(), &version.type_, *resolution, + &java_version.architecture )? .into_iter() .collect::>(), @@ -362,7 +406,7 @@ pub async fn launch_minecraft( // Get Modrinth logs directories let datetime_string = - chrono::Local::now().format("%Y%m%y_%H%M%S").to_string(); + chrono::Local::now().format("%Y%m%d_%H%M%S").to_string(); let logs_dir = { let st = State::get().await?; st.directories diff --git a/theseus/src/state/dirs.rs b/theseus/src/state/dirs.rs index 68b81f3e7..5ca86c0cd 100644 --- a/theseus/src/state/dirs.rs +++ b/theseus/src/state/dirs.rs @@ -44,6 +44,12 @@ impl DirectoryInfo { self.config_dir.join("meta") } + /// Get the Minecraft java versions metadata directory + #[inline] + pub fn java_versions_dir(&self) -> PathBuf { + self.metadata_dir().join("java_versions") + } + /// Get the Minecraft versions metadata directory #[inline] pub fn versions_dir(&self) -> PathBuf { diff --git a/theseus/src/state/mod.rs b/theseus/src/state/mod.rs index f741676ff..e969106e6 100644 --- a/theseus/src/state/mod.rs +++ b/theseus/src/state/mod.rs @@ -1,5 +1,6 @@ //! Theseus state management system use crate::event::emit::emit_loading; +use std::path::PathBuf; use crate::event::emit::init_loading; use crate::event::LoadingBarType; @@ -7,9 +8,14 @@ use crate::loading_join; use crate::state::users::Users; use crate::util::fetch::{FetchSemaphore, IoSemaphore}; +use notify::RecommendedWatcher; +use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer}; use std::sync::Arc; +use std::time::Duration; use tokio::sync::{OnceCell, RwLock, Semaphore}; +use futures::{channel::mpsc::channel, SinkExt, StreamExt}; + // Submodules mod dirs; pub use self::dirs::*; @@ -69,6 +75,9 @@ pub struct State { pub(crate) users: RwLock, /// Launcher tags pub(crate) tags: RwLock, + + /// File watcher debouncer + pub(crate) file_watcher: RwLock>, } impl State { @@ -84,6 +93,8 @@ impl State { ) .await?; + let mut file_watcher = init_watcher().await?; + let directories = DirectoryInfo::init().await?; emit_loading(&loading_bar, 10.0, None).await?; @@ -100,7 +111,8 @@ impl State { let metadata_fut = Metadata::init(&directories, &io_semaphore); - let profiles_fut = Profiles::init(&directories); + let profiles_fut = + Profiles::init(&directories, &mut file_watcher); let tags_fut = Tags::init( &directories, &io_semaphore, @@ -137,6 +149,7 @@ impl State { children: RwLock::new(children), auth_flow: RwLock::new(auth_flow), tags: RwLock::new(tags), + file_watcher: RwLock::new(file_watcher), })) } }) @@ -220,3 +233,51 @@ impl State { *io_semaphore = Semaphore::new(settings.max_concurrent_downloads); } } + +async fn init_watcher() -> crate::Result> { + let (mut tx, mut rx) = channel(1); + + let file_watcher = new_debouncer( + Duration::from_secs(2), + None, + move |res: DebounceEventResult| { + futures::executor::block_on(async { + tx.send(res).await.unwrap(); + }) + }, + )?; + + tokio::task::spawn(async move { + while let Some(res) = rx.next().await { + match res { + Ok(events) => { + let mut visited_paths = Vec::new(); + events.iter().for_each(|e| { + let mut new_path = PathBuf::new(); + let mut found = false; + + for component in e.path.components() { + new_path.push(component); + if found { + break; + } + if component.as_os_str() == "profiles" { + found = true; + } + } + + if !visited_paths.contains(&new_path) { + Profile::sync_projects_task(new_path.clone()); + visited_paths.push(new_path); + } + }); + } + Err(errors) => errors.iter().for_each(|err| { + tracing::warn!("Unable to watch file: {err}") + }), + } + } + }); + + Ok(file_watcher) +} diff --git a/theseus/src/state/profiles.rs b/theseus/src/state/profiles.rs index f6d777bec..0e463377d 100644 --- a/theseus/src/state/profiles.rs +++ b/theseus/src/state/profiles.rs @@ -3,6 +3,7 @@ use crate::config::MODRINTH_API_URL; use crate::data::DirectoryInfo; use crate::event::emit::emit_profile; use crate::event::ProfilePayloadType; +use crate::prelude::JavaVersion; use crate::state::projects::Project; use crate::state::{ModrinthVersion, ProjectMetadata, ProjectType}; use crate::util::fetch::{ @@ -13,6 +14,8 @@ use daedalus::get_hash; use daedalus::modded::LoaderVersion; use dunce::canonicalize; use futures::prelude::*; +use notify::{RecommendedWatcher, RecursiveMode}; +use notify_debouncer_mini::Debouncer; use reqwest::Method; use serde::{Deserialize, Serialize}; use std::io::Cursor; @@ -124,7 +127,7 @@ impl ModLoader { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct JavaSettings { #[serde(skip_serializing_if = "Option::is_none")] - pub jre_key: Option, + pub override_version: Option, #[serde(skip_serializing_if = "Option::is_none")] pub extra_arguments: Option>, } @@ -173,36 +176,10 @@ impl Profile { semaphore: &IoSemaphore, icon: bytes::Bytes, file_name: &str, - ) -> crate::Result<&'a mut Self> { + ) -> crate::Result<()> { let file = write_cached_icon(file_name, cache_dir, icon, semaphore).await?; self.metadata.icon = Some(file); - Ok(self) - } - - pub async fn sync_projects(&mut self) -> crate::Result<()> { - let state = State::get().await?; - - let paths = self.get_profile_project_paths()?; - let projects = crate::state::infer_data_from_files( - self.clone(), - paths, - state.directories.caches_dir(), - &state.io_semaphore, - &state.fetch_semaphore, - ) - .await?; - - self.projects = projects; - - emit_profile( - self.uuid, - self.path.clone(), - &self.metadata.name, - ProfilePayloadType::Synced, - ) - .await?; - Ok(()) } @@ -228,6 +205,10 @@ impl Profile { if let Some(profile) = new_profiles.0.get_mut(&path) { profile.projects = projects; } + } else { + tracing::warn!( + "Unable to fetch single profile projects: path {path:?} invalid", + ); } Ok::<(), crate::Error>(()) @@ -268,8 +249,44 @@ impl Profile { Ok(files) } + pub async fn watch_fs( + profile_path: &Path, + watcher: &mut Debouncer, + ) -> crate::Result<()> { + async fn watch_path( + profile_path: &Path, + watcher: &mut Debouncer, + path: &str, + ) -> crate::Result<()> { + let path = profile_path.join(path); + + fs::create_dir_all(&path).await?; + + watcher + .watcher() + .watch(&profile_path.join(path), RecursiveMode::Recursive)?; + + Ok(()) + } + + watch_path(profile_path, watcher, ProjectType::Mod.get_folder()) + .await?; + watch_path(profile_path, watcher, ProjectType::ShaderPack.get_folder()) + .await?; + watch_path( + profile_path, + watcher, + ProjectType::ResourcePack.get_folder(), + ) + .await?; + watch_path(profile_path, watcher, ProjectType::DataPack.get_folder()) + .await?; + + Ok(()) + } + pub async fn add_project_version( - &mut self, + &self, version_id: String, ) -> crate::Result<(PathBuf, ModrinthVersion)> { let state = State::get().await?; @@ -314,7 +331,7 @@ impl Profile { } pub async fn add_project_bytes( - &mut self, + &self, file_name: &str, bytes: bytes::Bytes, project_type: Option, @@ -354,16 +371,22 @@ impl Profile { write(&path, &bytes, &state.io_semaphore).await?; let hash = get_hash(bytes).await?; + { + let mut profiles = state.profiles.write().await; + + if let Some(profile) = profiles.0.get_mut(&self.path) { + profile.projects.insert( + path.clone(), + Project { + sha512: hash, + disabled: false, + metadata: ProjectMetadata::Unknown, + file_name: file_name.to_string(), + }, + ); + } + } - self.projects.insert( - path.clone(), - Project { - sha512: hash, - disabled: false, - metadata: ProjectMetadata::Unknown, - file_name: file_name.to_string(), - }, - ); emit_profile( self.uuid, self.path.clone(), @@ -376,10 +399,19 @@ impl Profile { } pub async fn toggle_disable_project( - &mut self, + &self, path: &Path, ) -> crate::Result<()> { - if let Some(mut project) = self.projects.remove(path) { + let state = State::get().await?; + if let Some(mut project) = { + let mut profiles = state.profiles.write().await; + + if let Some(profile) = profiles.0.get_mut(&self.path) { + profile.projects.remove(path) + } else { + None + } + } { let path = path.to_path_buf(); let mut new_path = path.clone(); @@ -395,7 +427,10 @@ impl Profile { fs::rename(path, &new_path).await?; - self.projects.insert(new_path, project); + let mut profiles = state.profiles.write().await; + if let Some(profile) = profiles.0.get_mut(&self.path) { + profile.projects.insert(new_path, project); + } } else { return Err(crate::ErrorKind::InputError(format!( "Project path does not exist: {:?}", @@ -408,14 +443,19 @@ impl Profile { } pub async fn remove_project( - &mut self, + &self, path: &Path, dont_remove_arr: Option, ) -> crate::Result<()> { + let state = State::get().await?; if self.projects.contains_key(path) { fs::remove_file(path).await?; if !dont_remove_arr.unwrap_or(false) { - self.projects.remove(path); + let mut profiles = state.profiles.write().await; + + if let Some(profile) = profiles.0.get_mut(&self.path) { + profile.projects.remove(path); + } } } else { return Err(crate::ErrorKind::InputError(format!( @@ -430,8 +470,11 @@ impl Profile { } impl Profiles { - #[tracing::instrument] - pub async fn init(dirs: &DirectoryInfo) -> crate::Result { + #[tracing::instrument(skip(file_watcher))] + pub async fn init( + dirs: &DirectoryInfo, + file_watcher: &mut Debouncer, + ) -> crate::Result { let mut profiles = HashMap::new(); fs::create_dir_all(dirs.profiles_dir()).await?; let mut entries = fs::read_dir(dirs.profiles_dir()).await?; @@ -449,6 +492,7 @@ impl Profiles { }; if let Some(profile) = prof { let path = canonicalize(path)?; + Profile::watch_fs(&path, file_watcher).await?; profiles.insert(path, profile); } } @@ -517,6 +561,11 @@ impl Profiles { ProfilePayloadType::Added, ) .await?; + + let state = State::get().await?; + let mut file_watcher = state.file_watcher.write().await; + Profile::watch_fs(&profile.path, &mut file_watcher).await?; + self.0.insert( canonicalize(&profile.path)? .to_str() @@ -529,15 +578,6 @@ impl Profiles { Ok(self) } - #[tracing::instrument(skip(self))] - pub async fn insert_from<'a>( - &'a mut self, - path: &'a Path, - ) -> crate::Result<&Self> { - self.insert(Self::read_profile_from_dir(&canonicalize(path)?).await?) - .await - } - #[tracing::instrument(skip(self))] pub async fn remove( &mut self, diff --git a/theseus/src/util/jre.rs b/theseus/src/util/jre.rs index 9e07034fa..8bdf86b62 100644 --- a/theseus/src/util/jre.rs +++ b/theseus/src/util/jre.rs @@ -1,14 +1,15 @@ use dunce::canonicalize; use futures::prelude::*; -use lazy_static::lazy_static; -use regex::Regex; use serde::{Deserialize, Serialize}; use std::env; +use std::io::Write; use std::path::PathBuf; use std::process::Command; use std::{collections::HashSet, path::Path}; +use tempfile::NamedTempFile; use tokio::task::JoinError; +use crate::State; #[cfg(target_os = "windows")] use winreg::{ enums::{HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, KEY_WOW64_64KEY}, @@ -19,6 +20,7 @@ use winreg::{ pub struct JavaVersion { pub path: String, pub version: String, + pub architecture: String, } // Entrypoint function (Windows) @@ -30,6 +32,7 @@ pub async fn get_all_jre() -> Result, JREError> { // Add JRES directly on PATH jre_paths.extend(get_all_jre_path().await?); + jre_paths.extend(get_all_autoinstalled_jre_path().await?); // Hard paths for locations for commonly installed .exes let java_paths = [r"C:/Program Files/Java", r"C:/Program Files (x86)/Java"]; @@ -110,6 +113,7 @@ pub async fn get_all_jre() -> Result, JREError> { // Add JREs directly on PATH jre_paths.extend(get_all_jre_path().await?); + jre_paths.extend(get_all_autoinstalled_jre_path().await?); // Hard paths for locations for commonly installed .exes let java_paths = [ @@ -128,6 +132,7 @@ pub async fn get_all_jre() -> Result, JREError> { jre_paths.insert(entry); } } + // Get JRE versions from potential paths concurrently let j = check_java_at_filepaths(jre_paths) .await? @@ -146,6 +151,7 @@ pub async fn get_all_jre() -> Result, JREError> { // Add JREs directly on PATH jre_paths.extend(get_all_jre_path().await?); + jre_paths.extend(get_all_autoinstalled_jre_path().await?); // Hard paths for locations for commonly installed locations let java_paths = [ @@ -177,6 +183,30 @@ pub async fn get_all_jre() -> Result, JREError> { Ok(j) } +// Gets all JREs from the PATH env variable +#[tracing::instrument] +async fn get_all_autoinstalled_jre_path() -> Result, JREError> +{ + let state = State::get().await.map_err(|_| JREError::StateError)?; + + let mut jre_paths = HashSet::new(); + let base_path = state.directories.java_versions_dir(); + + if base_path.is_dir() { + for entry in std::fs::read_dir(base_path)? { + let entry = entry?; + let file_path = entry.path().join("bin"); + let contents = std::fs::read_to_string(file_path)?; + + let entry = entry.path().join(contents); + println!("{:?}", entry); + jre_paths.insert(entry); + } + } + + Ok(jre_paths) +} + // Gets all JREs from the PATH env variable #[tracing::instrument] async fn get_all_jre_path() -> Result, JREError> { @@ -231,26 +261,49 @@ pub async fn check_java_at_filepath(path: &Path) -> Option { return None; }; - // Run 'java -version' using found java binary - let output = Command::new(&java).arg("-version").output().ok()?; - let stderr = String::from_utf8_lossy(&output.stderr); - - // Match: version "1.8.0_361" - // Extracting version numbers - lazy_static! { - static ref JAVA_VERSION_CAPTURE: Regex = - Regex::new(r#"version "([\d\._]+)""#) - .expect("Error creating java version capture regex"); + let mut file = NamedTempFile::new().ok()?; + file.write_all(include_bytes!("../../library/JavaInfo.class")) + .ok()?; + + let original_path = file.path().to_path_buf(); + let mut new_path = original_path.clone(); + new_path.set_file_name("JavaInfo"); + new_path.set_extension("class"); + tokio::fs::rename(&original_path, &new_path).await.ok()?; + + // Run java checker on java binary + let output = Command::new(&java) + .arg("-cp") + .arg(file.path().parent().unwrap()) + .arg("JavaInfo") + .output() + .ok()?; + + let stdout = String::from_utf8_lossy(&output.stdout); + + let mut java_version = None; + let mut java_arch = None; + + for line in stdout.lines() { + let mut parts = line.split('='); + let key = parts.next().unwrap_or_default(); + let value = parts.next().unwrap_or_default(); + + if key == "os.arch" { + java_arch = Some(value); + } else if key == "java.version" { + java_version = Some(value); + } } // Extract version info from it - if let Some(captures) = JAVA_VERSION_CAPTURE.captures(&stderr) { - if let Some(version) = captures.get(1) { - let Some(path) = java.to_str() else { return None }; - let path = path.to_string(); + if let Some(arch) = java_arch { + if let Some(version) = java_version { + let path = java.to_string_lossy().to_string(); return Some(JavaVersion { path, - version: version.as_str().to_string(), + version: version.to_string(), + architecture: arch.to_string(), }); } } @@ -311,6 +364,9 @@ pub enum JREError { #[error("No stored tag for Minecraft Version {0}")] NoMinecraftVersionFound(String), + + #[error("Error getting launcher sttae")] + StateError, } #[cfg(test)] diff --git a/theseus/src/util/platform.rs b/theseus/src/util/platform.rs index 54fbce776..9509ab6ea 100644 --- a/theseus/src/util/platform.rs +++ b/theseus/src/util/platform.rs @@ -8,27 +8,27 @@ pub trait OsExt { fn native() -> Self; /// Gets the OS + Arch of the current system - fn native_arch() -> Self; + fn native_arch(java_arch: &str) -> Self; } impl OsExt for Os { - fn native_arch() -> Self { + fn native_arch(java_arch: &str) -> Self { if std::env::consts::OS == "windows" { - if std::env::consts::ARCH == "aarch64" { + if java_arch == "aarch64" { Os::WindowsArm64 } else { Os::Windows } } else if std::env::consts::OS == "linux" { - if std::env::consts::ARCH == "aarch64" { + if java_arch == "aarch64" { Os::LinuxArm64 - } else if std::env::consts::ARCH == "arm" { + } else if java_arch == "arm" { Os::LinuxArm32 } else { Os::Linux } } else if std::env::consts::OS == "macos" { - if std::env::consts::ARCH == "aarch64" { + if java_arch == "aarch64" { Os::OsxArm64 } else { Os::Osx @@ -39,30 +39,6 @@ impl OsExt for Os { } fn native() -> Self { - if std::env::consts::OS == "windows" { - if std::env::consts::ARCH == "aarch64" { - Os::WindowsArm64 - } else { - Os::Windows - } - } else if std::env::consts::OS == "linux" { - if std::env::consts::ARCH == "aarch64" { - Os::LinuxArm64 - } else if std::env::consts::ARCH == "arm" { - Os::LinuxArm32 - } else { - Os::Linux - } - } else if std::env::consts::OS == "macos" { - if std::env::consts::ARCH == "aarch64" { - Os::OsxArm64 - } else { - Os::Osx - } - } else { - Os::Unknown - }; - match std::env::consts::OS { "windows" => Self::Windows, "macos" => Self::Osx, @@ -80,7 +56,7 @@ pub const ARCH_WIDTH: &str = "64"; pub const ARCH_WIDTH: &str = "32"; // Platform rule handling -pub fn os_rule(rule: &OsRule) -> bool { +pub fn os_rule(rule: &OsRule, java_arch: &str) -> bool { let mut rule_match = true; if let Some(ref arch) = rule.arch { @@ -88,7 +64,8 @@ pub fn os_rule(rule: &OsRule) -> bool { } if let Some(name) = &rule.name { - rule_match &= &Os::native() == name || &Os::native_arch() == name; + rule_match &= + &Os::native() == name || &Os::native_arch(java_arch) == name; } if let Some(version) = &rule.version { @@ -101,8 +78,8 @@ pub fn os_rule(rule: &OsRule) -> bool { rule_match } -pub fn classpath_separator() -> &'static str { - match Os::native_arch() { +pub fn classpath_separator(java_arch: &str) -> &'static str { + match Os::native_arch(java_arch) { Os::Osx | Os::OsxArm64 | Os::Linux diff --git a/theseus_gui/src-tauri/src/api/jre.rs b/theseus_gui/src-tauri/src/api/jre.rs index 3fedad587..e9bfe293e 100644 --- a/theseus_gui/src-tauri/src/api/jre.rs +++ b/theseus_gui/src-tauri/src/api/jre.rs @@ -1,11 +1,9 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use crate::api::Result; use theseus::prelude::JavaVersion; use theseus::prelude::*; -use super::TheseusSerializableError; - /// Get all JREs that exist on the system #[tauri::command] pub async fn jre_get_all_jre() -> Result> { @@ -37,23 +35,6 @@ pub async fn jre_autodetect_java_globals() -> Result { Ok(jre::autodetect_java_globals().await?) } -// Gets key for the optimal JRE to use, for a given profile Profile -// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists) -#[tauri::command] -pub async fn jre_get_optimal_jre_key(profile: Profile) -> Result { - Ok(jre::get_optimal_jre_key(&profile).await?) -} - -// Gets key for the optimal JRE to use, for a given profile path -// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists) -#[tauri::command] -pub async fn jre_get_optimal_jre_key_by_path(path: &Path) -> Result { - let profile = profile::get(path, Some(true)).await?.ok_or_else(|| { - TheseusSerializableError::NoProfileFound(path.display().to_string()) - })?; - Ok(jre::get_optimal_jre_key(&profile).await?) -} - // Validates java globals, by checking if the paths exist // If false, recommend to direct them to reassign, or to re-guess #[tauri::command] @@ -67,3 +48,15 @@ pub async fn jre_validate_globals() -> Result { pub async fn jre_get_jre(path: PathBuf) -> Result> { jre::check_jre(path).await.map_err(|e| e.into()) } + +// Auto installs java for the given java version +#[tauri::command] +pub async fn jre_auto_install_java(java_version: u32) -> Result { + Ok(jre::auto_install_java(java_version).await?) +} + +// Gets the maximum memory a system has available. +#[tauri::command] +pub async fn jre_get_max_memory() -> Result { + Ok(jre::get_max_memory().await?) +} diff --git a/theseus_gui/src-tauri/src/api/mod.rs b/theseus_gui/src-tauri/src/api/mod.rs index fbcfa3181..3905de054 100644 --- a/theseus_gui/src-tauri/src/api/mod.rs +++ b/theseus_gui/src-tauri/src/api/mod.rs @@ -31,9 +31,6 @@ pub enum TheseusSerializableError { #[error("IO error: {0}")] IO(#[from] std::io::Error), - - #[error("No profile found at {0}")] - NoProfileFound(String), } // Generic implementation of From for ErrorTypeA @@ -92,6 +89,5 @@ macro_rules! impl_serialize { // Use the macro to implement Serialize for TheseusSerializableError impl_serialize! { - IO, - NoProfileFound, + IO } diff --git a/theseus_gui/src-tauri/src/api/profile.rs b/theseus_gui/src-tauri/src/api/profile.rs index 9b6a19544..351c31476 100644 --- a/theseus_gui/src-tauri/src/api/profile.rs +++ b/theseus_gui/src-tauri/src/api/profile.rs @@ -54,14 +54,6 @@ pub async fn profile_check_installed( } } -/// Syncs a profile's in memory state with the state on the disk -/// // invoke('profile_sync') -#[tauri::command] -pub async fn profile_sync(path: &Path) -> Result<()> { - profile::sync(path).await?; - Ok(()) -} - /// Installs/Repairs a profile /// invoke('profile_install') #[tauri::command] @@ -89,18 +81,6 @@ pub async fn profile_update_project( Ok(()) } -/// Replaces a project with the given version ID -/// invoke('profile_replace_project') -#[tauri::command] -pub async fn profile_replace_project( - path: &Path, - project: &Path, - version_id: String, -) -> Result { - let res = profile::replace_project(path, project, version_id).await?; - Ok(res) -} - // Adds a project to a profile from a version ID // invoke('profile_add_project_from_version') #[tauri::command] diff --git a/theseus_gui/src-tauri/src/main.rs b/theseus_gui/src-tauri/src/main.rs index af99868c7..39afbeb3d 100644 --- a/theseus_gui/src-tauri/src/main.rs +++ b/theseus_gui/src-tauri/src/main.rs @@ -79,11 +79,9 @@ fn main() { api::profile::profile_remove, api::profile::profile_get, api::profile::profile_list, - api::profile::profile_sync, api::profile::profile_install, api::profile::profile_update_all, api::profile::profile_update_project, - api::profile::profile_replace_project, api::profile::profile_add_project_from_version, api::profile::profile_add_project_from_path, api::profile::profile_toggle_disable_project, @@ -117,9 +115,9 @@ fn main() { api::jre::jre_find_jre_17_jres, api::jre::jre_find_jre_8_jres, api::jre::jre_validate_globals, - api::jre::jre_get_optimal_jre_key, - api::jre::jre_get_optimal_jre_key_by_path, api::jre::jre_get_jre, + api::jre::jre_auto_install_java, + api::jre::jre_get_max_memory, api::process::process_get_all_uuids, api::process::process_get_all_running_uuids, api::process::process_get_uuids_by_profile_path, diff --git a/theseus_gui/src/helpers/jre.js b/theseus_gui/src/helpers/jre.js index bb5f8c34c..2201ffc0f 100644 --- a/theseus_gui/src/helpers/jre.js +++ b/theseus_gui/src/helpers/jre.js @@ -11,7 +11,7 @@ JavaVersion { path: Path version: String } - + */ /// Get all JREs that exist on the system @@ -50,20 +50,18 @@ export async function get_jre(path) { return await invoke('jre_get_jre', { path }) } -// Gets key for the optimal JRE to use, for a given profile path -// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists) -export async function get_optimal_jre_key_by_path(path) { - return await invoke('jre_get_optimal_jre_key_by_path', { path }) -} - -// Gets key for the optimal JRE to use, for a given profile -// The key can be used in the hashmap contained by JavaGlobals in Settings (if it exists) -export async function get_optimal_jre_key(path) { - return await invoke('jre_get_optimal_jre_key', { path }) -} - // Autodetect Java globals, by searching the users computer. // Returns a *NEW* JavaGlobals that can be put into Settings export async function autodetect_java_globals(path) { return await invoke('jre_autodetect_java_globals', { path }) } + +// Automatically installs specified java version +export async function jre_auto_install_java(javaVersion) { + return await invoke('jre_auto_install_java', { javaVersion }) +} + +// Get max memory in KiB +export async function get_max_memory() { + return await invoke('jre_get_max_memory') +} diff --git a/theseus_gui/src/helpers/profile.js b/theseus_gui/src/helpers/profile.js index f68b2d213..a9a92afe8 100644 --- a/theseus_gui/src/helpers/profile.js +++ b/theseus_gui/src/helpers/profile.js @@ -46,11 +46,6 @@ export async function check_installed(path, projectId) { return await invoke('profile_check_installed', { path, projectId }) } -// Syncs a profile with the disk -export async function sync(path) { - return await invoke('profile_sync', { path }) -} - // Installs/Repairs a profile export async function install(path) { return await invoke('profile_install', { path }) @@ -66,12 +61,6 @@ export async function update_project(path, projectPath) { return await invoke('profile_update_project', { path, projectPath }) } -// Replaces a given project with the specified version ID -// Returns a path to the new project file -export async function replace_project(path, projectPath, versionId) { - return await invoke('profile_replace_project', { path, projectPath, versionId }) -} - // Add a project to a profile from a version // Returns a path to the new project file export async function add_project_from_version(path, versionId) { diff --git a/theseus_gui/src/pages/Settings.vue b/theseus_gui/src/pages/Settings.vue index 788b1fae8..cce34fb7d 100644 --- a/theseus_gui/src/pages/Settings.vue +++ b/theseus_gui/src/pages/Settings.vue @@ -17,7 +17,7 @@ import { import { BrowseIcon } from '@/assets/icons' import { useTheming } from '@/store/state' import { get, set } from '@/helpers/settings' -import { find_jre_8_jres, find_jre_17_jres, get_jre } from '@/helpers/jre' +import { find_jre_8_jres, find_jre_17_jres, get_jre, get_max_memory } from '@/helpers/jre' import { open } from '@tauri-apps/api/dialog' const themeStore = useTheming() @@ -29,6 +29,7 @@ if (!fetchSettings.java_globals?.JAVA_8) if (!fetchSettings.java_globals?.JAVA_17) fetchSettings.java_globals.JAVA_17 = { path: '', version: '' } const settings = ref(fetchSettings) +const maxMemory = ref((await get_max_memory()) / 1024) const chosenInstallOptions = ref([]) const browsingInstall = ref(0) @@ -285,11 +286,11 @@ const setJavaInstall = (javaInstall) => {
Minimum Memory - + Maximum Memory - +
diff --git a/theseus_playground/src/main.rs b/theseus_playground/src/main.rs index 185549412..176b7ab4d 100644 --- a/theseus_playground/src/main.rs +++ b/theseus_playground/src/main.rs @@ -7,8 +7,7 @@ use dunce::canonicalize; use theseus::jre::autodetect_java_globals; use theseus::prelude::*; - - +use theseus::profile_create::profile_create; use tokio::time::{sleep, Duration}; use tracing_error::ErrorLayer; use tracing_subscriber::layer::SubscriberExt; @@ -51,6 +50,8 @@ async fn main() -> theseus::Result<()> { let st = State::get().await?; //State::update(); + // let path = jre::auto_install_java(8).await.unwrap(); + st.settings.write().await.java_globals = autodetect_java_globals().await?; st.settings.write().await.max_concurrent_downloads = 50; st.settings.write().await.hooks.post_exit = @@ -58,6 +59,12 @@ async fn main() -> theseus::Result<()> { // Changed the settings, so need to reset the semaphore st.reset_fetch_semaphore().await; + // + // st.settings + // .write() + // .await + // .java_globals + // .insert(JAVA_8_KEY.to_string(), check_jre(path).await?.unwrap()); // Clear profiles println!("Clearing profiles."); { @@ -69,52 +76,53 @@ async fn main() -> theseus::Result<()> { println!("Creating/adding profile."); - // let name = "Example".to_string(); - // let game_version = "1.19.2".to_string(); - // let modloader = ModLoader::Vanilla; - // let loader_version = "stable".to_string(); - // - // let profile_path = profile_create( - // name.clone(), - // game_version, - // modloader, - // Some(loader_version), - // None, - // None, - // None, - // None, - // ) - // .await?; + let name = "Example".to_string(); + let game_version = "1.19.2".to_string(); + let modloader = ModLoader::Vanilla; + let loader_version = "stable".to_string(); + + let profile_path = profile_create( + name.clone(), + game_version, + modloader, + Some(loader_version), + None, + None, + None, + None, + ) + .await?; // // install(&profile_path).await.unwrap(); // let mut value = list().await?; // let profile_path = value.iter().next().map(|x| x.0).unwrap(); - // println!("Adding sodium"); - // let sodium_path = profile::add_project_from_version( - // &profile_path, - // "rAfhHfow".to_string(), - // ) - // .await?; - // - // let mod_menu_path = profile::add_project_from_version( - // &profile_path, - // "gSoPJyVn".to_string(), - // ) - // .await?; - // - // println!("Disabling sodium"); - // profile::toggle_disable_project(&profile_path, &sodium_path).await?; + println!("Adding sodium"); + let sodium_path = profile::add_project_from_version( + &profile_path, + "rAfhHfow".to_string(), + ) + .await?; - // profile::remove_project(&profile_path, &mod_menu_path).await?; - let profile_path = pack::install_pack_from_version_id( - "CeeCkHke".to_string(), - "Technical Electrical".to_string(), - None, + let mod_menu_path = profile::add_project_from_version( + &profile_path, + "gSoPJyVn".to_string(), ) - .await - .unwrap(); + .await?; + + println!("Disabling sodium"); + profile::toggle_disable_project(&profile_path, &sodium_path).await?; + + profile::remove_project(&profile_path, &mod_menu_path).await?; + + // let profile_path = pack::install_pack_from_version_id( + // "CeeCkHke".to_string(), + // "Technical Electrical".to_string(), + // None, + // ) + // .await + // .unwrap(); // async closure for testing any desired edits // (ie: changing the java runtime of an added profile) @@ -147,8 +155,8 @@ async fn main() -> theseus::Result<()> { println!("Minecraft PID: {:?}", pid); // Wait 5 seconds - println!("Waiting 20 seconds to gather logs..."); - sleep(Duration::from_secs(20)).await; + println!("Waiting 5 seconds to gather logs..."); + sleep(Duration::from_secs(5)).await; let stdout = process::get_stdout_by_uuid(&uuid).await?; let stderr = process::get_stderr_by_uuid(&uuid).await?; println!("Logs after 5sec <<< {stdout} >>> end stdout");