Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"18e8206ff2b7b95a116175888ed5411fc849de739f0f0e1a9d771c8b8ed3cf73","Cargo.toml":"809ec9adddee28f8acbe163d8d2b9aeb4c538bee0a769ab0790059cebacd262c","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1","src/app.rs":"c5fa549ca4ed6ae0e98b14092c55d2cdacd2a498ad59a0e3eb7be22dac83bbb8","src/commands.rs":"13910ae4c00ceb3d70eafadf8b4ef7fd222e60b09fe2c02c5470557c6cb05f1f","src/condition.rs":"48d4eadc4dadebf6a08c638d2f314828d9908409072fe714b9c5ef6f35c2285b","src/lib.rs":"e6274006f13404696e31e35bc6d6d0a4dd3b5c61b484bc798c4eb28ca3885297","src/reflect.rs":"23dcd1ba7b25560171d71edf320410b0362d5a458931d53697d859fd128377c8","src/state/computed_states.rs":"ae81c3a1dcaaeefa528c970c7febcdc6ab0f1a9389fee4da3f3c78c1780da94e","src/state/freely_mutable_state.rs":"4a602b79820d7c2cfffd3b0bfe44ff9fa109b45af0c35a72512c9575d1d67a66","src/state/mod.rs":"729d9d3cb2b7ce38b46baf97c5acf5838718b585bf1c6d08ba12f3faf7e1b7cb","src/state/resources.rs":"d323399e907a2c6050a9aa7cd637cfd61476c47c749f09cff4ee3914af77104f","src/state/state_set.rs":"308af32ea020f6c2d9e272ae4b2808ddff76105f923026e91ff1881b0bc8e5a6","src/state/states.rs":"96a097c6e0018b1643a836bdbb400a24339b3f629c07ac17412759ebad061f30","src/state/sub_states.rs":"27511350537d8179a5f81f69ea32e291b1ef5829af89591b525b8f5fa828001b","src/state/transitions.rs":"484c41f8c0b0a454aa263e2944fa9ebdb9a7659a3207ee47cef430b5b7151563","src/state_scoped.rs":"684874df6f66f64b27f3fb5e09fb1494e5322e5e0ebc3398d6c6409ac4ff1945","src/state_scoped_events.rs":"4c2aa6738eac112d71a2bcfbf94b5d6f2f404c7ecca501df8ed535d57978a6f9"},"package":"155d3cd97b900539008cdcaa702f88b724d94b08977b8e591a32536ce66faa8c"}

