~starkingdoms/starkingdoms

06663f6e947e1f514a0dd87b4e256ef9de377d17 — core a month ago 39d56ab
feat(netcode-rewrite): remove all networking
M Cargo.lock => Cargo.lock +10 -1137
@@ 48,7 48,7 @@ dependencies = [
 "accesskit_consumer",
 "hashbrown 0.15.5",
 "objc2 0.5.2",
 "objc2-app-kit 0.2.2",
 "objc2-app-kit",
 "objc2-foundation 0.2.2",
]



@@ 86,98 86,6 @@ 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_replicon"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3660e988e020e887f8e623e99eb8f0945e772ae0e35c4032026b5f73a40f0a3"
dependencies = [
 "aeronet_io",
 "aeronet_transport",
 "bevy_app",
 "bevy_ecs",
 "bevy_platform",
 "bevy_reflect",
 "bevy_replicon",
 "bevy_state",
 "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"


@@ 297,26 205,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ad8689a486416c401ea15715a4694de30054248ec627edbf31f49cb64ee4086"

[[package]]
name = "arboard"
version = "3.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
dependencies = [
 "clipboard-win",
 "image",
 "log",
 "objc2 0.6.4",
 "objc2-app-kit 0.3.2",
 "objc2-core-foundation",
 "objc2-core-graphics",
 "objc2-foundation 0.3.2",
 "parking_lot",
 "percent-encoding",
 "windows-sys 0.60.2",
 "x11rb",
]

[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 510,34 398,6 @@ dependencies = [
]

[[package]]
name = "aws-lc-rs"
version = "1.16.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f"
dependencies = [
 "aws-lc-sys",
 "zeroize",
]

[[package]]
name = "aws-lc-sys"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7"
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"


@@ 868,53 728,6 @@ dependencies = [
]

[[package]]
name = "bevy_egui"
version = "0.39.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d0a7e4806f3f242326d2c6157531c36d710f3bf320ebc0a1678e44635ed0eac"
dependencies = [
 "arboard",
 "bevy_app",
 "bevy_asset",
 "bevy_camera",
 "bevy_color",
 "bevy_core_pipeline",
 "bevy_derive",
 "bevy_ecs",
 "bevy_image",
 "bevy_input",
 "bevy_log",
 "bevy_math",
 "bevy_mesh",
 "bevy_picking",
 "bevy_platform",
 "bevy_reflect",
 "bevy_render",
 "bevy_shader",
 "bevy_time",
 "bevy_transform",
 "bevy_ui_render",
 "bevy_utils",
 "bevy_window",
 "bevy_winit",
 "bytemuck",
 "crossbeam-channel",
 "egui",
 "encase",
 "getrandom 0.3.4",
 "image",
 "itertools 0.14.0",
 "js-sys",
 "thread_local",
 "wasm-bindgen",
 "wasm-bindgen-futures",
 "web-sys",
 "webbrowser",
 "wgpu-types 27.0.1",
 "winit",
]

[[package]]
name = "bevy_encase_derive"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 1476,27 1289,6 @@ dependencies = [
]

[[package]]
name = "bevy_replicon"
version = "0.39.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a0d6f7d37938f1f6797950774a41adc189a459d73cffb1941804a5d9d8fd059"
dependencies = [
 "bevy",
 "bitflags 2.11.1",
 "bumpalo",
 "bytes",
 "deterministic-hash",
 "log",
 "petgraph",
 "postcard",
 "serde",
 "smallbitvec",
 "typeid",
 "variadics_please",
 "xxhash-rust",
]

[[package]]
name = "bevy_scene"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2010,9 1802,6 @@ name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
dependencies = [
 "portable-atomic",
]

[[package]]
name = "calloop"


@@ 2163,33 1952,6 @@ dependencies = [
]

[[package]]
name = "clipboard-win"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
dependencies = [
 "error-code",
]

[[package]]
name = "cmake"
version = "0.1.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678"
dependencies = [
 "cc",
]

[[package]]
name = "cobs"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
dependencies = [
 "thiserror 2.0.18",
]

[[package]]
name = "codespan-reporting"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2632,15 2394,6 @@ 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"


@@ 2695,12 2448,6 @@ dependencies = [
]

[[package]]
name = "deterministic-hash"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11277822c27bde750de02c5dc5159b91e88bf2661a2c1d98106f2fb1c5c6f590"

[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2759,7 2506,7 @@ dependencies = [
 "subsecond",
 "thiserror 2.0.18",
 "tracing",
 "tungstenite 0.28.0",
 "tungstenite",
]

[[package]]


@@ 2808,17 2555,6 @@ dependencies = [
]

[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
]

[[package]]
name = "disqualified"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2861,12 2597,6 @@ 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"


@@ 2885,48 2615,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fdab65db9274e0168143841eb8f864a0a21f8b1b8d2ba6812bbe6024346e99e"

[[package]]
name = "ecolor"
version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e"
dependencies = [
 "bytemuck",
 "emath",
]

[[package]]
name = "egui"
version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3"
dependencies = [
 "ahash",
 "bitflags 2.11.1",
 "emath",
 "epaint",
 "log",
 "nohash-hasher",
 "profiling",
 "smallvec",
 "unicode-segmentation",
]

[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"

[[package]]
name = "emath"
version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32"
dependencies = [
 "bytemuck",
]

[[package]]
name = "ena"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 2990,30 2684,6 @@ dependencies = [
]

[[package]]
name = "epaint"
version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62"
dependencies = [
 "ab_glyph",
 "ahash",
 "bytemuck",
 "ecolor",
 "emath",
 "epaint_default_fonts",
 "log",
 "nohash-hasher",
 "parking_lot",
 "profiling",
]

[[package]]
name = "epaint_default_fonts"
version = "0.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4fbe202b6578d3d56428fa185cdf114a05e49da05f477b3c7f0fbb221f1862"

[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3041,12 2711,6 @@ dependencies = [
]

[[package]]
name = "error-code"
version = "3.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"

[[package]]
name = "euclid"
version = "0.22.14"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3083,26 2747,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"

[[package]]
name = "fax"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
dependencies = [
 "fax_derive",
]

[[package]]
name = "fax_derive"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
]

[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3229,52 2873,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"

[[package]]
name = "form_urlencoded"
version = "1.2.2"
name = "fsevent-sys"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
dependencies = [
 "percent-encoding",
 "libc",
]

[[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"
checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2"
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"
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]]


@@ 3284,17 2897,6 @@ 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"


@@ 3325,12 2927,6 @@ 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"


@@ 3342,13 2938,9 @@ 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",
]


@@ 3385,27 2977,14 @@ 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"
dependencies = [
 "cfg-if",
 "js-sys",
 "libc",
 "r-efi 5.3.0",
 "wasip2",
 "wasm-bindgen",
]

[[package]]


@@ 3760,88 3339,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"

[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
 "displaydoc",
 "potential_utf",
 "utf8_iter",
 "yoke",
 "zerofrom",
 "zerovec",
]

[[package]]
name = "icu_locale_core"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
 "displaydoc",
 "litemap",
 "tinystr",
 "writeable",
 "zerovec",
]

[[package]]
name = "icu_normalizer"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
 "icu_collections",
 "icu_normalizer_data",
 "icu_properties",
 "icu_provider",
 "smallvec",
 "zerovec",
]

[[package]]
name = "icu_normalizer_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"

[[package]]
name = "icu_properties"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
 "icu_collections",
 "icu_locale_core",
 "icu_properties_data",
 "icu_provider",
 "zerotrie",
 "zerovec",
]

[[package]]
name = "icu_properties_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"

[[package]]
name = "icu_provider"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
 "displaydoc",
 "icu_locale_core",
 "writeable",
 "yoke",
 "zerofrom",
 "zerotrie",
 "zerovec",
]

[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3854,27 3351,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"

[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
 "idna_adapter",
 "smallvec",
 "utf8_iter",
]

[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
 "icu_normalizer",
 "icu_properties",
]

[[package]]
name = "image"
version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 3885,7 3361,6 @@ dependencies = [
 "moxcms",
 "num-traits",
 "png",
 "tiff",
]

[[package]]


@@ 4234,12 3709,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"

[[package]]
name = "litemap"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"

[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4486,12 3955,6 @@ dependencies = [
]

[[package]]
name = "nohash-hasher"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"

[[package]]
name = "nonmax"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4556,12 4019,6 @@ 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"


@@ 4655,18 4112,6 @@ dependencies = [
]

[[package]]
name = "objc2-app-kit"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
dependencies = [
 "bitflags 2.11.1",
 "objc2 0.6.4",
 "objc2-core-graphics",
 "objc2-foundation 0.3.2",
]

[[package]]
name = "objc2-cloud-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4714,19 4159,6 @@ dependencies = [
]

[[package]]
name = "objc2-core-graphics"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
dependencies = [
 "bitflags 2.11.1",
 "dispatch2",
 "objc2 0.6.4",
 "objc2-core-foundation",
 "objc2-io-surface",
]

[[package]]
name = "objc2-core-image"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4792,17 4224,6 @@ dependencies = [
]

[[package]]
name = "objc2-io-surface"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
dependencies = [
 "bitflags 2.11.1",
 "objc2 0.6.4",
 "objc2-core-foundation",
]

[[package]]
name = "objc2-link-presentation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 4810,7 4231,7 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
 "block2 0.5.1",
 "objc2 0.5.2",
 "objc2-app-kit 0.2.2",
 "objc2-app-kit",
 "objc2-foundation 0.2.2",
]



@@ 4934,15 4355,6 @@ 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"


@@ 4965,12 4377,6 @@ 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"


@@ 4987,8 4393,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e"
dependencies = [
 "num-traits",
 "rand 0.8.6",
 "serde",
]

[[package]]


@@ 5112,34 4516,12 @@ 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"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"

[[package]]
name = "petgraph"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [
 "fixedbitset",
 "hashbrown 0.15.5",
 "indexmap",
 "serde",
]

[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5265,43 4647,6 @@ dependencies = [
]

[[package]]
name = "postcard"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [
 "cobs",
 "postcard-derive",
 "serde",
]

[[package]]
name = "postcard-derive"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
]

