From f02a26613d42d88c6c9c33a4ce599605d9977462 Mon Sep 17 00:00:00 2001 From: core Date: Sun, 17 May 2026 17:14:19 -0400 Subject: [PATCH] feat(netcode-rewrite): re-add networking --- Cargo.lock | 479 ++++++++++++++++++++- Cargo.toml | 3 + crates/unified/Cargo.toml | 4 + crates/unified/src/cli.rs | 60 ++- crates/unified/src/client/mod.rs | 59 ++- crates/unified/src/client/plugins.rs | 26 +- crates/unified/src/main.rs | 30 +- crates/unified/src/server/mod.rs | 123 +++++- crates/unified/src/server/player/join.rs | 2 + crates/unified/src/server/plugins.rs | 11 +- crates/unified/src/shared/mod.rs | 3 +- crates/unified/src/shared/net.rs | 3 + crates/unified/src/shared/plugins.rs | 12 +- crates/unified/src/universal_entrypoint.rs | 35 ++ crates/unified/src/wasm_entrypoint.rs | 22 +- 15 files changed, 790 insertions(+), 82 deletions(-) create mode 100644 crates/unified/src/shared/net.rs create mode 100644 crates/unified/src/universal_entrypoint.rs diff --git a/Cargo.lock b/Cargo.lock index c70de7a0e24819fd63758f6ba6905eb399b1c5ed..f9daf05a275b9f5b34ee85524987868995ed23b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,81 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +[[package]] +name = "aeronet" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c7a1ae9f2a25a6ce704cdcac85aa2d62f12cd34e8c6b5e259a400bfb4bde573" +dependencies = [ + "aeronet_io", + "aeronet_transport", + "bevy_app", +] + +[[package]] +name = "aeronet_io" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bdc30d6e77d611cffd0e6c418299de1eccf21696205de3368f75afc71413a98" +dependencies = [ + "anyhow", + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bytes", + "derive_more", + "log", +] + +[[package]] +name = "aeronet_transport" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460e73efd6b13dfd43992aff40d0a7a74202ae90b37dd31f4c0db7e368d10e63" +dependencies = [ + "aeronet_io", + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_reflect", + "bevy_time", + "bit-vec 0.8.0", + "derive_more", + "either", + "log", + "octs", + "ringbuf", + "typesize", +] + +[[package]] +name = "aeronet_websocket" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a721edcf893daea9bf5f565e81b58162c986b0c70d985d7a4ecf3e32e94edd" +dependencies = [ + "aeronet_io", + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bytes", + "cfg-if", + "derive_more", + "futures", + "js-sys", + "rcgen", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "tracing", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + [[package]] name = "ahash" version = "0.8.12" @@ -388,6 +463,34 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bevy" version = "0.18.1" @@ -1793,6 +1896,9 @@ name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "portable-atomic", +] [[package]] name = "calloop" @@ -1942,6 +2048,15 @@ dependencies = [ "web-time 0.2.4", ] +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + [[package]] name = "codespan-reporting" version = "0.12.0" @@ -2349,6 +2464,15 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + [[package]] name = "derive_builder" version = "0.11.2" @@ -2462,7 +2586,7 @@ dependencies = [ "subsecond", "thiserror 2.0.18", "tracing", - "tungstenite", + "tungstenite 0.28.0", ] [[package]] @@ -2552,6 +2676,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "dyn-clone" version = "1.0.20" @@ -2828,6 +2958,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent-sys" version = "4.1.0" @@ -2837,6 +2973,21 @@ dependencies = [ "libc", ] +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.32" @@ -2844,6 +2995,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -2852,6 +3004,17 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.32" @@ -2882,6 +3045,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + [[package]] name = "futures-task" version = "0.3.32" @@ -2894,9 +3063,13 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ + "futures-channel", "futures-core", + "futures-io", "futures-macro", + "futures-sink", "futures-task", + "memchr", "pin-project-lite", "slab", ] @@ -2931,6 +3104,17 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "getrandom" version = "0.3.4" @@ -3964,6 +4148,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + [[package]] name = "num-derive" version = "0.4.2" @@ -4300,6 +4490,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "octs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3beef54705459f4a421ff43b6c9b8381f5b84769e4ae69942783dd8918837b7" +dependencies = [ + "bytes", +] + [[package]] name = "offset-allocator" version = "0.2.0" @@ -4322,6 +4521,12 @@ version = "11.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + [[package]] name = "orbclient" version = "0.3.53" @@ -4451,6 +4656,16 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -4582,6 +4797,12 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "pp-rs" version = "0.2.1" @@ -4819,6 +5040,19 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "rcgen" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75e669e5202259b5314d1ea5397316ad400819437857b90861765f24c4cf80a2" +dependencies = [ + "pem", + "ring", + "rustls-pki-types", + "time", + "yasna", +] + [[package]] name = "rdst" version = "0.20.14" @@ -4922,6 +5156,31 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ringbuf" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe47b720588c8702e34b5979cb3271a8b1842c7cb6f57408efa70c779363488c" +dependencies = [ + "crossbeam-utils", + "portable-atomic", + "portable-atomic-util", +] + [[package]] name = "robust" version = "1.2.0" @@ -4995,6 +5254,54 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -5028,6 +5335,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -5053,6 +5369,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -5292,6 +5631,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + [[package]] name = "spade" version = "2.15.1" @@ -5351,6 +5700,9 @@ dependencies = [ name = "starkingdoms" version = "0.1.0" dependencies = [ + "aeronet", + "aeronet_transport", + "aeronet_websocket", "avian2d", "bevy", "bevy_common_assets", @@ -5415,6 +5767,12 @@ dependencies = [ "serde", ] +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "svg_fmt" version = "0.4.5" @@ -5565,6 +5923,25 @@ dependencies = [ "tikv-jemalloc-sys", ] +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + [[package]] name = "tiny-skia" version = "0.11.4" @@ -5615,6 +5992,46 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite 0.26.2", +] + [[package]] name = "toml" version = "0.9.12+spec-1.1.0" @@ -5793,6 +6210,25 @@ dependencies = [ "core_maths", ] +[[package]] +name = "tungstenite" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "tungstenite" version = "0.28.0" @@ -5828,6 +6264,26 @@ version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" +[[package]] +name = "typesize" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da66c62c5b7017a2787e77373c03e6a5aafde77a73bff1ff96e91cd2e128179" +dependencies = [ + "typesize-derive", +] + +[[package]] +name = "typesize-derive" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "536b6812192bda8551cfa0e52524e328c6a951b48e66529ee4522d6c721243d6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "typewit" version = "1.15.2" @@ -5876,6 +6332,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "utf-8" version = "0.7.6" @@ -7215,6 +7677,15 @@ dependencies = [ "qsv-tabwriter", ] +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + [[package]] name = "yazi" version = "0.2.1" @@ -7247,6 +7718,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + [[package]] name = "zmij" version = "1.0.21" diff --git a/Cargo.toml b/Cargo.toml index ec1a644861d9fbe798e5427a199626c328e02848..411fb9c19f7ff30c0e419f53e95e3d28bd27b9c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,9 @@ console_error_panic_hook = "0.1" test_each_file = "0.3.7" colored = "3" qsv-tabwriter = "2" +aeronet = { version = "0.20" } +aeronet_websocket = { version = "0.20" } +aeronet_transport = { version = "0.20" } [profile.dev] opt-level = 1 diff --git a/crates/unified/Cargo.toml b/crates/unified/Cargo.toml index bafc3ceea0caa6617aad410c511f54bb1cc6f4ac..e77ab9b6dc2c01c4621cbb6d144e9d7b925a0838 100644 --- a/crates/unified/Cargo.toml +++ b/crates/unified/Cargo.toml @@ -18,14 +18,18 @@ pico-args = { workspace = true } leafwing-input-manager = { workspace = true } good_lp = { workspace = true } web-time = { workspace = true } +aeronet = { workspace = true } +aeronet_transport = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] ctrlc = { workspace = true, optional = true } +aeronet_websocket = { workspace = true, features = ["client", "server"] } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm-bindgen = { workspace = true } tracing-web = { workspace = true } console_error_panic_hook = { workspace = true } +aeronet_websocket = { workspace = true, features = ["client"] } [features] native_dev = [ diff --git a/crates/unified/src/cli.rs b/crates/unified/src/cli.rs index 16e1d3e02d747a3e81f02213fb67b89fd7fe8f8d..3c58714de8badb24e06086207f239424a01ea9de 100644 --- a/crates/unified/src/cli.rs +++ b/crates/unified/src/cli.rs @@ -4,7 +4,14 @@ compile_error!("You need to enable one of native, wasm features"); compile_error!("You cannot enable both native and wasm features"); pub enum StkArgs { - Play, + Client { + server: String, + }, + #[cfg(not(target_arch = "wasm32"))] + Server { + bind_to: std::net::SocketAddr, + with_client: bool + }, } #[cfg(not(target_arch = "wasm32"))] @@ -21,12 +28,27 @@ pub fn parse_args() -> StkArgs { std::process::exit(0); } - let subcommand = pargs.subcommand().unwrap(); + let Some(subcommand) = pargs.subcommand().unwrap() else { + eprintln!("a subcommand is required"); + print_help(); + std::process::exit(1); + }; - match subcommand.as_deref() { - None | Some("play") => StkArgs::Play, - Some(unknown) => { - eprintln!("unknown subcommand: {unknown}"); + match subcommand.as_str() { + "client" => { + StkArgs::Client { + server: pargs.value_from_str(["-s", "--server"]).unwrap(), + } + }, + #[cfg(not(target_arch = "wasm32"))] + "server" => { + StkArgs::Server { + bind_to: pargs.value_from_str(["-b", "--bind-to"]).unwrap(), + with_client: pargs.contains("--with-client"), + } + }, + unknown => { + eprintln!("unknown subcommand: {unknown} (is that feature supported on this platform?)"); eprintln!("-h, --help for help"); std::process::exit(1); } @@ -36,12 +58,32 @@ pub fn parse_args() -> StkArgs { fn print_help() { println!("\ USAGE: - starkingdoms [FLAGS] [subcommand] + starkingdoms [FLAGS] [...] FLAGS: -h, --help Prints help information -v, --version Prints version information SUBCOMMANDS: - play Run the game (default)"); -} + "); + + println!(" client Run the client (see CLIENT for options)"); + + if cfg!(not(target_arch = "wasm32")) { + println!(" server Run the server (see SERVER for options)"); + } + + println!("\n"); + + println!("\ +CLIENT: + -s, --server WebSocket URL to connect to + "); + if cfg!(not(target_arch = "wasm32")) { + print!("\ +SERVER: + -b, --bind-to Socket address to bind to + --with-client Start a client connected to this server + \n"); + } +} \ No newline at end of file diff --git a/crates/unified/src/client/mod.rs b/crates/unified/src/client/mod.rs index a555f2e1218f943382440954631df16f57007fb6..2dbbc4ec18f3d74deb22e889890a1ec643d1ebd0 100644 --- a/crates/unified/src/client/mod.rs +++ b/crates/unified/src/client/mod.rs @@ -1,3 +1,7 @@ +use aeronet::io::{Session, SessionEndpoint}; +use aeronet::io::connection::{DisconnectReason, Disconnected}; +use aeronet_transport::{Transport, TransportConfig}; +use aeronet_websocket::client::{ClientConfig, WebSocketClient}; use crate::client::crafting::ui::crafting_ui_plugin; use crate::client::key_input::key_input_plugin; use crate::client::parts::parts_plugin; @@ -14,6 +18,7 @@ use crate::prelude::*; use planet::incoming_planets::incoming_planets_plugin; use crate::client::ship::attachment::client_attachment_plugin; use crate::shared::ecs::GameplayState; +use crate::shared::net::LANES; pub mod colors; pub mod key_input; @@ -30,7 +35,9 @@ pub mod crafting; pub mod components; pub mod plugins; -pub struct ClientPlugin; +pub struct ClientPlugin { + pub server: Option +} impl Plugin for ClientPlugin { fn build(&self, app: &mut App) { @@ -52,5 +59,55 @@ impl Plugin for ClientPlugin { .add_plugins(crafting_ui_plugin) .insert_state(GameplayState::Main) .insert_resource(DebugPickingMode::Disabled); + + let server = self.server.clone(); + app.add_systems(Startup, move |mut commands: Commands| { + let Some(server) = server.as_ref() else { return }; + commands.spawn((Name::new("default-session"), TransportConfig { max_memory_usage: 536_870_912, ..default() })) + .queue(WebSocketClient::connect(ClientConfig::builder().with_no_cert_validation(), server.clone())); + }); + if self.server.is_some() { + app.add_observer(on_connecting); + app.add_observer(on_connected); + app.add_observer(on_disconnected); + } } } + +pub fn on_connecting( + trigger: On, + names: Query<&Name>, + mut commands: Commands, +) { + let entity = trigger.event_target(); + let name = names.get(entity).unwrap(); + info!("{name} is connecting"); +} +pub fn on_connected(trigger: On, names: Query<&Name>, mut sessions: Query<(&Session)>, mut commands: Commands) { + let entity = trigger.event_target(); + let name = names.get(entity).unwrap(); + info!("{name} is connected"); + let session = sessions.get_mut(entity).expect("should exist"); + commands.entity(entity).insert(Transport::new( + session, + LANES, + LANES, + bevy::platform::time::Instant::now() + ).expect("MTU cannot support")); +} +pub fn on_disconnected(trigger: On, names: Query<&Name>) { + let session = trigger.event_target(); + let name = names.get(session).unwrap(); + + match &trigger.reason { + DisconnectReason::ByUser(reason) => { + info!(?name, ?reason, "session disconnected by user"); + } + DisconnectReason::ByPeer(reason) => { + info!(?name, ?reason, "session disconnected by peer"); + } + DisconnectReason::ByError(err) => { + warn!(?name, "session disconnected due to error: {err:?}"); + } + } +} \ No newline at end of file diff --git a/crates/unified/src/client/plugins.rs b/crates/unified/src/client/plugins.rs index 1fa6fba232457c4c31a3ff6d0ad7a963458f1130..faf4b7500067110cb1905bd9fe1adb6fb37e4999 100644 --- a/crates/unified/src/client/plugins.rs +++ b/crates/unified/src/client/plugins.rs @@ -1,10 +1,16 @@ +use aeronet_websocket::client::WebSocketClientPlugin; +use bevy::a11y::AccessibilityPlugin; use crate::client::ClientPlugin; -use bevy::app::{PluginGroup, PluginGroupBuilder}; +use bevy::app::{PanicHandlerPlugin, PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin}; use bevy::dev_tools::picking_debug::DebugPickingPlugin; +use bevy::diagnostic::{DiagnosticsPlugin, FrameCountPlugin}; use bevy::ecs::schedule::ScheduleLabel; +use bevy::input::InputPlugin; use bevy::input_focus::InputDispatchPlugin; +use bevy::log::LogPlugin; +use bevy::state::app::StatesPlugin; +use bevy::time::TimePlugin; use crate::prelude::*; -use bevy::ui::UiPlugin; use leafwing_input_manager::plugin::InputManagerPlugin; pub struct ClientPluginGroup; @@ -12,12 +18,22 @@ pub struct ClientPluginGroup; impl PluginGroup for ClientPluginGroup { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() - .add(MeshPickingPlugin) + .add_group( + DefaultPlugins.build() + .disable::() + .disable::() + .disable::() + .disable::() + .disable::() + .disable::() + .disable::() + .disable::() + ) .add(DebugPickingPlugin) - .add(ClientPlugin) - .add(UiPlugin) .add(InputDispatchPlugin) .add(InputManagerPlugin::::default()) + .add(WebSocketClientPlugin + ) } } diff --git a/crates/unified/src/main.rs b/crates/unified/src/main.rs index 596a62448b3aa41153d968b1559cf04b87a39bdf..95d8df487a6fb7115fc3192691b0cc3c7c5c343c 100644 --- a/crates/unified/src/main.rs +++ b/crates/unified/src/main.rs @@ -15,6 +15,7 @@ #![allow(clippy::missing_panics_doc, reason = "Gamedev! We panic a lot")] #![allow(clippy::too_many_arguments, reason = "Le Bevy:tm:")] #![allow(clippy::too_many_lines, reason = "With the three of us, this is impossible")] +#![allow(dead_code, unused, reason = "We have a lot of this and it's getting annoying")] pub mod client; pub mod server; @@ -23,37 +24,16 @@ pub mod prelude; #[cfg(target_arch = "wasm32")] pub mod wasm_entrypoint; -mod cli; +pub mod cli; pub mod shared; +pub mod universal_entrypoint; use std::str::FromStr; -use shared::plugins; #[cfg(target_arch = "wasm32")] pub use wasm_entrypoint::*; -use crate::cli::StkArgs; -use client::plugins::ClientPluginGroup; use crate::prelude::*; -use server::plugins::ServerPluginGroup; - -fn run(cli: StkArgs) -> AppExit { - let mut app = App::new(); - - match cli { - StkArgs::Play => { - app.add_plugins( - DefaultPlugins.build() - .disable::() - .disable::() - ); - app.add_plugins(plugins::SharedPluginGroup); - app.add_plugins(ServerPluginGroup); - app.add_plugins(ClientPluginGroup); - } - } - - app.run() -} +use crate::universal_entrypoint::run; #[cfg(feature = "wasm")] fn main() { @@ -65,7 +45,7 @@ fn main() -> AppExit { use tracing_subscriber::util::SubscriberInitExt; use bevy::log::tracing_subscriber; - let cli = crate::cli::parse_args(); + let cli = cli::parse_args(); tracing_subscriber::fmt() .with_env_filter( diff --git a/crates/unified/src/server/mod.rs b/crates/unified/src/server/mod.rs index a75b06b9307603ba40f837ca3f0f10731b966dfd..60766ff6bb5abb43f9edd0a2bee7830e23531163 100644 --- a/crates/unified/src/server/mod.rs +++ b/crates/unified/src/server/mod.rs @@ -12,6 +12,13 @@ pub mod orbit; pub mod plugins; pub mod components; +use std::net::SocketAddr; +use aeronet::io::connection::{DisconnectReason, Disconnected, LocalAddr}; +use aeronet::io::server::Server; +use aeronet::io::Session; +use aeronet_transport::lane::LaneKind; +use aeronet_transport::Transport; +use aeronet_websocket::server::{ServerConfig, WebSocketServer}; use crate::server::craft::craft_plugin; use crate::server::damping::damping_plugin; use crate::server::drill::drill_plugin; @@ -24,26 +31,39 @@ use crate::server::system_sets::{PlayerInputSet, WorldUpdateSet}; use crate::prelude::*; use crate::server::orbit::OrbitPlugin; use crate::server::player::thrust::server_thrust_plugin; +use crate::shared::net::LANES; -pub struct ServerPlugin; +pub struct ServerPlugin { + pub bind: SocketAddr +} impl Plugin for ServerPlugin { fn build(&self, app: &mut App) { - app.add_systems(Startup, player::join::spawn_singleplayer_player) - .add_plugins(planets_plugin) - .add_plugins(newtonian_gravity_plugin) - .add_plugins(player_management_plugin) - .add_plugins(spawn_parts_plugin) - .add_plugins(part_management_plugin) - .add_plugins(server_thrust_plugin) - /*.add_plugins(heat_cooling_plugin) - .add_plugins(heat_radiation_plugin) - .add_plugins(heat_conduction_plugin)*/ - .add_plugins(drill_plugin) - .add_plugins(craft_plugin) - .add_plugins(OrbitPlugin) - .add_plugins(damping_plugin) - .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet)); + let bind = self.bind; + app + .add_plugins(planets_plugin) + .add_plugins(newtonian_gravity_plugin) + .add_plugins(player_management_plugin) + .add_plugins(spawn_parts_plugin) + .add_plugins(part_management_plugin) + .add_plugins(server_thrust_plugin) + /*.add_plugins(heat_cooling_plugin) + .add_plugins(heat_radiation_plugin) + .add_plugins(heat_conduction_plugin)*/ + .add_plugins(drill_plugin) + .add_plugins(craft_plugin) + .add_plugins(OrbitPlugin) + .add_plugins(damping_plugin) + .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet)) + .add_observer(on_opened) + .add_observer(on_connected) + .add_observer(on_disconnected) + .add_systems(Startup, move |mut commands: Commands| { + commands.spawn(Name::new("websocket-server")) + .queue(WebSocketServer::open(ServerConfig::builder() + .with_bind_address(bind) + .with_no_encryption())); + }); } } @@ -55,3 +75,74 @@ pub struct ConnectedGameEntity { pub struct ConnectedNetworkEntity { pub game_entity: Entity, } + + +fn on_opened(trigger: On, servers: Query<&LocalAddr>) { + let server = trigger.event_target(); + let local_addr = servers.get(server).unwrap(); + info!(server_entity=?server, "websocket server opened on {:?}", *local_addr); +} + +fn on_connected( + trigger: On, + clients: Query<&ChildOf>, + sessions: Query<&Session>, + mut commands: Commands, +) { + let client = trigger.event_target(); + let Ok(&ChildOf(server)) = clients.get(client) else { + return; + }; + info!(?client, ?server, "client connected"); + + let session = sessions.get(client).expect("must exist"); + + commands.entity(client).insert(Transport::new( + session, + LANES, + LANES, + bevy::platform::time::Instant::now() + ).expect("MTU is too small")); + + let player = commands + .spawn(ConnectedGameEntity { + network_entity: client, + }) + .id(); + + commands.entity(client).insert(( + //TODO: Replicated, + ConnectedNetworkEntity { + game_entity: player, + }, + )); +} +fn on_disconnected( + trigger: On, + clients: Query<&ChildOf>, + player_entity: Query<&ConnectedNetworkEntity>, + mut commands: Commands, +) { + let client = trigger.event_target(); + let Ok(&ChildOf(server)) = clients.get(client) else { + return; + }; + match &trigger.reason { + DisconnectReason::ByUser(reason) => { + info!(?client, ?server, ?reason, "client disconnected by user"); + } + DisconnectReason::ByPeer(reason) => { + info!(?client, ?server, ?reason, "client disconnected by peer"); + } + DisconnectReason::ByError(err) => { + warn!(?client, ?server, "client disconnected with error: {err:?}"); + } + } + let Ok(other_entity) = player_entity.get(client) else { + return; + }; + let Ok(mut commands) = commands.get_entity(other_entity.game_entity) else { + return; + }; + commands.despawn(); +} diff --git a/crates/unified/src/server/player/join.rs b/crates/unified/src/server/player/join.rs index dc5e0d9e4134f433abaf7a28726eb6e4348f512a..a710a13938543b4c104159b978dc82f7e605844c 100644 --- a/crates/unified/src/server/player/join.rs +++ b/crates/unified/src/server/player/join.rs @@ -68,6 +68,7 @@ pub fn handle_new_players( planets: Query<(&Transform, &LinearVelocity, &Planet)>, asset_server: Res, ) { + if q_new_clients.is_empty() { return } let Some(wc) = &world_config.config else { warn!("got a joined player, but world config is not loaded! waiting until it is..."); for joined_player in &q_new_clients { @@ -87,6 +88,7 @@ pub fn handle_pending_players( planets: Query<(&Transform, &LinearVelocity, &Planet)>, asset_server: Res, ) { + if pending_players.is_empty() { return }; let Some(wc) = &world_config.config else { warn!("there are pending players, but world config is not loaded! waiting until it is..."); return; diff --git a/crates/unified/src/server/plugins.rs b/crates/unified/src/server/plugins.rs index 8d31043dafd0d22dcf5d01242488884850b908b1..438125043cbd2ed7a9b7f9b0d0f66a3033eaaa49 100644 --- a/crates/unified/src/server/plugins.rs +++ b/crates/unified/src/server/plugins.rs @@ -1,12 +1,15 @@ -use bevy::app::{PluginGroup, PluginGroupBuilder}; -use avian2d::PhysicsPlugins; -use avian2d::prelude::IslandPlugin; +use std::time::Duration; +use aeronet_transport::AeronetTransportPlugin; +use aeronet_websocket::server::WebSocketServerPlugin; +use bevy::app::{PluginGroup, PluginGroupBuilder, ScheduleRunnerPlugin}; +use crate::shared::plugins::TICK_RATE; pub struct ServerPluginGroup; impl PluginGroup for ServerPluginGroup { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() - .add(crate::server::ServerPlugin) + .add(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / TICK_RATE))) + .add(WebSocketServerPlugin) } } diff --git a/crates/unified/src/shared/mod.rs b/crates/unified/src/shared/mod.rs index 21ff3afbf32044c5882b9aa0dd03d6abc42efdb0..880df229b4c7ed86c5a67734738791a2515afbcd 100644 --- a/crates/unified/src/shared/mod.rs +++ b/crates/unified/src/shared/mod.rs @@ -4,4 +4,5 @@ pub mod physics; pub mod thrust; pub mod world_config; pub mod plugins; -pub mod ecs; \ No newline at end of file +pub mod ecs; +pub mod net; \ No newline at end of file diff --git a/crates/unified/src/shared/net.rs b/crates/unified/src/shared/net.rs new file mode 100644 index 0000000000000000000000000000000000000000..ca9b365f497ef88d0656a5d45297a121d8d3bed6 --- /dev/null +++ b/crates/unified/src/shared/net.rs @@ -0,0 +1,3 @@ +use aeronet_transport::lane::LaneKind; + +pub const LANES: [LaneKind; 1] = [LaneKind::ReliableOrdered]; diff --git a/crates/unified/src/shared/plugins.rs b/crates/unified/src/shared/plugins.rs index f05550162fb565c963441ae28290cf7a3ce4ff4b..30806b070979bf50a2d897a1c8d7767440c9de55 100644 --- a/crates/unified/src/shared/plugins.rs +++ b/crates/unified/src/shared/plugins.rs @@ -1,6 +1,9 @@ +use aeronet_transport::AeronetTransportPlugin; use crate::shared::ecs::{CraftPartRequest, DragRequestEvent, ToggleDrillEvent}; use crate::shared::thrust::ThrustSolution; use bevy::app::{App, PluginGroup, PluginGroupBuilder}; +use bevy::diagnostic::DiagnosticsPlugin; +use bevy::state::app::StatesPlugin; use bevy_common_assets::toml::TomlAssetPlugin; use crate::prelude::*; use crate::shared::config::part::PartConfig; @@ -9,7 +12,7 @@ use crate::shared::config::recipe::RecipesConfig; use crate::shared::config::world::GlobalWorldConfig; use crate::shared::world_config::world_config_plugin; -const PHYSICS_TICK_RATE: f64 = 20.0; +pub const TICK_RATE: f64 = 20.0; const PHYSICS_LENGTH_UNIT: f64 = 100.0; pub struct SharedPluginGroup; @@ -17,8 +20,13 @@ pub struct SharedPluginGroup; impl PluginGroup for SharedPluginGroup { fn build(self) -> PluginGroupBuilder { PluginGroupBuilder::start::() + .add(AssetPlugin::default()) + .add(StatesPlugin) + .add(TransformPlugin) + .add(DiagnosticsPlugin) + .add(AeronetTransportPlugin) .add(|app: &mut App| { - app.insert_resource(Time::from_hz(PHYSICS_TICK_RATE)); + app.insert_resource(Time::from_hz(TICK_RATE)); }) .add_group( PhysicsPlugins::default() diff --git a/crates/unified/src/universal_entrypoint.rs b/crates/unified/src/universal_entrypoint.rs new file mode 100644 index 0000000000000000000000000000000000000000..66c3f60f50a0cfef8ffd677a40dc8dab7501d4a6 --- /dev/null +++ b/crates/unified/src/universal_entrypoint.rs @@ -0,0 +1,35 @@ +use bevy::app::ScheduleRunnerPlugin; +use bevy::prelude::Startup; +use crate::cli::StkArgs; +use crate::client::ClientPlugin; +use crate::client::plugins::ClientPluginGroup; +use crate::prelude::{App, AppExit, MinimalPlugins, PluginGroup}; +use crate::server::player::join::spawn_singleplayer_player; +use crate::server::plugins::ServerPluginGroup; +use crate::server::ServerPlugin; +use crate::shared::plugins::SharedPluginGroup; + +pub fn run(cli: StkArgs) -> AppExit { + let mut app = App::new(); + + app.add_plugins(MinimalPlugins.build().disable::()); + app.add_plugins(SharedPluginGroup); + + match cli { + StkArgs::Client { server } => { + app.add_plugins(ClientPluginGroup); + app.add_plugins(ClientPlugin { server: Some(server) }); + }, + StkArgs::Server { bind_to, with_client } => { + app.add_plugins(ServerPluginGroup); + app.add_plugins(ServerPlugin { bind: bind_to }); + if with_client { + app.add_plugins(ClientPluginGroup); + app.add_plugins(ClientPlugin { server: None }); + app.add_systems(Startup, spawn_singleplayer_player); + } + } + } + + app.run() +} \ No newline at end of file diff --git a/crates/unified/src/wasm_entrypoint.rs b/crates/unified/src/wasm_entrypoint.rs index 154dcfebfe81d9d8c9b4f96fb1dd4de6c32b0983..3342b913ba49b82202aa7fe66f110b97f9a7f3f7 100644 --- a/crates/unified/src/wasm_entrypoint.rs +++ b/crates/unified/src/wasm_entrypoint.rs @@ -11,9 +11,10 @@ use bevy::prelude::PluginGroup; use tracing_web::MakeWebConsoleWriter; use tracing_subscriber::prelude::*; use tracing_subscriber::filter::LevelFilter; +use crate::cli::StkArgs; #[wasm_bindgen] -pub fn play(_server: &str) -> Result<(), JsValue> { +pub fn play(server: &str) -> Result<(), JsValue> { console_error_panic_hook::set_once(); let fmt_layer = tracing_subscriber::fmt::layer() .with_ansi(false) @@ -25,23 +26,8 @@ pub fn play(_server: &str) -> Result<(), JsValue> { .with(fmt_layer) .init(); - for instance in wgpu::Instance::enabled_backend_features().iter_names() { - bevy::log::debug!(?instance, "available backend"); - } - - let mut app = App::new(); - app.add_plugins( - DefaultPlugins.build() - .disable::() - .disable::() - ); - app.add_plugins(SharedPluginGroup); - app.add_plugins(ServerPluginGroup); - app.add_plugins(ClientPluginGroup); - - app.run(); - - bevy::prelude::info!("goodbye!"); + let cli = StkArgs::Client { server }; + run(cli); Ok(()) }