958
vendor/bevy_state/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,958 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "assert_type_match"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f548ad2c4031f2902e3edc1f29c29e835829437de49562d8eb5dc5584d3a1043"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
dependencies = [
"portable-atomic",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
dependencies = [
"portable-atomic",
]
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bevy_app"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4491cc4c718ae76b4c6883df58b94cc88b32dcd894ea8d5b603c7c7da72ca967"
dependencies = [
"bevy_derive",
"bevy_ecs",
"bevy_platform",
"bevy_reflect",
"bevy_tasks",
"bevy_utils",
"cfg-if",
"ctrlc",
"downcast-rs",
"log",
"thiserror",
"variadics_please",
]
[[package]]
name = "bevy_derive"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b837bf6c51806b10ebfa9edf1844ad80a3a0760d6c5fac4e90761df91a8901a"
dependencies = [
"bevy_macro_utils",
"quote",
"syn",
]
[[package]]
name = "bevy_ecs"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c2bf6521aae57a0ec3487c4bfb59e36c4a378e834b626a4bea6a885af2fdfe7"
dependencies = [
"arrayvec",
"bevy_ecs_macros",
"bevy_platform",
"bevy_ptr",
"bevy_reflect",
"bevy_tasks",
"bevy_utils",
"bitflags",
"bumpalo",
"concurrent-queue",
"derive_more",
"disqualified",
"fixedbitset",
"indexmap",
"log",
"nonmax",
"serde",
"smallvec",
"thiserror",
"variadics_please",
]
[[package]]
name = "bevy_ecs_macros"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38748d6f3339175c582d751f410fb60a93baf2286c3deb7efebb0878dce7f413"
dependencies = [
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bevy_macro_utils"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052eeebcb8e7e072beea5031b227d9a290f8a7fbbb947573ab6ec81df0fb94be"
dependencies = [
"parking_lot",
"proc-macro2",
"quote",
"syn",
"toml_edit",
]
[[package]]
name = "bevy_platform"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7573dc824a1b08b4c93fdbe421c53e1e8188e9ca1dd74a414455fe571facb47"
dependencies = [
"cfg-if",
"critical-section",
"foldhash",
"hashbrown",
"portable-atomic",
"portable-atomic-util",
"serde",
"spin",
]
[[package]]
name = "bevy_ptr"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df7370d0e46b60e071917711d0860721f5347bc958bf325975ae6913a5dfcf01"
[[package]]
name = "bevy_reflect"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daeb91a63a1a4df00aa58da8cc4ddbd4b9f16ab8bb647c5553eb156ce36fa8c2"
dependencies = [
"assert_type_match",
"bevy_platform",
"bevy_ptr",
"bevy_reflect_derive",
"bevy_utils",
"derive_more",
"disqualified",
"downcast-rs",
"erased-serde",
"foldhash",
"glam",
"serde",
"smallvec",
"smol_str",
"thiserror",
"uuid",
"variadics_please",
"wgpu-types",
]
[[package]]
name = "bevy_reflect_derive"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40ddadc55fe16b45faaa54ab2f9cb00548013c74812e8b018aa172387103cce6"
dependencies = [
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
"uuid",
]
[[package]]
name = "bevy_state"
version = "0.16.1"
dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_platform",
"bevy_reflect",
"bevy_state_macros",
"bevy_utils",
"log",
"variadics_please",
]
[[package]]
name = "bevy_state_macros"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2481c1304fd2a1851a0d4cb63a1ce6421ae40f3f0117cbc9882963ee4c9bb609"
dependencies = [
"bevy_macro_utils",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bevy_tasks"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b674242641cab680688fc3b850243b351c1af49d4f3417a576debd6cca8dcf5"
dependencies = [
"async-task",
"atomic-waker",
"bevy_platform",
"cfg-if",
"crossbeam-queue",
"derive_more",
"futures-lite",
"heapless",
]
[[package]]
name = "bevy_utils"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94f7a8905a125d2017e8561beefb7f2f5e67e93ff6324f072ad87c5fd6ec3b99"
dependencies = [
"bevy_platform",
"thread_local",
]
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
dependencies = [
"serde",
]
[[package]]
name = "bumpalo"
version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
"portable-atomic",
]
[[package]]
name = "critical-section"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
[[package]]
name = "crossbeam-queue"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "ctrlc"
version = "3.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73"
dependencies = [
"nix",
"windows-sys",
]
[[package]]
name = "derive_more"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22"
dependencies = [
"proc-macro2",
"quote",
"syn",
"unicode-xid",
]
[[package]]
name = "disqualified"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9c272297e804878a2a4b707cfcfc6d2328b5bb936944613b4fdf2b9269afdfd"
[[package]]
name = "downcast-rs"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea8a8b81cacc08888170eef4d13b775126db426d0b348bee9d18c2c1eaf123cf"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "erased-serde"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7"
dependencies = [
"serde",
"typeid",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "glam"
version = "0.29.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee"
dependencies = [
"serde",
]
[[package]]
name = "hash32"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
dependencies = [
"byteorder",
]
[[package]]
name = "hashbrown"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
dependencies = [
"equivalent",
"serde",
]
[[package]]
name = "heapless"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
dependencies = [
"hash32",
"portable-atomic",
"stable_deref_trait",
]
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "nonmax"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "610a5acd306ec67f907abe5567859a3c693fb9886eb1f012ab8f2a47bef3db51"
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "portable-atomic"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
dependencies = [
"critical-section",
]
[[package]]
name = "portable-atomic-util"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
dependencies = [
"portable-atomic",
]
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "redox_syscall"
version = "0.5.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af"
dependencies = [
"bitflags",
]
[[package]]
name = "rustversion"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "smallvec"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9"
[[package]]
name = "smol_str"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
dependencies = [
"serde",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"portable-atomic",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
[[package]]
name = "toml_edit"
version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
"indexmap",
"toml_datetime",
"winnow",
]
[[package]]
name = "typeid"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "uuid"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
dependencies = [
"getrandom",
"js-sys",
"serde",
"wasm-bindgen",
]
[[package]]
name = "variadics_please"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b6d82be61465f97d42bd1d15bf20f3b0a3a0905018f38f9d6f6962055b0b5c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wgpu-types"
version = "24.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c"
dependencies = [
"bitflags",
"js-sys",
"log",
"serde",
"web-sys",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]

131
vendor/bevy_state/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,131 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2024"
name = "bevy_state"
version = "0.16.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Finite state machines for Bevy"
homepage = "https://bevyengine.org"
readme = false
keywords = ["bevy"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/bevyengine/bevy"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["-Zunstable-options"]
[features]
bevy_app = ["dep:bevy_app"]
bevy_reflect = [
"dep:bevy_reflect",
"bevy_ecs/bevy_reflect",
"bevy_app?/bevy_reflect",
]
critical-section = [
"bevy_ecs/critical-section",
"bevy_utils/critical-section",
"bevy_app?/critical-section",
"bevy_reflect?/critical-section",
"bevy_platform/critical-section",
]
default = [
"std",
"bevy_reflect",
"bevy_app",
]
std = [
"bevy_ecs/std",
"bevy_utils/std",
"bevy_reflect?/std",
"bevy_app?/std",
"bevy_platform/std",
]
[lib]
name = "bevy_state"
path = "src/lib.rs"
[dependencies.bevy_app]
version = "0.16.1"
optional = true
default-features = false
[dependencies.bevy_ecs]
version = "0.16.1"
default-features = false
[dependencies.bevy_platform]
version = "0.16.1"
default-features = false
[dependencies.bevy_reflect]
version = "0.16.1"
optional = true
default-features = false
[dependencies.bevy_state_macros]
version = "0.16.1"
[dependencies.bevy_utils]
version = "0.16.1"
default-features = false
[dependencies.log]
version = "0.4"
default-features = false
[dependencies.variadics_please]
version = "1.1"
[lints.clippy]
alloc_instead_of_core = "warn"
allow_attributes = "warn"
allow_attributes_without_reason = "warn"
doc_markdown = "warn"
manual_let_else = "warn"
match_same_arms = "warn"
needless_lifetimes = "allow"
nonstandard_macro_braces = "warn"
print_stderr = "warn"
print_stdout = "warn"
ptr_as_ptr = "warn"
ptr_cast_constness = "warn"
redundant_closure_for_method_calls = "warn"
redundant_else = "warn"
ref_as_ptr = "warn"
semicolon_if_nothing_returned = "warn"
std_instead_of_alloc = "warn"
std_instead_of_core = "warn"
too_long_first_doc_paragraph = "allow"
too_many_arguments = "allow"
type_complexity = "allow"
undocumented_unsafe_blocks = "warn"
unwrap_or_default = "warn"
[lints.rust]
missing_docs = "warn"
unsafe_code = "deny"
unsafe_op_in_unsafe_fn = "warn"
unused_qualifications = "warn"
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = ["cfg(docsrs_dep)"]

176
vendor/bevy_state/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

19
vendor/bevy_state/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

375
vendor/bevy_state/src/app.rs vendored Normal file
View File

@@ -0,0 +1,375 @@
use bevy_app::{App, MainScheduleOrder, Plugin, PreStartup, PreUpdate, SubApp};
use bevy_ecs::{event::Events, schedule::IntoScheduleConfigs, world::FromWorld};
use bevy_utils::once;
use log::warn;
use crate::{
state::{
setup_state_transitions_in_world, ComputedStates, FreelyMutableState, NextState, State,
StateTransition, StateTransitionEvent, StateTransitionSteps, States, SubStates,
},
state_scoped::clear_state_scoped_entities,
};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{FromReflect, GetTypeRegistration, Typed};
/// State installation methods for [`App`] and [`SubApp`].
pub trait AppExtStates {
/// Initializes a [`State`] with standard starting values.
///
/// This method is idempotent: it has no effect when called again using the same generic type.
///
/// Adds [`State<S>`] and [`NextState<S>`] resources, and enables use of the [`OnEnter`](crate::state::OnEnter),
/// [`OnTransition`](crate::state::OnTransition) and [`OnExit`](crate::state::OnExit) schedules.
/// These schedules are triggered before [`Update`](bevy_app::Update) and at startup.
///
/// If you would like to control how other systems run based on the current state, you can
/// emulate this behavior using the [`in_state`](crate::condition::in_state) [`Condition`](bevy_ecs::prelude::Condition).
///
/// Note that you can also apply state transitions at other points in the schedule
/// by triggering the [`StateTransition`](struct@StateTransition) schedule manually.
///
/// The use of any states requires the presence of [`StatesPlugin`] (which is included in `DefaultPlugins`).
fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self;
/// Inserts a specific [`State`] to the current [`App`] and overrides any [`State`] previously
/// added of the same type.
///
/// Adds [`State<S>`] and [`NextState<S>`] resources, and enables use of the [`OnEnter`](crate::state::OnEnter),
/// [`OnTransition`](crate::state::OnTransition) and [`OnExit`](crate::state::OnExit) schedules.
/// These schedules are triggered before [`Update`](bevy_app::Update) and at startup.
///
/// If you would like to control how other systems run based on the current state, you can
/// emulate this behavior using the [`in_state`](crate::condition::in_state) [`Condition`](bevy_ecs::prelude::Condition).
///
/// Note that you can also apply state transitions at other points in the schedule
/// by triggering the [`StateTransition`](struct@StateTransition) schedule manually.
fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self;
/// Sets up a type implementing [`ComputedStates`].
///
/// This method is idempotent: it has no effect when called again using the same generic type.
fn add_computed_state<S: ComputedStates>(&mut self) -> &mut Self;
/// Sets up a type implementing [`SubStates`].
///
/// This method is idempotent: it has no effect when called again using the same generic type.
fn add_sub_state<S: SubStates>(&mut self) -> &mut Self;
/// Enable state-scoped entity clearing for state `S`.
///
/// If the [`States`] trait was derived with the `#[states(scoped_entities)]` attribute, it
/// will be called automatically.
///
/// For more information refer to [`StateScoped`](crate::state_scoped::StateScoped).
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self;
#[cfg(feature = "bevy_reflect")]
/// Registers the state type `T` using [`App::register_type`],
/// and adds [`ReflectState`](crate::reflect::ReflectState) type data to `T` in the type registry.
///
/// This enables reflection code to access the state. For detailed information, see the docs on [`crate::reflect::ReflectState`] .
fn register_type_state<S>(&mut self) -> &mut Self
where
S: States + FromReflect + GetTypeRegistration + Typed;
#[cfg(feature = "bevy_reflect")]
/// Registers the state type `T` using [`App::register_type`],
/// and adds [`crate::reflect::ReflectState`] and [`crate::reflect::ReflectFreelyMutableState`] type data to `T` in the type registry.
///
/// This enables reflection code to access and modify the state.
/// For detailed information, see the docs on [`crate::reflect::ReflectState`] and [`crate::reflect::ReflectFreelyMutableState`].
fn register_type_mutable_state<S>(&mut self) -> &mut Self
where
S: FreelyMutableState + FromReflect + GetTypeRegistration + Typed;
}
/// Separate function to only warn once for all state installation methods.
fn warn_if_no_states_plugin_installed(app: &SubApp) {
if !app.is_plugin_added::<StatesPlugin>() {
once!(warn!(
"States were added to the app, but `StatesPlugin` is not installed."
));
}
}
impl AppExtStates for SubApp {
fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self {
warn_if_no_states_plugin_installed(self);
if !self.world().contains_resource::<State<S>>() {
self.init_resource::<State<S>>()
.init_resource::<NextState<S>>()
.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).expect(
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling init_state?"
);
S::register_state(schedule);
let state = self.world().resource::<State<S>>().get().clone();
self.world_mut().send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
if S::SCOPED_ENTITIES_ENABLED {
self.enable_state_scoped_entities::<S>();
}
} else {
let name = core::any::type_name::<S>();
warn!("State {} is already initialized.", name);
}
self
}
fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self {
warn_if_no_states_plugin_installed(self);
if !self.world().contains_resource::<State<S>>() {
self.insert_resource::<State<S>>(State::new(state.clone()))
.init_resource::<NextState<S>>()
.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).expect(
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling insert_state?"
);
S::register_state(schedule);
self.world_mut().send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
if S::SCOPED_ENTITIES_ENABLED {
self.enable_state_scoped_entities::<S>();
}
} else {
// Overwrite previous state and initial event
self.insert_resource::<State<S>>(State::new(state.clone()));
self.world_mut()
.resource_mut::<Events<StateTransitionEvent<S>>>()
.clear();
self.world_mut().send_event(StateTransitionEvent {
exited: None,
entered: Some(state),
});
}
self
}
fn add_computed_state<S: ComputedStates>(&mut self) -> &mut Self {
warn_if_no_states_plugin_installed(self);
if !self
.world()
.contains_resource::<Events<StateTransitionEvent<S>>>()
{
self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).expect(
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling add_computed_state?"
);
S::register_computed_state_systems(schedule);
let state = self
.world()
.get_resource::<State<S>>()
.map(|s| s.get().clone());
self.world_mut().send_event(StateTransitionEvent {
exited: None,
entered: state,
});
if S::SCOPED_ENTITIES_ENABLED {
self.enable_state_scoped_entities::<S>();
}
} else {
let name = core::any::type_name::<S>();
warn!("Computed state {} is already initialized.", name);
}
self
}
fn add_sub_state<S: SubStates>(&mut self) -> &mut Self {
warn_if_no_states_plugin_installed(self);
if !self
.world()
.contains_resource::<Events<StateTransitionEvent<S>>>()
{
self.init_resource::<NextState<S>>();
self.add_event::<StateTransitionEvent<S>>();
let schedule = self.get_schedule_mut(StateTransition).expect(
"The `StateTransition` schedule is missing. Did you forget to add StatesPlugin or DefaultPlugins before calling add_sub_state?"
);
S::register_sub_state_systems(schedule);
let state = self
.world()
.get_resource::<State<S>>()
.map(|s| s.get().clone());
self.world_mut().send_event(StateTransitionEvent {
exited: None,
entered: state,
});
if S::SCOPED_ENTITIES_ENABLED {
self.enable_state_scoped_entities::<S>();
}
} else {
let name = core::any::type_name::<S>();
warn!("Sub state {} is already initialized.", name);
}
self
}
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
if !self
.world()
.contains_resource::<Events<StateTransitionEvent<S>>>()
{
let name = core::any::type_name::<S>();
warn!("State scoped entities are enabled for state `{}`, but the state isn't installed in the app!", name);
}
// We work with [`StateTransition`] in set [`StateTransitionSteps::ExitSchedules`] as opposed to [`OnExit`],
// because [`OnExit`] only runs for one specific variant of the state.
self.add_systems(
StateTransition,
clear_state_scoped_entities::<S>.in_set(StateTransitionSteps::ExitSchedules),
)
}
#[cfg(feature = "bevy_reflect")]
fn register_type_state<S>(&mut self) -> &mut Self
where
S: States + FromReflect + GetTypeRegistration + Typed,
{
self.register_type::<S>();
self.register_type::<State<S>>();
self.register_type_data::<S, crate::reflect::ReflectState>();
self
}
#[cfg(feature = "bevy_reflect")]
fn register_type_mutable_state<S>(&mut self) -> &mut Self
where
S: FreelyMutableState + FromReflect + GetTypeRegistration + Typed,
{
self.register_type::<S>();
self.register_type::<State<S>>();
self.register_type::<NextState<S>>();
self.register_type_data::<S, crate::reflect::ReflectState>();
self.register_type_data::<S, crate::reflect::ReflectFreelyMutableState>();
self
}
}
impl AppExtStates for App {
fn init_state<S: FreelyMutableState + FromWorld>(&mut self) -> &mut Self {
self.main_mut().init_state::<S>();
self
}
fn insert_state<S: FreelyMutableState>(&mut self, state: S) -> &mut Self {
self.main_mut().insert_state::<S>(state);
self
}
fn add_computed_state<S: ComputedStates>(&mut self) -> &mut Self {
self.main_mut().add_computed_state::<S>();
self
}
fn add_sub_state<S: SubStates>(&mut self) -> &mut Self {
self.main_mut().add_sub_state::<S>();
self
}
fn enable_state_scoped_entities<S: States>(&mut self) -> &mut Self {
self.main_mut().enable_state_scoped_entities::<S>();
self
}
#[cfg(feature = "bevy_reflect")]
fn register_type_state<S>(&mut self) -> &mut Self
where
S: States + FromReflect + GetTypeRegistration + Typed,
{
self.main_mut().register_type_state::<S>();
self
}
#[cfg(feature = "bevy_reflect")]
fn register_type_mutable_state<S>(&mut self) -> &mut Self
where
S: FreelyMutableState + FromReflect + GetTypeRegistration + Typed,
{
self.main_mut().register_type_mutable_state::<S>();
self
}
}
/// Registers the [`StateTransition`] schedule in the [`MainScheduleOrder`] to enable state processing.
#[derive(Default)]
pub struct StatesPlugin;
impl Plugin for StatesPlugin {
fn build(&self, app: &mut App) {
let mut schedule = app.world_mut().resource_mut::<MainScheduleOrder>();
schedule.insert_after(PreUpdate, StateTransition);
schedule.insert_startup_before(PreStartup, StateTransition);
setup_state_transitions_in_world(app.world_mut());
}
}
#[cfg(test)]
mod tests {
use crate::{
app::StatesPlugin,
state::{State, StateTransition, StateTransitionEvent},
};
use bevy_app::App;
use bevy_ecs::event::Events;
use bevy_state_macros::States;
use super::AppExtStates;
#[derive(States, Default, PartialEq, Eq, Hash, Debug, Clone)]
enum TestState {
#[default]
A,
B,
C,
}
#[test]
fn insert_state_can_overwrite_init_state() {
let mut app = App::new();
app.add_plugins(StatesPlugin);
app.init_state::<TestState>();
app.insert_state(TestState::B);
let world = app.world_mut();
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<TestState>>().0, TestState::B);
let events = world.resource::<Events<StateTransitionEvent<TestState>>>();
assert_eq!(events.len(), 1);
let mut reader = events.get_cursor();
let last = reader.read(events).last().unwrap();
assert_eq!(last.exited, None);
assert_eq!(last.entered, Some(TestState::B));
}
#[test]
fn insert_state_can_overwrite_insert_state() {
let mut app = App::new();
app.add_plugins(StatesPlugin);
app.insert_state(TestState::B);
app.insert_state(TestState::C);
let world = app.world_mut();
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<TestState>>().0, TestState::C);
let events = world.resource::<Events<StateTransitionEvent<TestState>>>();
assert_eq!(events.len(), 1);
let mut reader = events.get_cursor();
let last = reader.read(events).last().unwrap();
assert_eq!(last.exited, None);
assert_eq!(last.entered, Some(TestState::C));
}
}

30
vendor/bevy_state/src/commands.rs vendored Normal file
View File

@@ -0,0 +1,30 @@
use bevy_ecs::{system::Commands, world::World};
use log::debug;
use crate::state::{FreelyMutableState, NextState};
/// Extension trait for [`Commands`] adding `bevy_state` helpers.
pub trait CommandsStatesExt {
/// Sets the next state the app should move to.
///
/// Internally this schedules a command that updates the [`NextState<S>`](crate::prelude::NextState)
/// resource with `state`.
///
/// Note that commands introduce sync points to the ECS schedule, so modifying `NextState`
/// directly may be more efficient depending on your use-case.
fn set_state<S: FreelyMutableState>(&mut self, state: S);
}
impl CommandsStatesExt for Commands<'_, '_> {
fn set_state<S: FreelyMutableState>(&mut self, state: S) {
self.queue(move |w: &mut World| {
let mut next = w.resource_mut::<NextState<S>>();
if let NextState::Pending(prev) = &*next {
if *prev != state {
debug!("overwriting next state {:?} with {:?}", prev, state);
}
}
next.set(state);
});
}
}

