diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ae0188b --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,31 @@ +name: Test + +on: [pull_request, push] + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Update Rust + run: rustup update stable + + - name: Install dependencies + run: sudo apt install librust-alsa-sys-dev libudev-dev + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@cargo-llvm-cov + + - name: Generate code coverage + run: cargo llvm-cov --all-features --no-cfg-coverage --workspace --lcov --output-path lcov.info + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: lcov.info + fail_ci_if_error: true \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ca0060 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/target +/.vscode +/.fleet +/.idea +database.db diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..68c3ef0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,4884 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80179d7dd5d7e8c285d67c4a1e652972a92de7475beddfb92028c76463b13225" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb10ed32c63247e4e39a8f42e8e30fb9442fbf7878c8e4a9849e7e381619bea" + +[[package]] +name = "accesskit_consumer" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c17cca53c09fbd7288667b22a201274b9becaa27f0b91bf52a526db95de45e6" +dependencies = [ + "accesskit", +] + +[[package]] +name = "accesskit_macos" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd3b6ae1eabbfbced10e840fd3fce8a93ae84f174b3e4ba892ab7bcb42e477a7" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2", + "once_cell", +] + +[[package]] +name = "accesskit_windows" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcae27ec0974fc7c3b0b318783be89fd1b2e66dd702179fe600166a38ff4a0b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "once_cell", + "paste", + "static_assertions", + "windows 0.48.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88e39fcec2e10971e188730b7a76bab60647dacc973d4591855ebebcadfaa738" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_windows", + "winit", +] + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "alsa" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2562ad8dcf0f789f65c6fdaad8a8a9708ed6b488e649da28c01656ad66b8b47" +dependencies = [ + "alsa-sys", + "bitflags 1.3.2", + "libc", + "nix 0.24.3", +] + +[[package]] +name = "alsa-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android-activity" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64529721f27c2314ced0890ce45e469574a73e5e6fdd6e9da1860eb29285f5e0" +dependencies = [ + "android-properties", + "bitflags 1.3.2", + "cc", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum 0.6.1", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "arboard" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aafb29b107435aa276664c1db8954ac27a6e105cdad3c88287a199eb0e313c08" +dependencies = [ + "clipboard-win", + "core-graphics", + "image", + "log", + "objc", + "objc-foundation", + "objc_id", + "parking_lot", + "thiserror", + "winapi", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "async-broadcast" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +dependencies = [ + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +dependencies = [ + "concurrent-queue", + "event-listener 4.0.3", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +dependencies = [ + "async-lock 3.3.0", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite 2.2.0", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" +dependencies = [ + "async-lock 2.8.0", + "autocfg", + "blocking", + "futures-lite 1.13.0", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +dependencies = [ + "event-listener 4.0.3", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c79fed4cdb43e993fcdadc7e58a09fd0e3e649c4436fa11da71c9f1f3ee7feb9" + +[[package]] +name = "bevy" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bc7e09282a82a48d70ade0c4c1154b0fd7882a735a39c66766a5d0f4718ea9" +dependencies = [ + "bevy_internal", +] + +[[package]] +name = "bevy-inspector-egui" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d65b98d6fca1209c36c4d403c377f303aad22d940281fe1a9e431217516f0622" +dependencies = [ + "bevy-inspector-egui-derive", + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_egui", + "bevy_hierarchy", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_reflect", + "bevy_render", + "bevy_utils", + "bevy_window", + "egui", + "image", + "once_cell", + "pretty-type-name", + "smallvec", +] + +[[package]] +name = "bevy-inspector-egui-derive" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec800b7cf98151b5dbff80f0eb6dffcb4bcfceef6e457888b395ead4eb7e75ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_a11y" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68080288c932634f6563d3a8299efe0ddc9ea6787539c4c771ba250d089a94f0" +dependencies = [ + "accesskit", + "bevy_app", + "bevy_derive", + "bevy_ecs", +] + +[[package]] +name = "bevy_animation" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa37683b1281e1ba8cf285644e6e3f0704f14b3901c5ee282067ff7ff6f4a56" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_time", + "bevy_transform", + "bevy_utils", +] + +[[package]] +name = "bevy_app" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41731817993f92e4363dd3335558e779e290bc71eefc0b5547052b85810907e" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "downcast-rs", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_asset" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "935984568f75867dd7357133b06f4b1502cd2be55e4642d483ce597e46e63bff" +dependencies = [ + "async-broadcast", + "async-fs", + "async-lock 2.8.0", + "bevy_app", + "bevy_asset_macros", + "bevy_ecs", + "bevy_log", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bevy_winit", + "blake3", + "crossbeam-channel", + "downcast-rs", + "futures-io", + "futures-lite 1.13.0", + "js-sys", + "parking_lot", + "ron", + "serde", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "bevy_asset_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f48b9bbe4ec605e4910b5cd1e1a0acbfbe0b80af5f3bcc4489a9fdd1e80058c" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_audio" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a69889e1bfa4dbac4e641536b94f91c441da55796ad9832e77836b8264688b" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_transform", + "bevy_utils", + "oboe", + "rodio", +] + +[[package]] +name = "bevy_core" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3daa24502a14839509f02407bc7e48299fe84d260877de23b60662de0f4f4b6c" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bytemuck", +] + +[[package]] +name = "bevy_core_pipeline" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4b77c4fca6e90edbe2e72da7bc9aa7aed7dfdfded0920ae0a0c845f5e11084a" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_derive", + "bevy_ecs", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bitflags 2.4.1", + "radsort", + "serde", +] + +[[package]] +name = "bevy_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f484318350462c58ba3942a45a656c1fd6b6e484a6b6b7abc3a787ad1a51e500" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_diagnostic" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa38ca5967d335cc1006a0e0f1a86c350e2f15fd1878449f61d04cd57a7c4060" +dependencies = [ + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_log", + "bevy_time", + "bevy_utils", + "sysinfo", +] + +[[package]] +name = "bevy_ecs" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7709fbd22f81fb681534cd913c41e1cd18b17143368743281195d7f024b61aea" +dependencies = [ + "async-channel 1.9.0", + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "downcast-rs", + "event-listener 2.5.3", + "fixedbitset", + "rustc-hash", + "serde", + "thiserror", + "thread_local", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8843aa489f159f25cdcd9fee75cd7d221a7098a71eaa72cb2d6b40ac4e3f1ba" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_editor_pls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170e3ee3293f70be7fa1b82e8ffe52dcb190c68b2f0ef4847afe65626b417ad7" +dependencies = [ + "bevy", + "bevy_editor_pls_core", + "bevy_editor_pls_default_windows", + "egui", + "egui-gizmo", +] + +[[package]] +name = "bevy_editor_pls_core" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9bab331de653d7d976a1fb0ab3ae8eff1dc821c5991c09bdbe945e70234839c" +dependencies = [ + "bevy", + "bevy-inspector-egui", + "egui_dock", + "indexmap 2.1.0", +] + +[[package]] +name = "bevy_editor_pls_default_windows" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b833e3b3d5c69b92527e766b470b27cb4745406e2087be19be81b595cc48792c" +dependencies = [ + "bevy", + "bevy-inspector-egui", + "bevy_editor_pls_core", + "bevy_mod_debugdump", + "egui-gizmo", + "indexmap 2.1.0", + "opener", + "pretty-type-name", +] + +[[package]] +name = "bevy_egui" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85450af551b7e1cb766f710763b60a12a82ffd6323945a8f776c6334c59ccdc1" +dependencies = [ + "arboard", + "bevy", + "egui", + "thread_local", + "webbrowser", +] + +[[package]] +name = "bevy_encase_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5328a3715e933ebbff07d0e99528dc423c4f7a53590ed1ac19a120348b028990" +dependencies = [ + "bevy_macro_utils", + "encase_derive_impl", +] + +[[package]] +name = "bevy_gilrs" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b81ca2ebf66cbc7f998f1f142b15038ffe3c4ae1d51f70adda26dcf51b0c4ca" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_log", + "bevy_time", + "bevy_utils", + "gilrs", + "thiserror", +] + +[[package]] +name = "bevy_gizmos" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db232274ddca2ae452eb2731b98267b795d133ddd14013121bc7daddde1c7491" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_math", + "bevy_pbr", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_transform", + "bevy_utils", +] + +[[package]] +name = "bevy_gltf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85adc6b1fc86687bf67149e0bafaa4d6da432232fa956472d1b37f19121d3ace" +dependencies = [ + "base64 0.13.1", + "bevy_animation", + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_core_pipeline", + "bevy_ecs", + "bevy_hierarchy", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_reflect", + "bevy_render", + "bevy_scene", + "bevy_tasks", + "bevy_transform", + "bevy_utils", + "gltf", + "percent-encoding", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "bevy_hierarchy" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06bd477152ce2ae1430f5e0a4f19216e5785c22fee1ab23788b5982dc59d1a55" +dependencies = [ + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_log", + "bevy_reflect", + "bevy_utils", + "smallvec", +] + +[[package]] +name = "bevy_input" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab9a599189b2a694c182d60cd52219dd9364f9892ff542d87799b8e45d9e6dc" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_utils", + "thiserror", +] + +[[package]] +name = "bevy_internal" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f124bece9831afd80897815231072d51bfe3ac58c6bb58eca8880963b6d0487c" +dependencies = [ + "bevy_a11y", + "bevy_animation", + "bevy_app", + "bevy_asset", + "bevy_audio", + "bevy_core", + "bevy_core_pipeline", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_gilrs", + "bevy_gizmos", + "bevy_gltf", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_pbr", + "bevy_ptr", + "bevy_reflect", + "bevy_render", + "bevy_scene", + "bevy_sprite", + "bevy_tasks", + "bevy_text", + "bevy_time", + "bevy_transform", + "bevy_ui", + "bevy_utils", + "bevy_window", + "bevy_winit", +] + +[[package]] +name = "bevy_log" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc10ba1d225a8477b9e80a1bf797d8a8b8274e83c9b24fb4d9351aec9229755" +dependencies = [ + "android_log-sys", + "bevy_app", + "bevy_ecs", + "bevy_utils", + "console_error_panic_hook", + "tracing-log 0.1.4", + "tracing-subscriber", + "tracing-wasm", +] + +[[package]] +name = "bevy_macro_utils" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e566640c6b6dced73d2006c764c2cffebe1a82be4809486c4a5d7b4b50efed4d" +dependencies = [ + "proc-macro2", + "quote", + "rustc-hash", + "syn 2.0.48", + "toml_edit 0.20.7", +] + +[[package]] +name = "bevy_math" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ddc2b76783939c530178f88e5711a1b01044d7b02db4033e2eb8b43b6cf4ec" +dependencies = [ + "glam", + "serde", +] + +[[package]] +name = "bevy_mikktspace" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ec4962977a746d870170532fc92759e04d3dbcae8b7b82e7ca3bb83b1d75277" +dependencies = [ + "glam", +] + +[[package]] +name = "bevy_mod_debugdump" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4db8601f41ea570b7d32f3177292a608196c59bdf3298001a9e202d5e7439438" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_render", + "bevy_utils", + "once_cell", + "petgraph", + "pretty-type-name", +] + +[[package]] +name = "bevy_pbr" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520bfd2a898c74f84ea52cfb8eb061f37373ad15e623489d5f75d27ebd6138fe" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.4.1", + "bytemuck", + "fixedbitset", + "naga_oil", + "radsort", + "smallvec", + "thread_local", +] + +[[package]] +name = "bevy_ptr" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c77ec20c8fafcdc196508ef5ccb4f0400a8d193cb61f7b14a36ed9a25ad423cf" + +[[package]] +name = "bevy_reflect" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7921f15fc944c9c8ad01d7dbcea6505b8909c6655cd9382bab1407181556038" +dependencies = [ + "bevy_math", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "downcast-rs", + "erased-serde", + "glam", + "serde", + "smallvec", + "smol_str", + "thiserror", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a8c5475f216e751ef4452a1306b00711f33d2d04d9f149e4c845dfeb6753a0" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn 2.0.48", + "uuid", +] + +[[package]] +name = "bevy_render" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdefdd3737125b0d94a6ff20bb70fa8cfe9d7d5dcd72ba4dfe6c5f1d30d9f6e4" +dependencies = [ + "async-channel 1.9.0", + "bevy_app", + "bevy_asset", + "bevy_core", + "bevy_derive", + "bevy_ecs", + "bevy_encase_derive", + "bevy_hierarchy", + "bevy_log", + "bevy_math", + "bevy_mikktspace", + "bevy_reflect", + "bevy_render_macros", + "bevy_tasks", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bitflags 2.4.1", + "bytemuck", + "codespan-reporting", + "downcast-rs", + "encase", + "futures-lite 1.13.0", + "hexasphere", + "image", + "js-sys", + "ktx2", + "naga", + "naga_oil", + "ruzstd", + "serde", + "smallvec", + "thiserror", + "thread_local", + "wasm-bindgen", + "web-sys", + "wgpu", +] + +[[package]] +name = "bevy_render_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d86bfc5a1e7fbeeaec0c4ceab18155530f5506624670965db3415f75826bea" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_scene" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7df078b5e406e37c8a1c6ba0d652bf105fde713ce3c3efda7263fe27467eee5" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "ron", + "serde", + "thiserror", + "uuid", +] + +[[package]] +name = "bevy_sprite" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7cc0c9d946e17e3e0aaa202f182837bc796c4f862b2e5a805134f873f21cf7f" +dependencies = [ + "bevy_app", + "bevy_asset", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_transform", + "bevy_utils", + "bitflags 2.4.1", + "bytemuck", + "fixedbitset", + "guillotiere", + "radsort", + "rectangle-pack", + "thiserror", +] + +[[package]] +name = "bevy_tasks" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fefa7fe0da8923525f7500e274f1bd60dbd79918a25cf7d0dfa0a6ba15c1cf" +dependencies = [ + "async-channel 1.9.0", + "async-executor", + "async-task", + "concurrent-queue", + "futures-lite 1.13.0", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_text" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9a79d49ca06170d69149949b134c14e8b99ace1444c1ca2cd4743b19d5b055" +dependencies = [ + "ab_glyph", + "bevy_app", + "bevy_asset", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_transform", + "bevy_utils", + "bevy_window", + "glyph_brush_layout", + "serde", + "thiserror", +] + +[[package]] +name = "bevy_time" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6250d76eed3077128b6a3d004f9f198b01107800b9824051e32bb658054e837" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_reflect", + "bevy_utils", + "crossbeam-channel", + "thiserror", +] + +[[package]] +name = "bevy_transform" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d541e0c292edbd96afae816ee680e02247422423ccd5dc635c1e211a20ed64be" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", + "bevy_reflect", + "thiserror", +] + +[[package]] +name = "bevy_ui" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d785e3b75dabcb2a8ad0d50933f8f3446d59e512cabc2d2a145e28c2bb8792ba" +dependencies = [ + "bevy_a11y", + "bevy_app", + "bevy_asset", + "bevy_core_pipeline", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_reflect", + "bevy_render", + "bevy_sprite", + "bevy_text", + "bevy_transform", + "bevy_utils", + "bevy_window", + "bytemuck", + "serde", + "smallvec", + "taffy", + "thiserror", +] + +[[package]] +name = "bevy_utils" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7915222f4a08ccc782e08d10b751b42e5f9d786e697d0cb3fd09333cb7e8b6ea" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown 0.14.3", + "instant", + "nonmax", + "petgraph", + "thiserror", + "tracing", + "uuid", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aafecc952b6b8eb1a93c12590bd867d25df2f4ae1033a01dfdfc3c35ebccfff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "bevy_window" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ee72bf7f974000e9b31bb971a89387f1432ba9413f35c4fef59fef49767260" +dependencies = [ + "bevy_a11y", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_reflect", + "bevy_utils", + "raw-window-handle", +] + +[[package]] +name = "bevy_winit" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb71f287eca9006dda998784c7b931e400ae2cc4c505da315882a8b082f21ad" +dependencies = [ + "accesskit_winit", + "approx", + "bevy_a11y", + "bevy_app", + "bevy_derive", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_math", + "bevy_tasks", + "bevy_utils", + "bevy_window", + "crossbeam-channel", + "raw-window-handle", + "wasm-bindgen", + "web-sys", + "winit", +] + +[[package]] +name = "bindgen" +version = "0.69.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" +dependencies = [ + "bitflags 2.4.1", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.48", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] + +[[package]] +name = "blake3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0231f06152bf547e9c2b5194f247cd97aacf6dcd8b15d8e5ec0663f64580da87" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", +] + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + +[[package]] +name = "blocking" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +dependencies = [ + "async-channel 2.1.1", + "async-lock 3.3.0", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite 2.2.0", + "piper", + "tracing", +] + +[[package]] +name = "bstr" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +dependencies = [ + "memchr", + "regex-automata 0.4.3", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "bytemuck" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "calloop" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +dependencies = [ + "bitflags 1.3.2", + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "jobserver", + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading 0.8.1", +] + +[[package]] +name = "clipboard-win" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "com-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +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_panic" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" + +[[package]] +name = "const_soft_float" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ca1caa64ef4ed453e68bb3db612e51cf1b2f5b871337f0fcab1c8f87cc3dff" + +[[package]] +name = "constant_time_eq" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" + +[[package]] +name = "constgebra" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd23e864550e6dafc1e41ac78ce4f1ccddc8672b40c403524a04ff3f0518420" +dependencies = [ + "const_soft_float", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "coreaudio-rs" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +dependencies = [ + "bitflags 1.3.2", + "core-foundation-sys", + "coreaudio-sys", +] + +[[package]] +name = "coreaudio-sys" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f01585027057ff5f0a5bf276174ae4c1594a2c5bde93d5f46a016d76270f5a9" +dependencies = [ + "bindgen", +] + +[[package]] +name = "cpal" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d959d90e938c5493000514b446987c07aed46c668faaa7d34d6c7a67b1a578c" +dependencies = [ + "alsa", + "core-foundation-sys", + "coreaudio-rs", + "dasp_sample", + "jni 0.19.0", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "oboe", + "once_cell", + "parking_lot", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.46.0", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "d3d12" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20" +dependencies = [ + "bitflags 2.4.1", + "libloading 0.8.1", + "winapi", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.1", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "duplicate" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de78e66ac9061e030587b2a2e75cc88f22304913c907b11307bca737141230cb" +dependencies = [ + "heck", + "proc-macro-error", +] + +[[package]] +name = "ecolor" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf4e52dbbb615cfd30cf5a5265335c217b5fd8d669593cea74a517d9c605af" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "egui" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bd69fed5fcf4fbb8225b24e80ea6193b61e17a625db105ef0c4d71dde6eb8b7" +dependencies = [ + "ahash", + "epaint", + "nohash-hasher", +] + +[[package]] +name = "egui-gizmo" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f732ad247afe275d6cf901e0f134025ad735007c8f4d82e667a6871f1b4a5441" +dependencies = [ + "egui", + "glam", +] + +[[package]] +name = "egui_dock" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a52f67bcab0eb6050cf8051c614966c1c57129fab23dbeae9c157214779053c7" +dependencies = [ + "duplicate", + "egui", + "paste", +] + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "emath" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef2b29de53074e575c18b694167ccbe6e5191f7b25fe65175a0d905a32eeec0" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "encase" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fce2eeef77fd4a293a54b62aa00ac9daebfbcda4bf8998c5a815635b004aa1c" +dependencies = [ + "const_panic", + "encase_derive", + "glam", + "thiserror", +] + +[[package]] +name = "encase_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e520cde08cbf4f7cc097f61573ec06ce467019803de8ae82fb2823fa1554a0e" +dependencies = [ + "encase_derive_impl", +] + +[[package]] +name = "encase_derive_impl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fe2568f851fd6144a45fa91cfed8fe5ca8fc0b56ba6797bfc1ed2771b90e37c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "epaint" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58067b840d009143934d91d8dcb8ded054d8301d7c11a517ace0a99bb1e1595e" +dependencies = [ + "ab_glyph", + "ahash", + "bytemuck", + "ecolor", + "emath", + "nohash-hasher", + "parking_lot", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + +[[package]] +name = "error-code" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "event-listener" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +dependencies = [ + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.71.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "fdeflate" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-lite" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445ba825b27408685aaecefd65178908c36c6e96aaf6d8599419d46e624192ba" +dependencies = [ + "fastrand 2.0.1", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "gethostname" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb65d4ba3173c56a500b555b532f72c42e8d1fe64962b518897f8959fae2c177" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gilrs" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b2e57a9cb946b5d04ae8638c5f554abb5a9f82c4c950fd5b1fee6d119592fb" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0af1827b7dd2f36d740ae804c1b3ea0d64c12533fb61ff91883005143a0e8c5a" +dependencies = [ + "core-foundation", + "inotify", + "io-kit-sys", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix 0.27.1", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "windows 0.52.0", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" +dependencies = [ + "bytemuck", + "serde", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "glow" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0fe580e4b60a8ab24a868bc08e2f03cbcb20d3d676601fa909386713333728" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "gltf" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b78f069cf941075835822953c345b9e1edd67ae347b81ace3aea9de38c2ef33" +dependencies = [ + "byteorder", + "gltf-json", + "lazy_static", + "serde_json", +] + +[[package]] +name = "gltf-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "438ffe1a5540d75403feaf23636b164e816e93f6f03131674722b3886ce32a57" +dependencies = [ + "inflections", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "gltf-json" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "655951ba557f2bc69ea4b0799446bae281fa78efae6319968bdd2c3e9a06d8e1" +dependencies = [ + "gltf-derive", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "glyph_brush_layout" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38" +dependencies = [ + "ab_glyph", + "approx", + "xi-unicode", +] + +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.4.1", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.4.1", +] + +[[package]] +name = "gpu-allocator" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce95f9e2e11c2c6fadfce42b5af60005db06576f231f5c92550fdded43c423e8" +dependencies = [ + "backtrace", + "log", + "thiserror", + "winapi", + "windows 0.44.0", +] + +[[package]] +name = "gpu-descriptor" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +dependencies = [ + "bitflags 2.4.1", + "gpu-descriptor-types", + "hashbrown 0.14.3", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +dependencies = [ + "bitflags 2.4.1", +] + +[[package]] +name = "grid" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" + +[[package]] +name = "guillotiere" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62d5865c036cb1393e23c50693df631d3f5d7bcca4c04fe4cc0fd592e74a782" +dependencies = [ + "euclid", + "svg_fmt", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", + "serde", +] + +[[package]] +name = "hashlink" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "hassle-rs" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1397650ee315e8891a0df210707f0fc61771b0cc518c3023896064c5407cb3b0" +dependencies = [ + "bitflags 1.3.2", + "com-rs", + "libc", + "libloading 0.7.4", + "thiserror", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hexasphere" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb3df16a7bcb1b5bc092abd55e14f77ca70aea14445026e264586fc62889a10" +dependencies = [ + "constgebra", + "glam", +] + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "image" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "inflections" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" + +[[package]] +name = "inotify" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" +dependencies = [ + "bitflags 1.3.2", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "io-kit-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4769cb30e5dcf1710fc6730d3e94f78c47723a014a567de385e113c737394640" +dependencies = [ + "core-foundation-sys", + "mach2", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" +dependencies = [ + "libc", + "libloading 0.7.4", + "pkg-config", +] + +[[package]] +name = "ktx2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87d65e08a9ec02e409d27a0139eaa6b9756b4d81fe7cde71f6941a83730ce838" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "lewton" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030" +dependencies = [ + "byteorder", + "ogg", + "tinyvec", +] + +[[package]] +name = "libc" +version = "0.2.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libloading" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libredox" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607" +dependencies = [ + "bitflags 2.4.1", + "libc", + "redox_syscall 0.4.1", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "matrixmultiply" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7574c1cf36da4798ab73da5b215bbf444f50718207754cb522201d78d1cd0ff2" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + +[[package]] +name = "metal" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "623b5e6cefd76e58f774bd3cc0c6f5c7615c58c03a97815245a25c3c9bdee318" +dependencies = [ + "bitflags 2.4.1", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "naga" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e" +dependencies = [ + "bit-set", + "bitflags 2.4.1", + "codespan-reporting", + "hexf-parse", + "indexmap 1.9.3", + "log", + "num-traits", + "pp-rs", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + +[[package]] +name = "naga_oil" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac54c77b3529887f9668d3dd81e955e58f252b31a333f836e3548c06460b958" +dependencies = [ + "bit-set", + "codespan-reporting", + "data-encoding", + "indexmap 1.9.3", + "naga", + "once_cell", + "regex", + "regex-syntax 0.7.5", + "rustc-hash", + "thiserror", + "tracing", + "unicode-ident", +] + +[[package]] +name = "nalgebra" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307ed9b18cc2423f29e83f84fd23a8e73628727990181f18641a8b5dc2ab1caa" +dependencies = [ + "approx", + "matrixmultiply", + "nalgebra-macros", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "nalgebra-macros" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91761aed67d03ad966ef783ae962ef9bbaca728d2dd7ceb7939ec110fffad998" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum 0.5.11", + "raw-window-handle", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset 0.7.1", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + +[[package]] +name = "normpath" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a015b430d3c108a207fd776d2e2196aaf8b1cf8cf93253e3a097ff3085076a1" +dependencies = [ + "num_enum_derive 0.6.1", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96667db765a921f7b295ffee8b60472b686a51d4f21c2ee4ffdb94c7013b65a6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3.patch-leaks.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e01640f9f2cb1220bbe80325e179e532cb3379ebcd1bf2279d703c19fe3a468" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "oboe" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8868cc237ee02e2d9618539a23a8d228b9bb3fc2e7a5b11eed3831de77c395d0" +dependencies = [ + "jni 0.20.0", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "oboe-sys", +] + +[[package]] +name = "oboe-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f44155e7fb718d3cfddcf70690b2b51ac4412f347cd9e4fbe511abe9cd7b5f2" +dependencies = [ + "cc", +] + +[[package]] +name = "ogg" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e" +dependencies = [ + "byteorder", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opener" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788" +dependencies = [ + "bstr", + "normpath", + "winapi", +] + +[[package]] +name = "orbclient" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f0d54bde9774d3a51dcf281a5def240c71996bc6ca05d2c847ec8b2b216166" +dependencies = [ + "libredox", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owned_ttf_parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.4.1", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.1.0", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" + +[[package]] +name = "png" +version = "0.17.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "pp-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb458bb7f6e250e6eb79d5026badc10a3ebb8f9a15d1fff0f13d17c71f4d6dee" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pretty-type-name" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f73cdaf19b52e6143685c3606206e114a4dfa969d6b14ec3894c88eb38bd4b" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135ede8821cf6376eb7a64148901e1690b788c11ae94dc297ae917dbc91dc0e" + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radsort" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17fd96390ed3feda12e1dfe2645ed587e0bea749e319333f104a33ff62f77a0b" + +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rectangle-pack" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d463f2884048e7153449a55166f91028d5b0ea53c79377099ce4e8cf0cf9bb" + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.3", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.2", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "renderdoc-sys" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" + +[[package]] +name = "rodio" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1bb7b48ee48471f55da122c0044fcc7600cfcc85db88240b89cb832935e611" +dependencies = [ + "cpal", + "lewton", +] + +[[package]] +name = "ron" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.6", + "bitflags 2.4.1", + "serde", + "serde_derive", +] + +[[package]] +name = "rusqlite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +dependencies = [ + "bitflags 2.4.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "ruzstd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3ffab8f9715a0d455df4bbb9d21e91135aab3cd3ca187af0cd0c3c3f868fdc" +dependencies = [ + "byteorder", + "thiserror-core", + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" + +[[package]] +name = "safe_arch" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f398075ce1e6a179b46f51bd88d0598b92b00d3551f1a2d4ac49e771b56ac354" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sctk-adwaita" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "serde" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.195" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "serde_json" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" + +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "siphasher" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ac45299ccbd390721be55b412d41931911f654fa99e2cb8bfb57184b2061fe" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +dependencies = [ + "serde", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "sokoban-rs" +version = "0.1.0" +dependencies = [ + "arboard", + "bevy", + "bevy_editor_pls", + "bitflags 2.4.1", + "image", + "nalgebra", + "rusqlite", + "siphasher", + "winit", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags 1.3.2", + "num-traits", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str-buf" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "svg_fmt" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysinfo" +version = "0.29.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "winapi", +] + +[[package]] +name = "taffy" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c2287b6d7f721ada4cddf61ade5e760b2c6207df041cac9bfaa192897362fd3" +dependencies = [ + "arrayvec", + "grid", + "num-traits", + "slotmap", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-core" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c001ee18b7e5e3f62cbf58c7fe220119e68d902bb7443179c0c8aef30090e999" +dependencies = [ + "thiserror-core-impl", +] + +[[package]] +name = "thiserror-core-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "thread_local" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "tiny-skia" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adbfb5d3f3dd57a0e11d12f4f13d4ebbbc1b5c15b7ab0a156d030b21da5f677c" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "toml_edit" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.2.0", +] + +[[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 = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webbrowser" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b2391658b02c27719fc5a0a73d6e696285138e8b12fba9d4baa70451023c71" +dependencies = [ + "core-foundation", + "home", + "jni 0.21.1", + "log", + "ndk-context", + "objc", + "raw-window-handle", + "url", + "web-sys", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "wgpu" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "752e44d3998ef35f71830dd1ad3da513e628e2e4d4aedb0ab580f850827a0b41" +dependencies = [ + "arrayvec", + "cfg-if", + "js-sys", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8a44dd301a30ceeed3c27d8c0090433d3da04d7b2a4042738095a424d12ae7" +dependencies = [ + "arrayvec", + "bit-vec", + "bitflags 2.4.1", + "codespan-reporting", + "log", + "naga", + "parking_lot", + "profiling", + "raw-window-handle", + "rustc-hash", + "smallvec", + "thiserror", + "web-sys", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a80bf0e3c77399bb52850cb0830af9bad073d5cfcb9dd8253bef8125c42db17" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags 2.4.1", + "block", + "core-graphics-types", + "d3d12", + "glow", + "gpu-alloc", + "gpu-allocator", + "gpu-descriptor", + "hassle-rs", + "js-sys", + "khronos-egl", + "libc", + "libloading 0.8.1", + "log", + "metal", + "naga", + "objc", + "parking_lot", + "profiling", + "range-alloc", + "raw-window-handle", + "renderdoc-sys", + "rustc-hash", + "smallvec", + "thiserror", + "wasm-bindgen", + "web-sys", + "wgpu-types", + "winapi", +] + +[[package]] +name = "wgpu-types" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee64d7398d0c2f9ca48922c902ef69c42d000c759f3db41e355f4a570b052b67" +dependencies = [ + "bitflags 2.4.1", + "js-sys", + "web-sys", +] + +[[package]] +name = "wide" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68938b57b33da363195412cfc5fc37c9ed49aa9cfe2156fde64b8d2c9498242" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "widestring" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-wsapoll" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e745dab35a0c4c77aa3ce42d595e13d2003d6902d6b08c9ef5fc326d08da12b" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-targets 0.48.5", +] + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-implement" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2ee588991b9e7e6c8338edf3333fbe4da35dc72092643958ebb43f0ab2c49c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-interface" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6fb8df20c9bcaa8ad6ab513f7b40104840c8867d5751126e4df3b08388d0cc7" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "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.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "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 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "winit" +version = "0.28.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9596d90b45384f5281384ab204224876e8e8bf7d58366d9b795ad99aa9894b94" +dependencies = [ + "android-activity", + "bitflags 1.3.2", + "cfg_aliases", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle", + "redox_syscall 0.3.5", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-commons", + "wayland-protocols", + "wayland-scanner", + "web-sys", + "windows-sys 0.45.0", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.5.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cf47b659b318dccbd69cc4797a39ae128f533dce7902a1096044d1967b9c16" +dependencies = [ + "memchr", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1641b26d4dec61337c35a1b1aaf9e3cba8f46f0b43636c609ab0291a648040a" +dependencies = [ + "gethostname", + "nix 0.26.4", + "winapi", + "winapi-wsapoll", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82d6c3f9a0fb6701fab8f6cea9b0c0bd5d6876f1f89f7fada07e558077c344bc" +dependencies = [ + "nix 0.26.4", +] + +[[package]] +name = "xcursor" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a0ccd7b4a5345edfcd0c3535718a4e9ff7798ffc536bb5b5a0e26ff84732911" + +[[package]] +name = "xi-unicode" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a" + +[[package]] +name = "xml-rs" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9a8b21b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "sokoban-rs" +version = "0.1.0" +edition = "2021" + +authors = ["ShenMian "] +license = "MIT OR Apache-2.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["dep:bevy", "dep:bevy_editor_pls"] + +[dependencies] +bitflags = "2.4" +nalgebra = "0.32" + +bevy = { version = "0.12", optional = true } +bevy_editor_pls = { version = "0.6", optional = true } +rusqlite = { version = "0.30", features = ["bundled"] } +arboard = "3.3" +siphasher = "1.0" + +image = "0.24" +winit = "0.28" + +[profile.dev.package."*"] +opt-level = 3 diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..e000d82 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 ShenMian + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..f896e9b --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 ShenMian + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..61c80d3 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# sokoban-rs + +A sokoban with a solver. + +

+ +## Features + +- Full mouse control. +- Integrated solver. +- Front-end and back-end separation. +- Levels and solutions are stored in the database. + +## Keymap + +### Mouse + +| Key | Action | +|---------------------------------------|------------------------------------------| +| Click `Left` on player reachable area | Move the character to this position | +| Click `Left` on a crate | Show the reachable area of the crate | +| Click `Left` on crate reachable area | Push the selected crate to this position | +| Hold `Right` and drag | Drag the board | +| Click `Button 4` | Undo last push | +| Scroll `Middle` | Zoom in/out | + +### Keyboard + +| Key | Action | +|-----------------|-----------------------------------| +| `W`/`A`/`S`/`D` | Move the character | +| Arrow keys | Move the character | +| `[`/`]` | Switch to the previous/next level | +| `Ctrl` + `Z` | Undo last push | +| `-`/`=` | Zoom in/out | +| `Ctrl` + `V` | Import levels from clipboard | +| `Ctrl` + `C` | Export level to clipboard | +| `P` | Automatic solution | +| `I` | Switch instant move[^1] | + +### Keyboard (Vim) + +| Key | Action | +|-----------------|-----------------------------------| +| `H`/`J`/`K`/`L` | Move the character | +| `[`/`]` | Switch to the previous/next level | +| `U` | Undo last push | + +### Controller + +| Key | Action | +|-------------------|-----------------------------------| +| D-Pad | Move the character | +| `LB`[^2]/`RB`[^3] | Switch to the previous/next level | +| `B` / `Circle` | Undo last push | + +[^1]: Turn off character and crates movement animations. +[^2]: Left Bumper. +[^3]: Right Bumper. diff --git a/assets/levels/box_world.xsb b/assets/levels/box_world.xsb new file mode 100644 index 0000000..df9f09e --- /dev/null +++ b/assets/levels/box_world.xsb @@ -0,0 +1,1518 @@ +Author: Thinking Rabbit +Comment: +Box World levels extracted by Box World Extractor +http://members.lycos.nl/sokoban/ + +NOTE: This is extracted from the corrected version of boxworld. + + + +In the original version: +-- level 26 and level 54 are identical. +-- level 61 is missing a box + +In the correcte version: +-- level 54 is new +-- level 61 is no longer missing the box. +Comment-End: + +;Level 1 +__###___ +__#.#___ +__#-#### +###$-$.# +#.-$@### +####$#__ +___#.#__ +___###__ +Title: Boxworld 1 +Author: Thinking Rabbit + + +;Level 2 +#####____ +#@--#____ +#-$$#_### +#-$-#_#.# +###-###.# +_##----.# +_#---#--# +_#---#### +_#####___ +Title: Boxworld 2 +Author: Thinking Rabbit + + +;Level 3 +_#######__ +_#-----### +##$###---# +#-@-$--$-# +#-..#-$-## +##..#---#_ +_########_ +Title: Boxworld 3 +Author: Thinking Rabbit + + +;Level 4 +_####_ +##--#_ +#@$-#_ +##$-## +##-$-# +#.$--# +#..*.# +###### +Title: Boxworld 4 +Author: Thinking Rabbit + + +;Level 5 +_####___ +_#@-###_ +_#-$--#_ +###-#-## +#.#-#--# +#.$--#-# +#.---$-# +######## +Title: Boxworld 5 +Author: Thinking Rabbit + + +;Level 6 +___#######___ +####-----#___ +#---.###-#___ +#-#-#----##__ +#-#-$-$#.-#__ +#-#--*--#-#__ +#-.#$-$-#-#__ +##----#-#-### +_#-###.----@# +_#-----##---# +_############ +Title: Boxworld 6 +Author: Thinking Rabbit + + +;Level 7 +___####### +__##--#-@# +__#---#--# +__#$-$-$-# +__#-$##--# +###-$-#-## +#.....--#_ +#########_ +Title: Boxworld 7 +Author: Thinking Rabbit + + +;Level 8 +___######_ +_###----#_ +##.-$##-## +#..$-$--@# +#..-$-$-## +######--#_ +_____####_ +Title: Boxworld 8 +Author: Thinking Rabbit + + +;Level 9 +_#########_ +_#--##---#_ +_#---$---#_ +_#$-###-$#_ +_#-#...#-#_ +##-#...#-## +#-$--$--$-# +#-----#-@-# +########### +Title: Boxworld 9 +Author: Thinking Rabbit + + +;Level 10 +__###### +__#----# +###$$$-# +#@-$..-# +#-$...## +####--#_ +___####_ +Title: Boxworld 10 +Author: Thinking Rabbit + + +;Level 11 +_####__##### +##--#__#---# +#-$-####$--# +#--$....-$-# +##----#-@-## +_##########_ +Title: Boxworld 11 +Author: Thinking Rabbit + + +;Level 12 +__#####_ +###--@#_ +#--$.-## +#--.$.-# +###-*$-# +__#---## +__#####_ +Title: Boxworld 12 +Author: Thinking Rabbit + + +;Level 13 +__####__ +__#..#__ +_##-.##_ +_#--$.#_ +##-$--## +#--#$$-# +#--@---# +######## +Title: Boxworld 13 +Author: Thinking Rabbit + + +;Level 14 +######## +#--#---# +#-$..$-# +#@$.*-## +#-$..$-# +#--#---# +######## +Title: Boxworld 14 +Author: Thinking Rabbit + + +;Level 15 +_######_ +##----## +#-$-$$-# +#......# +#-$$-$-# +###-@### +__####__ +Title: Boxworld 15 +Author: Thinking Rabbit + + +;Level 16 +__######__ +__#----### +__#-$----# +###-$-##-# +#...-$---# +#...$#$-## +####-#-$-# +___#--@--# +___####### +Title: Boxworld 16 +Author: Thinking Rabbit + + +;Level 17 +######___ +#----#___ +#-$$$##__ +#--#..### +##--..$-# +_#-@----# +_######## +Title: Boxworld 17 +Author: Thinking Rabbit + + +;Level 18 +__######## +__#---#.-# +_##--$...# +_#--$-#*.# +##-##$#-## +#---$--$-# +#---#----# +#######@-# +______#### +Title: Boxworld 18 +Author: Thinking Rabbit + + +;Level 19 +_#######__ +_#....-#__ +###...$### +#--$#$-$-# +#-$$--#$-# +#----#---# +####-@-### +___#####__ +Title: Boxworld 19 +Author: Thinking Rabbit + + +;Level 20 +####### +#..$..# +#..#..# +#-$$$-# +#--$--# +#-$$$-# +#--#@-# +####### +Title: Boxworld 20 +Author: Thinking Rabbit + + +;Level 21 +___######__ +___#-...#__ +####....#__ +#--###$-### +#-$-$--$$-# +#@-$-$----# +#---###---# +#####_##### +Title: Boxworld 21 +Author: Thinking Rabbit + + +;Level 22 +########_ +#------#_ +#-#$$--#_ +#-...#-#_ +##...$-## +_#-##-$-# +_#$--$--# +_#--#--@# +_######## +Title: Boxworld 22 +Author: Thinking Rabbit + + +;Level 23 +__#####___ +###---#### +#---$-$--# +#-$---$-@# +###$$##### +__#--..#__ +__#....#__ +__######__ +Title: Boxworld 23 +Author: Thinking Rabbit + + +;Level 24 +######___##### +#----###_#--.# +#--$-$-#_#...# +#-#--$-###--.# +#--$$$---$-@.# +###--$--$#--.# +__#--$#$-#...# +__##-----#--.# +___########### +Title: Boxworld 24 +Author: Thinking Rabbit + + +;Level 25 +_____###### +_#####.---# +_#--#..##-# +_#--$..---# +_#--#-.#-## +###-##$#--# +#-$----$$-# +#-#$#--#--# +#@--####### +#####______ +Title: Boxworld 25 +Author: Thinking Rabbit + + +;Level 26 +_#########___ +_#---##--#### +_#-$--------# +_##$###-##--# +_#--##-*-#-## +_#-$......-#_ +##-###-.-#-#_ +#-----$###$#_ +#---#----$@#_ +#####$#-####_ +____#---#____ +____#####____ +Title: Boxworld 26 +Author: Thinking Rabbit + + +;Level 27 +______######### +______#-------# +______#-#-#-#-# +______#--$-$#-# +#######---$---# +#..#--##-$-$#-# +#..---##-$-$--# +#..#--##-###### +#..#-#-$-$-#___ +#..-----$--#___ +#--###-@-###___ +####_#####_____ +Title: Boxworld 27 +Author: Thinking Rabbit + + +;Level 28 +____####________ +#####--#________ +#--$-$-#_####### +#---$--#_#*.*.*# +##-$-$-###.*.*.# +_#$-$--#--*.*.*# +_#@$-$----.*.*## +_#$-$--#--*.*.*# +##-$-$-###.*.*.# +#---$--#_#*.*.*# +#--$-$-#_####### +#####--#________ +____####________ +Title: Boxworld 28 +Author: Thinking Rabbit + + +;Level 29 +########_ +#......#_ +#--$-#-## +#-$-#-$-# +##$-$-$-# +_#--@---# +_######## +Title: Boxworld 29 +Author: Thinking Rabbit + + +;Level 30 +__########## +###---.----# +#---##$##--# +#-@$.-.-.$## +##-$##$##-#_ +_#----.---#_ +_##########_ +Title: Boxworld 30 +Author: Thinking Rabbit + + +;Level 31 +___###### +####.--@# +#--$$$--# +#.##.##.# +#---$---# +#--$.#-## +####---#_ +___#####_ +Title: Boxworld 31 +Author: Thinking Rabbit + + +;Level 32 +_######_ +_#.-..#_ +_#.-$.#_ +###--$## +#-$--$-# +#-#$##-# +#---@--# +######## +Title: Boxworld 32 +Author: Thinking Rabbit + + +;Level 33 +____######____ +__###----###__ +__#---#$---### +__#---$---$$-# +__#-$$-#$----# +__##---$---$-# +######-#$##### +#..@-#$--#____ +#.#..--$##____ +#....$#-#_____ +#....---#_____ +#########_____ +Title: Boxworld 33 +Author: Thinking Rabbit + + +;Level 34 +############### +#------#------# +#-$-#$-#-$##$-# +#-#--$-#------# +#---##$#$##$$-# +#-#-#-...-#---# +#-$--.-#-.$-#-# +#-$#@$...#-#--# +#----.-#-.--$-# +#-##.$###$.-#-# +#-#-$.....-##-# +#-------------# +############### +Title: Boxworld 34 +Author: Thinking Rabbit + + +;Level 35 +#########_ +#---##--#_ +#-#-$-$-#_ +#--*.#--#_ +##-#.@.##_ +##$###*### +#--------# +#---##-#-# +######---# +_____##### +Title: Boxworld 35 +Author: Thinking Rabbit + + +;Level 36 +########_______ +#------#_______ +#-$$---###_____ +#--$-$$$-#####_ +##-##-...----## +_#-#@#...###$-# +_#-#-$...-----# +##-#-$...$-#-## +#--#####-###-#_ +#------$---$-#_ +###########--#_ +__________####_ +Title: Boxworld 36 +Author: Thinking Rabbit + + +;Level 37 +___#####_ +___#-@-#_ +___#$$$#_ +####---#_ +#---.#$## +#-$.$.-.# +#--#.#.## +########_ +Title: Boxworld 37 +Author: Thinking Rabbit + + +;Level 38 +############ +#...-#-----# +#..--#-##--# +#..-----#--# +#..--#-$##-# +#...-#$-$--# +######--$$-# +_##--$-$$--# +_#@-$$$--#-# +_##-$-##---# +__#--------# +__########## +Title: Boxworld 38 +Author: Thinking Rabbit + + +;Level 39 +#########__ +#-------#__ +#--$-$-$#__ +##-#$##-#__ +_#-..-..##_ +_##..-..-#_ +__#-##$#-## +__#$-$-$--# +__#------@# +__######### +Title: Boxworld 39 +Author: Thinking Rabbit + + +;Level 40 +#####______#### +#@--########--# +##-$-------$--# +_#-#-#--####--# +_#--$---####$## +_#$-##-#-$-$-#_ +##-$--$#-----#_ +#---#------#-#_ +#---#####$####_ +#####---#---#__ +____#...--$-#__ +____#....#--#__ +____#....####__ +____######_____ +Title: Boxworld 40 +Author: Thinking Rabbit + + +;Level 41 +_____##### +_#####---# +_#-..-$#-# +_#-#.*---# +##-*.#$-## +#-$--$--#_ +#---##-@#_ +#########_ +Title: Boxworld 41 +Author: Thinking Rabbit + + +;Level 42 +#####_####### +#---###--#--# +#-$-----$-@-# +##-#$##.##--# +_#--...*.-$-# +_#-$#-#.#-#-# +_##----$----# +__#--######## +__####_______ +Title: Boxworld 42 +Author: Thinking Rabbit + + +;Level 43 +_________###___ +____######@##__ +____#....#$-##_ +____#....#-$-#_ +____#....-$--#_ +____#-...#---#_ +######-#####-## +#-$-$---$--#--# +#----$$---$-$-# +###-$-$-$--#### +__##---$-$-#___ +___#--######___ +___####________ +Title: Boxworld 43 +Author: Thinking Rabbit + + +;Level 44 +___#####___ +_###---###_ +##--@$-$-#_ +#--##-##-## +#-$.#.$---# +#-#.#*#---# +#-$...--### +###$#-###__ +__#---#____ +__#####____ +Title: Boxworld 44 +Author: Thinking Rabbit + + +;Level 45 +______####_____ +______#--###### +______#----#--# +______#-$$----# +#######$#--#--# +#--#.-..-###$## +#--#.#*.$-----# +#--#.#.*#-#---# +#-$$....#-##### +#-@$-#-##-#____ +#-$$$#----#____ +#----######____ +######_________ +Title: Boxworld 45 +Author: Thinking Rabbit + + +;Level 46 +####___________ +#--###_________ +#-$--###_______ +#-$-$--###_____ +#-$-$-$--###___ +#-$-$-$----#___ +#-$-$--#---##__ +#-$--##-$$$-#__ +#@-####-----##_ +##-#_#.$$$$$.#_ +_#-###.......## +_#---.*******.# +_####.........# +____########### +Title: Boxworld 46 +Author: Thinking Rabbit + + +;Level 47 +#######____ +#-@#--##### +#-$$--$---# +#--#.##$#-# +##$#...---# +##-...##$## +#--##.##--# +#--$--$---# +#--#---#--# +########### +Title: Boxworld 47 +Author: Thinking Rabbit + + +;Level 48 +___#####___ +___#-@-#___ +___#-$-#___ +___#$.$#___ +_###.$.##__ +##-.$.$.### +#--$.$.$--# +#----.----# +########### +Title: Boxworld 48 +Author: Thinking Rabbit + + +;Level 49 +############# +#--$-$-$.*..# +#-$-$-$-*...# +#--$-$-$.*..# +#-$-$-$-*...# +#--$-$-$.*..# +#-$-$-$-*...# +#--$-$-$.*..# +#-$-$-$-*...# +#--$-$-$.*..# +#@$-$-$-*...# +############# +Title: Boxworld 49 +Author: Thinking Rabbit + + +;Level 50 +_____________#_ +____________##_ +___________###_ +__________#---# +___########-#-# +__#-$-$-$-$---# +_##-#.#.#.#@$#_ +###.......---## +_##-#-#-#-#$##_ +__#-$-$-$-$---# +___########-#-# +__________#---# +___________###_ +____________##_ +_____________#_ +Title: Boxworld 50 +Author: Thinking Rabbit + + +;Level 51 +#######__ +#--.$-### +#-.$.$--# +#*$.$.@-# +#-.$.$-## +#--.$--#_ +########_ +Title: Boxworld 51 +Author: Thinking Rabbit + + +;Level 52 +_________#####__ +_________#---#__ +##########-*-### +#----------.---# +#-$$$$****$...@# +#----------.---# +##########-*-### +_________#---#__ +_________#####__ +Title: Boxworld 52 +Author: Thinking Rabbit + + +;Level 53 +_____####____ +######--##### +#@$----$--$-# +#$###-$-#-#-# +#--#--#-$---# +#-$#----#-### +#--$-#$#---#_ +#.........-#_ +########---#_ +_______#####_ +Title: Boxworld 53 +Author: Thinking Rabbit + + +;Level 54 +________####___ +__#######--#### +__#----$--....# +###---#-#-..#.# +#-$$-#--#-....# +#----#-$#-..#.# +##$###$--###--# +_#--#--$-$----# +_#----$-$--#$-# +_#-$##-$-##--## +_##-####-#-@##_ +__#---#--$-##__ +__###-----##___ +____#######____ +Title: Boxworld 54 +Author: Thinking Rabbit + + +;Level 55 +########### +#----#----# +#-$@$$$$$-# +#---------# +#####-##### +___#.--#___ +___#.--#___ +___#...#___ +___#.--#___ +___#####___ +Title: Boxworld 55 +Author: Thinking Rabbit + + +;Level 56 +__#####__ +__#-@-#__ +__#-$-#__ +###-.-### +#---*---# +#-*****-# +#---*---# +###$*$### +__#-.-#__ +__#-*-#__ +__#-.-#__ +__#####__ +Title: Boxworld 56 +Author: Thinking Rabbit + + +;Level 57 +############## +#.-----------# +#.$-$-$-$-$--# +#.#########--# +#.#.*-$-..$*## +#.#-$-$-*.$@#_ +#.#.--$-..$$#_ +#.#########.#_ +#.----------#_ +#.#$#$#$#$#$#_ +#.----------#_ +#############_ +Title: Boxworld 57 +Author: Thinking Rabbit + + +;Level 58 +############__ +#..--#-----### +#..--#-$--$--# +#..--#$####--# +#..----@-##--# +#..--#-#--$-## +######-##$-$-# +__#-$--$-$-$-# +__#----#-----# +__############ +Title: Boxworld 58 +Author: Thinking Rabbit + + +;Level 59 +_______####___ +########--#### +#---##.....--# +#--$--##...#-# +##--$--###-#-# +_#-#-$--#----# +_#--#-$--#---# +_#---#-$--#--# +_#----#-$-#-## +_####--#-$--#_ +____##--#-$-#_ +_____##@#---#_ +______#######_ +Title: Boxworld 59 +Author: Thinking Rabbit + + +;Level 60 +####______#### +#..########..# +#*.*.....*.*.# +#-$-$-$-$-$-$# +#$-$-$@$-$-$-# +#-$-$-$-$-$-$# +#$-$-$-$-$-$-# +#.*.*.....*.*# +#..########..# +####______#### +Title: Boxworld 60 +Author: Thinking Rabbit + + +;Level 61 +____#####___ +___##---#### +___#-..*-$-# +####-#.#---# +#----.*.#@## +#-#$##$##-#_ +#-----$-$-#_ +##--#---###_ +_########___ +Title: Boxworld 61 +Author: Thinking Rabbit + + +;Level 62 +######___ +#----#___ +#-$--#### +#-$*..*-# +#-*..*$-# +####--$-# +___#-@--# +___###### +Title: Boxworld 62 +Author: Thinking Rabbit + + +;Level 63 +___#####_ +####.--## +#-$.$.--# +#@$#-#$-# +#-$.-.--# +####$#$-# +__#.-.--# +__####### +Title: Boxworld 63 +Author: Thinking Rabbit + + +;Level 64 +############ +#----...-$-# +#-$$$***-$@# +#----...-$-# +############ +Title: Boxworld 64 +Author: Thinking Rabbit + + +;Level 65 +_######### +##-------# +#---#$#$-# +#-$$--.$.# +#-@###...# +####_##### +Title: Boxworld 65 +Author: Thinking Rabbit + + +;Level 66 +_####_____ +_#--#####_ +##$-##--#_ +#--$@$--#_ +#---##$-#_ +###.##-### +_#...$-$-# +_##..----# +__######## +Title: Boxworld 66 +Author: Thinking Rabbit + + +;Level 67 +#####_####______ +#...#_#--####___ +#...###--$--#___ +#....##-$--$###_ +##....##---$--#_ +###...-##-$-$-#_ +#-##----#--$--#_ +#--##-#-###-#### +#-$-#-#$--$----# +#--$-@-$----$--# +#---#-$-$$-$-### +#--######--###__ +#-##____####____ +###_____________ +Title: Boxworld 67 +Author: Thinking Rabbit + + +;Level 68 +####### +#.-.-.# +#-$$$-# +#.$@$.# +#-$$$-# +#.-.-.# +####### +Title: Boxworld 68 +Author: Thinking Rabbit + + +;Level 69 +______####_ +#######--#_ +#-----$--#_ +#---$##@$#_ +##$#...#-#_ +_#-$...--#_ +_#-#.-.#-## +_#---#-#$-# +_#$--$----# +_#--####### +_####______ +Title: Boxworld 69 +Author: Thinking Rabbit + + +;Level 70 +___######### +___#---#---# +___#-------# +#####*###-## +#---...---#_ +#-#-#*###$## +#-$----$---# +#####@-#---# +____######## +Title: Boxworld 70 +Author: Thinking Rabbit + + +;Level 71 +#####___________ +#...#_#####_____ +#...###---#_____ +#....---$$#####_ +#....--#--#---## +#..#$####-#$#--# +##-$--#-----$$-# +#--$#-@-$-$$#--# +#-$-$-$-#---$-## +#---#--$-##---#_ +######---######_ +_____#####______ +Title: Boxworld 71 +Author: Thinking Rabbit + + +;Level 72 +__##########___ +_##.#------#### +##..#-$--$-#--# +#...#-##-$-#--# +#.....--#$$---# +##....$----#$-# +####-#######--# +#---$--------## +#--$-#--$#-$-## +#-$###-$-#-$$-# +#---@#--##----# +############### +Title: Boxworld 72 +Author: Thinking Rabbit + + +;Level 73 +_______######## +_______#--#---# +_#######-$$...# +_#--------#...# +_#-######$#...# +##-#------#...# +#--#-#$-$-##### +#-#-$-$-$-#____ +#-@--$-#--#____ +#####$-$$-#____ +____#-----#____ +____#######____ +Title: Boxworld 73 +Author: Thinking Rabbit + + +;Level 74 +_______##### +__######---# +###----.-$-# +#-$--#$.#$## +#--#--@.#--# +##-####.---# +_#-$--#*#### +_#-##-#.--#_ +_#-----.#-#_ +_###$-----#_ +___#--#####_ +___####_____ +Title: Boxworld 74 +Author: Thinking Rabbit + + +;Level 75 +____####____ +#####--##### +#----$---$-# +#--$#$##---# +###-#.*.-### +__#-...-@#__ +__##-#$###__ +___#---#____ +___#####____ +Title: Boxworld 75 +Author: Thinking Rabbit + + +;Level 76 +___######## +####----.-# +#--$-$-$.-# +#--.####.## +#-$.$-$-@#_ +#--.--####_ +#######____ +Title: Boxworld 76 +Author: Thinking Rabbit + + +;Level 77 +__#####____ +__#---#____ +###$.$##### +#---.-$---# +#-##$##-@-# +#---.-##### +###-.-#____ +__#---#____ +__#####____ +Title: Boxworld 77 +Author: Thinking Rabbit + + +;Level 78 +_#####_____ +_#-@-###### +_#-#..*---# +_#-...#---# +##$##-$-$-# +#---#$##### +#---$---#__ +#####-#-#__ +____#---#__ +____#####__ +Title: Boxworld 78 +Author: Thinking Rabbit + + +;Level 79 +_____######___ +___###----##__ +___#---##--#__ +_###$##--#-#__ +##-----..#-#__ +#--$#$#*.#-#__ +#-$$@-#.*#-### +#--$$-#..#---# +##----#..$---# +_###$##.-#-### +___#--###--#__ +___##-----##__ +____#######___ +Title: Boxworld 79 +Author: Thinking Rabbit + + +;Level 80 +_____#######_ +_____#--#--#_ +_____#--$$-#_ +######-$#--#_ +#...###-#--## +#.--#--$-#--# +#.----$-$-$-# +#.--#--$-#--# +#...###-#--## +######-$---#_ +_____#@-#--#_ +_____#######_ +Title: Boxworld 80 +Author: Thinking Rabbit + + +;Level 81 +__________###### +__________#----# +#####___###-##-# +#..-#####-$--#-# +#..-----$---$#-# +#..--##-##---#-# +#..-##-$-#$-$#-# +#..-#-----$--#-# +#..-#--$-###$--# +#..-#-$-$--$-### +###-##-#-$----#_ +__#----#@##-$-#_ +__#########--##_ +__________####__ +Title: Boxworld 81 +Author: Thinking Rabbit + + +;Level 82 +_______#_______ +_____#####_____ +___###-@-###___ +___#--$-$--#___ +___#-*.*.*-#___ +__##-.$-$.-##__ +_###-*.*.*-###_ +####--$-$--#### +___######--#___ +____##___##____ +Title: Boxworld 82 +Author: Thinking Rabbit + + +;Level 83 +_##############_ +_#-@-*-*-*-#--## +_#$#--*-*--#---# +_#-#-*-*-*-----# +_#-#--*-*--##-## +_#-#-*-*-*-##-#_ +_#-#--*-*--##-#_ +_#-#-*-*-*-##-#_ +_#-#--*-*--##-#_ +_#-#-*-.-*-##-## +##-##########--# +#--------------# +#---#########--# +#####_______#### +Title: Boxworld 83 +Author: Thinking Rabbit + + +;Level 84 +_#############_ +_#----#--##--#_ +_#$$$-#-$$--$## +_#-$--#--....-# +_#--$--#$.##.-# +_#--#-$#-....## +##$-$--#$.##.-# +#-$--$-@$....-# +#---###--###### +#####_####_____ +Title: Boxworld 84 +Author: Thinking Rabbit + + +;Level 85 +_____######### +_____#-------# +_____#--$#$#-# +_######--#-$-# +_#---#-$--$--# +##-$-----###-# +#---#$####---# +#----$-###-### +#####..-@#-##_ +___#...$-$$-#_ +___#...#----#_ +___#...######_ +___#####______ +Title: Boxworld 85 +Author: Thinking Rabbit + + +;Level 86 +####_____####___ +#--#######..###_ +#-$-$-$--#....#_ +#-$---$$-#***.#_ +#-$-$-$--#..*.#_ +#--$-$-$-#*.*.#_ +##-$-$-$-.*.*.## +#--$-$-$-.*.*.@# +#--$-$-$-#*.*.## +#-$-$-$--#..*.#_ +#-$---$$-#***.#_ +#-$-$-$--#....#_ +#--#######..###_ +####_____####___ +Title: Boxworld 86 +Author: Thinking Rabbit + + +;Level 87 +______#####_____ +#######---#_____ +#---##.---#_____ +#--$#..--###____ +##--...#$$-##### +_#-$.#.$-------# +_#-$###$##-#-$-# +_#---#-----$$#-# +_##$$#-##$#$---# +_#...-$@-$---### +_#...#$#---###__ +_#...--#####____ +_#######________ +Title: Boxworld 87 +Author: Thinking Rabbit + + +;Level 88 +_####__###### +_#--####----# +##*---*-**--# +#-$-*----*#-# +#-.---###---# +######---#@## +#-*-.-*--**-# +#---#---#---# +##*---*-#$#-# +_#--#####---# +_####___##### +Title: Boxworld 88 +Author: Thinking Rabbit + + +;Level 89 +_#########_ +_#---#---#_ +_#-$$$$$-#_ +##-$-$-$-#_ +#-$--@---#_ +#-$-####-## +#--#.....-# +##--.....-# +_########## +Title: Boxworld 89 +Author: Thinking Rabbit + + +;Level 90 +___#######_____ +___#-----#_____ +___#-$-$-##____ +___#####..##### +######..*.--$-# +#--$@$....#$$-# +#---$-#$###---# +#####-------### +____###--####__ +______#--#_____ +______####_____ +Title: Boxworld 90 +Author: Thinking Rabbit + + +;Level 91 +#####_____ +#---###### +#-$-..-$-# +##$-..$$@# +_#--..-$-# +_######### +Title: Boxworld 91 +Author: Thinking Rabbit + + +;Level 92 +__#####_____ +__#---###### +###$#.-----# +#-$-...#-$-# +#@-$.#*$---# +####----#### +___######___ +Title: Boxworld 92 +Author: Thinking Rabbit + + +;Level 93 +____#####______ +#####---#______ +#---$-@-#______ +#--$-#.#####___ +##$-##.##--#### +_#--.....-$#--# +_#-$##.##--#$-# +_#---#.##-----# +_###-$-#####-## +___#-#$-----$-# +___#----###---# +___######_##### +Title: Boxworld 93 +Author: Thinking Rabbit + + +;Level 94 +####### +#--*--# +#-@.$-# +#-$.--# +#*.***# +#--*--# +#-$*$-# +#--.--# +####### +Title: Boxworld 94 +Author: Thinking Rabbit + + +;Level 95 +__#########_____ +__#----#--#_____ +###-#$--$-####__ +#--$--##..#--### +#-#--$-#..$-$--# +#-$$--$#..--#--# +##--#--...#$---# +_#$-@-#...#-$--# +_#----#...$---## +_#-##$-###---##_ +_#---$-----###__ +_#--########____ +_####___________ +Title: Boxworld 95 +Author: Thinking Rabbit + + +;Level 96 +___##########_ +####......--#_ +#---.....#--#_ +#--#......-##_ +##-####$##$#__ +#@$--$-$---### +#-$$----##---# +#-#--$$##--#-# +#---$--#-$$--# +#--$--$---#$-# +####---#-$-$-# +___#---#-----# +___########### +Title: Boxworld 96 +Author: Thinking Rabbit + + +;Level 97 +______######### +______#-------# +______#$-$$$--# +______#---#-$-# +______#---$@$-# +____###$-$-#-## +____#---$#$#-#_ +#####-#--#---#_ +#...--#-$#-###_ +#.....---#-#___ +#.....#-$#-#___ +########---#___ +_______#####___ +Title: Boxworld 97 +Author: Thinking Rabbit + + +;Level 98 +_#####_######_ +_#---###----#_ +##-$-$-#$-#$#_ +#--$-@-$--$-## +#-#--##-#....# +#--##-$-#.##.# +##--$----....# +_#-$$-#$#....# +_#---#---#$-## +_#####-$----#_ +_____####--##_ +________####__ +Title: Boxworld 98 +Author: Thinking Rabbit + + +;Level 99 +####___________ +#--#___________ +#--##########__ +#----##-----#__ +#..#----$$#-#__ +#..--##---$-### +#..#--##$#-$--# +#..---#-@$-$--# +#..#--#-$-$---# +#-.---#-$-$-### +#--#--#---###__ +#--#----###____ +#########______ +Title: Boxworld 99 +Author: Thinking Rabbit + + +;Level 100 +_________#####__ +________#-----#_ +_______#--$##--# +______#--#---$-# +#__#__#-#--#-#-# +_#_#__#-$-$--$-# +__#######-#$#--# +__#--$-$------#_ +__#@..$-**.###__ +___#......#####_ +____############ +Title: Boxworld 100 +Author: Thinking Rabbit diff --git a/assets/textures/crate.png b/assets/textures/crate.png new file mode 100644 index 0000000..bb52e80 Binary files /dev/null and b/assets/textures/crate.png differ diff --git a/assets/textures/keymap/keyboard.png b/assets/textures/keymap/keyboard.png new file mode 100644 index 0000000..da9dd18 Binary files /dev/null and b/assets/textures/keymap/keyboard.png differ diff --git a/assets/textures/keymap/playstation.png b/assets/textures/keymap/playstation.png new file mode 100644 index 0000000..895625a Binary files /dev/null and b/assets/textures/keymap/playstation.png differ diff --git a/assets/textures/keymap/switch.png b/assets/textures/keymap/switch.png new file mode 100644 index 0000000..de6fbcc Binary files /dev/null and b/assets/textures/keymap/switch.png differ diff --git a/assets/textures/keymap/xbox.png b/assets/textures/keymap/xbox.png new file mode 100644 index 0000000..49dcd12 Binary files /dev/null and b/assets/textures/keymap/xbox.png differ diff --git a/assets/textures/spritesheet-full.png b/assets/textures/spritesheet-full.png new file mode 100644 index 0000000..b1833fa Binary files /dev/null and b/assets/textures/spritesheet-full.png differ diff --git a/assets/textures/spritesheet.kra b/assets/textures/spritesheet.kra new file mode 100644 index 0000000..3602209 Binary files /dev/null and b/assets/textures/spritesheet.kra differ diff --git a/assets/textures/spritesheet.png b/assets/textures/spritesheet.png new file mode 100644 index 0000000..9188b65 Binary files /dev/null and b/assets/textures/spritesheet.png differ diff --git a/docs/screenshot.png b/docs/screenshot.png new file mode 100644 index 0000000..79c298f Binary files /dev/null and b/docs/screenshot.png differ diff --git a/src/board.rs b/src/board.rs new file mode 100644 index 0000000..7fbe477 --- /dev/null +++ b/src/board.rs @@ -0,0 +1,103 @@ +use crate::direction::Direction; +use crate::level::Level; +use crate::movement::Movement; +use crate::Tile; + +use nalgebra::Vector2; + +#[derive(Clone)] +pub struct Board { + pub level: Level, + pub movements: Vec, +} + +impl Board { + pub fn move_or_push(&mut self, direction: Direction) -> bool { + let direction_vector = direction.to_vector(); + let player_next_position = self.level.player_position + direction_vector; + if self + .level + .get_unchecked(&player_next_position) + .intersects(Tile::Wall) + { + return false; + } + if self + .level + .get_unchecked(&player_next_position) + .intersects(Tile::Crate) + { + let crate_next_position = player_next_position + direction_vector; + if self + .level + .get_unchecked(&crate_next_position) + .intersects(Tile::Wall | Tile::Crate) + { + return false; + } + self.move_crate(player_next_position, crate_next_position); + + self.movements.push(Movement::with_push(direction)); + } else { + self.movements.push(Movement::with_move(direction)); + } + self.move_player(player_next_position); + return true; + } + + pub fn undo_push(&mut self) { + while let Some(history) = self.movements.last() { + if history.is_push { + self.undo_move(); + return; + } + self.undo_move(); + } + } + + fn undo_move(&mut self) { + debug_assert!(!self.movements.is_empty()); + let history = self.movements.pop().unwrap(); + let direction = history.direction; + if history.is_push { + let crate_position = self.level.player_position + direction.to_vector(); + self.move_crate(crate_position, self.level.player_position.clone()); + } + let player_prev_position = self.level.player_position - direction.to_vector(); + self.move_player(player_prev_position); + } + + pub fn is_solved(&self) -> bool { + return self.level.crate_positions == self.level.target_positions; + } + + pub fn move_count(&self) -> usize { + self.movements.len() + } + + pub fn push_count(&self) -> usize { + self.movements.iter().filter(|x| x.is_push).count() + } + + pub fn export_movements(&self) -> String { + self.movements + .iter() + .map(|x| Into::::into(x.clone())) + .collect() + } + + fn move_player(&mut self, to: Vector2) { + self.level + .get_unchecked_mut(&self.level.player_position.clone()) + .remove(Tile::Player); + self.level.get_unchecked_mut(&to).insert(Tile::Player); + self.level.player_position = to; + } + + fn move_crate(&mut self, from: Vector2, to: Vector2) { + self.level.get_unchecked_mut(&from).remove(Tile::Crate); + self.level.get_unchecked_mut(&to).insert(Tile::Crate); + self.level.crate_positions.remove(&from); + self.level.crate_positions.insert(to); + } +} diff --git a/src/components.rs b/src/components.rs new file mode 100644 index 0000000..ab0c08b --- /dev/null +++ b/src/components.rs @@ -0,0 +1,36 @@ +use bevy::prelude::*; +use nalgebra::Vector2; + +use crate::board; + +#[derive(Component)] +pub struct MainCamera { + pub target_scale: f32, +} + +impl Default for MainCamera { + fn default() -> Self { + Self { target_scale: 1.0 } + } +} + +#[derive(Component)] +pub struct HUD; + +#[derive(Component)] +pub struct Board { + pub board: board::Board, + pub tile_size: Vector2, +} + +#[derive(Component)] +pub struct Player; + +#[derive(Component)] +pub struct Crate; + +#[derive(Component)] +pub struct ReachableMark; + +#[derive(Component, Deref)] +pub struct GridPosition(pub Vector2); diff --git a/src/database.rs b/src/database.rs new file mode 100644 index 0000000..e185b4d --- /dev/null +++ b/src/database.rs @@ -0,0 +1,217 @@ +use nalgebra::Vector2; +use rusqlite::Connection; +use siphasher::sip::SipHasher24; + +use crate::level::Level; +use crate::movement::Movement; + +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +use std::path::Path; + +pub struct Database { + connection: Connection, +} + +impl Database { + pub fn from_memory() -> Self { + Self { + connection: Connection::open_in_memory().expect("failed to open database"), + } + } + + pub fn from_file>(path: P) -> Self { + Self { + connection: Connection::open(path).expect("failed to open database"), + } + } + + /// Initializes the database by creating the necessary tables. + pub fn initialize(&self) { + const CREATE_LEVEL_TABLE: &str = " + CREATE TABLE IF NOT EXISTS tb_level ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + title TEXT, + author TEXT, + map TEXT NOT NULL, + width INTEGER NOT NULL, + height INTEGER NOT NULL, + hash INTEGER NOT NULL UNIQUE, + date DATE NOT NULL + ) + "; + const CREATE_SOLUTION_TABLE: &str = " + CREATE TABLE IF NOT EXISTS tb_solution ( + level_id INTEGER PRIMARY KEY, + best_move_solution TEXT, + best_push_solution TEXT + ) + "; + const CREATE_SESSION_TABLE: &str = " + CREATE TABLE IF NOT EXISTS tb_session ( + level_id INTEGER PRIMARY KEY, + movement TEXT, + datetime DATETIME NOT NULL, + FOREIGN KEY (level_id) REFERENCES tb_level(id) + ) + "; + + self.connection.execute(CREATE_LEVEL_TABLE, ()).unwrap(); + self.connection.execute(CREATE_SOLUTION_TABLE, ()).unwrap(); + self.connection.execute(CREATE_SESSION_TABLE, ()).unwrap(); + } + + /// Imports multiple levels into the database. + pub fn import_levels(&self, levels: &[Level]) { + self.connection.execute("BEGIN TRANSACTION", []).unwrap(); + for level in levels { + self.import_level(level); + } + self.connection.execute("COMMIT", []).unwrap(); + } + + /// Imports a single level into the database. + pub fn import_level(&self, level: &Level) { + let title = level.metadata.get("title"); + let author = level.metadata.get("author"); + + let mut hasher = SipHasher24::new(); + let mut normalized_level = level.clone(); + normalized_level.normalize(); + normalized_level.hash(&mut hasher); + let hash = hasher.finish(); + + let _ = self.connection.execute( + "INSERT INTO tb_level(title, author, map, width, height, hash, date) VALUES (?, ?, ?, ?, ?, ?, DATE('now'))", + (&title, &author, &level.export_map(), level.dimensions.x, level.dimensions.y, hash), + ); + } + + pub fn get_level_id(&self, level: &Level) -> Option { + let mut hasher = SipHasher24::new(); + let mut normalized_level = level.clone(); + normalized_level.normalize(); + normalized_level.hash(&mut hasher); + let hash = hasher.finish(); + + match self + .connection + .query_row("SELECT id FROM tb_level WHERE hash = ?", [hash], |row| { + row.get(0) + }) { + Ok(level_id) => level_id, + Err(_) => None, + } + } + + pub fn get_level_by_id(&self, id: u64) -> Option { + let mut statement = self + .connection + .prepare("SELECT map, width, height, title, author FROM tb_level WHERE id = ?") + .unwrap(); + let mut rows = statement.query([id]).unwrap(); + let row = rows.next().unwrap()?; + + let map = row + .get::<_, String>(0) + .unwrap() + .split('\n') + .map(|x| x.to_string()) + .collect(); + let size = Vector2::new(row.get(1).unwrap(), row.get(2).unwrap()); + let mut metadata = HashMap::new(); + if let Ok(title) = row.get(3) { + metadata.insert("title".to_string(), title); + } + if let Ok(author) = row.get(4) { + metadata.insert("author".to_string(), author); + } + let level = Level::new(map, size, metadata).unwrap(); + Some(level) + } + + pub fn update_solution(&self, level_id: u64, solution: &[Movement]) { + let move_count = solution.len(); + let push_count = solution.iter().filter(|x| x.is_push).count(); + let lurd: String = solution + .iter() + .map(|x| Into::::into(x.clone())) + .collect(); + + if let Some(best_move_count) = self.get_best_move_count(level_id) { + if move_count < best_move_count { + self.connection + .execute( + "UPDATE tb_solution SET best_move_solution = ? WHERE level_id = ?", + (lurd.clone(), level_id), + ) + .unwrap(); + } + } + + if let Some(best_push_count) = self.get_best_push_count(level_id) { + if push_count < best_push_count { + self.connection + .execute( + "UPDATE tb_solution SET best_push_solution = ? WHERE level_id = ?", + (lurd.clone(), level_id), + ) + .unwrap(); + } + } + + let _ = self.connection.execute( + "INSERT INTO tb_solution (level_id, best_move_solution, best_push_solution) VALUES (?, ?, ?)", + (level_id, lurd.clone(), lurd.clone()), + ); + } + + pub fn get_best_move_count(&self, level_id: u64) -> Option { + Some(self.get_best_move_solution(level_id)?.len()) + } + + pub fn get_best_push_count(&self, level_id: u64) -> Option { + Some( + self.get_best_push_solution(level_id)? + .chars() + .filter(|x| x.is_ascii_uppercase()) + .count(), + ) + } + + pub fn get_best_move_solution(&self, level_id: u64) -> Option { + let mut statement = self + .connection + .prepare("SELECT best_move_solution FROM tb_solution WHERE level_id = ?") + .unwrap(); + let mut rows = statement.query([level_id]).unwrap(); + let row = rows.next().unwrap()?; + let best_move: String = row.get(0).unwrap(); + Some(best_move) + } + + pub fn get_best_push_solution(&self, level_id: u64) -> Option { + let mut statement = self + .connection + .prepare("SELECT best_push_solution FROM tb_solution WHERE level_id = ?") + .unwrap(); + let mut rows = statement.query([level_id]).unwrap(); + let row = rows.next().unwrap()?; + let best_push: String = row.get(0).unwrap(); + Some(best_push) + } + + /// Retrieves the maximum level ID. + pub fn max_level_id(&self) -> Option { + self.connection + .query_row("SELECT MAX(id) FROM tb_level", [], |row| row.get(0)) + .unwrap() + } + + /// Retrieves the minimum level ID. + pub fn min_level_id(&self) -> Option { + self.connection + .query_row("SELECT MIN(id) FROM tb_level", [], |row| row.get(0)) + .unwrap() + } +} diff --git a/src/direction.rs b/src/direction.rs new file mode 100644 index 0000000..1eb00a9 --- /dev/null +++ b/src/direction.rs @@ -0,0 +1,48 @@ +use nalgebra::Vector2; +use std::ops::Neg; + +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum Direction { + Up, + Down, + Left, + Right, +} + +impl Neg for Direction { + type Output = Self; + + fn neg(self) -> Self::Output { + match self { + Direction::Up => Direction::Down, + Direction::Down => Direction::Up, + Direction::Left => Direction::Right, + Direction::Right => Direction::Left, + } + } +} + +impl Direction { + pub fn to_vector(&self) -> Vector2 { + match self { + Direction::Up => -Vector2::::y(), + Direction::Down => Vector2::::y(), + Direction::Left => -Vector2::::x(), + Direction::Right => Vector2::::x(), + } + } + + pub fn from_vector(vector: Vector2) -> Option { + if vector == -Vector2::::y() { + Some(Direction::Up) + } else if vector == Vector2::::y() { + Some(Direction::Down) + } else if vector == -Vector2::::x() { + Some(Direction::Left) + } else if vector == Vector2::::x() { + Some(Direction::Right) + } else { + None + } + } +} diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..1bd5958 --- /dev/null +++ b/src/events.rs @@ -0,0 +1,11 @@ +use bevy::prelude::*; +use nalgebra::Vector2; + +#[derive(Event)] +pub struct SelectCrate(pub Vector2); + +#[derive(Event)] +pub struct UnselectCrate; + +#[derive(Event)] +pub struct UpdateGridPositionEvent; diff --git a/src/level.rs b/src/level.rs new file mode 100644 index 0000000..59523b6 --- /dev/null +++ b/src/level.rs @@ -0,0 +1,729 @@ +use bitflags::bitflags; +use nalgebra::Vector2; +use siphasher::sip::SipHasher24; + +use crate::direction::Direction; + +use std::collections::{HashMap, HashSet, VecDeque}; +use std::hash::{Hash, Hasher}; +use std::path::Path; +use std::{fmt, fs}; + +#[derive(Clone, PartialEq, Eq, Hash)] +struct Node { + crate_position: Vector2, + player_position: Vector2, +} + +bitflags! { + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + pub struct Tile: u8 { + const Void = 1 << 0; + const Floor = 1 << 1; + const Wall = 1 << 2; + const Crate = 1 << 3; + const Target = 1 << 4; + const Player = 1 << 5; + + const Deadlock = 1 << 6; + const Tunnel = 1 << 7; + } +} + +#[derive(Clone)] +pub struct Level { + data: Vec, + pub dimensions: Vector2, + pub metadata: HashMap, + + pub player_position: Vector2, + pub crate_positions: HashSet>, + pub target_positions: HashSet>, +} + +impl Hash for Level { + fn hash(&self, state: &mut H) { + self.data.hash(state); + } +} + +#[derive(Debug)] +pub enum ParseMapError { + MoreThanOnePlayer, + NoPlayer, + MismatchBetweenCratesAndTargets, + InvalidCharacter(char), +} + +impl fmt::Display for ParseMapError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ParseMapError::MoreThanOnePlayer => write!(f, "more than one player"), + ParseMapError::NoPlayer => write!(f, "no player"), + ParseMapError::MismatchBetweenCratesAndTargets => { + write!(f, "mismatch between number of crates and targets") + } + ParseMapError::InvalidCharacter(c) => write!(f, "invalid character: {}", c), + } + } +} + +type Result = std::result::Result; + +impl Level { + pub fn new( + map: Vec, + dimensions: Vector2, + metadata: HashMap, + ) -> Result { + let mut data = vec![Tile::Void; (dimensions.x * dimensions.y) as usize]; + let mut player_position: Option> = None; + let mut crate_positions = HashSet::>::new(); + let mut target_positions = HashSet::>::new(); + + for (y, line) in map.iter().enumerate() { + for (x, char) in line.chars().enumerate() { + let position = Vector2::::new(x as i32, y as i32); + data[(y * dimensions.x as usize + x) as usize] = match char { + ' ' | '-' | '_' => Tile::Void, + '#' => Tile::Wall, + '$' => { + crate_positions.insert(position); + Tile::Crate + } + '.' => { + target_positions.insert(position); + Tile::Target + } + '@' => { + if player_position.is_some() { + return Err(ParseMapError::MoreThanOnePlayer); + } + player_position = Some(position); + Tile::Player + } + '*' => { + crate_positions.insert(position); + target_positions.insert(position); + Tile::Crate | Tile::Target + } + '+' => { + if player_position.is_some() { + return Err(ParseMapError::MoreThanOnePlayer); + } + player_position = Some(position); + target_positions.insert(position); + Tile::Player | Tile::Target + } + _ => return Err(ParseMapError::InvalidCharacter(char)), + }; + } + } + if player_position.is_none() { + return Err(ParseMapError::NoPlayer); + } + if crate_positions.len() != target_positions.len() { + return Err(ParseMapError::MismatchBetweenCratesAndTargets); + } + + let mut instance = Self { + data, + dimensions, + metadata, + player_position: player_position.unwrap(), + crate_positions, + target_positions, + }; + instance.flood_fill(&instance.player_position.clone(), Tile::Floor, Tile::Wall); + instance.calculate_dead_positions(); + Ok(instance) + } + + pub fn load_from_memory(buffer: String) -> Result> { + let buffer = buffer.replace("\r", "") + "\n"; + + let mut levels = Vec::new(); + + let mut map_data = Vec::::new(); + let mut map_dimensions = Vector2::::zeros(); + let mut metadata = HashMap::::new(); + + let mut in_comment_block = false; + for line in buffer.split(&['\n', '|']) { + let trimmed_line = line.trim(); + + // comment + if in_comment_block { + if trimmed_line.to_lowercase().starts_with("comment-end") + || trimmed_line.to_lowercase().starts_with("comment_end") + { + in_comment_block = false; + } + continue; + } + if trimmed_line.starts_with(";") { + continue; + } + + if trimmed_line.is_empty() { + // multiple empty lines + if map_data.is_empty() { + continue; + } + + levels.push(Level::new( + map_data.clone(), + map_dimensions, + metadata.clone(), + )?); + map_data.clear(); + map_dimensions = Vector2::::zeros(); + metadata.clear(); + continue; + } + + // metadata + if trimmed_line.contains(":") { + let (key, value) = trimmed_line.split_once(":").unwrap(); + let key = key.trim().to_lowercase(); + + if key == "comment" { + in_comment_block = true; + continue; + } + + metadata.insert(key, value.trim().to_string()); + continue; + } + + // if line contains numbers, perform RLE decoding + if line.chars().any(|c| c.is_digit(10)) { + map_data.push(rle_decode(line)); + } else { + map_data.push(line.to_string()); + } + + map_dimensions.x = std::cmp::max(line.len() as i32, map_dimensions.x); + map_dimensions.y += 1; + } + + Ok(levels) + } + + pub fn load_from_file(file_path: &Path) -> Result> { + Self::load_from_memory(fs::read_to_string(file_path).unwrap()) + } + + pub fn get(&self, position: &Vector2) -> Option { + if self.in_bounds(position) { + Some(self.data[(position.y * self.dimensions.x + position.x) as usize]) + } else { + None + } + } + + pub fn get_mut(&mut self, position: &Vector2) -> Option<&mut Tile> { + if self.in_bounds(position) { + Some(&mut self.data[(position.y * self.dimensions.x + position.x) as usize]) + } else { + None + } + } + + pub fn get_unchecked(&self, position: &Vector2) -> Tile { + debug_assert!(self.in_bounds(position)); + self.data[(position.y * self.dimensions.x + position.x) as usize] + } + + pub fn get_unchecked_mut(&mut self, position: &Vector2) -> &mut Tile { + debug_assert!(self.in_bounds(position)); + &mut self.data[(position.y * self.dimensions.x + position.x) as usize] + } + + pub fn export_map(&self) -> String { + let mut result = String::new(); + for y in 0..self.dimensions.y { + for x in 0..self.dimensions.x { + let tiles = self.get_unchecked(&Vector2::::new(x, y)); + if tiles.contains(Tile::Crate | Tile::Target) { + result.push('*'); + } else if tiles.contains(Tile::Player | Tile::Target) { + result.push('+'); + } else if tiles.contains(Tile::Wall) { + result.push('#'); + } else if tiles.contains(Tile::Crate) { + result.push('$'); + } else if tiles.contains(Tile::Target) { + result.push('.'); + } else if tiles.contains(Tile::Player) { + result.push('@'); + } else { + result.push(' '); + } + } + result.push('\n'); + } + result + } + + pub fn export_metadata(&self) -> String { + let mut result = String::new(); + for (key, value) in self.metadata.iter() { + result.push_str(&format!("{}: {}\n", key, value)); + } + result + } + + pub fn normalize(&mut self) { + assert!(self + .get_unchecked(&self.player_position) + .contains(Tile::Floor)); + self.clear(Tile::Wall); + self.clear(Tile::Void); + for x in 0..self.dimensions.x { + for y in 0..self.dimensions.y { + let position = Vector2::::new(x, y); + if self.get_unchecked(&position).intersects(Tile::Floor) { + let directions = [ + Vector2::::y(), + -Vector2::::y(), + Vector2::::x(), + -Vector2::::x(), + Vector2::::new(1, 1), + Vector2::::new(-1, -1), + Vector2::::new(1, -1), + Vector2::::new(-1, 1), + ]; + for direction in directions { + let neighbor_position = position + direction; + if !self.get_unchecked(&neighbor_position).contains(Tile::Floor) { + self.get_unchecked_mut(&neighbor_position) + .insert(Tile::Wall); + } + } + } + } + } + + let mut min_hash = u64::MAX; + for i in 1..=8 { + self.rotate(); + if i == 5 { + self.flip(); + } + + self.set_player_position(&normalized_area(&self.reachable_area( + &self.player_position, + |position| { + self.get_unchecked(position) + .intersects(Tile::Wall | Tile::Crate) + }, + ))); + + let mut hasher = SipHasher24::new(); + self.hash(&mut hasher); + let hash = hasher.finish(); + + min_hash = std::cmp::min(min_hash, hash); + + // dbg!(hash); + // print!("{}", self.export_map()); + } + + for i in 1..=8 { + self.rotate(); + if i == 5 { + self.flip(); + } + + self.set_player_position(&normalized_area(&self.reachable_area( + &self.player_position, + |position| { + self.get_unchecked(position) + .intersects(Tile::Wall | Tile::Crate) + }, + ))); + + let mut hasher = SipHasher24::new(); + self.hash(&mut hasher); + let hash = hasher.finish(); + + if hash == min_hash { + return; + } + } + unreachable!(); + } + + // TODO: 有遗漏: box_world.xsb #11 + fn calculate_dead_positions(&mut self) { + for x in 1..self.dimensions.x - 1 { + for y in 1..self.dimensions.y - 1 { + let position = Vector2::new(x, y); + if !self.get_unchecked(&position).intersects(Tile::Floor) + || self + .get_unchecked(&position) + .intersects(Tile::Target | Tile::Deadlock) + { + continue; + } + + for directions in [ + Direction::Up, + Direction::Right, + Direction::Down, + Direction::Left, + Direction::Up, + ] + .windows(2) + { + let neighbor = [ + position + directions[0].to_vector(), + position + directions[1].to_vector(), + ]; + if !(self.get_unchecked(&neighbor[0]).intersects(Tile::Wall) + && self.get_unchecked(&neighbor[1]).intersects(Tile::Wall)) + { + continue; + } + + self.get_unchecked_mut(&position).insert(Tile::Deadlock); + + let mut dead_positions = HashSet::new(); + let mut next_position = position; + while !self.get_unchecked(&next_position).intersects(Tile::Wall) + && self + .get_unchecked(&(next_position + directions[1].to_vector())) + .intersects(Tile::Wall) + { + dead_positions.insert(next_position); + next_position += -directions[0].to_vector(); + if self.get_unchecked(&next_position).intersects(Tile::Target) { + break; + } + if self.get_unchecked(&next_position).intersects(Tile::Wall) { + for dead_position in dead_positions { + self.get_unchecked_mut(&dead_position) + .insert(Tile::Deadlock); + } + break; + } + } + } + } + } + } + + pub fn reachable_area( + &self, + player_position: &Vector2, + is_block: impl Fn(&Vector2) -> bool, + ) -> HashSet> { + let mut reachable = HashSet::new(); + let mut queue = VecDeque::>::new(); + queue.push_back(*player_position); + + while let Some(position) = queue.pop_front() { + if !reachable.insert(position) { + continue; + } + + for direction in [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] { + let next_position = position + direction.to_vector(); + if is_block(&next_position) { + continue; + } + queue.push_back(next_position); + } + } + + reachable + } + + pub fn crate_reachable_path( + &self, + crate_position: &Vector2, + ) -> HashMap, Vec>> { + debug_assert!(self.crate_positions.contains(crate_position)); + + let mut visited = HashSet::new(); + let mut cost = HashMap::new(); + + let mut path = HashMap::new(); + path.insert(*crate_position, vec![*crate_position]); + + let mut queue = VecDeque::new(); + queue.push_back(Node { + crate_position: *crate_position, + player_position: self.player_position, + }); + + while let Some(node) = queue.pop_front() { + let mut new_crate_positions = self.crate_positions.clone(); + new_crate_positions.remove(crate_position); + new_crate_positions.insert(node.crate_position); + + let player_reachable_area = self.reachable_area(&node.player_position, |position| { + self.get_unchecked(position).intersects(Tile::Wall) + || new_crate_positions.contains(position) + }); + + for &push_direction in [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] + .iter() + { + let new_crate_position = node.crate_position + push_direction.to_vector(); + let new_player_position = node.crate_position - push_direction.to_vector(); + + if self + .get_unchecked(&new_player_position) + .intersects(Tile::Wall) + || !player_reachable_area.contains(&new_player_position) + { + continue; + } + + if self + .get_unchecked(&new_crate_position) + .intersects(Tile::Wall | Tile::Deadlock) + || new_crate_positions.contains(&new_crate_position) + { + continue; + } + + let new_node = Node { + crate_position: new_crate_position, + player_position: node.crate_position, + }; + + if !visited.insert(new_node.clone()) { + continue; + } + // FIXME: 最终得到的 path 并非最短路径 + let new_cost = path[&node.crate_position].len() as i32; + // FIXME: 判断条件始终为 false + if new_cost > *cost.get(&new_node).unwrap_or(&i32::MAX) { + continue; + } + cost.insert(new_node.clone(), new_cost); + + let mut new_path = path[&node.crate_position].clone(); + new_path.push(new_crate_position); + path.insert(new_crate_position, new_path); + + queue.push_back(new_node); + } + } + + path + } + + fn set_player_position(&mut self, position: &Vector2) { + self.get_unchecked_mut(&self.player_position.clone()) + .remove(Tile::Player); + self.player_position = *position; + self.get_unchecked_mut(&self.player_position.clone()) + .insert(Tile::Player); + } + + fn rotate(&mut self) { + let rotate_position = + |position: &Vector2| Vector2::new(position.y, self.dimensions.x - 1 - position.x); + + let mut rotated_data = vec![Tile::Void; (self.dimensions.x * self.dimensions.y) as usize]; + for x in 0..self.dimensions.x { + for y in 0..self.dimensions.y { + let position = Vector2::new(x, y); + let rotated_position = rotate_position(&position); + rotated_data + [(rotated_position.x + rotated_position.y * self.dimensions.y) as usize] = + self.get_unchecked(&position); + } + } + + self.data = rotated_data; + self.player_position = rotate_position(&self.player_position); + self.crate_positions = self + .crate_positions + .iter() + .map(|x| rotate_position(x)) + .collect(); + self.dimensions = self.dimensions.yx(); + } + + fn flip(&mut self) { + let flip_position = + |position: &Vector2| Vector2::new(self.dimensions.x - 1 - position.x, position.y); + + let mut flipped_data = vec![Tile::Void; (self.dimensions.x * self.dimensions.y) as usize]; + for x in 0..self.dimensions.x { + for y in 0..self.dimensions.y { + let position = Vector2::new(x, y); + let flipped_position = flip_position(&position); + flipped_data + [(flipped_position.x + flipped_position.y * self.dimensions.x) as usize] = + self.get_unchecked(&position); + } + } + + self.data = flipped_data; + self.player_position = flip_position(&self.player_position); + self.crate_positions = self + .crate_positions + .iter() + .map(|x| flip_position(x)) + .collect(); + } + + pub fn in_bounds(&self, position: &Vector2) -> bool { + 0 <= position.x + && position.x < self.dimensions.x + && 0 <= position.y + && position.y < self.dimensions.y + } + + pub fn clear(&mut self, value: Tile) { + for x in 0..self.dimensions.x { + for y in 0..self.dimensions.y { + self.get_unchecked_mut(&Vector2::::new(x, y)) + .remove(value); + } + } + } + + fn flood_fill(&mut self, start_position: &Vector2, value: Tile, border: Tile) { + let mut visited = HashSet::new(); + let mut queue = VecDeque::new(); + + if !self.in_bounds(start_position) { + return; + } + + queue.push_back(*start_position); + while let Some(position) = queue.pop_front() { + if visited.contains(&position) { + continue; + } + visited.insert(position); + + let curr_tiles = self.get_unchecked(&position); + if curr_tiles.contains(value) { + continue; + } + self.get_unchecked_mut(&position).insert(value); + + let directions = [ + Vector2::::y(), + -Vector2::::y(), + Vector2::::x(), + -Vector2::::x(), + ]; + for direction in directions { + let neighbor_position = position + direction; + if !self.in_bounds(start_position) { + continue; + } + + if self + .get_unchecked(&neighbor_position) + .intersects(value | border) + || visited.contains(&neighbor_position) + { + continue; + } + + queue.push_back(neighbor_position); + } + } + } +} + +#[allow(dead_code)] +fn rle_encode(data: &str) -> String { + let mut result = String::new(); + let mut chars = data.chars().peekable(); + let mut count = 0; + while let Some(char) = chars.next() { + count += 1; + if chars.peek() != Some(&char) { + if count > 1 { + result.push_str(&count.to_string()) + } + result.push(char); + count = 0; + } + } + result +} + +fn rle_decode(data: &str) -> String { + let mut result = String::new(); + let mut length_str = String::new(); + + let mut iter = data.chars(); + while let Some(char) = iter.next() { + if char.is_digit(10) { + length_str.push(char); + continue; + } + let mut token = String::new(); + if char == '(' { + let mut nesting_level = 0; + while let Some(char) = iter.next() { + if char == '(' { + nesting_level += 1; + } else if char == ')' { + if nesting_level == 0 { + break; + } + nesting_level -= 1; + } + token.push(char); + } + } else { + token = char.to_string(); + } + let length = length_str.parse().unwrap_or(1); + result += &token.repeat(length); + length_str.clear(); + } + + if result.contains("(") { + return rle_decode(&result); + } + result +} + +pub fn normalized_area(area: &HashSet>) -> Vector2 { + *area + .iter() + .min_by(|a, b| a.x.cmp(&b.x).then_with(|| a.y.cmp(&b.y))) + .unwrap() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_rle_encode() { + assert_eq!(rle_encode(""), ""); + assert_eq!(rle_encode("aaabbbcdd"), "3a3bc2d"); + } + + #[test] + fn test_rle_decode() { + assert_eq!(rle_decode(""), ""); + assert_eq!(rle_decode("-#$.*+@"), "-#$.*+@"); + assert_eq!(rle_decode("3-##3(.$2(+*))-#"), "---##.$+*+*.$+*+*.$+*+*-#"); + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..4d5e822 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,83 @@ +// #![feature(test)] + +mod level; +use level::*; + +mod systems; +use systems::input::*; +use systems::level::*; +use systems::render::*; + +mod plugins; +use plugins::performance_matrix::*; + +mod resources; +use resources::*; + +mod events; +use events::*; + +mod board; +mod components; +mod database; +mod direction; +mod movement; +mod solver; +mod test; + +use bevy::diagnostic::FrameTimeDiagnosticsPlugin; +use bevy::prelude::*; + +#[allow(unused_imports)] +use bevy_editor_pls::prelude::*; + +#[bevy_main] +fn main() { + App::new() + .add_plugins(( + DefaultPlugins, + FrameTimeDiagnosticsPlugin, + PerformanceMatrixPlugin, + // EditorPlugin::default(), + )) + .add_systems(PreStartup, (setup_camera, setup_database)) + .add_systems( + Startup, + (setup_window, setup_version_info, setup_hud, setup_level), + ) + .add_systems(PostStartup, spawn_board) + .add_systems( + Update, + ( + update_grid_position_from_board, + select_crate, + unselect_crate, + ( + keyboard_input, + gamepad_input, + mouse_input, + mouse_drag, + check_level_solved, + spawn_board, + ) + .chain(), + update_hud, + file_drag_and_drop, + ), + ) + .add_systems( + FixedUpdate, + ( + animate_tiles_movement, + animate_player_movement, + animate_camera_zoom, + ), + ) + .add_event::() + .add_event::() + .add_event::() + .insert_resource(Settings::default()) + .insert_resource(PlayerMovement::default()) + .insert_resource(CrateReachable::default()) + .run(); +} diff --git a/src/movement.rs b/src/movement.rs new file mode 100644 index 0000000..cf3efe1 --- /dev/null +++ b/src/movement.rs @@ -0,0 +1,55 @@ +use crate::direction::Direction; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Movement { + pub direction: Direction, + pub is_push: bool, +} + +impl From for Movement { + fn from(item: char) -> Self { + let direction = match item.to_ascii_lowercase() { + 'u' => Direction::Up, + 'd' => Direction::Down, + 'l' => Direction::Left, + 'r' => Direction::Right, + _ => panic!("invalid character"), + }; + Self { + direction, + is_push: item.is_uppercase(), + } + } +} + +impl Into for Movement { + fn into(self) -> char { + let c = match self.direction { + Direction::Up => 'u', + Direction::Down => 'd', + Direction::Left => 'l', + Direction::Right => 'r', + }; + if self.is_push { + c.to_ascii_uppercase() + } else { + c + } + } +} + +impl Movement { + pub fn with_move(direction: Direction) -> Self { + Self { + direction, + is_push: false, + } + } + + pub fn with_push(direction: Direction) -> Self { + Self { + direction, + is_push: true, + } + } +} diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs new file mode 100644 index 0000000..330280e --- /dev/null +++ b/src/plugins/mod.rs @@ -0,0 +1 @@ +pub mod performance_matrix; diff --git a/src/plugins/performance_matrix.rs b/src/plugins/performance_matrix.rs new file mode 100644 index 0000000..a9c4fc5 --- /dev/null +++ b/src/plugins/performance_matrix.rs @@ -0,0 +1,76 @@ +use bevy::diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin}; +use bevy::prelude::*; + +pub struct PerformanceMatrixPlugin; + +impl Plugin for PerformanceMatrixPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, setup_performance_matrix) + .add_systems(Update, display_performance_matrix); + } +} + +#[derive(Component)] +pub struct PerformanceCounter; + +#[derive(Bundle)] +pub struct PerformanceBundle { + text: TextBundle, + performance_counter: PerformanceCounter, +} + +impl PerformanceBundle { + pub fn new() -> PerformanceBundle { + const ALPHA: f32 = 0.8; + let text_section = move |color, value: &str| { + TextSection::new( + value, + TextStyle { + font_size: 14.0, + color, + ..default() + }, + ) + }; + PerformanceBundle { + text: TextBundle::from_sections([ + text_section(Color::GREEN.with_a(ALPHA), "FPS : "), + text_section(Color::CYAN.with_a(ALPHA), ""), + text_section(Color::GREEN.with_a(ALPHA), "\nFPS(SMA): "), + text_section(Color::CYAN.with_a(ALPHA), ""), + text_section(Color::GREEN.with_a(ALPHA), "\nFPS(EMA): "), + text_section(Color::CYAN.with_a(ALPHA), ""), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(5.0), + left: Val::Px(5.0), + ..default() + }), + performance_counter: PerformanceCounter, + } + } +} + +fn setup_performance_matrix(mut commands: Commands) { + commands.spawn(PerformanceBundle::new()); +} + +fn display_performance_matrix( + diagnostics: Res, + mut query: Query<&mut Text, With>, +) { + let mut text = query.single_mut(); + + if let Some(fps) = diagnostics.get(FrameTimeDiagnosticsPlugin::FPS) { + if let Some(raw) = fps.value() { + text.sections[1].value = format!("{raw:.2}"); + } + if let Some(sma) = fps.average() { + text.sections[3].value = format!("{sma:.2}"); + } + if let Some(ema) = fps.smoothed() { + text.sections[5].value = format!("{ema:.2}"); + } + }; +} diff --git a/src/resources.rs b/src/resources.rs new file mode 100644 index 0000000..06c4e36 --- /dev/null +++ b/src/resources.rs @@ -0,0 +1,55 @@ +use bevy::prelude::*; +use nalgebra::Vector2; + +use crate::database; +use crate::direction::Direction; + +use std::collections::{HashMap, VecDeque}; +use std::sync::Mutex; + +#[derive(Resource)] +pub struct Settings { + pub instant_move: bool, +} + +impl Default for Settings { + fn default() -> Self { + Self { instant_move: false } + } +} + +#[derive(Resource, Deref)] +pub struct Database(pub Mutex); + +#[derive(Resource, Deref, DerefMut)] +pub struct LevelId(pub u64); + +#[derive(Resource)] +pub enum CrateReachable { + None, + Some { + selected_crate: Vector2, + path: HashMap, Vec>>, + }, +} + +impl Default for CrateReachable { + fn default() -> Self { + Self::None + } +} + +#[derive(Resource)] +pub struct PlayerMovement { + pub directions: VecDeque, + pub timer: Timer, +} + +impl Default for PlayerMovement { + fn default() -> Self { + Self { + directions: VecDeque::new(), + timer: Timer::from_seconds(0.05, TimerMode::Repeating), + } + } +} diff --git a/src/solver/mod.rs b/src/solver/mod.rs new file mode 100644 index 0000000..86378be --- /dev/null +++ b/src/solver/mod.rs @@ -0,0 +1,2 @@ +pub mod solver; +pub mod state; diff --git a/src/solver/solver.rs b/src/solver/solver.rs new file mode 100644 index 0000000..652fd0a --- /dev/null +++ b/src/solver/solver.rs @@ -0,0 +1,285 @@ +use nalgebra::Vector2; + +use crate::direction::Direction; +use crate::level::{Level, Tile}; +use crate::movement::Movement; +use crate::solver::state::*; + +use std::cell::OnceCell; +use std::cmp::Ordering; +use std::collections::{BinaryHeap, HashMap, HashSet}; +use std::hash::Hash; +use std::time; + +use std::io::Write; + +#[derive(Clone)] +pub struct Solver { + pub level: Level, + lower_bounds: OnceCell, usize>>, + strategy: Strategy, + visited: HashSet, + heap: BinaryHeap, +} + +impl From for Solver { + fn from(mut level: Level) -> Self { + level.clear(Tile::Player | Tile::Crate); + let mut instance = Self { + level, + strategy: Strategy::Fast, + lower_bounds: OnceCell::new(), + visited: HashSet::new(), + heap: BinaryHeap::new(), + }; + instance.calculate_tunnel_positions(); + instance + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum SolveError { + Timeout, + NoSolution, +} + +type Result = std::result::Result; + +// FIXME: solver 可能给出错误答案: box_world.xsb #6, #70 +impl Solver { + pub fn initial(&mut self, strategy: Strategy) { + self.strategy = strategy; + self.heap.push(State::new( + self.level.player_position, + self.level.crate_positions.clone(), + Vec::new(), + self, + )); + } + + pub fn solve(&mut self, timeout: time::Duration) -> Result> { + let timer = std::time::Instant::now(); + while let Some(state) = self.heap.pop() { + self.visited.insert(state.normalized(&self)); + + if timer.elapsed() >= timeout { + return Err(SolveError::Timeout); + } + + // Solver::shrink_heap(&mut self.heap); + Solver::print_info(&self.visited, &self.heap, &state); + + for successor in state.successors(&self) { + if self.visited.contains(&successor.normalized(&self)) { + continue; + } + if successor.is_solved(&self) { + return Ok(successor.movements); + } + self.heap.push(successor); + } + } + + Err(SolveError::NoSolution) + } + + pub fn strategy(&self) -> Strategy { + self.strategy + } + + pub fn lower_bounds(&self) -> &HashMap, usize> { + self.lower_bounds + .get_or_init(|| self.calculate_lower_bounds()) + } + + fn calculate_lower_bounds(&self) -> HashMap, usize> { + let mut lower_bounds = HashMap::new(); + for x in 1..self.level.dimensions.x - 1 { + for y in 1..self.level.dimensions.y - 1 { + // 到最近目标点的最短路径长度 + let position = Vector2::new(x, y); + if !self.level.get_unchecked(&position).intersects(Tile::Floor) + || self + .level + .get_unchecked(&position) + .intersects(Tile::Deadlock) + { + continue; + } + let closest_target_position = self + .level + .target_positions + .iter() + .min_by_key(|crate_pos| manhattan_distance(crate_pos, &position)) + .unwrap(); + let movements = find_path(&position, &closest_target_position, |position| { + self.level.get_unchecked(&position).intersects(Tile::Wall) + }) + .unwrap(); + lower_bounds.insert(position, movements.len() - 1); + + // 到最近目标点的曼哈顿距离 + // let position = Vector2::new(x, y); + // if !self.level.at(&position).intersects(Tile::Floor) + // || self.dead_positions.contains(&position) + // { + // continue; + // } + // let closest_target_distance = self + // .level + // .target_positions + // .iter() + // .map(|crate_pos| manhattan_distance(crate_pos, &position)) + // .min() + // .unwrap(); + // self.lower_bounds.insert(position, closest_target_distance); + } + } + lower_bounds + } + + fn calculate_tunnel_positions(&mut self) { + for x in 1..self.level.dimensions.x - 1 { + for y in 1..self.level.dimensions.y - 1 { + let position = Vector2::new(x, y); + if !self.level.get_unchecked(&position).intersects(Tile::Floor) { + continue; + } + for directions in [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] + .chunks(2) + { + let neighbor = [ + position + directions[0].to_vector(), + position + directions[1].to_vector(), + ]; + if !(self + .level + .get_unchecked(&neighbor[0]) + .intersects(Tile::Wall) + && self + .level + .get_unchecked(&neighbor[1]) + .intersects(Tile::Wall)) + { + continue; + } + + self.level.get_unchecked_mut(&position).insert(Tile::Tunnel); + } + } + } + } + + #[allow(dead_code)] + fn shrink_heap(heap: &mut BinaryHeap) { + let max_pressure = 200_000; + if heap.len() > max_pressure { + let mut heuristics: Vec<_> = heap.iter().map(|state| state.heuristic()).collect(); + heuristics.sort_unstable(); + let mut costs: Vec<_> = heap.iter().map(|state| state.move_count()).collect(); + costs.sort_unstable(); + + let alpha = 0.8; + let heuristic_median = heuristics[(heuristics.len() as f32 * alpha) as usize]; + let cost_median = costs[(costs.len() as f32 * alpha) as usize]; + heap.retain(|state| { + state.heuristic() <= heuristic_median && state.move_count() <= cost_median + }); + } + } + + fn print_info(visited: &HashSet, heap: &BinaryHeap, state: &State) { + print!( + "Visited: {:<6}, Heuristic: {:<4}, Moves: {:<4}, Pushes: {:<4}, Pressure: {:<4}\r", + visited.len(), + state.heuristic(), + state.move_count(), + state.push_count(), + heap.len() + ); + std::io::stdout().flush().unwrap(); + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +struct Node { + position: Vector2, + heuristic: i32, +} + +impl Ord for Node { + fn cmp(&self, other: &Self) -> Ordering { + other.heuristic.cmp(&self.heuristic) + } +} + +impl PartialOrd for Node { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Finds a path from `from` point to `to` point using the A* algorithm. +pub fn find_path( + from: &Vector2, + to: &Vector2, + is_block: impl Fn(&Vector2) -> bool, +) -> Option>> { + let mut open_set = BinaryHeap::new(); + let mut came_from = HashMap::new(); + let mut cost = HashMap::new(); + + open_set.push(Node { + position: *from, + heuristic: manhattan_distance(from, to), + }); + cost.insert(*from, 0); + + while let Some(node) = open_set.pop() { + if node.position == *to { + let mut path = Vec::new(); + let mut current = *to; + while current != *from { + path.push(current); + current = came_from[¤t]; + } + path.push(*from); + path.reverse(); + return Some(path); + } + + for direction in [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] { + let new_position = node.position + direction.to_vector(); + if is_block(&new_position) { + continue; + } + + let new_cost = cost[&node.position] + 1; + if !cost.contains_key(&new_position) || new_cost < cost[&new_position] { + cost.insert(new_position, new_cost); + let priority = new_cost + manhattan_distance(&new_position, to); + open_set.push(Node { + position: new_position, + heuristic: priority, + }); + came_from.insert(new_position, node.position); + } + } + } + + None +} + +fn manhattan_distance(a: &Vector2, b: &Vector2) -> i32 { + (a.x - b.x).abs() + (a.y - b.y).abs() +} diff --git a/src/solver/state.rs b/src/solver/state.rs new file mode 100644 index 0000000..24704bf --- /dev/null +++ b/src/solver/state.rs @@ -0,0 +1,241 @@ +use nalgebra::Vector2; + +use crate::direction::Direction; +use crate::level::{normalized_area, Tile}; +use crate::movement::Movement; +use crate::solver::solver::*; + +use std::cell::OnceCell; +use std::cmp::Ordering; +use std::collections::HashSet; +use std::hash::{Hash, Hasher}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +pub enum Strategy { + /// Find any solution + Fast, + + /// Find move optimal solutions with best pushes + // FIXME: 结果非最优解, 甚至可能比其他策略差. 可能是由于遇到答案就直接返回忽略剩余状态导致的 + OptimalMovePush, + + /// Find push optimal solutions with best moves + OptimalPushMove, + + Mixed, +} + +#[derive(Clone, Eq)] +pub struct State { + pub player_position: Vector2, + pub crate_positions: HashSet>, + pub movements: Vec, + heuristic: usize, + lower_bound: OnceCell, +} + +impl PartialEq for State { + fn eq(&self, other: &Self) -> bool { + self.player_position == other.player_position + && self.crate_positions == other.crate_positions + } +} + +impl Hash for State { + fn hash(&self, state: &mut H) { + self.player_position.hash(state); + for position in &self.crate_positions { + position.hash(state); + } + } +} + +impl Ord for State { + fn cmp(&self, other: &Self) -> Ordering { + other.heuristic.cmp(&self.heuristic) + } +} + +impl PartialOrd for State { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl State { + pub fn new( + player_position: Vector2, + crate_positions: HashSet>, + movements: Vec, + solver: &Solver, + ) -> Self { + let mut instance = Self { + player_position, + crate_positions, + movements, + heuristic: 0, + lower_bound: OnceCell::new(), + }; + debug_assert!(instance.move_count() < 10000); + debug_assert!(instance.push_count() < 10000); + debug_assert!(instance.lower_bound(solver) < 10000); + instance.heuristic = match solver.strategy() { + Strategy::Fast => instance.lower_bound(solver) * 10000 + instance.move_count(), + Strategy::Mixed => instance.lower_bound(solver) + instance.move_count(), + Strategy::OptimalMovePush => { + instance.move_count() * 10000_0000 + + instance.push_count() * 10000 + + instance.lower_bound(solver) + } + Strategy::OptimalPushMove => { + instance.push_count() * 10000_0000 + + instance.move_count() * 10000 + + instance.lower_bound(solver) + } + }; + instance + } + + /// Returns a vector of successor states for the current state. + pub fn successors(&self, solver: &Solver) -> Vec { + let mut successors = Vec::new(); + let player_reachable_area = self.player_reachable_area(solver); + for crate_position in &self.crate_positions { + for push_direction in [ + Direction::Up, + Direction::Down, + Direction::Left, + Direction::Right, + ] { + let mut new_crate_position = crate_position + push_direction.to_vector(); + + let next_player_position = crate_position - push_direction.to_vector(); + if self.can_block_player(&next_player_position, solver) + || !player_reachable_area.contains(&next_player_position) + { + continue; + } + + if self.can_block_crate(&new_crate_position, solver) { + continue; + } + + // slide in tunnel + let mut slide_movements = Vec::new(); + if solver + .level + .get_unchecked(&crate_position) + .intersects(Tile::Tunnel) + { + while solver + .level + .get_unchecked(&new_crate_position) + .intersects(Tile::Tunnel) + && solver + .level + .get_unchecked(&(new_crate_position + push_direction.to_vector())) + .intersects(Tile::Tunnel) + && !solver + .level + .get_unchecked(&new_crate_position) + .intersects(Tile::Target) + { + new_crate_position += push_direction.to_vector(); + slide_movements.push(Movement::with_push(push_direction)); + } + } + + let mut new_movements = self.movements.clone(); + if let Some(path) = + find_path(&self.player_position, &next_player_position, |position| { + self.can_block_player(position, solver) + }) + { + new_movements.extend( + path.windows(2) + .map(|p| Direction::from_vector(p[1] - p[0]).unwrap()) + .map(Movement::with_move), + ) + } + new_movements.push(Movement::with_push(push_direction)); + new_movements.extend(slide_movements); + + let mut new_crate_positions = self.crate_positions.clone(); + new_crate_positions.remove(crate_position); + new_crate_positions.insert(new_crate_position); + + let new_player_position = new_crate_position - push_direction.to_vector(); + + let new_state = State::new( + new_player_position, + new_crate_positions, + new_movements, + solver, + ); + successors.push(new_state); + } + } + successors + } + + pub fn is_solved(&self, solver: &Solver) -> bool { + self.lower_bound(solver) == 0 + } + + pub fn heuristic(&self) -> usize { + self.heuristic + } + + pub fn move_count(&self) -> usize { + self.movements.len() + } + + pub fn push_count(&self) -> usize { + self.movements.iter().filter(|x| x.is_push).count() + } + + pub fn normalized(&self, solver: &Solver) -> Self { + let mut instance = self.clone(); + instance.player_position = self.normalized_player_position(solver); + instance + } + + /// Minimum number of pushes required to complete the level. + fn lower_bound(&self, solver: &Solver) -> usize { + *self + .lower_bound + .get_or_init(|| self.calculate_lower_bound(solver)) + } + + fn calculate_lower_bound(&self, solver: &Solver) -> usize { + self.crate_positions + .iter() + .map(|crate_pos| solver.lower_bounds()[&crate_pos]) + .sum() + } + + fn can_block_player(&self, position: &Vector2, solver: &Solver) -> bool { + solver.level.get_unchecked(position).intersects(Tile::Wall) + || self.crate_positions.contains(position) + } + + fn can_block_crate(&self, position: &Vector2, solver: &Solver) -> bool { + solver + .level + .get_unchecked(position) + .intersects(Tile::Wall | Tile::Deadlock) + || self.crate_positions.contains(position) + } + + fn normalized_player_position(&self, solver: &Solver) -> Vector2 { + normalized_area(&self.player_reachable_area(solver)) + } + + fn player_reachable_area(&self, solver: &Solver) -> HashSet> { + solver + .level + .reachable_area(&self.player_position, |position| { + self.can_block_player(&position, solver) + }) + } +} diff --git a/src/systems/input.rs b/src/systems/input.rs new file mode 100644 index 0000000..6c29912 --- /dev/null +++ b/src/systems/input.rs @@ -0,0 +1,363 @@ +use bevy::input::mouse::{MouseMotion, MouseWheel}; +use bevy::prelude::*; +use nalgebra::Vector2; + +use crate::components::*; +use crate::direction::Direction; +use crate::events::*; +use crate::level::{Level, Tile}; +use crate::resources::*; +use crate::solver::{ + solver::{find_path, SolveError, Solver}, + state::Strategy, +}; +use crate::systems::level::*; + +pub fn player_move_to( + target: &Vector2, + board: &mut crate::board::Board, + player_movement: &mut ResMut, +) { + if let Some(path) = find_path(&board.level.player_position, target, |position| { + board + .level + .get_unchecked(&position) + .intersects(Tile::Wall | Tile::Crate) + }) { + // player_movement.directions.clear(); + let directions = path + .windows(2) + .map(|pos| Direction::from_vector(pos[1] - pos[0]).unwrap()); + for direction in directions { + player_move_or_push(direction, board, player_movement); + } + } +} + +fn player_move_or_push( + direction: Direction, + board: &mut crate::board::Board, + player_movement: &mut ResMut, +) { + if board.move_or_push(direction) { + player_movement.directions.push_front(direction); + } +} + +fn solve_level(board: &mut crate::board::Board, player_movement: &mut ResMut) { + let timeout = std::time::Duration::from_secs(15); + info!("Start solving (timeout: {:?})", timeout); + let solver = Solver::from(board.level.clone()); + + // Strategy::Fast, Strategy::Mixed + for strategy in [Strategy::Fast] { + let mut solver = solver.clone(); + let timer = std::time::Instant::now(); + solver.initial(strategy); + match solver.solve(timeout) { + Ok(solution) => { + let mut verify_board = board.clone(); + for movement in &solution { + verify_board.move_or_push(movement.direction); + } + assert!(verify_board.is_solved()); + + let lurd = solution + .iter() + .map(|x| Into::::into(x.clone())) + .collect::(); + info!( + "Solved ({:?}): {} sec, ", + strategy, + timer.elapsed().as_millis() as f32 / 1000.0 + ); + info!( + " Moves: {}, pushes: {}", + solution.len(), + lurd.chars().filter(|x| x.is_uppercase()).count() + ); + info!(" Solution: {}", lurd); + + for movement in solution { + player_move_or_push(movement.direction, board, player_movement); + } + } + Err(SolveError::NoSolution) => { + info!( + "No solution ({:?}): {} sec", + strategy, + timer.elapsed().as_millis() as f32 / 1000.0 + ); + break; + } + Err(SolveError::Timeout) => { + info!( + "Failed to find a solution within the given time limit ({:?}): {} sec", + strategy, + timer.elapsed().as_millis() as f32 / 1000.0 + ); + break; + } + } + } +} + +pub fn mouse_input( + mouse_buttons: Res>, + mut mouse_wheel_events: EventReader, + mut board: Query<&mut Board>, + windows: Query<&Window>, + mut camera: Query<(&Camera, &GlobalTransform, &mut MainCamera)>, + + mut player_movement: ResMut, + mut crate_reachable: ResMut, + mut select_crate_events: EventWriter, + mut unselect_crate_events: EventWriter, + mut update_grid_position_events: EventWriter, +) { + let Board { board, tile_size } = &mut *board.single_mut(); + let (camera, camera_transform, mut main_camera) = camera.single_mut(); + + if mouse_buttons.just_pressed(MouseButton::Other(1)) { + board.undo_push(); + player_movement.directions.clear(); + update_grid_position_events.send(UpdateGridPositionEvent); + unselect_crate_events.send(UnselectCrate); + return; + } + + if mouse_buttons.just_pressed(MouseButton::Left) { + let cursor_position = windows.single().cursor_position().unwrap(); + let position = camera + .viewport_to_world_2d(camera_transform, cursor_position) + .unwrap(); + let grid_position = ((position + (tile_size.x / 2.0)) / tile_size.x).as_ivec2(); + let grid_position = + Vector2::new(grid_position.x, board.level.dimensions.y - grid_position.y); + + // TODO: 将选择箱子作为一个 State + unselect_crate_events.send(UnselectCrate); + match &*crate_reachable { + CrateReachable::None => { + if board.level.crate_positions.contains(&grid_position) { + select_crate_events.send(SelectCrate(grid_position)); + return; + } + } + CrateReachable::Some { + selected_crate, + path, + } => { + if path.contains_key(&grid_position) { + if *selected_crate == grid_position { + *crate_reachable = CrateReachable::None; + return; + } + let crate_path = path[&grid_position].clone(); + for (crate_position, push_direction) in crate_path + .windows(2) + .map(|pos| (pos[0], Direction::from_vector(pos[1] - pos[0]).unwrap())) + { + let player_position = crate_position - push_direction.to_vector(); + player_move_to(&player_position, board, &mut player_movement); + player_move_or_push(push_direction, board, &mut player_movement); + } + } else if grid_position != *selected_crate + && board.level.crate_positions.contains(&grid_position) + { + select_crate_events.send(SelectCrate(grid_position)); + return; + } + *crate_reachable = CrateReachable::None; + return; + } + } + + player_move_to(&grid_position, board, &mut player_movement); + } + + for event in mouse_wheel_events.read() { + if event.y > 0.0 { + main_camera.target_scale /= 1.25; + } else { + main_camera.target_scale *= 1.25; + } + } +} + +pub fn mouse_drag( + mouse_buttons: Res>, + mut motion_events: EventReader, + mut camera: Query<(&mut Transform, &mut MainCamera), With>, +) { + let (mut camera_transform, main_camera) = camera.single_mut(); + if mouse_buttons.pressed(MouseButton::Right) { + for event in motion_events.read() { + camera_transform.translation.x -= event.delta.x * main_camera.target_scale * 0.6; + camera_transform.translation.y += event.delta.y * main_camera.target_scale * 0.6; + } + } else { + motion_events.clear(); + } +} + +pub fn keyboard_input( + keyboard: Res>, + mut board: Query<&mut Board>, + mut level_id: ResMut, + database: Res, + mut camera: Query<&mut MainCamera>, + mut player_movement: ResMut, + mut settings: ResMut, + + mut unselect_crate_events: EventWriter, + mut update_grid_position_events: EventWriter, +) { + let board = &mut board.single_mut().board; + + let mut any_pressed = false; + + if keyboard.any_just_pressed([KeyCode::W, KeyCode::Up, KeyCode::K]) { + player_move_or_push(Direction::Up, board, &mut player_movement); + any_pressed = true; + } + if keyboard.any_just_pressed([KeyCode::S, KeyCode::Down, KeyCode::J]) { + player_move_or_push(Direction::Down, board, &mut player_movement); + any_pressed = true; + } + if keyboard.any_just_pressed([KeyCode::A, KeyCode::Left, KeyCode::H]) { + player_move_or_push(Direction::Left, board, &mut player_movement); + any_pressed = true; + } + if keyboard.any_just_pressed([KeyCode::D, KeyCode::Right, KeyCode::L]) { + player_move_or_push(Direction::Right, board, &mut player_movement); + any_pressed = true; + } + + if (keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::Z)) + || keyboard.just_pressed(KeyCode::U) + { + board.undo_push(); + player_movement.directions.clear(); + update_grid_position_events.send(UpdateGridPositionEvent); + any_pressed = true; + } + + if player_movement.directions.is_empty() { + let database = database.lock().unwrap(); + if keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::V) { + import_from_clipboard(&mut level_id, &database); + any_pressed = true; + } + if keyboard.pressed(KeyCode::ControlLeft) && keyboard.just_pressed(KeyCode::C) { + export_to_clipboard(&board); + any_pressed = true; + } + + if keyboard.just_pressed(KeyCode::BracketRight) { + switch_to_next_level(&mut level_id, &database); + any_pressed = true; + } + if keyboard.just_pressed(KeyCode::BracketLeft) { + switch_to_previous_level(&mut level_id, &database); + any_pressed = true; + } + } + + let mut main_camera = camera.single_mut(); + if keyboard.just_pressed(KeyCode::Equals) { + main_camera.target_scale /= 1.25; + any_pressed = true; + } + if keyboard.just_pressed(KeyCode::Minus) { + main_camera.target_scale *= 1.25; + any_pressed = true; + } + + if keyboard.just_pressed(KeyCode::P) { + solve_level(board, &mut player_movement); + any_pressed = true; + } + + if keyboard.just_pressed(KeyCode::I) { + settings.instant_move = !settings.instant_move; + any_pressed = true; + } + + if any_pressed { + unselect_crate_events.send(UnselectCrate); + } +} + +pub fn gamepad_input( + gamepads: Res, + button_inputs: Res>, + mut board: Query<&mut Board>, + mut level_id: ResMut, + database: Res, + + mut player_movement: ResMut, + mut update_grid_position_events: EventWriter, + mut unselect_crate_events: EventWriter, +) { + let database = database.lock().unwrap(); + let board = &mut board.single_mut().board; + + let mut any_pressed = false; + + for gamepad in gamepads.iter() { + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::DPadUp)) { + board.move_or_push(Direction::Up); + any_pressed = true; + } + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::DPadDown)) { + board.move_or_push(Direction::Down); + any_pressed = true; + } + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::DPadLeft)) { + board.move_or_push(Direction::Left); + any_pressed = true; + } + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::DPadRight)) { + board.move_or_push(Direction::Right); + any_pressed = true; + } + + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::East)) { + board.undo_push(); + player_movement.directions.clear(); + update_grid_position_events.send(UpdateGridPositionEvent); + any_pressed = true; + } + + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::RightTrigger)) + { + switch_to_next_level(&mut level_id, &database); + any_pressed = true; + } + if button_inputs.just_pressed(GamepadButton::new(gamepad, GamepadButtonType::LeftTrigger)) { + switch_to_previous_level(&mut level_id, &database); + any_pressed = true; + } + } + + if any_pressed { + unselect_crate_events.send(UnselectCrate); + } +} + +pub fn file_drag_and_drop(mut events: EventReader) { + for event in events.read() { + if let FileDragAndDrop::DroppedFile { + window: _, + path_buf, + } = event + { + info!("Load levels from file {:?}", path_buf); + match Level::load_from_file(path_buf) { + Ok(levels) => info!("Done, {} levels loaded", levels.len()), + Err(msg) => warn!("Failed to load levels from file: {}", msg), + } + } + } +} diff --git a/src/systems/level.rs b/src/systems/level.rs new file mode 100644 index 0000000..3c71027 --- /dev/null +++ b/src/systems/level.rs @@ -0,0 +1,190 @@ +use arboard::Clipboard; +use bevy::prelude::*; +use nalgebra::Vector2; + +use crate::board; +use crate::components::*; +use crate::database; +use crate::level::Level; +use crate::level::Tile; +use crate::resources::*; + +use std::collections::HashMap; +use std::fs; +use std::path::Path; +use std::sync::Mutex; + +pub fn setup_database(mut commands: Commands) { + let database = database::Database::from_file(Path::new("database.db")); + database.initialize(); + info!("Loading levels from files"); + for path in fs::read_dir("assets/levels/").unwrap() { + let path = path.unwrap().path(); + if !path.is_file() { + continue; + } + info!(" {:?}", path); + let levels = Level::load_from_file(&path).unwrap(); + database.import_levels(&levels); + } + info!("Done"); + commands.insert_resource(Database(Mutex::new(database))); +} + +pub fn setup_level(mut commands: Commands, database: Res) { + let database = database.lock().unwrap(); + let level = database.get_level_by_id(1).unwrap(); + commands.spawn(Board { + board: board::Board { + level, + movements: Vec::new(), + }, + tile_size: Vector2::zeros(), + }); + commands.insert_resource(LevelId(1)); +} + +pub fn spawn_board( + mut commands: Commands, + database: Res, + mut camera: Query<&mut Transform, With>, + board: Query>, + level_id: Res, + asset_server: Res, + mut texture_atlases: ResMut>, +) { + if !level_id.is_changed() { + return; + } + + let database = database.lock().unwrap(); + let level = database.get_level_by_id(**level_id).unwrap(); + + let spritesheet_handle = asset_server.load("textures/spritesheet.png"); + let tile_size = Vector2::new(128.0, 128.0); + let texture_atlas = TextureAtlas::from_grid( + spritesheet_handle, + Vec2::new(tile_size.x, tile_size.y), + 4, + 2, + None, + None, + ); + let texture_atlas_handle = texture_atlases.add(texture_atlas); + + let board_size = tile_size.x * level.dimensions.map(|x| x as f32); + + // move the camera to the center of the board + let mut transform = camera.single_mut(); + transform.translation.x = board_size.x / 2.0; + transform.translation.y = board_size.y / 2.0; + + // despawn the previous `Board` + commands.entity(board.single()).despawn_recursive(); + + // spawn new `Board` + let board = board::Board { + level: level.clone(), + movements: Vec::new(), + }; + commands + .spawn((Board { board, tile_size }, SpatialBundle::default())) + .with_children(|parent| { + for y in 0..level.dimensions.y { + for x in 0..level.dimensions.x { + let grid_position = Vector2::::new(x, y); + if level.get_unchecked(&grid_position) == Tile::Void { + continue; + } + let tiles = HashMap::from([ + (Tile::Floor, 0), + (Tile::Wall, 1), + (Tile::Crate, 2), + (Tile::Target, 3), + (Tile::Player, 6), + ]); + for (tile, sprite_index) in tiles.into_iter() { + if level.get_unchecked(&grid_position).intersects(tile) { + let mut entity = parent.spawn(( + SpriteSheetBundle { + texture_atlas: texture_atlas_handle.clone(), + sprite: TextureAtlasSprite::new(sprite_index), + transform: Transform::from_xyz(0.0, 0.0, sprite_index as f32), + ..default() + }, + GridPosition(grid_position), + )); + if tile == Tile::Player { + entity.insert(Player); + } else if tile == Tile::Crate { + entity.insert(Crate); + } + } + } + } + } + }); +} + +pub fn check_level_solved( + mut board: Query<&mut Board>, + mut level_id: ResMut, + database: Res, + player_movement: Res, +) { + if !player_movement.directions.is_empty() { + return; + } + + let database = database.lock().unwrap(); + let board = &mut board.single_mut().board; + if board.is_solved() { + info!("{}", "=".repeat(15)); + info!("#{} Sloved!", **level_id); + info!("Moves : {}", board.move_count()); + info!("Pushes : {}", board.push_count()); + info!("Solution: {}", board.export_movements()); + database.update_solution(**level_id, &board.movements); + switch_to_next_level(&mut level_id, &database); + } +} + +pub fn import_from_clipboard( + level_id: &mut LevelId, + database: &std::sync::MutexGuard, +) { + let mut clipboard = Clipboard::new().unwrap(); + match Level::load_from_memory(clipboard.get_text().unwrap()) { + Ok(levels) => { + info!("import {} levels from clipboard", levels.len()); + database.import_levels(&levels); + **level_id = database.get_level_id(&levels[0]).unwrap(); + } + Err(msg) => error!("failed to import levels from clipboard: {}", msg), + } +} + +pub fn export_to_clipboard(board: &crate::board::Board) { + let mut clipboard = Clipboard::new().unwrap(); + clipboard + .set_text(board.level.export_map() + &board.level.export_metadata()) + .unwrap(); +} + +pub fn switch_to_next_level( + level_id: &mut LevelId, + database: &std::sync::MutexGuard, +) { + if **level_id < database.max_level_id().unwrap() { + **level_id += 1; + } +} + +pub fn switch_to_previous_level( + level_id: &mut LevelId, + database: &std::sync::MutexGuard, +) { + if **level_id > database.min_level_id().unwrap() { + **level_id -= 1; + } +} diff --git a/src/systems/mod.rs b/src/systems/mod.rs new file mode 100644 index 0000000..e9248de --- /dev/null +++ b/src/systems/mod.rs @@ -0,0 +1,3 @@ +pub mod input; +pub mod level; +pub mod render; diff --git a/src/systems/render.rs b/src/systems/render.rs new file mode 100644 index 0000000..3a3853b --- /dev/null +++ b/src/systems/render.rs @@ -0,0 +1,309 @@ +use bevy::prelude::*; +use bevy::winit::WinitWindows; + +use crate::components::*; +use crate::events::*; +use crate::resources::*; + +use std::collections::HashSet; + +pub fn setup_window(mut window: Query<&mut Window>, winit_windows: NonSend) { + let mut window = window.single_mut(); + window.title = "Sokoban".to_string(); + // window.mode = bevy::window::WindowMode::BorderlessFullscreen; + + // set window icon + let (icon_rgba, icon_width, icon_height) = { + let image = image::open("assets/textures/crate.png") + .expect("Failed to open icon path") + .into_rgba8(); + let (width, height) = image.dimensions(); + let rgba = image.into_raw(); + (rgba, width, height) + }; + let icon = winit::window::Icon::from_rgba(icon_rgba, icon_width, icon_height).unwrap(); + for window in winit_windows.windows.values() { + window.set_window_icon(Some(icon.clone())); + } +} + +pub fn setup_camera(mut commands: Commands) { + commands.spawn((Camera2dBundle::default(), MainCamera::default())); +} + +pub fn setup_version_info(mut commands: Commands) { + const ALPHA: f32 = 0.8; + commands.spawn( + TextBundle::from_sections([TextSection::new( + "version: ".to_string() + env!("CARGO_PKG_VERSION"), + TextStyle { + font_size: 14.0, + color: Color::GRAY.with_a(ALPHA), + ..default() + }, + )]) + .with_style(Style { + position_type: PositionType::Absolute, + bottom: Val::Px(5.0), + right: Val::Px(5.0), + ..default() + }), + ); +} + +pub fn setup_hud(mut commands: Commands) { + const ALPHA: f32 = 0.8; + let text_section = move |color, value: &str| { + TextSection::new( + value, + TextStyle { + font_size: 18.0, + color, + ..default() + }, + ) + }; + commands.spawn(( + HUD, + TextBundle::from_sections([ + text_section(Color::SEA_GREEN.with_a(ALPHA), "Level : "), + text_section(Color::GOLD.with_a(ALPHA), ""), + text_section(Color::SEA_GREEN.with_a(ALPHA), "\nMoves : "), + text_section(Color::GOLD.with_a(ALPHA), ""), + text_section(Color::SEA_GREEN.with_a(ALPHA), "\nPushes: "), + text_section(Color::GOLD.with_a(ALPHA), ""), + text_section(Color::SEA_GREEN.with_a(ALPHA), "\nBest moves : "), + text_section(Color::GOLD.with_a(ALPHA), ""), + text_section(Color::SEA_GREEN.with_a(ALPHA), "\nBest pushes: "), + text_section(Color::GOLD.with_a(ALPHA), ""), + ]) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(5.0), + right: Val::Px(5.0), + ..default() + }), + )); +} + +pub fn update_hud( + mut hud: Query<&mut Text, With>, + board: Query<&Board>, + level_id: Res, + database: Res, +) { + let mut hud = hud.single_mut(); + let board = &board.single().board; + + if level_id.is_changed() { + hud.sections[1].value = format!("#{}", **level_id); + + let database = database.lock().unwrap(); + hud.sections[7].value = + format!("{}", database.get_best_move_count(**level_id).unwrap_or(0)); + hud.sections[9].value = + format!("{}", database.get_best_push_count(**level_id).unwrap_or(0)); + } + + hud.sections[3].value = format!("{}", board.move_count()); + hud.sections[5].value = format!("{}", board.push_count()); +} + +pub fn animate_player_movement( + mut player: Query<&mut GridPosition, With>, + mut crates: Query<&mut GridPosition, (With, Without)>, + mut player_movement: ResMut, + time: Res