diff --git a/.actrc b/.actrc new file mode 100644 index 0000000..2bea0a7 --- /dev/null +++ b/.actrc @@ -0,0 +1,3 @@ +--env ACTIONS_CACHE_URL=http://127.0.0.1:8000/ +--env ACTIONS_RUNTIME_URL=http://127.0.0.1:8000/ +--env ACTIONS_RUNTIME_TOKEN=local-cache-server-token \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6b2248e..80f267f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Check, Test, Lint +name: Validate code and build on: push: @@ -7,98 +7,23 @@ on: branches: [ main ] jobs: - fmt: - name: Cargo fmt + validate-code: runs-on: ubuntu-latest steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - uses: Swatinem/rust-cache@v1 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: rustfmt - - - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - clippy: - name: Cargo clippy - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - components: clippy - - - uses: Swatinem/rust-cache@v1 - - - name: Annotate commit with clippy warnings - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-targets --all-features -- -D warnings - - audit: - name: Cargo audit - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 - - - uses: Swatinem/rust-cache@v1 - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - profile: minimal toolchain: stable - override: true - components: clippy - - - name: Security audit - uses: actions-rs/audit-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - test: - name: Cargo test - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v2 + components: clippy, rustfmt - - uses: Swatinem/rust-cache@v1 + - run: cargo fmt --all -- --check + - run: cargo clippy --all-targets --all-features -- -D warnings - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - - name: Run cargo test - uses: actions-rs/cargo@v1 - with: - command: test - args: --all-features + - run: cargo install cargo-audit + - run: cargo audit - build: - name: Cargo build & test + build-and-test: + needs: validate-code runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -106,35 +31,20 @@ jobs: include: - os: ubuntu-latest target: x86_64-unknown-linux-gnu - use-cross: true - os: macos-latest target: x86_64-apple-darwin - use-cross: false - - os: macos-latest - target: aarch64-apple-darwin - use-cross: false - os: windows-latest target: x86_64-pc-windows-msvc - cross: false + steps: - name: Checkout sources - uses: actions/checkout@v2 - - - uses: Swatinem/rust-cache@v1 + uses: actions/checkout@v4 + - uses: actions-rust-lang/setup-rust-toolchain@v1 with: - sharedKey: ${{ matrix.target }} - - - name: Install stable toolchain - uses: actions-rs/toolchain@v1 - with: - profile: minimal toolchain: stable - target: ${{ matrix.target }} - override: true - name: Run cargo build - uses: actions-rs/cargo@v1 - with: - use-cross: ${{ matrix.use-cross }} - command: build - args: --target=${{ matrix.target }} --all-features + run: cargo build --target ${{ matrix.target }} --all-features + + - name: Run cargo test + run: cargo test --target ${{ matrix.target }} --all-features -- --test-threads=1 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c734a52 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1632 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "accesskit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b" + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "android_log-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ecc8056bf6ab9892dcd53216c83d1597487d7dacac16c8df6b877d127df9937" + +[[package]] +name = "async-channel" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +dependencies = [ + "concurrent-queue", + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-task" +version = "4.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" + +[[package]] +name = "async-trait" +version = "0.1.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "backtrace" +version = "0.3.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bevy" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65b9eadaacf8fe971331bc3f250f35c18bc9dace3f96b483062f38ac07e3a1b4" +dependencies = [ + "bevy_internal", +] + +[[package]] +name = "bevy_a11y" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8ef2795f7f5c816a4eda04834083eb5a92e8fef603bc21d2091c6e3b63621a" +dependencies = [ + "accesskit", + "bevy_app", + "bevy_derive", + "bevy_ecs", +] + +[[package]] +name = "bevy_app" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab348a32e46d21c5d61794294a92d415a770d26c7ba8951830b127b40b53ccc4" +dependencies = [ + "bevy_derive", + "bevy_ecs", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "downcast-rs", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "bevy_core" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12b0042f241ba7cd61487aadd8addfb56f7eeb662d713ac1577026704508fc6c" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "bytemuck", +] + +[[package]] +name = "bevy_derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0e01f8343f391e2d6a63b368b82fb5b252ed43c8713fc87f9a8f2d59407dd00" +dependencies = [ + "bevy_macro_utils", + "quote", + "syn", +] + +[[package]] +name = "bevy_diagnostic" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1401cdccec7e49378d013dfb0ff62c251f85b3be19dcdf04cfd827f793d1ee9" +dependencies = [ + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_log", + "bevy_time", + "bevy_utils", + "const-fnv1a-hash", + "sysinfo", +] + +[[package]] +name = "bevy_ecs" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e612a8e7962ead849e370f3a7e972b88df879ced05cd9dad6a0286d14650cf" +dependencies = [ + "async-channel", + "bevy_ecs_macros", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_utils", + "downcast-rs", + "fixedbitset", + "rustc-hash", + "serde", + "thiserror", + "thread_local", +] + +[[package]] +name = "bevy_ecs_macros" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "807b5106c3410e58f4f523b55ea3c071e2a09e31e9510f3c22021c6a04732b5b" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_hierarchy" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb3dfad24866a6713dafa3065a91c5cf5e355f6e1b191c25d704ae54185246c" +dependencies = [ + "bevy_app", + "bevy_core", + "bevy_ecs", + "bevy_log", + "bevy_reflect", + "bevy_utils", +] + +[[package]] +name = "bevy_input" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47f2b2b3df168c6ef661d25e09abf5bd4fecaacd400f27e5db650df1c3fa3a3b" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_math", + "bevy_reflect", + "bevy_utils", + "smol_str", + "thiserror", +] + +[[package]] +name = "bevy_internal" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f58ec0ce77603df9474cde61f429126bfe06eb79094440e9141afb4217751c79" +dependencies = [ + "bevy_a11y", + "bevy_app", + "bevy_core", + "bevy_derive", + "bevy_diagnostic", + "bevy_ecs", + "bevy_hierarchy", + "bevy_input", + "bevy_log", + "bevy_math", + "bevy_ptr", + "bevy_reflect", + "bevy_tasks", + "bevy_time", + "bevy_transform", + "bevy_utils", + "bevy_window", +] + +[[package]] +name = "bevy_log" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5eea6c527fd828b7fef8d0f518167f27f405b904a16f227b644687d3f46a809" +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.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb270c98a96243b29465139ed10bda2f675d00a11904f6588a5f7fc4774119c7" +dependencies = [ + "proc-macro2", + "quote", + "rustc-hash", + "syn", + "toml_edit", +] + +[[package]] +name = "bevy_math" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f06daa26ffb82d90ba772256c0ba286f6c305c392f6976c9822717974805837c" +dependencies = [ + "glam", + "serde", +] + +[[package]] +name = "bevy_ptr" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8050e2869fe341db6874203b5a01ff12673807a2c7c80cb829f6c7bea6997268" + +[[package]] +name = "bevy_reflect" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccbd7de21d586457a340a0962ad0747dc5098ff925eb6b27a918c4bdd8252f7b" +dependencies = [ + "bevy_math", + "bevy_ptr", + "bevy_reflect_derive", + "bevy_utils", + "downcast-rs", + "erased-serde", + "glam", + "serde", + "smol_str", + "thiserror", +] + +[[package]] +name = "bevy_reflect_derive" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ce33051bd49036d4a5a62aa3f2068672ec55f3ebe92aa0d003a341f15cc37ac" +dependencies = [ + "bevy_macro_utils", + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "bevy_slinet" +version = "0.10.0" +dependencies = [ + "async-trait", + "bevy", + "bincode", + "byteorder", + "dashmap", + "futures", + "log", + "serde", + "tokio", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_tasks" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07fcc4969b357de143509925b39c9a2c56eaa8750828d97f319ca9ed41897cb" +dependencies = [ + "async-channel", + "async-executor", + "async-task", + "concurrent-queue", + "futures-lite", + "wasm-bindgen-futures", +] + +[[package]] +name = "bevy_time" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38ea5ae9fe7f56f555dbb05a88d34931907873e3f0c7dc426591839eef72fe3e" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_reflect", + "bevy_utils", + "crossbeam-channel", + "thiserror", +] + +[[package]] +name = "bevy_transform" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d51a1f332cc00939d2f19ed6b909e5ed7037e39c7e25cc86930d79d432163e" +dependencies = [ + "bevy_app", + "bevy_ecs", + "bevy_hierarchy", + "bevy_math", + "bevy_reflect", + "thiserror", +] + +[[package]] +name = "bevy_utils" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9f845a985c00e0ee8dc2d8af3f417be925fb52aad4bda5b96e2e58a2b4d2eb" +dependencies = [ + "ahash", + "bevy_utils_proc_macros", + "getrandom", + "hashbrown", + "nonmax", + "petgraph", + "smallvec", + "thiserror", + "tracing", + "uuid", + "web-time", +] + +[[package]] +name = "bevy_utils_proc_macros" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef158627f30503d5c18c20c60b444829f698d343516eeaf6eeee078c9a45163" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bevy_window" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976202d2ed838176595b550ac654b15ae236e0178a6f19a94ca6d58f2a96ca60" +dependencies = [ + "bevy_a11y", + "bevy_app", + "bevy_ecs", + "bevy_input", + "bevy_math", + "bevy_reflect", + "bevy_utils", + "raw-window-handle", + "smol_str", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[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-fnv1a-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "crossbeam-channel" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[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.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" +dependencies = [ + "serde", +] + +[[package]] +name = "event-listener" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "futures" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-executor" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[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 = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" +dependencies = [ + "bytemuck", + "serde", +] + +[[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 = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[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.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[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 = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nonmax" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51" + +[[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_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" + +[[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", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.6", + "regex-syntax 0.8.3", +] + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.3", +] + +[[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.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[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 = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.198" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] + +[[package]] +name = "smol_str" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6845563ada680337a52d43bb0b29f396f2d911616f6573012645b9e3d048a49" +dependencies = [ + "serde", +] + +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysinfo" +version = "0.30.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "libc", + "ntapi", + "once_cell", + "windows", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tokio" +version = "1.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap", + "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", +] + +[[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 = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "uuid" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +dependencies = [ + "getrandom", + "serde", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +dependencies = [ + "windows-core", + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-sys" +version = "0.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.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[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", +] diff --git a/Cargo.toml b/Cargo.toml index a346a7e..93a2cc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "bevy_slinet" -version = "0.9.0" -authors = ["Sliman4 <4sliman4@gmail.com>"] +version = "0.10.0" +authors = [ + "Sliman4 <4sliman4@gmail.com>", + "aggyomfg " + ] edition = "2021" license = "Apache-2.0 OR MIT" readme = "README.md" @@ -55,6 +58,12 @@ name = "hello_world_tcp" path = "examples/hello_world_tcp.rs" required-features = ["client", "server", "protocol_tcp", "serializer_bincode"] +[[example]] +name = "hello_world_tcp_encrypted" +path = "examples/hello_world_tcp_encrypted.rs" +required-features = ["client", "server", "protocol_tcp", "serializer_bincode"] + + [[example]] name = "hello_world_udp" path = "examples/hello_world_udp.rs" diff --git a/README.md b/README.md index 64c7868..664efd2 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,15 @@ use bevy_slinet::ClientConfig; struct Config; impl ClientConfig for Config { - type ClientPacket = ClientPacket; - type ServerPacket = ServerPacket; + type ClientPacket = Packet; + type ServerPacket = Packet; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; type LengthSerializer = LittleEndian; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } } #[derive(Serialize, Deserialize, Debug)] @@ -87,11 +91,15 @@ use bevy_slinet::ServerConfig; struct Config; impl ServerConfig for Config { - type ClientPacket = ClientPacket; - type ServerPacket = ServerPacket; + type ClientPacket = Packet; + type ServerPacket = Packet; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; type LengthSerializer = LittleEndian; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } } #[derive(Serialize, Deserialize, Debug)] @@ -151,4 +159,5 @@ Note: you should implement keep-alive and disconnection systems yourself, or loo | `0.7` | `0.11` | | `0.8` | `0.12` | | `0.9` | `0.13` | +| `0.10` | `0.13` | | `main` | `0.13` | diff --git a/examples/hello_world_tcp.rs b/examples/hello_world_tcp.rs index dbe2a64..17d1950 100644 --- a/examples/hello_world_tcp.rs +++ b/examples/hello_world_tcp.rs @@ -1,6 +1,8 @@ +use std::sync::Arc; use std::time::Duration; use bevy::prelude::*; +use bevy_slinet::serializer::SerializerAdapter; use bincode::DefaultOptions; use serde::{Deserialize, Serialize}; @@ -17,7 +19,11 @@ impl ServerConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -25,7 +31,11 @@ impl ClientConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -40,12 +50,10 @@ enum ServerPacket { } fn main() { + let server_addr = "127.0.0.1:3000"; let server = std::thread::spawn(move || { App::new() - .add_plugins(( - MinimalPlugins, - ServerPlugin::::bind("127.0.0.1:3000"), - )) + .add_plugins((MinimalPlugins, ServerPlugin::::bind(server_addr))) .add_systems( Update, (server_new_connection_system, server_packet_receive_system), @@ -57,7 +65,7 @@ fn main() { let client = std::thread::spawn(move || { App::new() .add_plugins(MinimalPlugins) - .add_plugins(ClientPlugin::::connect("127.0.0.1:3000")) + .add_plugins(ClientPlugin::::connect(server_addr)) .add_systems(Update, client_packet_receive_system) .run(); }); diff --git a/examples/hello_world_tcp_encrypted.rs b/examples/hello_world_tcp_encrypted.rs new file mode 100644 index 0000000..28e0c09 --- /dev/null +++ b/examples/hello_world_tcp_encrypted.rs @@ -0,0 +1,150 @@ +use std::sync::{Arc, Mutex}; +use std::time::Duration; + +use bevy::prelude::*; +use bevy_slinet::serializer::SerializerAdapter; +use bevy_slinet::serializers::custom_crypt::{ + CustomCryptClientPacket, CustomCryptEngine, CustomCryptSerializer, CustomCryptServerPacket, + CustomSerializationError, +}; + +use serde::{Deserialize, Serialize}; + +use bevy_slinet::client::ClientPlugin; +use bevy_slinet::packet_length_serializer::LittleEndian; +use bevy_slinet::protocols::tcp::TcpProtocol; + +use bevy_slinet::server::{NewConnectionEvent, ServerPlugin}; +use bevy_slinet::{client, server, ClientConfig, ServerConfig}; + +struct Config; + +impl ServerConfig for Config { + type ClientPacket = CustomCryptClientPacket; + type ServerPacket = CustomCryptServerPacket; + type Protocol = TcpProtocol; + type SerializerError = CustomSerializationError; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::Mutable(Arc::new(Mutex::new(CustomCryptSerializer::< + CustomCryptEngine, + CustomCryptClientPacket, + CustomCryptServerPacket, + >::new( + CustomCryptEngine::default() + )))) + } + type LengthSerializer = LittleEndian; +} + +impl ClientConfig for Config { + type ClientPacket = CustomCryptClientPacket; + type ServerPacket = CustomCryptServerPacket; + type Protocol = TcpProtocol; + type SerializerError = CustomSerializationError; + + type LengthSerializer = LittleEndian; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::Mutable(Arc::new(Mutex::new(CustomCryptSerializer::< + CustomCryptEngine, + Self::ServerPacket, + Self::ClientPacket, + >::new( + CustomCryptEngine::default() + )))) + } +} + +#[derive(Serialize, Deserialize, Debug)] +enum ClientPacket { + String(String), +} + +#[derive(Serialize, Deserialize, Debug)] +enum ServerPacket { + String(String), +} + +fn main() { + let server_addr = "127.0.0.1:3000"; + let server = std::thread::spawn(move || { + App::new() + .add_plugins((MinimalPlugins, ServerPlugin::::bind(server_addr))) + .add_systems( + Update, + (server_new_connection_system, server_packet_receive_system), + ) + .run(); + }); + println!("Waiting 5000ms to make sure the server side has started"); + std::thread::sleep(Duration::from_millis(5000)); + let client = std::thread::spawn(move || { + App::new() + .add_plugins(MinimalPlugins) + .add_plugins(ClientPlugin::::connect(server_addr)) + .add_systems(Update, client_packet_receive_system) + .run(); + }); + let client2 = std::thread::spawn(move || { + App::new() + .add_plugins(MinimalPlugins) + .add_plugins(ClientPlugin::::connect(server_addr)) + .add_systems(Update, client2_packet_receive_system) + .run(); + }); + server.join().unwrap(); + client.join().unwrap(); + client2.join().unwrap(); +} + +fn server_new_connection_system(mut events: EventReader>) { + for event in events.read() { + event + .connection + .send(CustomCryptServerPacket::String("Hello, World!".to_string())) + .unwrap(); + } +} + +fn client_packet_receive_system(mut events: EventReader>) { + for event in events.read() { + match &event.packet { + CustomCryptServerPacket::String(s) => println!("Server -> Client: {s}"), + } + event + .connection + .send(CustomCryptClientPacket::String( + "Hello, Server!".to_string(), + )) + .unwrap(); + } +} + +fn client2_packet_receive_system(mut events: EventReader>) { + for event in events.read() { + match &event.packet { + CustomCryptServerPacket::String(s) => println!("Server -> Client2: {s}"), + } + event + .connection + .send(CustomCryptClientPacket::String( + "Hello, Server!, I'm Client2".to_string(), + )) + .unwrap(); + } +} + +fn server_packet_receive_system(mut events: EventReader>) { + for event in events.read() { + match &event.packet { + CustomCryptClientPacket::String(s) => println!("Server <- Client: {s}"), + } + event + .connection + .send(CustomCryptServerPacket::String( + "Hello, Client!".to_string(), + )) + .unwrap(); + } +} diff --git a/examples/hello_world_udp.rs b/examples/hello_world_udp.rs index b6e994f..a1fa37b 100644 --- a/examples/hello_world_udp.rs +++ b/examples/hello_world_udp.rs @@ -1,6 +1,8 @@ +use std::sync::Arc; use std::time::Duration; use bevy::prelude::*; +use bevy_slinet::serializer::SerializerAdapter; use bincode::DefaultOptions; use serde::{Deserialize, Serialize}; @@ -17,7 +19,11 @@ impl ServerConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = BigEndian; } @@ -25,7 +31,11 @@ impl ClientConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = BigEndian; } @@ -40,12 +50,10 @@ enum ServerPacket { } fn main() { + let server_addr = "127.0.0.1:3000"; let server = std::thread::spawn(move || { App::new() - .add_plugins(( - MinimalPlugins, - ServerPlugin::::bind("127.0.0.1:3000"), - )) + .add_plugins((MinimalPlugins, ServerPlugin::::bind(server_addr))) .add_systems( Update, (server_new_connection_system, server_packet_receive_system), @@ -57,7 +65,7 @@ fn main() { let client = std::thread::spawn(move || { App::new() .add_plugins(MinimalPlugins) - .add_plugins(ClientPlugin::::connect("127.0.0.1:3000")) + .add_plugins(ClientPlugin::::connect(server_addr)) .add_systems(Update, client_packet_receive_system) .run(); }); diff --git a/examples/lobby_and_battle_servers.rs b/examples/lobby_and_battle_servers.rs index 4976f18..2207874 100644 --- a/examples/lobby_and_battle_servers.rs +++ b/examples/lobby_and_battle_servers.rs @@ -35,11 +35,13 @@ use std::collections::HashMap; use std::marker::PhantomData; use std::net::SocketAddr; +use std::sync::Arc; use std::time::Duration; use bevy::log::LogPlugin; use bevy::prelude::*; use bevy::time::common_conditions::on_timer; +use bevy_slinet::serializer::SerializerAdapter; use bincode::DefaultOptions; use serde::{Deserialize, Serialize}; @@ -64,7 +66,11 @@ impl ServerConfig for LobbyConfig { type ClientPacket = LobbyClientPacket; type ServerPacket = LobbyServerPacket; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -72,7 +78,11 @@ impl ClientConfig for LobbyConfig { type ClientPacket = LobbyClientPacket; type ServerPacket = LobbyServerPacket; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -82,7 +92,11 @@ impl ServerConfig for BattleConfig { type ClientPacket = BattleClientPacket; type ServerPacket = BattleServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -90,7 +104,11 @@ impl ClientConfig for BattleConfig { type ClientPacket = BattleClientPacket; type ServerPacket = BattleServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } diff --git a/examples/multiple_connections.rs b/examples/multiple_connections.rs index b45c8df..dd3262f 100644 --- a/examples/multiple_connections.rs +++ b/examples/multiple_connections.rs @@ -1,6 +1,8 @@ +use std::sync::Arc; use std::time::Duration; use bevy::prelude::*; +use bevy_slinet::serializer::SerializerAdapter; use bincode::DefaultOptions; use serde::{Deserialize, Serialize}; @@ -17,7 +19,11 @@ impl ServerConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } @@ -25,7 +31,11 @@ impl ClientConfig for Config { type ClientPacket = ClientPacket; type ServerPacket = ServerPacket; type Protocol = UdpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } type LengthSerializer = LittleEndian; } diff --git a/src/client.rs b/src/client.rs index 67c3e79..ed6bf6b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -16,12 +16,20 @@ use crate::connection::{ use crate::protocol::ReadStream; use crate::protocol::WriteStream; use crate::protocol::{NetworkStream, ReceiveError}; +use crate::serializer::Serializer; use crate::{ClientConfig, Protocol, SystemSets}; /// Client-side connection to a server. pub type ClientConnection = EcsConnection<::ClientPacket>; -/// List of client-side connections to a server. +type RawClientConnection = RawConnection< + ::ServerPacket, + ::ClientPacket, + <::Protocol as Protocol>::ClientStream, + ::SerializerError, + ::LengthSerializer, +>; +/// List of client-side connections to a server. #[derive(Resource)] pub struct ClientConnections(Vec>); impl ClientConnections { @@ -174,17 +182,12 @@ struct ConnectionReceiver( UnboundedReceiver<(SocketAddr, ClientConnection)>, ); -#[derive(Resource)] #[allow(clippy::type_complexity)] +#[derive(Resource)] struct DisconnectionReceiver( UnboundedReceiver<( - ReceiveError< - Config::ServerPacket, - Config::ClientPacket, - Config::Serializer, - Config::LengthSerializer, - >, + ReceiveError, SocketAddr, )>, PhantomData, @@ -214,7 +217,7 @@ fn setup_system(mut commands: Commands) { let (tx, rx) = tokio::sync::mpsc::unbounded_channel(); match create_connection::( address, - Config::Serializer::default(), + Arc::new(Config::build_serializer()), Config::LengthSerializer::default(), rx, ) @@ -250,8 +253,6 @@ fn setup_system(mut commands: Commands) { packet_length_serializer, mut packets_rx, id, - _receive_packet, - _send_packet, } = connection; let pack_tx2 = pack_tx.clone(); let disc_tx2 = disc_tx.clone(); @@ -262,7 +263,7 @@ fn setup_system(mut commands: Commands) { tokio::spawn(async move { loop { tokio::select! { - result = read.receive(&*serializer2, &*packet_length_serializer2) => { + result = read.receive(Arc::clone(&serializer2), &*packet_length_serializer2) => { match result { Ok(packet) => { log::trace!("({id:?}) Received packet {packet:?}"); @@ -294,7 +295,7 @@ fn setup_system(mut commands: Commands) { while let Some(packet) = packets_rx.recv().await { log::trace!("({id:?}) Sending packet {:?}", packet); match write - .send(packet, &*serializer, &*packet_length_serializer) + .send(packet, Arc::clone(&serializer), &*packet_length_serializer) .await { Ok(()) => (), @@ -318,21 +319,14 @@ fn connection_request_system( } } -#[allow(clippy::type_complexity)] pub(crate) async fn create_connection( addr: SocketAddr, - serializer: Config::Serializer, + serializer: Arc< + dyn Serializer, + >, packet_length_serializer: Config::LengthSerializer, packet_rx: UnboundedReceiver, -) -> io::Result< - RawConnection< - Config::ServerPacket, - Config::ClientPacket, - ::ClientStream, - Config::Serializer, - Config::LengthSerializer, - >, -> { +) -> io::Result> { Ok(RawConnection::new( Config::Protocol::connect_to_server(addr).await?, serializer, @@ -399,12 +393,7 @@ pub struct ConnectionEstablishEvent { #[derive(Event)] pub struct DisconnectionEvent { /// The error. - pub error: ReceiveError< - Config::ServerPacket, - Config::ClientPacket, - Config::Serializer, - Config::LengthSerializer, - >, + pub error: ReceiveError, /// A server's IP address. pub address: SocketAddr, _marker: PhantomData, diff --git a/src/connection.rs b/src/connection.rs index f928253..4b20ec2 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -1,8 +1,8 @@ //! This module contains structs that are used connection handling. +use std::error::Error; use std::fmt::{Debug, Formatter}; use std::future::Future; -use std::marker::PhantomData; use std::net::SocketAddr; use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -88,31 +88,29 @@ where } } -pub(crate) struct RawConnection +pub(crate) struct RawConnection where ReceivingPacket: Send + Sync + Debug + 'static, SendingPacket: Send + Sync + Debug + 'static, NS: NetworkStream, - S: Serializer, + SE: Error + Send + Sync, LS: PacketLengthSerializer, { pub disconnect_task: DisconnectTask, pub stream: NS, - pub serializer: Arc, + pub serializer: Arc>, pub packet_length_serializer: Arc, pub packets_rx: UnboundedReceiver, pub id: ConnectionId, - pub _receive_packet: PhantomData, - pub _send_packet: PhantomData, } -impl Debug - for RawConnection +impl Debug + for RawConnection where ReceivingPacket: Send + Sync + Debug + 'static, SendingPacket: Send + Sync + Debug + 'static, NS: NetworkStream, - S: Serializer, + SE: Error + Send + Sync, LS: PacketLengthSerializer, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -130,7 +128,6 @@ where /// counter that increments for every clientside/serverside connection. #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, bevy::ecs::component::Component)] pub struct ConnectionId(usize); - impl Debug for ConnectionId { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "#{}", self.0) @@ -145,6 +142,10 @@ impl ConnectionId { ConnectionId(CONNECTION_ID.fetch_add(1, Ordering::Relaxed)) } + // Allows to convert ConnectionId for more flexible usage + pub fn read(&self) -> usize { + self.0 + } } pub(crate) static MAX_PACKET_SIZE: AtomicUsize = AtomicUsize::new(usize::MAX); @@ -156,31 +157,29 @@ pub(crate) static MAX_PACKET_SIZE: AtomicUsize = AtomicUsize::new(usize::MAX); #[derive(Copy, Clone, Resource)] pub struct MaxPacketSize(pub usize); -impl - RawConnection +impl + RawConnection where ReceivingPacket: Send + Sync + Debug + 'static, SendingPacket: Send + Sync + Debug + 'static, NS: NetworkStream, - S: Serializer, + SE: Error + Send + Sync, LS: PacketLengthSerializer, { #[cfg(feature = "client")] pub fn new( stream: NS, - serializer: S, + serializer: Arc>, packet_length_serializer: LS, packets_rx: UnboundedReceiver, ) -> Self { Self { disconnect_task: DisconnectTask::default(), stream, - serializer: Arc::new(serializer), + serializer, packet_length_serializer: Arc::new(packet_length_serializer), packets_rx, id: ConnectionId::next(), - _receive_packet: PhantomData, - _send_packet: PhantomData, } } diff --git a/src/lib.rs b/src/lib.rs index faaba61..7aa45d6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,12 @@ #![cfg_attr(not(debug_assertions), deny(missing_docs))] #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] -use std::fmt::Debug; +use std::{error::Error, fmt::Debug}; use crate::packet_length_serializer::PacketLengthSerializer; use crate::protocol::Protocol; -use crate::serializer::Serializer; use bevy::prelude::SystemSet; +use serializer::SerializerAdapter; #[cfg(feature = "client")] pub mod client; @@ -20,9 +20,10 @@ pub mod serializers; #[cfg(feature = "server")] pub mod server; -// Tests work fine on my pc but fail on CI, idk how to make them pass, networking speed depends on machine #[cfg(test)] mod tests; +#[cfg(test)] +mod tests_mut_serializer; /// [`SystemSets`](bevy::ecs::schedule::SystemSet) in [`bevy`] are used for system ordering. /// See [System Order of Execution][cheatbook_order] on unofficial bevy cheatbook for details. @@ -52,8 +53,10 @@ pub trait ServerConfig: Send + Sync + 'static { type ServerPacket: Send + Sync + Debug + 'static; /// The connection's protocol. type Protocol: Protocol; + type SerializerError: Error + Send + Sync; /// A packet serializer. - type Serializer: Serializer + Default; + fn build_serializer( + ) -> SerializerAdapter; /// A packet length serializer type LengthSerializer: PacketLengthSerializer + Default; } @@ -66,8 +69,10 @@ pub trait ClientConfig: Send + Sync + 'static { type ServerPacket: Send + Sync + Debug + 'static; /// The connection's protocol. type Protocol: Protocol; + type SerializerError: Error + Send + Sync; /// A packet serializer. - type Serializer: Serializer + Default; + fn build_serializer( + ) -> SerializerAdapter; /// A packet length serializer type LengthSerializer: PacketLengthSerializer + Default; } diff --git a/src/protocol.rs b/src/protocol.rs index f0add8e..a4af583 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -4,16 +4,19 @@ //! Built-in protocols are listed in the [`protocols`](crate::protocols) module. use io::Write; +use std::error::Error; use std::fmt::{Debug, Formatter}; use std::io; use std::net::SocketAddr; use std::sync::atomic::Ordering; +use std::sync::Arc; use async_trait::async_trait; use crate::connection::MAX_PACKET_SIZE; use crate::packet_length_serializer::PacketLengthDeserializationError; -use crate::{PacketLengthSerializer, Serializer}; +use crate::serializer::Serializer; +use crate::PacketLengthSerializer; /// In order to simplify protocol switching and implementation, there is a [`Protocol`] trait. /// Implement it or use built-in [`protocols`](crate::protocols). @@ -95,13 +98,13 @@ pub trait ReadStream: Send + Sync + 'static { /// You shouldn't override this method unless you know what you're doing. async fn receive( &mut self, - serializer: &S, + serializer: Arc, length_serializer: &LS, - ) -> Result> + ) -> Result> where ReceivingPacket: Send + Sync + Debug + 'static, SendingPacket: Send + Sync + Debug + 'static, - S: Serializer, + S: Serializer + ?Sized, LS: PacketLengthSerializer, { let mut buf = Vec::new(); @@ -134,17 +137,15 @@ pub trait ReadStream: Send + Sync + 'static { } /// An error that may happen when receiving packets. -pub enum ReceiveError +pub enum ReceiveError where - ReceivingPacket: Send + Sync + Debug + 'static, - SendingPacket: Send + Sync + Debug + 'static, - S: Serializer, + SerializationError: Error + Send + Sync, LS: PacketLengthSerializer, { /// IO error. Io(io::Error), /// Deserialization error. - Deserialization(S::Error), + Deserialization(SerializationError), /// Length deserialization error. LengthDeserialization(LS::Error), /// The packet size is too large (set by [`MaxPacketSize`](crate::connection::MaxPacketSize) resource). @@ -155,12 +156,9 @@ where IntentionalDisconnection, } -impl Debug - for ReceiveError +impl Debug for ReceiveError where - ReceivingPacket: Send + Sync + Debug + 'static, - SendingPacket: Send + Sync + Debug + 'static, - S: Serializer, + SerializationError: Error + Send + Sync, LS: PacketLengthSerializer, { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -191,13 +189,13 @@ pub trait WriteStream: Send + Sync + 'static { async fn send( &mut self, packet: SendingPacket, - serializer: &S, + serializer: Arc, length_serializer: &LS, ) -> io::Result<()> where ReceivingPacket: Send + Sync + Debug + 'static, SendingPacket: Send + Sync + Debug + 'static, - S: Serializer, + S: Serializer + ?Sized, LS: PacketLengthSerializer, { let serialized = serializer diff --git a/src/protocols/udp.rs b/src/protocols/udp.rs index 79a21d1..58987f6 100644 --- a/src/protocols/udp.rs +++ b/src/protocols/udp.rs @@ -104,10 +104,11 @@ impl NetworkStream for UdpServerStream { type WriteHalf = UdpServerWriteHalf; async fn into_split(self) -> io::Result<(Self::ReadHalf, Self::WriteHalf)> { + let peer_addr = self.peer_addr(); Ok(( UdpServerReadHalf(self.task.clone()), UdpServerWriteHalf { - peer_addr: self.peer_addr(), + peer_addr, socket: self.socket, }, )) diff --git a/src/serializer.rs b/src/serializer.rs index 8760af1..dcded76 100644 --- a/src/serializer.rs +++ b/src/serializer.rs @@ -1,14 +1,78 @@ -//! Implement [`Serializer`] trait or use built-in serializers listed in the [`serializers`](crate::serializers) module. +//! Implement the [`ReadOnlySerializer`] or [`MutableSerializer`] trait for serialzer and build it in config refer +//! BincodeSerializer to check how to do this. -use std::error::Error; +use core::fmt::Debug; +use std::{ + error::Error, + sync::{Arc, Mutex}, +}; -/// A packet serializer -pub trait Serializer: Send + Sync + 'static { - /// A serializer's error that may be returned from [`Self::deserialize`]. - type Error: Error + Send + Sync; +// Serializer trait defines the core functionality for serializing and deserializing packets, +// ensuring compatibility with multi-threaded contexts as it requires the Send and Sync bounds. +pub trait Serializer: Send + Sync + 'static +where + ReceivingPacket: Send + Sync + Debug + 'static, + SendingPacket: Send + Sync + Debug + 'static, +{ + type Error: Error + Send + Sync; // Defines a custom error type that must also be thread-safe. + + // Serializes a packet into bytes to be sent over a network. The method takes ownership of the packet + // and a peer address, returning either a byte vector or an error if serialization fails. + fn serialize(&self, packet: SendingPacket) -> Result, Self::Error>; + // Deserializes bytes received from a network back into a packet structure. + fn deserialize(&self, data: &[u8]) -> Result; +} + +// SerializerAdapter allows for flexibility in serializer implementation; supporting both immutable +// and mutable serialization strategies. +pub enum SerializerAdapter +where + E: Error + Send + Sync, +{ + ReadOnly(Arc>), + Mutable(Arc>>), +} + +// Implementing Serializer for SerializerAdapter provides a concrete example of polymorphism, +// enabling different serialization strategies under the same interface. +impl Serializer + for SerializerAdapter +where + SendingPacket: Send + Sync + Debug + 'static, + ReceivingPacket: Send + Sync + Debug + 'static, + E: Error + Send + Sync + 'static, +{ + type Error = E; - /// Serialize a packet - fn serialize(&self, t: SendingPacket) -> Result, Self::Error>; - /// Deserialize a packet + // Depending on the adapter's type, serialization can either directly pass the data or + // require locking a mutex to ensure thread-safety in mutable contexts. + fn serialize(&self, packet: SendingPacket) -> Result, Self::Error> { + match self { + SerializerAdapter::ReadOnly(serializer) => serializer.serialize(packet), + SerializerAdapter::Mutable(serializer) => serializer.lock().unwrap().serialize(packet), + } + } + + // Deserialization behaves similarly to serialization, respecting the adapter's type. + fn deserialize(&self, data: &[u8]) -> Result { + match self { + SerializerAdapter::ReadOnly(serializer) => serializer.deserialize(data), + SerializerAdapter::Mutable(serializer) => serializer.lock().unwrap().deserialize(data), + } + } +} + +/// ReadOnlySerializer is designed for scenarios where the data structure does not change, +/// ensuring efficient and thread-safe operations without the overhead of locking mechanisms. +pub trait ReadOnlySerializer: Send + Sync + 'static { + type Error: Error + Send + Sync; + fn serialize(&self, packet: SendingPacket) -> Result, Self::Error>; fn deserialize(&self, buffer: &[u8]) -> Result; } +/// MutableSerializer supports scenarios where serialization state needs to be altered during operation, +/// useful in cases like cryptographic transformations where state is critical. +pub trait MutableSerializer: Send + Sync + 'static { + type Error: Error + Send + Sync; + fn serialize(&mut self, p: SendingPacket) -> Result, Self::Error>; + fn deserialize(&mut self, buf: &[u8]) -> Result; +} diff --git a/src/serializers/bincode.rs b/src/serializers/bincode.rs index 4d9f44e..6f94981 100644 --- a/src/serializers/bincode.rs +++ b/src/serializers/bincode.rs @@ -1,6 +1,6 @@ //! A [`bincode`]-based packet serializer. You can enable it by adding `serializer_bincode` feature. -use crate::serializer::Serializer; +use crate::serializer::ReadOnlySerializer; pub use bincode::{DefaultOptions, Options}; use serde::{Deserialize, Serialize}; @@ -10,7 +10,7 @@ pub struct BincodeSerializer(pub O) where O: Options + Send + Sync + 'static; -impl Serializer +impl ReadOnlySerializer for BincodeSerializer where ReceivingPacket: for<'de> Deserialize<'de>, diff --git a/src/serializers/custom_crypt.rs b/src/serializers/custom_crypt.rs new file mode 100644 index 0000000..d5d9307 --- /dev/null +++ b/src/serializers/custom_crypt.rs @@ -0,0 +1,222 @@ +//! A custom packet serializer capable of handling encryption and decryption. +//! Demonstrates usage with mutable serializers that can mutate their internal state. + +use std::{ + error::Error, + fmt::{self, Display}, + marker::PhantomData, +}; + +use crate::serializer::MutableSerializer; +use serde::{Deserialize, Serialize}; + +/// Represents custom packets sent from the client, allowing different types of content. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub enum CustomCryptClientPacket { + String(String), +} + +/// Represents custom packets received by the server, allowing different types of content. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub enum CustomCryptServerPacket { + String(String), +} + +// Define a custom error type for serialization errors. +#[derive(Debug)] +pub struct CustomSerializationError; +impl Display for CustomSerializationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SerializationFailed") + } +} + +impl Error for CustomSerializationError {} + +/// Defines a trait for cryptographic engines with methods for packet encryption and decryption. +pub trait CryptEngine: Default { + fn encrypt(&mut self, packet: SendingPacket) -> Result, CustomSerializationError>; + fn decrypt(&mut self, packet: &[u8]) -> Result; +} + +/// A simple key pair structure used for XOR encryption operations. +#[derive(Debug, Default, Clone)] +pub struct ExampleKeyPair(u64, u64); + +/// A cryptographic engine implementing XOR encryption, typically not secure but used for demonstration. +#[derive(Debug, Default, Clone)] +pub struct CustomCryptEngine { + key_pair: ExampleKeyPair, +} + +impl CustomCryptEngine { + /// Encrypts data using XOR operation and rotates the key to simulate a stream cipher. + fn xor_encrypt(&mut self, data: Vec) -> Vec { + let mut key = self.key_pair.0; + let encrypted: Vec = data + .into_iter() + .map(|byte| { + let result = byte ^ (key as u8); + key = key.wrapping_add(1); + result + }) + .collect(); + self.key_pair.0 = key; + encrypted + } + + /// Decrypts data using XOR operation, ensuring the key is rotated in the same manner as encryption. + fn xor_decrypt(&mut self, data: Vec) -> Vec { + let mut key = self.key_pair.1; + let decrypted: Vec = data + .into_iter() + .map(|byte| { + let result = byte ^ (key as u8); + key = key.wrapping_add(1); + result + }) + .collect(); + self.key_pair.1 = key; + decrypted + } +} + +/// Implements the encryption and decryption processes for specified packet types using the XOR method. +/// This implement server-side encryption +impl CryptEngine for CustomCryptEngine { + fn encrypt( + &mut self, + packet: CustomCryptServerPacket, + ) -> Result, CustomSerializationError> { + let packet_data = bincode::serialize(&packet).unwrap(); + let encrypted_data = self.xor_encrypt(packet_data); + Ok(encrypted_data) + } + + fn decrypt( + &mut self, + packet: &[u8], + ) -> Result { + let decrypted_data = self.xor_decrypt(packet.to_vec()); + let packet = bincode::deserialize(&decrypted_data).unwrap(); + Ok(packet) + } +} +// This is the client-side encryption +impl CryptEngine for CustomCryptEngine { + fn encrypt( + &mut self, + packet: CustomCryptClientPacket, + ) -> Result, CustomSerializationError> { + let packet_data = bincode::serialize(&packet).unwrap(); + let encrypted_data = self.xor_encrypt(packet_data); + Ok(encrypted_data) + } + + fn decrypt( + &mut self, + packet: &[u8], + ) -> Result { + let decrypted_data = self.xor_decrypt(packet.to_vec()); + let packet = bincode::deserialize(&decrypted_data).unwrap(); + Ok(packet) + } +} + +/// A serializer that integrates encryption, using a cryptographic engine to ensure secure data transmission. +#[derive(Default, Clone)] +pub struct CustomCryptSerializer +where + C: Send + Sync + 'static + CryptEngine, +{ + crypt_engine: C, + _client: PhantomData, + _server: PhantomData, +} +impl< + C: Send + Sync + 'static + CryptEngine, + SendingPacket, + ReceivingPacket, + > CustomCryptSerializer +where + C: Send + Sync + 'static + CryptEngine, +{ + pub fn new(crypt_engine: C) -> Self { + Self { + crypt_engine, + _client: PhantomData, + _server: PhantomData, + } + } +} +impl MutableSerializer + for CustomCryptSerializer +where + C: Send + Sync + 'static + CryptEngine, + ReceivingPacket: Send + Sync + 'static, + SendingPacket: Send + Sync + 'static, +{ + type Error = CustomSerializationError; + + /// Serializes a packet into a byte vector. + fn serialize(&mut self, packet: SendingPacket) -> Result, Self::Error> { + Ok(self.crypt_engine.encrypt(packet).unwrap()) + } + + /// Deserializes a packet from a byte slice + fn deserialize(&mut self, buffer: &[u8]) -> Result { + match self.crypt_engine.decrypt(buffer) { + Ok(encrypted) => Ok(encrypted), + Err(e) => { + log::error!("{}", e); + Err(e) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_xor_encrypt_decrypt() { + let mut engine = CustomCryptEngine::default(); + let data = vec![1, 2, 3, 4, 5]; + + let encrypted = engine.xor_encrypt(data.clone()); + assert_ne!( + encrypted, data, + "Encrypted data should not be equal to original data" + ); + + let decrypted = engine.xor_decrypt(encrypted); + assert_eq!( + decrypted, data, + "Decrypted data should be equal to original data" + ); + } + + #[test] + fn test_crypt_engine_encrypt_decrypt() { + let mut engine = CustomCryptEngine::default(); + let client_packet = CustomCryptClientPacket::String("Hello, Server!".to_string()); + let server_packet = CustomCryptServerPacket::String("Hello, Client!".to_string()); + + // Client packet encryption and decryption + let encrypted = engine.encrypt(client_packet.clone()).unwrap(); + let decrypted: CustomCryptClientPacket = engine.decrypt(&encrypted).unwrap(); + assert_eq!( + decrypted, client_packet, + "Decrypted client packet should be equal to original packet" + ); + + // Server packet encryption and decryption + let encrypted = engine.encrypt(server_packet.clone()).unwrap(); + let decrypted: CustomCryptServerPacket = engine.decrypt(&encrypted).unwrap(); + assert_eq!( + decrypted, server_packet, + "Decrypted server packet should be equal to original packet" + ); + } +} diff --git a/src/serializers/mod.rs b/src/serializers/mod.rs index f1e3f19..b9fa794 100644 --- a/src/serializers/mod.rs +++ b/src/serializers/mod.rs @@ -2,3 +2,4 @@ #[cfg(feature = "serializer_bincode")] pub mod bincode; +pub mod custom_crypt; diff --git a/src/server.rs b/src/server.rs index ee3a92c..1b3af6b 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,5 @@ //! Server part of the plugin. You can enable it by adding `server` feature. -use core::default::Default; use std::marker::PhantomData; use std::net::{SocketAddr, ToSocketAddrs}; use std::sync::Arc; @@ -18,9 +17,17 @@ use crate::{ServerConfig, SystemSets}; /// Server-side connection to a server. pub type ServerConnection = EcsConnection<::ServerPacket>; - +type RawServerConnection = ( + RawConnection< + ::ClientPacket, + ::ServerPacket, + <::Protocol as Protocol>::ServerStream, + ::SerializerError, + ::LengthSerializer, + >, + ServerConnection, +); /// List of server-side connections to a server. - #[derive(Resource)] pub struct ServerConnections(Vec>); impl ServerConnections { @@ -28,7 +35,6 @@ impl ServerConnections { Self(Vec::new()) } } - impl std::ops::Deref for ServerConnections { type Target = Vec>; @@ -36,7 +42,6 @@ impl std::ops::Deref for ServerConnections { &self.0 } } - impl std::ops::DerefMut for ServerConnections { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 @@ -109,12 +114,7 @@ struct ConnectionReceiver( #[derive(Resource)] struct DisconnectionReceiver( UnboundedReceiver<( - ReceiveError< - Config::ClientPacket, - Config::ServerPacket, - Config::Serializer, - Config::LengthSerializer, - >, + ReceiveError, ServerConnection, )>, ); @@ -130,28 +130,9 @@ fn create_setup_system(address: SocketAddr) -> impl Fn(Com move |mut commands: Commands| { let (conn_tx, conn_rx) = tokio::sync::mpsc::unbounded_channel(); - #[allow(clippy::type_complexity)] let (conn_tx2, mut conn_rx2): ( - UnboundedSender<( - RawConnection< - Config::ClientPacket, - Config::ServerPacket, - ::ServerStream, - Config::Serializer, - Config::LengthSerializer, - >, - ServerConnection, - )>, - UnboundedReceiver<( - RawConnection< - Config::ClientPacket, - Config::ServerPacket, - ::ServerStream, - Config::Serializer, - Config::LengthSerializer, - >, - ServerConnection, - )>, + UnboundedSender>, + UnboundedReceiver>, ) = tokio::sync::mpsc::unbounded_channel(); let (disc_tx, disc_rx) = tokio::sync::mpsc::unbounded_channel(); let (pack_tx, pack_rx) = tokio::sync::mpsc::unbounded_channel(); @@ -176,8 +157,6 @@ fn create_setup_system(address: SocketAddr) -> impl Fn(Com packet_length_serializer, mut packets_rx, id, - _receive_packet, - _send_packet, } = connection; let (mut read, mut write) = stream.into_split().await.expect("Couldn't split stream"); @@ -191,7 +170,7 @@ fn create_setup_system(address: SocketAddr) -> impl Fn(Com // `select!` handles intentional disconnections (ecs_connection.disconnect()). // AsyncReadExt::read_exact is not cancel-safe and loses data, but we don't need that data anymore tokio::select! { - result = read.receive(&*serializer2, &*packet_length_serializer2) => { + result = read.receive(Arc::clone(&serializer2), &*packet_length_serializer2) => { match result { Ok(packet) => { log::trace!("({id:?}) Received packet {:?}", packet); @@ -220,7 +199,7 @@ fn create_setup_system(address: SocketAddr) -> impl Fn(Com while let Some(packet) = packets_rx.recv().await { log::trace!("({id:?}) Sending packet {:?}", packet); match write - .send(packet, &*serializer, &*packet_length_serializer) + .send(packet, Arc::clone(&serializer), &*packet_length_serializer) .await { Ok(()) => (), @@ -250,12 +229,10 @@ fn create_setup_system(address: SocketAddr) -> impl Fn(Com let connection = RawConnection { disconnect_task: disconnect_task.clone(), stream: connection, - serializer: Arc::new(Default::default()), + serializer: Arc::new(Config::build_serializer()), packet_length_serializer: Arc::new(Default::default()), id: ConnectionId::next(), packets_rx: rx, - _receive_packet: PhantomData, - _send_packet: PhantomData, }; let ecs_conn = EcsConnection { disconnect_task, @@ -294,12 +271,7 @@ pub struct NewConnectionEvent { #[derive(Event)] pub struct DisconnectionEvent { /// The error. - pub error: ReceiveError< - Config::ClientPacket, - Config::ServerPacket, - Config::Serializer, - Config::LengthSerializer, - >, + pub error: ReceiveError, /// The connection. pub connection: ServerConnection, } diff --git a/src/tests.rs b/src/tests.rs index d6f40d6..9db2d5c 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -2,6 +2,7 @@ use crate::client; use crate::client::{ClientConnection, ClientPlugin, ConnectionEstablishEvent}; use crate::packet_length_serializer::LittleEndian; use crate::protocols::tcp::TcpProtocol; +use crate::serializer::SerializerAdapter; use crate::serializers::bincode::BincodeSerializer; use crate::server::{NewConnectionEvent, ServerConnections, ServerPlugin}; use crate::{server, ClientConfig, ServerConfig}; @@ -10,6 +11,7 @@ use bevy::ecs::event::Events; use bevy::prelude::{EventReader, Update}; use bincode::DefaultOptions; use serde::{Deserialize, Serialize}; +use std::sync::Arc; use std::time::Duration; #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)] @@ -21,25 +23,39 @@ impl ServerConfig for TcpConfig { type ClientPacket = Packet; type ServerPacket = Packet; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + + type SerializerError = bincode::Error; + type LengthSerializer = LittleEndian; + + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } } impl ClientConfig for TcpConfig { type ClientPacket = Packet; type ServerPacket = Packet; type Protocol = TcpProtocol; - type Serializer = BincodeSerializer; + type SerializerError = bincode::Error; + type LengthSerializer = LittleEndian; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::ReadOnly(Arc::new(BincodeSerializer::::default())) + } } #[test] fn tcp_connection() { + let server_addr = "127.0.0.1:3000"; + let mut app_server = App::new(); - app_server.add_plugins(ServerPlugin::::bind("127.0.0.1:3000")); + app_server.add_plugins(ServerPlugin::::bind(server_addr)); let mut app_client = App::new(); - app_client.add_plugins(ClientPlugin::::connect("127.0.0.1:3000")); + app_client.add_plugins(ClientPlugin::::connect(server_addr)); app_server.update(); // bind app_client.update(); // connect @@ -68,9 +84,10 @@ fn tcp_connection() { fn tcp_packets() { let client_to_server_packet = Packet(42); let server_to_client_packet = Packet(24); + let server_addr = "127.0.0.1:3007"; let mut app_server = App::new(); - app_server.add_plugins(ServerPlugin::::bind("127.0.0.1:3001")); + app_server.add_plugins(ServerPlugin::::bind(server_addr)); app_server.add_systems( Update, move |mut events: EventReader>| { @@ -84,7 +101,7 @@ fn tcp_packets() { ); let mut app_client = App::new(); - app_client.add_plugins(ClientPlugin::::connect("127.0.0.1:3001")); + app_client.add_plugins(ClientPlugin::::connect(server_addr)); app_client.add_systems( Update, move |mut events: EventReader>| { diff --git a/src/tests_mut_serializer.rs b/src/tests_mut_serializer.rs new file mode 100644 index 0000000..105b3e5 --- /dev/null +++ b/src/tests_mut_serializer.rs @@ -0,0 +1,185 @@ +use bevy::app::{App, Update}; +use bevy::ecs::event::{EventReader, Events}; + +use crate::client::{self, ClientConnection, ClientPlugin, ConnectionEstablishEvent}; +use crate::packet_length_serializer::LittleEndian; +use crate::protocols::tcp::TcpProtocol; +use crate::serializer::SerializerAdapter; +use crate::serializers::custom_crypt::{ + CustomCryptClientPacket, CustomCryptEngine, CustomCryptSerializer, CustomCryptServerPacket, + CustomSerializationError, +}; +use crate::server::{self, NewConnectionEvent, ServerConnections, ServerPlugin}; +use crate::{ClientConfig, ServerConfig}; + +use std::sync::{Arc, Mutex}; + +struct TcpConfig; + +impl ServerConfig for TcpConfig { + type ClientPacket = CustomCryptClientPacket; + type ServerPacket = CustomCryptServerPacket; + type Protocol = TcpProtocol; + + type SerializerError = CustomSerializationError; + + type LengthSerializer = LittleEndian; + + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::Mutable(Arc::new(Mutex::new(CustomCryptSerializer::< + CustomCryptEngine, + Self::ClientPacket, + Self::ServerPacket, + >::new( + CustomCryptEngine::default() + )))) + } +} + +impl ClientConfig for TcpConfig { + type ClientPacket = CustomCryptClientPacket; + type ServerPacket = CustomCryptServerPacket; + type Protocol = TcpProtocol; + type SerializerError = CustomSerializationError; + + type LengthSerializer = LittleEndian; + fn build_serializer( + ) -> SerializerAdapter { + SerializerAdapter::Mutable(Arc::new(Mutex::new(CustomCryptSerializer::< + CustomCryptEngine, + Self::ServerPacket, + Self::ClientPacket, + >::new( + CustomCryptEngine::default() + )))) + } +} + +#[test] +fn tcp_connection() { + let srv_addr = "127.0.0.1:3004"; + let mut app_server = App::new(); + app_server.add_plugins(ServerPlugin::::bind(srv_addr)); + + let mut app_client = App::new(); + app_client.add_plugins(ClientPlugin::::connect(srv_addr)); + + app_server.update(); // bind + app_client.update(); // connect + std::thread::sleep(std::time::Duration::from_secs(1)); + app_client.update(); // add connection resource + app_server.update(); // handle connection + + assert!( + app_client + .world + .get_resource::>() + .is_some(), + "No ClientConnection resource found" + ); + assert_eq!( + app_server + .world + .get_resource::>() + .unwrap() + .len(), + 1, + ) +} + +#[test] +fn tcp_packets() { + // Define server and client configurations + let srv_addr = "127.0.0.1:3005"; + + // Setup server and client applications + let mut app_server = setup_server_app(srv_addr); + let mut app_client = setup_client_app(srv_addr, "Hello, Server!"); + + // Simulate application lifecycle + run_simulation(&mut app_server, &mut app_client); + + // Check events and packets + check_server_received_packets(&app_server); + check_client_received_packets(&app_client, "Hello, Client!"); +} + +fn server_receive_system(mut events: EventReader>) { + let server_to_client_packet = CustomCryptServerPacket::String(String::from("Hello, Client!")); + for event in events.read() { + event + .connection + .send(server_to_client_packet.clone()) + .expect("Failed to send packet to client"); + } +} + +fn setup_server_app(srv_addr: &str) -> App { + let mut app = App::new(); + app.add_plugins(ServerPlugin::::bind(srv_addr)); + app.add_systems(Update, server_receive_system); + app +} + +fn setup_client_app(srv_addr: &str, message: &str) -> App { + let packet = CustomCryptClientPacket::String(String::from(message)); + let mut app = App::new(); + app.add_plugins(ClientPlugin::::connect(srv_addr)); + app.add_systems( + Update, + move |mut events: EventReader>| { + for event in events.read() { + event + .connection + .send(packet.clone()) + .expect("Failed to send packet"); + } + }, + ); + app +} + +// Simulate the test scenario +fn run_simulation(app_server: &mut App, app_client: &mut App) { + app_server.update(); + app_client.update(); + std::thread::sleep(std::time::Duration::from_secs(1)); + app_client.update(); + app_server.update(); + std::thread::sleep(std::time::Duration::from_secs(1)); + app_client.update(); + app_server.update(); +} + +fn check_server_received_packets(app_server: &App) { + let server_events = app_server + .world + .resource::>>(); + let mut server_reader = server_events.get_reader(); + let mut server_events_iter = server_reader.read(server_events); + + assert_eq!( + server_events_iter.next().map(|event| event.packet.clone()), + Some(CustomCryptClientPacket::String(String::from( + "Hello, Server!" + ))), + "Server did not receive packet from client 1" + ); +} + +fn check_client_received_packets(app_client: &App, expected_message: &str) { + let client_events = app_client + .world + .resource::>>(); + let mut client_reader = client_events.get_reader(); + let mut client_events_iter = client_reader.read(client_events); + + assert_eq!( + client_events_iter.next().map(|event| event.packet.clone()), + Some(CustomCryptServerPacket::String(String::from( + expected_message + ))), + "Client did not receive packet from server" + ); +}