198
vendor/bevy_state/src/condition.rs vendored Normal file
View File

@@ -0,0 +1,198 @@
use crate::state::{State, States};
use bevy_ecs::{change_detection::DetectChanges, system::Res};
/// A [`Condition`](bevy_ecs::prelude::Condition)-satisfying system that returns `true`
/// if the state machine exists.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
/// # use bevy_app::{App, Update};
/// # use bevy_state::app::StatesPlugin;
/// # #[derive(Resource, Default)]
/// # struct Counter(u8);
/// # let mut app = App::new();
/// # app
/// # .init_resource::<Counter>()
/// # .add_plugins(StatesPlugin);
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
/// enum GameState {
/// #[default]
/// Playing,
/// Paused,
/// }
///
/// app.add_systems(Update,
/// // `state_exists` will only return true if the
/// // given state exists
/// my_system.run_if(state_exists::<GameState>),
/// );
///
/// fn my_system(mut counter: ResMut<Counter>) {
/// counter.0 += 1;
/// }
///
/// // `GameState` does not yet exist so `my_system` won't run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 0);
///
/// app.init_state::<GameState>();
///
/// // `GameState` now exists so `my_system` will run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 1);
/// ```
pub fn state_exists<S: States>(current_state: Option<Res<State<S>>>) -> bool {
current_state.is_some()
}
/// Generates a [`Condition`](bevy_ecs::prelude::Condition)-satisfying closure that returns `true`
/// if the state machine is currently in `state`.
///
/// Will return `false` if the state does not exist or if not in `state`.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
/// # use bevy_app::{App, Update};
/// # use bevy_state::app::StatesPlugin;
/// # #[derive(Resource, Default)]
/// # struct Counter(u8);
/// # let mut app = App::new();
/// # app
/// # .init_resource::<Counter>()
/// # .add_plugins(StatesPlugin);
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
/// enum GameState {
/// #[default]
/// Playing,
/// Paused,
/// }
///
/// app
/// .init_state::<GameState>()
/// .add_systems(Update, (
/// // `in_state` will only return true if the
/// // given state equals the given value
/// play_system.run_if(in_state(GameState::Playing)),
/// pause_system.run_if(in_state(GameState::Paused)),
/// ));
///
/// fn play_system(mut counter: ResMut<Counter>) {
/// counter.0 += 1;
/// }
///
/// fn pause_system(mut counter: ResMut<Counter>) {
/// counter.0 -= 1;
/// }
///
/// // We default to `GameState::Playing` so `play_system` runs
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 1);
///
/// app.insert_state(GameState::Paused);
///
/// // Now that we are in `GameState::Pause`, `pause_system` will run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 0);
/// ```
pub fn in_state<S: States>(state: S) -> impl FnMut(Option<Res<State<S>>>) -> bool + Clone {
move |current_state: Option<Res<State<S>>>| match current_state {
Some(current_state) => *current_state == state,
None => false,
}
}
/// A [`Condition`](bevy_ecs::prelude::Condition)-satisfying system that returns `true`
/// if the state machine changed state.
///
/// To do things on transitions to/from specific states, use their respective OnEnter/OnExit
/// schedules. Use this run condition if you want to detect any change, regardless of the value.
///
/// Returns false if the state does not exist or the state has not changed.
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
/// # use bevy_state::app::StatesPlugin;
/// # use bevy_app::{App, Update};
/// # #[derive(Resource, Default)]
/// # struct Counter(u8);
/// # let mut app = App::new();
/// # app
/// # .init_resource::<Counter>()
/// # .add_plugins(StatesPlugin);
/// #[derive(States, Clone, Copy, Default, Eq, PartialEq, Hash, Debug)]
/// enum GameState {
/// #[default]
/// Playing,
/// Paused,
/// }
///
/// app
/// .init_state::<GameState>()
/// .add_systems(Update,
/// // `state_changed` will only return true if the
/// // given states value has just been updated or
/// // the state has just been added
/// my_system.run_if(state_changed::<GameState>),
/// );
///
/// fn my_system(mut counter: ResMut<Counter>) {
/// counter.0 += 1;
/// }
///
/// // `GameState` has just been added so `my_system` will run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 1);
///
/// // `GameState` has not been updated so `my_system` will not run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 1);
///
/// app.insert_state(GameState::Paused);
///
/// // Now that `GameState` has been updated `my_system` will run
/// app.update();
/// assert_eq!(app.world().resource::<Counter>().0, 2);
/// ```
pub fn state_changed<S: States>(current_state: Option<Res<State<S>>>) -> bool {
let Some(current_state) = current_state else {
return false;
};
current_state.is_changed()
}
#[cfg(test)]
mod tests {
use bevy_ecs::schedule::{Condition, IntoScheduleConfigs, Schedule};
use crate::prelude::*;
use bevy_state_macros::States;
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
enum TestState {
#[default]
A,
B,
}
fn test_system() {}
// Ensure distributive_run_if compiles with the common conditions.
#[test]
fn distributive_run_if_compiles() {
Schedule::default().add_systems(
(test_system, test_system)
.distributive_run_if(state_exists::<TestState>)
.distributive_run_if(in_state(TestState::A).or(in_state(TestState::B)))
.distributive_run_if(state_changed::<TestState>),
);
}
}

94
vendor/bevy_state/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
#![no_std]
//! In Bevy, states are app-wide interdependent, finite state machines that are generally used to model the large scale structure of your program: whether a game is paused, if the player is in combat, if assets are loaded and so on.
//!
//! This module provides 3 distinct types of state, all of which implement the [`States`](state::States) trait:
//!
//! - Standard [`States`](state::States) can only be changed by manually setting the [`NextState<S>`](state::NextState) resource.
//! These states are the baseline on which the other state types are built, and can be used on
//! their own for many simple patterns. See the [states example](https://github.com/bevyengine/bevy/blob/latest/examples/state/states.rs)
//! for a simple use case.
//! - [`SubStates`](state::SubStates) are children of other states - they can be changed manually using [`NextState<S>`](state::NextState),
//! but are removed from the [`World`](bevy_ecs::prelude::World) if the source states aren't in the right state. See the [sub_states example](https://github.com/bevyengine/bevy/blob/latest/examples/state/sub_states.rs)
//! for a simple use case based on the derive macro, or read the trait docs for more complex scenarios.
//! - [`ComputedStates`](state::ComputedStates) are fully derived from other states - they provide a [`compute`](state::ComputedStates::compute) method
//! that takes in the source states and returns their derived value. They are particularly useful for situations
//! where a simplified view of the source states is necessary - such as having an `InAMenu` computed state, derived
//! from a source state that defines multiple distinct menus. See the [computed state example](https://github.com/bevyengine/bevy/blob/latest/examples/state/computed_states.rs)
//! to see usage samples for these states.
//!
//! Most of the utilities around state involve running systems during transitions between states, or
//! determining whether to run certain systems, though they can be used more directly as well. This
//! makes it easier to transition between menus, add loading screens, pause games, and more.
//!
//! Specifically, Bevy provides the following utilities:
//!
//! - 3 Transition Schedules - [`OnEnter<S>`](crate::state::OnEnter), [`OnExit<S>`](crate::state::OnExit) and [`OnTransition<S>`](crate::state::OnTransition) - which are used
//! to trigger systems specifically during matching transitions.
//! - A [`StateTransitionEvent<S>`](crate::state::StateTransitionEvent) that gets fired when a given state changes.
//! - The [`in_state<S>`](crate::condition::in_state) and [`state_changed<S>`](crate::condition::state_changed) run conditions - which are used
//! to determine whether a system should run based on the current state.
#![cfg_attr(
any(docsrs, docsrs_dep),
expect(
internal_features,
reason = "rustdoc_internals is needed for fake_variadic"
)
)]
#![cfg_attr(any(docsrs, docsrs_dep), feature(rustdoc_internals))]
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
// Required to make proc macros work in bevy itself.
extern crate self as bevy_state;
#[cfg(feature = "bevy_app")]
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with state installation methods
pub mod app;
/// Provides extension methods for [`Commands`](bevy_ecs::prelude::Commands).
pub mod commands;
/// Provides definitions for the runtime conditions that interact with the state system
pub mod condition;
/// Provides definitions for the basic traits required by the state system
pub mod state;
/// Provides [`StateScoped`](crate::state_scoped::StateScoped) and
/// [`clear_state_scoped_entities`](crate::state_scoped::clear_state_scoped_entities) for managing lifetime of entities.
pub mod state_scoped;
#[cfg(feature = "bevy_app")]
/// Provides [`App`](bevy_app::App) and [`SubApp`](bevy_app::SubApp) with methods for registering
/// state-scoped events.
pub mod state_scoped_events;
#[cfg(feature = "bevy_reflect")]
/// Provides definitions for the basic traits required by the state system
pub mod reflect;
/// The state prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[cfg(feature = "bevy_app")]
#[doc(hidden)]
pub use crate::{app::AppExtStates, state_scoped_events::StateScopedEventsAppExt};
#[cfg(feature = "bevy_reflect")]
#[doc(hidden)]
pub use crate::reflect::{ReflectFreelyMutableState, ReflectState};
#[doc(hidden)]
pub use crate::{
commands::CommandsStatesExt,
condition::*,
state::{
last_transition, ComputedStates, EnterSchedules, ExitSchedules, NextState, OnEnter,
OnExit, OnTransition, State, StateSet, StateTransition, StateTransitionEvent, States,
SubStates, TransitionSchedules,
},
state_scoped::StateScoped,
};
}

165
vendor/bevy_state/src/reflect.rs vendored Normal file
View File

