From 8fdb26ac068d04acef474d32057a7116e672cf88 Mon Sep 17 00:00:00 2001 From: white-axe Date: Thu, 12 Oct 2023 03:12:26 -0400 Subject: [PATCH] Undeprecate WebAssembly support (#47) * WASM test (cherry picked from commit 11e2cdef65d15d804e67d5e7ce509139be609694) * Cleanup the changes from the previous commit (cherry picked from commit 52016dda50d89cbe531ac2184b2020fc3b8442ba) * Set default build target to `wasm32-unknown-unknown` This is so that rust-analyzer will use this build target when analyzing code. (cherry picked from commit ddfa8842a739ed7797f051ae1f01f7602ad4ca01) * Enable WASM threading and disable WASM audio I turned on some compiler flags to enable experimental support for atomics and shared memory in WebAssembly, because this will probably be needed if we end up using web workers to handle the filesystem operations in WebAssembly builds. Unfortunately, rodio doesn't support web workers, and in fact rodio crashes Luminol in WebAssembly when I simply turn on those compiler flags without making any changes to the code! The audio in WebAssembly builds will have to be reimplemented without rodio. I don't think this will be too hard, given that all we really use rodio for is straightforward audio playback... * Disable WASM threading flags Apparently you can't use shared memory from inside of WebAssembly without passing extra arguments to wasm-bindgen that Trunk doesn't support. That's okay, we'll make do without these compiler flags. We still need coi-serviceworker.js because `Atomics.wait()` can't be used without it in most browsers. rodio also still won't work because we're going to run in a web worker, where the audio APIs that rodio needs aren't available. * Revert previous commit Nevermind, there's no flag incompatibility issue with Trunk. I'm probably just sleep-deprived... * Refactor to not use `eframe::Frame` I'm making a completely custom egui app runner for WebAssembly builds, since eframe's web runner makes such widespread assumptions about the availability of certain JavaScript APIs that are only present outside of web workers that it's unsalvageable when it comes to getting it to run in a web worker. (Both egui and wgpu can run in a web worker, though, so a custom runner should work.) But I ran into problems because implementing a custom runner requires creating `eframe::Frame`s which can't be done outside of the eframe crate. In the future it would be best to stop using eframe entirely, but I don't want to also have to rewrite the native runner right now so this is the best I can do. * Rudimentary web worker rendering I added a new app runner that can run in a web worker. The entire program now runs in a web worker, which means we can now use things like mutexes, channels and other synchronization constructs without making the browser angry when they block. If you try it right now, the canvas will be very small and will not react to user input. These things will be fixed later. * Rewrite web worker launcher code in Rust The WebAssembly code now runs in both the main thread and the worker thread with the same code and the same memory (i.e. multithreading), but with different entry points. The code on the main thread gets some information from the environment and sends it to the code on the worker thread. The worker thread runs the actual program. The purpose of doing this is so that we can write event handlers in Rust, for things like detecting user input on the canvas from the main thread and sending it to the worker thread, or sending a request for a filesystem operation from the worker thread to the main thread. * Add a hook to the main thread for detecting screen size changes The main thread now listens for changes to the screen size and then sends them to the worker thread so that it can resize the canvas to fill the screen. We use a `std::sync::mpsc::Sender` on the main thread to send the new screen sizes to a `std::sync::mpsc::Receiver` on the worker thread. This has the benefit of being more convenient than `Worker.postMessage()` and also is probably faster because we don't have to deal with the serialization overhead associated with `Worker.postMessage()`. The Rust senders and receivers internally use WebAssembly's experimental atomics and shared memory features since I enabled these things in the compiler flags. They need Cross-Origin Isolation to be enabled to work, so don't remove the coi-serviceworker.js shim. * Web worker runner now responds to mouse events * Move event handler code into web_worker_runner.rs * Remove duplicate `window` from event handler code * Use Tokio channels instead of std channels We need a channel with an asynchronous `recv()` method in order to send messages from the worker thread to the main thread, so we might as well use Tokio channels everywhere. * Fix memory leak in `WebWorkerRunner::setup_render_hooks()` At least I *think* this was a memory leak. Anyways, the use of `CustomFrame` was somehow causing the rendering to slow down slightly every frame. So that makes me think there was a memory leak somewhere. I couldn't figure out how to fix it properly so I just removed `CustomFrame` since we don't need it anyways. But the most important thing is that it runs at full speed now! * Add a channel for filesystem operations This commit adds a channel from the worker thread to the main thread so that the worker thread can send the main thread requests for filesystem operations. The results are sent back using a Tokio oneshot channel. * Channel senders are now stored as clones instead of by reference This frees us from having to deal with manually specifying lifetimes while still otherwise behaving exactly the same. * Set `max_texture_side` properly in `egui::RawInput` * Implement `FileSystem` for WebAssembly builds The `File` implementation is not done yet, but it will be soon! * `FileSystemCommand` variants are now private * Implement `File` for WebAssembly builds We now have a complete filesystem implementation for web browsers. Finally. * Implement project loading for WebAssembly builds Project loading is quite slow! I will be experimenting with a path cache for the filesystem later to see if that helps with speed. * Use egui context's time instead of system time `std::time::Instant` can't be used in WASM. * Also stop using system time in tilepicker.rs * Split sprite.wgsl and tilemap.wgsl into two parts * Enable wasm-bindgen support for reference types and weakrefs * Use uniforms instead of push constants in web builds * Remove `USING_PUSH_CONSTANTS` from shader headers * Tilemap now changes the projection matrix instead of the viewport rect Before, the tilemap implemented panning and scaling by simply panning and scaling the viewport rectangle that it rendered onto. This meant if any part of the tilemap were offscreen, that part of the viewport would be offscreen, too. The WebGPU standard actually specifies that you're not allowed to have any part of the viewport outside of the screen! (i.e. the intersection of the viewport rectangle and the screen rectangle must be equal to the viewport rectangle) Consequently, most web browsers will not allow you to do this. The version of wgpu that we currently use, version 0.16.3, allows you to do this on native builds, but only because the developers forgot to implement a check for this. It's actually undefined behaviour - some drivers will allow this and some won't. It will be explicitly disallowed in a future release of wgpu and already is in their trunk branch. So, I made a minor change to the tilemap so that its viewport rectangle always stays on screen. Now, the viewport rectangle is always the same as the UI rectangle that egui allocates for the tilemap, and the viewport projection matrix changes instead to handle panning and scaling. This should be the last change needed for the map editor to work in WebAssembly, although it also needs to be applied to the event graphics and the tilepicker, which is not done yet. * Tilemap event graphics now use projection matrix The event graphics on the tilemap now render using the area allocated by egui for the map editor as the viewport and handle scaling and panning by modifying the projection matrix. The event graphics in the hover tooltips do not do this, they just render using the tooltip as the viewport because the tooltip doesn't normally go offscreen. For some reason, egui sometimes draws the tooltip offscreen for one frame before snapping it back onscreen, so a check is added to disable rendering the tooltip graphic unless the graphic is fully onscreen. * Tilepicker now uses projection matrix The map editor should now be working in web builds! Except you won't be able to use the keyboard because I haven't put the keyboard handling code into the app runner yet. * Move JavaScript bindings into bindings.js and bindings.rs * Upload bindings.js and bindings.rs * Fix native builds Native builds can now be built with `cargo build` in addition to web builds being able to be built with `trunk build`. I really don't like these hooks, but we need to enable build-std only in web builds, and Trunk provides us no other way to do this. * Apply clippy recommendation * Add web event handler for keyboard presses/releases * Add web event handler for modifier keys * Remove unnecessary field from tile shader.rs * Cleanup web keyboard handler code * Add web event handler for text input This is used by egui when typing in text boxes. * Add web event handler for scrolling * Fix log messages not appearing in web builds * Fix handling of `pixels_per_point` in web builds * Add web event handler for egui's cursor change requests * Refactor web initialization code This refactors the code for initializing Luminol in web builds to not use global state that is shared between different parts of the program, and also moves the `USE_PUSH_CONSTANTS` constant into src/components, the only place it's used from. * Add web event handlers for clipboard and opening URLs * Add `Clipboard` feature to web-sys * Remove unnecessary `mut` in src/filesystem/web.rs * Add handler for RTP loading in web builds Since we have no way to load from an arbitrary directory without the user's permission, they're currently loaded from the "RTP" subdirectory in the project directory. This can be changed in the future if required. * Remove another unnecessary `mut` in src/filesystem/web.rs * Refactor archiver to work in web builds I don't have any archives to test with, but I can't think of any reason why these simple changes wouldn't work. Feel free to prove me wrong! * Remove unnecessary `.clone()` from src/main.rs * Implement eframe storage and egui memory in web builds This allows most state to be persisted. Unfortunately, we'll need a different kind of storage to persist the project directory handles. * Implement project directory persistence for web builds Project directory handles are now stored in IndexedDB so that they can be recovered after the user closes or refreshes the page. * Unregister render loop and event listeners on panic This prevents the console from being spammed with WebGPU errors in the event of a panic. * Re-add rodio as a dependency in web builds * Save on mouseleave in web builds * Restore the fullscreen toggle in native builds * Restore playtest and console in native builds * Restore the quit button in native builds * Implement project creation for web builds I had to replace surf with reqwest because surf won't compile with wasm-bindgen > 0.2.84 and eframe won't compile with wasm-bindgen < 0.2.86. * Drop directory handles from IndexedDB on error * Implement audio in web builds I replaced the Symphonia decoders with rodio's default ones because rodio can't decode OGG files properly when using Symphonia's decoder. * Don't include `AudioWrapper` in native builds * Use `.removeEntry` to remove files/dirs instead of `.remove` `FileSystemDirectoryHandle.removeEntry` is the standard way to remove files and directories. `FileSystemHandle.remove` is nonstandard. * Fix `Drop` implementation in `AudioWrapper` It's possible for the `AudioWrapper` to be dropped on a different thread than it was created on. This commit handles that case. * Show an alert box in web builds on panic * Fix command names in hooks on Windows * Use `wgpu::Adapter::features` to detect push constants support * Don't show panic message if COI isn't enabled * Archiver filesystem now wraps a file instead of a filesystem * Apply clippy recommendations * Re-enable sound effect picker in the items window * Make some minor stylistic changes to names in src/filesystem/web.rs * Fix RTPs not being detected properly in web builds That's seriously embarassing, haha. * Auto-enable `Write` when `Truncate` or `Create` are set in web builds This probably shouldn't be necessary given that Rust's documentation states that you need to manually enable `Write` to use `Truncate` or `Create`, but I guess if it worked before in native then it should be supported in web builds, too. * Web filesystem `remove_dir` is now recursive This is for consistency with the host filesystem's implementation. * Make `project_path` in web builds consistent with native * Optimize path cache to avoid double-loading the web RTP folder * Move `skip` variable from previous commit outside of the loop * Fix crash when loading from nonexistent folder in web builds --- .cargo/config.toml | 3 + .gitattributes | 1 + Cargo.lock | 1034 ++++++---------- Cargo.toml | 85 +- Trunk.toml | 14 + assets/bindings.js | 54 + assets/coi-serviceworker.js | 118 ++ assets/favicon.ico | Bin 0 -> 9662 bytes assets/icon_ios_touch_192.png | Bin 0 -> 12569 bytes assets/main.js | 20 + assets/manifest.json | 28 + assets/worker.js | 24 + hooks/trunk_enable_build_std.sh | 4 + hooks/trunk_enable_build_std.sh.cmd | 3 + hooks/trunk_enable_build_std_background.sh | 9 + .../trunk_enable_build_std_background.sh.cmd | 12 + hooks/trunk_enable_build_std_pre.sh | 8 + hooks/trunk_enable_build_std_pre.sh.cmd | 7 + index.html | 139 +++ src/audio/mod.rs | 47 +- src/audio/wrapper.rs | 250 ++++ src/cache/data.rs | 47 + src/components/map_view.rs | 69 +- src/components/tilepicker.rs | 57 +- src/components/top_bar.rs | 80 +- src/config/global.rs | 5 + src/filesystem/archiver.rs | 56 +- src/filesystem/mod.rs | 3 + src/filesystem/path_cache.rs | 7 +- src/filesystem/project.rs | 175 ++- src/filesystem/web.rs | 1089 +++++++++++++++++ src/graphics/event.rs | 24 +- src/graphics/map.rs | 53 +- src/graphics/plane.rs | 4 +- src/graphics/primitives/sprite/graphic.rs | 99 +- src/graphics/primitives/sprite/mod.rs | 19 +- src/graphics/primitives/sprite/shader.rs | 114 +- src/graphics/primitives/sprite/sprite.wgsl | 19 +- .../sprite/sprite_header_push_constants.wgsl | 6 + .../sprite/sprite_header_uniforms.wgsl | 4 + src/graphics/primitives/tiles/autotiles.rs | 81 +- src/graphics/primitives/tiles/instance.rs | 8 +- src/graphics/primitives/tiles/mod.rs | 46 +- src/graphics/primitives/tiles/opacity.rs | 122 ++ src/graphics/primitives/tiles/shader.rs | 85 +- src/graphics/primitives/tiles/tilemap.wgsl | 30 +- .../tiles/tilemap_header_push_constants.wgsl | 7 + .../tiles/tilemap_header_uniforms.wgsl | 6 + src/graphics/primitives/viewport.rs | 86 +- src/lib.rs | 16 +- src/luminol.rs | 71 +- src/main.rs | 143 +++ src/prelude.rs | 8 + src/tabs/map.rs | 20 +- src/tabs/started.rs | 59 +- src/web/bindings.rs | 40 + src/web/mod.rs | 21 + src/web/web_worker_runner.rs | 1076 ++++++++++++++++ src/windows/mod.rs | 1 + src/windows/new_project.rs | 10 +- 60 files changed, 4792 insertions(+), 934 deletions(-) create mode 100644 .gitattributes create mode 100644 Trunk.toml create mode 100644 assets/bindings.js create mode 100644 assets/coi-serviceworker.js create mode 100644 assets/favicon.ico create mode 100644 assets/icon_ios_touch_192.png create mode 100644 assets/main.js create mode 100644 assets/manifest.json create mode 100644 assets/worker.js create mode 100755 hooks/trunk_enable_build_std.sh create mode 100644 hooks/trunk_enable_build_std.sh.cmd create mode 100755 hooks/trunk_enable_build_std_background.sh create mode 100644 hooks/trunk_enable_build_std_background.sh.cmd create mode 100755 hooks/trunk_enable_build_std_pre.sh create mode 100644 hooks/trunk_enable_build_std_pre.sh.cmd create mode 100644 index.html create mode 100644 src/audio/wrapper.rs create mode 100644 src/filesystem/web.rs create mode 100644 src/graphics/primitives/sprite/sprite_header_push_constants.wgsl create mode 100644 src/graphics/primitives/sprite/sprite_header_uniforms.wgsl create mode 100644 src/graphics/primitives/tiles/opacity.rs create mode 100644 src/graphics/primitives/tiles/tilemap_header_push_constants.wgsl create mode 100644 src/graphics/primitives/tiles/tilemap_header_uniforms.wgsl create mode 100644 src/web/bindings.rs create mode 100644 src/web/mod.rs create mode 100644 src/web/web_worker_runner.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index bc0b7619..3de5aa1b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,3 +3,6 @@ linker = "rust-lld" [target.x86_64-unknown-linux-gnu] rustflags = ["-C", "linker=clang", "-C", "link-arg=-fuse-ld=mold"] + +[target.'cfg(target_arch = "wasm32")'] +rustflags = ["--cfg=web_sys_unstable_apis", "-C", "target-feature=+atomics,+bulk-memory,+mutable-globals"] diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8524696a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/coi-serviceworker.js linguist-vendored diff --git a/Cargo.lock b/Cargo.lock index 5463b600..9dc6a183 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,67 +106,13 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aead" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" -dependencies = [ - "generic-array", -] - -[[package]] -name = "aes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" -dependencies = [ - "aes-soft", - "aesni", - "cipher", -] - -[[package]] -name = "aes-gcm" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "aes-soft" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" -dependencies = [ - "cipher", - "opaque-debug", -] - -[[package]] -name = "aesni" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" -dependencies = [ - "cipher", - "opaque-debug", -] - [[package]] name = "ahash" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.10", + "getrandom", "once_cell", "version_check", ] @@ -359,21 +305,6 @@ dependencies = [ "futures-lite", ] -[[package]] -name = "async-global-executor" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" -dependencies = [ - "async-channel", - "async-executor", - "async-io", - "async-lock", - "blocking", - "futures-lite", - "once_cell", -] - [[package]] name = "async-io" version = "1.13.0" @@ -390,7 +321,7 @@ dependencies = [ "polling", "rustix 0.37.23", "slab", - "socket2", + "socket2 0.4.9", "waker-fn", ] @@ -444,32 +375,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "async-std" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" -dependencies = [ - "async-channel", - "async-global-executor", - "async-io", - "async-lock", - "crossbeam-utils", - "futures-channel", - "futures-core", - "futures-io", - "futures-lite", - "gloo-timers", - "kv-log-macro", - "log", - "memchr", - "once_cell", - "pin-project-lite", - "pin-utils", - "slab", - "wasm-bindgen-futures", -] - [[package]] name = "async-task" version = "4.4.0" @@ -565,12 +470,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - [[package]] name = "base64" version = "0.13.1" @@ -738,12 +637,6 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - [[package]] name = "bytes" version = "1.4.0" @@ -845,15 +738,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cipher" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" -dependencies = [ - "generic-array", -] - [[package]] name = "clang-sys" version = "1.6.1" @@ -968,7 +852,7 @@ version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" dependencies = [ - "bytes 1.4.0", + "bytes", "memchr", ] @@ -976,7 +860,7 @@ dependencies = [ name = "command-lib" version = "0.1.0" dependencies = [ - "rand 0.8.5", + "rand", "serde", "strum", ] @@ -990,6 +874,16 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-random" version = "0.1.15" @@ -1006,33 +900,30 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb" dependencies = [ - "getrandom 0.2.10", + "getrandom", "once_cell", "proc-macro-hack", "tiny-keccak", ] [[package]] -name = "const_fn" -version = "0.4.9" +name = "const_format" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] [[package]] -name = "cookie" -version = "0.14.4" +name = "const_format_proc_macros" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" dependencies = [ - "aes-gcm", - "base64 0.13.1", - "hkdf", - "hmac", - "percent-encoding", - "rand 0.8.5", - "sha2 0.9.9", - "time 0.2.27", - "version_check", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] @@ -1104,8 +995,7 @@ dependencies = [ [[package]] name = "cpal" version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" +source = "git+https://github.com/DouglasDwyer/cpal.git?rev=91aeb4d6b02c25791f636fdf92a73637597c077a#91aeb4d6b02c25791f636fdf92a73637597c077a" dependencies = [ "alsa", "core-foundation-sys 0.8.4", @@ -1135,12 +1025,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" - [[package]] name = "crc" version = "3.0.1" @@ -1248,16 +1132,6 @@ dependencies = [ "typenum", ] -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "csscolorparser" version = "0.6.2" @@ -1268,46 +1142,6 @@ dependencies = [ "phf 0.11.2", ] -[[package]] -name = "ctr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" -dependencies = [ - "cipher", -] - -[[package]] -name = "curl" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509bd11746c7ac09ebd19f0b17782eae80aadee26237658a6b4808afb5c11a22" -dependencies = [ - "curl-sys", - "libc", - "openssl-probe", - "openssl-sys", - "schannel", - "socket2", - "winapi", -] - -[[package]] -name = "curl-sys" -version = "0.4.65+curl-8.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986" -dependencies = [ - "cc", - "libc", - "libnghttp2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", - "winapi", -] - [[package]] name = "d3d12" version = "0.6.0" @@ -1427,12 +1261,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - [[package]] name = "dispatch" version = "0.2.0" @@ -1760,7 +1588,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18" dependencies = [ "bit_field", - "flume 0.10.14", + "flume", "half", "lebe", "miniz_oxide 0.7.1", @@ -1863,17 +1691,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -[[package]] -name = "flume" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bebadab126f8120d410b677ed95eee4ba6eb7c6dd8e34a5ec88a08050e26132" -dependencies = [ - "futures-core", - "futures-sink", - "spinning_top", -] - [[package]] name = "flume" version = "0.10.14" @@ -2080,17 +1897,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.10" @@ -2100,20 +1906,10 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] -[[package]] -name = "ghash" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" -dependencies = [ - "opaque-debug", - "polyval", -] - [[package]] name = "gif" version = "0.12.0" @@ -2201,18 +1997,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "glow" version = "0.12.3" @@ -2370,6 +2154,25 @@ dependencies = [ "system-deps", ] +[[package]] +name = "h2" +version = "0.3.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap 1.9.3", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "half" version = "2.2.1" @@ -2439,26 +2242,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" -[[package]] -name = "hkdf" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" -dependencies = [ - "digest 0.9.0", - "hmac", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - [[package]] name = "home" version = "0.5.5" @@ -2480,46 +2263,33 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" dependencies = [ - "bytes 1.4.0", + "bytes", "fnv", "itoa", ] [[package]] -name = "http-client" -version = "6.5.3" +name = "http-body" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1947510dc91e2bf586ea5ffb412caad7673264e14bb39fb9078da114a94ce1a5" +checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "http-types", - "isahc", - "log", + "bytes", + "http", + "pin-project-lite", ] [[package]] -name = "http-types" -version = "2.12.0" +name = "httparse" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" -dependencies = [ - "anyhow", - "async-channel", - "async-std", - "base64 0.13.1", - "cookie", - "futures-lite", - "infer", - "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", -] +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humansize" @@ -2530,6 +2300,43 @@ dependencies = [ "libm", ] +[[package]] +name = "hyper" +version = "0.14.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.4.9", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "idna" version = "0.4.0" @@ -2589,6 +2396,20 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +[[package]] +name = "indexed_db_futures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbcff6ae46750b15cc594bfd277b188cbddcfdc1817848f97f03f26f8625b9e" +dependencies = [ + "cfg-if", + "js-sys", + "uuid", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2611,14 +2432,8 @@ dependencies = [ ] [[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - -[[package]] -name = "inotify" -version = "0.9.6" +name = "inotify" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" dependencies = [ @@ -2669,27 +2484,10 @@ dependencies = [ ] [[package]] -name = "isahc" -version = "0.9.14" +name = "ipnet" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2948a0ce43e2c2ef11d7edf6816508998d99e13badd1150be0914205df9388a" -dependencies = [ - "bytes 0.5.6", - "crossbeam-utils", - "curl", - "curl-sys", - "flume 0.9.2", - "futures-lite", - "http", - "log", - "once_cell", - "slab", - "sluice", - "tracing", - "tracing-futures", - "url", - "waker-fn", -] +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" [[package]] name = "itertools" @@ -2829,15 +2627,6 @@ dependencies = [ "arrayvec", ] -[[package]] -name = "kv-log-macro" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" -dependencies = [ - "log", -] - [[package]] name = "lab" version = "0.11.0" @@ -2905,28 +2694,6 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" -[[package]] -name = "libnghttp2-sys" -version = "0.1.8+1.55.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "libz-sys" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "line-wrap" version = "0.1.1" @@ -2975,9 +2742,6 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -dependencies = [ - "value-bag", -] [[package]] name = "lru" @@ -3001,6 +2765,8 @@ dependencies = [ "cfg-if", "color-eyre", "command-lib", + "console_error_panic_hook", + "const_format", "crc", "crossbeam", "dashmap", @@ -3015,7 +2781,9 @@ dependencies = [ "git-version", "glam", "image 0.24.7", + "indexed_db_futures", "itertools", + "js-sys", "luminol-term", "notify", "num_enum 0.7.0", @@ -3024,7 +2792,8 @@ dependencies = [ "parking_lot", "paste", "poll-promise", - "rand 0.8.5", + "rand", + "reqwest", "rfd", "rmxp-types", "rodio", @@ -3036,11 +2805,16 @@ dependencies = [ "static_assertions", "steamworks", "strum", - "surf", "syntect", "thiserror", + "tokio", "tracing", + "tracing-log", "tracing-subscriber", + "tracing-wasm", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", "windows 0.51.1", "winreg 0.51.0", "winres", @@ -3178,16 +2952,6 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3222,7 +2986,7 @@ checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -3252,7 +3016,25 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.10", + "getrandom", +] + +[[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]] @@ -3613,6 +3395,32 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "foreign-types", + "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 2.0.29", +] + [[package]] name = "openssl-probe" version = "0.1.5" @@ -3621,9 +3429,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.91" +version = "0.9.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" dependencies = [ "cc", "libc", @@ -3867,7 +3675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" dependencies = [ "phf_shared 0.11.2", - "rand 0.8.5", + "rand", ] [[package]] @@ -3956,7 +3764,7 @@ dependencies = [ "line-wrap", "quick-xml", "serde", - "time 0.3.27", + "time", ] [[package]] @@ -3981,6 +3789,8 @@ dependencies = [ "document-features", "smol", "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", ] [[package]] @@ -4005,17 +3815,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" -[[package]] -name = "polyval" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" -dependencies = [ - "cpuid-bool", - "opaque-debug", - "universal-hash", -] - [[package]] name = "portable-pty" version = "0.8.1" @@ -4124,19 +3923,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -4144,18 +3930,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -4165,16 +3941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -4183,16 +3950,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.10", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -4259,7 +4017,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.10", + "getrandom", "redox_syscall 0.2.16", "thiserror", ] @@ -4299,6 +4057,44 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64 0.21.2", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + [[package]] name = "resvg" version = "0.28.0" @@ -4357,7 +4153,7 @@ dependencies = [ "num-traits", "num_enum 0.6.1", "paste", - "rand 0.8.5", + "rand", "serde", "slab", "strum", @@ -4419,15 +4215,6 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustix" version = "0.37.23" @@ -4523,28 +4310,36 @@ dependencies = [ ] [[package]] -name = "semver" -version = "0.9.0" +name = "security-framework" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "semver-parser 0.7.0", + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys 0.8.4", + "libc", + "security-framework-sys", ] [[package]] -name = "semver" -version = "0.11.0" +name = "security-framework-sys" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ - "semver-parser 0.10.2", + "core-foundation-sys 0.8.4", + "libc", ] [[package]] -name = "semver-parser" -version = "0.7.0" +name = "semver" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] [[package]] name = "semver-parser" @@ -4586,17 +4381,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - [[package]] name = "serde_repr" version = "0.1.16" @@ -4671,15 +4455,6 @@ dependencies = [ "serial-core", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - [[package]] name = "sha1" version = "0.10.5" @@ -4691,12 +4466,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sha2" version = "0.9.9" @@ -4748,9 +4517,9 @@ checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" [[package]] name = "shlex" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[package]] name = "signal-hook" @@ -4821,17 +4590,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "sluice" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" -dependencies = [ - "async-channel", - "futures-core", - "futures-io", -] - [[package]] name = "smallvec" version = "1.11.0" @@ -4895,19 +4653,20 @@ dependencies = [ ] [[package]] -name = "spin" -version = "0.9.8" +name = "socket2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" dependencies = [ - "lock_api", + "libc", + "windows-sys 0.48.0", ] [[package]] -name = "spinning_top" -version = "0.2.5" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] @@ -4922,70 +4681,12 @@ dependencies = [ "num-traits", ] -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - [[package]] name = "steamworks" version = "0.10.0" @@ -5047,35 +4748,6 @@ dependencies = [ "syn 2.0.29", ] -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "surf" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718b1ae6b50351982dedff021db0def601677f2120938b070eadb10ba4038dd7" -dependencies = [ - "async-std", - "async-trait", - "cfg-if", - "encoding_rs", - "futures-util", - "getrandom 0.2.10", - "http-client", - "http-types", - "log", - "mime_guess", - "once_cell", - "pin-project-lite", - "serde", - "serde_json", - "web-sys", -] - [[package]] name = "svgtypes" version = "0.8.2" @@ -5178,6 +4850,27 @@ dependencies = [ "yaml-rust", ] +[[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 0.8.4", + "libc", +] + [[package]] name = "system-deps" version = "6.1.1" @@ -5275,7 +4968,7 @@ dependencies = [ "pest_derive", "phf 0.10.1", "regex", - "semver 0.11.0", + "semver", "sha2 0.9.9", "signal-hook 0.1.17", "siphasher", @@ -5356,21 +5049,6 @@ dependencies = [ "weezl", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi", -] - [[package]] name = "time" version = "0.3.27" @@ -5381,7 +5059,7 @@ dependencies = [ "itoa", "serde", "time-core", - "time-macros 0.2.13", + "time-macros", ] [[package]] @@ -5390,16 +5068,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - [[package]] name = "time-macros" version = "0.2.13" @@ -5409,19 +5077,6 @@ dependencies = [ "time-core", ] -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tiny-keccak" version = "2.0.2" @@ -5471,6 +5126,45 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2 0.5.4", + "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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml" version = "0.5.11" @@ -5514,6 +5208,12 @@ dependencies = [ "winnow", ] +[[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.37" @@ -5521,7 +5221,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5558,16 +5257,6 @@ dependencies = [ "tracing-subscriber", ] -[[package]] -name = "tracing-futures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" -dependencies = [ - "pin-project", - "tracing", -] - [[package]] name = "tracing-log" version = "0.1.3" @@ -5593,6 +5282,23 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + +[[package]] +name = "try-lock" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" + [[package]] name = "ttf-parser" version = "0.19.1" @@ -5630,15 +5336,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - [[package]] name = "unicode-bidi" version = "0.3.13" @@ -5678,16 +5375,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" -[[package]] -name = "universal-hash" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" -dependencies = [ - "generic-array", - "subtle", -] - [[package]] name = "url" version = "2.4.0" @@ -5697,7 +5384,6 @@ dependencies = [ "form_urlencoded", "idna", "percent-encoding", - "serde", ] [[package]] @@ -5733,7 +5419,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" dependencies = [ "atomic", - "getrandom 0.2.10", + "getrandom", + "wasm-bindgen", ] [[package]] @@ -5742,12 +5429,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "value-bag" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" - [[package]] name = "vcpkg" version = "0.2.15" @@ -5797,10 +5478,13 @@ dependencies = [ ] [[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] [[package]] name = "wasi" @@ -6006,7 +5690,7 @@ name = "wezterm-blob-leases" version = "0.1.0" source = "git+https://github.com/wez/wezterm?rev=3666303c7b26c6c966b3f136dbb954686d334cc3#3666303c7b26c6c966b3f136dbb954686d334cc3" dependencies = [ - "getrandom 0.2.10", + "getrandom", "mac_address", "once_cell", "sha2 0.9.9", @@ -6475,6 +6159,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 = "winreg" version = "0.51.0" @@ -6594,10 +6288,10 @@ dependencies = [ "nix 0.26.2", "once_cell", "ordered-stream", - "rand 0.8.5", + "rand", "serde", "serde_repr", - "sha1 0.10.5", + "sha1", "static_assertions", "tracing", "uds_windows", diff --git a/Cargo.toml b/Cargo.toml index a4fec802..29544067 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ git-version = "0.3" once_cell = "1.17" parking_lot = { version = "0.12", features = [ + "nightly", # This is required for parking_lot to work properly in WebAssembly builds with atomics support "deadlock_detection", "hardware-lock-elision", ] } @@ -51,8 +52,7 @@ dashmap = "5.5" crossbeam = "0.8" futures = "0.3" -poll-promise = { version = "0.3.0", features = ["smol"] } -surf = "2.3.2" +reqwest = "0.11.22" slab = { version = "0.4", features = ["serde"] } @@ -87,16 +87,88 @@ tracing-subscriber = "0.3" itertools = "0.11" static_assertions = "1.1.0" -rodio = { version = "0.17.1" } rustysynth = "1.2" +const_format = { version = "0.2.31", features = ["rust_1_51"] } + command-lib = { version = "*", path = "command-lib" } rmxp-types = { version = "*", path = "rmxp-types" } -luminol-term = { version = "*", path = "luminol-term" } steamworks = { version = "0.10.0", optional = true } crc = { version = "3.0", optional = true } +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +poll-promise = { version = "0.3.0", features = ["smol"] } +rodio = { version = "0.17.1" } + +luminol-term = { version = "*", path = "luminol-term" } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = "0.1" +tracing-wasm = "0.2" +tracing-log = "0.1.3" +wasm-bindgen = "0.2.87" +poll-promise = { version = "0.3.0", features = ["web"] } +tokio = { version = "1.32", features = ["sync"] } +wasm-bindgen-futures = "0.4" +indexed_db_futures = "0.3.0" +rodio = { version = "0.17.1", features = ["wasm-bindgen"] } +js-sys = "0.3" +web-sys = { version = "0.3", features = [ + "console", + "Window", + "Document", + "Element", + "DomException", + + "EventTarget", + "EventListener", + "AddEventListenerOptions", + + "Event", + "MouseEvent", + + "DedicatedWorkerGlobalScope", + "WorkerGlobalScope", + + "Worker", + "WorkerOptions", + "WorkerType", + + "Performance", + + "HtmlCanvasElement", + "OffscreenCanvas", + "OffscreenCanvasRenderingContext2d", + + "MutationObserver", + "MutationObserverInit", + "MutationRecord", + "Node", + + "Clipboard", + + "Storage", + + "WorkerLocation", + "Location", + "WorkerNavigator", + "Navigator", + + "Blob", + "File", + "FileSystemCreateWritableOptions", + "FileSystemDirectoryHandle", + "FileSystemFileHandle", + "FileSystemGetDirectoryOptions", + "FileSystemGetFileOptions", + "FileSystemHandle", + "FileSystemHandleKind", + "FileSystemRemoveOptions", + "FileSystemWritableFileStream", + "WritableStream", +] } + [features] steamworks = ["dep:steamworks", "crc"] @@ -156,6 +228,11 @@ opt-level = 3 members = ["command-lib", "rmxp-types", "luminol-term"] [patch.crates-io] +# We need this for rodio to work in WebAssembly until +# https://github.com/RustAudio/cpal/pull/774 +# is merged. +cpal = { git = "https://github.com/DouglasDwyer/cpal.git", rev = "91aeb4d6b02c25791f636fdf92a73637597c077a" } + # If you want to use the bleeding edge version of egui and eframe: # egui = { git = "https://github.com/emilk/egui", branch = "master" } # eframe = { git = "https://github.com/emilk/egui", branch = "master" } diff --git a/Trunk.toml b/Trunk.toml new file mode 100644 index 00000000..0621c299 --- /dev/null +++ b/Trunk.toml @@ -0,0 +1,14 @@ +[build] +filehash = false +inject_scripts = false + +[watch] +ignore = [".cargo"] + +[[hooks]] +stage = "pre_build" +command = "./hooks/trunk_enable_build_std_pre.sh" + +[[hooks]] +stage = "build" +command = "./hooks/trunk_enable_build_std.sh" diff --git a/assets/bindings.js b/assets/bindings.js new file mode 100644 index 00000000..91adb10f --- /dev/null +++ b/assets/bindings.js @@ -0,0 +1,54 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . + +export function is_worker() { + return typeof DedicatedWorkerGlobalScope === 'function' + && self instanceof DedicatedWorkerGlobalScope; +} + +export function worker() { + return is_worker() ? self : null; +} + +// A binding for this attribute was added in July 2023 but hasn't made its way into a release of +// web-sys as of September 2023 +export function performance(worker) { + return worker.performance; +} + +export function filesystem_supported() { + return typeof window?.showOpenFilePicker === 'function' + && typeof window?.showDirectoryPicker === 'function' + && typeof FileSystemFileHandle === 'function' + && typeof FileSystemWritableFileStream === 'function'; +} + +export async function _show_directory_picker() { + return await window.showDirectoryPicker({ mode: 'readwrite' }); +} + +export function dir_values(dir) { + return dir.values(); +} + +export async function _request_permission(handle) { + return (await handle.requestPermission({ mode: 'readwrite' })) === 'granted' +} + +export function cross_origin_isolated() { + return crossOriginIsolated === true; +} diff --git a/assets/coi-serviceworker.js b/assets/coi-serviceworker.js new file mode 100644 index 00000000..af2caf1b --- /dev/null +++ b/assets/coi-serviceworker.js @@ -0,0 +1,118 @@ +/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */ +let coepCredentialless = false; +if (typeof window === 'undefined') { + self.addEventListener("install", () => self.skipWaiting()); + self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim())); + + self.addEventListener("message", (ev) => { + if (!ev.data) { + return; + } else if (ev.data.type === "deregister") { + self.registration + .unregister() + .then(() => { + return self.clients.matchAll(); + }) + .then(clients => { + clients.forEach((client) => client.navigate(client.url)); + }); + } else if (ev.data.type === "coepCredentialless") { + coepCredentialless = ev.data.value; + } + }); + + self.addEventListener("fetch", function (event) { + const r = event.request; + if (r.cache === "only-if-cached" && r.mode !== "same-origin") { + return; + } + + const request = (coepCredentialless && r.mode === "no-cors") + ? new Request(r, { + credentials: "omit", + }) + : r; + event.respondWith( + fetch(request) + .then((response) => { + if (response.status === 0) { + return response; + } + + const newHeaders = new Headers(response.headers); + newHeaders.set("Cross-Origin-Embedder-Policy", + coepCredentialless ? "credentialless" : "require-corp" + ); + if (!coepCredentialless) { + newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin"); + } + newHeaders.set("Cross-Origin-Opener-Policy", "same-origin"); + + return new Response(response.body, { + status: response.status, + statusText: response.statusText, + headers: newHeaders, + }); + }) + .catch((e) => console.error(e)) + ); + }); + +} else { + (() => { + // You can customize the behavior of this script through a global `coi` variable. + const coi = { + shouldRegister: () => true, + shouldDeregister: () => false, + coepCredentialless: () => (window.chrome !== undefined || window.netscape !== undefined), + doReload: () => window.location.reload(), + quiet: false, + ...window.coi + }; + + const n = navigator; + + if (n.serviceWorker && n.serviceWorker.controller) { + n.serviceWorker.controller.postMessage({ + type: "coepCredentialless", + value: coi.coepCredentialless(), + }); + + if (coi.shouldDeregister()) { + n.serviceWorker.controller.postMessage({ type: "deregister" }); + } + } + + // If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are + // already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here. + if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return; + + if (!window.isSecureContext) { + !coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required."); + return; + } + + // In some environments (e.g. Chrome incognito mode) this won't be available + if (n.serviceWorker) { + n.serviceWorker.register(window.document.currentScript.src).then( + (registration) => { + !coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope); + + registration.addEventListener("updatefound", () => { + !coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker."); + coi.doReload(); + }); + + // If the registration is active, but it's not controlling the page + if (registration.active && !n.serviceWorker.controller) { + !coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker."); + coi.doReload(); + } + }, + (err) => { + !coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err); + } + ); + } + })(); +} diff --git a/assets/favicon.ico b/assets/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7c48df2bbd7ef6285a32dd6abf08b004631c5247 GIT binary patch literal 9662 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 literal 0 HcmV?d00001 diff --git a/assets/icon_ios_touch_192.png b/assets/icon_ios_touch_192.png new file mode 100644 index 0000000000000000000000000000000000000000..33d0a3c3bd6443b94f6586b42845e8fe37edc381 GIT binary patch literal 12569 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 Ls%(|CdFcNEDP6|e literal 0 HcmV?d00001 diff --git a/assets/main.js b/assets/main.js new file mode 100644 index 00000000..f14e1a35 --- /dev/null +++ b/assets/main.js @@ -0,0 +1,20 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +import wasm_bindgen, { luminol_main_start } from '/luminol.js'; + +await wasm_bindgen(); +luminol_main_start(); diff --git a/assets/manifest.json b/assets/manifest.json new file mode 100644 index 00000000..9c664e73 --- /dev/null +++ b/assets/manifest.json @@ -0,0 +1,28 @@ +{ + "name": "Luminol Editor", + "short_name": "Luminol", + "icons": [ + { + "src": "./icon-256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "./maskable_icon_x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "./icon-1024.png", + "sizes": "1024x1024", + "type": "image/png" + } + ], + "lang": "en-US", + "id": "/index.html", + "start_url": "./index.html", + "display": "standalone", + "background_color": "white", + "theme_color": "white" +} diff --git a/assets/worker.js b/assets/worker.js new file mode 100644 index 00000000..0df15da6 --- /dev/null +++ b/assets/worker.js @@ -0,0 +1,24 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +import wasm_bindgen, { luminol_worker_start } from '/luminol.js'; + +onmessage = async function (e) { + if (e.data[0] === 'init') { + await wasm_bindgen(undefined, e.data[1]); + await luminol_worker_start(e.data[2]); + } +}; diff --git a/hooks/trunk_enable_build_std.sh b/hooks/trunk_enable_build_std.sh new file mode 100755 index 00000000..36a0f868 --- /dev/null +++ b/hooks/trunk_enable_build_std.sh @@ -0,0 +1,4 @@ +#!/bin/sh +set -e + +$TRUNK_SOURCE_DIR/hooks/trunk_enable_build_std_background.sh & diff --git a/hooks/trunk_enable_build_std.sh.cmd b/hooks/trunk_enable_build_std.sh.cmd new file mode 100644 index 00000000..6c28a0a9 --- /dev/null +++ b/hooks/trunk_enable_build_std.sh.cmd @@ -0,0 +1,3 @@ +@echo off + +start /b %TRUNK_SOURCE_DIR%\hooks\trunk_enable_build_std_background.sh diff --git a/hooks/trunk_enable_build_std_background.sh b/hooks/trunk_enable_build_std_background.sh new file mode 100755 index 00000000..2b4f83a0 --- /dev/null +++ b/hooks/trunk_enable_build_std_background.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e + +# Wait until Trunk errors out or builds successfully, then restore the old Cargo config +sleep 1 +while [ -d $TRUNK_STAGING_DIR ] && [ ! -f $TRUNK_STAGING_DIR/luminol.js ] && pgrep -x 'trunk' > /dev/null; do + sleep 1 +done +mv $TRUNK_SOURCE_DIR/.cargo/config.toml.bak $TRUNK_SOURCE_DIR/.cargo/config.toml diff --git a/hooks/trunk_enable_build_std_background.sh.cmd b/hooks/trunk_enable_build_std_background.sh.cmd new file mode 100644 index 00000000..d0f587cd --- /dev/null +++ b/hooks/trunk_enable_build_std_background.sh.cmd @@ -0,0 +1,12 @@ +@echo off + +:: Wait until Trunk errors out or builds successfully, then restore the old Cargo config +:loop +timeout /t 1 /nobreak >nul +if not exist %TRUNK_STAGING_DIR%\ goto end +if exist %TRUNK_STAGING_DIR%\luminol.js goto end +tasklist | find trunk.exe >nul +if errorlevel 1 goto end +goto loop +:end +move %TRUNK_SOURCE_DIR%\.cargo\config.toml.bak %TRUNK_SOURCE_DIR%\.cargo\config.toml diff --git a/hooks/trunk_enable_build_std_pre.sh b/hooks/trunk_enable_build_std_pre.sh new file mode 100755 index 00000000..983b99bf --- /dev/null +++ b/hooks/trunk_enable_build_std_pre.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Enable std support for multithreading +[ ! -f $TRUNK_SOURCE_DIR/.cargo/config.toml.bak ] || mv $TRUNK_SOURCE_DIR/.cargo/config.toml.bak $TRUNK_SOURCE_DIR/.cargo/config.toml +cp $TRUNK_SOURCE_DIR/.cargo/config.toml $TRUNK_SOURCE_DIR/.cargo/config.toml.bak +echo '[unstable]' >> $TRUNK_SOURCE_DIR/.cargo/config.toml +echo 'build-std = ["std", "panic_abort"]' >> $TRUNK_SOURCE_DIR/.cargo/config.toml diff --git a/hooks/trunk_enable_build_std_pre.sh.cmd b/hooks/trunk_enable_build_std_pre.sh.cmd new file mode 100644 index 00000000..21354b1d --- /dev/null +++ b/hooks/trunk_enable_build_std_pre.sh.cmd @@ -0,0 +1,7 @@ +@echo off + +:: Enable std support for multithreading +if exist %TRUNK_SOURCE_DIR%\.cargo\config.toml.bak move %TRUNK_SOURCE_DIR%\.cargo\config.toml.bak %TRUNK_SOURCE_DIR%\.cargo\config.toml +copy %TRUNK_SOURCE_DIR%\.cargo\config.toml %TRUNK_SOURCE_DIR%\.cargo\config.toml.bak +echo [unstable] >> %TRUNK_SOURCE_DIR%\.cargo\config.toml +echo build-std = ["std", "panic_abort"] >> %TRUNK_SOURCE_DIR%\.cargo\config.toml diff --git a/index.html b/index.html new file mode 100644 index 00000000..d90251c9 --- /dev/null +++ b/index.html @@ -0,0 +1,139 @@ + + + + + + + + + + + Luminol + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/audio/mod.rs b/src/audio/mod.rs index 49da8f3d..25826cad 100644 --- a/src/audio/mod.rs +++ b/src/audio/mod.rs @@ -26,6 +26,12 @@ use crate::prelude::*; mod midi; +#[cfg(target_arch = "wasm32")] +mod wrapper; +#[cfg(target_arch = "wasm32")] +pub use wrapper::*; + +use std::io::{Read, Seek}; use strum::Display; use strum::EnumIter; @@ -53,6 +59,7 @@ struct Inner { sinks: HashMap, } +#[cfg(not(target_arch = "wasm32"))] // Audio can't be shared between threads in wasm either /// # Safety /// cpal claims that Stream (which is why Inner is not send) is not thread safe on android, which is why it is not Send anywhere else. /// We don't support android. The only other solution would be to use thread_local and... no. @@ -61,6 +68,11 @@ unsafe impl Send for Inner {} impl Default for Audio { fn default() -> Self { + #[cfg(target_arch = "wasm32")] + if web_sys::window().is_none() { + panic!("in web builds, `Audio` can only be created on the main thread"); + } + let (output_stream, output_stream_handle) = rodio::OutputStream::try_default().unwrap(); Self { inner: Mutex::new(Inner { @@ -81,24 +93,36 @@ impl Audio { pitch: u8, source: Source, ) -> Result<(), String> { - let mut inner = self.inner.lock(); - // Create a sink - let sink = rodio::Sink::try_new(&inner.output_stream_handle).map_err(|e| e.to_string())?; - let path = path.as_ref(); let file = state!() .filesystem .open_file(path, filesystem::OpenFlags::Read) .map_err(|e| e.to_string())?; + let is_midi = path + .extension() + .is_some_and(|e| matches!(e, "mid" | "midi")); + + self.play_from_file(file, is_midi, volume, pitch, source) + } + + fn play_from_file( + &self, + file: impl Read + Seek + Send + Sync + 'static, + is_midi: bool, + volume: u8, + pitch: u8, + source: Source, + ) -> Result<(), String> { + let mut inner = self.inner.lock(); + // Create a sink + let sink = rodio::Sink::try_new(&inner.output_stream_handle).map_err(|e| e.to_string())?; + // Select decoder type based on sound source match source { Source::SE | Source::ME => { // Non looping - if path - .extension() - .is_some_and(|e| matches!(e, "mid" | "midi")) - { + if is_midi { sink.append(midi::MidiSource::new(file, false)?); } else { sink.append(rodio::Decoder::new(file).map_err(|e| e.to_string())?); @@ -106,10 +130,7 @@ impl Audio { } _ => { // Looping - if path - .extension() - .is_some_and(|e| matches!(e, "mid" | "midi")) - { + if is_midi { sink.append(midi::MidiSource::new(file, true)?); } else { sink.append(rodio::Decoder::new_looped(file).map_err(|e| e.to_string())?); @@ -125,6 +146,7 @@ impl Audio { // Add sink to hash, stop the current one if it's there. if let Some(s) = inner.sinks.insert(source, sink) { s.stop(); + #[cfg(not(target_arch = "wasm32"))] s.sleep_until_end(); // wait for the sink to stop, there is a ~5ms delay where it will not }; @@ -151,6 +173,7 @@ impl Audio { let mut inner = self.inner.lock(); for (_, sink) in inner.sinks.iter_mut() { sink.stop(); + #[cfg(not(target_arch = "wasm32"))] // Sleeping ensures that the inner file is dropped. There is a delay of ~5ms where it is not dropped and this could lead to a panic sink.sleep_until_end(); } diff --git a/src/audio/wrapper.rs b/src/audio/wrapper.rs new file mode 100644 index 00000000..079eb09c --- /dev/null +++ b/src/audio/wrapper.rs @@ -0,0 +1,250 @@ +// Copyright (C) 2023 Lily Lyons +// +// This file is part of Luminol. +// +// Luminol is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Luminol is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Luminol. If not, see . +// +// Additional permission under GNU GPL version 3 section 7 +// +// If you modify this Program, or any covered work, by linking or combining +// it with Steamworks API by Valve Corporation, containing parts covered by +// terms of the Steamworks API by Valve Corporation, the licensors of this +// Program grant you additional permission to convey the resulting work. + +use crate::prelude::*; + +use poll_promise::Promise; +use slab::Slab; +use std::io::{Cursor, Read}; + +use super::{Audio, Source}; + +static_assertions::assert_impl_all!(AudioWrapper: Send, Sync); + +thread_local!(static SLAB: Lazy>>> = Lazy::new(|| Mutex::new(Slab::new()))); + +#[derive(Debug)] +pub struct AudioWrapper { + key: usize, + tx: mpsc::UnboundedSender, +} + +pub struct AudioWrapperCommand(AudioWrapperCommandInner); + +enum AudioWrapperCommandInner { + Play { + vec: Vec, + is_midi: bool, + volume: u8, + pitch: u8, + source: Source, + oneshot_tx: oneshot::Sender>, + }, + SetPitch { + pitch: u8, + source: Source, + oneshot_tx: oneshot::Sender<()>, + }, + SetVolume { + volume: u8, + source: Source, + oneshot_tx: oneshot::Sender<()>, + }, + ClearSinks { + oneshot_tx: oneshot::Sender<()>, + }, + Stop { + source: Source, + oneshot_tx: oneshot::Sender<()>, + }, + Drop { + key: usize, + oneshot_tx: oneshot::Sender, + }, +} + +impl AudioWrapper { + pub fn new(audio: Audio) -> Self { + audio.into() + } + + pub fn play( + &self, + path: impl AsRef, + volume: u8, + pitch: u8, + source: Source, + ) -> Result<(), String> { + // We have to load the file on the current thread, + // otherwise if we read the file in the main thread of a web browser + // we will block the main thread + let path = path.as_ref(); + let mut file = state!() + .filesystem + .open_file(path, filesystem::OpenFlags::Read) + .map_err(|e| e.to_string())?; + let length = state!() + .filesystem + .metadata(path) + .map_err(|e| e.to_string())? + .size as usize; + let mut vec = vec![0; length]; + file.read(&mut vec[..]).map_err(|e| e.to_string())?; + + let is_midi = path + .extension() + .is_some_and(|e| matches!(e, "mid" | "midi")); + + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + self.tx + .send(AudioWrapperCommand(AudioWrapperCommandInner::Play { + vec, + is_midi, + volume, + pitch, + source, + oneshot_tx, + })) + .unwrap(); + oneshot_rx.blocking_recv().unwrap() + } + + pub fn set_pitch(&self, pitch: u8, source: &Source) { + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + self.tx + .send(AudioWrapperCommand(AudioWrapperCommandInner::SetPitch { + pitch, + source: source.clone(), + oneshot_tx, + })) + .unwrap(); + oneshot_rx.blocking_recv().unwrap() + } + + pub fn set_volume(&self, volume: u8, source: &Source) { + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + self.tx + .send(AudioWrapperCommand(AudioWrapperCommandInner::SetVolume { + volume, + source: source.clone(), + oneshot_tx, + })) + .unwrap(); + oneshot_rx.blocking_recv().unwrap() + } + + pub fn clear_sinks(&self) { + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + self.tx + .send(AudioWrapperCommand(AudioWrapperCommandInner::ClearSinks { + oneshot_tx, + })) + .unwrap(); + oneshot_rx.blocking_recv().unwrap() + } + + pub fn stop(&self, source: &Source) { + let (oneshot_tx, oneshot_rx) = oneshot::channel(); + self.tx + .send(AudioWrapperCommand(AudioWrapperCommandInner::Stop { + source: source.clone(), + oneshot_tx, + })) + .unwrap(); + oneshot_rx.blocking_recv().unwrap() + } +} + +impl From