feat(server): stream chunks around a player entity via ECS

This commit is contained in:
Serkyo 2026-07-07 18:53:24 +02:00
parent 9b4cfccd43
commit 44511cde75
5 changed files with 669 additions and 39 deletions

500
Cargo.lock generated
View file

@ -25,7 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"getrandom", "getrandom 0.3.4",
"once_cell", "once_cell",
"version_check", "version_check",
"zerocopy", "zerocopy",
@ -109,11 +109,48 @@ dependencies = [
"raw-window-metal", "raw-window-metal",
] ]
[[package]]
name = "assert_type_match"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-executor"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "atomic-waker" name = "atomic-waker"
version = "1.1.2" version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
@ -121,6 +158,168 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bevy_ecs"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4654b9b3535fa7813d2878a50e1b5ad80a361dc1c913b96ded57667f65d70a01"
dependencies = [
"arrayvec",
"bevy_ecs_macros",
"bevy_platform",
"bevy_ptr",
"bevy_reflect",
"bevy_tasks",
"bevy_utils",
"bitflags 2.11.1",
"bumpalo",
"concurrent-queue",
"derive_more",
"fixedbitset",
"indexmap",
"log",
"nonmax",
"serde",
"slotmap",
"smallvec",
"thiserror 2.0.18",
"variadics_please",
]
[[package]]
name = "bevy_ecs_macro_logic"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee0b98a74141ce8dce4654c60020ebbaefab32646f42af6bbae55f282ae65ff7"
dependencies = [
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bevy_ecs_macros"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dc958a194d226d618404e0fea99a3c8b4146207a00a3ba07fa712bf71607240"
dependencies = [
"bevy_ecs_macro_logic",
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bevy_macro_utils"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "746a19912c6dc1bbe79188778573e8a253d5832c696b2fcb95578c17b29ff7ba"
dependencies = [
"proc-macro2",
"quote",
"syn",
"toml_edit",
]
[[package]]
name = "bevy_platform"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3120670bb2308980f723477c9d82476fcda31f08be9b256c3f0acdee82a65094"
dependencies = [
"critical-section",
"foldhash",
"futures-channel",
"futures-lite",
"hashbrown 0.16.1",
"js-sys",
"portable-atomic",
"portable-atomic-util",
"serde",
"spin",
"wasm-bindgen",
"wasm-bindgen-futures",
"windows-sys 0.61.2",
]
[[package]]
name = "bevy_ptr"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b511e89f7078fd21fdea77061a0ca3e737297c7011bb10c073e0aecb17c9fea6"
[[package]]
name = "bevy_reflect"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92abd59cbba1524c1847e9a50f74d94e6b7836c80a8edfa9c47e59f7fd81021d"
dependencies = [
"assert_type_match",
"bevy_platform",
"bevy_ptr",
"bevy_reflect_derive",
"bevy_utils",
"derive_more",
"disqualified",
"downcast-rs 2.0.2",
"erased-serde",
"foldhash",
"glam 0.32.1",
"indexmap",
"serde",
"smallvec",
"smol_str",
"thiserror 2.0.18",
"uuid",
"variadics_please",
"wgpu-types",
]
[[package]]
name = "bevy_reflect_derive"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fa4e6b97fd2d582dd5ed96762a70d04505a477c833cf4f69d016dcd03d2d04"
dependencies = [
"bevy_macro_utils",
"indexmap",
"proc-macro2",
"quote",
"syn",
"uuid",
]
[[package]]
name = "bevy_tasks"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa34e6b9b804851ec08a41c0346c859d82e2fa98c906d74b965ee61bbbb688e4"
dependencies = [
"async-executor",
"async-task",
"atomic-waker",
"bevy_platform",
"crossbeam-queue",
"derive_more",
"futures-lite",
"heapless",
"web-task",
]
[[package]]
name = "bevy_utils"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3aceea6c7ffdd568f866d5ca4dfe50c33440c54f7bc230c13a9ba7ab0c91aed1"
dependencies = [
"bevy_platform",
"disqualified",
"indexmap",
"thread_local",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -132,6 +331,9 @@ name = "bitflags"
version = "2.11.1" version = "2.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "block" name = "block"
@ -174,6 +376,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.11.1" version = "1.11.1"
@ -293,6 +501,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"portable-atomic",
]
[[package]]
name = "convert_case"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
] ]
[[package]] [[package]]
@ -335,6 +553,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.7" version = "0.8.7"
@ -354,6 +578,15 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "crossbeam-queue"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
dependencies = [
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.21" version = "0.8.21"
@ -366,12 +599,41 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
[[package]]
name = "derive_more"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
"unicode-xid",
]
[[package]] [[package]]
name = "dispatch" name = "dispatch"
version = "0.2.0" version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
[[package]]
name = "disqualified"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd"
[[package]] [[package]]
name = "dlib" name = "dlib"
version = "0.5.3" version = "0.5.3"
@ -387,6 +649,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "downcast-rs"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc"
[[package]] [[package]]
name = "dpi" name = "dpi"
version = "0.1.2" version = "0.1.2"
@ -405,6 +673,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "erased-serde"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec"
dependencies = [
"serde",
"serde_core",
"typeid",
]
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.14" version = "0.3.14"
@ -427,6 +706,18 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.5.0" version = "0.5.0"
@ -454,12 +745,40 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
]
[[package]] [[package]]
name = "futures-core" name = "futures-core"
version = "0.3.32" version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-io"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]] [[package]]
name = "futures-task" name = "futures-task"
version = "0.3.32" version = "0.3.32"
@ -496,10 +815,21 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi 5.3.0",
"wasip2", "wasip2",
] ]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
]
[[package]] [[package]]
name = "glam" name = "glam"
version = "0.27.0" version = "0.27.0"
@ -515,6 +845,15 @@ version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
[[package]]
name = "glam"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "gpu-allocator" name = "gpu-allocator"
version = "0.28.0" version = "0.28.0"
@ -531,12 +870,43 @@ dependencies = [
"windows", "windows",
] ]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
dependencies = [
"equivalent",
"serde",
"serde_core",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.17.0" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
[[package]]
name = "heapless"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4"
dependencies = [
"hash32",
"portable-atomic",
"stable_deref_trait",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.2" version = "0.5.2"
@ -550,7 +920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.17.0",
] ]
[[package]] [[package]]
@ -623,7 +993,7 @@ version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
dependencies = [ dependencies = [
"getrandom", "getrandom 0.3.4",
"libc", "libc",
] ]
@ -765,6 +1135,12 @@ dependencies = [
"rand_xorshift", "rand_xorshift",
] ]
[[package]]
name = "nonmax"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51"
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.50.3" version = "0.50.3"
@ -1072,6 +1448,12 @@ dependencies = [
"ttf-parser", "ttf-parser",
] ]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.3.2" version = "2.3.2"
@ -1130,6 +1512,21 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "portable-atomic"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
[[package]]
name = "portable-atomic-util"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
dependencies = [
"portable-atomic",
]
[[package]] [[package]]
name = "presser" name = "presser"
version = "0.3.1" version = "0.3.1"
@ -1178,6 +1575,12 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.6" version = "0.8.6"
@ -1415,6 +1818,7 @@ dependencies = [
name = "server" name = "server"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bevy_ecs",
"glam 0.27.0", "glam 0.27.0",
"rayon", "rayon",
"serde_json", "serde_json",
@ -1471,6 +1875,15 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "slotmap"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "smallvec" name = "smallvec"
version = "1.15.1" version = "1.15.1"
@ -1511,6 +1924,21 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "spin"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
dependencies = [
"portable-atomic",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]] [[package]]
name = "strict-num" name = "strict-num"
version = "0.1.1" version = "0.1.1"
@ -1699,6 +2127,12 @@ version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.24" version = "1.0.24"
@ -1711,12 +2145,41 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "uuid"
version = "1.23.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
dependencies = [
"getrandom 0.4.3",
"js-sys",
"serde_core",
"wasm-bindgen",
]
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "variadics_please"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -1804,7 +2267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
dependencies = [ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs 1.2.1",
"rustix 1.1.4", "rustix 1.1.4",
"scoped-tls", "scoped-tls",
"smallvec", "smallvec",
@ -1916,6 +2379,18 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "web-task"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cdc136a53ccd64a1211f107ccc34404769fbcc0f165f1afa065f5d88ab93538"
dependencies = [
"async-task",
"cfg-if",
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "web-time" name = "web-time"
version = "1.1.0" version = "1.1.0"
@ -1926,6 +2401,21 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "wgpu-types"
version = "29.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5"
dependencies = [
"bitflags 2.11.1",
"bytemuck",
"js-sys",
"log",
"raw-window-handle",
"serde",
"web-sys",
]
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.11" version = "0.1.11"

View file

@ -9,6 +9,7 @@ authors.workspace = true
workspace = true workspace = true
[dependencies] [dependencies]
bevy_ecs = "0.19"
glam = "0.27" glam = "0.27"
rayon = "1.12.0" rayon = "1.12.0"
serde_json = "1.0.149" serde_json = "1.0.149"

View file

@ -5,16 +5,43 @@
//! The server handles the authoritative game simulation, including world //! The server handles the authoritative game simulation, including world
//! management, physics, and combat. //! management, physics, and combat.
/// Entity components describing players and other world-streaming anchors.
pub mod player;
/// Authoritative chunk storage and generation logic for the server. /// Authoritative chunk storage and generation logic for the server.
pub mod world_server; pub mod world_server;
use std::collections::HashSet;
use std::fs; use std::fs;
use bevy_ecs::prelude::{Query, ResMut, Schedule, With, World};
use glam::Vec3; use glam::Vec3;
use shared::generator::{VoxelGenerator, WorldGenConfig}; use shared::generator::{VoxelGenerator, WorldGenConfig};
use shared::world::{ChunkPos, EntityPos}; use shared::world::{ChunkPos, EntityPos};
use tracing::{debug, info}; use tracing::{debug, info};
use player::{Player, Position, ViewDistance};
use world_server::{ServerWorld, cylinder_chunks};
/// Streaming system: loads and unloads chunks so that the resident set matches the union of the cylinders around every player anchor.
fn stream_chunks(
anchors: Query<(&Position, &ViewDistance), With<Player>>,
mut world: ResMut<ServerWorld>,
) {
// Desired set is the union of every anchor's cylinder; a chunk survives as long as it lies within any one player's view.
let mut desired = HashSet::new();
for (position, view) in &anchors {
cylinder_chunks(position.0.chunk, view.0, &mut desired);
}
let stats = world.reconcile(&desired);
info!(
loaded = stats.loaded,
unloaded = stats.unloaded,
resident = stats.resident,
"streaming reconcile"
);
}
fn main() { fn main() {
tracing_subscriber::fmt() tracing_subscriber::fmt()
.with_env_filter( .with_env_filter(
@ -44,10 +71,28 @@ fn main() {
let generator = VoxelGenerator::new(worldgen_config, seed); let generator = VoxelGenerator::new(worldgen_config, seed);
let mut world = world_server::ServerWorld::new(generator); let mut world = World::new();
world.insert_resource(ServerWorld::new(generator));
// Hardcoded spawn stand-in until a real player entity exists; the chunk anchor drives streaming. // Spawn a single dummy player anchor at the world origin.
let player = EntityPos::new(ChunkPos::new(0, 0, 0), Vec3::ZERO); world.spawn((
Player,
Position(EntityPos::new(ChunkPos::new(0, 0, 0), Vec3::ZERO)),
ViewDistance(4),
));
world.load_around(player, 4); // A schedule is one tick's worth of systems; running it advances the world.
let mut schedule = Schedule::default();
schedule.add_systems(stream_chunks);
// Drive several ticks, marching the dummy one chunk along +X between each. The manual movement stands in for network-driven player input and exists only to exercise load/unload as the anchor moves.
for step in 0..5 {
info!(step, "tick");
schedule.run(&mut world);
let mut movers = world.query_filtered::<&mut Position, With<Player>>();
for mut position in movers.iter_mut(&mut world) {
position.0.chunk.x += 1;
}
}
} }

View file

@ -0,0 +1,18 @@
// SPDX-License-Identifier: AGPL-3.0-only
//! Entity components describing players and other world-streaming anchors.
use bevy_ecs::prelude::Component;
use shared::world::EntityPos;
/// Marker component identifying an entity as a player.
#[derive(Component)]
pub struct Player;
/// Authoritative world-space position of an entity.
#[derive(Component)]
pub struct Position(pub EntityPos);
/// Chunk-streaming radius, in chunks, of the cylinder loaded around an entity.
#[derive(Component)]
pub struct ViewDistance(pub i32);

View file

@ -2,16 +2,31 @@
//! Authoritative chunk storage and generation logic for the server. //! Authoritative chunk storage and generation logic for the server.
use bevy_ecs::prelude::Resource;
use rayon::prelude::*; use rayon::prelude::*;
use shared::{ use shared::{
generator::VoxelGenerator, generator::VoxelGenerator,
world::{Chunk, ChunkPos, EntityPos}, world::{Chunk, ChunkPos},
}; };
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
/// Outcome of a single streaming reconcile pass, for logging and tests.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct StreamStats {
/// Number of chunks generated and inserted this pass.
pub loaded: usize,
/// Number of chunks evicted this pass.
pub unloaded: usize,
/// Number of chunks resident after the pass.
pub resident: usize,
}
/// The server's authoritative representation of the world. /// The server's authoritative representation of the world.
#[derive(Resource)]
pub struct ServerWorld { pub struct ServerWorld {
/// Deterministic voxel source used to generate missing chunks on demand.
generator: VoxelGenerator, generator: VoxelGenerator,
/// Currently resident chunks, keyed by chunk-space position.
chunks: HashMap<ChunkPos, Chunk>, chunks: HashMap<ChunkPos, Chunk>,
} }
@ -32,42 +47,103 @@ impl ServerWorld {
.or_insert_with(|| self.generator.generate_chunk(pos)) .or_insert_with(|| self.generator.generate_chunk(pos))
} }
/// Generates and caches all chunks in a cylindrical region around the given position. /// Number of chunks currently resident in memory.
pub fn load_around(&mut self, center: EntityPos, radius: i32) { #[must_use]
// The chunk anchor is the exact streaming key; no lossy world-to-chunk conversion is needed. pub fn loaded_count(&self) -> usize {
let center = center.chunk; self.chunks.len()
}
// Phase 1: collect the positions inside the cylinder that are not yet resident /// Reconciles resident chunks against a desired set: generates the chunks present in `desired` but not resident, and evicts resident chunks absent from `desired`.
let mut pending = Vec::new(); pub fn reconcile(&mut self, desired: &HashSet<ChunkPos>) -> StreamStats {
for x in center.x - radius..=center.x + radius { // Evict resident chunks that no anchor wants any more.
for z in center.z - radius..=center.z + radius { let stale: Vec<ChunkPos> = self
for y in center.y - radius / 2..=center.y + radius / 2 { .chunks
let dx = x - center.x; .keys()
let dz = z - center.z; .filter(|pos| !desired.contains(pos))
.copied()
if (dx * dx + dz * dz) <= radius * radius { .collect();
let pos = ChunkPos::new(x, y, z); for pos in &stale {
if !self.chunks.contains_key(&pos) { self.chunks.remove(pos);
pending.push(pos);
}
}
}
}
} }
// Phase 2: generate the missing chunks in parallel // Collect the desired positions that are not yet resident.
let generator = &self.generator; let pending: Vec<ChunkPos> = desired
let generated: Vec<(ChunkPos, Chunk)> = pending .iter()
.into_par_iter() .filter(|pos| !self.chunks.contains_key(pos))
.map(|pos| (pos, generator.generate_chunk(pos))) .copied()
.collect(); .collect();
// Phase 3: move the finished chunks into the map // Generate the missing chunks in parallel
let count = generated.len(); let generator = &self.generator;
let generated: Vec<(ChunkPos, Chunk)> = pending
.par_iter()
.map(|&pos| (pos, generator.generate_chunk(pos)))
.collect();
let loaded = generated.len();
for (pos, chunk) in generated { for (pos, chunk) in generated {
self.chunks.insert(pos, chunk); self.chunks.insert(pos, chunk);
} }
tracing::info!(chunks = count, center = ?center, radius, "generated chunk cylinder"); StreamStats {
loaded,
unloaded: stale.len(),
resident: self.chunks.len(),
}
}
}
/// Inserts every chunk position inside the streaming cylinder around `center` into `out`.
pub fn cylinder_chunks<S: std::hash::BuildHasher>(
center: ChunkPos,
radius: i32,
out: &mut HashSet<ChunkPos, S>,
) {
for x in center.x - radius..=center.x + radius {
for z in center.z - radius..=center.z + radius {
let dx = x - center.x;
let dz = z - center.z;
// Keep only the columns whose XZ distance falls within the disc.
if dx * dx + dz * dz <= radius * radius {
for y in center.y - radius / 2..=center.y + radius / 2 {
out.insert(ChunkPos::new(x, y, z));
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::{ChunkPos, cylinder_chunks};
use std::collections::HashSet;
#[test]
fn cylinder_is_symmetric_and_bounded() {
let mut set = HashSet::new();
cylinder_chunks(ChunkPos::new(0, 0, 0), 2, &mut set);
// Center column is always included.
assert!(set.contains(&ChunkPos::new(0, 0, 0)));
// A corner outside the disc (dx=2, dz=2 -> 8 > 4) is excluded.
assert!(!set.contains(&ChunkPos::new(2, 0, 2)));
// An axis cell at exactly the radius is included (dx=2, dz=0 -> 4 == 4).
assert!(set.contains(&ChunkPos::new(2, 0, 0)));
// Vertical extent is radius/2 = 1, so y=2 is out of range.
assert!(!set.contains(&ChunkPos::new(0, 2, 0)));
assert!(set.contains(&ChunkPos::new(0, 1, 0)));
}
#[test]
fn cylinder_translates_with_center() {
let mut origin = HashSet::new();
cylinder_chunks(ChunkPos::new(0, 0, 0), 3, &mut origin);
let mut shifted = HashSet::new();
cylinder_chunks(ChunkPos::new(10, 0, -5), 3, &mut shifted);
// Shape is translation-invariant: same count regardless of center.
assert_eq!(origin.len(), shifted.len());
} }
} }