@@ -0,0 +1,165 @@
use crate::state::{FreelyMutableState, NextState, State, States};
use bevy_ecs::{reflect::from_reflect_with_fallback, world::World};
use bevy_reflect::{FromType, Reflect, TypePath, TypeRegistry};
/// A struct used to operate on the reflected [`States`] trait of a type.
///
/// A [`ReflectState`] for type `T` can be obtained via
/// [`bevy_reflect::TypeRegistration::data`].
#[derive(Clone)]
pub struct ReflectState(ReflectStateFns);
/// The raw function pointers needed to make up a [`ReflectState`].
#[derive(Clone)]
pub struct ReflectStateFns {
/// Function pointer implementing [`ReflectState::reflect()`].
pub reflect: fn(&World) -> Option<&dyn Reflect>,
}
impl ReflectStateFns {
/// Get the default set of [`ReflectStateFns`] for a specific component type using its
/// [`FromType`] implementation.
///
/// This is useful if you want to start with the default implementation before overriding some
/// of the functions to create a custom implementation.
pub fn new<T: States + Reflect>() -> Self {
<ReflectState as FromType<T>>::from_type().0
}
}
impl ReflectState {
/// Gets the value of this [`States`] type from the world as a reflected reference.
pub fn reflect<'a>(&self, world: &'a World) -> Option<&'a dyn Reflect> {
(self.0.reflect)(world)
}
}
impl<S: States + Reflect> FromType<S> for ReflectState {
fn from_type() -> Self {
ReflectState(ReflectStateFns {
reflect: |world| {
world
.get_resource::<State<S>>()
.map(|res| res.get() as &dyn Reflect)
},
})
}
}
/// A struct used to operate on the reflected [`FreelyMutableState`] trait of a type.
///
/// A [`ReflectFreelyMutableState`] for type `T` can be obtained via
/// [`bevy_reflect::TypeRegistration::data`].
#[derive(Clone)]
pub struct ReflectFreelyMutableState(ReflectFreelyMutableStateFns);
/// The raw function pointers needed to make up a [`ReflectFreelyMutableState`].
#[derive(Clone)]
pub struct ReflectFreelyMutableStateFns {
/// Function pointer implementing [`ReflectFreelyMutableState::set_next_state()`].
pub set_next_state: fn(&mut World, &dyn Reflect, &TypeRegistry),
}
impl ReflectFreelyMutableStateFns {
/// Get the default set of [`ReflectFreelyMutableStateFns`] for a specific component type using its
/// [`FromType`] implementation.
///
/// This is useful if you want to start with the default implementation before overriding some
/// of the functions to create a custom implementation.
pub fn new<T: FreelyMutableState + Reflect + TypePath>() -> Self {
<ReflectFreelyMutableState as FromType<T>>::from_type().0
}
}
impl ReflectFreelyMutableState {
/// Tentatively set a pending state transition to a reflected [`ReflectFreelyMutableState`].
pub fn set_next_state(&self, world: &mut World, state: &dyn Reflect, registry: &TypeRegistry) {
(self.0.set_next_state)(world, state, registry);
}
}
impl<S: FreelyMutableState + Reflect + TypePath> FromType<S> for ReflectFreelyMutableState {
fn from_type() -> Self {
ReflectFreelyMutableState(ReflectFreelyMutableStateFns {
set_next_state: |world, reflected_state, registry| {
let new_state: S = from_reflect_with_fallback(
reflected_state.as_partial_reflect(),
world,
registry,
);
if let Some(mut next_state) = world.get_resource_mut::<NextState<S>>() {
next_state.set(new_state);
}
},
})
}
}
#[cfg(test)]
mod tests {
use crate::{
app::{AppExtStates, StatesPlugin},
reflect::{ReflectFreelyMutableState, ReflectState},
state::State,
};
use bevy_app::App;
use bevy_ecs::prelude::AppTypeRegistry;
use bevy_reflect::Reflect;
use bevy_state_macros::States;
use core::any::TypeId;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, States, Reflect)]
enum StateTest {
A,
B,
}
#[test]
fn test_reflect_state_operations() {
let mut app = App::new();
app.add_plugins(StatesPlugin)
.insert_state(StateTest::A)
.register_type_mutable_state::<StateTest>();
let type_registry = app.world_mut().resource::<AppTypeRegistry>().0.clone();
let type_registry = type_registry.read();
let (reflect_state, reflect_mutable_state) = (
type_registry
.get_type_data::<ReflectState>(TypeId::of::<StateTest>())
.unwrap()
.clone(),
type_registry
.get_type_data::<ReflectFreelyMutableState>(TypeId::of::<StateTest>())
.unwrap()
.clone(),
);
let current_value = reflect_state.reflect(app.world()).unwrap();
assert_eq!(
current_value.downcast_ref::<StateTest>().unwrap(),
&StateTest::A
);
reflect_mutable_state.set_next_state(app.world_mut(), &StateTest::B, &type_registry);
assert_ne!(
app.world().resource::<State<StateTest>>().get(),
&StateTest::B
);
app.update();
assert_eq!(
app.world().resource::<State<StateTest>>().get(),
&StateTest::B
);
let current_value = reflect_state.reflect(app.world()).unwrap();
assert_eq!(
current_value.downcast_ref::<StateTest>().unwrap(),
&StateTest::B
);
}
}

View File

@@ -0,0 +1,95 @@
use core::{fmt::Debug, hash::Hash};
use bevy_ecs::schedule::Schedule;
use super::{state_set::StateSet, states::States};
/// A state whose value is automatically computed based on the values of other [`States`].
///
/// A **computed state** is a state that is deterministically derived from a set of `SourceStates`.
/// The [`StateSet`] is passed into the `compute` method whenever one of them changes, and the
/// result becomes the state's value.
///
/// ```
/// # use bevy_state::prelude::*;
/// # use bevy_ecs::prelude::*;
///
/// /// Computed States require some state to derive from
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// enum AppState {
/// #[default]
/// Menu,
/// InGame { paused: bool }
/// }
///
///
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
/// struct InGame;
///
/// impl ComputedStates for InGame {
/// /// We set the source state to be the state, or a tuple of states,
/// /// we want to depend on. You can also wrap each state in an Option,
/// /// if you want the computed state to execute even if the state doesn't
/// /// currently exist in the world.
/// type SourceStates = AppState;
///
/// /// We then define the compute function, which takes in
/// /// your SourceStates
/// fn compute(sources: AppState) -> Option<Self> {
/// match sources {
/// /// When we are in game, we want to return the InGame state
/// AppState::InGame { .. } => Some(InGame),
/// /// Otherwise, we don't want the `State<InGame>` resource to exist,
/// /// so we return None.
/// _ => None
/// }
/// }
/// }
/// ```
///
/// you can then add it to an App, and from there you use the state as normal
///
/// ```
/// # use bevy_state::prelude::*;
/// # use bevy_ecs::prelude::*;
///
/// # struct App;
/// # impl App {
/// # fn new() -> Self { App }
/// # fn init_state<S>(&mut self) -> &mut Self {self}
/// # fn add_computed_state<S>(&mut self) -> &mut Self {self}
/// # }
/// # struct AppState;
/// # struct InGame;
///
/// App::new()
/// .init_state::<AppState>()
/// .add_computed_state::<InGame>();
/// ```
pub trait ComputedStates: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {
/// The set of states from which the [`Self`] is derived.
///
/// This can either be a single type that implements [`States`], an Option of a type
/// that implements [`States`], or a tuple
/// containing multiple types that implement [`States`] or Optional versions of them.
///
/// For example, `(MapState, EnemyState)` is valid, as is `(MapState, Option<EnemyState>)`
type SourceStates: StateSet;
/// Computes the next value of [`State<Self>`](crate::state::State).
/// This function gets called whenever one of the [`SourceStates`](Self::SourceStates) changes.
///
/// If the result is [`None`], the [`State<Self>`](crate::state::State) resource will be removed from the world.
fn compute(sources: Self::SourceStates) -> Option<Self>;
/// This function sets up systems that compute the state whenever one of the [`SourceStates`](Self::SourceStates)
/// change. It is called by `App::add_computed_state`, but can be called manually if `App` is not
/// used.
fn register_computed_state_systems(schedule: &mut Schedule) {
Self::SourceStates::register_computed_state_systems_in_schedule::<Self>(schedule);
}
}
impl<S: ComputedStates> States for S {
const DEPENDENCY_DEPTH: usize = S::SourceStates::SET_DEPENDENCY_DEPTH + 1;
}

View File

@@ -0,0 +1,62 @@
use bevy_ecs::{
event::EventWriter,
prelude::Schedule,
schedule::IntoScheduleConfigs,
system::{Commands, IntoSystem, ResMut},
};
use super::{states::States, take_next_state, transitions::*, NextState, State};
/// This trait allows a state to be mutated directly using the [`NextState<S>`](crate::state::NextState) resource.
///
/// While ordinary states are freely mutable (and implement this trait as part of their derive macro),
/// computed states are not: instead, they can *only* change when the states that drive them do.
#[diagnostic::on_unimplemented(note = "consider annotating `{Self}` with `#[derive(States)]`")]
pub trait FreelyMutableState: States {
/// This function registers all the necessary systems to apply state changes and run transition schedules
fn register_state(schedule: &mut Schedule) {
schedule.configure_sets((
ApplyStateTransition::<Self>::default()
.in_set(StateTransitionSteps::DependentTransitions),
ExitSchedules::<Self>::default().in_set(StateTransitionSteps::ExitSchedules),
TransitionSchedules::<Self>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<Self>::default().in_set(StateTransitionSteps::EnterSchedules),
));
schedule
.add_systems(
apply_state_transition::<Self>.in_set(ApplyStateTransition::<Self>::default()),
)
.add_systems(
last_transition::<Self>
.pipe(run_exit::<Self>)
.in_set(ExitSchedules::<Self>::default()),
)
.add_systems(
last_transition::<Self>
.pipe(run_transition::<Self>)
.in_set(TransitionSchedules::<Self>::default()),
)
.add_systems(
last_transition::<Self>
.pipe(run_enter::<Self>)
.in_set(EnterSchedules::<Self>::default()),
);
}
}
fn apply_state_transition<S: FreelyMutableState>(
event: EventWriter<StateTransitionEvent<S>>,
commands: Commands,
current_state: Option<ResMut<State<S>>>,
next_state: Option<ResMut<NextState<S>>>,
) {
let Some(next_state) = take_next_state(next_state) else {
return;
};
let Some(current_state) = current_state else {
return;
};
internal_apply_state_transition(event, commands, Some(current_state), Some(next_state));
}

727
vendor/bevy_state/src/state/mod.rs vendored Normal file
View File

