diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b934c..2f17426 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,25 @@ # Changelog ## Unreleased - XXXX-XX-XX -- Migrate to bevy 0.18 -- Update flake +- Technically breaking (But hidden behind plugin): + - Add `enable_debug` field to `TimeRunnerPlugin` by [#15](https://github.com/Multirious/bevy_time_runner/pull/15) + - Add `Tagging` variant to `TimeRunnerSet` by [#15](https://github.com/Multirious/bevy_time_runner/pull/15) + - Systems now expected `TimeCtx` generic parameter by [#15](https://github.com/Multirious/bevy_time_runner/pull/15) + +- Migrate to bevy 0.18 by [#16](https://github.com/Multirious/bevy_time_runner/pull/16) +- Update flake by [#17](https://github.com/Multirious/bevy_time_runner/pull/17) - Use latest instead of a version for stableRust in flake.nix - `nix flake update` - Remove flake-utils dependency from flake.nix +- Systems can now be registered in non-default time context. (Virtual, Fixed, and/or Real) See [#15](https://github.com/Multirious/bevy_time_runner/pull/15) for more details. + + Notably: + - `TimeRunnerPlugin` which now uses `TimeRunnerSystemsPlugin<()>` by default. + - `TimeRunner` is always expected to have the `TimeContext` marker component. + - `TimeContext` is automatically inserted to children of `TimeRunner`. + - Add feature `debug`. This adds `TimeRunnerDebugPlugin` which logs warnings on missing `TimeContext` marker component when enabled. + ## v0.5.2 - 2025-10-6 - Fix documentation diff --git a/Cargo.lock b/Cargo.lock index 379d99d..d727746 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + [[package]] name = "android-activity" version = "0.6.0" @@ -29,6 +38,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + [[package]] name = "arrayvec" version = "0.7.6" @@ -58,6 +73,20 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-executor" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + [[package]] name = "async-task" version = "4.7.1" @@ -113,10 +142,14 @@ dependencies = [ "bevy_tasks", "bevy_utils", "cfg-if", + "console_error_panic_hook", + "ctrlc", "downcast-rs", "log", "thiserror 2.0.17", "variadics_please", + "wasm-bindgen", + "web-sys", ] [[package]] @@ -152,6 +185,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24637a7c8643cab493f4085cda6bde4895f0e0816699c59006f18819da2ca0b8" dependencies = [ + "arrayvec", "bevy_ecs_macros", "bevy_platform", "bevy_ptr", @@ -166,6 +200,7 @@ dependencies = [ "indexmap", "log", "nonmax", + "serde", "slotmap", "smallvec", "thiserror 2.0.17", @@ -222,6 +257,24 @@ dependencies = [ "bevy_utils", ] +[[package]] +name = "bevy_log" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "406304a9b867a2de98c3edf0cc9e5a608fad1a1ddc567e15e72c186a8273ef51" +dependencies = [ + "android_log-sys", + "bevy_app", + "bevy_ecs", + "bevy_platform", + "bevy_utils", + "tracing", + "tracing-log", + "tracing-oslog", + "tracing-subscriber", + "tracing-wasm", +] + [[package]] name = "bevy_macro_utils" version = "0.18.0" @@ -259,13 +312,18 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b29ea749a8e85f98186ab662f607b885b97c804bb14cdb0cdf838164496d474" dependencies = [ + "critical-section", "foldhash", "futures-channel", "hashbrown", + "js-sys", "portable-atomic", "portable-atomic-util", "serde", "spin", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-time", ] [[package]] @@ -294,8 +352,11 @@ dependencies = [ "indexmap", "serde", "smallvec", + "smol_str", "thiserror 2.0.17", + "uuid", "variadics_please", + "wgpu-types", ] [[package]] @@ -319,6 +380,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "990ffedd374dd2c4fe8f0fd4bcefd5617d1ee59164b6c3fcc356a69b48e26e8e" dependencies = [ "async-channel", + "async-executor", "async-task", "atomic-waker", "bevy_platform", @@ -349,6 +411,7 @@ dependencies = [ "bevy", "bevy_app", "bevy_ecs", + "bevy_log", "bevy_reflect", "bevy_time", "rustc_version", @@ -378,6 +441,7 @@ checksum = "e258c44d869f9c41ac0f88a16815c67f2569eb9fff4716828a40273d127b6f84" dependencies = [ "bevy_platform", "disqualified", + "thread_local", ] [[package]] @@ -385,6 +449,18 @@ name = "bitflags" version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" +dependencies = [ + "serde", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] [[package]] name = "bumpalo" @@ -448,6 +524,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "combine" version = "4.6.7" @@ -468,12 +550,28 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + [[package]] name = "const-fnv1a-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" +[[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" @@ -489,6 +587,17 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "ctrlc" +version = "3.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73736a89c4aff73035ba2ed2e565061954da00d4970fc9ac25dcc85a2a20d790" +dependencies = [ + "dispatch2", + "nix", + "windows-sys 0.61.1", +] + [[package]] name = "derive_more" version = "2.0.1" @@ -510,6 +619,18 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + [[package]] name = "disqualified" version = "1.0.0" @@ -565,6 +686,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "find-msvc-tools" version = "0.1.3" @@ -598,13 +725,22 @@ 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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ + "fastrand", "futures-core", + "futures-io", + "parking", "pin-project-lite", ] @@ -723,6 +859,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.176" @@ -741,6 +883,15 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + [[package]] name = "memchr" version = "2.7.6" @@ -776,12 +927,33 @@ dependencies = [ "jni-sys", ] +[[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 = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.1", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -814,12 +986,33 @@ dependencies = [ "syn", ] +[[package]] +name = "objc2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + [[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 = "pin-project" version = "1.1.10" @@ -942,6 +1135,23 @@ dependencies = [ "rand", ] +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + [[package]] name = "rustc_version" version = "0.4.1" @@ -1002,12 +1212,27 @@ dependencies = [ "syn", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + [[package]] name = "slotmap" version = "1.0.7" @@ -1023,6 +1248,15 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[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.10.0" @@ -1089,6 +1323,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "toml_datetime" version = "0.7.2" @@ -1119,6 +1362,90 @@ dependencies = [ "winnow", ] +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-oslog" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76902d2a8d5f9f55a81155c08971734071968c90f2d9bfe645fe700579b2950" +dependencies = [ + "cc", + "cfg-if", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "tracing-wasm" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4575c663a174420fa2d78f4108ff68f65bf2fbb7dd89f33749b6e826b3626e07" +dependencies = [ + "tracing", + "tracing-subscriber", + "wasm-bindgen", +] + [[package]] name = "typeid" version = "1.0.3" @@ -1143,10 +1470,18 @@ version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ + "getrandom", "js-sys", + "serde", "wasm-bindgen", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "variadics_please" version = "1.1.0" @@ -1219,6 +1554,19 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.104" @@ -1251,6 +1599,41 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "web-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu-types" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" +dependencies = [ + "bitflags", + "bytemuck", + "js-sys", + "log", + "serde", + "thiserror 2.0.17", + "web-sys", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/Cargo.toml b/Cargo.toml index 0cd1099..96df423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,15 +18,19 @@ bevy_time = { version = "0.18.0", default-features = false } bevy_ecs = { version = "0.18.0", default-features = false } bevy_app = { version = "0.18.0", default-features = false, optional = true } bevy_reflect = { version = "0.18.0", default-features = false, optional = true } +bevy_log = { version = "0.18.0", default-features = false, optional = true } [features] default = [ "bevy_reflect", - "bevy_app" + "bevy_app", + "debug" ] bevy_reflect = [ "dep:bevy_reflect", "bevy_ecs/bevy_reflect", "bevy_app/bevy_reflect" ] bevy_app = [ "dep:bevy_app" ] +debug = [ "dep:bevy_log" ] + [dev-dependencies] bevy = { version = "0.18.0" , default-features = false } diff --git a/src/lib.rs b/src/lib.rs index c3d2ca9..b7e93e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,22 +38,57 @@ #[cfg(feature = "bevy_app")] use bevy_app::prelude::*; +#[cfg(feature = "debug")] +use bevy_ecs::component::ComponentId; use bevy_ecs::prelude::*; #[cfg(feature = "bevy_app")] use bevy_ecs::schedule::{InternedScheduleLabel, ScheduleLabel}; mod time_runner; mod time_span; +#[cfg(feature = "debug")] +use std::any::TypeId; +#[cfg(feature = "bevy_app")] +use std::marker::PhantomData; pub use time_runner::*; pub use time_span::*; -/// Add [`time_runner_system`] -/// Registers [`TimeRunner`] +/// Add [`time_runner_system::`] on schedule #[cfg(feature = "bevy_app")] #[derive(Debug)] -pub struct TimeRunnerPlugin { +pub struct TimeRunnerSystemsPlugin +where + TimeCtx: Default + Send + Sync + 'static, +{ /// All systems will be put to this schedule pub schedule: InternedScheduleLabel, + /// The time step ticked by (for example, () for regular time or Fixed for fixed time steps) + _time_step: PhantomData, +} + +#[cfg(feature = "bevy_app")] +impl TimeRunnerSystemsPlugin +where + TimeCtx: Default + Send + Sync + 'static, +{ + /// Initializes the plugin to run on the specified schedule + pub fn from_schedule_intern(schedule: InternedScheduleLabel) -> Self { + Self { + schedule, + _time_step: Default::default(), + } + } +} + +#[cfg(feature = "bevy_app")] +/// Registers all types and adds TimeRunnerRegistrationPlugin with default config +pub struct TimeRunnerPlugin { + /// The schedule where the default time runners will be registered (TimerRunner<()>) + pub schedule: InternedScheduleLabel, + /// Enables [`TimeRunnerDebugPlugin`] with default configuration. + /// You may manually insert [`TimeRunnerDebugPlugin`] for custom configuration. + #[cfg(feature = "debug")] + pub enable_debug: bool, } #[cfg(feature = "bevy_app")] @@ -61,6 +96,8 @@ impl Default for TimeRunnerPlugin { fn default() -> Self { TimeRunnerPlugin { schedule: PostUpdate.intern(), + #[cfg(feature = "debug")] + enable_debug: true, } } } @@ -68,18 +105,12 @@ impl Default for TimeRunnerPlugin { #[cfg(feature = "bevy_app")] impl Plugin for TimeRunnerPlugin { fn build(&self, app: &mut App) { - app.configure_sets( - self.schedule, - (TimeRunnerSet::TickTimer, TimeRunnerSet::Progress).chain(), - ) - .add_systems( - self.schedule, - ( - tick_time_runner_system.in_set(TimeRunnerSet::TickTimer), - time_runner_system.in_set(TimeRunnerSet::Progress), - ), - ) - .add_message::(); + if !app.is_plugin_added::>() { + app.add_plugins(TimeRunnerSystemsPlugin::<()>::from_schedule_intern( + self.schedule, + )); + } + app.add_message::(); #[cfg(feature = "bevy_reflect")] app.register_type::() @@ -92,14 +123,121 @@ impl Plugin for TimeRunnerPlugin { .register_type::() .register_type::() .register_type::(); + + #[cfg(feature = "debug")] + if self.enable_debug && !app.is_plugin_added::() { + app.add_plugins(TimeRunnerDebugPlugin::default()); + } + } +} + +#[cfg(feature = "bevy_app")] +impl Plugin for TimeRunnerSystemsPlugin +where + TimeCtx: Default + Send + Sync + 'static, +{ + fn build(&self, app: &mut App) { + app.configure_sets( + self.schedule, + ( + TimeRunnerSet::Tagging, + TimeRunnerSet::TickTimer, + TimeRunnerSet::Progress, + ) + .chain(), + ) + .add_systems( + self.schedule, + ( + tag_time_runner_children_with_context::.in_set(TimeRunnerSet::Tagging), + tick_time_runner_system::.in_set(TimeRunnerSet::TickTimer), + time_runner_system::.in_set(TimeRunnerSet::Progress), + ), + ); } } /// System set in this crate #[derive(Debug, PartialEq, Eq, Hash, Clone, SystemSet)] pub enum TimeRunnerSet { + /// Systems responsible for automatic tagging of time runner children + Tagging, /// Systems responsible for ticking timer TickTimer, /// Systems responsible for updating [`TimeSpanProgress`] Progress, } + +/// Includes infos at runtime for debugging TimeRunner related issues. +/// This is inserted via [`TimeRunnerDebugPlugin`]. +#[cfg(feature = "debug")] +#[derive(Debug, Default, Clone, Resource)] +pub struct TimeRunnerDebugInfo { + time_steps: Vec, +} + +/// Debugs TimeRunner related issues +/// +/// By default, this print warnings for missing [`TimeContext`] where `T` is: +/// - `()` (The default time step), +/// - [`bevy_time::Fixed`], +/// - [`bevy_time::Real`] and +/// - [`bevy_time::Virtual`]. +/// +/// If you have addtional custom context, you may add it via [`Self::add_time_step`]. +#[cfg(feature = "debug")] +pub struct TimeRunnerDebugPlugin { + time_step_markers: Vec<(TypeId, &'static str)>, +} + +#[cfg(feature = "debug")] +impl Default for TimeRunnerDebugPlugin { + fn default() -> Self { + let mut a = TimeRunnerDebugPlugin { + time_step_markers: Vec::new(), + }; + a.add_time_step::<()>(); + a.add_time_step::(); + a.add_time_step::(); + a.add_time_step::(); + a + } +} + +#[cfg(feature = "debug")] +impl TimeRunnerDebugPlugin { + /// Enables a [`bevy_time::Time`]'s specific context to be checked. + pub fn add_time_step(&mut self) + where + TimeCtx: Default + Send + Sync + 'static, + { + self.time_step_markers.push(( + TypeId::of::>(), + std::any::type_name::>(), + )); + } +} + +#[cfg(all(feature = "debug", feature = "bevy_app"))] +impl Plugin for TimeRunnerDebugPlugin { + /// # Panics + /// + /// This method panics if a requested component to be debug haven't been registered. + fn build(&self, app: &mut App) { + let mut info = TimeRunnerDebugInfo::default(); + let world_mut = app.world_mut(); + world_mut.register_component::>(); + world_mut.register_component::>(); + world_mut.register_component::>(); + world_mut.register_component::>(); + for (type_id, type_name) in &self.time_step_markers { + let Some(component_id) = app.world().components().get_id(*type_id) else { + panic!( + "{type_name} have not been registered as a componenet yet. It is required for `TimeRunnerDebugPlugin`." + ) + }; + info.time_steps.push(component_id); + } + app.world_mut().insert_resource(info); + } +} diff --git a/src/time_runner.rs b/src/time_runner.rs index fbbff76..1eb639b 100644 --- a/src/time_runner.rs +++ b/src/time_runner.rs @@ -2,7 +2,7 @@ use bevy_ecs::prelude::*; #[cfg(feature = "bevy_reflect")] use bevy_reflect::prelude::*; use bevy_time::prelude::*; -use std::{cmp::Ordering, time::Duration}; +use std::{cmp::Ordering, marker::PhantomData, time::Duration}; use crate::time_span::*; @@ -51,6 +51,7 @@ impl TimeRunnerElasped { #[derive(Debug, Clone, PartialEq, Component)] #[cfg_attr(feature = "bevy_reflect", derive(Reflect))] #[cfg_attr(feature = "bevy_reflect", reflect(Component))] +#[cfg_attr(feature = "debug", component(on_add = on_time_runner_added))] pub struct TimeRunner { paused: bool, /// The current elasped time with other useful information. @@ -65,9 +66,46 @@ pub struct TimeRunner { repeat: Option<(Repeat, RepeatStyle)>, } +#[cfg(feature = "debug")] +fn on_time_runner_added( + world: bevy_ecs::world::DeferredWorld<'_>, + hook_context: bevy_ecs::lifecycle::HookContext, +) { + use bevy_log::{error, warn}; + let Some(debug_info) = world.get_resource::() else { + return; + }; + let entity = match world.get_entity(hook_context.entity) { + Ok(entity) => entity, + Err(e) => { + error!( + "Attempted to get entity {} but got error: {}", + hook_context.entity, e + ); + return; + } + }; + let has_any_time_step = debug_info.time_steps.iter().any(|t| entity.contains_id(*t)); + if !has_any_time_step { + warn!( + "TimeRunner {} does not have any `TimeContext` component. This may cause problems. If this warning is false, you can disable it by removing TimeRunnerDebugPlugin or check its documentation.", + hook_context.entity + ); + } +} + +/// A marker component attached to TimeRunner to make the time delta applied to it based on a specific TimeCtx +#[derive(Debug, Clone, PartialEq, Component, Default)] +pub struct TimeContext +where + TimeCtx: Default + Send + Sync + 'static, +{ + _time_step: PhantomData, +} + impl TimeRunner { /// Create new [`TimeRunner`] with this duration. - pub fn new(length: Duration) -> TimeRunner { + pub fn new(length: Duration) -> Self { TimeRunner { length, ..Default::default() @@ -403,13 +441,31 @@ impl TimeRunnerEnded { } } +/// Tag the chilren of newely created [`TimeRunner`]s with their [`TimeContext`] to ease querying +pub fn tag_time_runner_children_with_context( + newly_created_time_runners: Query<&Children, (Added, With>)>, + mut commands: Commands, +) where + TimeCtx: Default + Send + Sync + 'static, +{ + for children in &newly_created_time_runners { + for child_entity in children.iter() { + commands + .entity(child_entity) + .try_insert(TimeContext::::default()); + } + } +} + /// Tick time runner then send [`TimeRunnerEnded`] event if qualified for. -pub fn tick_time_runner_system( +pub fn tick_time_runner_system( mut commands: Commands, - time: Res