~starkingdoms/starkingdoms

f02a26613d42d88c6c9c33a4ce599605d9977462 — core 30 days ago fe41c97
feat(netcode-rewrite): re-add networking
M Cargo.lock => Cargo.lock +478 -1
@@ 86,6 86,81 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 389,6 464,34 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 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"


@@ 1943,6 2049,15 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2350,6 2465,15 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2462,7 2586,7 @@ dependencies = [
 "subsecond",
 "thiserror 2.0.18",
 "tracing",
 "tungstenite",
 "tungstenite 0.28.0",
]

[[package]]


@@ 2553,6 2677,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2829,6 2959,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2838,12 2974,28 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
 "futures-core",
 "futures-sink",
]

[[package]]


@@ 2853,6 3005,17 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2883,6 3046,12 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 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",
]


@@ 2933,6 3106,17 @@ dependencies = [

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"


@@ 3965,6 4149,12 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4301,6 4491,15 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4323,6 4522,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4452,6 4657,16 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4583,6 4798,12 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4820,6 5041,19 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4923,6 5157,31 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4996,6 5255,54 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5029,6 5336,15 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5054,6 5370,29 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5293,6 5632,16 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5351,6 5700,9 @@ dependencies = [
name = "starkingdoms"
version = "0.1.0"
dependencies = [
 "aeronet",
 "aeronet_transport",
 "aeronet_websocket",
 "avian2d",
 "bevy",
 "bevy_common_assets",


@@ 5416,6 5768,12 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5566,6 5924,25 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5616,6 5993,46 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5795,6 6212,25 @@ dependencies = [

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442"


@@ 5829,6 6265,26 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5877,6 6333,12 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 7216,6 7678,15 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 7248,6 7719,12 @@ dependencies = [
]

[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"

M Cargo.toml => Cargo.toml +3 -0
@@ 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

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +4 -0
@@ 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 = [

M crates/unified/src/cli.rs => crates/unified/src/cli.rs +51 -9
@@ 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] <subcommand> [<args>...]

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 <URL>      WebSocket URL to connect to
        ");
    if cfg!(not(target_arch = "wasm32")) {
        print!("\
SERVER:
    -b, --bind-to <IP:PORT> Socket address to bind to
        --with-client       Start a client connected to this server
    \n");
    }
}
\ No newline at end of file

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +58 -1
@@ 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<String>
}

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<Add, SessionEndpoint>,
    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<Add, Session>, 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<Disconnected>, 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

M crates/unified/src/client/plugins.rs => crates/unified/src/client/plugins.rs +21 -5
@@ 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::<Self>()
            .add(MeshPickingPlugin)
            .add_group(
                DefaultPlugins.build()
                    .disable::<LogPlugin>()
                    .disable::<TaskPoolPlugin>()
                    .disable::<FrameCountPlugin>()
                    .disable::<TimePlugin>()
                    .disable::<TransformPlugin>()
                    .disable::<DiagnosticsPlugin>()
                    .disable::<AssetPlugin>()
                    .disable::<StatesPlugin>()
            )
            .add(DebugPickingPlugin)
            .add(ClientPlugin)
            .add(UiPlugin)
            .add(InputDispatchPlugin)
            .add(InputManagerPlugin::<crate::client::input::ClientAction>::default())
            .add(WebSocketClientPlugin
            )
    }
}


M crates/unified/src/main.rs => crates/unified/src/main.rs +5 -25
@@ 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::<bevy::log::LogPlugin>()
                    .disable::<bevy::ui::UiPlugin>()
            );
            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(

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +107 -16
@@ 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<Add, Server>, 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<Add, Session>,
    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<Disconnected>,
    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();
}

M crates/unified/src/server/player/join.rs => crates/unified/src/server/player/join.rs +2 -0
@@ 68,6 68,7 @@ pub fn handle_new_players(
    planets: Query<(&Transform, &LinearVelocity, &Planet)>,
    asset_server: Res<AssetServer>,
) {
    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<AssetServer>,
) {
    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;

M crates/unified/src/server/plugins.rs => crates/unified/src/server/plugins.rs +7 -4
@@ 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::<Self>()
            .add(crate::server::ServerPlugin)
            .add(ScheduleRunnerPlugin::run_loop(Duration::from_secs_f64(1.0 / TICK_RATE)))
            .add(WebSocketServerPlugin)
    }
}

M crates/unified/src/shared/mod.rs => crates/unified/src/shared/mod.rs +2 -1
@@ 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

A crates/unified/src/shared/net.rs => crates/unified/src/shared/net.rs +3 -0
@@ 0,0 1,3 @@
use aeronet_transport::lane::LaneKind;

pub const LANES: [LaneKind; 1] = [LaneKind::ReliableOrdered];

M crates/unified/src/shared/plugins.rs => crates/unified/src/shared/plugins.rs +10 -2
@@ 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::<Self>()
            .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()

A crates/unified/src/universal_entrypoint.rs => crates/unified/src/universal_entrypoint.rs +35 -0
@@ 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::<ScheduleRunnerPlugin>());
    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

M crates/unified/src/wasm_entrypoint.rs => crates/unified/src/wasm_entrypoint.rs +4 -18
@@ 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::<LogPlugin>()
            .disable::<UiPlugin>()
    );
    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(())
}