@@ -0,0 +1,727 @@
mod computed_states;
mod freely_mutable_state;
mod resources;
mod state_set;
mod states;
mod sub_states;
mod transitions;
pub use bevy_state_macros::*;
pub use computed_states::*;
pub use freely_mutable_state::*;
pub use resources::*;
pub use state_set::*;
pub use states::*;
pub use sub_states::*;
pub use transitions::*;
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use bevy_ecs::{event::EventRegistry, prelude::*};
use bevy_state_macros::{States, SubStates};
use super::*;
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
enum SimpleState {
#[default]
A,
B(bool),
}
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
enum TestComputedState {
BisTrue,
BisFalse,
}
impl ComputedStates for TestComputedState {
type SourceStates = Option<SimpleState>;
fn compute(sources: Option<SimpleState>) -> Option<Self> {
sources.and_then(|source| match source {
SimpleState::A => None,
SimpleState::B(value) => Some(if value { Self::BisTrue } else { Self::BisFalse }),
})
}
}
#[test]
fn computed_state_with_a_single_source_is_correctly_derived() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TestComputedState>>(&mut world);
world.init_resource::<State<SimpleState>>();
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
TestComputedState::register_computed_state_systems(&mut apply_changes);
SimpleState::register_state(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<TestComputedState>>());
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(true)
);
assert_eq!(
world.resource::<State<TestComputedState>>().0,
TestComputedState::BisTrue
);
world.insert_resource(NextState::Pending(SimpleState::B(false)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(false)
);
assert_eq!(
world.resource::<State<TestComputedState>>().0,
TestComputedState::BisFalse
);
world.insert_resource(NextState::Pending(SimpleState::A));
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<TestComputedState>>());
}
#[derive(SubStates, PartialEq, Eq, Debug, Default, Hash, Clone)]
#[source(SimpleState = SimpleState::B(true))]
enum SubState {
#[default]
One,
Two,
}
#[test]
fn sub_state_exists_only_when_allowed_but_can_be_modified_freely() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
world.init_resource::<State<SimpleState>>();
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
SubState::register_sub_state_systems(&mut apply_changes);
SimpleState::register_state(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<SubState>>());
world.insert_resource(NextState::Pending(SubState::Two));
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<SubState>>());
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(true)
);
assert_eq!(world.resource::<State<SubState>>().0, SubState::One);
world.insert_resource(NextState::Pending(SubState::Two));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(true)
);
assert_eq!(world.resource::<State<SubState>>().0, SubState::Two);
world.insert_resource(NextState::Pending(SimpleState::B(false)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(false)
);
assert!(!world.contains_resource::<State<SubState>>());
}
#[derive(SubStates, PartialEq, Eq, Debug, Default, Hash, Clone)]
#[source(TestComputedState = TestComputedState::BisTrue)]
enum SubStateOfComputed {
#[default]
One,
Two,
}
#[test]
fn substate_of_computed_states_works_appropriately() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TestComputedState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SubStateOfComputed>>(&mut world);
world.init_resource::<State<SimpleState>>();
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
TestComputedState::register_computed_state_systems(&mut apply_changes);
SubStateOfComputed::register_sub_state_systems(&mut apply_changes);
SimpleState::register_state(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<SubStateOfComputed>>());
world.insert_resource(NextState::Pending(SubStateOfComputed::Two));
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(!world.contains_resource::<State<SubStateOfComputed>>());
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(true)
);
assert_eq!(
world.resource::<State<SubStateOfComputed>>().0,
SubStateOfComputed::One
);
world.insert_resource(NextState::Pending(SubStateOfComputed::Two));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(true)
);
assert_eq!(
world.resource::<State<SubStateOfComputed>>().0,
SubStateOfComputed::Two
);
world.insert_resource(NextState::Pending(SimpleState::B(false)));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<SimpleState>>().0,
SimpleState::B(false)
);
assert!(!world.contains_resource::<State<SubStateOfComputed>>());
}
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
struct OtherState {
a_flexible_value: &'static str,
another_value: u8,
}
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
enum ComplexComputedState {
InAAndStrIsBobOrJane,
InTrueBAndUsizeAbove8,
}
impl ComputedStates for ComplexComputedState {
type SourceStates = (Option<SimpleState>, Option<OtherState>);
fn compute(sources: (Option<SimpleState>, Option<OtherState>)) -> Option<Self> {
match sources {
(Some(simple), Some(complex)) => {
if simple == SimpleState::A
&& (complex.a_flexible_value == "bob" || complex.a_flexible_value == "jane")
{
Some(ComplexComputedState::InAAndStrIsBobOrJane)
} else if simple == SimpleState::B(true) && complex.another_value > 8 {
Some(ComplexComputedState::InTrueBAndUsizeAbove8)
} else {
None
}
}
_ => None,
}
}
}
#[test]
fn complex_computed_state_gets_derived_correctly() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<OtherState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<ComplexComputedState>>(&mut world);
world.init_resource::<State<SimpleState>>();
world.init_resource::<State<OtherState>>();
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
ComplexComputedState::register_computed_state_systems(&mut apply_changes);
SimpleState::register_state(&mut apply_changes);
OtherState::register_state(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert_eq!(
world.resource::<State<OtherState>>().0,
OtherState::default()
);
assert!(!world.contains_resource::<State<ComplexComputedState>>());
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert!(!world.contains_resource::<State<ComplexComputedState>>());
world.insert_resource(NextState::Pending(OtherState {
a_flexible_value: "felix",
another_value: 13,
}));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<ComplexComputedState>>().0,
ComplexComputedState::InTrueBAndUsizeAbove8
);
world.insert_resource(NextState::Pending(SimpleState::A));
world.insert_resource(NextState::Pending(OtherState {
a_flexible_value: "jane",
another_value: 13,
}));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<ComplexComputedState>>().0,
ComplexComputedState::InAAndStrIsBobOrJane
);
world.insert_resource(NextState::Pending(SimpleState::B(false)));
world.insert_resource(NextState::Pending(OtherState {
a_flexible_value: "jane",
another_value: 13,
}));
world.run_schedule(StateTransition);
assert!(!world.contains_resource::<State<ComplexComputedState>>());
}
#[derive(Resource, Default)]
struct ComputedStateTransitionCounter {
enter: usize,
exit: usize,
}
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
enum SimpleState2 {
#[default]
A1,
B2,
}
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
enum TestNewcomputedState {
A1,
B2,
B1,
}
impl ComputedStates for TestNewcomputedState {
type SourceStates = (Option<SimpleState>, Option<SimpleState2>);
fn compute((s1, s2): (Option<SimpleState>, Option<SimpleState2>)) -> Option<Self> {
match (s1, s2) {
(Some(SimpleState::A), Some(SimpleState2::A1)) => Some(TestNewcomputedState::A1),
(Some(SimpleState::B(true)), Some(SimpleState2::B2)) => {
Some(TestNewcomputedState::B2)
}
(Some(SimpleState::B(true)), _) => Some(TestNewcomputedState::B1),
_ => None,
}
}
}
#[test]
fn computed_state_transitions_are_produced_correctly() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SimpleState2>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TestNewcomputedState>>(&mut world);
world.init_resource::<State<SimpleState>>();
world.init_resource::<State<SimpleState2>>();
world.init_resource::<Schedules>();
setup_state_transitions_in_world(&mut world);
let mut schedules = world
.get_resource_mut::<Schedules>()
.expect("Schedules don't exist in world");
let apply_changes = schedules
.get_mut(StateTransition)
.expect("State Transition Schedule Doesn't Exist");
TestNewcomputedState::register_computed_state_systems(apply_changes);
SimpleState::register_state(apply_changes);
SimpleState2::register_state(apply_changes);
schedules.insert({
let mut schedule = Schedule::new(OnEnter(TestNewcomputedState::A1));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.enter += 1;
});
schedule
});
schedules.insert({
let mut schedule = Schedule::new(OnExit(TestNewcomputedState::A1));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.exit += 1;
});
schedule
});
schedules.insert({
let mut schedule = Schedule::new(OnEnter(TestNewcomputedState::B1));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.enter += 1;
});
schedule
});
schedules.insert({
let mut schedule = Schedule::new(OnExit(TestNewcomputedState::B1));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.exit += 1;
});
schedule
});
schedules.insert({
let mut schedule = Schedule::new(OnEnter(TestNewcomputedState::B2));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.enter += 1;
});
schedule
});
schedules.insert({
let mut schedule = Schedule::new(OnExit(TestNewcomputedState::B2));
schedule.add_systems(|mut count: ResMut<ComputedStateTransitionCounter>| {
count.exit += 1;
});
schedule
});
world.init_resource::<ComputedStateTransitionCounter>();
setup_state_transitions_in_world(&mut world);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert_eq!(world.resource::<State<SimpleState2>>().0, SimpleState2::A1);
assert!(!world.contains_resource::<State<TestNewcomputedState>>());
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.insert_resource(NextState::Pending(SimpleState2::B2));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<TestNewcomputedState>>().0,
TestNewcomputedState::B2
);
assert_eq!(world.resource::<ComputedStateTransitionCounter>().enter, 1);
assert_eq!(world.resource::<ComputedStateTransitionCounter>().exit, 0);
world.insert_resource(NextState::Pending(SimpleState2::A1));
world.insert_resource(NextState::Pending(SimpleState::A));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<TestNewcomputedState>>().0,
TestNewcomputedState::A1
);
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().enter,
2,
"Should Only Enter Twice"
);
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().exit,
1,
"Should Only Exit Once"
);
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.insert_resource(NextState::Pending(SimpleState2::B2));
world.run_schedule(StateTransition);
assert_eq!(
world.resource::<State<TestNewcomputedState>>().0,
TestNewcomputedState::B2
);
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().enter,
3,
"Should Only Enter Three Times"
);
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().exit,
2,
"Should Only Exit Twice"
);
world.insert_resource(NextState::Pending(SimpleState::A));
world.run_schedule(StateTransition);
assert!(!world.contains_resource::<State<TestNewcomputedState>>());
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().enter,
3,
"Should Only Enter Three Times"
);
assert_eq!(
world.resource::<ComputedStateTransitionCounter>().exit,
3,
"Should Only Exit Twice"
);
}
#[derive(Resource, Default, PartialEq, Debug)]
struct TransitionCounter {
exit: u8,
transition: u8,
enter: u8,
}
#[test]
fn same_state_transition_should_emit_event_and_not_run_schedules() {
let mut world = World::new();
setup_state_transitions_in_world(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
world.init_resource::<State<SimpleState>>();
let mut schedules = world.resource_mut::<Schedules>();
let apply_changes = schedules.get_mut(StateTransition).unwrap();
SimpleState::register_state(apply_changes);
let mut on_exit = Schedule::new(OnExit(SimpleState::A));
on_exit.add_systems(|mut c: ResMut<TransitionCounter>| c.exit += 1);
schedules.insert(on_exit);
let mut on_transition = Schedule::new(OnTransition {
exited: SimpleState::A,
entered: SimpleState::A,
});
on_transition.add_systems(|mut c: ResMut<TransitionCounter>| c.transition += 1);
schedules.insert(on_transition);
let mut on_enter = Schedule::new(OnEnter(SimpleState::A));
on_enter.add_systems(|mut c: ResMut<TransitionCounter>| c.enter += 1);
schedules.insert(on_enter);
world.insert_resource(TransitionCounter::default());
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert!(world
.resource::<Events<StateTransitionEvent<SimpleState>>>()
.is_empty());
world.insert_resource(TransitionCounter::default());
world.insert_resource(NextState::Pending(SimpleState::A));
world.run_schedule(StateTransition);
assert_eq!(world.resource::<State<SimpleState>>().0, SimpleState::A);
assert_eq!(
*world.resource::<TransitionCounter>(),
TransitionCounter {
exit: 0,
transition: 1, // Same state transitions are allowed
enter: 0
}
);
assert_eq!(
world
.resource::<Events<StateTransitionEvent<SimpleState>>>()
.len(),
1
);
}
#[test]
fn same_state_transition_should_propagate_to_sub_state() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
world.insert_resource(State(SimpleState::B(true)));
world.init_resource::<State<SubState>>();
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
SimpleState::register_state(&mut apply_changes);
SubState::register_sub_state_systems(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert_eq!(
world
.resource::<Events<StateTransitionEvent<SimpleState>>>()
.len(),
1
);
assert_eq!(
world
.resource::<Events<StateTransitionEvent<SubState>>>()
.len(),
1
);
}
#[test]
fn same_state_transition_should_propagate_to_computed_state() {
let mut world = World::new();
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TestComputedState>>(&mut world);
world.insert_resource(State(SimpleState::B(true)));
world.insert_resource(State(TestComputedState::BisTrue));
let mut schedules = Schedules::new();
let mut apply_changes = Schedule::new(StateTransition);
SimpleState::register_state(&mut apply_changes);
TestComputedState::register_computed_state_systems(&mut apply_changes);
schedules.insert(apply_changes);
world.insert_resource(schedules);
setup_state_transitions_in_world(&mut world);
world.insert_resource(NextState::Pending(SimpleState::B(true)));
world.run_schedule(StateTransition);
assert_eq!(
world
.resource::<Events<StateTransitionEvent<SimpleState>>>()
.len(),
1
);
assert_eq!(
world
.resource::<Events<StateTransitionEvent<TestComputedState>>>()
.len(),
1
);
}
#[derive(Resource, Default, Debug)]
struct TransitionTracker(Vec<&'static str>);
#[derive(PartialEq, Eq, Debug, Hash, Clone)]
enum TransitionTestingComputedState {
IsA,
IsBAndEven,
IsBAndOdd,
}
impl ComputedStates for TransitionTestingComputedState {
type SourceStates = (Option<SimpleState>, Option<SubState>);
fn compute(sources: (Option<SimpleState>, Option<SubState>)) -> Option<Self> {
match sources {
(Some(simple), sub) => {
if simple == SimpleState::A {
Some(Self::IsA)
} else if sub == Some(SubState::One) {
Some(Self::IsBAndOdd)
} else if sub == Some(SubState::Two) {
Some(Self::IsBAndEven)
} else {
None
}
}
_ => None,
}
}
}
#[test]
fn check_transition_orders() {
let mut world = World::new();
setup_state_transitions_in_world(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SimpleState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<SubState>>(&mut world);
EventRegistry::register_event::<StateTransitionEvent<TransitionTestingComputedState>>(
&mut world,
);
world.insert_resource(State(SimpleState::B(true)));
world.init_resource::<State<SubState>>();
world.insert_resource(State(TransitionTestingComputedState::IsA));
let mut schedules = world.remove_resource::<Schedules>().unwrap();
let apply_changes = schedules.get_mut(StateTransition).unwrap();
SimpleState::register_state(apply_changes);
SubState::register_sub_state_systems(apply_changes);
TransitionTestingComputedState::register_computed_state_systems(apply_changes);
world.init_resource::<TransitionTracker>();
fn register_transition(string: &'static str) -> impl Fn(ResMut<TransitionTracker>) {
move |mut transitions: ResMut<TransitionTracker>| transitions.0.push(string)
}
schedules.add_systems(
StateTransition,
register_transition("simple exit").in_set(ExitSchedules::<SimpleState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("simple transition")
.in_set(TransitionSchedules::<SimpleState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("simple enter").in_set(EnterSchedules::<SimpleState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("sub exit").in_set(ExitSchedules::<SubState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("sub transition")
.in_set(TransitionSchedules::<SubState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("sub enter").in_set(EnterSchedules::<SubState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("computed exit")
.in_set(ExitSchedules::<TransitionTestingComputedState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("computed transition")
.in_set(TransitionSchedules::<TransitionTestingComputedState>::default()),
);
schedules.add_systems(
StateTransition,
register_transition("computed enter")
.in_set(EnterSchedules::<TransitionTestingComputedState>::default()),
);
world.insert_resource(schedules);
world.run_schedule(StateTransition);
let transitions = &world.resource::<TransitionTracker>().0;
assert_eq!(transitions.len(), 9);
assert_eq!(transitions[0], "computed exit");
assert_eq!(transitions[1], "sub exit");
assert_eq!(transitions[2], "simple exit");
// Transition order is arbitrary and doesn't need testing.
assert_eq!(transitions[6], "simple enter");
assert_eq!(transitions[7], "sub enter");
assert_eq!(transitions[8], "computed enter");
}
}

156
vendor/bevy_state/src/state/resources.rs vendored Normal file
View File

@@ -0,0 +1,156 @@
use core::ops::Deref;
use bevy_ecs::{
change_detection::DetectChangesMut,
resource::Resource,
system::ResMut,
world::{FromWorld, World},
};
use super::{freely_mutable_state::FreelyMutableState, states::States};
#[cfg(feature = "bevy_reflect")]
use bevy_ecs::prelude::ReflectResource;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::ReflectDefault;
/// A finite-state machine whose transitions have associated schedules
/// ([`OnEnter(state)`](crate::state::OnEnter) and [`OnExit(state)`](crate::state::OnExit)).
///
/// The current state value can be accessed through this resource. To *change* the state,
/// queue a transition in the [`NextState<S>`] resource, and it will be applied during the
/// [`StateTransition`](crate::state::StateTransition) schedule - which by default runs after `PreUpdate`.
///
/// You can also manually trigger the [`StateTransition`](crate::state::StateTransition) schedule to apply the changes
/// at an arbitrary time.
///
/// The starting state is defined via the [`Default`] implementation for `S`.
///
/// ```
/// use bevy_state::prelude::*;
/// use bevy_ecs::prelude::*;
/// use bevy_state_macros::States;
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// enum GameState {
/// #[default]
/// MainMenu,
/// SettingsMenu,
/// InGame,
/// }
///
/// fn game_logic(game_state: Res<State<GameState>>) {
/// match game_state.get() {
/// GameState::InGame => {
/// // Run game logic here...
/// },
/// _ => {},
/// }
/// }
/// ```
#[derive(Resource, Debug)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Resource, Debug, PartialEq)
)]
pub struct State<S: States>(pub(crate) S);
impl<S: States> State<S> {
/// Creates a new state with a specific value.
///
/// To change the state use [`NextState<S>`] rather than using this to modify the `State<S>`.
pub fn new(state: S) -> Self {
Self(state)
}
/// Get the current state.
pub fn get(&self) -> &S {
&self.0
}
}
impl<S: States + FromWorld> FromWorld for State<S> {
fn from_world(world: &mut World) -> Self {
Self(S::from_world(world))
}
}
impl<S: States> PartialEq<S> for State<S> {
fn eq(&self, other: &S) -> bool {
self.get() == other
}
}
impl<S: States> Deref for State<S> {
type Target = S;
fn deref(&self) -> &Self::Target {
self.get()
}
}
/// The next state of [`State<S>`].
///
/// This can be fetched as a resource and used to queue state transitions.
/// To queue a transition, call [`NextState::set`] or mutate the value to [`NextState::Pending`] directly.
///
/// Note that these transitions can be overridden by other systems:
/// only the actual value of this resource during the [`StateTransition`](crate::state::StateTransition) schedule matters.
///
/// ```
/// use bevy_state::prelude::*;
/// use bevy_ecs::prelude::*;
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// enum GameState {
/// #[default]
/// MainMenu,
/// SettingsMenu,
/// InGame,
/// }
///
/// fn start_game(mut next_game_state: ResMut<NextState<GameState>>) {
/// next_game_state.set(GameState::InGame);
/// }
/// ```
#[derive(Resource, Debug, Default, Clone)]
#[cfg_attr(
feature = "bevy_reflect",
derive(bevy_reflect::Reflect),
reflect(Resource, Default, Debug)
)]
pub enum NextState<S: FreelyMutableState> {
/// No state transition is pending
#[default]
Unchanged,
/// There is a pending transition for state `S`
Pending(S),
}
impl<S: FreelyMutableState> NextState<S> {
/// Tentatively set a pending state transition to `Some(state)`.
pub fn set(&mut self, state: S) {
*self = Self::Pending(state);
}
/// Remove any pending changes to [`State<S>`]
pub fn reset(&mut self) {
*self = Self::Unchanged;
}
}
pub(crate) fn take_next_state<S: FreelyMutableState>(
next_state: Option<ResMut<NextState<S>>>,
) -> Option<S> {
let mut next_state = next_state?;
match core::mem::take(next_state.bypass_change_detection()) {
NextState::Pending(x) => {
next_state.set_changed();
Some(x)
}
NextState::Unchanged => None,
}
}

351
vendor/bevy_state/src/state/state_set.rs vendored Normal file
View File

@@ -0,0 +1,351 @@
use bevy_ecs::{
event::{EventReader, EventWriter},
schedule::{IntoScheduleConfigs, Schedule},
system::{Commands, IntoSystem, Res, ResMut},
};
use variadics_please::all_tuples;
use self::sealed::StateSetSealed;
use super::{
computed_states::ComputedStates, internal_apply_state_transition, last_transition, run_enter,
run_exit, run_transition, sub_states::SubStates, take_next_state, ApplyStateTransition,
EnterSchedules, ExitSchedules, NextState, State, StateTransitionEvent, StateTransitionSteps,
States, TransitionSchedules,
};
mod sealed {
/// Sealed trait used to prevent external implementations of [`StateSet`](super::StateSet).
pub trait StateSetSealed {}
}
/// A [`States`] type or tuple of types which implement [`States`].
///
/// This trait is used allow implementors of [`States`], as well
/// as tuples containing exclusively implementors of [`States`], to
/// be used as [`ComputedStates::SourceStates`].
///
/// It is sealed, and auto implemented for all [`States`] types and
/// tuples containing them.
pub trait StateSet: StateSetSealed {
/// The total [`DEPENDENCY_DEPTH`](`States::DEPENDENCY_DEPTH`) of all
/// the states that are part of this [`StateSet`], added together.
///
/// Used to de-duplicate computed state executions and prevent cyclic
/// computed states.
const SET_DEPENDENCY_DEPTH: usize;
/// Sets up the systems needed to compute `T` whenever any `State` in this
/// `StateSet` is changed.
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
schedule: &mut Schedule,
);
/// Sets up the systems needed to compute whether `T` exists whenever any `State` in this
/// `StateSet` is changed.
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
schedule: &mut Schedule,
);
}
/// The `InnerStateSet` trait is used to isolate [`ComputedStates`] & [`SubStates`] from
/// needing to wrap all state dependencies in an [`Option<S>`].
///
/// Some [`ComputedStates`]'s might need to exist in different states based on the existence
/// of other states. So we needed the ability to use[`Option<S>`] when appropriate.
///
/// The isolation works because it is implemented for both S & [`Option<S>`], and has the `RawState` associated type
/// that allows it to know what the resource in the world should be. We can then essentially "unwrap" it in our
/// `StateSet` implementation - and the behavior of that unwrapping will depend on the arguments expected by the
/// the [`ComputedStates`] & [`SubStates]`.
trait InnerStateSet: Sized {
type RawState: States;
const DEPENDENCY_DEPTH: usize;
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self>;
}
impl<S: States> InnerStateSet for S {
type RawState = Self;
const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {
wrapped.map(|v| v.0.clone())
}
}
impl<S: States> InnerStateSet for Option<S> {
type RawState = S;
const DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
fn convert_to_usable_state(wrapped: Option<&State<Self::RawState>>) -> Option<Self> {
Some(wrapped.map(|v| v.0.clone()))
}
}
impl<S: InnerStateSet> StateSetSealed for S {}
impl<S: InnerStateSet> StateSet for S {
const SET_DEPENDENCY_DEPTH: usize = S::DEPENDENCY_DEPTH;
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
schedule: &mut Schedule,
) {
let apply_state_transition =
|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,
event: EventWriter<StateTransitionEvent<T>>,
commands: Commands,
current_state: Option<ResMut<State<T>>>,
state_set: Option<Res<State<S::RawState>>>| {
if parent_changed.is_empty() {
return;
}
parent_changed.clear();
let new_state =
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
T::compute(state_set)
} else {
None
};
internal_apply_state_transition(event, commands, current_state, new_state);
};
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::default()),
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
.before(ExitSchedules::<S::RawState>::default()),
TransitionSchedules::<T>::default().in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
.after(EnterSchedules::<S::RawState>::default()),
));
schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(ExitSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(TransitionSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(EnterSchedules::<T>::default()),
);
}
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
schedule: &mut Schedule,
) {
// | parent changed | next state | already exists | should exist | what happens |
// | -------------- | ---------- | -------------- | ------------ | -------------------------------- |
// | false | false | false | - | - |
// | false | false | true | - | - |
// | false | true | false | false | - |
// | true | false | false | false | - |
// | true | true | false | false | - |
// | true | false | true | false | Some(current) -> None |
// | true | true | true | false | Some(current) -> None |
// | true | false | false | true | None -> Some(default) |
// | true | true | false | true | None -> Some(next) |
// | true | true | true | true | Some(current) -> Some(next) |
// | false | true | true | true | Some(current) -> Some(next) |
// | true | false | true | true | Some(current) -> Some(current) |
let apply_state_transition =
|mut parent_changed: EventReader<StateTransitionEvent<S::RawState>>,
event: EventWriter<StateTransitionEvent<T>>,
commands: Commands,
current_state_res: Option<ResMut<State<T>>>,
next_state_res: Option<ResMut<NextState<T>>>,
state_set: Option<Res<State<S::RawState>>>| {
let parent_changed = parent_changed.read().last().is_some();
let next_state = take_next_state(next_state_res);
if !parent_changed && next_state.is_none() {
return;
}
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
let initial_state = if parent_changed {
if let Some(state_set) = S::convert_to_usable_state(state_set.as_deref()) {
T::should_exist(state_set)
} else {
None
}
} else {
current_state.clone()
};
let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));
internal_apply_state_transition(event, commands, current_state_res, new_state);
};
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
.after(ApplyStateTransition::<S::RawState>::default()),
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
.before(ExitSchedules::<S::RawState>::default()),
TransitionSchedules::<T>::default().in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
.after(EnterSchedules::<S::RawState>::default()),
));
schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(
last_transition::<T>
.pipe(run_exit::<T>)
.in_set(ExitSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_transition::<T>)
.in_set(TransitionSchedules::<T>::default()),
)
.add_systems(
last_transition::<T>
.pipe(run_enter::<T>)
.in_set(EnterSchedules::<T>::default()),
);
}
}
macro_rules! impl_state_set_sealed_tuples {
($(#[$meta:meta])* $(($param: ident, $val: ident, $evt: ident)), *) => {
$(#[$meta])*
impl<$($param: InnerStateSet),*> StateSetSealed for ($($param,)*) {}
$(#[$meta])*
impl<$($param: InnerStateSet),*> StateSet for ($($param,)*) {
const SET_DEPENDENCY_DEPTH : usize = $($param::DEPENDENCY_DEPTH +)* 0;
fn register_computed_state_systems_in_schedule<T: ComputedStates<SourceStates = Self>>(
schedule: &mut Schedule,
) {
let apply_state_transition =
|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),
event: EventWriter<StateTransitionEvent<T>>,
commands: Commands,
current_state: Option<ResMut<State<T>>>,
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
if ($($evt.is_empty())&&*) {
return;
}
$($evt.clear();)*
let new_state = if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
T::compute(($($val),*, ))
} else {
None
};
internal_apply_state_transition(event, commands, current_state, new_state);
};
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
$(.before(ExitSchedules::<$param::RawState>::default()))*,
TransitionSchedules::<T>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
$(.after(EnterSchedules::<$param::RawState>::default()))*,
));
schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
}
fn register_sub_state_systems_in_schedule<T: SubStates<SourceStates = Self>>(
schedule: &mut Schedule,
) {
let apply_state_transition =
|($(mut $evt),*,): ($(EventReader<StateTransitionEvent<$param::RawState>>),*,),
event: EventWriter<StateTransitionEvent<T>>,
commands: Commands,
current_state_res: Option<ResMut<State<T>>>,
next_state_res: Option<ResMut<NextState<T>>>,
($($val),*,): ($(Option<Res<State<$param::RawState>>>),*,)| {
let parent_changed = ($($evt.read().last().is_some())&&*);
let next_state = take_next_state(next_state_res);
if !parent_changed && next_state.is_none() {
return;
}
let current_state = current_state_res.as_ref().map(|s| s.get()).cloned();
let initial_state = if parent_changed {
if let ($(Some($val)),*,) = ($($param::convert_to_usable_state($val.as_deref())),*,) {
T::should_exist(($($val),*, ))
} else {
None
}
} else {
current_state.clone()
};
let new_state = initial_state.map(|x| next_state.or(current_state).unwrap_or(x));
internal_apply_state_transition(event, commands, current_state_res, new_state);
};
schedule.configure_sets((
ApplyStateTransition::<T>::default()
.in_set(StateTransitionSteps::DependentTransitions)
$(.after(ApplyStateTransition::<$param::RawState>::default()))*,
ExitSchedules::<T>::default()
.in_set(StateTransitionSteps::ExitSchedules)
$(.before(ExitSchedules::<$param::RawState>::default()))*,
TransitionSchedules::<T>::default()
.in_set(StateTransitionSteps::TransitionSchedules),
EnterSchedules::<T>::default()
.in_set(StateTransitionSteps::EnterSchedules)
$(.after(EnterSchedules::<$param::RawState>::default()))*,
));
schedule
.add_systems(apply_state_transition.in_set(ApplyStateTransition::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_exit::<T>).in_set(ExitSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_transition::<T>).in_set(TransitionSchedules::<T>::default()))
.add_systems(last_transition::<T>.pipe(run_enter::<T>).in_set(EnterSchedules::<T>::default()));
}
}
};
}
all_tuples!(
#[doc(fake_variadic)]
impl_state_set_sealed_tuples,
1,
15,
S,
s,
ereader
);

