mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
Bevy 0.10 (#79)
* replace 0.9.1 with 0.10.0 * start migrating to bevy .10 * well it compiles * doesn't immediately panic * remove unused imports * fmt * delete azalea-ecs * make RelativeEntityUpdate an EntityCommand * fix a doc test * explain what FixedUpdate does
This commit is contained in:
parent
bf4ff51789
commit
719379a8a7
51 changed files with 632 additions and 2261 deletions
215
Cargo.lock
generated
215
Cargo.lock
generated
|
@ -79,7 +79,7 @@ version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
|
checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"concurrent-queue 2.1.0",
|
"concurrent-queue",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
]
|
]
|
||||||
|
@ -105,7 +105,7 @@ checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock",
|
"async-lock",
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue 2.1.0",
|
"concurrent-queue",
|
||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"slab",
|
"slab",
|
||||||
|
@ -176,11 +176,12 @@ dependencies = [
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
"azalea-client",
|
"azalea-client",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-ecs",
|
|
||||||
"azalea-physics",
|
"azalea-physics",
|
||||||
"azalea-protocol",
|
"azalea-protocol",
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
"azalea-world",
|
"azalea-world",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
"bevy_tasks",
|
"bevy_tasks",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"futures",
|
"futures",
|
||||||
|
@ -284,11 +285,12 @@ dependencies = [
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-crypto",
|
"azalea-crypto",
|
||||||
"azalea-ecs",
|
|
||||||
"azalea-physics",
|
"azalea-physics",
|
||||||
"azalea-protocol",
|
"azalea-protocol",
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
"azalea-world",
|
"azalea-world",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
"bevy_log",
|
"bevy_log",
|
||||||
"bevy_tasks",
|
"bevy_tasks",
|
||||||
"bevy_time",
|
"bevy_time",
|
||||||
|
@ -331,26 +333,6 @@ dependencies = [
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "azalea-ecs"
|
|
||||||
version = "0.6.0"
|
|
||||||
dependencies = [
|
|
||||||
"azalea-ecs-macros",
|
|
||||||
"bevy_app",
|
|
||||||
"bevy_ecs",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "azalea-ecs-macros"
|
|
||||||
version = "0.6.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
"toml 0.7.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azalea-language"
|
name = "azalea-language"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -381,9 +363,11 @@ version = "0.6.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"azalea-block",
|
"azalea-block",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-ecs",
|
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
"azalea-world",
|
"azalea-world",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
|
"bevy_time",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -461,9 +445,10 @@ dependencies = [
|
||||||
"azalea-buf",
|
"azalea-buf",
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-ecs",
|
|
||||||
"azalea-nbt",
|
"azalea-nbt",
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
|
"bevy_app",
|
||||||
|
"bevy_ecs",
|
||||||
"derive_more",
|
"derive_more",
|
||||||
"enum-as-inner",
|
"enum-as-inner",
|
||||||
"log",
|
"log",
|
||||||
|
@ -497,9 +482,9 @@ checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_app"
|
name = "bevy_app"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "536e4d0018347478545ed8b6cb6e57b9279ee984868e81b7c0e78e0fb3222e42"
|
checksum = "960c6e444dc6a25dd51a2196f04872ae9e2e876802b66c391104849ec9225e38"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_derive",
|
"bevy_derive",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
|
@ -512,9 +497,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_derive"
|
name = "bevy_derive"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7baf73c58d41c353c6fd08e6764a2e7420c9f19e8227b391c50981db6d0282a6"
|
checksum = "cdf11701c01bf4dc7a3fac9f4547f3643d3db4cc1682af40c8c86e2f8734b617"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -523,9 +508,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ecs"
|
name = "bevy_ecs"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4c071d7c6bc9801253485e05d0c257284150de755391902746837ba21c0cf74"
|
checksum = "fdc5b19451128091e8507c9247888359ca0bfa895e7f6ca749ccc55c5463bef6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"bevy_ecs_macros",
|
"bevy_ecs_macros",
|
||||||
|
@ -536,16 +521,16 @@ dependencies = [
|
||||||
"downcast-rs",
|
"downcast-rs",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"fixedbitset",
|
"fixedbitset",
|
||||||
"fxhash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ecs_macros"
|
name = "bevy_ecs_macros"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c15bd45438eeb681ad74f2d205bb07a5699f98f9524462a30ec764afab2742ce"
|
checksum = "b1e79757319533bde006a4f30c268223ec6426371297182925932075ccfdae30"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -555,9 +540,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_log"
|
name = "bevy_log"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c480bac54cf4ae76edc3ae9ae3fa7c5e1b385e7f2111ef5ec3fd00cf3a7998b"
|
checksum = "47dcb09ec71145c80d88a84181cc1449d30f23c571bdd58c59c10eece82dfaa5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_log-sys",
|
"android_log-sys",
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
|
@ -571,20 +556,20 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_macro_utils"
|
name = "bevy_macro_utils"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "022bb69196deeea691b6997414af85bbd7f2b34a8914c4aa7a7ff4dfa44f7677"
|
checksum = "f24ca3363292f1435641fbafd5c24ce362137dd7d69bee56dcaaa2bc1d512ffe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"toml 0.5.11",
|
"toml_edit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_math"
|
name = "bevy_math"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d434c77ab766c806ed9062ef8a7285b3b02b47df51f188d4496199c3ac062eaf"
|
checksum = "5e45e46c2ac0a92db3ae622f2ed690928fe2612e7c9470a46d0ed4c2c77e2e95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -592,15 +577,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_ptr"
|
name = "bevy_ptr"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ec44f7655039546bc5d34d98de877083473f3e9b2b81d560c528d6d74d3eff4"
|
checksum = "a96c24da064370917b92c2a84527e6a73b620c50ac5ef8b1af8c04ccf5256a7c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_reflect"
|
name = "bevy_reflect"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6deae303a7f69dc243b2fa35b5e193cc920229f448942080c8eb2dbd9de6d37a"
|
checksum = "ab880e0eed9df5c99ce1a2f89edc11cdef1bc78413719b29e9ad7e3bc27f4c20"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_math",
|
"bevy_math",
|
||||||
"bevy_ptr",
|
"bevy_ptr",
|
||||||
|
@ -618,9 +603,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_reflect_derive"
|
name = "bevy_reflect_derive"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2bf4cb9cd5acb4193f890f36cb63679f1502e2de025e66a63b194b8b133d018"
|
checksum = "3b361b8671bdffe93978270dd770b03b48560c3127fdf9003f98111fb806bb11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_macro_utils",
|
"bevy_macro_utils",
|
||||||
"bit-set",
|
"bit-set",
|
||||||
|
@ -632,14 +617,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_tasks"
|
name = "bevy_tasks"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "680b16b53df9c9f24681dd95f4d772d83760bd19adf8bca00f358a3aad997853"
|
checksum = "3e368e4177fe70d695d5cb67fb7480fa262de79948d9b883a21788b9abf5a85a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel",
|
"async-channel",
|
||||||
"async-executor",
|
"async-executor",
|
||||||
"async-task",
|
"async-task",
|
||||||
"concurrent-queue 1.2.4",
|
"concurrent-queue",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
@ -647,31 +632,46 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_time"
|
name = "bevy_time"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a5c38a6d3ea929c7f81e6adf5a6c62cf7e8c40f5106c2174d6057e9d8ea624d"
|
checksum = "d2f2863cfc08fa38909e047a1bbc2dd71d0836057ed0840c69ace9dff3e0c298"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bevy_app",
|
"bevy_app",
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
"bevy_reflect",
|
"bevy_reflect",
|
||||||
"bevy_utils",
|
"bevy_utils",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bevy_utils"
|
name = "bevy_utils"
|
||||||
version = "0.9.1"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16750aae52cd35bd7b60eb61cee883420b250e11b4a290b8d44b2b2941795739"
|
checksum = "04d90ce493910ad9af3b4220ea6864c7d1472761086a98230ecac59c8d547e95"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.7.6",
|
"ahash 0.7.6",
|
||||||
|
"bevy_utils_proc_macros",
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"instant",
|
"instant",
|
||||||
|
"petgraph",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bevy_utils_proc_macros"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62a42e465c446800c57a5bf65b64f4fa1c1f3a74efc2a64a2a001e4a4f548a2e"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit-set"
|
name = "bit-set"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -738,12 +738,6 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cache-padded"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cast"
|
name = "cast"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -802,15 +796,6 @@ dependencies = [
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "concurrent-queue"
|
|
||||||
version = "1.2.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
|
|
||||||
dependencies = [
|
|
||||||
"cache-padded",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "concurrent-queue"
|
name = "concurrent-queue"
|
||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
|
@ -1199,15 +1184,6 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fxhash"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.6"
|
version = "0.14.6"
|
||||||
|
@ -1239,9 +1215,9 @@ checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "glam"
|
name = "glam"
|
||||||
version = "0.22.0"
|
version = "0.23.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774"
|
checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -1580,15 +1556,6 @@ 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 = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom8"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-ansi-term"
|
name = "nu-ansi-term"
|
||||||
version = "0.46.0"
|
version = "0.46.0"
|
||||||
|
@ -1829,9 +1796,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.50"
|
version = "1.0.51"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
|
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
@ -2003,6 +1970,12 @@ version = "0.1.21"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc-hash"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -2111,15 +2084,6 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_spanned"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2c68e921cef53841b8925c2abadd27c9b891d9613bdc43d6b823062866df38e8"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -2209,9 +2173,9 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.107"
|
version = "1.0.109"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2357,47 +2321,21 @@ dependencies = [
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.5.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f560bc7fb3eb31f5eee1340c68a2160cad39605b7b9c9ec32045ddbdee13b85"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_edit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "886f31a9b85b6182cabd4d8b07df3b451afcc216563748201490940d2a28ed36"
|
checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.19.0"
|
version = "0.19.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "233d8716cdc5d20ec88a18a839edaf545edc71efa4a5ff700ef4a102c26cd8fa"
|
checksum = "9a1eb0622d28f4b9c90adc4ea4b2b46b47663fde9ac5fafcb14a1369d5508825"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"nom8",
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2822,6 +2760,15 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee7b2c67f962bf5042bfd8b6a916178df33a26eec343ae064cb8e069f638fa6f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winreg"
|
name = "winreg"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
|
@ -15,7 +15,6 @@ members = [
|
||||||
"azalea-buf",
|
"azalea-buf",
|
||||||
"azalea-physics",
|
"azalea-physics",
|
||||||
"azalea-registry",
|
"azalea-registry",
|
||||||
"azalea-ecs",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
|
@ -16,14 +16,15 @@ azalea-block = { path = "../azalea-block", version = "0.6.0" }
|
||||||
azalea-chat = { path = "../azalea-chat", version = "0.6.0" }
|
azalea-chat = { path = "../azalea-chat", version = "0.6.0" }
|
||||||
azalea-core = { path = "../azalea-core", version = "0.6.0" }
|
azalea-core = { path = "../azalea-core", version = "0.6.0" }
|
||||||
azalea-crypto = { path = "../azalea-crypto", version = "0.6.0" }
|
azalea-crypto = { path = "../azalea-crypto", version = "0.6.0" }
|
||||||
azalea-ecs = { path = "../azalea-ecs", version = "0.6.0" }
|
|
||||||
azalea-physics = { path = "../azalea-physics", version = "0.6.0" }
|
azalea-physics = { path = "../azalea-physics", version = "0.6.0" }
|
||||||
azalea-protocol = { path = "../azalea-protocol", version = "0.6.0" }
|
azalea-protocol = { path = "../azalea-protocol", version = "0.6.0" }
|
||||||
azalea-registry = { path = "../azalea-registry", version = "0.6.0" }
|
azalea-registry = { path = "../azalea-registry", version = "0.6.0" }
|
||||||
azalea-world = { path = "../azalea-world", version = "0.6.0" }
|
azalea-world = { path = "../azalea-world", version = "0.6.0" }
|
||||||
bevy_log = "0.9.1"
|
bevy_app = "0.10.0"
|
||||||
bevy_tasks = "0.9.1"
|
bevy_ecs = "0.10.0"
|
||||||
bevy_time = "0.9.1"
|
bevy_log = "0.10.0"
|
||||||
|
bevy_tasks = "0.10.0"
|
||||||
|
bevy_time = "0.10.0"
|
||||||
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
|
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
|
||||||
futures = "0.3.25"
|
futures = "0.3.25"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
//! Implementations of chat-related features.
|
//! Implementations of chat-related features.
|
||||||
|
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
entity::Entity,
|
|
||||||
event::{EventReader, EventWriter},
|
|
||||||
schedule::IntoSystemDescriptor,
|
|
||||||
};
|
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
clientbound_player_chat_packet::ClientboundPlayerChatPacket,
|
clientbound_player_chat_packet::ClientboundPlayerChatPacket,
|
||||||
clientbound_system_chat_packet::ClientboundSystemChatPacket,
|
clientbound_system_chat_packet::ClientboundSystemChatPacket,
|
||||||
serverbound_chat_command_packet::ServerboundChatCommandPacket,
|
serverbound_chat_command_packet::ServerboundChatCommandPacket,
|
||||||
serverbound_chat_packet::{LastSeenMessagesUpdate, ServerboundChatPacket},
|
serverbound_chat_packet::{LastSeenMessagesUpdate, ServerboundChatPacket},
|
||||||
};
|
};
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
entity::Entity,
|
||||||
|
event::{EventReader, EventWriter},
|
||||||
|
schedule::{IntoSystemConfig, IntoSystemConfigs},
|
||||||
|
};
|
||||||
use std::{
|
use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
|
@ -159,12 +159,12 @@ impl Plugin for ChatPlugin {
|
||||||
app.add_event::<SendChatEvent>()
|
app.add_event::<SendChatEvent>()
|
||||||
.add_event::<SendChatKindEvent>()
|
.add_event::<SendChatKindEvent>()
|
||||||
.add_event::<ChatReceivedEvent>()
|
.add_event::<ChatReceivedEvent>()
|
||||||
.add_system(handle_send_chat_event.label("handle_send_chat_event"))
|
.add_systems(
|
||||||
.add_system(
|
(
|
||||||
handle_send_chat_kind_event
|
handle_send_chat_event,
|
||||||
.label("handle_send_chat_kind_event")
|
handle_send_chat_kind_event.after(handle_send_packet_event),
|
||||||
.after(handle_send_chat_event)
|
)
|
||||||
.after(handle_send_packet_event),
|
.chain(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,25 +6,16 @@ use crate::{
|
||||||
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
|
death_event, handle_send_packet_event, update_in_loaded_chunk, GameProfileComponent,
|
||||||
LocalPlayer, PhysicsState, SendPacketEvent,
|
LocalPlayer, PhysicsState, SendPacketEvent,
|
||||||
},
|
},
|
||||||
movement::{local_player_ai_step, send_position, sprint_listener, walk_listener},
|
movement::PlayerMovePlugin,
|
||||||
packet_handling::{self, PacketHandlerPlugin, PacketReceiver},
|
packet_handling::{self, PacketHandlerPlugin, PacketReceiver},
|
||||||
player::retroactively_add_game_profile_component,
|
player::retroactively_add_game_profile_component,
|
||||||
task_pool::TaskPoolPlugin,
|
task_pool::TaskPoolPlugin,
|
||||||
Account, PlayerInfo, StartSprintEvent, StartWalkEvent,
|
Account, PlayerInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError};
|
use azalea_auth::{game_profile::GameProfile, sessionserver::ClientSessionServerError};
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_ecs::{
|
use azalea_physics::{PhysicsPlugin, PhysicsSet};
|
||||||
app::{App, Plugin, PluginGroup, PluginGroupBuilder},
|
|
||||||
bundle::Bundle,
|
|
||||||
component::Component,
|
|
||||||
entity::Entity,
|
|
||||||
schedule::{IntoSystemDescriptor, ReportExecutionOrderAmbiguities, Schedule, Stage, SystemSet},
|
|
||||||
AppTickExt,
|
|
||||||
};
|
|
||||||
use azalea_ecs::{ecs::Ecs, TickPlugin};
|
|
||||||
use azalea_physics::PhysicsPlugin;
|
|
||||||
use azalea_protocol::{
|
use azalea_protocol::{
|
||||||
connect::{Connection, ConnectionError},
|
connect::{Connection, ConnectionError},
|
||||||
packets::{
|
packets::{
|
||||||
|
@ -46,13 +37,23 @@ use azalea_protocol::{
|
||||||
resolver, ServerAddress,
|
resolver, ServerAddress,
|
||||||
};
|
};
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{EntityPlugin, Local, WorldName},
|
entity::{EntityPlugin, EntityUpdateSet, Local, WorldName},
|
||||||
PartialWorld, World, WorldContainer,
|
Instance, PartialWorld, WorldContainer,
|
||||||
|
};
|
||||||
|
use bevy_app::{App, CoreSchedule, Plugin, PluginGroup, PluginGroupBuilder};
|
||||||
|
use bevy_ecs::{
|
||||||
|
bundle::Bundle,
|
||||||
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
|
schedule::IntoSystemConfig,
|
||||||
|
schedule::{LogLevel, ScheduleBuildSettings, ScheduleLabel},
|
||||||
|
world::World,
|
||||||
};
|
};
|
||||||
use bevy_log::LogPlugin;
|
use bevy_log::LogPlugin;
|
||||||
|
use bevy_time::{prelude::FixedTime, TimePlugin};
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc};
|
use std::{collections::HashMap, fmt::Debug, io, net::SocketAddr, sync::Arc, time::Duration};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::{sync::mpsc, time};
|
use tokio::{sync::mpsc, time};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -86,7 +87,7 @@ pub struct Client {
|
||||||
/// The entity component system. You probably don't need to access this
|
/// The entity component system. You probably don't need to access this
|
||||||
/// directly. Note that if you're using a shared world (i.e. a swarm), this
|
/// directly. Note that if you're using a shared world (i.e. a swarm), this
|
||||||
/// will contain all entities in all worlds.
|
/// will contain all entities in all worlds.
|
||||||
pub ecs: Arc<Mutex<Ecs>>,
|
pub ecs: Arc<Mutex<World>>,
|
||||||
|
|
||||||
/// Use this to force the client to run the schedule outside of a tick.
|
/// Use this to force the client to run the schedule outside of a tick.
|
||||||
pub run_schedule_sender: mpsc::UnboundedSender<()>,
|
pub run_schedule_sender: mpsc::UnboundedSender<()>,
|
||||||
|
@ -120,7 +121,7 @@ impl Client {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
profile: GameProfile,
|
profile: GameProfile,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
ecs: Arc<Mutex<Ecs>>,
|
ecs: Arc<Mutex<World>>,
|
||||||
run_schedule_sender: mpsc::UnboundedSender<()>,
|
run_schedule_sender: mpsc::UnboundedSender<()>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -180,7 +181,7 @@ impl Client {
|
||||||
/// Create a [`Client`] when you already have the ECS made with
|
/// Create a [`Client`] when you already have the ECS made with
|
||||||
/// [`start_ecs`]. You'd usually want to use [`Self::join`] instead.
|
/// [`start_ecs`]. You'd usually want to use [`Self::join`] instead.
|
||||||
pub async fn start_client(
|
pub async fn start_client(
|
||||||
ecs_lock: Arc<Mutex<Ecs>>,
|
ecs_lock: Arc<Mutex<World>>,
|
||||||
account: &Account,
|
account: &Account,
|
||||||
address: &ServerAddress,
|
address: &ServerAddress,
|
||||||
resolved_address: &SocketAddr,
|
resolved_address: &SocketAddr,
|
||||||
|
@ -226,7 +227,7 @@ impl Client {
|
||||||
packet_writer_sender,
|
packet_writer_sender,
|
||||||
// default to an empty world, it'll be set correctly later when we
|
// default to an empty world, it'll be set correctly later when we
|
||||||
// get the login packet
|
// get the login packet
|
||||||
Arc::new(RwLock::new(World::default())),
|
Arc::new(RwLock::new(Instance::default())),
|
||||||
read_packets_task,
|
read_packets_task,
|
||||||
write_packets_task,
|
write_packets_task,
|
||||||
);
|
);
|
||||||
|
@ -382,13 +383,13 @@ impl Client {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_player<'a>(&'a self, ecs: &'a mut Ecs) -> &'a LocalPlayer {
|
pub fn local_player<'a>(&'a self, ecs: &'a mut World) -> &'a LocalPlayer {
|
||||||
self.query::<&LocalPlayer>(ecs)
|
self.query::<&LocalPlayer>(ecs)
|
||||||
}
|
}
|
||||||
pub fn local_player_mut<'a>(
|
pub fn local_player_mut<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
ecs: &'a mut Ecs,
|
ecs: &'a mut World,
|
||||||
) -> azalea_ecs::ecs::Mut<'a, LocalPlayer> {
|
) -> bevy_ecs::world::Mut<'a, LocalPlayer> {
|
||||||
self.query::<&mut LocalPlayer>(ecs)
|
self.query::<&mut LocalPlayer>(ecs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +417,7 @@ impl Client {
|
||||||
/// client, then it'll be the same as the world the client has loaded.
|
/// client, then it'll be the same as the world the client has loaded.
|
||||||
/// If the client using a shared world, then the shared world will be a
|
/// If the client using a shared world, then the shared world will be a
|
||||||
/// superset of the client's world.
|
/// superset of the client's world.
|
||||||
pub fn world(&self) -> Arc<RwLock<World>> {
|
pub fn world(&self) -> Arc<RwLock<Instance>> {
|
||||||
let world_name = self.component::<WorldName>();
|
let world_name = self.component::<WorldName>();
|
||||||
let ecs = self.ecs.lock();
|
let ecs = self.ecs.lock();
|
||||||
let world_container = ecs.resource::<WorldContainer>();
|
let world_container = ecs.resource::<WorldContainer>();
|
||||||
|
@ -493,33 +494,20 @@ pub struct JoinedClientBundle {
|
||||||
pub struct AzaleaPlugin;
|
pub struct AzaleaPlugin;
|
||||||
impl Plugin for AzaleaPlugin {
|
impl Plugin for AzaleaPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<StartWalkEvent>()
|
// Minecraft ticks happen every 50ms
|
||||||
.add_event::<StartSprintEvent>();
|
app.insert_resource(FixedTime::new(Duration::from_millis(50)));
|
||||||
|
|
||||||
app.add_tick_system_set(
|
app.add_system(
|
||||||
SystemSet::new()
|
update_in_loaded_chunk
|
||||||
.with_system(send_position.after("ai_step"))
|
.after(PhysicsSet)
|
||||||
.with_system(update_in_loaded_chunk.before(send_position).after("travel"))
|
.after(handle_send_packet_event),
|
||||||
.with_system(
|
|
||||||
local_player_ai_step
|
|
||||||
.before(azalea_physics::ai_step)
|
|
||||||
.label("ai_step"),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// fire the Death event when the player dies.
|
// fire the Death event when the player dies.
|
||||||
app.add_system(death_event);
|
app.add_system(death_event);
|
||||||
|
|
||||||
// walk and sprint event listeners
|
|
||||||
app.add_system(walk_listener.label("walk_listener"))
|
|
||||||
.add_system(
|
|
||||||
sprint_listener
|
|
||||||
.label("sprint_listener")
|
|
||||||
.before("walk_listener"),
|
|
||||||
);
|
|
||||||
|
|
||||||
// add GameProfileComponent when we get an AddPlayerEvent
|
// add GameProfileComponent when we get an AddPlayerEvent
|
||||||
app.add_system(retroactively_add_game_profile_component.after("update_indexes"));
|
app.add_system(retroactively_add_game_profile_component.after(EntityUpdateSet::Index));
|
||||||
|
|
||||||
app.add_event::<SendPacketEvent>()
|
app.add_event::<SendPacketEvent>()
|
||||||
.add_system(handle_send_packet_event);
|
.add_system(handle_send_packet_event);
|
||||||
|
@ -544,7 +532,12 @@ pub fn init_ecs_app() -> App {
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
|
|
||||||
app.insert_resource(ReportExecutionOrderAmbiguities);
|
app.edit_schedule(CoreSchedule::Main, |schedule| {
|
||||||
|
schedule.set_build_settings(ScheduleBuildSettings {
|
||||||
|
ambiguity_detection: LogLevel::Warn,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
app.add_plugins(DefaultPlugins);
|
app.add_plugins(DefaultPlugins);
|
||||||
app
|
app
|
||||||
|
@ -554,17 +547,19 @@ pub fn init_ecs_app() -> App {
|
||||||
/// first.
|
/// first.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn start_ecs(
|
pub fn start_ecs(
|
||||||
app: App,
|
mut app: App,
|
||||||
run_schedule_receiver: mpsc::UnboundedReceiver<()>,
|
run_schedule_receiver: mpsc::UnboundedReceiver<()>,
|
||||||
run_schedule_sender: mpsc::UnboundedSender<()>,
|
run_schedule_sender: mpsc::UnboundedSender<()>,
|
||||||
) -> Arc<Mutex<Ecs>> {
|
) -> Arc<Mutex<World>> {
|
||||||
|
app.setup();
|
||||||
|
|
||||||
// all resources should have been added by now so we can take the ecs from the
|
// all resources should have been added by now so we can take the ecs from the
|
||||||
// app
|
// app
|
||||||
let ecs = Arc::new(Mutex::new(app.world));
|
let ecs = Arc::new(Mutex::new(app.world));
|
||||||
|
|
||||||
tokio::spawn(run_schedule_loop(
|
tokio::spawn(run_schedule_loop(
|
||||||
ecs.clone(),
|
ecs.clone(),
|
||||||
app.schedule,
|
app.outer_schedule_label,
|
||||||
run_schedule_receiver,
|
run_schedule_receiver,
|
||||||
));
|
));
|
||||||
tokio::spawn(tick_run_schedule_loop(run_schedule_sender));
|
tokio::spawn(tick_run_schedule_loop(run_schedule_sender));
|
||||||
|
@ -573,14 +568,16 @@ pub fn start_ecs(
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_schedule_loop(
|
async fn run_schedule_loop(
|
||||||
ecs: Arc<Mutex<Ecs>>,
|
ecs: Arc<Mutex<World>>,
|
||||||
mut schedule: Schedule,
|
outer_schedule_label: Box<dyn ScheduleLabel>,
|
||||||
mut run_schedule_receiver: mpsc::UnboundedReceiver<()>,
|
mut run_schedule_receiver: mpsc::UnboundedReceiver<()>,
|
||||||
) {
|
) {
|
||||||
loop {
|
loop {
|
||||||
// whenever we get an event from run_schedule_receiver, run the schedule
|
// whenever we get an event from run_schedule_receiver, run the schedule
|
||||||
run_schedule_receiver.recv().await;
|
run_schedule_receiver.recv().await;
|
||||||
schedule.run(&mut ecs.lock());
|
let mut ecs = ecs.lock();
|
||||||
|
ecs.run_schedule_ref(&*outer_schedule_label);
|
||||||
|
ecs.clear_trackers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -609,7 +606,7 @@ impl PluginGroup for DefaultPlugins {
|
||||||
fn build(self) -> PluginGroupBuilder {
|
fn build(self) -> PluginGroupBuilder {
|
||||||
PluginGroupBuilder::start::<Self>()
|
PluginGroupBuilder::start::<Self>()
|
||||||
.add(LogPlugin::default())
|
.add(LogPlugin::default())
|
||||||
.add(TickPlugin::default())
|
.add(TimePlugin::default())
|
||||||
.add(PacketHandlerPlugin)
|
.add(PacketHandlerPlugin)
|
||||||
.add(AzaleaPlugin)
|
.add(AzaleaPlugin)
|
||||||
.add(EntityPlugin)
|
.add(EntityPlugin)
|
||||||
|
@ -618,5 +615,6 @@ impl PluginGroup for DefaultPlugins {
|
||||||
.add(TaskPoolPlugin::default())
|
.add(TaskPoolPlugin::default())
|
||||||
.add(ChatPlugin)
|
.add(ChatPlugin)
|
||||||
.add(DisconnectPlugin)
|
.add(DisconnectPlugin)
|
||||||
|
.add(PlayerMovePlugin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
//! Disconnect a client from the server.
|
//! Disconnect a client from the server.
|
||||||
|
|
||||||
use azalea_ecs::{
|
use bevy_app::{App, CoreSet, Plugin};
|
||||||
app::{App, CoreStage, Plugin},
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::{EventReader, EventWriter},
|
event::{EventReader, EventWriter},
|
||||||
query::Changed,
|
query::Changed,
|
||||||
schedule::IntoSystemDescriptor,
|
schedule::IntoSystemConfigs,
|
||||||
system::{Commands, Query},
|
system::{Commands, Query},
|
||||||
AppTickExt,
|
|
||||||
};
|
};
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
|
|
||||||
use crate::{client::JoinedClientBundle, movement::send_position, LocalPlayer};
|
use crate::{client::JoinedClientBundle, LocalPlayer};
|
||||||
|
|
||||||
pub struct DisconnectPlugin;
|
pub struct DisconnectPlugin;
|
||||||
impl Plugin for DisconnectPlugin {
|
impl Plugin for DisconnectPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<DisconnectEvent>()
|
app.add_event::<DisconnectEvent>().add_systems(
|
||||||
.add_system_to_stage(CoreStage::PostUpdate, handle_disconnect)
|
(
|
||||||
.add_tick_system(
|
update_read_packets_task_running_component,
|
||||||
update_read_packets_task_running_component
|
disconnect_on_read_packets_ended,
|
||||||
.before(disconnect_on_read_packets_ended)
|
remove_components_from_disconnected_players,
|
||||||
.before(send_position),
|
|
||||||
)
|
)
|
||||||
.add_tick_system(disconnect_on_read_packets_ended);
|
.in_base_set(CoreSet::PostUpdate)
|
||||||
|
.chain(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,10 @@ pub struct DisconnectEvent {
|
||||||
|
|
||||||
/// System that removes the [`JoinedClientBundle`] from the entity when it
|
/// System that removes the [`JoinedClientBundle`] from the entity when it
|
||||||
/// receives a [`DisconnectEvent`].
|
/// receives a [`DisconnectEvent`].
|
||||||
pub fn handle_disconnect(mut commands: Commands, mut events: EventReader<DisconnectEvent>) {
|
pub fn remove_components_from_disconnected_players(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut events: EventReader<DisconnectEvent>,
|
||||||
|
) {
|
||||||
for DisconnectEvent { entity } in events.iter() {
|
for DisconnectEvent { entity } in events.iter() {
|
||||||
commands.entity(*entity).remove::<JoinedClientBundle>();
|
commands.entity(*entity).remove::<JoinedClientBundle>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use azalea_ecs::{
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
ecs::Ecs,
|
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{ROQueryItem, ReadOnlyWorldQuery, WorldQuery},
|
query::{ROQueryItem, ReadOnlyWorldQuery, WorldQuery},
|
||||||
|
world::World,
|
||||||
};
|
};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ impl Client {
|
||||||
/// .is_some();
|
/// .is_some();
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn query<'w, Q: WorldQuery>(&self, ecs: &'w mut Ecs) -> <Q as WorldQuery>::Item<'w> {
|
pub fn query<'w, Q: WorldQuery>(&self, ecs: &'w mut World) -> <Q as WorldQuery>::Item<'w> {
|
||||||
ecs.query::<Q>()
|
ecs.query::<Q>()
|
||||||
.get_mut(ecs, self.entity)
|
.get_mut(ecs, self.entity)
|
||||||
.expect("Our client is missing a required component.")
|
.expect("Our client is missing a required component.")
|
||||||
|
@ -38,7 +38,7 @@ impl Client {
|
||||||
/// Note that this will very likely change in the future.
|
/// Note that this will very likely change in the future.
|
||||||
/// ```
|
/// ```
|
||||||
/// use azalea_client::{Client, GameProfileComponent};
|
/// use azalea_client::{Client, GameProfileComponent};
|
||||||
/// use azalea_ecs::query::With;
|
/// use bevy_ecs::query::With;
|
||||||
/// use azalea_world::entity::{Position, metadata::Player};
|
/// use azalea_world::entity::{Position, metadata::Player};
|
||||||
///
|
///
|
||||||
/// # fn example(mut bot: Client, sender_name: String) {
|
/// # fn example(mut bot: Client, sender_name: String) {
|
||||||
|
@ -74,7 +74,7 @@ impl Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EntityPredicate<Q: ReadOnlyWorldQuery, Filter: ReadOnlyWorldQuery> {
|
pub trait EntityPredicate<Q: ReadOnlyWorldQuery, Filter: ReadOnlyWorldQuery> {
|
||||||
fn find(&self, ecs_lock: Arc<Mutex<Ecs>>) -> Option<Entity>;
|
fn find(&self, ecs_lock: Arc<Mutex<World>>) -> Option<Entity>;
|
||||||
}
|
}
|
||||||
impl<F, Q, Filter> EntityPredicate<(Q,), Filter> for F
|
impl<F, Q, Filter> EntityPredicate<(Q,), Filter> for F
|
||||||
where
|
where
|
||||||
|
@ -82,7 +82,7 @@ where
|
||||||
Q: ReadOnlyWorldQuery,
|
Q: ReadOnlyWorldQuery,
|
||||||
Filter: ReadOnlyWorldQuery,
|
Filter: ReadOnlyWorldQuery,
|
||||||
{
|
{
|
||||||
fn find(&self, ecs_lock: Arc<Mutex<Ecs>>) -> Option<Entity> {
|
fn find(&self, ecs_lock: Arc<Mutex<World>>) -> Option<Entity> {
|
||||||
let mut ecs = ecs_lock.lock();
|
let mut ecs = ecs_lock.lock();
|
||||||
let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
|
let mut query = ecs.query_filtered::<(Entity, Q), Filter>();
|
||||||
let entity = query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e);
|
let entity = query.iter(&ecs).find(|(_, q)| (self)(q)).map(|(e, _)| e);
|
||||||
|
|
|
@ -3,18 +3,12 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
component::Component,
|
|
||||||
event::EventReader,
|
|
||||||
query::Added,
|
|
||||||
system::Query,
|
|
||||||
AppTickExt,
|
|
||||||
};
|
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, ClientboundGamePacket,
|
clientbound_player_combat_kill_packet::ClientboundPlayerCombatKillPacket, ClientboundGamePacket,
|
||||||
};
|
};
|
||||||
use azalea_world::entity::MinecraftEntityId;
|
use azalea_world::entity::MinecraftEntityId;
|
||||||
|
use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, Plugin};
|
||||||
|
use bevy_ecs::{component::Component, event::EventReader, query::Added, system::Query};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
@ -115,7 +109,7 @@ impl Plugin for EventPlugin {
|
||||||
.add_system(remove_player_listener)
|
.add_system(remove_player_listener)
|
||||||
.add_system(death_listener)
|
.add_system(death_listener)
|
||||||
.add_system(keepalive_listener)
|
.add_system(keepalive_listener)
|
||||||
.add_tick_system(tick_listener);
|
.add_system(tick_listener.in_schedule(CoreSchedule::FixedUpdate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ mod player;
|
||||||
pub mod task_pool;
|
pub mod task_pool;
|
||||||
|
|
||||||
pub use account::Account;
|
pub use account::Account;
|
||||||
pub use azalea_ecs as ecs;
|
|
||||||
pub use client::{init_ecs_app, start_ecs, Client, ClientInformation, JoinError};
|
pub use client::{init_ecs_app, start_ecs, Client, ClientInformation, JoinError};
|
||||||
pub use events::Event;
|
pub use events::Event;
|
||||||
pub use local_player::{GameProfileComponent, LocalPlayer};
|
pub use local_player::{GameProfileComponent, LocalPlayer};
|
||||||
|
|
|
@ -2,14 +2,13 @@ use std::{collections::HashMap, io, sync::Arc};
|
||||||
|
|
||||||
use azalea_auth::game_profile::GameProfile;
|
use azalea_auth::game_profile::GameProfile;
|
||||||
use azalea_core::ChunkPos;
|
use azalea_core::ChunkPos;
|
||||||
use azalea_ecs::component::Component;
|
|
||||||
use azalea_ecs::entity::Entity;
|
|
||||||
use azalea_ecs::event::EventReader;
|
|
||||||
use azalea_ecs::{query::Added, system::Query};
|
|
||||||
use azalea_protocol::packets::game::ServerboundGamePacket;
|
use azalea_protocol::packets::game::ServerboundGamePacket;
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{self, Dead},
|
entity::{self, Dead},
|
||||||
PartialWorld, World,
|
Instance, PartialWorld,
|
||||||
|
};
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component, entity::Entity, event::EventReader, query::Added, system::Query,
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -44,7 +43,7 @@ pub struct LocalPlayer {
|
||||||
pub partial_world: Arc<RwLock<PartialWorld>>,
|
pub partial_world: Arc<RwLock<PartialWorld>>,
|
||||||
/// The world is the combined [`PartialWorld`]s of all clients in the same
|
/// The world is the combined [`PartialWorld`]s of all clients in the same
|
||||||
/// world. (Only relevant if you're using a shared world, i.e. a swarm)
|
/// world. (Only relevant if you're using a shared world, i.e. a swarm)
|
||||||
pub world: Arc<RwLock<World>>,
|
pub world: Arc<RwLock<Instance>>,
|
||||||
|
|
||||||
/// A task that reads packets from the server. The client is disconnected
|
/// A task that reads packets from the server. The client is disconnected
|
||||||
/// when this task ends.
|
/// when this task ends.
|
||||||
|
@ -88,7 +87,7 @@ impl LocalPlayer {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,
|
packet_writer: mpsc::UnboundedSender<ServerboundGamePacket>,
|
||||||
world: Arc<RwLock<World>>,
|
world: Arc<RwLock<Instance>>,
|
||||||
read_packets_task: JoinHandle<()>,
|
read_packets_task: JoinHandle<()>,
|
||||||
write_packets_task: JoinHandle<()>,
|
write_packets_task: JoinHandle<()>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -129,7 +128,7 @@ impl Drop for LocalPlayer {
|
||||||
|
|
||||||
/// Update the [`LocalPlayerInLoadedChunk`] component for all [`LocalPlayer`]s.
|
/// Update the [`LocalPlayerInLoadedChunk`] component for all [`LocalPlayer`]s.
|
||||||
pub fn update_in_loaded_chunk(
|
pub fn update_in_loaded_chunk(
|
||||||
mut commands: azalea_ecs::system::Commands,
|
mut commands: bevy_ecs::system::Commands,
|
||||||
query: Query<(Entity, &LocalPlayer, &entity::Position)>,
|
query: Query<(Entity, &LocalPlayer, &entity::Position)>,
|
||||||
) {
|
) {
|
||||||
for (entity, local_player, position) in &query {
|
for (entity, local_player, position) in &query {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use crate::client::Client;
|
use crate::client::Client;
|
||||||
use crate::local_player::{LocalPlayer, LocalPlayerInLoadedChunk, PhysicsState};
|
use crate::local_player::{
|
||||||
use azalea_ecs::entity::Entity;
|
update_in_loaded_chunk, LocalPlayer, LocalPlayerInLoadedChunk, PhysicsState,
|
||||||
use azalea_ecs::{event::EventReader, query::With, system::Query};
|
};
|
||||||
|
use azalea_physics::{force_jump_listener, PhysicsSet};
|
||||||
use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket;
|
use azalea_protocol::packets::game::serverbound_player_command_packet::ServerboundPlayerCommandPacket;
|
||||||
use azalea_protocol::packets::game::{
|
use azalea_protocol::packets::game::{
|
||||||
serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket,
|
serverbound_move_player_pos_packet::ServerboundMovePlayerPosPacket,
|
||||||
|
@ -13,6 +14,14 @@ use azalea_world::{
|
||||||
entity::{self, metadata::Sprinting, Attributes, Jumping, MinecraftEntityId},
|
entity::{self, metadata::Sprinting, Attributes, Jumping, MinecraftEntityId},
|
||||||
MoveEntityError,
|
MoveEntityError,
|
||||||
};
|
};
|
||||||
|
use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
entity::Entity,
|
||||||
|
event::EventReader,
|
||||||
|
query::With,
|
||||||
|
schedule::{IntoSystemConfig, IntoSystemConfigs},
|
||||||
|
system::Query,
|
||||||
|
};
|
||||||
use std::backtrace::Backtrace;
|
use std::backtrace::Backtrace;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -34,6 +43,28 @@ impl From<MoveEntityError> for MovePlayerError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct PlayerMovePlugin;
|
||||||
|
|
||||||
|
impl Plugin for PlayerMovePlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
app.add_event::<StartWalkEvent>()
|
||||||
|
.add_event::<StartSprintEvent>()
|
||||||
|
.add_systems(
|
||||||
|
(sprint_listener, walk_listener)
|
||||||
|
.chain()
|
||||||
|
.before(force_jump_listener),
|
||||||
|
)
|
||||||
|
.add_systems(
|
||||||
|
(
|
||||||
|
local_player_ai_step.in_set(PhysicsSet),
|
||||||
|
send_position.after(update_in_loaded_chunk),
|
||||||
|
)
|
||||||
|
.chain()
|
||||||
|
.in_schedule(CoreSchedule::FixedUpdate),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
/// Set whether we're jumping. This acts as if you held space in
|
/// Set whether we're jumping. This acts as if you held space in
|
||||||
/// vanilla. If you want to jump once, use the `jump` function.
|
/// vanilla. If you want to jump once, use the `jump` function.
|
||||||
|
|
|
@ -1,15 +1,6 @@
|
||||||
use std::{collections::HashSet, io::Cursor, sync::Arc};
|
use std::{collections::HashSet, io::Cursor, sync::Arc};
|
||||||
|
|
||||||
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
|
use azalea_core::{ChunkPos, ResourceLocation, Vec3};
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, CoreStage, Plugin},
|
|
||||||
component::Component,
|
|
||||||
ecs::Ecs,
|
|
||||||
entity::Entity,
|
|
||||||
event::{EventReader, EventWriter, Events},
|
|
||||||
schedule::{StageLabel, SystemStage},
|
|
||||||
system::{Commands, Query, ResMut, SystemState},
|
|
||||||
};
|
|
||||||
use azalea_protocol::{
|
use azalea_protocol::{
|
||||||
connect::{ReadConnection, WriteConnection},
|
connect::{ReadConnection, WriteConnection},
|
||||||
packets::game::{
|
packets::game::{
|
||||||
|
@ -25,12 +16,21 @@ use azalea_protocol::{
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{
|
entity::{
|
||||||
metadata::{apply_metadata, Health, PlayerMetadataBundle},
|
metadata::{apply_metadata, Health, PlayerMetadataBundle},
|
||||||
set_rotation, Dead, EntityBundle, EntityKind, LastSentPosition, MinecraftEntityId, Physics,
|
set_rotation, Dead, EntityBundle, EntityKind, EntityUpdateSet, LastSentPosition,
|
||||||
PlayerBundle, Position, WorldName,
|
MinecraftEntityId, Physics, PlayerBundle, Position, WorldName,
|
||||||
},
|
},
|
||||||
entity::{LoadedBy, RelativeEntityUpdate},
|
entity::{LoadedBy, RelativeEntityUpdate},
|
||||||
PartialWorld, WorldContainer,
|
PartialWorld, WorldContainer,
|
||||||
};
|
};
|
||||||
|
use bevy_app::{App, CoreSet, Plugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
|
event::{EventReader, EventWriter, Events},
|
||||||
|
schedule::IntoSystemConfig,
|
||||||
|
system::{Commands, Query, ResMut, SystemState},
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
use log::{debug, error, trace, warn};
|
use log::{debug, error, trace, warn};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
@ -46,7 +46,7 @@ use crate::{
|
||||||
/// ```
|
/// ```
|
||||||
/// # use azalea_client::packet_handling::PacketEvent;
|
/// # use azalea_client::packet_handling::PacketEvent;
|
||||||
/// # use azalea_protocol::packets::game::ClientboundGamePacket;
|
/// # use azalea_protocol::packets::game::ClientboundGamePacket;
|
||||||
/// # use azalea_ecs::event::EventReader;
|
/// # use bevy_ecs::event::EventReader;
|
||||||
///
|
///
|
||||||
/// fn handle_packets(mut events: EventReader<PacketEvent>) {
|
/// fn handle_packets(mut events: EventReader<PacketEvent>) {
|
||||||
/// for PacketEvent {
|
/// for PacketEvent {
|
||||||
|
@ -72,25 +72,22 @@ pub struct PacketEvent {
|
||||||
|
|
||||||
pub struct PacketHandlerPlugin;
|
pub struct PacketHandlerPlugin;
|
||||||
|
|
||||||
#[derive(StageLabel)]
|
|
||||||
pub struct SendPacketEventsStage;
|
|
||||||
|
|
||||||
impl Plugin for PacketHandlerPlugin {
|
impl Plugin for PacketHandlerPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_stage_before(
|
app.add_system(send_packet_events.in_base_set(CoreSet::First))
|
||||||
CoreStage::PreUpdate,
|
.add_system(
|
||||||
SendPacketEventsStage,
|
process_packet_events
|
||||||
SystemStage::parallel(),
|
.in_base_set(CoreSet::PreUpdate)
|
||||||
)
|
// we want to index and deindex right after
|
||||||
.add_system_to_stage(SendPacketEventsStage, send_packet_events)
|
.before(EntityUpdateSet::Deindex),
|
||||||
.add_system_to_stage(CoreStage::PreUpdate, process_packet_events)
|
)
|
||||||
.init_resource::<Events<PacketEvent>>()
|
.init_resource::<Events<PacketEvent>>()
|
||||||
.add_event::<AddPlayerEvent>()
|
.add_event::<AddPlayerEvent>()
|
||||||
.add_event::<RemovePlayerEvent>()
|
.add_event::<RemovePlayerEvent>()
|
||||||
.add_event::<UpdatePlayerEvent>()
|
.add_event::<UpdatePlayerEvent>()
|
||||||
.add_event::<ChatReceivedEvent>()
|
.add_event::<ChatReceivedEvent>()
|
||||||
.add_event::<DeathEvent>()
|
.add_event::<DeathEvent>()
|
||||||
.add_event::<KeepAliveEvent>();
|
.add_event::<KeepAliveEvent>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +165,7 @@ pub fn send_packet_events(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_packet_events(ecs: &mut Ecs) {
|
fn process_packet_events(ecs: &mut World) {
|
||||||
let mut events_owned = Vec::new();
|
let mut events_owned = Vec::new();
|
||||||
let mut system_state: SystemState<EventReader<PacketEvent>> = SystemState::new(ecs);
|
let mut system_state: SystemState<EventReader<PacketEvent>> = SystemState::new(ecs);
|
||||||
let mut events = system_state.get_mut(ecs);
|
let mut events = system_state.get_mut(ecs);
|
||||||
|
@ -715,8 +712,7 @@ fn process_packet_events(ecs: &mut Ecs) {
|
||||||
|
|
||||||
if let Some(entity) = entity {
|
if let Some(entity) = entity {
|
||||||
let new_position = p.position;
|
let new_position = p.position;
|
||||||
commands.add(RelativeEntityUpdate {
|
commands.entity(entity).add(RelativeEntityUpdate {
|
||||||
entity,
|
|
||||||
partial_world: local_player.partial_world.clone(),
|
partial_world: local_player.partial_world.clone(),
|
||||||
update: Box::new(move |entity| {
|
update: Box::new(move |entity| {
|
||||||
let mut position = entity.get_mut::<Position>().unwrap();
|
let mut position = entity.get_mut::<Position>().unwrap();
|
||||||
|
@ -747,8 +743,7 @@ fn process_packet_events(ecs: &mut Ecs) {
|
||||||
|
|
||||||
if let Some(entity) = entity {
|
if let Some(entity) = entity {
|
||||||
let delta = p.delta.clone();
|
let delta = p.delta.clone();
|
||||||
commands.add(RelativeEntityUpdate {
|
commands.entity(entity).add(RelativeEntityUpdate {
|
||||||
entity,
|
|
||||||
partial_world: local_player.partial_world.clone(),
|
partial_world: local_player.partial_world.clone(),
|
||||||
update: Box::new(move |entity_mut| {
|
update: Box::new(move |entity_mut| {
|
||||||
let mut position = entity_mut.get_mut::<Position>().unwrap();
|
let mut position = entity_mut.get_mut::<Position>().unwrap();
|
||||||
|
@ -776,8 +771,7 @@ fn process_packet_events(ecs: &mut Ecs) {
|
||||||
|
|
||||||
if let Some(entity) = entity {
|
if let Some(entity) = entity {
|
||||||
let delta = p.delta.clone();
|
let delta = p.delta.clone();
|
||||||
commands.add(RelativeEntityUpdate {
|
commands.entity(entity).add(RelativeEntityUpdate {
|
||||||
entity,
|
|
||||||
partial_world: local_player.partial_world.clone(),
|
partial_world: local_player.partial_world.clone(),
|
||||||
update: Box::new(move |entity_mut| {
|
update: Box::new(move |entity_mut| {
|
||||||
let mut position = entity_mut.get_mut::<Position>().unwrap();
|
let mut position = entity_mut.get_mut::<Position>().unwrap();
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use azalea_auth::game_profile::GameProfile;
|
use azalea_auth::game_profile::GameProfile;
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_core::GameType;
|
use azalea_core::GameType;
|
||||||
use azalea_ecs::{
|
use azalea_world::entity::EntityInfos;
|
||||||
|
use bevy_ecs::{
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
system::{Commands, Res},
|
system::{Commands, Res},
|
||||||
};
|
};
|
||||||
use azalea_world::entity::EntityInfos;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{packet_handling::AddPlayerEvent, GameProfileComponent};
|
use crate::{packet_handling::AddPlayerEvent, GameProfileComponent};
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
//! Borrowed from `bevy_core`.
|
//! Borrowed from `bevy_core`.
|
||||||
|
|
||||||
use azalea_ecs::{
|
use std::marker::PhantomData;
|
||||||
app::{App, Plugin},
|
|
||||||
schedule::IntoSystemDescriptor,
|
use bevy_app::{App, CoreSet, Plugin};
|
||||||
system::Resource,
|
use bevy_ecs::{
|
||||||
|
schedule::IntoSystemConfig,
|
||||||
|
system::{NonSend, Resource},
|
||||||
|
};
|
||||||
|
use bevy_tasks::{
|
||||||
|
tick_global_task_pools_on_main_thread, AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool,
|
||||||
|
TaskPoolBuilder,
|
||||||
};
|
};
|
||||||
use bevy_tasks::{AsyncComputeTaskPool, ComputeTaskPool, IoTaskPool, TaskPoolBuilder};
|
|
||||||
|
|
||||||
/// Setup of default task pools: `AsyncComputeTaskPool`, `ComputeTaskPool`,
|
/// Setup of default task pools: `AsyncComputeTaskPool`, `ComputeTaskPool`,
|
||||||
/// `IoTaskPool`.
|
/// `IoTaskPool`.
|
||||||
|
@ -22,13 +27,16 @@ impl Plugin for TaskPoolPlugin {
|
||||||
self.task_pool_options.create_default_pools();
|
self.task_pool_options.create_default_pools();
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
app.add_system_to_stage(
|
app.add_system(tick_global_task_pools.in_base_set(CoreSet::Last));
|
||||||
azalea_ecs::app::CoreStage::Last,
|
|
||||||
bevy_tasks::tick_global_task_pools_on_main_thread.at_end(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct NonSendMarker(PhantomData<*mut ()>);
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
fn tick_global_task_pools(_main_thread_marker: Option<NonSend<NonSendMarker>>) {
|
||||||
|
tick_global_task_pools_on_main_thread();
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper for configuring and creating the default task pools. For end-users
|
/// Helper for configuring and creating the default task pools. For end-users
|
||||||
/// who want full control, set up [`TaskPoolPlugin`](TaskPoolPlugin)
|
/// who want full control, set up [`TaskPoolPlugin`](TaskPoolPlugin)
|
||||||
#[derive(Clone, Resource)]
|
#[derive(Clone, Resource)]
|
||||||
|
|
|
@ -9,10 +9,10 @@ version = "0.6.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
|
azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
|
||||||
azalea-chat = {path = "../azalea-chat", version = "^0.6.0" }
|
azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
|
||||||
azalea-nbt = {path = "../azalea-nbt", version = "^0.6.0" }
|
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
|
||||||
bevy_ecs = {version = "0.9.1", default-features = false, optional = true}
|
bevy_ecs = { version = "0.10.0", default-features = false, optional = true }
|
||||||
uuid = "^1.1.2"
|
uuid = "^1.1.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
[package]
|
|
||||||
description = "ECS stuff used in Azalea"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT"
|
|
||||||
name = "azalea-ecs"
|
|
||||||
version = "0.6.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
azalea-ecs-macros = {path = "./azalea-ecs-macros", version = "^0.6.0"}
|
|
||||||
bevy_app = "0.9.1"
|
|
||||||
bevy_ecs = {version = "0.9.1", default-features = false}
|
|
||||||
tokio = {version = "1.25.0", features = ["time"]}
|
|
|
@ -1,15 +0,0 @@
|
||||||
[package]
|
|
||||||
description = "Azalea ECS Macros"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
name = "azalea-ecs-macros"
|
|
||||||
version = "0.6.0"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2 = "1.0"
|
|
||||||
quote = "1.0"
|
|
||||||
syn = "1.0"
|
|
||||||
toml = "0.7.0"
|
|
|
@ -1,125 +0,0 @@
|
||||||
use crate::utils::{get_lit_str, Symbol};
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
||||||
use quote::{quote, ToTokens};
|
|
||||||
use syn::{parse_macro_input, parse_quote, DeriveInput, Error, Ident, Path, Result};
|
|
||||||
|
|
||||||
use crate::utils;
|
|
||||||
|
|
||||||
pub fn derive_resource(input: TokenStream) -> TokenStream {
|
|
||||||
let mut ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
let azalea_ecs_path: Path = crate::azalea_ecs_path();
|
|
||||||
|
|
||||||
ast.generics
|
|
||||||
.make_where_clause()
|
|
||||||
.predicates
|
|
||||||
.push(parse_quote! { Self: Send + Sync + 'static });
|
|
||||||
|
|
||||||
let struct_name = &ast.ident;
|
|
||||||
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
impl #impl_generics #azalea_ecs_path::system::_BevyResource for #struct_name #type_generics #where_clause {
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn derive_component(input: TokenStream) -> TokenStream {
|
|
||||||
let mut ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
let azalea_ecs_path: Path = crate::azalea_ecs_path();
|
|
||||||
|
|
||||||
let attrs = match parse_component_attr(&ast) {
|
|
||||||
Ok(attrs) => attrs,
|
|
||||||
Err(e) => return e.into_compile_error().into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let storage = storage_path(&azalea_ecs_path, attrs.storage);
|
|
||||||
|
|
||||||
ast.generics
|
|
||||||
.make_where_clause()
|
|
||||||
.predicates
|
|
||||||
.push(parse_quote! { Self: Send + Sync + 'static });
|
|
||||||
|
|
||||||
let struct_name = &ast.ident;
|
|
||||||
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
impl #impl_generics #azalea_ecs_path::component::_BevyComponent for #struct_name #type_generics #where_clause {
|
|
||||||
type Storage = #storage;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const COMPONENT: Symbol = Symbol("component");
|
|
||||||
pub const STORAGE: Symbol = Symbol("storage");
|
|
||||||
|
|
||||||
struct Attrs {
|
|
||||||
storage: StorageTy,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
enum StorageTy {
|
|
||||||
Table,
|
|
||||||
SparseSet,
|
|
||||||
}
|
|
||||||
|
|
||||||
// values for `storage` attribute
|
|
||||||
const TABLE: &str = "Table";
|
|
||||||
const SPARSE_SET: &str = "SparseSet";
|
|
||||||
|
|
||||||
fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
|
|
||||||
let meta_items = utils::parse_attrs(ast, COMPONENT)?;
|
|
||||||
|
|
||||||
let mut attrs = Attrs {
|
|
||||||
storage: StorageTy::Table,
|
|
||||||
};
|
|
||||||
|
|
||||||
for meta in meta_items {
|
|
||||||
use syn::{
|
|
||||||
Meta::NameValue,
|
|
||||||
NestedMeta::{Lit, Meta},
|
|
||||||
};
|
|
||||||
match meta {
|
|
||||||
Meta(NameValue(m)) if m.path == STORAGE => {
|
|
||||||
attrs.storage = match get_lit_str(STORAGE, &m.lit)?.value().as_str() {
|
|
||||||
TABLE => StorageTy::Table,
|
|
||||||
SPARSE_SET => StorageTy::SparseSet,
|
|
||||||
s => {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
m.lit,
|
|
||||||
format!(
|
|
||||||
"Invalid storage type `{s}`, expected '{TABLE}' or '{SPARSE_SET}'."
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Meta(meta_item) => {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
meta_item.path(),
|
|
||||||
format!(
|
|
||||||
"unknown component attribute `{}`",
|
|
||||||
meta_item.path().into_token_stream()
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Lit(lit) => {
|
|
||||||
return Err(Error::new_spanned(
|
|
||||||
lit,
|
|
||||||
"unexpected literal in component attribute",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn storage_path(azalea_ecs_path: &Path, ty: StorageTy) -> TokenStream2 {
|
|
||||||
let typename = match ty {
|
|
||||||
StorageTy::Table => Ident::new("TableStorage", Span::call_site()),
|
|
||||||
StorageTy::SparseSet => Ident::new("SparseStorage", Span::call_site()),
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! { #azalea_ecs_path::component::#typename }
|
|
||||||
}
|
|
|
@ -1,466 +0,0 @@
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::{Ident, Span};
|
|
||||||
use quote::{quote, ToTokens};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
parse_quote,
|
|
||||||
punctuated::Punctuated,
|
|
||||||
Attribute, Data, DataStruct, DeriveInput, Field, Fields,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::azalea_ecs_path;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct FetchStructAttributes {
|
|
||||||
pub is_mutable: bool,
|
|
||||||
pub derive_args: Punctuated<syn::NestedMeta, syn::token::Comma>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static MUTABLE_ATTRIBUTE_NAME: &str = "mutable";
|
|
||||||
static DERIVE_ATTRIBUTE_NAME: &str = "derive";
|
|
||||||
|
|
||||||
mod field_attr_keywords {
|
|
||||||
syn::custom_keyword!(ignore);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static WORLD_QUERY_ATTRIBUTE_NAME: &str = "world_query";
|
|
||||||
|
|
||||||
pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
|
|
||||||
let visibility = ast.vis;
|
|
||||||
|
|
||||||
let mut fetch_struct_attributes = FetchStructAttributes::default();
|
|
||||||
for attr in &ast.attrs {
|
|
||||||
if !attr
|
|
||||||
.path
|
|
||||||
.get_ident()
|
|
||||||
.map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
attr.parse_args_with(|input: ParseStream| {
|
|
||||||
let meta = input.parse_terminated::<syn::Meta, syn::token::Comma>(syn::Meta::parse)?;
|
|
||||||
for meta in meta {
|
|
||||||
let ident = meta.path().get_ident().unwrap_or_else(|| {
|
|
||||||
panic!(
|
|
||||||
"Unrecognized attribute: `{}`",
|
|
||||||
meta.path().to_token_stream()
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if ident == MUTABLE_ATTRIBUTE_NAME {
|
|
||||||
if let syn::Meta::Path(_) = meta {
|
|
||||||
fetch_struct_attributes.is_mutable = true;
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"The `{MUTABLE_ATTRIBUTE_NAME}` attribute is expected to have no value or arguments"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if ident == DERIVE_ATTRIBUTE_NAME {
|
|
||||||
if let syn::Meta::List(meta_list) = meta {
|
|
||||||
fetch_struct_attributes
|
|
||||||
.derive_args
|
|
||||||
.extend(meta_list.nested.iter().cloned());
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"Expected a structured list within the `{DERIVE_ATTRIBUTE_NAME}` attribute"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
panic!(
|
|
||||||
"Unrecognized attribute: `{}`",
|
|
||||||
meta.path().to_token_stream()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_ATTRIBUTE_NAME}` attribute format"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = azalea_ecs_path();
|
|
||||||
|
|
||||||
let user_generics = ast.generics.clone();
|
|
||||||
let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl();
|
|
||||||
let user_generics_with_world = {
|
|
||||||
let mut generics = ast.generics.clone();
|
|
||||||
generics.params.insert(0, parse_quote!('__w));
|
|
||||||
generics
|
|
||||||
};
|
|
||||||
let (user_impl_generics_with_world, user_ty_generics_with_world, user_where_clauses_with_world) =
|
|
||||||
user_generics_with_world.split_for_impl();
|
|
||||||
|
|
||||||
let struct_name = ast.ident.clone();
|
|
||||||
let read_only_struct_name = if fetch_struct_attributes.is_mutable {
|
|
||||||
Ident::new(&format!("{struct_name}ReadOnly"), Span::call_site())
|
|
||||||
} else {
|
|
||||||
struct_name.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let item_struct_name = Ident::new(&format!("{struct_name}Item"), Span::call_site());
|
|
||||||
let read_only_item_struct_name = if fetch_struct_attributes.is_mutable {
|
|
||||||
Ident::new(&format!("{struct_name}ReadOnlyItem"), Span::call_site())
|
|
||||||
} else {
|
|
||||||
item_struct_name.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let fetch_struct_name = Ident::new(&format!("{struct_name}Fetch"), Span::call_site());
|
|
||||||
let read_only_fetch_struct_name = if fetch_struct_attributes.is_mutable {
|
|
||||||
Ident::new(&format!("{struct_name}ReadOnlyFetch"), Span::call_site())
|
|
||||||
} else {
|
|
||||||
fetch_struct_name.clone()
|
|
||||||
};
|
|
||||||
|
|
||||||
let state_struct_name = Ident::new(&format!("{struct_name}State"), Span::call_site());
|
|
||||||
|
|
||||||
let fields = match &ast.data {
|
|
||||||
Data::Struct(DataStruct {
|
|
||||||
fields: Fields::Named(fields),
|
|
||||||
..
|
|
||||||
}) => &fields.named,
|
|
||||||
_ => panic!("Expected a struct with named fields"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ignored_field_attrs = Vec::new();
|
|
||||||
let mut ignored_field_visibilities = Vec::new();
|
|
||||||
let mut ignored_field_idents = Vec::new();
|
|
||||||
let mut ignored_field_types = Vec::new();
|
|
||||||
let mut field_attrs = Vec::new();
|
|
||||||
let mut field_visibilities = Vec::new();
|
|
||||||
let mut field_idents = Vec::new();
|
|
||||||
let mut field_types = Vec::new();
|
|
||||||
let mut read_only_field_types = Vec::new();
|
|
||||||
|
|
||||||
for field in fields {
|
|
||||||
let WorldQueryFieldInfo { is_ignored, attrs } = read_world_query_field_info(field);
|
|
||||||
|
|
||||||
let field_ident = field.ident.as_ref().unwrap().clone();
|
|
||||||
if is_ignored {
|
|
||||||
ignored_field_attrs.push(attrs);
|
|
||||||
ignored_field_visibilities.push(field.vis.clone());
|
|
||||||
ignored_field_idents.push(field_ident.clone());
|
|
||||||
ignored_field_types.push(field.ty.clone());
|
|
||||||
} else {
|
|
||||||
field_attrs.push(attrs);
|
|
||||||
field_visibilities.push(field.vis.clone());
|
|
||||||
field_idents.push(field_ident.clone());
|
|
||||||
let field_ty = field.ty.clone();
|
|
||||||
field_types.push(quote!(#field_ty));
|
|
||||||
read_only_field_types.push(quote!(<#field_ty as #path::query::WorldQuery>::ReadOnly));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let derive_args = &fetch_struct_attributes.derive_args;
|
|
||||||
// `#[derive()]` is valid syntax
|
|
||||||
let derive_macro_call = quote! { #[derive(#derive_args)] };
|
|
||||||
|
|
||||||
let impl_fetch = |is_readonly: bool| {
|
|
||||||
let struct_name = if is_readonly {
|
|
||||||
&read_only_struct_name
|
|
||||||
} else {
|
|
||||||
&struct_name
|
|
||||||
};
|
|
||||||
let item_struct_name = if is_readonly {
|
|
||||||
&read_only_item_struct_name
|
|
||||||
} else {
|
|
||||||
&item_struct_name
|
|
||||||
};
|
|
||||||
let fetch_struct_name = if is_readonly {
|
|
||||||
&read_only_fetch_struct_name
|
|
||||||
} else {
|
|
||||||
&fetch_struct_name
|
|
||||||
};
|
|
||||||
|
|
||||||
let field_types = if is_readonly {
|
|
||||||
&read_only_field_types
|
|
||||||
} else {
|
|
||||||
&field_types
|
|
||||||
};
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#derive_macro_call
|
|
||||||
#[doc = "Automatically generated [`WorldQuery`] item type for [`"]
|
|
||||||
#[doc = stringify!(#struct_name)]
|
|
||||||
#[doc = "`], returned when iterating over query results."]
|
|
||||||
#[automatically_derived]
|
|
||||||
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
|
|
||||||
#(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQuery>::Item<'__w>,)*
|
|
||||||
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[doc = "Automatically generated internal [`WorldQuery`] fetch type for [`"]
|
|
||||||
#[doc = stringify!(#struct_name)]
|
|
||||||
#[doc = "`], used to define the world data accessed by this query."]
|
|
||||||
#[automatically_derived]
|
|
||||||
#visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
|
|
||||||
#(#field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)*
|
|
||||||
#(#ignored_field_idents: #ignored_field_types,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field
|
|
||||||
unsafe impl #user_impl_generics #path::query::WorldQuery
|
|
||||||
for #struct_name #user_ty_generics #user_where_clauses {
|
|
||||||
|
|
||||||
type Item<'__w> = #item_struct_name #user_ty_generics_with_world;
|
|
||||||
type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world;
|
|
||||||
type ReadOnly = #read_only_struct_name #user_ty_generics;
|
|
||||||
type State = #state_struct_name #user_ty_generics;
|
|
||||||
|
|
||||||
fn shrink<'__wlong: '__wshort, '__wshort>(
|
|
||||||
item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong>
|
|
||||||
) -> <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wshort> {
|
|
||||||
#item_struct_name {
|
|
||||||
#(
|
|
||||||
#field_idents: <#field_types>::shrink(item.#field_idents),
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#ignored_field_idents: item.#ignored_field_idents,
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn init_fetch<'__w>(
|
|
||||||
_world: &'__w #path::world::World,
|
|
||||||
state: &Self::State,
|
|
||||||
_last_change_tick: u32,
|
|
||||||
_change_tick: u32
|
|
||||||
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
|
|
||||||
#fetch_struct_name {
|
|
||||||
#(#field_idents:
|
|
||||||
<#field_types>::init_fetch(
|
|
||||||
_world,
|
|
||||||
&state.#field_idents,
|
|
||||||
_last_change_tick,
|
|
||||||
_change_tick
|
|
||||||
),
|
|
||||||
)*
|
|
||||||
#(#ignored_field_idents: Default::default(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn clone_fetch<'__w>(
|
|
||||||
_fetch: &<Self as #path::query::WorldQuery>::Fetch<'__w>
|
|
||||||
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
|
|
||||||
#fetch_struct_name {
|
|
||||||
#(
|
|
||||||
#field_idents: <#field_types>::clone_fetch(& _fetch. #field_idents),
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
#ignored_field_idents: Default::default(),
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*;
|
|
||||||
|
|
||||||
const IS_ARCHETYPAL: bool = true #(&& <#field_types>::IS_ARCHETYPAL)*;
|
|
||||||
|
|
||||||
/// SAFETY: we call `set_archetype` for each member that implements `Fetch`
|
|
||||||
#[inline]
|
|
||||||
unsafe fn set_archetype<'__w>(
|
|
||||||
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
|
|
||||||
_state: &Self::State,
|
|
||||||
_archetype: &'__w #path::archetype::Archetype,
|
|
||||||
_table: &'__w #path::storage::Table
|
|
||||||
) {
|
|
||||||
#(<#field_types>::set_archetype(&mut _fetch.#field_idents, &_state.#field_idents, _archetype, _table);)*
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SAFETY: we call `set_table` for each member that implements `Fetch`
|
|
||||||
#[inline]
|
|
||||||
unsafe fn set_table<'__w>(
|
|
||||||
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
|
|
||||||
_state: &Self::State,
|
|
||||||
_table: &'__w #path::storage::Table
|
|
||||||
) {
|
|
||||||
#(<#field_types>::set_table(&mut _fetch.#field_idents, &_state.#field_idents, _table);)*
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SAFETY: we call `fetch` for each member that implements `Fetch`.
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn fetch<'__w>(
|
|
||||||
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
|
|
||||||
_entity: Entity,
|
|
||||||
_table_row: usize
|
|
||||||
) -> <Self as #path::query::WorldQuery>::Item<'__w> {
|
|
||||||
Self::Item {
|
|
||||||
#(#field_idents: <#field_types>::fetch(&mut _fetch.#field_idents, _entity, _table_row),)*
|
|
||||||
#(#ignored_field_idents: Default::default(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn filter_fetch<'__w>(
|
|
||||||
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
|
|
||||||
_entity: Entity,
|
|
||||||
_table_row: usize
|
|
||||||
) -> bool {
|
|
||||||
true #(&& <#field_types>::filter_fetch(&mut _fetch.#field_idents, _entity, _table_row))*
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) {
|
|
||||||
#( <#field_types>::update_component_access(&state.#field_idents, _access); )*
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_archetype_component_access(
|
|
||||||
state: &Self::State,
|
|
||||||
_archetype: &#path::archetype::Archetype,
|
|
||||||
_access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId>
|
|
||||||
) {
|
|
||||||
#(
|
|
||||||
<#field_types>::update_archetype_component_access(&state.#field_idents, _archetype, _access);
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
|
|
||||||
#state_struct_name {
|
|
||||||
#(#field_idents: <#field_types>::init_state(world),)*
|
|
||||||
#(#ignored_field_idents: Default::default(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool {
|
|
||||||
true #(&& <#field_types>::matches_component_set(&state.#field_idents, _set_contains_id))*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mutable_impl = impl_fetch(false);
|
|
||||||
let readonly_impl = if fetch_struct_attributes.is_mutable {
|
|
||||||
let world_query_impl = impl_fetch(true);
|
|
||||||
quote! {
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[doc = "Automatically generated internal [`WorldQuery`] type for [`"]
|
|
||||||
#[doc = stringify!(#struct_name)]
|
|
||||||
#[doc = "`], used for read-only access."]
|
|
||||||
#[automatically_derived]
|
|
||||||
#visibility struct #read_only_struct_name #user_impl_generics #user_where_clauses {
|
|
||||||
#( #field_idents: #read_only_field_types, )*
|
|
||||||
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
#world_query_impl
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let read_only_asserts = if fetch_struct_attributes.is_mutable {
|
|
||||||
quote! {
|
|
||||||
// Double-check that the data fetched by `<_ as WorldQuery>::ReadOnly` is read-only.
|
|
||||||
// This is technically unnecessary as `<_ as WorldQuery>::ReadOnly: ReadOnlyWorldQuery`
|
|
||||||
// but to protect against future mistakes we assert the assoc type implements `ReadOnlyWorldQuery` anyway
|
|
||||||
#( assert_readonly::<#read_only_field_types>(); )*
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote! {
|
|
||||||
// Statically checks that the safety guarantee of `ReadOnlyWorldQuery` for `$fetch_struct_name` actually holds true.
|
|
||||||
// We need this to make sure that we don't compile `ReadOnlyWorldQuery` if our struct contains nested `WorldQuery`
|
|
||||||
// members that don't implement it. I.e.:
|
|
||||||
// ```
|
|
||||||
// #[derive(WorldQuery)]
|
|
||||||
// pub struct Foo { a: &'static mut MyComponent }
|
|
||||||
// ```
|
|
||||||
#( assert_readonly::<#field_types>(); )*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
#mutable_impl
|
|
||||||
|
|
||||||
#readonly_impl
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[doc = "Automatically generated internal [`WorldQuery`] state type for [`"]
|
|
||||||
#[doc = stringify!(#struct_name)]
|
|
||||||
#[doc = "`], used for caching."]
|
|
||||||
#[automatically_derived]
|
|
||||||
#visibility struct #state_struct_name #user_impl_generics #user_where_clauses {
|
|
||||||
#(#field_idents: <#field_types as #path::query::WorldQuery>::State,)*
|
|
||||||
#(#ignored_field_idents: #ignored_field_types,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SAFETY: we assert fields are readonly below
|
|
||||||
unsafe impl #user_impl_generics #path::query::ReadOnlyWorldQuery
|
|
||||||
for #read_only_struct_name #user_ty_generics #user_where_clauses {}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const _: () = {
|
|
||||||
fn assert_readonly<T>()
|
|
||||||
where
|
|
||||||
T: #path::query::ReadOnlyWorldQuery,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// We generate a readonly assertion for every struct member.
|
|
||||||
fn assert_all #user_impl_generics_with_world () #user_where_clauses_with_world {
|
|
||||||
#read_only_asserts
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// The original struct will most likely be left unused. As we don't want our users having
|
|
||||||
// to specify `#[allow(dead_code)]` for their custom queries, we are using this cursed
|
|
||||||
// workaround.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const _: () = {
|
|
||||||
fn dead_code_workaround #user_impl_generics (
|
|
||||||
q: #struct_name #user_ty_generics,
|
|
||||||
q2: #read_only_struct_name #user_ty_generics
|
|
||||||
) #user_where_clauses {
|
|
||||||
#(q.#field_idents;)*
|
|
||||||
#(q.#ignored_field_idents;)*
|
|
||||||
#(q2.#field_idents;)*
|
|
||||||
#(q2.#ignored_field_idents;)*
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
struct WorldQueryFieldInfo {
|
|
||||||
/// Has `#[fetch(ignore)]` or `#[filter_fetch(ignore)]` attribute.
|
|
||||||
is_ignored: bool,
|
|
||||||
/// All field attributes except for `world_query` ones.
|
|
||||||
attrs: Vec<Attribute>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read_world_query_field_info(field: &Field) -> WorldQueryFieldInfo {
|
|
||||||
let is_ignored = field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.find(|attr| {
|
|
||||||
attr.path
|
|
||||||
.get_ident()
|
|
||||||
.map_or(false, |ident| ident == WORLD_QUERY_ATTRIBUTE_NAME)
|
|
||||||
})
|
|
||||||
.map_or(false, |attr| {
|
|
||||||
let mut is_ignored = false;
|
|
||||||
attr.parse_args_with(|input: ParseStream| {
|
|
||||||
if input
|
|
||||||
.parse::<Option<field_attr_keywords::ignore>>()?
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
is_ignored = true;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| panic!("Invalid `{WORLD_QUERY_ATTRIBUTE_NAME}` attribute format"));
|
|
||||||
|
|
||||||
is_ignored
|
|
||||||
});
|
|
||||||
|
|
||||||
let attrs = field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.filter(|attr| {
|
|
||||||
attr.path
|
|
||||||
.get_ident()
|
|
||||||
.map_or(true, |ident| ident != WORLD_QUERY_ATTRIBUTE_NAME)
|
|
||||||
})
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
WorldQueryFieldInfo { is_ignored, attrs }
|
|
||||||
}
|
|
|
@ -1,525 +0,0 @@
|
||||||
//! A fork of bevy_ecs_macros that uses azalea_ecs instead of bevy_ecs.
|
|
||||||
|
|
||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
mod component;
|
|
||||||
mod fetch;
|
|
||||||
pub(crate) mod utils;
|
|
||||||
|
|
||||||
use crate::fetch::derive_world_query_impl;
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use proc_macro2::Span;
|
|
||||||
use quote::{format_ident, quote};
|
|
||||||
use syn::{
|
|
||||||
parse::{Parse, ParseStream},
|
|
||||||
parse_macro_input,
|
|
||||||
punctuated::Punctuated,
|
|
||||||
spanned::Spanned,
|
|
||||||
token::Comma,
|
|
||||||
DeriveInput, Field, GenericParam, Ident, Index, LitInt, Meta, MetaList, NestedMeta, Result,
|
|
||||||
Token, TypeParam,
|
|
||||||
};
|
|
||||||
use utils::{derive_label, get_named_struct_fields, BevyManifest};
|
|
||||||
|
|
||||||
struct AllTuples {
|
|
||||||
macro_ident: Ident,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
idents: Vec<Ident>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Parse for AllTuples {
|
|
||||||
fn parse(input: ParseStream) -> Result<Self> {
|
|
||||||
let macro_ident = input.parse::<Ident>()?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let start = input.parse::<LitInt>()?.base10_parse()?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let end = input.parse::<LitInt>()?.base10_parse()?;
|
|
||||||
input.parse::<Comma>()?;
|
|
||||||
let mut idents = vec![input.parse::<Ident>()?];
|
|
||||||
while input.parse::<Comma>().is_ok() {
|
|
||||||
idents.push(input.parse::<Ident>()?);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(AllTuples {
|
|
||||||
macro_ident,
|
|
||||||
start,
|
|
||||||
end,
|
|
||||||
idents,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn all_tuples(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as AllTuples);
|
|
||||||
let len = input.end - input.start;
|
|
||||||
let mut ident_tuples = Vec::with_capacity(len);
|
|
||||||
for i in input.start..=input.end {
|
|
||||||
let idents = input
|
|
||||||
.idents
|
|
||||||
.iter()
|
|
||||||
.map(|ident| format_ident!("{}{}", ident, i));
|
|
||||||
if input.idents.len() < 2 {
|
|
||||||
ident_tuples.push(quote! {
|
|
||||||
#(#idents)*
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
ident_tuples.push(quote! {
|
|
||||||
(#(#idents),*)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let macro_ident = &input.macro_ident;
|
|
||||||
let invocations = (input.start..=input.end).map(|i| {
|
|
||||||
let ident_tuples = &ident_tuples[..i];
|
|
||||||
quote! {
|
|
||||||
#macro_ident!(#(#ident_tuples),*);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
#(
|
|
||||||
#invocations
|
|
||||||
)*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BundleFieldKind {
|
|
||||||
Component,
|
|
||||||
Ignore,
|
|
||||||
}
|
|
||||||
|
|
||||||
const BUNDLE_ATTRIBUTE_NAME: &str = "bundle";
|
|
||||||
const BUNDLE_ATTRIBUTE_IGNORE_NAME: &str = "ignore";
|
|
||||||
|
|
||||||
#[proc_macro_derive(Bundle, attributes(bundle))]
|
|
||||||
pub fn derive_bundle(input: TokenStream) -> TokenStream {
|
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
let ecs_path = azalea_ecs_path();
|
|
||||||
|
|
||||||
let named_fields = match get_named_struct_fields(&ast.data) {
|
|
||||||
Ok(fields) => &fields.named,
|
|
||||||
Err(e) => return e.into_compile_error().into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut field_kind = Vec::with_capacity(named_fields.len());
|
|
||||||
|
|
||||||
'field_loop: for field in named_fields.iter() {
|
|
||||||
for attr in &field.attrs {
|
|
||||||
if attr.path.is_ident(BUNDLE_ATTRIBUTE_NAME) {
|
|
||||||
if let Ok(Meta::List(MetaList { nested, .. })) = attr.parse_meta() {
|
|
||||||
if let Some(&NestedMeta::Meta(Meta::Path(ref path))) = nested.first() {
|
|
||||||
if path.is_ident(BUNDLE_ATTRIBUTE_IGNORE_NAME) {
|
|
||||||
field_kind.push(BundleFieldKind::Ignore);
|
|
||||||
continue 'field_loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
return syn::Error::new(
|
|
||||||
path.span(),
|
|
||||||
format!(
|
|
||||||
"Invalid bundle attribute. Use `{BUNDLE_ATTRIBUTE_IGNORE_NAME}`"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.into_compile_error()
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
|
|
||||||
return syn::Error::new(attr.span(), format!("Invalid bundle attribute. Use `#[{BUNDLE_ATTRIBUTE_NAME}({BUNDLE_ATTRIBUTE_IGNORE_NAME})]`")).into_compile_error().into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
field_kind.push(BundleFieldKind::Component);
|
|
||||||
}
|
|
||||||
|
|
||||||
let field = named_fields
|
|
||||||
.iter()
|
|
||||||
.map(|field| field.ident.as_ref().unwrap())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let field_type = named_fields
|
|
||||||
.iter()
|
|
||||||
.map(|field| &field.ty)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let mut field_component_ids = Vec::new();
|
|
||||||
let mut field_get_components = Vec::new();
|
|
||||||
let mut field_from_components = Vec::new();
|
|
||||||
for ((field_type, field_kind), field) in
|
|
||||||
field_type.iter().zip(field_kind.iter()).zip(field.iter())
|
|
||||||
{
|
|
||||||
match field_kind {
|
|
||||||
BundleFieldKind::Component => {
|
|
||||||
field_component_ids.push(quote! {
|
|
||||||
<#field_type as #ecs_path::bundle::_BevyBundle>::component_ids(components, storages, &mut *ids);
|
|
||||||
});
|
|
||||||
field_get_components.push(quote! {
|
|
||||||
self.#field.get_components(&mut *func);
|
|
||||||
});
|
|
||||||
field_from_components.push(quote! {
|
|
||||||
#field: <#field_type as #ecs_path::bundle::_BevyBundle>::from_components(ctx, &mut *func),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
BundleFieldKind::Ignore => {
|
|
||||||
field_from_components.push(quote! {
|
|
||||||
#field: ::std::default::Default::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let generics = ast.generics;
|
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
||||||
let struct_name = &ast.ident;
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
/// SAFETY: ComponentId is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
|
|
||||||
unsafe impl #impl_generics #ecs_path::bundle::_BevyBundle for #struct_name #ty_generics #where_clause {
|
|
||||||
fn component_ids(
|
|
||||||
components: &mut #ecs_path::component::Components,
|
|
||||||
storages: &mut #ecs_path::storage::Storages,
|
|
||||||
ids: &mut impl FnMut(#ecs_path::component::ComponentId)
|
|
||||||
){
|
|
||||||
#(#field_component_ids)*
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables, non_snake_case)]
|
|
||||||
unsafe fn from_components<__T, __F>(ctx: &mut __T, func: &mut __F) -> Self
|
|
||||||
where
|
|
||||||
__F: FnMut(&mut __T) -> #ecs_path::ptr::OwningPtr<'_>
|
|
||||||
{
|
|
||||||
Self {
|
|
||||||
#(#field_from_components)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn get_components(self, func: &mut impl FnMut(#ecs_path::ptr::OwningPtr<'_>)) {
|
|
||||||
#(#field_get_components)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
|
|
||||||
(0..count)
|
|
||||||
.map(|i| Ident::new(&fmt_string(i), Span::call_site()))
|
|
||||||
.collect::<Vec<Ident>>()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro]
|
|
||||||
pub fn impl_param_set(_input: TokenStream) -> TokenStream {
|
|
||||||
let mut tokens = TokenStream::new();
|
|
||||||
let max_params = 8;
|
|
||||||
let params = get_idents(|i| format!("P{i}"), max_params);
|
|
||||||
let params_fetch = get_idents(|i| format!("PF{i}"), max_params);
|
|
||||||
let metas = get_idents(|i| format!("m{i}"), max_params);
|
|
||||||
let mut param_fn_muts = Vec::new();
|
|
||||||
for (i, param) in params.iter().enumerate() {
|
|
||||||
let fn_name = Ident::new(&format!("p{i}"), Span::call_site());
|
|
||||||
let index = Index::from(i);
|
|
||||||
param_fn_muts.push(quote! {
|
|
||||||
pub fn #fn_name<'a>(&'a mut self) -> <#param::Fetch as SystemParamFetch<'a, 'a>>::Item {
|
|
||||||
// SAFETY: systems run without conflicts with other systems.
|
|
||||||
// Conflicting params in ParamSet are not accessible at the same time
|
|
||||||
// ParamSets are guaranteed to not conflict with other SystemParams
|
|
||||||
unsafe {
|
|
||||||
<#param::Fetch as SystemParamFetch<'a, 'a>>::get_param(&mut self.param_states.#index, &self.system_meta, self.world, self.change_tick)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for param_count in 1..=max_params {
|
|
||||||
let param = ¶ms[0..param_count];
|
|
||||||
let param_fetch = ¶ms_fetch[0..param_count];
|
|
||||||
let meta = &metas[0..param_count];
|
|
||||||
let param_fn_mut = ¶m_fn_muts[0..param_count];
|
|
||||||
tokens.extend(TokenStream::from(quote! {
|
|
||||||
impl<'w, 's, #(#param: SystemParam,)*> SystemParam for ParamSet<'w, 's, (#(#param,)*)>
|
|
||||||
{
|
|
||||||
type Fetch = ParamSetState<(#(#param::Fetch,)*)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: All parameters are constrained to ReadOnlyFetch, so World is only read
|
|
||||||
|
|
||||||
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> ReadOnlySystemParamFetch for ParamSetState<(#(#param_fetch,)*)>
|
|
||||||
where #(#param_fetch: ReadOnlySystemParamFetch,)*
|
|
||||||
{ }
|
|
||||||
|
|
||||||
// SAFETY: Relevant parameter ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any ParamState conflicts
|
|
||||||
// with any prior access, a panic will occur.
|
|
||||||
|
|
||||||
unsafe impl<#(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamState for ParamSetState<(#(#param_fetch,)*)>
|
|
||||||
{
|
|
||||||
fn init(world: &mut World, system_meta: &mut SystemMeta) -> Self {
|
|
||||||
#(
|
|
||||||
// Pretend to add each param to the system alone, see if it conflicts
|
|
||||||
let mut #meta = system_meta.clone();
|
|
||||||
#meta.component_access_set.clear();
|
|
||||||
#meta.archetype_component_access.clear();
|
|
||||||
#param_fetch::init(world, &mut #meta);
|
|
||||||
let #param = #param_fetch::init(world, &mut system_meta.clone());
|
|
||||||
)*
|
|
||||||
#(
|
|
||||||
system_meta
|
|
||||||
.component_access_set
|
|
||||||
.extend(#meta.component_access_set);
|
|
||||||
system_meta
|
|
||||||
.archetype_component_access
|
|
||||||
.extend(&#meta.archetype_component_access);
|
|
||||||
)*
|
|
||||||
ParamSetState((#(#param,)*))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
|
|
||||||
let (#(#param,)*) = &mut self.0;
|
|
||||||
#(
|
|
||||||
#param.new_archetype(archetype, system_meta);
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(&mut self, world: &mut World) {
|
|
||||||
self.0.apply(world)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl<'w, 's, #(#param_fetch: for<'w1, 's1> SystemParamFetch<'w1, 's1>,)*> SystemParamFetch<'w, 's> for ParamSetState<(#(#param_fetch,)*)>
|
|
||||||
{
|
|
||||||
type Item = ParamSet<'w, 's, (#(<#param_fetch as SystemParamFetch<'w, 's>>::Item,)*)>;
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
unsafe fn get_param(
|
|
||||||
state: &'s mut Self,
|
|
||||||
system_meta: &SystemMeta,
|
|
||||||
world: &'w World,
|
|
||||||
change_tick: u32,
|
|
||||||
) -> Self::Item {
|
|
||||||
ParamSet {
|
|
||||||
param_states: &mut state.0,
|
|
||||||
system_meta: system_meta.clone(),
|
|
||||||
world,
|
|
||||||
change_tick,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'w, 's, #(#param: SystemParam,)*> ParamSet<'w, 's, (#(#param,)*)>
|
|
||||||
{
|
|
||||||
|
|
||||||
#(#param_fn_mut)*
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct SystemParamFieldAttributes {
|
|
||||||
pub ignore: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
static SYSTEM_PARAM_ATTRIBUTE_NAME: &str = "system_param";
|
|
||||||
|
|
||||||
/// Implement `SystemParam` to use a struct as a parameter in a system
|
|
||||||
#[proc_macro_derive(SystemParam, attributes(system_param))]
|
|
||||||
pub fn derive_system_param(input: TokenStream) -> TokenStream {
|
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
let fields = match get_named_struct_fields(&ast.data) {
|
|
||||||
Ok(fields) => &fields.named,
|
|
||||||
Err(e) => return e.into_compile_error().into(),
|
|
||||||
};
|
|
||||||
let path = azalea_ecs_path();
|
|
||||||
|
|
||||||
let field_attributes = fields
|
|
||||||
.iter()
|
|
||||||
.map(|field| {
|
|
||||||
(
|
|
||||||
field,
|
|
||||||
field
|
|
||||||
.attrs
|
|
||||||
.iter()
|
|
||||||
.find(|a| *a.path.get_ident().as_ref().unwrap() == SYSTEM_PARAM_ATTRIBUTE_NAME)
|
|
||||||
.map_or_else(SystemParamFieldAttributes::default, |a| {
|
|
||||||
syn::custom_keyword!(ignore);
|
|
||||||
let mut attributes = SystemParamFieldAttributes::default();
|
|
||||||
a.parse_args_with(|input: ParseStream| {
|
|
||||||
if input.parse::<Option<ignore>>()?.is_some() {
|
|
||||||
attributes.ignore = true;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.expect("Invalid 'system_param' attribute format.");
|
|
||||||
|
|
||||||
attributes
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect::<Vec<(&Field, SystemParamFieldAttributes)>>();
|
|
||||||
let mut fields = Vec::new();
|
|
||||||
let mut field_indices = Vec::new();
|
|
||||||
let mut field_types = Vec::new();
|
|
||||||
let mut ignored_fields = Vec::new();
|
|
||||||
let mut ignored_field_types = Vec::new();
|
|
||||||
for (i, (field, attrs)) in field_attributes.iter().enumerate() {
|
|
||||||
if attrs.ignore {
|
|
||||||
ignored_fields.push(field.ident.as_ref().unwrap());
|
|
||||||
ignored_field_types.push(&field.ty);
|
|
||||||
} else {
|
|
||||||
fields.push(field.ident.as_ref().unwrap());
|
|
||||||
field_types.push(&field.ty);
|
|
||||||
field_indices.push(Index::from(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let generics = ast.generics;
|
|
||||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
|
||||||
|
|
||||||
let lifetimeless_generics: Vec<_> = generics
|
|
||||||
.params
|
|
||||||
.iter()
|
|
||||||
.filter(|g| matches!(g, GenericParam::Type(_)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut punctuated_generics = Punctuated::<_, Token![,]>::new();
|
|
||||||
punctuated_generics.extend(lifetimeless_generics.iter().map(|g| match g {
|
|
||||||
GenericParam::Type(g) => GenericParam::Type(TypeParam {
|
|
||||||
default: None,
|
|
||||||
..g.clone()
|
|
||||||
}),
|
|
||||||
_ => unreachable!(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let mut punctuated_generic_idents = Punctuated::<_, Token![,]>::new();
|
|
||||||
punctuated_generic_idents.extend(lifetimeless_generics.iter().map(|g| match g {
|
|
||||||
GenericParam::Type(g) => &g.ident,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let struct_name = &ast.ident;
|
|
||||||
let fetch_struct_visibility = &ast.vis;
|
|
||||||
|
|
||||||
TokenStream::from(quote! {
|
|
||||||
// We define the FetchState struct in an anonymous scope to avoid polluting the user namespace.
|
|
||||||
// The struct can still be accessed via SystemParam::Fetch, e.g. EventReaderState can be accessed via
|
|
||||||
// <EventReader<'static, 'static, T> as SystemParam>::Fetch
|
|
||||||
const _: () = {
|
|
||||||
impl #impl_generics #path::system::SystemParam for #struct_name #ty_generics #where_clause {
|
|
||||||
type Fetch = FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
#fetch_struct_visibility struct FetchState <TSystemParamState, #punctuated_generic_idents> {
|
|
||||||
state: TSystemParamState,
|
|
||||||
marker: std::marker::PhantomData<fn()->(#punctuated_generic_idents)>
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<TSystemParamState: #path::system::SystemParamState, #punctuated_generics> #path::system::SystemParamState for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {
|
|
||||||
fn init(world: &mut #path::world::World, system_meta: &mut #path::system::SystemMeta) -> Self {
|
|
||||||
Self {
|
|
||||||
state: TSystemParamState::init(world, system_meta),
|
|
||||||
marker: std::marker::PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_archetype(&mut self, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
|
|
||||||
self.state.new_archetype(archetype, system_meta)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply(&mut self, world: &mut #path::world::World) {
|
|
||||||
self.state.apply(world)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl #impl_generics #path::system::SystemParamFetch<'w, 's> for FetchState <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> #where_clause {
|
|
||||||
type Item = #struct_name #ty_generics;
|
|
||||||
unsafe fn get_param(
|
|
||||||
state: &'s mut Self,
|
|
||||||
system_meta: &#path::system::SystemMeta,
|
|
||||||
world: &'w #path::world::World,
|
|
||||||
change_tick: u32,
|
|
||||||
) -> Self::Item {
|
|
||||||
#struct_name {
|
|
||||||
#(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
|
|
||||||
#(#ignored_fields: <#ignored_field_types>::default(),)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Safety: The `ParamState` is `ReadOnlySystemParamFetch`, so this can only read from the `World`
|
|
||||||
unsafe impl<TSystemParamState: #path::system::SystemParamState + #path::system::ReadOnlySystemParamFetch, #punctuated_generics> #path::system::ReadOnlySystemParamFetch for FetchState <TSystemParamState, #punctuated_generic_idents> #where_clause {}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Implement `WorldQuery` to use a struct as a parameter in a query
|
|
||||||
#[proc_macro_derive(WorldQuery, attributes(world_query))]
|
|
||||||
pub fn derive_world_query(input: TokenStream) -> TokenStream {
|
|
||||||
let ast = parse_macro_input!(input as DeriveInput);
|
|
||||||
derive_world_query_impl(ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an impl of the `SystemLabel` trait.
|
|
||||||
///
|
|
||||||
/// This works only for unit structs, or enums with only unit variants.
|
|
||||||
/// You may force a struct or variant to behave as if it were fieldless with
|
|
||||||
/// `#[system_label(ignore_fields)]`.
|
|
||||||
#[proc_macro_derive(SystemLabel, attributes(system_label))]
|
|
||||||
pub fn derive_system_label(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let mut trait_path = azalea_ecs_path();
|
|
||||||
trait_path.segments.push(format_ident!("schedule").into());
|
|
||||||
trait_path
|
|
||||||
.segments
|
|
||||||
.push(format_ident!("SystemLabel").into());
|
|
||||||
derive_label(input, &trait_path, "system_label")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an impl of the `StageLabel` trait.
|
|
||||||
///
|
|
||||||
/// This works only for unit structs, or enums with only unit variants.
|
|
||||||
/// You may force a struct or variant to behave as if it were fieldless with
|
|
||||||
/// `#[stage_label(ignore_fields)]`.
|
|
||||||
#[proc_macro_derive(StageLabel, attributes(stage_label))]
|
|
||||||
pub fn derive_stage_label(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let mut trait_path = azalea_ecs_path();
|
|
||||||
trait_path.segments.push(format_ident!("schedule").into());
|
|
||||||
trait_path
|
|
||||||
.segments
|
|
||||||
.push(format_ident!("_BevyStageLabel").into());
|
|
||||||
derive_label(input, &trait_path, "stage_label")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates an impl of the `RunCriteriaLabel` trait.
|
|
||||||
///
|
|
||||||
/// This works only for unit structs, or enums with only unit variants.
|
|
||||||
/// You may force a struct or variant to behave as if it were fieldless with
|
|
||||||
/// `#[run_criteria_label(ignore_fields)]`.
|
|
||||||
#[proc_macro_derive(RunCriteriaLabel, attributes(run_criteria_label))]
|
|
||||||
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
|
|
||||||
let input = parse_macro_input!(input as DeriveInput);
|
|
||||||
let mut trait_path = azalea_ecs_path();
|
|
||||||
trait_path.segments.push(format_ident!("schedule").into());
|
|
||||||
trait_path
|
|
||||||
.segments
|
|
||||||
.push(format_ident!("RunCriteriaLabel").into());
|
|
||||||
derive_label(input, &trait_path, "run_criteria_label")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn azalea_ecs_path() -> syn::Path {
|
|
||||||
BevyManifest::default().get_path("azalea_ecs")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(Resource)]
|
|
||||||
pub fn derive_resource(input: TokenStream) -> TokenStream {
|
|
||||||
component::derive_resource(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[proc_macro_derive(Component, attributes(component))]
|
|
||||||
pub fn derive_component(input: TokenStream) -> TokenStream {
|
|
||||||
component::derive_component(input)
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use syn::DeriveInput;
|
|
||||||
|
|
||||||
use super::symbol::Symbol;
|
|
||||||
|
|
||||||
pub fn parse_attrs(ast: &DeriveInput, attr_name: Symbol) -> syn::Result<Vec<syn::NestedMeta>> {
|
|
||||||
let mut list = Vec::new();
|
|
||||||
for attr in ast.attrs.iter().filter(|a| a.path == attr_name) {
|
|
||||||
match attr.parse_meta()? {
|
|
||||||
syn::Meta::List(meta) => list.extend(meta.nested.into_iter()),
|
|
||||||
other => {
|
|
||||||
return Err(syn::Error::new_spanned(
|
|
||||||
other,
|
|
||||||
format!("expected #[{attr_name}(...)]"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_lit_str(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<&syn::LitStr> {
|
|
||||||
if let syn::Lit::Str(lit) = lit {
|
|
||||||
Ok(lit)
|
|
||||||
} else {
|
|
||||||
Err(syn::Error::new_spanned(
|
|
||||||
lit,
|
|
||||||
format!("expected {attr_name} attribute to be a string: `{attr_name} = \"...\"`"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_lit_bool(attr_name: Symbol, lit: &syn::Lit) -> syn::Result<bool> {
|
|
||||||
if let syn::Lit::Bool(lit) = lit {
|
|
||||||
Ok(lit.value())
|
|
||||||
} else {
|
|
||||||
Err(syn::Error::new_spanned(
|
|
||||||
lit,
|
|
||||||
format!(
|
|
||||||
"expected {attr_name} attribute to be a bool value, `true` or `false`: `{attr_name} = ...`"
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
extern crate proc_macro;
|
|
||||||
|
|
||||||
mod attrs;
|
|
||||||
mod shape;
|
|
||||||
mod symbol;
|
|
||||||
|
|
||||||
pub use attrs::*;
|
|
||||||
pub use shape::*;
|
|
||||||
pub use symbol::*;
|
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::{quote, quote_spanned};
|
|
||||||
use std::{env, path::PathBuf};
|
|
||||||
use syn::spanned::Spanned;
|
|
||||||
use toml::{map::Map, Value};
|
|
||||||
|
|
||||||
pub struct BevyManifest {
|
|
||||||
manifest: Map<String, Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for BevyManifest {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
manifest: env::var_os("CARGO_MANIFEST_DIR")
|
|
||||||
.map(PathBuf::from)
|
|
||||||
.map(|mut path| {
|
|
||||||
path.push("Cargo.toml");
|
|
||||||
let manifest = std::fs::read_to_string(path).unwrap();
|
|
||||||
toml::from_str(&manifest).unwrap()
|
|
||||||
})
|
|
||||||
.unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BevyManifest {
|
|
||||||
pub fn maybe_get_path(&self, name: &str) -> Option<syn::Path> {
|
|
||||||
const AZALEA: &str = "azalea";
|
|
||||||
const AZALEA_ECS: &str = "azalea_ecs";
|
|
||||||
const BEVY_ECS: &str = "bevy_ecs";
|
|
||||||
const BEVY: &str = "bevy";
|
|
||||||
|
|
||||||
fn dep_package(dep: &Value) -> Option<&str> {
|
|
||||||
if dep.as_str().is_some() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
dep.as_table()
|
|
||||||
.unwrap()
|
|
||||||
.get("package")
|
|
||||||
.map(|name| name.as_str().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let find_in_deps = |deps: &Map<String, Value>| -> Option<syn::Path> {
|
|
||||||
let package = if let Some(dep) = deps.get(name) {
|
|
||||||
return Some(Self::parse_str(dep_package(dep).unwrap_or(name)));
|
|
||||||
} else if let Some(dep) = deps.get(AZALEA) {
|
|
||||||
dep_package(dep).unwrap_or(AZALEA)
|
|
||||||
} else if let Some(dep) = deps.get(AZALEA_ECS) {
|
|
||||||
dep_package(dep).unwrap_or(AZALEA_ECS)
|
|
||||||
} else if let Some(dep) = deps.get(BEVY_ECS) {
|
|
||||||
dep_package(dep).unwrap_or(BEVY_ECS)
|
|
||||||
} else if let Some(dep) = deps.get(BEVY) {
|
|
||||||
dep_package(dep).unwrap_or(BEVY)
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut path = Self::parse_str::<syn::Path>(package);
|
|
||||||
if let Some(module) = name.strip_prefix("azalea_") {
|
|
||||||
path.segments.push(Self::parse_str(module));
|
|
||||||
}
|
|
||||||
Some(path)
|
|
||||||
};
|
|
||||||
|
|
||||||
let deps = self
|
|
||||||
.manifest
|
|
||||||
.get("dependencies")
|
|
||||||
.map(|deps| deps.as_table().unwrap());
|
|
||||||
let deps_dev = self
|
|
||||||
.manifest
|
|
||||||
.get("dev-dependencies")
|
|
||||||
.map(|deps| deps.as_table().unwrap());
|
|
||||||
|
|
||||||
deps.and_then(find_in_deps)
|
|
||||||
.or_else(|| deps_dev.and_then(find_in_deps))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the path for the crate with the given name.
|
|
||||||
///
|
|
||||||
/// This is a convenience method for constructing a [manifest] and
|
|
||||||
/// calling the [`get_path`] method.
|
|
||||||
///
|
|
||||||
/// This method should only be used where you just need the path and can't
|
|
||||||
/// cache the [manifest]. If caching is possible, it's recommended to create
|
|
||||||
/// the [manifest] yourself and use the [`get_path`] method.
|
|
||||||
///
|
|
||||||
/// [`get_path`]: Self::get_path
|
|
||||||
/// [manifest]: Self
|
|
||||||
pub fn get_path_direct(name: &str) -> syn::Path {
|
|
||||||
Self::default().get_path(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_path(&self, name: &str) -> syn::Path {
|
|
||||||
self.maybe_get_path(name)
|
|
||||||
.unwrap_or_else(|| Self::parse_str(name))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse_str<T: syn::parse::Parse>(path: &str) -> T {
|
|
||||||
syn::parse(path.parse::<TokenStream>().unwrap()).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Derive a label trait
|
|
||||||
///
|
|
||||||
/// # Args
|
|
||||||
///
|
|
||||||
/// - `input`: The [`syn::DeriveInput`] for struct that is deriving the label
|
|
||||||
/// trait
|
|
||||||
/// - `trait_path`: The path [`syn::Path`] to the label trait
|
|
||||||
pub fn derive_label(
|
|
||||||
input: syn::DeriveInput,
|
|
||||||
trait_path: &syn::Path,
|
|
||||||
attr_name: &str,
|
|
||||||
) -> TokenStream {
|
|
||||||
// return true if the variant specified is an `ignore_fields` attribute
|
|
||||||
fn is_ignore(attr: &syn::Attribute, attr_name: &str) -> bool {
|
|
||||||
if attr.path.get_ident().as_ref().unwrap() != &attr_name {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
syn::custom_keyword!(ignore_fields);
|
|
||||||
attr.parse_args_with(|input: syn::parse::ParseStream| {
|
|
||||||
let ignore = input.parse::<Option<ignore_fields>>()?.is_some();
|
|
||||||
Ok(ignore)
|
|
||||||
})
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
let ident = input.ident.clone();
|
|
||||||
|
|
||||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
||||||
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
|
|
||||||
where_token: Default::default(),
|
|
||||||
predicates: Default::default(),
|
|
||||||
});
|
|
||||||
where_clause
|
|
||||||
.predicates
|
|
||||||
.push(syn::parse2(quote! { Self: 'static }).unwrap());
|
|
||||||
|
|
||||||
let as_str = match input.data {
|
|
||||||
syn::Data::Struct(d) => {
|
|
||||||
// see if the user tried to ignore fields incorrectly
|
|
||||||
if let Some(attr) = d
|
|
||||||
.fields
|
|
||||||
.iter()
|
|
||||||
.flat_map(|f| &f.attrs)
|
|
||||||
.find(|a| is_ignore(a, attr_name))
|
|
||||||
{
|
|
||||||
let err_msg = format!("`#[{attr_name}(ignore_fields)]` cannot be applied to fields individually: add it to the struct declaration");
|
|
||||||
return quote_spanned! {
|
|
||||||
attr.span() => compile_error!(#err_msg);
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
// Structs must either be fieldless, or explicitly ignore the fields.
|
|
||||||
let ignore_fields = input.attrs.iter().any(|a| is_ignore(a, attr_name));
|
|
||||||
if matches!(d.fields, syn::Fields::Unit) || ignore_fields {
|
|
||||||
let lit = ident.to_string();
|
|
||||||
quote! { #lit }
|
|
||||||
} else {
|
|
||||||
let err_msg = format!("Labels cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
|
|
||||||
return quote_spanned! {
|
|
||||||
d.fields.span() => compile_error!(#err_msg);
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syn::Data::Enum(d) => {
|
|
||||||
// check if the user put #[label(ignore_fields)] in the wrong place
|
|
||||||
if let Some(attr) = input.attrs.iter().find(|a| is_ignore(a, attr_name)) {
|
|
||||||
let err_msg = format!("`#[{attr_name}(ignore_fields)]` can only be applied to enum variants or struct declarations");
|
|
||||||
return quote_spanned! {
|
|
||||||
attr.span() => compile_error!(#err_msg);
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
let arms = d.variants.iter().map(|v| {
|
|
||||||
// Variants must either be fieldless, or explicitly ignore the fields.
|
|
||||||
let ignore_fields = v.attrs.iter().any(|a| is_ignore(a, attr_name));
|
|
||||||
if matches!(v.fields, syn::Fields::Unit) | ignore_fields {
|
|
||||||
let mut path = syn::Path::from(ident.clone());
|
|
||||||
path.segments.push(v.ident.clone().into());
|
|
||||||
let lit = format!("{ident}::{}", v.ident.clone());
|
|
||||||
quote! { #path { .. } => #lit }
|
|
||||||
} else {
|
|
||||||
let err_msg = format!("Label variants cannot contain data, unless explicitly ignored with `#[{attr_name}(ignore_fields)]`");
|
|
||||||
quote_spanned! {
|
|
||||||
v.fields.span() => _ => { compile_error!(#err_msg); }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
quote! {
|
|
||||||
match self {
|
|
||||||
#(#arms),*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syn::Data::Union(_) => {
|
|
||||||
return quote_spanned! {
|
|
||||||
input.span() => compile_error!("Unions cannot be used as labels.");
|
|
||||||
}
|
|
||||||
.into();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
(quote! {
|
|
||||||
impl #impl_generics #trait_path for #ident #ty_generics #where_clause {
|
|
||||||
fn as_str(&self) -> &'static str {
|
|
||||||
#as_str
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
use proc_macro::Span;
|
|
||||||
use syn::{Data, DataStruct, Error, Fields, FieldsNamed};
|
|
||||||
|
|
||||||
/// Get the fields of a data structure if that structure is a struct with named
|
|
||||||
/// fields; otherwise, return a compile error that points to the site of the
|
|
||||||
/// macro invocation.
|
|
||||||
pub fn get_named_struct_fields(data: &syn::Data) -> syn::Result<&FieldsNamed> {
|
|
||||||
match data {
|
|
||||||
Data::Struct(DataStruct {
|
|
||||||
fields: Fields::Named(fields),
|
|
||||||
..
|
|
||||||
}) => Ok(fields),
|
|
||||||
_ => Err(Error::new(
|
|
||||||
// This deliberately points to the call site rather than the structure
|
|
||||||
// body; marking the entire body as the source of the error makes it
|
|
||||||
// impossible to figure out which `derive` has a problem.
|
|
||||||
Span::call_site().into(),
|
|
||||||
"Only structs with named fields are supported",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
use std::fmt::{self, Display};
|
|
||||||
use syn::{Ident, Path};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Symbol(pub &'static str);
|
|
||||||
|
|
||||||
impl PartialEq<Symbol> for Ident {
|
|
||||||
fn eq(&self, word: &Symbol) -> bool {
|
|
||||||
self == word.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PartialEq<Symbol> for &'a Ident {
|
|
||||||
fn eq(&self, word: &Symbol) -> bool {
|
|
||||||
*self == word.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq<Symbol> for Path {
|
|
||||||
fn eq(&self, word: &Symbol) -> bool {
|
|
||||||
self.is_ident(word.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> PartialEq<Symbol> for &'a Path {
|
|
||||||
fn eq(&self, word: &Symbol) -> bool {
|
|
||||||
self.is_ident(word.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Symbol {
|
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
formatter.write_str(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,157 +0,0 @@
|
||||||
#![feature(trait_alias)]
|
|
||||||
|
|
||||||
//! Re-export important parts of [`bevy_ecs`] and [`bevy_app`] and make them
|
|
||||||
//! more compatible with Azalea.
|
|
||||||
//!
|
|
||||||
//! This is completely compatible with `bevy_ecs`, so it won't cause issues if
|
|
||||||
//! you use plugins meant for Bevy.
|
|
||||||
//!
|
|
||||||
//! Changes:
|
|
||||||
//! - Add [`TickPlugin`], [`TickStage`] and [`AppTickExt`] (which adds
|
|
||||||
//! `app.add_tick_system` and `app.add_tick_system_set`).
|
|
||||||
//! - Change the macros to use azalea/azalea_ecs instead of bevy/bevy_ecs
|
|
||||||
//! - Rename `world::World` to [`ecs::Ecs`]
|
|
||||||
//! - Re-export `bevy_app` in the [`app`] module.
|
|
||||||
//!
|
|
||||||
//! [`bevy_ecs`]: https://docs.rs/bevy_ecs
|
|
||||||
//! [`bevy_app`]: https://docs.rs/bevy_app
|
|
||||||
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
pub mod ecs {
|
|
||||||
pub use bevy_ecs::world::World as Ecs;
|
|
||||||
pub use bevy_ecs::world::{EntityMut, EntityRef, Mut};
|
|
||||||
}
|
|
||||||
pub mod component {
|
|
||||||
pub use azalea_ecs_macros::Component;
|
|
||||||
pub use bevy_ecs::component::{ComponentId, ComponentStorage, Components, TableStorage};
|
|
||||||
|
|
||||||
// we do this because re-exporting Component would re-export the macro as well,
|
|
||||||
// which is bad (since we have our own Component macro)
|
|
||||||
// instead, we have to do this so Component is a trait alias and the original
|
|
||||||
// impl-able trait is still available as _BevyComponent
|
|
||||||
pub trait Component = bevy_ecs::component::Component;
|
|
||||||
pub use bevy_ecs::component::Component as _BevyComponent;
|
|
||||||
}
|
|
||||||
pub mod bundle {
|
|
||||||
pub use azalea_ecs_macros::Bundle;
|
|
||||||
pub trait Bundle = bevy_ecs::bundle::Bundle;
|
|
||||||
pub use bevy_ecs::bundle::Bundle as _BevyBundle;
|
|
||||||
}
|
|
||||||
pub mod system {
|
|
||||||
pub use azalea_ecs_macros::Resource;
|
|
||||||
pub use bevy_ecs::system::{
|
|
||||||
Command, Commands, EntityCommands, Query, Res, ResMut, SystemState,
|
|
||||||
};
|
|
||||||
pub trait Resource = bevy_ecs::system::Resource;
|
|
||||||
pub use bevy_ecs::system::Resource as _BevyResource;
|
|
||||||
}
|
|
||||||
pub mod schedule {
|
|
||||||
pub use azalea_ecs_macros::StageLabel;
|
|
||||||
pub use bevy_ecs::schedule::{
|
|
||||||
IntoRunCriteria, IntoSystemDescriptor, ReportExecutionOrderAmbiguities, Schedule, Stage,
|
|
||||||
SystemSet, SystemStage,
|
|
||||||
};
|
|
||||||
pub trait StageLabel = bevy_ecs::schedule::StageLabel;
|
|
||||||
pub use bevy_ecs::schedule::StageLabel as _BevyStageLabel;
|
|
||||||
}
|
|
||||||
pub use bevy_app as app;
|
|
||||||
pub use bevy_ecs::{entity, event, ptr, query, storage};
|
|
||||||
|
|
||||||
use app::{App, CoreStage, Plugin};
|
|
||||||
use bevy_ecs::schedule::*;
|
|
||||||
use ecs::Ecs;
|
|
||||||
|
|
||||||
pub struct TickPlugin {
|
|
||||||
/// How often a tick should happen. 50 milliseconds by default. Set to 0 to
|
|
||||||
/// tick every update.
|
|
||||||
pub tick_interval: Duration,
|
|
||||||
}
|
|
||||||
impl Plugin for TickPlugin {
|
|
||||||
fn build(&self, app: &mut App) {
|
|
||||||
app.add_stage_before(
|
|
||||||
CoreStage::Update,
|
|
||||||
TickLabel,
|
|
||||||
TickStage {
|
|
||||||
interval: self.tick_interval,
|
|
||||||
next_tick: Instant::now(),
|
|
||||||
stage: Box::new(SystemStage::parallel()),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for TickPlugin {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
tick_interval: Duration::from_millis(50),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(StageLabel)]
|
|
||||||
struct TickLabel;
|
|
||||||
|
|
||||||
/// A [`Stage`] that runs every 50 milliseconds.
|
|
||||||
pub struct TickStage {
|
|
||||||
pub interval: Duration,
|
|
||||||
pub next_tick: Instant,
|
|
||||||
stage: Box<dyn Stage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stage for TickStage {
|
|
||||||
fn run(&mut self, ecs: &mut Ecs) {
|
|
||||||
// if the interval is 0, that means it runs every tick
|
|
||||||
if self.interval.is_zero() {
|
|
||||||
self.stage.run(ecs);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// keep calling run until it's caught up
|
|
||||||
// TODO: Minecraft bursts up to 10 ticks and then skips, we should too (but
|
|
||||||
// check the source so we do it right)
|
|
||||||
while Instant::now() > self.next_tick {
|
|
||||||
self.next_tick += self.interval;
|
|
||||||
self.stage.run(ecs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AppTickExt {
|
|
||||||
fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App;
|
|
||||||
fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppTickExt for App {
|
|
||||||
/// Adds a set of ECS systems that will run every 50 milliseconds.
|
|
||||||
///
|
|
||||||
/// Note that you should NOT have `EventReader`s in tick systems, as this
|
|
||||||
/// will make them sometimes be missed.
|
|
||||||
fn add_tick_system_set(&mut self, system_set: SystemSet) -> &mut App {
|
|
||||||
let tick_stage = self
|
|
||||||
.schedule
|
|
||||||
.get_stage_mut::<TickStage>(TickLabel)
|
|
||||||
.expect("Tick Stage not found");
|
|
||||||
let stage = tick_stage
|
|
||||||
.stage
|
|
||||||
.downcast_mut::<SystemStage>()
|
|
||||||
.expect("Fixed Timestep sub-stage is not a SystemStage");
|
|
||||||
stage.add_system_set(system_set);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new ECS system that will run every 50 milliseconds.
|
|
||||||
///
|
|
||||||
/// Note that you should NOT have `EventReader`s in tick systems, as this
|
|
||||||
/// will make them sometimes be missed.
|
|
||||||
fn add_tick_system<Params>(&mut self, system: impl IntoSystemDescriptor<Params>) -> &mut App {
|
|
||||||
let tick_stage = self
|
|
||||||
.schedule
|
|
||||||
.get_stage_mut::<TickStage>(TickLabel)
|
|
||||||
.expect("Tick Stage not found");
|
|
||||||
let stage = tick_stage
|
|
||||||
.stage
|
|
||||||
.downcast_mut::<SystemStage>()
|
|
||||||
.expect("Fixed Timestep sub-stage is not a SystemStage");
|
|
||||||
stage.add_system(system);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,13 +9,15 @@ version = "0.6.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
azalea-block = {path = "../azalea-block", version = "^0.6.0"}
|
azalea-block = { path = "../azalea-block", version = "^0.6.0" }
|
||||||
azalea-core = {path = "../azalea-core", version = "^0.6.0"}
|
azalea-core = { path = "../azalea-core", version = "^0.6.0" }
|
||||||
azalea-ecs = {version = "0.6.0", path = "../azalea-ecs"}
|
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
|
||||||
azalea-registry = {path = "../azalea-registry", version = "^0.6.0"}
|
azalea-world = { path = "../azalea-world", version = "^0.6.0" }
|
||||||
azalea-world = {path = "../azalea-world", version = "^0.6.0"}
|
bevy_app = "0.10.0"
|
||||||
|
bevy_ecs = "0.10.0"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.16.0"
|
||||||
parking_lot = "^0.12.1"
|
parking_lot = "^0.12.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
bevy_time = "0.10.0"
|
||||||
uuid = "^1.1.2"
|
uuid = "^1.1.2"
|
||||||
|
|
|
@ -7,7 +7,7 @@ mod world_collisions;
|
||||||
use azalea_core::{Axis, Vec3, AABB, EPSILON};
|
use azalea_core::{Axis, Vec3, AABB, EPSILON};
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{self},
|
entity::{self},
|
||||||
MoveEntityError, World,
|
Instance, MoveEntityError,
|
||||||
};
|
};
|
||||||
pub use blocks::BlockWithShape;
|
pub use blocks::BlockWithShape;
|
||||||
pub use discrete_voxel_shape::*;
|
pub use discrete_voxel_shape::*;
|
||||||
|
@ -52,7 +52,7 @@ pub enum MoverType {
|
||||||
|
|
||||||
// return var4;
|
// return var4;
|
||||||
// }
|
// }
|
||||||
fn collide(movement: &Vec3, world: &World, physics: &entity::Physics) -> Vec3 {
|
fn collide(movement: &Vec3, world: &Instance, physics: &entity::Physics) -> Vec3 {
|
||||||
let entity_bounding_box = physics.bounding_box;
|
let entity_bounding_box = physics.bounding_box;
|
||||||
// TODO: get_entity_collisions
|
// TODO: get_entity_collisions
|
||||||
// let entity_collisions = world.get_entity_collisions(self,
|
// let entity_collisions = world.get_entity_collisions(self,
|
||||||
|
@ -73,7 +73,7 @@ fn collide(movement: &Vec3, world: &World, physics: &entity::Physics) -> Vec3 {
|
||||||
pub fn move_colliding(
|
pub fn move_colliding(
|
||||||
_mover_type: &MoverType,
|
_mover_type: &MoverType,
|
||||||
movement: &Vec3,
|
movement: &Vec3,
|
||||||
world: &World,
|
world: &Instance,
|
||||||
position: &mut entity::Position,
|
position: &mut entity::Position,
|
||||||
physics: &mut entity::Physics,
|
physics: &mut entity::Physics,
|
||||||
) -> Result<(), MoveEntityError> {
|
) -> Result<(), MoveEntityError> {
|
||||||
|
@ -186,7 +186,7 @@ pub fn move_colliding(
|
||||||
fn collide_bounding_box(
|
fn collide_bounding_box(
|
||||||
movement: &Vec3,
|
movement: &Vec3,
|
||||||
entity_bounding_box: &AABB,
|
entity_bounding_box: &AABB,
|
||||||
world: &World,
|
world: &Instance,
|
||||||
entity_collisions: Vec<VoxelShape>,
|
entity_collisions: Vec<VoxelShape>,
|
||||||
) -> Vec3 {
|
) -> Vec3 {
|
||||||
let mut collision_boxes: Vec<VoxelShape> = Vec::with_capacity(entity_collisions.len() + 1);
|
let mut collision_boxes: Vec<VoxelShape> = Vec::with_capacity(entity_collisions.len() + 1);
|
||||||
|
|
|
@ -2,16 +2,16 @@ use super::Shapes;
|
||||||
use crate::collision::{BlockWithShape, VoxelShape, AABB};
|
use crate::collision::{BlockWithShape, VoxelShape, AABB};
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
|
use azalea_core::{ChunkPos, ChunkSectionPos, Cursor3d, CursorIterationType, EPSILON};
|
||||||
use azalea_world::{Chunk, World};
|
use azalea_world::{Chunk, Instance};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub fn get_block_collisions(world: &World, aabb: AABB) -> BlockCollisions<'_> {
|
pub fn get_block_collisions(world: &Instance, aabb: AABB) -> BlockCollisions<'_> {
|
||||||
BlockCollisions::new(world, aabb)
|
BlockCollisions::new(world, aabb)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BlockCollisions<'a> {
|
pub struct BlockCollisions<'a> {
|
||||||
pub world: &'a World,
|
pub world: &'a Instance,
|
||||||
pub aabb: AABB,
|
pub aabb: AABB,
|
||||||
pub entity_shape: VoxelShape,
|
pub entity_shape: VoxelShape,
|
||||||
pub cursor: Cursor3d,
|
pub cursor: Cursor3d,
|
||||||
|
@ -19,7 +19,7 @@ pub struct BlockCollisions<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BlockCollisions<'a> {
|
impl<'a> BlockCollisions<'a> {
|
||||||
pub fn new(world: &'a World, aabb: AABB) -> Self {
|
pub fn new(world: &'a Instance, aabb: AABB) -> Self {
|
||||||
let origin_x = (aabb.min_x - EPSILON).floor() as i32 - 1;
|
let origin_x = (aabb.min_x - EPSILON).floor() as i32 - 1;
|
||||||
let origin_y = (aabb.min_y - EPSILON).floor() as i32 - 1;
|
let origin_y = (aabb.min_y - EPSILON).floor() as i32 - 1;
|
||||||
let origin_z = (aabb.min_z - EPSILON).floor() as i32 - 1;
|
let origin_z = (aabb.min_z - EPSILON).floor() as i32 - 1;
|
||||||
|
|
|
@ -5,37 +5,38 @@ pub mod collision;
|
||||||
|
|
||||||
use azalea_block::{Block, BlockState};
|
use azalea_block::{Block, BlockState};
|
||||||
use azalea_core::{BlockPos, Vec3};
|
use azalea_core::{BlockPos, Vec3};
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
entity::Entity,
|
|
||||||
event::{EventReader, EventWriter},
|
|
||||||
query::With,
|
|
||||||
schedule::IntoSystemDescriptor,
|
|
||||||
system::{Query, Res},
|
|
||||||
AppTickExt,
|
|
||||||
};
|
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{
|
entity::{
|
||||||
metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
|
metadata::Sprinting, move_relative, Attributes, Jumping, Local, Physics, Position,
|
||||||
WorldName,
|
WorldName,
|
||||||
},
|
},
|
||||||
World, WorldContainer,
|
Instance, WorldContainer,
|
||||||
|
};
|
||||||
|
use bevy_app::{App, CoreSchedule, IntoSystemAppConfigs, Plugin};
|
||||||
|
use bevy_ecs::{
|
||||||
|
entity::Entity,
|
||||||
|
event::{EventReader, EventWriter},
|
||||||
|
query::With,
|
||||||
|
schedule::{IntoSystemConfig, IntoSystemConfigs, SystemSet},
|
||||||
|
system::{Query, Res},
|
||||||
};
|
};
|
||||||
use collision::{move_colliding, MoverType};
|
use collision::{move_colliding, MoverType};
|
||||||
|
|
||||||
|
/// A Bevy [`SystemSet`] for running physics that makes entities do things.
|
||||||
|
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub struct PhysicsSet;
|
||||||
|
|
||||||
pub struct PhysicsPlugin;
|
pub struct PhysicsPlugin;
|
||||||
impl Plugin for PhysicsPlugin {
|
impl Plugin for PhysicsPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<ForceJumpEvent>()
|
app.add_event::<ForceJumpEvent>()
|
||||||
.add_system(
|
.add_system(force_jump_listener.before(azalea_world::entity::update_bounding_box))
|
||||||
force_jump_listener
|
.add_systems(
|
||||||
.label("force_jump_listener")
|
(ai_step, travel)
|
||||||
.after("walk_listener")
|
.chain()
|
||||||
.after("sprint_listener")
|
.in_set(PhysicsSet)
|
||||||
.before(azalea_world::entity::update_bounding_box),
|
.in_schedule(CoreSchedule::FixedUpdate),
|
||||||
)
|
);
|
||||||
.add_tick_system(travel.label("travel").after(ai_step))
|
|
||||||
.add_tick_system(ai_step.label("ai_step"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +157,7 @@ pub fn ai_step(
|
||||||
/// Jump even if we aren't on the ground.
|
/// Jump even if we aren't on the ground.
|
||||||
pub struct ForceJumpEvent(pub Entity);
|
pub struct ForceJumpEvent(pub Entity);
|
||||||
|
|
||||||
fn force_jump_listener(
|
pub fn force_jump_listener(
|
||||||
mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
|
mut query: Query<(&mut Physics, &Position, &Sprinting, &WorldName)>,
|
||||||
world_container: Res<WorldContainer>,
|
world_container: Res<WorldContainer>,
|
||||||
mut events: EventReader<ForceJumpEvent>,
|
mut events: EventReader<ForceJumpEvent>,
|
||||||
|
@ -201,7 +202,7 @@ fn get_block_pos_below_that_affects_movement(position: &Position) -> BlockPos {
|
||||||
|
|
||||||
fn handle_relative_friction_and_calculate_movement(
|
fn handle_relative_friction_and_calculate_movement(
|
||||||
block_friction: f32,
|
block_friction: f32,
|
||||||
world: &World,
|
world: &Instance,
|
||||||
physics: &mut Physics,
|
physics: &mut Physics,
|
||||||
position: &mut Position,
|
position: &mut Position,
|
||||||
attributes: &Attributes,
|
attributes: &Attributes,
|
||||||
|
@ -251,7 +252,7 @@ fn get_friction_influenced_speed(physics: &Physics, attributes: &Attributes, fri
|
||||||
|
|
||||||
/// Returns the what the entity's jump should be multiplied by based on the
|
/// Returns the what the entity's jump should be multiplied by based on the
|
||||||
/// block they're standing on.
|
/// block they're standing on.
|
||||||
fn block_jump_factor(world: &World, position: &Position) -> f32 {
|
fn block_jump_factor(world: &Instance, position: &Position) -> f32 {
|
||||||
let block_at_pos = world.chunks.get_block_state(&position.into());
|
let block_at_pos = world.chunks.get_block_state(&position.into());
|
||||||
let block_below = world
|
let block_below = world
|
||||||
.chunks
|
.chunks
|
||||||
|
@ -279,7 +280,7 @@ fn block_jump_factor(world: &World, position: &Position) -> f32 {
|
||||||
// public double getJumpBoostPower() {
|
// public double getJumpBoostPower() {
|
||||||
// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F *
|
// return this.hasEffect(MobEffects.JUMP) ? (double)(0.1F *
|
||||||
// (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D; }
|
// (float)(this.getEffect(MobEffects.JUMP).getAmplifier() + 1)) : 0.0D; }
|
||||||
fn jump_power(world: &World, position: &Position) -> f32 {
|
fn jump_power(world: &Instance, position: &Position) -> f32 {
|
||||||
0.42 * block_jump_factor(world, position)
|
0.42 * block_jump_factor(world, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,22 +304,21 @@ mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use azalea_core::{ChunkPos, ResourceLocation};
|
use azalea_core::{ChunkPos, ResourceLocation};
|
||||||
use azalea_ecs::{app::App, TickPlugin};
|
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
entity::{EntityBundle, EntityPlugin, MinecraftEntityId},
|
entity::{EntityBundle, EntityPlugin, MinecraftEntityId},
|
||||||
Chunk, PartialWorld,
|
Chunk, PartialWorld,
|
||||||
};
|
};
|
||||||
|
use bevy_app::App;
|
||||||
|
use bevy_time::fixed_timestep::FixedTime;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
/// You need an app to spawn entities in the world and do updates.
|
/// You need an app to spawn entities in the world and do updates.
|
||||||
fn make_test_app() -> App {
|
fn make_test_app() -> App {
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
app.add_plugin(TickPlugin {
|
app.add_plugin(PhysicsPlugin)
|
||||||
tick_interval: Duration::ZERO,
|
.add_plugin(EntityPlugin)
|
||||||
})
|
.insert_resource(FixedTime::new(Duration::from_millis(50)))
|
||||||
.add_plugin(PhysicsPlugin)
|
.init_resource::<WorldContainer>();
|
||||||
.add_plugin(EntityPlugin)
|
|
||||||
.init_resource::<WorldContainer>();
|
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,6 +353,7 @@ mod tests {
|
||||||
// y should start at 70
|
// y should start at 70
|
||||||
assert_eq!(entity_pos.y, 70.);
|
assert_eq!(entity_pos.y, 70.);
|
||||||
}
|
}
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
{
|
{
|
||||||
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -361,6 +362,7 @@ mod tests {
|
||||||
let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
|
let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
|
||||||
assert!(entity_physics.delta.y < 0.);
|
assert!(entity_physics.delta.y < 0.);
|
||||||
}
|
}
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
{
|
{
|
||||||
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -413,6 +415,7 @@ mod tests {
|
||||||
block_state.is_some(),
|
block_state.is_some(),
|
||||||
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
|
"Block state should exist, if this fails that means the chunk wasn't loaded and the block didn't get placed"
|
||||||
);
|
);
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
{
|
{
|
||||||
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -421,6 +424,7 @@ mod tests {
|
||||||
let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
|
let entity_physics = app.world.get::<Physics>(entity).unwrap().clone();
|
||||||
assert!(entity_physics.delta.y < 0.);
|
assert!(entity_physics.delta.y < 0.);
|
||||||
}
|
}
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
{
|
{
|
||||||
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
let entity_pos = *app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -476,6 +480,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
// do a few steps so we fall on the slab
|
// do a few steps so we fall on the slab
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
}
|
}
|
||||||
let entity_pos = app.world.get::<Position>(entity).unwrap();
|
let entity_pos = app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -528,6 +533,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
// do a few steps so we fall on the slab
|
// do a few steps so we fall on the slab
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
}
|
}
|
||||||
let entity_pos = app.world.get::<Position>(entity).unwrap();
|
let entity_pos = app.world.get::<Position>(entity).unwrap();
|
||||||
|
@ -584,6 +590,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
// do a few steps so we fall on the wall
|
// do a few steps so we fall on the wall
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,6 +652,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
// do a few steps so we fall on the wall
|
// do a few steps so we fall on the wall
|
||||||
for _ in 0..20 {
|
for _ in 0..20 {
|
||||||
|
app.world.run_schedule(CoreSchedule::FixedUpdate);
|
||||||
app.update();
|
app.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,32 +9,39 @@ version = "0.6.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
async-compression = {version = "^0.3.8", features = ["tokio", "zlib"], optional = true}
|
async-compression = { version = "^0.3.8", features = [
|
||||||
|
"tokio",
|
||||||
|
"zlib",
|
||||||
|
], optional = true }
|
||||||
async-recursion = "1.0.0"
|
async-recursion = "1.0.0"
|
||||||
azalea-auth = {path = "../azalea-auth", version = "^0.6.0" }
|
azalea-auth = { path = "../azalea-auth", version = "^0.6.0" }
|
||||||
azalea-block = {path = "../azalea-block", default-features = false, version = "^0.6.0" }
|
azalea-block = { path = "../azalea-block", default-features = false, version = "^0.6.0" }
|
||||||
azalea-brigadier = {path = "../azalea-brigadier", version = "^0.6.0", features = ["azalea-buf"]}
|
azalea-brigadier = { path = "../azalea-brigadier", version = "^0.6.0", features = [
|
||||||
azalea-buf = {path = "../azalea-buf", version = "^0.6.0" }
|
"azalea-buf",
|
||||||
azalea-chat = {path = "../azalea-chat", version = "^0.6.0" }
|
] }
|
||||||
azalea-core = {path = "../azalea-core", optional = true, version = "^0.6.0" }
|
azalea-buf = { path = "../azalea-buf", version = "^0.6.0" }
|
||||||
azalea-crypto = {path = "../azalea-crypto", version = "^0.6.0" }
|
azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
|
||||||
azalea-nbt = {path = "../azalea-nbt", version = "^0.6.0" }
|
azalea-core = { path = "../azalea-core", optional = true, version = "^0.6.0" }
|
||||||
azalea-protocol-macros = {path = "./azalea-protocol-macros", version = "^0.6.0" }
|
azalea-crypto = { path = "../azalea-crypto", version = "^0.6.0" }
|
||||||
azalea-registry = {path = "../azalea-registry", version = "^0.6.0" }
|
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
|
||||||
azalea-world = {path = "../azalea-world", version = "^0.6.0" }
|
azalea-protocol-macros = { path = "./azalea-protocol-macros", version = "^0.6.0" }
|
||||||
bevy_ecs = { version = "0.9.1", default-features = false }
|
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
|
||||||
|
azalea-world = { path = "../azalea-world", version = "^0.6.0" }
|
||||||
|
bevy_ecs = { version = "0.10.0", default-features = false }
|
||||||
byteorder = "^1.4.3"
|
byteorder = "^1.4.3"
|
||||||
bytes = "^1.1.0"
|
bytes = "^1.1.0"
|
||||||
flate2 = "1.0.23"
|
flate2 = "1.0.23"
|
||||||
futures = "0.3.24"
|
futures = "0.3.24"
|
||||||
futures-util = "0.3.24"
|
futures-util = "0.3.24"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
serde = {version = "1.0.130", features = ["serde_derive"]}
|
serde = { version = "1.0.130", features = ["serde_derive"] }
|
||||||
serde_json = "^1.0.72"
|
serde_json = "^1.0.72"
|
||||||
thiserror = "1.0.37"
|
thiserror = "1.0.37"
|
||||||
tokio = {version = "^1.24.2", features = ["io-util", "net", "macros"]}
|
tokio = { version = "^1.24.2", features = ["io-util", "net", "macros"] }
|
||||||
tokio-util = {version = "0.7.4", features = ["codec"]}
|
tokio-util = { version = "0.7.4", features = ["codec"] }
|
||||||
trust-dns-resolver = {version = "^0.22.0", default-features = false, features = ["tokio-runtime"]}
|
trust-dns-resolver = { version = "^0.22.0", default-features = false, features = [
|
||||||
|
"tokio-runtime",
|
||||||
|
] }
|
||||||
uuid = "1.1.2"
|
uuid = "1.1.2"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
|
|
@ -15,9 +15,10 @@ azalea-chat = { path = "../azalea-chat", version = "^0.6.0" }
|
||||||
azalea-core = { path = "../azalea-core", version = "^0.6.0", features = [
|
azalea-core = { path = "../azalea-core", version = "^0.6.0", features = [
|
||||||
"bevy_ecs",
|
"bevy_ecs",
|
||||||
] }
|
] }
|
||||||
azalea-ecs = { version = "0.6.0", path = "../azalea-ecs" }
|
|
||||||
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
|
azalea-nbt = { path = "../azalea-nbt", version = "^0.6.0" }
|
||||||
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
|
azalea-registry = { path = "../azalea-registry", version = "^0.6.0" }
|
||||||
|
bevy_app = "0.10.0"
|
||||||
|
bevy_ecs = "0.10.0"
|
||||||
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
|
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
|
||||||
enum-as-inner = "0.5.1"
|
enum-as-inner = "0.5.1"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use azalea_core::ResourceLocation;
|
use azalea_core::ResourceLocation;
|
||||||
use azalea_ecs::system::Resource;
|
use bevy_ecs::system::Resource;
|
||||||
use log::error;
|
use log::error;
|
||||||
use nohash_hasher::IntMap;
|
use nohash_hasher::IntMap;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
@ -8,7 +8,7 @@ use std::{
|
||||||
sync::{Arc, Weak},
|
sync::{Arc, Weak},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{ChunkStorage, World};
|
use crate::{ChunkStorage, Instance};
|
||||||
|
|
||||||
/// A container of [`World`]s. Worlds are stored as a Weak pointer here, so
|
/// A container of [`World`]s. Worlds are stored as a Weak pointer here, so
|
||||||
/// if no clients are using a world it will be forgotten.
|
/// if no clients are using a world it will be forgotten.
|
||||||
|
@ -26,7 +26,7 @@ pub struct WorldContainer {
|
||||||
// telling them apart. We hope most servers are nice and don't do that though. It's only an
|
// telling them apart. We hope most servers are nice and don't do that though. It's only an
|
||||||
// issue when there's multiple clients with the same WorldContainer in different worlds
|
// issue when there's multiple clients with the same WorldContainer in different worlds
|
||||||
// anyways.
|
// anyways.
|
||||||
pub worlds: HashMap<ResourceLocation, Weak<RwLock<World>>>,
|
pub worlds: HashMap<ResourceLocation, Weak<RwLock<Instance>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WorldContainer {
|
impl WorldContainer {
|
||||||
|
@ -37,7 +37,7 @@ impl WorldContainer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a world from the container.
|
/// Get a world from the container.
|
||||||
pub fn get(&self, name: &ResourceLocation) -> Option<Arc<RwLock<World>>> {
|
pub fn get(&self, name: &ResourceLocation) -> Option<Arc<RwLock<Instance>>> {
|
||||||
self.worlds.get(name).and_then(|world| world.upgrade())
|
self.worlds.get(name).and_then(|world| world.upgrade())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ impl WorldContainer {
|
||||||
name: ResourceLocation,
|
name: ResourceLocation,
|
||||||
height: u32,
|
height: u32,
|
||||||
min_y: i32,
|
min_y: i32,
|
||||||
) -> Arc<RwLock<World>> {
|
) -> Arc<RwLock<Instance>> {
|
||||||
if let Some(existing_lock) = self.worlds.get(&name).and_then(|world| world.upgrade()) {
|
if let Some(existing_lock) = self.worlds.get(&name).and_then(|world| world.upgrade()) {
|
||||||
let existing = existing_lock.read();
|
let existing = existing_lock.read();
|
||||||
if existing.chunks.height != height {
|
if existing.chunks.height != height {
|
||||||
|
@ -66,7 +66,7 @@ impl WorldContainer {
|
||||||
}
|
}
|
||||||
existing_lock.clone()
|
existing_lock.clone()
|
||||||
} else {
|
} else {
|
||||||
let world = Arc::new(RwLock::new(World {
|
let world = Arc::new(RwLock::new(Instance {
|
||||||
chunks: ChunkStorage::new(height, min_y),
|
chunks: ChunkStorage::new(height, min_y),
|
||||||
entities_by_chunk: HashMap::new(),
|
entities_by_chunk: HashMap::new(),
|
||||||
entity_by_id: IntMap::default(),
|
entity_by_id: IntMap::default(),
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
|
use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
|
||||||
use azalea_ecs::component::Component;
|
use bevy_ecs::component::Component;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::{uuid, Uuid};
|
use uuid::{uuid, Uuid};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use azalea_buf::{
|
||||||
};
|
};
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_core::{BlockPos, Direction, GlobalPos, Particle, Slot};
|
use azalea_core::{BlockPos, Direction, GlobalPos, Particle, Slot};
|
||||||
use azalea_ecs::component::Component;
|
use bevy_ecs::component::Component;
|
||||||
use derive_more::Deref;
|
use derive_more::Deref;
|
||||||
use enum_as_inner::EnumAsInner;
|
use enum_as_inner::EnumAsInner;
|
||||||
use nohash_hasher::IntSet;
|
use nohash_hasher::IntSet;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use azalea_core::{Vec3, AABB};
|
use azalea_core::{Vec3, AABB};
|
||||||
use azalea_ecs::{query::Changed, system::Query};
|
use bevy_ecs::{query::Changed, system::Query};
|
||||||
|
|
||||||
use super::{Physics, Position};
|
use super::{Physics, Position};
|
||||||
|
|
||||||
|
|
|
@ -9,15 +9,14 @@ use crate::{
|
||||||
update_entity_by_id_index, update_uuid_index, PartialWorld, WorldContainer,
|
update_entity_by_id_index, update_uuid_index, PartialWorld, WorldContainer,
|
||||||
};
|
};
|
||||||
use azalea_core::ChunkPos;
|
use azalea_core::ChunkPos;
|
||||||
use azalea_ecs::{
|
use bevy_app::{App, CoreSet, Plugin};
|
||||||
app::{App, CoreStage, Plugin},
|
use bevy_ecs::{
|
||||||
component::Component,
|
component::Component,
|
||||||
ecs::Ecs,
|
|
||||||
ecs::EntityMut,
|
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{Added, Changed, With, Without},
|
query::{Added, Changed, With, Without},
|
||||||
schedule::{IntoSystemDescriptor, SystemSet},
|
schedule::{IntoSystemConfig, IntoSystemConfigs, SystemSet},
|
||||||
system::{Command, Commands, Query, Res, ResMut, Resource},
|
system::{Commands, EntityCommand, Query, Res, ResMut, Resource},
|
||||||
|
world::{EntityMut, World},
|
||||||
};
|
};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
|
@ -32,6 +31,18 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use super::Local;
|
use super::Local;
|
||||||
|
|
||||||
|
/// A Bevy [`SystemSet`] for various types of entity updates.
|
||||||
|
#[derive(SystemSet, Debug, Hash, Eq, PartialEq, Clone)]
|
||||||
|
pub enum EntityUpdateSet {
|
||||||
|
/// Remove ECS entities that refer to an entity that was already in the ECS
|
||||||
|
/// before.
|
||||||
|
Deduplicate,
|
||||||
|
/// Create search indexes for entities.
|
||||||
|
Index,
|
||||||
|
/// Remove despawned entities from search indexes.
|
||||||
|
Deindex,
|
||||||
|
}
|
||||||
|
|
||||||
/// Plugin handling some basic entity functionality.
|
/// Plugin handling some basic entity functionality.
|
||||||
pub struct EntityPlugin;
|
pub struct EntityPlugin;
|
||||||
impl Plugin for EntityPlugin {
|
impl Plugin for EntityPlugin {
|
||||||
|
@ -40,30 +51,31 @@ impl Plugin for EntityPlugin {
|
||||||
// added to indexes during update (done by this plugin)
|
// added to indexes during update (done by this plugin)
|
||||||
// modified during update
|
// modified during update
|
||||||
// despawned post-update (done by this plugin)
|
// despawned post-update (done by this plugin)
|
||||||
app.add_system_set_to_stage(
|
app.add_system(
|
||||||
CoreStage::PreUpdate,
|
remove_despawned_entities_from_indexes
|
||||||
SystemSet::new().with_system(remove_despawned_entities_from_indexes),
|
.in_base_set(CoreSet::PreUpdate)
|
||||||
|
.in_set(EntityUpdateSet::Deindex),
|
||||||
)
|
)
|
||||||
.add_system_set_to_stage(
|
.add_systems(
|
||||||
CoreStage::PostUpdate,
|
(deduplicate_entities, deduplicate_local_entities)
|
||||||
SystemSet::new()
|
.in_base_set(CoreSet::PostUpdate)
|
||||||
.with_system(deduplicate_entities.label("deduplicate_entities"))
|
.in_set(EntityUpdateSet::Deduplicate),
|
||||||
.with_system(deduplicate_local_entities.label("deduplicate_entities")),
|
|
||||||
)
|
)
|
||||||
.add_system_set(
|
.add_systems(
|
||||||
SystemSet::new()
|
(
|
||||||
.with_system(update_entity_chunk_positions)
|
update_entity_chunk_positions,
|
||||||
.with_system(update_uuid_index.label("update_indexes"))
|
update_uuid_index,
|
||||||
.with_system(update_entity_by_id_index.label("update_indexes")),
|
update_entity_by_id_index,
|
||||||
)
|
)
|
||||||
.add_system_set(
|
.in_set(EntityUpdateSet::Index),
|
||||||
SystemSet::new()
|
|
||||||
.with_system(add_updates_received.label("add_updates_received"))
|
|
||||||
.with_system(debug_new_entity)
|
|
||||||
.with_system(debug_detect_updates_received_on_local_entities)
|
|
||||||
.with_system(add_dead)
|
|
||||||
.with_system(update_bounding_box),
|
|
||||||
)
|
)
|
||||||
|
.add_systems((
|
||||||
|
add_updates_received,
|
||||||
|
debug_new_entity,
|
||||||
|
debug_detect_updates_received_on_local_entities,
|
||||||
|
add_dead,
|
||||||
|
update_bounding_box,
|
||||||
|
))
|
||||||
.init_resource::<EntityInfos>();
|
.init_resource::<EntityInfos>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,26 +146,24 @@ impl PartialEntityInfos {
|
||||||
/// other clients within render distance will get too. You usually don't need
|
/// other clients within render distance will get too. You usually don't need
|
||||||
/// this when the change isn't relative either.
|
/// this when the change isn't relative either.
|
||||||
pub struct RelativeEntityUpdate {
|
pub struct RelativeEntityUpdate {
|
||||||
pub entity: Entity,
|
|
||||||
pub partial_world: Arc<RwLock<PartialWorld>>,
|
pub partial_world: Arc<RwLock<PartialWorld>>,
|
||||||
// a function that takes the entity and updates it
|
// a function that takes the entity and updates it
|
||||||
pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>,
|
pub update: Box<dyn FnOnce(&mut EntityMut) + Send + Sync>,
|
||||||
}
|
}
|
||||||
impl Command for RelativeEntityUpdate {
|
impl EntityCommand for RelativeEntityUpdate {
|
||||||
fn write(self, world: &mut Ecs) {
|
fn write(self, entity: Entity, world: &mut World) {
|
||||||
let partial_entity_infos = &mut self.partial_world.write().entity_infos;
|
let partial_entity_infos = &mut self.partial_world.write().entity_infos;
|
||||||
|
|
||||||
let mut entity = world.entity_mut(self.entity);
|
let mut entity_mut = world.entity_mut(entity);
|
||||||
|
|
||||||
if Some(self.entity) == partial_entity_infos.owner_entity {
|
if Some(entity) == partial_entity_infos.owner_entity {
|
||||||
// if the entity owns this partial world, it's always allowed to update itself
|
// if the entity owns this partial world, it's always allowed to update itself
|
||||||
(self.update)(&mut entity);
|
(self.update)(&mut entity_mut);
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let entity_id = *entity.get::<MinecraftEntityId>().unwrap();
|
let entity_id = *entity_mut.get::<MinecraftEntityId>().unwrap();
|
||||||
|
let Some(updates_received) = entity_mut.get_mut::<UpdatesReceived>() else {
|
||||||
let Some(updates_received) = entity.get_mut::<UpdatesReceived>() else {
|
|
||||||
// a client tried to update another client, which isn't allowed
|
// a client tried to update another client, which isn't allowed
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
@ -170,9 +180,9 @@ impl Command for RelativeEntityUpdate {
|
||||||
.updates_received
|
.updates_received
|
||||||
.insert(entity_id, new_updates_received);
|
.insert(entity_id, new_updates_received);
|
||||||
|
|
||||||
**entity.get_mut::<UpdatesReceived>().unwrap() = new_updates_received;
|
**entity_mut.get_mut::<UpdatesReceived>().unwrap() = new_updates_received;
|
||||||
|
|
||||||
let mut entity = world.entity_mut(self.entity);
|
let mut entity = world.entity_mut(entity);
|
||||||
(self.update)(&mut entity);
|
(self.update)(&mut entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,7 +12,7 @@ use self::{attributes::AttributeInstance, metadata::Health};
|
||||||
pub use attributes::Attributes;
|
pub use attributes::Attributes;
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3, AABB};
|
use azalea_core::{BlockPos, ChunkPos, ResourceLocation, Vec3, AABB};
|
||||||
use azalea_ecs::{
|
use bevy_ecs::{
|
||||||
bundle::Bundle,
|
bundle::Bundle,
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
|
@ -22,7 +22,9 @@ use azalea_ecs::{
|
||||||
pub use data::*;
|
pub use data::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
pub use dimensions::{update_bounding_box, EntityDimensions};
|
pub use dimensions::{update_bounding_box, EntityDimensions};
|
||||||
pub use info::{EntityInfos, EntityPlugin, LoadedBy, PartialEntityInfos, RelativeEntityUpdate};
|
pub use info::{
|
||||||
|
EntityInfos, EntityPlugin, EntityUpdateSet, LoadedBy, PartialEntityInfos, RelativeEntityUpdate,
|
||||||
|
};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
ChunkStorage, PartialChunkStorage, WorldContainer,
|
ChunkStorage, PartialChunkStorage, WorldContainer,
|
||||||
};
|
};
|
||||||
use azalea_core::ChunkPos;
|
use azalea_core::ChunkPos;
|
||||||
use azalea_ecs::{
|
use bevy_ecs::{
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
query::{Changed, With, Without},
|
query::{Changed, With, Without},
|
||||||
system::{Commands, Query, Res, ResMut},
|
system::{Commands, Query, Res, ResMut},
|
||||||
|
@ -172,7 +172,7 @@ pub fn update_uuid_index(
|
||||||
/// A world where the chunks are stored as weak pointers. This is used for
|
/// A world where the chunks are stored as weak pointers. This is used for
|
||||||
/// shared worlds.
|
/// shared worlds.
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct World {
|
pub struct Instance {
|
||||||
pub chunks: ChunkStorage,
|
pub chunks: ChunkStorage,
|
||||||
|
|
||||||
/// An index of all the entities we know are in the chunks of the world
|
/// An index of all the entities we know are in the chunks of the world
|
||||||
|
@ -182,7 +182,7 @@ pub struct World {
|
||||||
pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
|
pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl World {
|
impl Instance {
|
||||||
/// Get an ECS [`Entity`] from a Minecraft entity ID.
|
/// Get an ECS [`Entity`] from a Minecraft entity ID.
|
||||||
pub fn entity_by_id(&self, entity_id: &MinecraftEntityId) -> Option<Entity> {
|
pub fn entity_by_id(&self, entity_id: &MinecraftEntityId) -> Option<Entity> {
|
||||||
self.entity_by_id.get(entity_id).copied()
|
self.entity_by_id.get(entity_id).copied()
|
||||||
|
@ -236,7 +236,7 @@ pub fn update_entity_by_id_index(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ChunkStorage> for World {
|
impl From<ChunkStorage> for Instance {
|
||||||
/// Make an empty world from this `ChunkStorage`. This is meant to be a
|
/// Make an empty world from this `ChunkStorage`. This is meant to be a
|
||||||
/// convenience function for tests.
|
/// convenience function for tests.
|
||||||
fn from(chunks: ChunkStorage) -> Self {
|
fn from(chunks: ChunkStorage) -> Self {
|
||||||
|
|
|
@ -8,31 +8,31 @@ version = "0.6.0"
|
||||||
|
|
||||||
[package.metadata.release]
|
[package.metadata.release]
|
||||||
pre-release-replacements = [
|
pre-release-replacements = [
|
||||||
{file = "README.md", search = "`azalea = \"[a-z0-9\\.-]+\"`", replace = "`azalea = \"{{version}}\"`"},
|
{ file = "README.md", search = "`azalea = \"[a-z0-9\\.-]+\"`", replace = "`azalea = \"{{version}}\"`" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "^1.0.65"
|
anyhow = "^1.0.65"
|
||||||
async-trait = "0.1.58"
|
async-trait = "0.1.58"
|
||||||
azalea-block = {version = "0.6.0", path = "../azalea-block"}
|
azalea-block = { version = "0.6.0", path = "../azalea-block" }
|
||||||
azalea-chat = {version = "0.6.0", path = "../azalea-chat"}
|
azalea-chat = { version = "0.6.0", path = "../azalea-chat" }
|
||||||
azalea-client = {version = "0.6.0", path = "../azalea-client"}
|
azalea-client = { version = "0.6.0", path = "../azalea-client" }
|
||||||
azalea-core = {version = "0.6.0", path = "../azalea-core"}
|
azalea-core = { version = "0.6.0", path = "../azalea-core" }
|
||||||
azalea-ecs = {version = "0.6.0", path = "../azalea-ecs"}
|
azalea-physics = { version = "0.6.0", path = "../azalea-physics" }
|
||||||
azalea-physics = {version = "0.6.0", path = "../azalea-physics"}
|
azalea-protocol = { version = "0.6.0", path = "../azalea-protocol" }
|
||||||
azalea-protocol = {version = "0.6.0", path = "../azalea-protocol"}
|
azalea-registry = { version = "0.6.0", path = "../azalea-registry" }
|
||||||
azalea-registry = {version = "0.6.0", path = "../azalea-registry"}
|
azalea-world = { version = "0.6.0", path = "../azalea-world" }
|
||||||
azalea-world = {version = "0.6.0", path = "../azalea-world"}
|
bevy_app = "0.10.0"
|
||||||
bevy_tasks = "0.9.1"
|
bevy_ecs = "0.10.0"
|
||||||
derive_more = {version = "0.99.17", features = ["deref", "deref_mut"]}
|
bevy_tasks = "0.10.0"
|
||||||
|
derive_more = { version = "0.99.17", features = ["deref", "deref_mut"] }
|
||||||
futures = "0.3.25"
|
futures = "0.3.25"
|
||||||
futures-lite = "1.12.0"
|
futures-lite = "1.12.0"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
nohash-hasher = "0.2.0"
|
nohash-hasher = "0.2.0"
|
||||||
num-traits = "0.2.15"
|
num-traits = "0.2.15"
|
||||||
parking_lot = {version = "^0.12.1", features = ["deadlock_detection"]}
|
parking_lot = { version = "^0.12.1", features = ["deadlock_detection"] }
|
||||||
priority-queue = "1.3.0"
|
priority-queue = "1.3.0"
|
||||||
thiserror = "^1.0.37"
|
thiserror = "^1.0.37"
|
||||||
tokio = "^1.24.2"
|
tokio = "^1.24.2"
|
||||||
uuid = "1.2.2"
|
uuid = "1.2.2"
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use azalea::ecs::query::With;
|
||||||
use azalea::entity::metadata::Player;
|
use azalea::entity::metadata::Player;
|
||||||
use azalea::{pathfinder, Account, Client, Event, GameProfileComponent};
|
use azalea::{pathfinder, Account, Client, Event, GameProfileComponent};
|
||||||
use azalea::{prelude::*, swarm::prelude::*};
|
use azalea::{prelude::*, swarm::prelude::*};
|
||||||
use azalea_ecs::query::With;
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
|
|
|
@ -52,17 +52,17 @@ async fn main() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let e = SwarmBuilder::new()
|
// let e = SwarmBuilder::new()
|
||||||
.add_accounts(accounts.clone())
|
// .add_accounts(accounts.clone())
|
||||||
.set_handler(handle)
|
|
||||||
.set_swarm_handler(swarm_handle)
|
|
||||||
.join_delay(Duration::from_millis(1000))
|
|
||||||
.start("localhost")
|
|
||||||
.await;
|
|
||||||
// let e = azalea::ClientBuilder::new()
|
|
||||||
// .set_handler(handle)
|
// .set_handler(handle)
|
||||||
// .start(Account::offline("bot"), "localhost")
|
// .set_swarm_handler(swarm_handle)
|
||||||
|
// .join_delay(Duration::from_millis(1000))
|
||||||
|
// .start("localhost")
|
||||||
// .await;
|
// .await;
|
||||||
|
let e = azalea::ClientBuilder::new()
|
||||||
|
.set_handler(handle)
|
||||||
|
.start(Account::offline("bot"), "localhost")
|
||||||
|
.await;
|
||||||
eprintln!("{e:?}");
|
eprintln!("{e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use azalea_core::Vec3;
|
use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin, PluginGroup, PluginGroupBuilder};
|
||||||
use azalea_ecs::{
|
use crate::ecs::{
|
||||||
app::{App, Plugin, PluginGroup, PluginGroupBuilder},
|
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::EventReader,
|
event::EventReader,
|
||||||
query::{With, Without},
|
query::{With, Without},
|
||||||
schedule::IntoSystemDescriptor,
|
schedule::IntoSystemConfig,
|
||||||
system::{Commands, Query},
|
system::{Commands, Query},
|
||||||
AppTickExt,
|
|
||||||
};
|
};
|
||||||
|
use azalea_core::Vec3;
|
||||||
|
use azalea_physics::{force_jump_listener, PhysicsSet};
|
||||||
use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position};
|
use azalea_world::entity::{metadata::Player, set_rotation, Jumping, Local, Physics, Position};
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
|
@ -20,14 +20,14 @@ impl Plugin for BotPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<LookAtEvent>()
|
app.add_event::<LookAtEvent>()
|
||||||
.add_event::<JumpEvent>()
|
.add_event::<JumpEvent>()
|
||||||
.add_system(insert_bot)
|
.add_systems((
|
||||||
.add_system(
|
insert_bot,
|
||||||
look_at_listener
|
look_at_listener.before(force_jump_listener),
|
||||||
.before("force_jump_listener")
|
jump_listener,
|
||||||
.before(azalea_world::entity::update_bounding_box),
|
stop_jumping
|
||||||
)
|
.in_schedule(CoreSchedule::FixedUpdate)
|
||||||
.add_system(jump_listener.label("jump_listener"))
|
.after(PhysicsSet),
|
||||||
.add_tick_system(stop_jumping.after("ai_step"));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,18 +6,15 @@ pub mod pathfinder;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod swarm;
|
pub mod swarm;
|
||||||
|
|
||||||
|
use app::{App, Plugin, PluginGroup};
|
||||||
pub use azalea_block as blocks;
|
pub use azalea_block as blocks;
|
||||||
pub use azalea_client::*;
|
pub use azalea_client::*;
|
||||||
pub use azalea_core::{BlockPos, Vec3};
|
pub use azalea_core::{BlockPos, Vec3};
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
component::Component,
|
|
||||||
};
|
|
||||||
pub use azalea_protocol as protocol;
|
pub use azalea_protocol as protocol;
|
||||||
pub use azalea_registry::EntityKind;
|
pub use azalea_registry::EntityKind;
|
||||||
pub use azalea_world::{entity, World};
|
pub use azalea_world::{entity, Instance};
|
||||||
use bot::DefaultBotPlugins;
|
use bot::DefaultBotPlugins;
|
||||||
use ecs::app::PluginGroup;
|
use ecs::component::Component;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use protocol::{
|
use protocol::{
|
||||||
resolver::{self, ResolverError},
|
resolver::{self, ResolverError},
|
||||||
|
@ -26,7 +23,10 @@ use protocol::{
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
pub type HandleFn<Fut, S> = fn(Client, Event, S) -> Fut;
|
pub use bevy_app as app;
|
||||||
|
pub use bevy_ecs as ecs;
|
||||||
|
|
||||||
|
pub type HandleFn<Fut, S> = fn(Client, azalea_client::Event, S) -> Fut;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum StartError {
|
pub enum StartError {
|
||||||
|
@ -142,6 +142,7 @@ where
|
||||||
|
|
||||||
// An event that causes the schedule to run. This is only used internally.
|
// An event that causes the schedule to run. This is only used internally.
|
||||||
let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel();
|
let (run_schedule_sender, run_schedule_receiver) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let ecs_lock = start_ecs(self.app, run_schedule_receiver, run_schedule_sender.clone());
|
let ecs_lock = start_ecs(self.app, run_schedule_receiver, run_schedule_sender.clone());
|
||||||
|
|
||||||
let (bot, mut rx) = Client::start_client(
|
let (bot, mut rx) = Client::start_client(
|
||||||
|
|
|
@ -4,18 +4,18 @@ mod mtdstarlite;
|
||||||
use crate::bot::{JumpEvent, LookAtEvent};
|
use crate::bot::{JumpEvent, LookAtEvent};
|
||||||
use crate::{SprintDirection, WalkDirection};
|
use crate::{SprintDirection, WalkDirection};
|
||||||
|
|
||||||
use azalea_client::{StartSprintEvent, StartWalkEvent};
|
use crate::app::{App, CoreSchedule, IntoSystemAppConfig, Plugin};
|
||||||
use azalea_core::{BlockPos, CardinalDirection};
|
use crate::ecs::{
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
component::Component,
|
component::Component,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::{EventReader, EventWriter},
|
event::{EventReader, EventWriter},
|
||||||
query::{With, Without},
|
query::{With, Without},
|
||||||
schedule::IntoSystemDescriptor,
|
schedule::IntoSystemConfig,
|
||||||
system::{Commands, Query, Res},
|
system::{Commands, Query, Res},
|
||||||
AppTickExt,
|
|
||||||
};
|
};
|
||||||
|
use azalea_client::{StartSprintEvent, StartWalkEvent};
|
||||||
|
use azalea_core::{BlockPos, CardinalDirection};
|
||||||
|
use azalea_physics::PhysicsSet;
|
||||||
use azalea_world::entity::metadata::Player;
|
use azalea_world::entity::metadata::Player;
|
||||||
use azalea_world::entity::Local;
|
use azalea_world::entity::Local;
|
||||||
use azalea_world::{
|
use azalea_world::{
|
||||||
|
@ -36,7 +36,13 @@ impl Plugin for PathfinderPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<GotoEvent>()
|
app.add_event::<GotoEvent>()
|
||||||
.add_event::<PathFoundEvent>()
|
.add_event::<PathFoundEvent>()
|
||||||
.add_tick_system(tick_execute_path.before("ai_step"))
|
.add_system(
|
||||||
|
// Adding `.in_schedule(CoreSchedule::FixedUpdate)` makes a system run every
|
||||||
|
// Minecraft tick (every 50 milliseconds).
|
||||||
|
tick_execute_path
|
||||||
|
.in_schedule(CoreSchedule::FixedUpdate)
|
||||||
|
.before(PhysicsSet),
|
||||||
|
)
|
||||||
.add_system(goto_listener)
|
.add_system(goto_listener)
|
||||||
.add_system(add_default_pathfinder)
|
.add_system(add_default_pathfinder)
|
||||||
.add_system(handle_tasks.before(path_found_listener))
|
.add_system(handle_tasks.before(path_found_listener))
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use super::{Node, VerticalVel};
|
use super::{Node, VerticalVel};
|
||||||
use azalea_core::{BlockPos, CardinalDirection};
|
use azalea_core::{BlockPos, CardinalDirection};
|
||||||
use azalea_physics::collision::{self, BlockWithShape};
|
use azalea_physics::collision::{self, BlockWithShape};
|
||||||
use azalea_world::World;
|
use azalea_world::Instance;
|
||||||
|
|
||||||
/// whether this block is passable
|
/// whether this block is passable
|
||||||
fn is_block_passable(pos: &BlockPos, world: &World) -> bool {
|
fn is_block_passable(pos: &BlockPos, world: &Instance) -> bool {
|
||||||
if let Some(block) = world.chunks.get_block_state(pos) {
|
if let Some(block) = world.chunks.get_block_state(pos) {
|
||||||
block.shape() == &collision::empty_shape()
|
block.shape() == &collision::empty_shape()
|
||||||
} else {
|
} else {
|
||||||
|
@ -13,7 +13,7 @@ fn is_block_passable(pos: &BlockPos, world: &World) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// whether this block has a solid hitbox (i.e. we can stand on it)
|
/// whether this block has a solid hitbox (i.e. we can stand on it)
|
||||||
fn is_block_solid(pos: &BlockPos, world: &World) -> bool {
|
fn is_block_solid(pos: &BlockPos, world: &Instance) -> bool {
|
||||||
if let Some(block) = world.chunks.get_block_state(pos) {
|
if let Some(block) = world.chunks.get_block_state(pos) {
|
||||||
block.shape() == &collision::block_shape()
|
block.shape() == &collision::block_shape()
|
||||||
} else {
|
} else {
|
||||||
|
@ -22,14 +22,14 @@ fn is_block_solid(pos: &BlockPos, world: &World) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether this block and the block above are passable
|
/// Whether this block and the block above are passable
|
||||||
fn is_passable(pos: &BlockPos, world: &World) -> bool {
|
fn is_passable(pos: &BlockPos, world: &Instance) -> bool {
|
||||||
is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
|
is_block_passable(pos, world) && is_block_passable(&pos.up(1), world)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether we can stand in this position. Checks if the block below is solid,
|
/// Whether we can stand in this position. Checks if the block below is solid,
|
||||||
/// and that the two blocks above that are passable.
|
/// and that the two blocks above that are passable.
|
||||||
|
|
||||||
fn is_standable(pos: &BlockPos, world: &World) -> bool {
|
fn is_standable(pos: &BlockPos, world: &Instance) -> bool {
|
||||||
is_block_solid(&pos.down(1), world) && is_passable(pos, world)
|
is_block_solid(&pos.down(1), world) && is_passable(pos, world)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ const JUMP_COST: f32 = 0.5;
|
||||||
const WALK_ONE_BLOCK_COST: f32 = 1.0;
|
const WALK_ONE_BLOCK_COST: f32 = 1.0;
|
||||||
|
|
||||||
pub trait Move: Send + Sync {
|
pub trait Move: Send + Sync {
|
||||||
fn cost(&self, world: &World, node: &Node) -> f32;
|
fn cost(&self, world: &Instance, node: &Node) -> f32;
|
||||||
/// Returns by how much the entity's position should be changed when this
|
/// Returns by how much the entity's position should be changed when this
|
||||||
/// move is executed.
|
/// move is executed.
|
||||||
fn offset(&self) -> BlockPos;
|
fn offset(&self) -> BlockPos;
|
||||||
|
@ -51,7 +51,7 @@ pub trait Move: Send + Sync {
|
||||||
|
|
||||||
pub struct ForwardMove(pub CardinalDirection);
|
pub struct ForwardMove(pub CardinalDirection);
|
||||||
impl Move for ForwardMove {
|
impl Move for ForwardMove {
|
||||||
fn cost(&self, world: &World, node: &Node) -> f32 {
|
fn cost(&self, world: &Instance, node: &Node) -> f32 {
|
||||||
if is_standable(&(node.pos + self.offset()), world)
|
if is_standable(&(node.pos + self.offset()), world)
|
||||||
&& node.vertical_vel == VerticalVel::None
|
&& node.vertical_vel == VerticalVel::None
|
||||||
{
|
{
|
||||||
|
@ -67,7 +67,7 @@ impl Move for ForwardMove {
|
||||||
|
|
||||||
pub struct AscendMove(pub CardinalDirection);
|
pub struct AscendMove(pub CardinalDirection);
|
||||||
impl Move for AscendMove {
|
impl Move for AscendMove {
|
||||||
fn cost(&self, world: &World, node: &Node) -> f32 {
|
fn cost(&self, world: &Instance, node: &Node) -> f32 {
|
||||||
if node.vertical_vel == VerticalVel::None
|
if node.vertical_vel == VerticalVel::None
|
||||||
&& is_block_passable(&node.pos.up(2), world)
|
&& is_block_passable(&node.pos.up(2), world)
|
||||||
&& is_standable(&(node.pos + self.offset()), world)
|
&& is_standable(&(node.pos + self.offset()), world)
|
||||||
|
@ -89,7 +89,7 @@ impl Move for AscendMove {
|
||||||
}
|
}
|
||||||
pub struct DescendMove(pub CardinalDirection);
|
pub struct DescendMove(pub CardinalDirection);
|
||||||
impl Move for DescendMove {
|
impl Move for DescendMove {
|
||||||
fn cost(&self, world: &World, node: &Node) -> f32 {
|
fn cost(&self, world: &Instance, node: &Node) -> f32 {
|
||||||
// check whether 3 blocks vertically forward are passable
|
// check whether 3 blocks vertically forward are passable
|
||||||
if node.vertical_vel == VerticalVel::None
|
if node.vertical_vel == VerticalVel::None
|
||||||
&& is_standable(&(node.pos + self.offset()), world)
|
&& is_standable(&(node.pos + self.offset()), world)
|
||||||
|
@ -112,7 +112,7 @@ impl Move for DescendMove {
|
||||||
}
|
}
|
||||||
pub struct DiagonalMove(pub CardinalDirection);
|
pub struct DiagonalMove(pub CardinalDirection);
|
||||||
impl Move for DiagonalMove {
|
impl Move for DiagonalMove {
|
||||||
fn cost(&self, world: &World, node: &Node) -> f32 {
|
fn cost(&self, world: &Instance, node: &Node) -> f32 {
|
||||||
if node.vertical_vel != VerticalVel::None {
|
if node.vertical_vel != VerticalVel::None {
|
||||||
return f32::INFINITY;
|
return f32::INFINITY;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,4 +3,6 @@
|
||||||
|
|
||||||
pub use crate::{bot::BotClientExt, pathfinder::PathfinderClientExt, ClientBuilder};
|
pub use crate::{bot::BotClientExt, pathfinder::PathfinderClientExt, ClientBuilder};
|
||||||
pub use azalea_client::{Account, Client, Event};
|
pub use azalea_client::{Account, Client, Event};
|
||||||
pub use azalea_ecs::{component::Component, system::Resource};
|
// this is necessary to make the macros that reference bevy_ecs work
|
||||||
|
pub use crate::ecs as bevy_ecs;
|
||||||
|
pub use crate::ecs::{component::Component, system::Resource};
|
||||||
|
|
|
@ -13,14 +13,14 @@
|
||||||
// in Swarm that's set to the smallest index of all the bots, and we remove all
|
// in Swarm that's set to the smallest index of all the bots, and we remove all
|
||||||
// messages from the queue that are before that index.
|
// messages from the queue that are before that index.
|
||||||
|
|
||||||
use azalea_client::chat::{ChatPacket, ChatReceivedEvent};
|
use crate::ecs::{
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
component::Component,
|
component::Component,
|
||||||
event::{EventReader, EventWriter},
|
event::{EventReader, EventWriter},
|
||||||
schedule::IntoSystemDescriptor,
|
schedule::IntoSystemConfigs,
|
||||||
system::{Commands, Query, Res, ResMut, Resource},
|
system::{Commands, Query, Res, ResMut, Resource},
|
||||||
};
|
};
|
||||||
|
use azalea_client::chat::{ChatPacket, ChatReceivedEvent};
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use super::{Swarm, SwarmEvent};
|
use super::{Swarm, SwarmEvent};
|
||||||
|
@ -30,8 +30,7 @@ pub struct SwarmChatPlugin;
|
||||||
impl Plugin for SwarmChatPlugin {
|
impl Plugin for SwarmChatPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_event::<NewChatMessageEvent>()
|
app.add_event::<NewChatMessageEvent>()
|
||||||
.add_system(chat_listener.label("chat_listener"))
|
.add_systems((chat_listener, update_min_index_and_shrink_queue).chain())
|
||||||
.add_system(update_min_index_and_shrink_queue.after("chat_listener"))
|
|
||||||
.insert_resource(GlobalChatState {
|
.insert_resource(GlobalChatState {
|
||||||
chat_queue: VecDeque::new(),
|
chat_queue: VecDeque::new(),
|
||||||
chat_min_index: 0,
|
chat_min_index: 0,
|
||||||
|
@ -151,7 +150,7 @@ fn update_min_index_and_shrink_queue(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use azalea_ecs::{ecs::Ecs, event::Events, system::SystemState};
|
use bevy_ecs::{event::Events, prelude::World, system::SystemState};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -161,8 +160,7 @@ mod tests {
|
||||||
// event mangement in drain_events
|
// event mangement in drain_events
|
||||||
app.init_resource::<Events<ChatReceivedEvent>>()
|
app.init_resource::<Events<ChatReceivedEvent>>()
|
||||||
.init_resource::<Events<NewChatMessageEvent>>()
|
.init_resource::<Events<NewChatMessageEvent>>()
|
||||||
.add_system(chat_listener.label("chat_listener"))
|
.add_systems((chat_listener, update_min_index_and_shrink_queue).chain())
|
||||||
.add_system(update_min_index_and_shrink_queue.after("chat_listener"))
|
|
||||||
.insert_resource(GlobalChatState {
|
.insert_resource(GlobalChatState {
|
||||||
chat_queue: VecDeque::new(),
|
chat_queue: VecDeque::new(),
|
||||||
chat_min_index: 0,
|
chat_min_index: 0,
|
||||||
|
@ -170,7 +168,7 @@ mod tests {
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drain_events(ecs: &mut Ecs) -> Vec<ChatPacket> {
|
fn drain_events(ecs: &mut World) -> Vec<ChatPacket> {
|
||||||
let mut system_state: SystemState<ResMut<Events<NewChatMessageEvent>>> =
|
let mut system_state: SystemState<ResMut<Events<NewChatMessageEvent>>> =
|
||||||
SystemState::new(ecs);
|
SystemState::new(ecs);
|
||||||
let mut events = system_state.get_mut(ecs);
|
let mut events = system_state.get_mut(ecs);
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
use azalea_client::LocalPlayer;
|
use azalea_client::LocalPlayer;
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin},
|
|
||||||
event::EventWriter,
|
|
||||||
query::With,
|
|
||||||
system::{Query, ResMut, Resource},
|
|
||||||
};
|
|
||||||
use azalea_world::entity::MinecraftEntityId;
|
use azalea_world::entity::MinecraftEntityId;
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
|
|
||||||
pub struct SwarmPlugin;
|
pub struct SwarmPlugin;
|
||||||
|
|
|
@ -6,19 +6,14 @@ pub mod prelude;
|
||||||
|
|
||||||
use crate::{bot::DefaultBotPlugins, HandleFn};
|
use crate::{bot::DefaultBotPlugins, HandleFn};
|
||||||
use azalea_client::{chat::ChatPacket, init_ecs_app, start_ecs, Account, Client, Event, JoinError};
|
use azalea_client::{chat::ChatPacket, init_ecs_app, start_ecs, Account, Client, Event, JoinError};
|
||||||
use azalea_ecs::{
|
|
||||||
app::{App, Plugin, PluginGroup, PluginGroupBuilder},
|
|
||||||
component::Component,
|
|
||||||
ecs::Ecs,
|
|
||||||
entity::Entity,
|
|
||||||
system::Resource,
|
|
||||||
};
|
|
||||||
use azalea_protocol::{
|
use azalea_protocol::{
|
||||||
connect::ConnectionError,
|
connect::ConnectionError,
|
||||||
resolver::{self, ResolverError},
|
resolver::{self, ResolverError},
|
||||||
ServerAddress,
|
ServerAddress,
|
||||||
};
|
};
|
||||||
use azalea_world::WorldContainer;
|
use azalea_world::WorldContainer;
|
||||||
|
use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder};
|
||||||
|
use bevy_ecs::{component::Component, entity::Entity, system::Resource, world::World};
|
||||||
use futures::future::join_all;
|
use futures::future::join_all;
|
||||||
use log::error;
|
use log::error;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
@ -35,7 +30,7 @@ use tokio::sync::mpsc;
|
||||||
/// It's used to make the [`Swarm::add`] function work.
|
/// It's used to make the [`Swarm::add`] function work.
|
||||||
#[derive(Clone, Resource)]
|
#[derive(Clone, Resource)]
|
||||||
pub struct Swarm {
|
pub struct Swarm {
|
||||||
pub ecs_lock: Arc<Mutex<Ecs>>,
|
pub ecs_lock: Arc<Mutex<World>>,
|
||||||
|
|
||||||
bots: Arc<Mutex<HashMap<Entity, Client>>>,
|
bots: Arc<Mutex<HashMap<Entity, Client>>>,
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ use super::{EntityDataItem, EntityDataValue, OptionalUnsignedInt, Pose, Rotation
|
||||||
use azalea_block::BlockState;
|
use azalea_block::BlockState;
|
||||||
use azalea_chat::FormattedText;
|
use azalea_chat::FormattedText;
|
||||||
use azalea_core::{BlockPos, Direction, Particle, Slot};
|
use azalea_core::{BlockPos, Direction, Particle, Slot};
|
||||||
use azalea_ecs::{bundle::Bundle, component::Component};
|
use bevy_ecs::{bundle::Bundle, component::Component};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
@ -183,7 +183,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
|
|
||||||
# impl Allay {
|
# impl Allay {
|
||||||
# pub fn apply_metadata(
|
# pub fn apply_metadata(
|
||||||
# entity: &mut azalea_ecs::system::EntityCommands,
|
# entity: &mut bevy_ecs::system::EntityCommands,
|
||||||
# d: EntityDataItem,
|
# d: EntityDataItem,
|
||||||
# ) -> Result<(), UpdateMetadataError> {
|
# ) -> Result<(), UpdateMetadataError> {
|
||||||
# match d.index {
|
# match d.index {
|
||||||
|
@ -196,7 +196,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
# }
|
# }
|
||||||
code.append(f'impl {struct_name} {{')
|
code.append(f'impl {struct_name} {{')
|
||||||
code.append(
|
code.append(
|
||||||
f' pub fn apply_metadata(entity: &mut azalea_ecs::system::EntityCommands, d: EntityDataItem) -> Result<(), UpdateMetadataError> {{')
|
f' pub fn apply_metadata(entity: &mut bevy_ecs::system::EntityCommands, d: EntityDataItem) -> Result<(), UpdateMetadataError> {{')
|
||||||
code.append(f' match d.index {{')
|
code.append(f' match d.index {{')
|
||||||
|
|
||||||
parent_last_index = -1
|
parent_last_index = -1
|
||||||
|
@ -400,7 +400,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
|
|
||||||
# and now make the main apply_metadata
|
# and now make the main apply_metadata
|
||||||
# pub fn apply_metadata(
|
# pub fn apply_metadata(
|
||||||
# entity: &mut azalea_ecs::system::EntityCommands,
|
# entity: &mut bevy_ecs::system::EntityCommands,
|
||||||
# items: Vec<EntityDataItem>,
|
# items: Vec<EntityDataItem>,
|
||||||
# ) -> Result<(), UpdateMetadataError> {
|
# ) -> Result<(), UpdateMetadataError> {
|
||||||
# if entity.contains::<Allay>() {
|
# if entity.contains::<Allay>() {
|
||||||
|
@ -414,7 +414,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
# }
|
# }
|
||||||
code.append(
|
code.append(
|
||||||
f'''pub fn apply_metadata(
|
f'''pub fn apply_metadata(
|
||||||
entity: &mut azalea_ecs::system::EntityCommands,
|
entity: &mut bevy_ecs::system::EntityCommands,
|
||||||
entity_kind: azalea_registry::EntityKind,
|
entity_kind: azalea_registry::EntityKind,
|
||||||
items: Vec<EntityDataItem>,
|
items: Vec<EntityDataItem>,
|
||||||
) -> Result<(), UpdateMetadataError> {{
|
) -> Result<(), UpdateMetadataError> {{
|
||||||
|
@ -436,7 +436,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
code.append('}')
|
code.append('}')
|
||||||
code.append('')
|
code.append('')
|
||||||
|
|
||||||
# pub fn apply_default_metadata(entity: &mut azalea_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {
|
# pub fn apply_default_metadata(entity: &mut bevy_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {
|
||||||
# match kind {
|
# match kind {
|
||||||
# azalea_registry::EntityKind::AreaEffectCloud => {
|
# azalea_registry::EntityKind::AreaEffectCloud => {
|
||||||
# entity.insert(AreaEffectCloudMetadataBundle::default());
|
# entity.insert(AreaEffectCloudMetadataBundle::default());
|
||||||
|
@ -444,7 +444,7 @@ impl From<EntityDataValue> for UpdateMetadataError {
|
||||||
# }
|
# }
|
||||||
# }
|
# }
|
||||||
code.append(
|
code.append(
|
||||||
'pub fn apply_default_metadata(entity: &mut azalea_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {')
|
'pub fn apply_default_metadata(entity: &mut bevy_ecs::system::EntityCommands, kind: azalea_registry::EntityKind) {')
|
||||||
code.append(' match kind {')
|
code.append(' match kind {')
|
||||||
for entity_id in burger_entity_data:
|
for entity_id in burger_entity_data:
|
||||||
if entity_id.startswith('~'):
|
if entity_id.startswith('~'):
|
||||||
|
|
Loading…
Add table
Reference in a new issue