[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [
 "zerovec",
]

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


@@ 5397,12 4742,6 @@ dependencies = [
]

[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"

[[package]]
name = "quick-xml"
version = "0.39.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 5440,16 4779,6 @@ checksum = "019b4b213425016d7d84a153c4c73afb0946fbb4840e4eece7ba8848b9d6da22"

[[package]]
name = "rand"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
dependencies = [
 "rand_core 0.6.4",
 "serde",
]

[[package]]
name = "rand"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"


@@ 5481,15 4810,6 @@ dependencies = [

[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
 "serde",
]

[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"


@@ 5564,19 4884,6 @@ 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"


@@ 5680,31 4987,6 @@ 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"


@@ 5778,54 5060,6 @@ dependencies = [
]

[[package]]
name = "rustls"
version = "0.23.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c2c118cb077cca2822033836dfb1b975355dfb784b5e8da48f7b6c5db74e60e"
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"


@@ 5859,15 5093,6 @@ 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"


@@ 5893,29 5118,6 @@ 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"


@@ 6112,12 5314,6 @@ dependencies = [
]

[[package]]
name = "smallbitvec"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d31d263dd118560e1a492922182ab6ca6dc1d03a3bf54e7699993f31a4150e3f"

[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 6161,16 5357,6 @@ 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"


@@ 6229,28 5415,19 @@ dependencies = [
name = "starkingdoms"
version = "0.1.0"
dependencies = [
 "aeronet",
 "aeronet_replicon",
 "aeronet_transport",
 "aeronet_websocket",
 "avian2d",
 "bevy",
 "bevy_common_assets",
 "bevy_egui",
 "bevy_replicon",
 "console_error_panic_hook",
 "ctrlc",
 "getrandom 0.4.2",
 "good_lp",
 "leafwing-input-manager",
 "ordered-float",
 "pico-args",
 "rand 0.10.1",
 "ron",
 "serde",
 "tracing-subscriber",
 "tracing-web",
 "url",
 "wasm-bindgen",
 "web-time 1.1.0",
 "wgpu 29.0.1",


@@ 6303,12 5480,6 @@ 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"


@@ 6348,17 5519,6 @@ dependencies = [
]

[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
]

[[package]]
name = "sys-locale"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 6450,20 5610,6 @@ dependencies = [
]

[[package]]
name = "tiff"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63feaf3343d35b6ca4d50483f94843803b0f51634937cc2ec519fc32232bc52"
dependencies = [
 "fax",
 "flate2",
 "half",
 "quick-error",
 "weezl",
 "zune-jpeg",
]

[[package]]
name = "tikv-jemalloc-sys"
version = "0.5.4+5.3.0-patched"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 6484,25 5630,6 @@ 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"


@@ 6528,16 5655,6 @@ dependencies = [
]

[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
 "displaydoc",
 "zerovec",
]

[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 6563,46 5680,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"

[[package]]
name = "tokio"
version = "1.52.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6"
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"


@@ 6782,25 5859,6 @@ 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"


@@ 6835,26 5893,6 @@ 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"


@@ 6903,36 5941,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 = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
 "form_urlencoded",
 "idna",
 "percent-encoding",
 "serde",
]

[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"

[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"

[[package]]
name = "uuid"
version = "1.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 7267,28 6281,6 @@ dependencies = [
]

[[package]]
name = "webbrowser"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72"
dependencies = [
 "core-foundation 0.10.1",
 "jni",
 "log",
 "ndk-context",
 "objc2 0.6.4",
 "objc2-foundation 0.3.2",
 "url",
 "web-sys",
]

[[package]]
name = "weezl"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"

[[package]]
name = "wgpu"
version = "27.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 8098,7 7090,7 @@ dependencies = [
 "memmap2",
 "ndk",
 "objc2 0.5.2",
 "objc2-app-kit 0.2.2",
 "objc2-app-kit",
 "objc2-foundation 0.2.2",
 "objc2-ui-kit",
 "orbclient",


@@ 8239,12 7231,6 @@ dependencies = [
]

[[package]]
name = "writeable"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"

[[package]]
name = "x11-dl"
version = "2.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 8316,50 7302,12 @@ dependencies = [
]

[[package]]
name = "xxhash-rust"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"

[[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"
checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"

[[package]]
name = "yoke"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
 "stable_deref_trait",
 "yoke-derive",
 "zerofrom",
]

[[package]]
name = "yoke-derive"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
 "synstructure",
]

[[package]]
name = "zeno"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"


@@ 8386,82 7334,7 @@ dependencies = [
]

[[package]]
name = "zerofrom"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
 "zerofrom-derive",
]

[[package]]
name = "zerofrom-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
 "synstructure",
]

[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"

[[package]]
name = "zerotrie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [
 "displaydoc",
 "yoke",
 "zerofrom",
]

[[package]]
name = "zerovec"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
 "yoke",
 "zerofrom",
 "zerovec-derive",
]

[[package]]
name = "zerovec-derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
 "proc-macro2",
 "quote",
 "syn 2.0.117",
]

[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

[[package]]
name = "zune-core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"

[[package]]
name = "zune-jpeg"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296"
dependencies = [
 "zune-core",
]

M crates/unified/Cargo.toml => crates/unified/Cargo.toml +5 -22
@@ 22,6 22,7 @@ bevy = { version = "0.18", default-features = false, features = [
    "bevy_log",
    "multi_threaded",
    "bevy_dev_tools",
    "bevy_state",
    "sprite_picking",
    "mesh_picking",
    "default_font",


@@ 50,10 51,6 @@ avian2d = { version = "0.6", default-features = false, features = [
]}
bevy_common_assets = { version = "0.16", features = ["toml"] }

bevy_replicon = "0.39"

url = "2"

tracing-subscriber = "0.3"

serde = { version = "1", features = ["derive"] }


@@ 61,22 58,13 @@ serde = { version = "1", features = ["derive"] }
rand = "0.10"
getrandom = { version = "0.4", features = [] }

aeronet = "0.20"
aeronet_replicon = { version = "0.20", features = ["client"] }
aeronet_websocket = { version = "0.20", features = ["client"] }
aeronet_transport = "0.20"

ordered-float = { version = "5", features = ["serde"] }
ron = "0.12"

wgpu = "*"

pico-args = "0.5"

bevy_egui = { version = "0.39", optional = true }

leafwing-input-manager = { version = "0.20", optional = true }
good_lp = { version = "1.15", default-features = false, features = ["clarabel"], optional = true }
leafwing-input-manager = { version = "0.20" }
good_lp = { version = "1.15", default-features = false, features = ["clarabel"] }

web-time = "1"



@@ 92,7 80,8 @@ console_error_panic_hook = "0.1"
native_dev = [
    "bevy/file_watcher",
    "bevy/hotpatching",
    "bevy/dynamic_linking"
    "bevy/dynamic_linking",
    "native"
]
native = [
    "bevy/x11",


@@ 101,9 90,3 @@ native = [
]
wasm = ["getrandom/wasm_js", "bevy/webgl2"]

particle_editor = ["dep:bevy_egui"]
server = ["aeronet_websocket/server", "aeronet_replicon/server"]
client = [
    "dep:leafwing-input-manager",
    "dep:good_lp"
]

M crates/unified/src/attachment.rs => crates/unified/src/attachment.rs +0 -10
@@ 20,13 20,11 @@ use serde::{Deserialize, Serialize};
use std::ops::Deref;

#[derive(Component, Serialize, Deserialize)]
#[require(Replicated)]
/// The primary component for a ship structure. Will be present on Hearty
pub struct Ship;

#[derive(Component, Serialize, Deserialize, MapEntities, Deref)]
#[relationship_target(relationship = PartInShip, linked_spawn)]
#[require(Replicated)]
/// List of all parts in this Ship. Will be present on Hearty.
/// Managed automatically by attachment system (do not modify)y,



@@ 34,14 32,12 @@ pub struct Parts(#[entities] Vec<Entity>);

#[derive(Component, Serialize, Deserialize, MapEntities, Debug)]
#[relationship(relationship_target = Parts)]
#[require(Replicated)]
/// A Pointer to the `Ship` that this part belongs to. Managed automatically by attachment system
/// (do not add/remove manually)
pub struct PartInShip(#[entities] pub Entity);

#[derive(Component, Serialize, Deserialize)]
#[require(Transform)]
#[require(Replicated)]
/// A `Joint` is a separate entity (child of a part) that represents a single possible attachment
/// point for that part.
pub struct Joint {


@@ 49,7 45,6 @@ pub struct Joint {
    pub transform: Transform,
}
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[require(Replicated)]
/// A `Peer` is a pointer component of a `Joint` that references the Joint entity it is connected to.
/// Two joints are said to be 'peered' when they are attached to each other.
/// Two Parts are connected when they have a set of peered `Joint`s


@@ 62,12 57,10 @@ pub struct Peer {

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = Joints)]
#[require(Replicated)]
/// A pointer to the `Part` that this `Joint` belongs to. Managed automatically, do not add/remove
pub struct JointOf(#[entities] pub Entity);
#[derive(Component, Serialize, Deserialize, MapEntities, Debug, Clone)]
#[relationship_target(relationship = JointOf)]
#[require(Replicated)]
/// All the `Joint`s that belong to this `Part`. Managed automatically, do not modify
pub struct Joints(#[entities] Vec<Entity>);
impl Deref for Joints {


@@ 79,13 72,11 @@ impl Deref for Joints {

#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship(relationship_target = Snaps)]
#[require(Replicated)]
/// The `Part` that this Snap belongs to. A Snap is an internal entity used for snapping during
/// the attachment routine. Managed automatically.
pub struct SnapOf(#[entities] pub Entity);
#[derive(Component, Serialize, Deserialize, MapEntities)]
#[relationship_target(relationship = SnapOf)]
#[require(Replicated)]
/// All the `Snap`s of this `Joint`. Managed automatically.
pub struct Snaps(#[entities] Vec<Entity>);
impl Deref for Snaps {


@@ 106,6 97,5 @@ impl JointId {
}

#[derive(Serialize, Deserialize, Component, MapEntities)]
#[require(Replicated)]
/// The `Joint` that this `Snap` belongs to
pub struct SnapOfJoint(#[entities] pub Entity);

M crates/unified/src/cli.rs => crates/unified/src/cli.rs +10 -81
@@ 1,25 1,10 @@
#[cfg(not(any(feature = "client", feature = "server", feature = "particle_editor")))]
compile_error!("You need to enable one or more of client, server, particle_editor features");
#[cfg(not(any(feature = "native", feature = "wasm")))]
compile_error!("You need to enable one of native, wasm features");
#[cfg(all(feature = "native", feature = "wasm"))]
compile_error!("You cannot enable both native and wasm features");

pub enum StkArgs {
    #[cfg(feature = "client")]
    Client {
        server: String,
    },
    #[cfg(feature = "server")]
    Server {
        bind_to: std::net::SocketAddr,
        max_clients: std::num::NonZeroUsize,
        tick_rate: f32,
        #[cfg(feature = "client")]
        with_client: bool
    },
    #[cfg(feature = "particle_editor")]
    ParticleEditor
    Play,
}

#[cfg(not(target_arch = "wasm32"))]


@@ 32,41 17,16 @@ pub fn parse_args() -> StkArgs {
    }

    if pargs.contains(["-v", "--version"]) {
        println!("{}",env!("CARGO_PKG_VERSION"));
        println!("{}", env!("CARGO_PKG_VERSION"));
        std::process::exit(0);
    }

    let Some(subcommand) = pargs.subcommand().unwrap() else {
        eprintln!("a subcommand is required");
        print_help();
        std::process::exit(1);
    };

    
    let subcommand = pargs.subcommand().unwrap();

    match subcommand.as_str() {
        #[cfg(feature = "client")]
        "client" => {
            StkArgs::Client {
                server: pargs.value_from_str(["-s", "--server"]).unwrap(),
            }
        },
        #[cfg(feature = "server")]
        "server" => {
            StkArgs::Server {
                bind_to: pargs.value_from_str(["-b", "--bind-to"]).unwrap(),
                max_clients: pargs.value_from_str(["-c", "--max-clients"]).unwrap(),
                tick_rate: pargs.value_from_str(["-r", "--tick-rate"]).unwrap(),
                #[cfg(feature = "client")]
                with_client: pargs.contains("--with-client"),
            }
        },
        #[cfg(feature = "particle_editor")]
        "particle_editor" => {
            StkArgs::ParticleEditor
        },
        unknown => {
            eprintln!("unknown subcommand: {unknown} (is that feature enabled?)");
    match subcommand.as_deref() {
        None | Some("play") => StkArgs::Play,
        Some(unknown) => {
            eprintln!("unknown subcommand: {unknown}");
            eprintln!("-h, --help for help");
            std::process::exit(1);
        }


@@ 76,43 36,12 @@ pub fn parse_args() -> StkArgs {
fn print_help() {
    println!("\
USAGE:
    starkingdoms [FLAGS] <subcommand> [<args>...]
    starkingdoms [FLAGS] [subcommand]

FLAGS:
    -h, --help        Prints help information
    -v, --version     Prints version information

SUBCOMMANDS:
    ");

    if cfg!(feature = "client") {
        println!("    client            Run the client (see CLIENT for options)");
    }
    if cfg!(feature = "server") {
        println!("    server            Run the server (see SERVER for options)");
    }
    if cfg!(feature = "particle_editor") {
        println!("particle_editor   Run the particle editor");
    }

    println!("\n");

    if cfg!(feature = "client") {
        println!("\
CLIENT:
    -s, --server <URL>      WebSocket URL to connect to
        ");
    }
    if cfg!(feature = "server") {
        print!("\
SERVER:
    -b, --bind-to <IP:PORT> Socket address to bind to
    -c, --max-clients <N>   Maximum number of clients to accept
    -r, --tick-rate   <N>   Tick rate\n");
        if cfg!(feature = "client") {
            println!("    --with-client           Start a client connected to this server");
        } else {
            println!();
        }
    }
}
\ No newline at end of file
    play              Run the game (default)");
}

M crates/unified/src/client/mod.rs => crates/unified/src/client/mod.rs +4 -42
@@ 8,18 8,15 @@ use crate::client::ui::ui_plugin;
use crate::client::zoom::zoom_plugin;
use crate::client::starguide::init::starguide_init_plugin;
use crate::client::starguide::input::starguide_input_plugin;
use crate::ecs::{Hi, StarguideGizmos};
use aeronet_websocket::client::WebSocketClient;
use crate::ecs::StarguideGizmos;
use bevy::dev_tools::picking_debug::DebugPickingMode;
use crate::prelude::*;
use planet::incoming_planets::incoming_planets_plugin;
use crate::client::ship::attachment::client_attachment_plugin;
use crate::ecs::{Me, GameplayState};
use crate::ecs::GameplayState;

pub mod colors;
pub mod key_input;
pub mod net;
pub mod particles;
pub mod parts;
pub mod planet;
pub mod starfield;


@@ 31,28 28,15 @@ pub mod input;
pub mod starguide;
pub mod crafting;

pub struct ClientPlugin {
    pub server: Option<String>,
}
pub struct ClientPlugin;

impl Plugin for ClientPlugin {
    fn build(&self, app: &mut App) {
        let server = self.server.clone();
        app

            .add_systems(Startup, move |mut commands: Commands| {
                let Some(server) = server.as_ref() else { return };
                let config = net::websocket_config();

                commands
                    .spawn(Name::new("default-session"))
                    .queue(WebSocketClient::connect(config, server.clone()));
            })
            .init_gizmo_group::<StarguideGizmos>()
            .add_plugins(rendering::render_plugin)
            .add_plugins(input::input_plugin)
            .add_plugins(ship::thrusters::client_thrusters_plugin)
            .add_systems(Update, find_me)
            .add_systems(Update, net::set_config)
            .add_plugins((incoming_planets_plugin, indicators_plugin))
            .add_plugins(parts_plugin)
            .add_plugins(key_input_plugin)


@@ 66,27 50,5 @@ impl Plugin for ClientPlugin {
            .add_plugins(crafting_ui_plugin)
            .insert_state(GameplayState::Main)
            .insert_resource(DebugPickingMode::Disabled);

        // These are only needed if we're actually doing network things
        if self.server.is_some() {
            app.add_observer(net::on_connecting)
                .add_observer(net::on_connected)
                .add_observer(net::on_disconnected);
        }
    }
}



fn find_me(
    mut commands: Commands,
    mut reader: MessageReader<Hi>,
) {
    for msg in reader.read() {
        info!("^^^^^^^^^^^ IGNORE THESE WARNINGS ^^^^^^^^^^^^^^");
        info!("they are normal! and are from the world state being replicated as it is sent over the network");
        info!(?msg, "finding me: got hello from server");
        commands.entity(msg.you_are).insert(Me);
    }
}


D crates/unified/src/client/particles/mod.rs => crates/unified/src/client/particles/mod.rs +0 -7
@@ 1,7 0,0 @@
use bevy::app::{App, Plugin};

pub struct ParticlePlugin;

impl Plugin for ParticlePlugin {
    fn build(&self, _app: &mut App) {}
}

M crates/unified/src/client_plugins.rs => crates/unified/src/client_plugins.rs +3 -10
@@ 1,6 1,4 @@
use crate::client::ClientPlugin;
use aeronet_replicon::client::AeronetRepliconClientPlugin;
use aeronet_websocket::client::WebSocketClientPlugin;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use bevy::dev_tools::picking_debug::DebugPickingPlugin;
use bevy::ecs::schedule::ScheduleLabel;


@@ 9,19 7,14 @@ use crate::prelude::*;
use bevy::ui::UiPlugin;
use leafwing_input_manager::plugin::InputManagerPlugin;

pub struct ClientPluginGroup {
    pub server: Option<String>,
}
pub struct ClientPluginGroup;

impl PluginGroup for ClientPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()
            .add(WebSocketClientPlugin)
            .add(AeronetRepliconClientPlugin)
            .add(MeshPickingPlugin)
            .add(DebugPickingPlugin)
            .add(ClientPlugin {
                server: self.server,
            })
            .add(ClientPlugin)
            .add(UiPlugin)
            .add(InputDispatchPlugin)
            .add(InputManagerPlugin::<crate::client::input::ClientAction>::default())

M crates/unified/src/config/planet.rs => crates/unified/src/config/planet.rs +0 -5
@@ 7,7 7,6 @@ use serde::{Deserialize, Serialize};
#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)]
#[require(
    RigidBody::Dynamic,
    Replicated,
)]
pub struct Planet {
    pub name: String,


@@ 24,16 23,12 @@ pub struct Planet {
#[derive(Component, Serialize, Deserialize)]
#[require(
    RigidBody::Static,
    Replicated,
)]
pub struct PlanetSpring {
    pub name: String
}

#[derive(Component, Serialize, Deserialize)]
#[require(
    Replicated,
)]
pub struct PlanetSpringJoint {
    pub name: String
}

M crates/unified/src/config/recipe.rs => crates/unified/src/config/recipe.rs +0 -1
@@ 5,7 5,6 @@ use serde::{Deserialize, Serialize};
use crate::prelude::*;

#[derive(Deserialize, Asset, TypePath, Component, Serialize, Clone, Debug)]
#[require(Replicated)]
pub struct RecipesConfig {
    pub recipes: HashMap<String, Vec<Recipe>>,
}

M crates/unified/src/ecs.rs => crates/unified/src/ecs.rs +3 -34
@@ 1,12 1,9 @@
pub mod thruster;

use crate::config::part::PartConfig;
use bevy::ecs::entity::MapEntities;
use bevy::math::{Quat, Vec2};
use bevy::camera::visibility::RenderLayers;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use serde::{Deserialize, Serialize};
use avian2d::prelude::*;
use std::collections::HashMap;
use std::sync::LazyLock;


@@ 52,43 49,28 @@ pub struct PlanetSensor(pub String); // corresponding planet name
    LinearVelocity,
    AngularVelocity,
    ConstantForce,
    Replicated,
)]
pub struct Part {
    pub strong_config: PartConfig,
}
#[derive(Component, Debug)]
#[require(Replicated)]
pub struct PartHandle(pub Handle<PartConfig>);

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Player {
    #[entities]
    pub client: Entity,
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Particles {
    pub effect: String,
    pub active: bool,
}

#[derive(Serialize, Deserialize, Message, Debug, MapEntities, Clone)]
#[derive(Message, Debug, Clone)]
pub struct DragRequestEvent {
    #[entities]
    pub drag_target: Entity,
    pub drag_to: Vec2,
    pub set_rotation: Quat,
    #[entities]
    pub snap_target: Option<Entity>,
    #[entities]
    pub peer_snap: Option<Entity>,
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct PlayerStorage {
    pub fuel_capacity: f32,
    pub fuel: f32,


@@ 107,27 89,18 @@ pub struct StarguideOrbitImage(pub Handle<Image>);
#[derive(Component)]
pub struct StarguideOrbit;

#[derive(Message, Serialize, Deserialize, Debug, MapEntities)]
pub struct Hi {
    #[entities]
    pub you_are: Entity
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct CanCraft;
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct CraftingUi;
#[derive(Message, Serialize, Deserialize, Debug, Clone, MapEntities)]
#[derive(Message, Debug, Clone)]
pub struct CraftPartRequest {
    #[entities]
    pub crafting_part: Entity,
    pub crafted_part: String,
    pub inputs: HashMap<String, u32>,
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Temperature(pub f64);
#[derive(Component, Serialize, Deserialize, Debug)]
pub struct TemperatureSprite;


@@ 143,27 116,23 @@ pub struct Radiator {
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct Drill {
    pub drilling: bool,
    pub resource_multiplier: f32,
    pub on_planet: Option<String>,
}
#[derive(Message, Serialize, Deserialize, Debug, MapEntities, Clone)]
#[derive(Message, Debug, Clone)]
pub struct ToggleDrillEvent {
    #[entities]
    pub drill_entity: Entity,
}

#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct SingleStorage {
    pub resource_name: String,
    pub capacity: f32,
    pub stored: f32,
}
#[derive(Component, Serialize, Deserialize, Debug)]
#[require(Replicated)]
pub struct VariableStorage {
    pub resources: HashMap<String, f32>,
    pub capacity: f32,

M crates/unified/src/ecs/thruster.rs => crates/unified/src/ecs/thruster.rs +0 -2
@@ 4,7 4,6 @@ use bevy::math::Vec2;
use bevy::prelude::Bundle;
use serde::{Deserialize, Serialize};
use crate::prelude::{ChildOf, Component, Entity, Transform};
use bevy_replicon::prelude::Replicated;

#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
pub struct ThrusterId(pub String);


@@ 31,7 30,6 @@ impl Deref for PartThrusters {
pub struct ThrusterOfPart(#[entities] pub Entity);

#[derive(Component, Serialize, Deserialize)]
#[require(Replicated)]
pub struct Thruster {
    pub id: ThrusterId,
    pub thrust_vector: Vec2,

M crates/unified/src/main.rs => crates/unified/src/main.rs +8 -77
@@ 1,4 1,4 @@
#![warn(clippy::pedantic)] // Be annoying, and disable specific irritating lints if needed
#![warn(clippy::pedantic)]
#![deny(
    clippy::allow_attributes_without_reason,
    clippy::assertions_on_result_states


@@ 14,24 14,14 @@
)]
#![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(clippy::too_many_lines, reason = "With the three of us, this is impossible")]

pub mod attachment;
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "client")]
pub mod client_plugins;
pub mod config;
pub mod ecs;
#[cfg(feature = "particle_editor")]
pub mod particle_editor;
pub mod particles;
#[cfg(feature = "server")]
pub mod server;
#[cfg(feature = "server")]
pub mod server_plugins;
pub mod shared_plugins;
pub mod world_config;


@@ 49,81 39,23 @@ use std::str::FromStr;
pub use wasm_entrypoint::*;

use crate::cli::StkArgs;
use crate::prelude::*;
#[cfg(feature = "client")]
use crate::client_plugins::ClientPluginGroup;
#[cfg(feature = "server")]
use crate::prelude::*;
use crate::server_plugins::ServerPluginGroup;


#[allow(unused_mut, reason = "Conditional compilation hell")]
fn run(cli: StkArgs) -> AppExit {
    let mut app = App::new();

    match cli {
        #[cfg(feature = "client")]
        StkArgs::Client { server } => {
        StkArgs::Play => {
            app.add_plugins(
                DefaultPlugins.build()
                    .disable::<bevy::log::LogPlugin>()
                    .disable::<bevy::ui::UiPlugin>()
            );
            app.add_plugins(ClientPluginGroup { server: Some(server) });
            app.add_plugins(shared_plugins::SharedPluginGroup);
        }
        #[cfg(feature = "server")]
        StkArgs::Server {
            bind_to,
            tick_rate,
            max_clients,
            #[cfg(feature = "client")]
            with_client
        } => {
            let mut with_client_all = false;
            #[cfg(feature = "client")]
            if with_client {
                with_client_all = true;
                app.add_plugins(
                    DefaultPlugins.build()
                        .disable::<bevy::log::LogPlugin>()
                        .disable::<bevy::ui::UiPlugin>()
                );
                app.add_plugins(|app: &mut App| {
                    app.add_systems(Startup, server::player::join::ls_magically_invent_player);
                });
            }
            if !with_client_all {
               app
                   .add_plugins(AssetPlugin::default())
                   .add_plugins(bevy::state::app::StatesPlugin)
                    .add_plugins(TaskPoolPlugin::default())
                    .add_plugins(bevy::diagnostic::FrameCountPlugin)
                    .add_plugins(bevy::time::TimePlugin)
                    .add_plugins(bevy::app::ScheduleRunnerPlugin::run_loop(std::time::Duration::from_secs_f32(
                        1.0 / tick_rate,
                    )));
            }

            app.add_plugins(shared_plugins::SharedPluginGroup);

            let mut pg = ServerPluginGroup {
                bind: bind_to,
                tick_rate,
                max_clients: max_clients.into(),
            }.build();

            #[cfg(feature = "client")]
            if with_client {
                pg = pg.add_group(ClientPluginGroup {
                    server: None
                });
            }

            app.add_plugins(pg);
        }
        #[cfg(feature = "particle_editor")]
        StkArgs::ParticleEditor {} => {
            app.add_plugins(crate::particle_editor::particle_editor_plugin);
            app.add_plugins(ServerPluginGroup);
            app.add_plugins(ClientPluginGroup);
        }
    }



@@ 138,7 70,7 @@ fn main() {
#[cfg(feature = "native")]
fn main() -> AppExit {
    use tracing_subscriber::util::SubscriberInitExt;
    use bevy::log::{tracing_subscriber};
    use bevy::log::tracing_subscriber;

    let cli = crate::cli::parse_args();



@@ 147,8 79,7 @@ fn main() -> AppExit {
            tracing_subscriber::EnvFilter::from_default_env()
                .add_directive(tracing_subscriber::filter::Directive::from_str("naga=error").unwrap())
                .add_directive(tracing_subscriber::filter::Directive::from_str("info").unwrap())
                .add_directive(tracing_subscriber::filter::Directive::from_str("starkingdoms=trace").unwrap())
            ,
                .add_directive(tracing_subscriber::filter::Directive::from_str("starkingdoms=trace").unwrap()),
        )
        .finish()
        .init();

D crates/unified/src/particle_editor/ecs.rs => crates/unified/src/particle_editor/ecs.rs +0 -38
@@ 1,38 0,0 @@
use std::time::Duration;

use crate::particles::ParticleEffect;
use bevy::{
    asset::Handle,
    ecs::component::Component,
    time::{Timer, TimerMode},
};
use crate::prelude::*;

#[derive(Component)]
pub struct Particle;

#[derive(Component)]
pub struct LifetimeTimer(pub Timer);
impl LifetimeTimer {
    #[allow(dead_code, reason = "ghostly, what is this for?")]
    pub fn new(lifetime: f32) -> LifetimeTimer {
        LifetimeTimer(Timer::new(
            Duration::from_secs_f32(lifetime),
            TimerMode::Once,
        ))
    }
}

#[derive(Component)]
pub struct SpawnDelayTimer(pub Timer);
impl SpawnDelayTimer {
    pub fn new(delay: f32) -> SpawnDelayTimer {
        SpawnDelayTimer(Timer::new(Duration::from_secs_f32(delay), TimerMode::Once))
    }
}

#[derive(Component)]
pub struct CircleMesh(pub Handle<Mesh>, pub Handle<ColorMaterial>);

#[derive(Component)]
pub struct ParentEffect(pub ParticleEffect);

D crates/unified/src/particle_editor/hooks.rs => crates/unified/src/particle_editor/hooks.rs +0 -21
@@ 1,21 0,0 @@
use crate::prelude::*;

use crate::{particle_editor::ecs::SpawnDelayTimer, particles::ParticleEffect};

pub fn hooks_plugin(app: &mut App) {
    app.add_systems(Update, init_particle_effect);
}

fn init_particle_effect(
    mut commands: Commands,
    particle_effect: Query<(Entity, &ParticleEffect), Added<ParticleEffect>>,
) {
    for (entity, effect) in particle_effect {
        commands
            .get_entity(entity)
            .unwrap()
            .insert(SpawnDelayTimer::new(
                effect.batch_spawn_delay_seconds.sample(&mut rand::rng()),
            ));
    }
}

D crates/unified/src/particle_editor/mod.rs => crates/unified/src/particle_editor/mod.rs +0 -329
@@ 1,329 0,0 @@
use crate::{
    particle_editor::{hooks::hooks_plugin, spawn::spawn_plugin},
    particles::{LifetimeCurve, ParticleEffect, RandF32, RandUsize, RandVec2},
};
use crate::prelude::*;
use bevy_egui::{EguiContexts, EguiPlugin, EguiPrimaryContextPass, egui};
use ordered_float::OrderedFloat;
use std::collections::BTreeMap;

mod ecs;
mod hooks;
mod spawn;

pub fn particle_editor_plugin(app: &mut App) {
    app.add_plugins(DefaultPlugins);
    app.insert_resource(Gravity::ZERO);
    app.add_plugins(EguiPlugin::default());
    app.add_plugins(PhysicsPlugins::default().with_length_unit(100.0));
    app.add_systems(Startup, setup_camera_system);
    app.add_systems(EguiPrimaryContextPass, editor_ui);
    app.add_systems(Startup, setup_editor_effect);
    app.add_plugins(spawn_plugin);
    app.add_plugins(hooks_plugin);
    app.insert_resource(EditorResource {
        ser_field: String::new(),
        status: "Ready".to_string(),
        add_scale_t: 0.0,
        add_scale_v: 0.0,
        add_color_t: 0.0,
        add_color_v: [0u8; 4],
        scale_curve: LifetimeCurve::new(&[(0.0f32, 5.0), (2.0, 0.0)])
            .0
            .iter()
            .map(|u| (*u.0, *u.1))
            .collect::<Vec<_>>(),
        color_curve: vec![(OrderedFloat(0.0f32), [255, 0, 0, 255])],
    });
}

fn setup_editor_effect(mut commands: Commands) {
    commands.spawn((
        ParticleEffect {
            lifetime_seconds: RandF32 {
                value: 2.0,
                randomness: 0.1,
            },
            batch_spawn_delay_seconds: RandF32 {
                value: 0.1,
                randomness: 0.05,
            },
            particles_in_batch: RandUsize {
                value: 1,
                randomness: 1,
            },
            initial_linear_velocity: RandVec2 {
                x: RandF32 {
                    value: 0.0,
                    randomness: 0.5,
                },
                y: RandF32 {
                    value: 10.0,
                    randomness: 1.0,
                },
            },
            initial_angular_velocity: RandF32 {
                value: 1.0,
                randomness: 0.5,
            },
            scale: LifetimeCurve::new(&[(0.0f32, 5.0), (2.0, 0.0)]),
            color: LifetimeCurve::new(&[(0.0f32, Srgba::new(1.0, 0.0, 0.0, 1.0).into())]),
        },
        Transform::from_xyz(0.0, 0.0, 0.0),
    ));
}

fn setup_camera_system(mut commands: Commands) {
    commands.spawn((Camera2d, Transform::from_scale(Vec3::splat(0.1))));
}

#[derive(Resource)]
struct EditorResource {
    ser_field: String,
    status: String,

    add_scale_t: f32,
    add_scale_v: f32,

    add_color_t: f32,
    add_color_v: [u8; 4],

    scale_curve: Vec<(OrderedFloat<f32>, f32)>,
    color_curve: Vec<(OrderedFloat<f32>, [u8; 4])>,
}

fn editor_ui(
    mut contexts: EguiContexts,
    effect: Single<&mut ParticleEffect>,
    mut editor_resource: ResMut<EditorResource>,
) -> Result {
    let mut effect = effect.into_inner();
    egui::Window::new("Particle Effect")
        .resizable(false)
        .show(contexts.ctx_mut()?, |ui| {
            egui::Grid::new("effect").striped(true).show(ui, |ui| {
                draw_rand_f32(&mut effect.lifetime_seconds, "Lifetime (seconds): ", ui);
                draw_rand_f32(
                    &mut effect.batch_spawn_delay_seconds,
                    "Delay in between batches (seconds): ",
                    ui,
                );
                draw_rand_usize(
                    &mut effect.particles_in_batch,
                    "Number of particles in batch: ",
                    ui,
                );

                draw_rand_f32(
                    &mut effect.initial_linear_velocity.x,
                    "Linear velocity (x-axis, m/s): ",
                    ui,
                );
                draw_rand_f32(
                    &mut effect.initial_linear_velocity.y,
                    "Linear velocity (y-axis, m/s): ",
                    ui,
                );
                draw_rand_f32(
                    &mut effect.initial_angular_velocity,
                    "Angular velocity (radians/second): ",
                    ui,
                );

                ui.separator();
                ui.label("Scale curve");
                if ui.button("sort").clicked() {
                    editor_resource.scale_curve.sort_by_key(|u| u.0);
                }
                ui.end_row();

                editor_resource.scale_curve.retain_mut(|(k, v)| {
                    ui.label("scale t=");
                    ui.add(
                        egui::DragValue::new(k.as_mut())
                            .speed(0.01)
                            .range(0.0f32..=effect.lifetime_seconds.value),
                    );
                    ui.label("v=");
                    ui.add(egui::DragValue::new(v).speed(0.01));
                    let r = ui.button("-");
                    ui.end_row();
                    !r.clicked()
                });

                ui.separator();
                ui.end_row();

                ui.label("new scale: t=");
                ui.add(
                    egui::DragValue::new(&mut editor_resource.add_scale_t)
                        .speed(0.01)
                        .range(0.0f32..=effect.lifetime_seconds.value),
                );
                ui.label("v=");
                ui.add(egui::DragValue::new(&mut editor_resource.add_scale_v).speed(0.01));
                if ui.button("+").clicked() {
                    let new_v = (
                        OrderedFloat(editor_resource.add_scale_t),
                        editor_resource.add_scale_v,
                    );
                    editor_resource.scale_curve.push(new_v);
                }
                ui.end_row();

                effect.scale = LifetimeCurve(
                    editor_resource
                        .scale_curve
                        .iter()
                        .copied()
                        .collect::<BTreeMap<_, _>>(),
                );

                ui.separator();
                ui.end_row();

                ui.separator();
                ui.label("Color curve");
                if ui.button("sort").clicked() {
                    editor_resource.color_curve.sort_by_key(|u| u.0);
                }
                ui.end_row();

                editor_resource.color_curve.retain_mut(|(k, v)| {
                    ui.label("color t=");
                    ui.add(
                        egui::DragValue::new(k.as_mut())
                            .speed(0.01)
                            .range(0.0f32..=effect.lifetime_seconds.value),
                    );
                    ui.label("v=");
                    ui.color_edit_button_srgba_unmultiplied(v);
                    let r = ui.button("-");
                    ui.end_row();
                    !r.clicked()
                });

                ui.separator();
                ui.end_row();

                ui.label("new color: t=");
                ui.add(
                    egui::DragValue::new(&mut editor_resource.add_color_t)
                        .speed(0.01)
                        .range(0.0f32..=effect.lifetime_seconds.value),
                );
                ui.label("v=");
                ui.color_edit_button_srgba_unmultiplied(&mut editor_resource.add_color_v);
                if ui.button("+").clicked() {
                    let new_v = (
                        OrderedFloat(editor_resource.add_color_t),
                        editor_resource.add_color_v,
                    );
                    editor_resource.color_curve.push(new_v);
                }
                ui.end_row();

                let curve_copied = editor_resource.color_curve.clone();
                effect.color = LifetimeCurve(
                    curve_copied
                        .iter()
                        .map(|(u, v)| {
                            (
                                *u,
                                Color::Srgba(Srgba::new(
                                    f32::from(v[0]) / 256.0,
                                    f32::from(v[1]) / 256.0,
                                    f32::from(v[2]) / 256.0,
                                    f32::from(v[3]) / 256.0,
                                )),
                            )
                        })
                        .collect::<BTreeMap<_, _>>(),
                );

                ui.separator();
                ui.end_row();
            });
            ui.horizontal(|ui| {
                if ui.button("Generate").clicked() {
                    effect.scale = LifetimeCurve(
                        editor_resource
                            .scale_curve
                            .iter()
                            .copied()
                            .collect::<BTreeMap<_, _>>(),
                    );
                    let curve_copied = editor_resource.color_curve.clone();
                    effect.color = LifetimeCurve(
                        curve_copied
                            .iter()
                            .map(|(u, v)| {
                                (
                                    *u,
                                    Color::Srgba(Srgba::new(
                                        f32::from(v[0]) / 256.0,
                                        f32::from(v[1]) / 256.0,
                                        f32::from(v[2]) / 256.0,
                                        f32::from(v[3]) / 256.0,
                                    )),
                                )
                            })
                            .collect::<BTreeMap<_, _>>(),
                    );

                    editor_resource.ser_field = ron::ser::to_string(effect.as_ref()).unwrap();
                    editor_resource.status = "Ready; Generated OK".to_string();
                }
                if ui.button("Load").clicked() {
                    match ron::from_str(&editor_resource.ser_field) {
                        Ok(e) => {
                            *effect = e;
                            editor_resource.scale_curve = effect
                                .scale
                                .0
                                .iter()
                                .map(|u| (*u.0, *u.1))
                                .collect::<Vec<_>>();
                            editor_resource.color_curve = effect
                                .color
                                .0
                                .iter()
                                .map(|u| {
                                    (*u.0, {
                                        let mut r = [0u8; 4];
                                        let srgba: Srgba = (*u.1).into();
                                        r[0] = (srgba.red * 256.0).floor() as u8;
                                        r[1] = (srgba.green * 256.0).floor() as u8;
                                        r[2] = (srgba.blue * 256.0).floor() as u8;
                                        r[3] = (srgba.alpha * 256.0).floor() as u8;
                                        r
                                    })
                                })
                                .collect::<Vec<_>>();
                        }
                        Err(e) => {
                            editor_resource.status = e.to_string();
                        }
                    }
                }
            });
            ui.text_edit_multiline(&mut editor_resource.ser_field);
            ui.text_edit_multiline(&mut editor_resource.status.as_str());
        });
    Ok(())
}

fn draw_rand_f32(v: &mut RandF32, l: &str, ui: &mut egui::Ui) {
    ui.label(l);
    ui.add(egui::DragValue::new(&mut v.value).speed(0.01));
    ui.label("variance:");
    ui.add(egui::DragValue::new(&mut v.randomness).speed(0.01));
    ui.end_row();
}
fn draw_rand_usize(v: &mut RandUsize, l: &str, ui: &mut egui::Ui) {
    ui.label(l);
    ui.add(egui::DragValue::new(&mut v.value).speed(0.1));
    ui.label("variance:");
    ui.add(egui::DragValue::new(&mut v.randomness).speed(0.1));
    ui.end_row();
}

D crates/unified/src/particle_editor/spawn.rs => crates/unified/src/particle_editor/spawn.rs +0 -100
@@ 1,100 0,0 @@
use std::time::Duration;
use avian2d::prelude::*;
use crate::prelude::*;

use crate::{
    particle_editor::ecs::{CircleMesh, LifetimeTimer, ParentEffect, Particle, SpawnDelayTimer},
    particles::ParticleEffect,
};

pub fn spawn_plugin(app: &mut App) {
    app.add_systems(Update, spawn_particles);
    app.add_systems(Update, lifetime_particles);
}

fn spawn_particles(
    mut commands: Commands,
    particle_effects: Query<(&Transform, &ParticleEffect, &mut SpawnDelayTimer)>,
    time: ResMut<Time>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for (transform, effect, mut delay_timer) in particle_effects {
        delay_timer.0.tick(time.delta());
        if delay_timer.0.just_finished() {
            for _ in 0..effect.particles_in_batch.sample(&mut rand::rng()) {
                let circle = CircleMesh(
                    meshes.add(Circle::new(1.0)),
                    materials.add(
                        effect
                            .color
                            .sample(effect.color.clamp_time(0.0).unwrap())
                            .unwrap(),
                    ),
                );
                commands.spawn((
                    RigidBody::Dynamic,
                    Particle,
                    transform.with_scale(Vec3::splat(
                        effect
                            .scale
                            .sample(effect.scale.clamp_time(0.0).unwrap())
                            .unwrap(),
                    )),
                    Mesh2d(circle.0.clone()),
                    MeshMaterial2d(circle.1.clone()),
                    LinearVelocity(effect.initial_linear_velocity.sample(&mut rand::rng())),
                    AngularVelocity(effect.initial_angular_velocity.sample(&mut rand::rng())),
                    LifetimeTimer(Timer::from_seconds(
                        effect.lifetime_seconds.sample(&mut rand::rng()),
                        TimerMode::Once,
                    )),
                    circle,
                    ParentEffect(effect.clone()),
                ));
            }
            delay_timer.0.set_duration(Duration::from_secs_f32(
                effect.batch_spawn_delay_seconds.sample(&mut rand::rng()),
            ));
            delay_timer.0.reset();
        }
    }
}

fn lifetime_particles(
    mut commands: Commands,
    mut particles: Query<
        (
            Entity,
            &mut LifetimeTimer,
            &mut Transform,
            &CircleMesh,
            &ParentEffect,
        ),
        With<Particle>,
    >,
    time: ResMut<Time>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    for (entity, mut timer, mut transform, circle, parent) in &mut particles {
        timer.0.tick(time.delta());
        transform.scale = Vec3::splat(
            parent
                .0
                .scale
                .sample(parent.0.scale.clamp_time(timer.0.elapsed_secs()).unwrap())
                .unwrap(),
        );
        materials.get_mut(&circle.1).unwrap().color = parent
            .0
            .color
            .sample(parent.0.color.clamp_time(timer.0.elapsed_secs()).unwrap())
            .unwrap();
        if timer.0.just_finished() {
            commands.entity(entity).despawn();
            meshes.remove(&circle.0);
            materials.remove(&circle.1);
        }
    }
}

D crates/unified/src/particles.rs => crates/unified/src/particles.rs +0 -148
@@ 1,148 0,0 @@
use bevy::color::{Color, LinearRgba};
use bevy::math::Vec2;
use crate::prelude::*;
use ordered_float::OrderedFloat;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::ops::Bound::{Excluded, Included, Unbounded};
use rand::RngExt;

#[derive(Deserialize, Serialize, Component, Clone)]
pub struct ParticleEffect {
    // -- lifetime / spawning -- //
    /// Particle lifetime in seconds
    pub lifetime_seconds: RandF32,
    /// Delay inbetween each batch of particles spawned
    pub batch_spawn_delay_seconds: RandF32,
    /// Number of distinct particles spawned per batch
    pub particles_in_batch: RandUsize,

    // -- velocity -- //
    /// Initial linear velocity added to the particle's velocity when it is spawned
    pub initial_linear_velocity: RandVec2,
    /// Initial angular velocity added to the particle's rotation when it is spawned
    pub initial_angular_velocity: RandF32,

    // -- scale -- //

    // Scale curve over the lifetime of the particle
    pub scale: LifetimeCurve<f32>,

    // -- color -- //

    // Color curve over the lifetime of the particle
    pub color: LifetimeCurve<Color>,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct LifetimeCurve<P: Lerp>(pub BTreeMap<OrderedFloat<f32>, P>);
impl<P: Lerp + Copy> LifetimeCurve<P> {
    pub fn new<'a>(points: impl IntoIterator<Item = &'a (f32, P)>) -> Self
    where
        P: 'a,
    {
        Self(
            points
                .into_iter()
                .map(|u| (OrderedFloat(u.0), u.1))
                .collect::<BTreeMap<_, _>>(),
        )
    }

    /// Sample for the value at T. Returns None if the curve has no points, or if T is outside
    /// the domain of the points specified.
    #[must_use]
    pub fn sample(&self, at: f32) -> Option<P> {
        if self.0.is_empty() {
            return None;
        }

        if self.0.len() == 1 && (at - self.0.iter().nth(0).unwrap().0.0).abs() < 0.01 {
            return Some(*self.0.iter().nth(0).unwrap().1);
        }

        // Find A, the point "to the left" of `at`
        let mut r1 = self.0.range((Unbounded, Included(OrderedFloat(at))));
        let (a_key, a_val) = r1.next_back()?;

        let (b_key, b_val) = if (at - self.0.iter().last().unwrap().0.0).abs() < 0.01 {
            self.0.iter().last().unwrap()
        } else {
            // Find B, the point "to the right" of `at`
            let mut r2 = self.0.range((Excluded(OrderedFloat(at)), Unbounded));
            r2.next()?
        };

        if a_key == b_key && b_key == self.0.iter().last().unwrap().0 {
            return Some(*b_val);
        }

        // Calculate `t` (value from 0 - 1 indicating our progress from A to B)
        let t = (at - **a_key) / *(b_key - a_key);

        // Lerp
        Some(a_val.lerp(b_val, t))
    }

    /// Given an input time value, use the ends of the spline's time to clamp
    /// the input value.
    #[must_use]
    pub fn clamp_time(&self, time: f32) -> Option<f32> {
        let first = self.0.iter().nth(0)?;
        let last = self.0.iter().last()?;

        Some(time.clamp(first.0.0, last.0.0))
    }
}

pub trait Lerp {
    #[must_use]
    fn lerp(&self, other: &Self, t: f32) -> Self;
}
impl Lerp for f32 {
    fn lerp(&self, other: &Self, t: f32) -> Self {
        (1.0 - t) * self + t * other
    }
}
impl Lerp for Color {
    fn lerp(&self, other: &Self, t: f32) -> Self {
        let a: LinearRgba = (*self).into();
        let b: LinearRgba = (*other).into();
        Color::from(a * (1.0 - t) + b * t)
    }
}

#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandF32 {
    pub value: f32,
    pub randomness: f32,
}
impl RandF32 {
    pub fn sample(&self, rng: &mut impl Rng) -> f32 {
        rng.random_range(self.value - self.randomness..self.value + self.randomness)
    }
}

#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandUsize {
    pub value: usize,
    pub randomness: usize,
}
impl RandUsize {
    pub fn sample(&self, rng: &mut impl Rng) -> usize {
        let lower_bound = self.value.saturating_sub(self.randomness);
        rng.random_range(lower_bound..self.value + self.randomness)
    }
}

#[derive(Deserialize, Serialize, Clone, Copy)]
pub struct RandVec2 {
    pub x: RandF32,
    pub y: RandF32,
}
impl RandVec2 {
    pub fn sample(&self, rng: &mut impl Rng) -> Vec2 {
        Vec2::new(self.x.sample(rng), self.y.sample(rng))
    }
}

M crates/unified/src/physics.rs => crates/unified/src/physics.rs +1 -88
@@ 1,90 1,3 @@
use crate::prelude::*;
use bevy_replicon::prelude::*;
use avian2d::prelude::*;

pub fn register_physics_components_for_replication(app: &mut App) {
    app
        // Colliders
        .replicate::<Collider>()
        .replicate::<ColliderDisabled>()
        /*.replicate::<CollidingEntities>()
        .replicate::<CollisionMargin>()
        .replicate::<Sensor>()*/

        // Dynamics - Rigidbodies
        //.replicate::<AngularDamping>()
        .replicate::<AngularVelocity>()
        /*.replicate::<Dominance>()
        .replicate::<Friction>()
        .replicate::<GravityScale>()
        .replicate::<LinearDamping>()*/
        .replicate::<LinearVelocity>()
        /*
        .replicate::<LockedAxes>()
        .replicate::<MaxAngularSpeed>()
        .replicate::<MaxLinearSpeed>()
        .replicate::<Restitution>()
        .replicate::<RigidBodyDisabled>()
         */
        .replicate::<RigidBody>()

        // Dynamics - Rigidbodies - Forces

        //.replicate::<ConstantAngularAcceleration>()
        .replicate::<ConstantForce>()
        //.replicate::<ConstantLinearAcceleration>()
        .replicate::<ConstantLocalForce>()
        //.replicate::<ConstantLocalLinearAcceleration>()
        //.replicate::<ConstantTorque>()


        // Dynamics - Rigidbodies - Mass properties

        //.replicate::<AngularInertia>()
        //.replicate::<CenterOfMass>()
        //.replicate::<ColliderDensity>()
        //.replicate::<ColliderMassProperties>()
        //.replicate::<ComputedAngularInertia>()
        //.replicate::<ComputedCenterOfMass>()
        //.replicate::<ComputedMass>()


        .replicate::<Mass>()

        /*
        .replicate::<NoAutoAngularInertia>()
        .replicate::<NoAutoCenterOfMass>()
        .replicate::<NoAutoMass>()

         */

        // Dynamics - Rigidbodies - Sleeping
        /*
        .replicate::<SleepThreshold>()
        .replicate::<SleepTimer>()
        .replicate::<Sleeping>()
        .replicate::<SleepingDisabled>()

         */

        // Interpolation - TODO

        // Physics transform
        .replicate::<Position>()
        .replicate::<Rotation>()

        // Spatial query
        //.replicate::<RayCaster>()

        // Joints

        //.replicate::<DistanceJoint>()
        .replicate::<FixedJoint>()
        //.replicate::<JointCollisionDisabled>()
        //.replicate::<JointDamping>()
        .replicate::<JointDisabled>()
        //.replicate::<JointForces>()
        //.replicate::<PrismaticJoint>()
        //.replicate::<RevoluteJoint>()
;
}
\ No newline at end of file
pub fn register_physics_components_for_replication(_app: &mut App) {}

M crates/unified/src/prelude.rs => crates/unified/src/prelude.rs +2 -2
@@ 1,3 1,3 @@
pub use bevy::prelude::*;
pub use bevy_replicon::prelude::*;
pub use avian2d::prelude::*;
\ No newline at end of file
pub use avian2d::prelude::*;
pub use serde::{Serialize, Deserialize};
\ No newline at end of file

M crates/unified/src/server/craft.rs => crates/unified/src/server/craft.rs +1 -1
@@ 7,7 7,7 @@ pub fn craft_plugin(app: &mut App) {
}

fn receive_crafting_request(
    mut craft_part_request: MessageReader<FromClient<CraftPartRequest>>,
    mut craft_part_request: MessageReader<CraftPartRequest>,
    part_query: Query<(&Transform, &LinearVelocity, &Part, &PartInShip)>,
    player_query: Query<(Entity, &Transform, &LinearVelocity, &Part), With<Player>>,
    parts_query: Query<&Parts>,

M crates/unified/src/server/drill.rs => crates/unified/src/server/drill.rs +1 -1
@@ 5,7 5,7 @@ pub fn drill_plugin(app: &mut App) {
}

fn toggle_drill(
    mut toggle_drill_reader: MessageReader<FromClient<ToggleDrillEvent>>,
    mut toggle_drill_reader: MessageReader<ToggleDrillEvent>,
    mut drills: Query<&mut Drill>,
) {
    for toggle_drill_event in toggle_drill_reader.read() {

M crates/unified/src/server/mod.rs => crates/unified/src/server/mod.rs +3 -97
@@ 22,38 22,15 @@ use crate::server::part::part_management_plugin;
use crate::server::planets::planets_plugin;
use crate::server::player::player_management_plugin;
use crate::server::system_sets::{PlayerInputSet, WorldUpdateSet};
use aeronet::io::Session;
use aeronet::io::connection::{DisconnectReason, Disconnected, LocalAddr};
use aeronet::io::server::Server;
use aeronet_replicon::server::AeronetRepliconServer;
use aeronet_websocket::server::WebSocketServer;
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use std::net::SocketAddr;
use crate::server::orbit::OrbitPlugin;
use crate::server::player::thrust::server_thrust_plugin;

pub struct ServerPlugin {
    pub bind: SocketAddr,
    pub max_clients: usize,
}
pub struct ServerPlugin;

impl Plugin for ServerPlugin {
    fn build(&self, app: &mut App) {
        let config = self.websocket_config();

        app.add_systems(Startup, move |mut commands: Commands| {
            let server = commands
                .spawn(Name::new("ws-server-stk"))
                .insert(AeronetRepliconServer)
                .insert(Transform::from_xyz(0.0, 0.0, 0.0))
                .queue(WebSocketServer::open(config.clone()))
                .id();

            info!(entity_id=?server, "opening websocket server");
        })
        .add_observer(on_opened)
        .add_observer(on_connected)
        .add_observer(on_disconnected)
        app.add_systems(Startup, player::join::spawn_singleplayer_player)
        .add_plugins(planets_plugin)
        .add_plugins(newtonian_gravity_plugin)
        .add_plugins(player_management_plugin)


@@ 68,85 45,14 @@ impl Plugin for ServerPlugin {
        .add_plugins(OrbitPlugin)
        .add_plugins(damping_plugin)
        .configure_sets(Update, WorldUpdateSet.before(PlayerInputSet));
        //.configure_sets(Update, PlayerInputSet.before(PhysicsSet::SyncBackend));
    }
}
impl ServerPlugin {
    fn websocket_config(&self) -> aeronet_websocket::server::ServerConfig {
        aeronet_websocket::server::ServerConfig::builder()
            .with_bind_address(self.bind)
            .with_no_encryption()
    }
}

#[derive(Component, Debug)]
#[require(Replicated)]
pub struct ConnectedGameEntity {
    pub network_entity: Entity,
}
#[derive(Component)]
#[require(Replicated)]
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>,
    mut commands: Commands,
) {
    let client = trigger.event_target();
    let Ok(&ChildOf(server)) = clients.get(client) else {
        return;
    };
    info!(?client, ?server, "client connected");

    // spawn the player

    let player = commands
        .spawn(ConnectedGameEntity {
            network_entity: client,
        })
        .id();

    commands.entity(client).insert((
        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/part.rs => crates/unified/src/server/part.rs +3 -6
@@ 4,7 4,6 @@ use crate::attachment::{Joint, JointId, JointOf, Joints, Peer, SnapOf, SnapOfJoi
use crate::config::part::{CoolingConfig, CraftingConfig, DrillConfig, JointConfig, PartConfig, StorageConfig, StorageType};
use crate::ecs::{CanCraft, Cooler, Drill, Part, PartHandle, Radiator, SingleStorage, Temperature, VariableStorage};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::ecs::thruster::{PartThrusters, Thruster, ThrusterBundle, ThrusterId, ThrusterOfPart};

pub fn part_management_plugin(app: &mut App) {


@@ 19,7 18,6 @@ pub struct SpawnPartBundle {
}

#[derive(Component)]
#[require(Replicated)]
pub struct SpawnPartRequest(pub Handle<PartConfig>);

// wait for parts assets to be ready, then spawn the full part


@@ 75,7 73,7 @@ fn handle_part_reloading(
                        commands.entity(*thruster).despawn();
                    }
                    spawn_thrusters(config, existing_part.0, commands.reborrow());
                    

                    // update all joints
                    let mut used_joints = vec![];
                    for joint_id in &**existing_part.2 {


@@ 194,14 192,14 @@ fn spawn_joint_bundle(joint: &JointConfig, part: &PartConfig, parent: &Entity) -
    let joint_transform: Transform = j_comp.transform;
    let joint_of = JointOf(*parent);

    (j_comp, joint_transform, joint_of, Replicated)
    (j_comp, joint_transform, joint_of)
}
fn spawn_snap_bundle(joint: &JointConfig, parent: &Entity, p_joint: &Entity) -> impl Bundle {
    let snap_transform: Transform = joint.snap.into();
    let snap_for = SnapOf(*parent);
    let snap_of = SnapOfJoint(*p_joint);

    (snap_transform, snap_for, snap_of, Replicated)
    (snap_transform, snap_for, snap_of)
}
fn spawn_joints(config: &PartConfig, parent: Entity, mut commands: Commands) {
    for joint in &config.joints {


@@ 228,4 226,3 @@ fn spawn_thrusters(config: &PartConfig, part: Entity, mut commands: Commands) {
            });
    }
}


M crates/unified/src/server/planets.rs => crates/unified/src/server/planets.rs +0 -1
@@ 1,7 1,6 @@
use crate::{config::planet::{Planet, PlanetBundle, PlanetConfigCollection}, ecs::PlanetSensor, world_config::WorldConfigResource};
use bevy::{asset::Handle, math::DVec3};
use crate::prelude::*;
use bevy_replicon::prelude::Replicated;
use crate::config::planet::{PlanetSpring, PlanetSpringJoint};

pub fn planets_plugin(app: &mut App) {

M crates/unified/src/server/player.rs => crates/unified/src/server/player.rs +9 -36
@@ 2,13 2,11 @@ pub mod join;
pub mod thrust;

use crate::attachment::{Joint, JointOf, Joints, PartInShip, Peer, SnapOf, SnapOfJoint};
use crate::ecs::{DragRequestEvent, Part, Player, PlayerStorage};
use crate::ecs::{DragRequestEvent, Me, Part, Player, PlayerStorage};
use crate::server::damping::ModuleJointDamping;
use crate::server::system_sets::PlayerInputSet;
use crate::server::ConnectedNetworkEntity;
use crate::prelude::*;
use crate::world_config::WorldConfigResource;
use bevy_replicon::prelude::{ClientId, FromClient};
use std::f64::consts::PI;

pub fn player_management_plugin(app: &mut App) {


@@ 27,7 25,6 @@ pub fn player_management_plugin(app: &mut App) {
}

#[derive(Component)]
#[require(Replicated)]
struct PartiallyDisconnected;

/// Partial Disconnects are created when a part is disconnected to indicate that a disconnect has started.


@@ 220,7 217,8 @@ fn disconnect_part(
}

fn dragging(
    mut events: MessageReader<FromClient<DragRequestEvent>>,
    mut events: MessageReader<DragRequestEvent>,
    me: Query<Entity, With<Me>>,
    mut parts: Query<
        (
            &mut Transform,


@@ 236,29 234,15 @@ fn dragging(
    snaps: Query<(&SnapOf, &SnapOfJoint)>,
    joints: Query<(&Joint, &JointOf, &Transform, Option<&Peer>, Entity)>,
    q_joints: Query<&Joints>,
    clients: Query<&ConnectedNetworkEntity>,
    q_ls_me: Query<Entity, With<crate::ecs::Me>>,
    world_config: Res<WorldConfigResource>,
    mut commands: Commands,
) {
    let Some(world_config) = &world_config.config else {
        return;
    };
    for FromClient {
        client_id,
        message: event,
    } in events.read()
    {
        let player_hearty_entity = match client_id {
            ClientId::Client(client_entity) => {
                let ConnectedNetworkEntity {
                    game_entity: player_hearty_entity,
                } = clients.get(*client_entity).unwrap();
                player_hearty_entity
            },
            ClientId::Server => &q_ls_me.iter().next().unwrap()
        };
    let Ok(player_hearty_entity) = me.single() else { return };

    for event in events.read() {
        debug!(?event, "got drag request event");

        let teleport_to_translation;


@@ 330,15 314,13 @@ fn dragging(
                source_part
            };

            let allowed = target_joint.1.0 == *player_hearty_entity
                || target_part.1.is_some_and(|u| u.0 == *player_hearty_entity);
            let allowed = target_joint.1.0 == player_hearty_entity
                || target_part.1.is_some_and(|u| u.0 == player_hearty_entity);
            if !allowed {
                warn!("drag request: this player cannot move this part, ignoring request");
                continue;
            }

            // TODO - validate source_part?

            // great, we have a valid peering request

            let mut processed = vec![source_joint.4];


@@ 351,15 333,6 @@ fn dragging(
            );

            // create the joint...
            /*let joint = PrismaticJoint::new(target_part.2, source_part.2)
                .with_slider_axis(source_joint.2.translation.xy().normalize())
                .with_local_anchor1(target_joint.2.translation.xy())
                .with_local_basis1(target_joint.0.transform.rotation.to_euler(EulerRot::ZYX).0 + PI
                    - source_joint.0.transform.rotation.to_euler(EulerRot::ZYX).0)
                .with_limits(0.0, 0.0)
                .with_align_compliance(world_config.part.joint_align_compliance)
                .with_angle_compliance(world_config.part.joint_angle_compliance)
                .with_limit_compliance(world_config.part.joint_limit_compliance);*/
            let joint = FixedJoint::new(target_part.2, source_part.2)
                .with_local_anchor1(target_joint.2.translation.xy().into())
                .with_local_basis1(target_joint.0.transform.rotation.to_euler(EulerRot::ZYX).0 as f64 + PI


@@ 385,8 358,8 @@ fn dragging(

            // propagate PartInShip...

            let part_in_ship = if target_joint.1.0 == *player_hearty_entity {
                PartInShip(*player_hearty_entity)
            let part_in_ship = if target_joint.1.0 == player_hearty_entity {
                PartInShip(player_hearty_entity)
            } else {
                PartInShip(target_part.1.unwrap().0) // unwrap: checked above (during 'allowed' calculation)
            };

M crates/unified/src/server/player/join.rs => crates/unified/src/server/player/join.rs +6 -24
@@ 1,7 1,7 @@
use crate::attachment::PartInShip;
use crate::config::planet::Planet;
use crate::config::world::GlobalWorldConfig;
use crate::ecs::{Hi, Player, PlayerStorage};
use crate::ecs::{Me, Player, PlayerStorage};
use crate::prelude::*;
use crate::server::ConnectedGameEntity;
use crate::server::part::SpawnPartRequest;


@@ 55,7 55,6 @@ fn join_player(joined_player: Entity, mut commands: Commands, wc: &GlobalWorldCo
}

#[derive(Component)]
#[require(Replicated)]
pub struct PendingPlayer;

pub fn handle_new_players(


@@ 63,17 62,8 @@ pub fn handle_new_players(
    q_new_clients: Query<(Entity, &ConnectedGameEntity), Added<ConnectedGameEntity>>,
    world_config: Res<WorldConfigResource>,
    planets: Query<(&Transform, &LinearVelocity, &Planet)>,
    mut hi_writer: MessageWriter<ToClients<Hi>>,
    asset_server: Res<AssetServer>,
) {
    for joined_player in &q_new_clients {
        hi_writer.write(ToClients {
            mode: SendMode::Direct(ClientId::Client(joined_player.1.network_entity)),
            message: Hi {
                you_are: joined_player.0
            }
        });
    }
    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 {


@@ 104,17 94,9 @@ pub fn handle_pending_players(
    }
}

pub fn ls_magically_invent_player(
    mut commands: Commands,
) {
    // Magically invent a player for listenserver
    let fake_network_entity = commands.spawn(Replicated).id();
    let local_player = commands.spawn((
        ConnectedGameEntity {
            network_entity: fake_network_entity,
        },
        Replicated,
        crate::ecs::Me
    )).id();
    debug!(?fake_network_entity, ?local_player, "listenserver: magically invented a player");
pub fn spawn_singleplayer_player(mut commands: Commands) {
    commands.spawn((
        ConnectedGameEntity { network_entity: Entity::PLACEHOLDER },
        Me,
    ));
}

M crates/unified/src/server/player/thrust.rs => crates/unified/src/server/player/thrust.rs +11 -48
@@ 1,13 1,11 @@
//! # Server thrust handling
//! The bulk of the actual thrust work is done in the thrust solver on the client.
//! It sends us it's `ThrustSolution` when it's done; in this file we process it
//! and apply it to the physics simulation.
//! The thrust solver runs on the client and sends a `ThrustSolution` message;
//! this file receives it and applies it to the physics simulation.

use crate::attachment::Parts;
use crate::ecs::{Part, Temperature};
use crate::ecs::{Me, Part, Temperature};
use crate::ecs::thruster::{Thruster, ThrusterOfPart};
use crate::prelude::*;
use crate::server::ConnectedNetworkEntity;
use crate::attachment::Parts;
use crate::thrust::ThrustSolution;

pub fn server_thrust_plugin(app: &mut App) {


@@ 16,34 14,14 @@ pub fn server_thrust_plugin(app: &mut App) {
        .add_systems(FixedUpdate, apply_thrust_solutions);
}

/// Handle new `ThrustSolution`s from clients as they come in
fn process_thrust_events(
    mut events: MessageReader<FromClient<ThrustSolution>>,

    clients: Query<&ConnectedNetworkEntity>,
    q_ls_me: Query<Entity, With<crate::ecs::Me>>,
    mut commands: Commands
    mut events: MessageReader<ThrustSolution>,
    me_entity: Query<Entity, With<Me>>,
    mut commands: Commands,
) {
    // For each event from a client...
    for FromClient {
        client_id,
        message: thrust_solution,
    } in events.read()
    {
        // Find the hearty entity of the player...
        let player_hearty_entity = match client_id {
            ClientId::Client(client_entity) => {
                let ConnectedNetworkEntity {
                    game_entity: player_hearty_entity,
                } = clients.get(*client_entity).unwrap();
                player_hearty_entity
            },
            ClientId::Server => &q_ls_me.iter().next().unwrap()
        };

        // and apply the new thrust solution
        commands.entity(*player_hearty_entity).insert(thrust_solution.clone());
        trace!("installed thrust solution {:?}", thrust_solution);
    for thrust_solution in events.read() {
        let Ok(player_entity) = me_entity.single() else { return };
        commands.entity(player_entity).insert(thrust_solution.clone());
    }
}



@@ 54,46 32,31 @@ fn apply_thrust_solutions(
    mut parts: Query<(Forces, &mut Temperature), With<Part>>,
    time: Res<Time>,
) {
    //gizmos.arrow_2d(
    //             thruster.2.translation().xy(),
    //             thruster.2.translation().xy() + thruster.2.rotation().mul_vec3(thruster.0.thrust_vector.extend(0.0)).xy(),
    //             color
    //         );

    // Iterate through all players with a ThrustSolution
    for (player_entity, thrust_solution, maybe_parts) in players {
        // If their reported thrust solution didn't converge, do nothing
        if !thrust_solution.converged {
            debug!(?player_entity, "ignoring unconverged thrust solution");
        }
        // If it's empty, exit early (no reason to waste time)
        if thrust_solution.thrusters_on.is_empty() { continue }

        // Collect a list of all parts in the player's ship, to validate
        // the thrusters the player sends
        let attached_parts = if let Some(parts) = maybe_parts {
            parts.as_slice()
        } else {
            &[]
        };

        // Go over each active thruster in the thrust solution...
        for thruster_entity in &thrust_solution.thrusters_on {
            // ...load it's data...
            let Ok((thruster_info, parent_part, thruster_transform)) = thrusters.get(*thruster_entity) else {
                debug!(?thruster_entity, "couldn't find thruster to apply force");
                continue
            };

            // ...verify this user is allowed to control this thruster...
            let parent_is_hearty = parent_part.0 == player_entity;
            let parent_is_in_ship = attached_parts.contains(&parent_part.0);
            if !(parent_is_hearty || parent_is_in_ship) {
                debug!(?thruster_entity, "ignoring disallowed thruster action");
                continue
            } // not permitted
            }

            // great, it's valid; apply the force and increase temperature
            let (mut part_forces, mut temperature) = parts.get_mut(parent_part.0).unwrap();
            temperature.0 += thruster_info.heat_constant * (thruster_info.exhaust_temperature - temperature.0) * time.delta_secs() as f64;


M crates/unified/src/server_plugins.rs => crates/unified/src/server_plugins.rs +3 -16
@@ 1,15 1,9 @@
use aeronet_replicon::server::AeronetRepliconServerPlugin;
use aeronet_websocket::server::WebSocketServerPlugin;
use bevy::app::{PluginGroup, PluginGroupBuilder};
use std::net::SocketAddr;
use avian2d::PhysicsPlugins;
use avian2d::prelude::IslandPlugin;

pub struct ServerPluginGroup {
    pub bind: SocketAddr,
    pub tick_rate: f32,
    pub max_clients: usize,
}
pub struct ServerPluginGroup;

impl PluginGroup for ServerPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()


@@ 18,14 12,7 @@ impl PluginGroup for ServerPluginGroup {
                    .with_length_unit(100.0)
                    .build()
                    .disable::<IslandPlugin>()
                //.set(PhysicsInterpolationPlugin::interpolate_all())
            )
            .add(WebSocketServerPlugin)
            .add(AeronetRepliconServerPlugin)
            .add(crate::server::ServerPlugin {
                bind: self.bind,
                max_clients: self.max_clients,
            })

            .add(crate::server::ServerPlugin)
    }
}

M crates/unified/src/shared_plugins.rs => crates/unified/src/shared_plugins.rs +9 -46
@@ 1,16 1,12 @@
use crate::attachment::{Joint, JointOf, PartInShip, Peer, Ship, SnapOf, SnapOfJoint};
use crate::config::planet::{Planet, PlanetConfigCollection, PlanetSpring, PlanetSpringJoint};
use crate::config::recipe::RecipesConfig;
use crate::ecs::{CanCraft, CraftPartRequest, DragRequestEvent, Drill, Hi, Part, Particles, Player, PlayerStorage, SingleStorage, Temperature, ToggleDrillEvent};
use crate::ecs::{CraftPartRequest, DragRequestEvent, ToggleDrillEvent};
use crate::thrust::ThrustSolution;
use bevy::app::{App, PluginGroup, PluginGroupBuilder};
use bevy_common_assets::toml::TomlAssetPlugin;
use crate::prelude::*;
use bevy_replicon::prelude::{AppRuleExt, Channel, ClientMessageAppExt};
use crate::config::part::PartConfig;
use crate::config::planet::PlanetConfigCollection;
use crate::config::recipe::RecipesConfig;
use crate::config::world::GlobalWorldConfig;
use crate::ecs::thruster::{Thruster, ThrusterOfPart};
use crate::physics::register_physics_components_for_replication;
use crate::thrust::ThrustSolution;
use crate::world_config::world_config_plugin;

pub struct SharedPluginGroup;


@@ 18,13 14,8 @@ pub struct SharedPluginGroup;
impl PluginGroup for SharedPluginGroup {
    fn build(self) -> PluginGroupBuilder {
        PluginGroupBuilder::start::<Self>()
            .add_group(RepliconPlugins.build().set(RepliconSharedPlugin {
                auth_method: AuthMethod::None
            }))
            //.add(RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0))
            .add(physics_setup_plugin)
            .add(register_everything)
            .add(register_physics_components_for_replication)
            .add(world_config_plugin)

            /* Assets */


@@ 36,34 27,10 @@ impl PluginGroup for SharedPluginGroup {
}

pub fn register_everything(app: &mut App) {
    app.add_mapped_client_message::<ThrustSolution>(Channel::Ordered);
    app.add_mapped_client_message::<DragRequestEvent>(Channel::Ordered);
    app.add_mapped_client_message::<ToggleDrillEvent>(Channel::Ordered);
    app.add_mapped_client_message::<CraftPartRequest>(Channel::Ordered);
    app.add_mapped_server_message::<Hi>(Channel::Ordered);
    app.replicate::<Transform>();
    app.replicate::<GlobalTransform>();
    app.replicate::<Planet>();
    app.replicate::<Part>();
    app.replicate::<Player>();
    app.replicate::<Particles>();
    app.replicate::<ChildOf>();
    app.replicate::<Ship>();
    app.replicate::<PartInShip>();
    app.replicate::<Joint>();
    app.replicate::<Peer>();
    app.replicate::<JointOf>();
    app.replicate::<SnapOfJoint>();
    app.replicate::<SnapOf>();
    app.replicate::<PlayerStorage>();
    app.replicate::<Thruster>();
    app.replicate::<ThrusterOfPart>();
    app.replicate::<CanCraft>();
    app.replicate::<Temperature>();
    app.replicate::<Drill>();
    app.replicate::<SingleStorage>();
    app.replicate::<PlanetSpring>();
    app.replicate::<PlanetSpringJoint>();
    app.add_message::<DragRequestEvent>();
    app.add_message::<ToggleDrillEvent>();
    app.add_message::<CraftPartRequest>();
    app.add_message::<ThrustSolution>();
}

fn physics_setup_plugin(app: &mut App) {


@@ 71,11 38,7 @@ fn physics_setup_plugin(app: &mut App) {
    app.add_systems(Startup, setup_physics);
}

fn setup_physics(

) {

}
fn setup_physics() {}

/*
fn setup_physics(

M crates/unified/src/thrust.rs => crates/unified/src/thrust.rs +2 -6
@@ 1,15 1,11 @@
use std::collections::BTreeSet;
use bevy::ecs::entity::MapEntities;
use bevy::prelude::{Entity, Resource};
use serde::{Deserialize, Serialize};
use crate::prelude::{Component, Message};
use bevy::prelude::{Component, Entity, Message, Resource};

/// A thrust solution, found by the thrust solver on the client.
/// `thrusters_on` is the set of thrusters that should be on.
/// Any thrusters not in this set should be off.
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone, Resource, Component, MapEntities, Message)]
#[derive(Eq, PartialEq, Debug, Clone, Resource, Component, Message)]
pub struct ThrustSolution {
    #[entities]
    pub thrusters_on: BTreeSet<Entity>,
    pub converged: bool
}

M crates/unified/src/wasm_entrypoint.rs => crates/unified/src/wasm_entrypoint.rs +10 -14
@@ 1,32 1,29 @@
use crate::client_plugins::ClientPluginGroup;
use crate::server_plugins::ServerPluginGroup;
use crate::shared_plugins::SharedPluginGroup;
use aeronet_websocket::client::WebSocketClient;
use bevy::app::{App, Startup};
use url::Url;
use bevy::log::{Level, LogPlugin};
use bevy::app::App;
use bevy::log::LogPlugin;
use bevy::ui::UiPlugin;
use bevy::DefaultPlugins;
use wasm_bindgen::prelude::*;
use bevy::prelude::PluginGroup;


use tracing_web::{MakeWebConsoleWriter, performance_layer};
use tracing_subscriber::fmt::format::Pretty;
use tracing_web::MakeWebConsoleWriter;
use tracing_subscriber::prelude::*;
use tracing_subscriber::filter::LevelFilter;

#[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) // Only partially supported across browsers
        .without_time()   // std::time is not available in browsers, see note below
        .with_ansi(false)
        .without_time()
        .with_writer(MakeWebConsoleWriter::new())
        .with_filter(LevelFilter::INFO);

    tracing_subscriber::registry()
        .with(fmt_layer)
        .init(); // Install these as subscribers to tracing events
        .init();

    for instance in wgpu::Instance::enabled_backend_features().iter_names() {
        bevy::log::debug!(?instance, "available backend");


@@ 38,10 35,9 @@ pub fn play(server: &str) -> Result<(), JsValue> {
            .disable::<LogPlugin>()
            .disable::<UiPlugin>()
    );
    app.add_plugins(ClientPluginGroup {
        server: Some(server.to_string()),
    });
    app.add_plugins(SharedPluginGroup);
    app.add_plugins(ServerPluginGroup);
    app.add_plugins(ClientPluginGroup);

    app.run();