71
vendor/bevy_state/src/state/states.rs vendored Normal file
View File

@@ -0,0 +1,71 @@
use core::fmt::Debug;
use core::hash::Hash;
/// Types that can define world-wide states in a finite-state machine.
///
/// The [`Default`] trait defines the starting state.
/// Multiple states can be defined for the same world,
/// allowing you to classify the state of the world across orthogonal dimensions.
/// You can access the current state of type `T` with the [`State<T>`](crate::state::State) resource,
/// and the queued state with the [`NextState<T>`](crate::state::NextState) resource.
///
/// State transitions typically occur in the [`OnEnter<T::Variant>`](crate::state::OnEnter) and [`OnExit<T::Variant>`](crate::state::OnExit) schedules,
/// which can be run by triggering the [`StateTransition`](crate::state::StateTransition) schedule.
///
/// Types used as [`ComputedStates`](crate::state::ComputedStates) do not need to and should not derive [`States`].
/// [`ComputedStates`](crate::state::ComputedStates) should not be manually mutated: functionality provided
/// by the [`States`] derive and the associated [`FreelyMutableState`](crate::state::FreelyMutableState) trait.
///
/// # Example
///
/// ```
/// use bevy_state::prelude::*;
/// use bevy_ecs::prelude::IntoScheduleConfigs;
/// use bevy_ecs::system::{ResMut, ScheduleSystem};
///
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// enum GameState {
/// #[default]
/// MainMenu,
/// SettingsMenu,
/// InGame,
/// }
///
/// fn handle_escape_pressed(mut next_state: ResMut<NextState<GameState>>) {
/// # let escape_pressed = true;
/// if escape_pressed {
/// next_state.set(GameState::SettingsMenu);
/// }
/// }
///
/// fn open_settings_menu() {
/// // Show the settings menu...
/// }
///
/// # struct AppMock;
/// # impl AppMock {
/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}
/// # }
/// # struct Update;
/// # let mut app = AppMock;
///
/// app.add_systems(Update, handle_escape_pressed.run_if(in_state(GameState::MainMenu)));
/// app.add_systems(OnEnter(GameState::SettingsMenu), open_settings_menu);
/// ```
#[diagnostic::on_unimplemented(
message = "`{Self}` can not be used as a state",
label = "invalid state",
note = "consider annotating `{Self}` with `#[derive(States)]`"
)]
pub trait States: 'static + Send + Sync + Clone + PartialEq + Eq + Hash + Debug {
/// How many other states this state depends on.
/// Used to help order transitions and de-duplicate [`ComputedStates`](crate::state::ComputedStates), as well as prevent cyclical
/// `ComputedState` dependencies.
const DEPENDENCY_DEPTH: usize = 1;
/// Should [`StateScoped`](crate::state_scoped::StateScoped) be enabled for this state? If set to `true`,
/// the `StateScoped` component will be used to remove entities when changing state.
const SCOPED_ENTITIES_ENABLED: bool = false;
}

View File

@@ -0,0 +1,175 @@
use bevy_ecs::schedule::Schedule;
use super::{freely_mutable_state::FreelyMutableState, state_set::StateSet, states::States};
pub use bevy_state_macros::SubStates;
/// A sub-state is a state that exists only when the source state meet certain conditions,
/// but unlike [`ComputedStates`](crate::state::ComputedStates) - while they exist they can be manually modified.
///
/// The default approach to creating [`SubStates`] is using the derive macro, and defining a single source state
/// and value to determine it's existence.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
///
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// enum AppState {
/// #[default]
/// Menu,
/// InGame
/// }
///
///
/// #[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// #[source(AppState = AppState::InGame)]
/// enum GamePhase {
/// #[default]
/// Setup,
/// Battle,
/// Conclusion
/// }
/// ```
///
/// you can then add it to an App, and from there you use the state as normal:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
///
/// # struct App;
/// # impl App {
/// # fn new() -> Self { App }
/// # fn init_state<S>(&mut self) -> &mut Self {self}
/// # fn add_sub_state<S>(&mut self) -> &mut Self {self}
/// # }
/// # struct AppState;
/// # struct GamePhase;
///
/// App::new()
/// .init_state::<AppState>()
/// .add_sub_state::<GamePhase>();
/// ```
///
/// In more complex situations, the recommendation is to use an intermediary computed state, like so:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
///
/// /// Computed States require some state to derive from
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// enum AppState {
/// #[default]
/// Menu,
/// InGame { paused: bool }
/// }
///
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
/// struct InGame;
///
/// impl ComputedStates for InGame {
/// /// We set the source state to be the state, or set of states,
/// /// we want to depend on. Any of the states can be wrapped in an Option.
/// type SourceStates = Option<AppState>;
///
/// /// We then define the compute function, which takes in the AppState
/// fn compute(sources: Option<AppState>) -> Option<Self> {
/// match sources {
/// /// When we are in game, we want to return the InGame state
/// Some(AppState::InGame { .. }) => Some(InGame),
/// /// Otherwise, we don't want the `State<InGame>` resource to exist,
/// /// so we return None.
/// _ => None
/// }
/// }
/// }
///
/// #[derive(SubStates, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// #[source(InGame = InGame)]
/// enum GamePhase {
/// #[default]
/// Setup,
/// Battle,
/// Conclusion
/// }
/// ```
///
/// However, you can also manually implement them. If you do so, you'll also need to manually implement the `States` & `FreelyMutableState` traits.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_state::prelude::*;
/// # use bevy_state::state::{FreelyMutableState, NextState};
///
/// /// Computed States require some state to derive from
/// #[derive(States, Clone, PartialEq, Eq, Hash, Debug, Default)]
/// enum AppState {
/// #[default]
/// Menu,
/// InGame { paused: bool }
/// }
///
/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
/// enum GamePhase {
/// Setup,
/// Battle,
/// Conclusion
/// }
///
/// impl SubStates for GamePhase {
/// /// We set the source state to be the state, or set of states,
/// /// we want to depend on. Any of the states can be wrapped in an Option.
/// type SourceStates = Option<AppState>;
///
/// /// We then define the compute function, which takes in the [`Self::SourceStates`]
/// fn should_exist(sources: Option<AppState>) -> Option<Self> {
/// match sources {
/// /// When we are in game, we want a GamePhase state to exist.
/// /// We can set the initial value here or overwrite it through [`NextState`].
/// Some(AppState::InGame { .. }) => Some(Self::Setup),
/// /// If we don't want the `State<GamePhase>` resource to exist we return [`None`].
/// _ => None
/// }
/// }
/// }
///
/// impl States for GamePhase {
/// const DEPENDENCY_DEPTH : usize = <GamePhase as SubStates>::SourceStates::SET_DEPENDENCY_DEPTH + 1;
/// }
///
/// impl FreelyMutableState for GamePhase {}
/// ```
#[diagnostic::on_unimplemented(
message = "`{Self}` can not be used as a sub-state",
label = "invalid sub-state",
note = "consider annotating `{Self}` with `#[derive(SubStates)]`"
)]
pub trait SubStates: States + FreelyMutableState {
/// The set of states from which the [`Self`] is derived.
///
/// This can either be a single type that implements [`States`], or a tuple
/// containing multiple types that implement [`States`], or any combination of
/// types implementing [`States`] and Options of types implementing [`States`].
type SourceStates: StateSet;
/// This function gets called whenever one of the [`SourceStates`](Self::SourceStates) changes.
/// The result is used to determine the existence of [`State<Self>`](crate::state::State).
///
/// If the result is [`None`], the [`State<Self>`](crate::state::State) resource will be removed from the world,
/// otherwise if the [`State<Self>`](crate::state::State) resource doesn't exist
/// it will be created from the returned [`Some`] as the initial state.
///
/// Value within [`Some`] is ignored if the state already exists in the world
/// and only symbolizes that the state should still exist.
///
/// Initial value can also be overwritten by [`NextState`](crate::state::NextState).
fn should_exist(sources: Self::SourceStates) -> Option<Self>;
/// This function sets up systems that compute the state whenever one of the [`SourceStates`](Self::SourceStates)
/// change. It is called by `App::add_computed_state`, but can be called manually if `App` is not
/// used.
fn register_sub_state_systems(schedule: &mut Schedule) {
Self::SourceStates::register_sub_state_systems_in_schedule::<Self>(schedule);
}
}

View File

@@ -0,0 +1,260 @@
use core::{marker::PhantomData, mem};
use bevy_ecs::{
event::{Event, EventReader, EventWriter},
schedule::{IntoScheduleConfigs, Schedule, ScheduleLabel, Schedules, SystemSet},
system::{Commands, In, ResMut},
world::World,
};
use super::{resources::State, states::States};
/// The label of a [`Schedule`] that **only** runs whenever [`State<S>`] enters the provided state.
///
/// This schedule ignores identity transitions.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct OnEnter<S: States>(pub S);
/// The label of a [`Schedule`] that **only** runs whenever [`State<S>`] exits the provided state.
///
/// This schedule ignores identity transitions.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct OnExit<S: States>(pub S);
/// The label of a [`Schedule`] that **only** runs whenever [`State<S>`]
/// exits AND enters the provided `exited` and `entered` states.
///
/// Systems added to this schedule are always ran *after* [`OnExit`], and *before* [`OnEnter`].
///
/// This schedule will run on identity transitions.
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct OnTransition<S: States> {
/// The state being exited.
pub exited: S,
/// The state being entered.
pub entered: S,
}
/// Runs [state transitions](States).
///
/// By default, it will be triggered once before [`PreStartup`] and then each frame after [`PreUpdate`], but
/// you can manually trigger it at arbitrary times by creating an exclusive
/// system to run the schedule.
///
/// ```rust
/// use bevy_state::prelude::*;
/// use bevy_ecs::prelude::*;
///
/// fn run_state_transitions(world: &mut World) {
/// let _ = world.try_run_schedule(StateTransition);
/// }
/// ```
///
/// [`PreStartup`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreStartup.html
/// [`PreUpdate`]: https://docs.rs/bevy/latest/bevy/prelude/struct.PreUpdate.html
#[derive(ScheduleLabel, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct StateTransition;
/// Event sent when any state transition of `S` happens.
/// This includes identity transitions, where `exited` and `entered` have the same value.
///
/// If you know exactly what state you want to respond to ahead of time, consider [`OnEnter`], [`OnTransition`], or [`OnExit`]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Event)]
pub struct StateTransitionEvent<S: States> {
/// The state being exited.
pub exited: Option<S>,
/// The state being entered.
pub entered: Option<S>,
}
/// Applies state transitions and runs transitions schedules in order.
///
/// These system sets are run sequentially, in the order of the enum variants.
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub enum StateTransitionSteps {
/// States apply their transitions from [`NextState`](super::NextState)
/// and compute functions based on their parent states.
DependentTransitions,
/// Exit schedules are executed in leaf to root order
ExitSchedules,
/// Transition schedules are executed in arbitrary order.
TransitionSchedules,
/// Enter schedules are executed in root to leaf order.
EnterSchedules,
}
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
/// System set that runs exit schedule(s) for state `S`.
pub struct ExitSchedules<S: States>(PhantomData<S>);
impl<S: States> Default for ExitSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
/// System set that runs transition schedule(s) for state `S`.
pub struct TransitionSchedules<S: States>(PhantomData<S>);
impl<S: States> Default for TransitionSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
/// System set that runs enter schedule(s) for state `S`.
pub struct EnterSchedules<S: States>(PhantomData<S>);
impl<S: States> Default for EnterSchedules<S> {
fn default() -> Self {
Self(Default::default())
}
}
/// System set that applies transitions for state `S`.
#[derive(SystemSet, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ApplyStateTransition<S: States>(PhantomData<S>);
impl<S: States> Default for ApplyStateTransition<S> {
fn default() -> Self {
Self(Default::default())
}
}
/// This function actually applies a state change, and registers the required
/// schedules for downstream computed states and transition schedules.
///
/// The `new_state` is an option to allow for removal - `None` will trigger the
/// removal of the `State<S>` resource from the [`World`].
pub(crate) fn internal_apply_state_transition<S: States>(
mut event: EventWriter<StateTransitionEvent<S>>,
mut commands: Commands,
current_state: Option<ResMut<State<S>>>,
new_state: Option<S>,
) {
match new_state {
Some(entered) => {
match current_state {
// If the [`State<S>`] resource exists, and the state is not the one we are
// entering - we need to set the new value, compute dependent states, send transition events
// and register transition schedules.
Some(mut state_resource) => {
let exited = match *state_resource == entered {
true => entered.clone(),
false => mem::replace(&mut state_resource.0, entered.clone()),
};
// Transition events are sent even for same state transitions
// Although enter and exit schedules are not run by default.
event.write(StateTransitionEvent {
exited: Some(exited.clone()),
entered: Some(entered.clone()),
});
}
None => {
// If the [`State<S>`] resource does not exist, we create it, compute dependent states, send a transition event and register the `OnEnter` schedule.
commands.insert_resource(State(entered.clone()));
event.write(StateTransitionEvent {
exited: None,
entered: Some(entered.clone()),
});
}
};
}
None => {
// We first remove the [`State<S>`] resource, and if one existed we compute dependent states, send a transition event and run the `OnExit` schedule.
if let Some(resource) = current_state {
commands.remove_resource::<State<S>>();
event.write(StateTransitionEvent {
exited: Some(resource.get().clone()),
entered: None,
});
}
}
}
}
/// Sets up the schedules and systems for handling state transitions
/// within a [`World`].
///
/// Runs automatically when using `App` to insert states, but needs to
/// be added manually in other situations.
pub fn setup_state_transitions_in_world(world: &mut World) {
let mut schedules = world.get_resource_or_init::<Schedules>();
if schedules.contains(StateTransition) {
return;
}
let mut schedule = Schedule::new(StateTransition);
schedule.configure_sets(
(
StateTransitionSteps::DependentTransitions,
StateTransitionSteps::ExitSchedules,
StateTransitionSteps::TransitionSchedules,
StateTransitionSteps::EnterSchedules,
)
.chain(),
);
schedules.insert(schedule);
}
/// Returns the latest state transition event of type `S`, if any are available.
pub fn last_transition<S: States>(
mut reader: EventReader<StateTransitionEvent<S>>,
) -> Option<StateTransitionEvent<S>> {
reader.read().last().cloned()
}
pub(crate) fn run_enter<S: States>(
transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World,
) {
let Some(transition) = transition.0 else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(entered) = transition.entered else {
return;
};
let _ = world.try_run_schedule(OnEnter(entered));
}
pub(crate) fn run_exit<S: States>(
transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World,
) {
let Some(transition) = transition.0 else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(exited) = transition.exited else {
return;
};
let _ = world.try_run_schedule(OnExit(exited));
}
pub(crate) fn run_transition<S: States>(
transition: In<Option<StateTransitionEvent<S>>>,
world: &mut World,
) {
let Some(transition) = transition.0 else {
return;
};
let Some(exited) = transition.exited else {
return;
};
let Some(entered) = transition.entered else {
return;
};
let _ = world.try_run_schedule(OnTransition { exited, entered });
}

93
vendor/bevy_state/src/state_scoped.rs vendored Normal file
View File

@@ -0,0 +1,93 @@
#[cfg(feature = "bevy_reflect")]
use bevy_ecs::reflect::ReflectComponent;
use bevy_ecs::{
component::Component,
entity::Entity,
event::EventReader,
system::{Commands, Query},
};
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*;
use crate::state::{StateTransitionEvent, States};
/// Entities marked with this component will be removed
/// when the world's state of the matching type no longer matches the supplied value.
///
/// To enable this feature remember to add the attribute `#[states(scoped_entities)]` when deriving [`States`].
/// It's also possible to enable it when adding the state to an app with [`enable_state_scoped_entities`](crate::app::AppExtStates::enable_state_scoped_entities).
///
/// ```
/// use bevy_state::prelude::*;
/// use bevy_ecs::prelude::*;
/// use bevy_ecs::system::ScheduleSystem;
///
/// #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)]
/// #[states(scoped_entities)]
/// enum GameState {
/// #[default]
/// MainMenu,
/// SettingsMenu,
/// InGame,
/// }
///
/// # #[derive(Component)]
/// # struct Player;
///
/// fn spawn_player(mut commands: Commands) {
/// commands.spawn((
/// StateScoped(GameState::InGame),
/// Player
/// ));
/// }
///
/// # struct AppMock;
/// # impl AppMock {
/// # fn init_state<S>(&mut self) {}
/// # fn enable_state_scoped_entities<S>(&mut self) {}
/// # fn add_systems<S, M>(&mut self, schedule: S, systems: impl IntoScheduleConfigs<ScheduleSystem, M>) {}
/// # }
/// # struct Update;
/// # let mut app = AppMock;
///
/// app.init_state::<GameState>();
/// app.add_systems(OnEnter(GameState::InGame), spawn_player);
/// ```
#[derive(Component, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Component, Clone))]
pub struct StateScoped<S: States>(pub S);
impl<S> Default for StateScoped<S>
where
S: States + Default,
{
fn default() -> Self {
Self(S::default())
}
}
/// Removes entities marked with [`StateScoped<S>`]
/// when their state no longer matches the world state.
pub fn clear_state_scoped_entities<S: States>(
mut commands: Commands,
mut transitions: EventReader<StateTransitionEvent<S>>,
query: Query<(Entity, &StateScoped<S>)>,
) {
// We use the latest event, because state machine internals generate at most 1
// transition event (per type) each frame. No event means no change happened
// and we skip iterating all entities.
let Some(transition) = transitions.read().last() else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(exited) = &transition.exited else {
return;
};
for (entity, binding) in &query {
if binding.0 == *exited {
commands.entity(entity).despawn();
}
}
}

View File

@@ -0,0 +1,111 @@
use alloc::vec::Vec;
use core::marker::PhantomData;
use bevy_app::{App, SubApp};
use bevy_ecs::{
event::{Event, EventReader, Events},
resource::Resource,
system::Commands,
world::World,
};
use bevy_platform::collections::HashMap;
use crate::state::{FreelyMutableState, OnExit, StateTransitionEvent};
fn clear_event_queue<E: Event>(w: &mut World) {
if let Some(mut queue) = w.get_resource_mut::<Events<E>>() {
queue.clear();
}
}
#[derive(Resource)]
struct StateScopedEvents<S: FreelyMutableState> {
cleanup_fns: HashMap<S, Vec<fn(&mut World)>>,
}
impl<S: FreelyMutableState> StateScopedEvents<S> {
fn add_event<E: Event>(&mut self, state: S) {
self.cleanup_fns
.entry(state)
.or_default()
.push(clear_event_queue::<E>);
}
fn cleanup(&self, w: &mut World, state: S) {
let Some(fns) = self.cleanup_fns.get(&state) else {
return;
};
for callback in fns {
(*callback)(w);
}
}
}
impl<S: FreelyMutableState> Default for StateScopedEvents<S> {
fn default() -> Self {
Self {
cleanup_fns: HashMap::default(),
}
}
}
fn cleanup_state_scoped_event<S: FreelyMutableState>(
mut c: Commands,
mut transitions: EventReader<StateTransitionEvent<S>>,
) {
let Some(transition) = transitions.read().last() else {
return;
};
if transition.entered == transition.exited {
return;
}
let Some(exited) = transition.exited.clone() else {
return;
};
c.queue(move |w: &mut World| {
w.resource_scope::<StateScopedEvents<S>, ()>(|w, events| {
events.cleanup(w, exited);
});
});
}
fn add_state_scoped_event_impl<E: Event, S: FreelyMutableState>(
app: &mut SubApp,
_p: PhantomData<E>,
state: S,
) {
if !app.world().contains_resource::<StateScopedEvents<S>>() {
app.init_resource::<StateScopedEvents<S>>();
}
app.add_event::<E>();
app.world_mut()
.resource_mut::<StateScopedEvents<S>>()
.add_event::<E>(state.clone());
app.add_systems(OnExit(state), cleanup_state_scoped_event::<S>);
}
/// Extension trait for [`App`] adding methods for registering state scoped events.
pub trait StateScopedEventsAppExt {
/// Adds an [`Event`] that is automatically cleaned up when leaving the specified `state`.
///
/// Note that event cleanup is ordered ambiguously relative to [`StateScoped`](crate::prelude::StateScoped) entity
/// cleanup and the [`OnExit`] schedule for the target state. All of these (state scoped
/// entities and events cleanup, and `OnExit`) occur within schedule [`StateTransition`](crate::prelude::StateTransition)
/// and system set `StateTransitionSteps::ExitSchedules`.
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self;
}
impl StateScopedEventsAppExt for App {
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self {
add_state_scoped_event_impl(self.main_mut(), PhantomData::<E>, state);
self
}
}
impl StateScopedEventsAppExt for SubApp {
fn add_state_scoped_event<E: Event>(&mut self, state: impl FreelyMutableState) -> &mut Self {
add_state_scoped_event_impl(self, PhantomData::<E>, state);
self
}
}