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

1
vendor/bevy_time/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"4249cd2b41be93391920cb9c114689d2562defb66dd30bf552a98cb012f40257","Cargo.toml":"2227c3c04672e3f22ebdb1144f8d879d4e10613bd219509d104ee9df851fe3b0","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"508a77d2e7b51d98adeed32648ad124b7b30241a8e70b2e72c99f92d8e5874d1","README.md":"a4a6c41d9d5c1febb7fe2a66bd921846b552c50ac357ec5008372a4fcf18ca65","src/common_conditions.rs":"648a758d9d381d2ab8b167fffd4b0e0f6f35bdc199c4ccfdd1365a250420e780","src/fixed.rs":"792c198d898d744eb66a352d5e9e1ec536f6cc002feb7f87c50c0772b1b6053c","src/lib.rs":"496bd5caa4aa4378b0eb40af341195d189cb0bc986c07c9525b51fb2e00f9857","src/real.rs":"d24830c7c91c508f50a210645e9fa5a423ea69a8f138a429cda6bf4262c0b692","src/stopwatch.rs":"0f89469949a788d5c34a3e2608a6feb5af224c57692f0a421f1846fcfc51e87f","src/time.rs":"9c253da8b905bab739865089ea7eb64f4f9d3ea180483145b9e90429f33dd42e","src/timer.rs":"328cb5376f4b062294888bb1bc64510798ec6727aedb0a7e217e18f72dddf45b","src/virt.rs":"29abf374affaa673900b1c0f6399a8ac70d5c987b2f0b2848395afd2aaa23f7e"},"package":"bc98eb356c75be04fbbc77bb3d8ffa24c8bacd99f76111cee23d444be6ac8c9c"}

979
vendor/bevy_time/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,979 @@
# 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-executor"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
dependencies = [
"portable-atomic",
]
[[package]]
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_tasks"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b674242641cab680688fc3b850243b351c1af49d4f3417a576debd6cca8dcf5"
dependencies = [
"async-executor",
"async-task",
"atomic-waker",
"bevy_platform",
"cfg-if",
"crossbeam-queue",
"derive_more",
"futures-lite",
"heapless",
]
[[package]]
name = "bevy_time"
version = "0.16.1"
dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_platform",
"bevy_reflect",
"crossbeam-channel",
"log",
"serde",
]
[[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-channel"
version = "0.5.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
dependencies = [
"crossbeam-utils",
]
[[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",
"serde",
]
[[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 = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[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",
]

139
vendor/bevy_time/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,139 @@
# 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_time"
version = "0.16.1"
build = false
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Provides time functionality for Bevy Engine"
homepage = "https://bevyengine.org"
readme = "README.md"
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",
"--generate-link-to-definition",
]
[features]
bevy_reflect = [
"dep:bevy_reflect",
"bevy_ecs/bevy_reflect",
"bevy_app/bevy_reflect",
]
critical-section = [
"bevy_ecs/critical-section",
"bevy_platform/critical-section",
"bevy_reflect?/critical-section",
"bevy_app/critical-section",
]
default = [
"std",
"bevy_reflect",
"bevy_app/default",
]
serialize = [
"dep:serde",
"bevy_ecs/serialize",
"bevy_platform/serialize",
]
std = [
"serde?/std",
"bevy_reflect?/std",
"bevy_ecs/std",
"bevy_app/std",
"bevy_platform/std",
"dep:crossbeam-channel",
]
[lib]
name = "bevy_time"
path = "src/lib.rs"
[dependencies.bevy_app]
version = "0.16.1"
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.crossbeam-channel]
version = "0.5.0"
features = ["std"]
optional = true
default-features = false
[dependencies.log]
version = "0.4"
default-features = false
[dependencies.serde]
version = "1"
features = ["derive"]
optional = true
default-features = false
[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_time/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_time/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.

9
vendor/bevy_time/README.md vendored Normal file
View File

@@ -0,0 +1,9 @@
# Bevy Time
[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/bevyengine/bevy#license)
[![Crates.io](https://img.shields.io/crates/v/bevy.svg)](https://crates.io/crates/bevy_time)
[![Downloads](https://img.shields.io/crates/d/bevy_time.svg)](https://crates.io/crates/bevy_time)
[![Docs](https://docs.rs/bevy_time/badge.svg)](https://docs.rs/bevy_time/latest/bevy_time/)
[![Discord](https://img.shields.io/discord/691052431525675048.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/bevy)
The built-in timekeeping plugin for the Bevy game engine.

View File

@@ -0,0 +1,254 @@
use crate::{Real, Time, Timer, TimerMode, Virtual};
use bevy_ecs::system::Res;
use core::time::Duration;
/// Run condition that is active on a regular time interval, using [`Time`] to advance
/// the timer. The timer ticks at the rate of [`Time::relative_speed`].
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::on_timer;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(on_timer(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran once a second
/// }
/// ```
///
/// Note that this does **not** guarantee that systems will run at exactly the
/// specified interval. If delta time is larger than the specified `duration` then
/// the system will only run once even though the timer may have completed multiple
/// times. This condition should only be used with large time durations (relative to
/// delta time).
///
/// For more accurate timers, use the [`Timer`] class directly (see
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
/// use fixed timesteps that allow systems to run multiple times per frame.
pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Repeating);
move |time: Res<Time>| {
timer.tick(time.delta());
timer.just_finished()
}
}
/// Run condition that is active on a regular time interval,
/// using [`Time<Real>`] to advance the timer.
/// The timer ticks are not scaled.
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::on_real_timer;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(on_real_timer(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran once a second
/// }
/// ```
///
/// Note that this does **not** guarantee that systems will run at exactly the
/// specified interval. If delta time is larger than the specified `duration` then
/// the system will only run once even though the timer may have completed multiple
/// times. This condition should only be used with large time durations (relative to
/// delta time).
///
/// For more accurate timers, use the [`Timer`] class directly (see
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
/// use fixed timesteps that allow systems to run multiple times per frame.
pub fn on_real_timer(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Repeating);
move |time: Res<Time<Real>>| {
timer.tick(time.delta());
timer.just_finished()
}
}
/// Run condition that is active *once* after the specified delay,
/// using [`Time`] to advance the timer.
/// The timer ticks at the rate of [`Time::relative_speed`].
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::once_after_delay;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(once_after_delay(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran once, after a second
/// }
/// ```
pub fn once_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Once);
move |time: Res<Time>| {
timer.tick(time.delta());
timer.just_finished()
}
}
/// Run condition that is active *once* after the specified delay,
/// using [`Time<Real>`] to advance the timer.
/// The timer ticks are not scaled.
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::once_after_delay;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(once_after_delay(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran once, after a second
/// }
/// ```
pub fn once_after_real_delay(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Once);
move |time: Res<Time<Real>>| {
timer.tick(time.delta());
timer.just_finished()
}
}
/// Run condition that is active *indefinitely* after the specified delay,
/// using [`Time`] to advance the timer.
/// The timer ticks at the rate of [`Time::relative_speed`].
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::repeating_after_delay;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(repeating_after_delay(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran every frame, after a second
/// }
/// ```
pub fn repeating_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Once);
move |time: Res<Time>| {
timer.tick(time.delta());
timer.finished()
}
}
/// Run condition that is active *indefinitely* after the specified delay,
/// using [`Time<Real>`] to advance the timer.
/// The timer ticks are not scaled.
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
/// # use core::time::Duration;
/// # use bevy_time::common_conditions::repeating_after_real_delay;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// tick.run_if(repeating_after_real_delay(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran every frame, after a second
/// }
/// ```
pub fn repeating_after_real_delay(
duration: Duration,
) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
let mut timer = Timer::new(duration, TimerMode::Once);
move |time: Res<Time<Real>>| {
timer.tick(time.delta());
timer.finished()
}
}
/// Run condition that is active when the [`Time<Virtual>`] clock is paused.
/// Use [`bevy_ecs::schedule::common_conditions::not`] to make it active when
/// it's not paused.
///
/// ```rust,no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
/// # use bevy_ecs::schedule::{common_conditions::not, IntoScheduleConfigs};
/// # use bevy_time::common_conditions::paused;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_systems(
/// Update,
/// (
/// is_paused.run_if(paused),
/// not_paused.run_if(not(paused)),
/// )
/// )
/// .run();
/// }
/// fn is_paused() {
/// // ran when time is paused
/// }
///
/// fn not_paused() {
/// // ran when time is not paused
/// }
/// ```
pub fn paused(time: Res<Time<Virtual>>) -> bool {
time.is_paused()
}
#[cfg(test)]
mod tests {
use super::*;
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};
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(on_timer(Duration::new(1, 0)))
.distributive_run_if(paused),
);
}
}

361
vendor/bevy_time/src/fixed.rs vendored Normal file
View File

@@ -0,0 +1,361 @@
use bevy_app::FixedMain;
use bevy_ecs::world::World;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::time::Duration;
use crate::{time::Time, virt::Virtual};
/// The fixed timestep game clock following virtual time.
///
/// A specialization of the [`Time`] structure. **For method documentation, see
/// [`Time<Fixed>#impl-Time<Fixed>`].**
///
/// It is automatically inserted as a resource by
/// [`TimePlugin`](crate::TimePlugin) and updated based on
/// [`Time<Virtual>`](Virtual). The fixed clock is automatically set as the
/// generic [`Time`] resource during [`FixedUpdate`](bevy_app::FixedUpdate)
/// schedule processing.
///
/// The fixed timestep clock advances in fixed-size increments, which is
/// extremely useful for writing logic (like physics) that should have
/// consistent behavior, regardless of framerate.
///
/// The default [`timestep()`](Time::timestep) is 64 hertz, or 15625
/// microseconds. This value was chosen because using 60 hertz has the potential
/// for a pathological interaction with the monitor refresh rate where the game
/// alternates between running two fixed timesteps and zero fixed timesteps per
/// frame (for example when running two fixed timesteps takes longer than a
/// frame). Additionally, the value is a power of two which losslessly converts
/// into [`f32`] and [`f64`].
///
/// To run a system on a fixed timestep, add it to one of the [`FixedMain`]
/// schedules, most commonly [`FixedUpdate`](bevy_app::FixedUpdate).
///
/// This schedule is run a number of times between
/// [`PreUpdate`](bevy_app::PreUpdate) and [`Update`](bevy_app::Update)
/// according to the accumulated [`overstep()`](Time::overstep) time divided by
/// the [`timestep()`](Time::timestep). This means the schedule may run 0, 1 or
/// more times during a single update (which typically corresponds to a rendered
/// frame).
///
/// `Time<Fixed>` and the generic [`Time`] resource will report a
/// [`delta()`](Time::delta) equal to [`timestep()`](Time::timestep) and always
/// grow [`elapsed()`](Time::elapsed) by one [`timestep()`](Time::timestep) per
/// iteration.
///
/// The fixed timestep clock follows the [`Time<Virtual>`](Virtual) clock, which
/// means it is affected by [`pause()`](Time::pause),
/// [`set_relative_speed()`](Time::set_relative_speed) and
/// [`set_max_delta()`](Time::set_max_delta) from virtual time. If the virtual
/// clock is paused, the [`FixedUpdate`](bevy_app::FixedUpdate) schedule will
/// not run. It is guaranteed that the [`elapsed()`](Time::elapsed) time in
/// `Time<Fixed>` is always between the previous `elapsed()` and the current
/// `elapsed()` value in `Time<Virtual>`, so the values are compatible.
///
/// Changing the timestep size while the game is running should not normally be
/// done, as having a regular interval is the point of this schedule, but it may
/// be necessary for effects like "bullet-time" if the normal granularity of the
/// fixed timestep is too big for the slowed down time. In this case,
/// [`set_timestep()`](Time::set_timestep) and be called to set a new value. The
/// new value will be used immediately for the next run of the
/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule, meaning that it will affect
/// the [`delta()`](Time::delta) value for the very next
/// [`FixedUpdate`](bevy_app::FixedUpdate), even if it is still during the same
/// frame. Any [`overstep()`](Time::overstep) present in the accumulator will be
/// processed according to the new [`timestep()`](Time::timestep) value.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]
pub struct Fixed {
timestep: Duration,
overstep: Duration,
}
impl Time<Fixed> {
/// Corresponds to 64 Hz.
const DEFAULT_TIMESTEP: Duration = Duration::from_micros(15625);
/// Return new fixed time clock with given timestep as [`Duration`]
///
/// # Panics
///
/// Panics if `timestep` is zero.
pub fn from_duration(timestep: Duration) -> Self {
let mut ret = Self::default();
ret.set_timestep(timestep);
ret
}
/// Return new fixed time clock with given timestep seconds as `f64`
///
/// # Panics
///
/// Panics if `seconds` is zero, negative or not finite.
pub fn from_seconds(seconds: f64) -> Self {
let mut ret = Self::default();
ret.set_timestep_seconds(seconds);
ret
}
/// Return new fixed time clock with given timestep frequency in Hertz (1/seconds)
///
/// # Panics
///
/// Panics if `hz` is zero, negative or not finite.
pub fn from_hz(hz: f64) -> Self {
let mut ret = Self::default();
ret.set_timestep_hz(hz);
ret
}
/// Returns the amount of virtual time that must pass before the fixed
/// timestep schedule is run again.
#[inline]
pub fn timestep(&self) -> Duration {
self.context().timestep
}
/// Sets the amount of virtual time that must pass before the fixed timestep
/// schedule is run again, as [`Duration`].
///
/// Takes effect immediately on the next run of the schedule, respecting
/// what is currently in [`Self::overstep`].
///
/// # Panics
///
/// Panics if `timestep` is zero.
#[inline]
pub fn set_timestep(&mut self, timestep: Duration) {
assert_ne!(
timestep,
Duration::ZERO,
"attempted to set fixed timestep to zero"
);
self.context_mut().timestep = timestep;
}
/// Sets the amount of virtual time that must pass before the fixed timestep
/// schedule is run again, as seconds.
///
/// Timestep is stored as a [`Duration`], which has fixed nanosecond
/// resolution and will be converted from the floating point number.
///
/// Takes effect immediately on the next run of the schedule, respecting
/// what is currently in [`Self::overstep`].
///
/// # Panics
///
/// Panics if `seconds` is zero, negative or not finite.
#[inline]
pub fn set_timestep_seconds(&mut self, seconds: f64) {
assert!(
seconds.is_sign_positive(),
"seconds less than or equal to zero"
);
assert!(seconds.is_finite(), "seconds is infinite");
self.set_timestep(Duration::from_secs_f64(seconds));
}
/// Sets the amount of virtual time that must pass before the fixed timestep
/// schedule is run again, as frequency.
///
/// The timestep value is set to `1 / hz`, converted to a [`Duration`] which
/// has fixed nanosecond resolution.
///
/// Takes effect immediately on the next run of the schedule, respecting
/// what is currently in [`Self::overstep`].
///
/// # Panics
///
/// Panics if `hz` is zero, negative or not finite.
#[inline]
pub fn set_timestep_hz(&mut self, hz: f64) {
assert!(hz.is_sign_positive(), "Hz less than or equal to zero");
assert!(hz.is_finite(), "Hz is infinite");
self.set_timestep_seconds(1.0 / hz);
}
/// Returns the amount of overstep time accumulated toward new steps, as
/// [`Duration`].
#[inline]
pub fn overstep(&self) -> Duration {
self.context().overstep
}
/// Discard a part of the overstep amount.
///
/// If `discard` is higher than overstep, the overstep becomes zero.
#[inline]
pub fn discard_overstep(&mut self, discard: Duration) {
let context = self.context_mut();
context.overstep = context.overstep.saturating_sub(discard);
}
/// Returns the amount of overstep time accumulated toward new steps, as an
/// [`f32`] fraction of the timestep.
#[inline]
pub fn overstep_fraction(&self) -> f32 {
self.context().overstep.as_secs_f32() / self.context().timestep.as_secs_f32()
}
/// Returns the amount of overstep time accumulated toward new steps, as an
/// [`f64`] fraction of the timestep.
#[inline]
pub fn overstep_fraction_f64(&self) -> f64 {
self.context().overstep.as_secs_f64() / self.context().timestep.as_secs_f64()
}
fn accumulate(&mut self, delta: Duration) {
self.context_mut().overstep += delta;
}
fn expend(&mut self) -> bool {
let timestep = self.timestep();
if let Some(new_value) = self.context_mut().overstep.checked_sub(timestep) {
// reduce accumulated and increase elapsed by period
self.context_mut().overstep = new_value;
self.advance_by(timestep);
true
} else {
// no more periods left in accumulated
false
}
}
}
impl Default for Fixed {
fn default() -> Self {
Self {
timestep: Time::<Fixed>::DEFAULT_TIMESTEP,
overstep: Duration::ZERO,
}
}
}
/// Runs [`FixedMain`] zero or more times based on delta of
/// [`Time<Virtual>`](Virtual) and [`Time::overstep`].
/// You can order your systems relative to this by using
/// [`RunFixedMainLoopSystem`](bevy_app::prelude::RunFixedMainLoopSystem).
pub(super) fn run_fixed_main_schedule(world: &mut World) {
let delta = world.resource::<Time<Virtual>>().delta();
world.resource_mut::<Time<Fixed>>().accumulate(delta);
// Run the schedule until we run out of accumulated time
let _ = world.try_schedule_scope(FixedMain, |world, schedule| {
while world.resource_mut::<Time<Fixed>>().expend() {
*world.resource_mut::<Time>() = world.resource::<Time<Fixed>>().as_generic();
schedule.run(world);
}
});
*world.resource_mut::<Time>() = world.resource::<Time<Virtual>>().as_generic();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_set_timestep() {
let mut time = Time::<Fixed>::default();
assert_eq!(time.timestep(), Time::<Fixed>::DEFAULT_TIMESTEP);
time.set_timestep(Duration::from_millis(500));
assert_eq!(time.timestep(), Duration::from_millis(500));
time.set_timestep_seconds(0.25);
assert_eq!(time.timestep(), Duration::from_millis(250));
time.set_timestep_hz(8.0);
assert_eq!(time.timestep(), Duration::from_millis(125));
}
#[test]
fn test_expend() {
let mut time = Time::<Fixed>::from_seconds(2.0);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
time.accumulate(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
assert!(!time.expend()); // false
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
time.accumulate(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.overstep(), Duration::from_secs(2));
assert_eq!(time.overstep_fraction(), 1.0);
assert_eq!(time.overstep_fraction_f64(), 1.0);
assert!(time.expend()); // true
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::ZERO);
assert_eq!(time.overstep_fraction(), 0.0);
assert_eq!(time.overstep_fraction_f64(), 0.0);
assert!(!time.expend()); // false
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::ZERO);
assert_eq!(time.overstep_fraction(), 0.0);
assert_eq!(time.overstep_fraction_f64(), 0.0);
time.accumulate(Duration::from_secs(1));
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
assert!(!time.expend()); // false
assert_eq!(time.delta(), Duration::from_secs(2));
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert_eq!(time.overstep_fraction(), 0.5);
assert_eq!(time.overstep_fraction_f64(), 0.5);
}
#[test]
fn test_expend_multiple() {
let mut time = Time::<Fixed>::from_seconds(2.0);
time.accumulate(Duration::from_secs(7));
assert_eq!(time.overstep(), Duration::from_secs(7));
assert!(time.expend()); // true
assert_eq!(time.elapsed(), Duration::from_secs(2));
assert_eq!(time.overstep(), Duration::from_secs(5));
assert!(time.expend()); // true
assert_eq!(time.elapsed(), Duration::from_secs(4));
assert_eq!(time.overstep(), Duration::from_secs(3));
assert!(time.expend()); // true
assert_eq!(time.elapsed(), Duration::from_secs(6));
assert_eq!(time.overstep(), Duration::from_secs(1));
assert!(!time.expend()); // false
assert_eq!(time.elapsed(), Duration::from_secs(6));
assert_eq!(time.overstep(), Duration::from_secs(1));
}
}

401
vendor/bevy_time/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,401 @@
#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
/// Common run conditions
pub mod common_conditions;
mod fixed;
mod real;
mod stopwatch;
mod time;
mod timer;
mod virt;
pub use fixed::*;
pub use real::*;
pub use stopwatch::*;
pub use time::*;
pub use timer::*;
pub use virt::*;
/// The time prelude.
///
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[doc(hidden)]
pub use crate::{Fixed, Real, Time, Timer, TimerMode, Virtual};
}
use bevy_app::{prelude::*, RunFixedMainLoop};
use bevy_ecs::{
event::{event_update_system, signal_event_update_system, EventRegistry, ShouldUpdateEvents},
prelude::*,
};
use bevy_platform::time::Instant;
use core::time::Duration;
#[cfg(feature = "std")]
pub use crossbeam_channel::TrySendError;
#[cfg(feature = "std")]
use crossbeam_channel::{Receiver, Sender};
/// Adds time functionality to Apps.
#[derive(Default)]
pub struct TimePlugin;
/// Updates the elapsed time. Any system that interacts with [`Time`] component should run after
/// this.
#[derive(Debug, PartialEq, Eq, Clone, Hash, SystemSet)]
pub struct TimeSystem;
impl Plugin for TimePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<Time>()
.init_resource::<Time<Real>>()
.init_resource::<Time<Virtual>>()
.init_resource::<Time<Fixed>>()
.init_resource::<TimeUpdateStrategy>();
#[cfg(feature = "bevy_reflect")]
{
app.register_type::<Time>()
.register_type::<Time<Real>>()
.register_type::<Time<Virtual>>()
.register_type::<Time<Fixed>>()
.register_type::<Timer>();
}
app.add_systems(
First,
time_system
.in_set(TimeSystem)
.ambiguous_with(event_update_system),
)
.add_systems(
RunFixedMainLoop,
run_fixed_main_schedule.in_set(RunFixedMainLoopSystem::FixedMainLoop),
);
// Ensure the events are not dropped until `FixedMain` systems can observe them
app.add_systems(FixedPostUpdate, signal_event_update_system);
let mut event_registry = app.world_mut().resource_mut::<EventRegistry>();
// We need to start in a waiting state so that the events are not updated until the first fixed update
event_registry.should_update = ShouldUpdateEvents::Waiting;
}
}
/// Configuration resource used to determine how the time system should run.
///
/// For most cases, [`TimeUpdateStrategy::Automatic`] is fine. When writing tests, dealing with
/// networking or similar, you may prefer to set the next [`Time`] value manually.
#[derive(Resource, Default)]
pub enum TimeUpdateStrategy {
/// [`Time`] will be automatically updated each frame using an [`Instant`] sent from the render world.
/// If nothing is sent, the system clock will be used instead.
#[cfg_attr(feature = "std", doc = "See [`TimeSender`] for more details.")]
#[default]
Automatic,
/// [`Time`] will be updated to the specified [`Instant`] value each frame.
/// In order for time to progress, this value must be manually updated each frame.
///
/// Note that the `Time` resource will not be updated until [`TimeSystem`] runs.
ManualInstant(Instant),
/// [`Time`] will be incremented by the specified [`Duration`] each frame.
ManualDuration(Duration),
}
/// Channel resource used to receive time from the render world.
#[cfg(feature = "std")]
#[derive(Resource)]
pub struct TimeReceiver(pub Receiver<Instant>);
/// Channel resource used to send time from the render world.
#[cfg(feature = "std")]
#[derive(Resource)]
pub struct TimeSender(pub Sender<Instant>);
/// Creates channels used for sending time between the render world and the main world.
#[cfg(feature = "std")]
pub fn create_time_channels() -> (TimeSender, TimeReceiver) {
// bound the channel to 2 since when pipelined the render phase can finish before
// the time system runs.
let (s, r) = crossbeam_channel::bounded::<Instant>(2);
(TimeSender(s), TimeReceiver(r))
}
/// The system used to update the [`Time`] used by app logic. If there is a render world the time is
/// sent from there to this system through channels. Otherwise the time is updated in this system.
pub fn time_system(
mut real_time: ResMut<Time<Real>>,
mut virtual_time: ResMut<Time<Virtual>>,
mut time: ResMut<Time>,
update_strategy: Res<TimeUpdateStrategy>,
#[cfg(feature = "std")] time_recv: Option<Res<TimeReceiver>>,
#[cfg(feature = "std")] mut has_received_time: Local<bool>,
) {
#[cfg(feature = "std")]
// TODO: Figure out how to handle this when using pipelined rendering.
let sent_time = match time_recv.map(|res| res.0.try_recv()) {
Some(Ok(new_time)) => {
*has_received_time = true;
Some(new_time)
}
Some(Err(_)) => {
if *has_received_time {
log::warn!("time_system did not receive the time from the render world! Calculations depending on the time may be incorrect.");
}
None
}
None => None,
};
match update_strategy.as_ref() {
TimeUpdateStrategy::Automatic => {
#[cfg(feature = "std")]
real_time.update_with_instant(sent_time.unwrap_or_else(Instant::now));
#[cfg(not(feature = "std"))]
real_time.update_with_instant(Instant::now());
}
TimeUpdateStrategy::ManualInstant(instant) => real_time.update_with_instant(*instant),
TimeUpdateStrategy::ManualDuration(duration) => real_time.update_with_duration(*duration),
}
update_virtual_time(&mut time, &mut virtual_time, &real_time);
}
#[cfg(test)]
#[expect(clippy::print_stdout, reason = "Allowed in tests.")]
mod tests {
use crate::{Fixed, Time, TimePlugin, TimeUpdateStrategy, Virtual};
use bevy_app::{App, FixedUpdate, Startup, Update};
use bevy_ecs::{
event::{Event, EventReader, EventRegistry, EventWriter, Events, ShouldUpdateEvents},
resource::Resource,
system::{Local, Res, ResMut},
};
use core::error::Error;
use core::time::Duration;
use std::println;
#[derive(Event)]
struct TestEvent<T: Default> {
sender: std::sync::mpsc::Sender<T>,
}
impl<T: Default> Drop for TestEvent<T> {
fn drop(&mut self) {
self.sender
.send(T::default())
.expect("Failed to send drop signal");
}
}
#[derive(Event)]
struct DummyEvent;
#[derive(Resource, Default)]
struct FixedUpdateCounter(u8);
fn count_fixed_updates(mut counter: ResMut<FixedUpdateCounter>) {
counter.0 += 1;
}
fn report_time(
mut frame_count: Local<u64>,
virtual_time: Res<Time<Virtual>>,
fixed_time: Res<Time<Fixed>>,
) {
println!(
"Virtual time on frame {}: {:?}",
*frame_count,
virtual_time.elapsed()
);
println!(
"Fixed time on frame {}: {:?}",
*frame_count,
fixed_time.elapsed()
);
*frame_count += 1;
}
#[test]
fn fixed_main_schedule_should_run_with_time_plugin_enabled() {
// Set the time step to just over half the fixed update timestep
// This way, it will have not accumulated enough time to run the fixed update after one update
// But will definitely have enough time after two updates
let fixed_update_timestep = Time::<Fixed>::default().timestep();
let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
let mut app = App::new();
app.add_plugins(TimePlugin)
.add_systems(FixedUpdate, count_fixed_updates)
.add_systems(Update, report_time)
.init_resource::<FixedUpdateCounter>()
.insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
// Frame 0
// Fixed update should not have run yet
app.update();
assert!(Duration::ZERO < fixed_update_timestep);
let counter = app.world().resource::<FixedUpdateCounter>();
assert_eq!(counter.0, 0, "Fixed update should not have run yet");
// Frame 1
// Fixed update should not have run yet
app.update();
assert!(time_step < fixed_update_timestep);
let counter = app.world().resource::<FixedUpdateCounter>();
assert_eq!(counter.0, 0, "Fixed update should not have run yet");
// Frame 2
// Fixed update should have run now
app.update();
assert!(2 * time_step > fixed_update_timestep);
let counter = app.world().resource::<FixedUpdateCounter>();
assert_eq!(counter.0, 1, "Fixed update should have run once");
// Frame 3
// Fixed update should have run exactly once still
app.update();
assert!(3 * time_step < 2 * fixed_update_timestep);
let counter = app.world().resource::<FixedUpdateCounter>();
assert_eq!(counter.0, 1, "Fixed update should have run once");
// Frame 4
// Fixed update should have run twice now
app.update();
assert!(4 * time_step > 2 * fixed_update_timestep);
let counter = app.world().resource::<FixedUpdateCounter>();
assert_eq!(counter.0, 2, "Fixed update should have run twice");
}
#[test]
fn events_get_dropped_regression_test_11528() -> Result<(), impl Error> {
let (tx1, rx1) = std::sync::mpsc::channel();
let (tx2, rx2) = std::sync::mpsc::channel();
let mut app = App::new();
app.add_plugins(TimePlugin)
.add_event::<TestEvent<i32>>()
.add_event::<TestEvent<()>>()
.add_systems(Startup, move |mut ev2: EventWriter<TestEvent<()>>| {
ev2.write(TestEvent {
sender: tx2.clone(),
});
})
.add_systems(Update, move |mut ev1: EventWriter<TestEvent<i32>>| {
// Keep adding events so this event type is processed every update
ev1.write(TestEvent {
sender: tx1.clone(),
});
})
.add_systems(
Update,
|mut ev1: EventReader<TestEvent<i32>>, mut ev2: EventReader<TestEvent<()>>| {
// Read events so they can be dropped
for _ in ev1.read() {}
for _ in ev2.read() {}
},
)
.insert_resource(TimeUpdateStrategy::ManualDuration(
Time::<Fixed>::default().timestep(),
));
for _ in 0..10 {
app.update();
}
// Check event type 1 as been dropped at least once
let _drop_signal = rx1.try_recv()?;
// Check event type 2 has been dropped
rx2.try_recv()
}
#[test]
fn event_update_should_wait_for_fixed_main() {
// Set the time step to just over half the fixed update timestep
// This way, it will have not accumulated enough time to run the fixed update after one update
// But will definitely have enough time after two updates
let fixed_update_timestep = Time::<Fixed>::default().timestep();
let time_step = fixed_update_timestep / 2 + Duration::from_millis(1);
fn send_event(mut events: ResMut<Events<DummyEvent>>) {
events.send(DummyEvent);
}
let mut app = App::new();
app.add_plugins(TimePlugin)
.add_event::<DummyEvent>()
.init_resource::<FixedUpdateCounter>()
.add_systems(Startup, send_event)
.add_systems(FixedUpdate, count_fixed_updates)
.insert_resource(TimeUpdateStrategy::ManualDuration(time_step));
for frame in 0..10 {
app.update();
let fixed_updates_seen = app.world().resource::<FixedUpdateCounter>().0;
let events = app.world().resource::<Events<DummyEvent>>();
let n_total_events = events.len();
let n_current_events = events.iter_current_update_events().count();
let event_registry = app.world().resource::<EventRegistry>();
let should_update = event_registry.should_update;
println!("Frame {frame}, {fixed_updates_seen} fixed updates seen. Should update: {should_update:?}");
println!("Total events: {n_total_events} | Current events: {n_current_events}",);
match frame {
0 | 1 => {
assert_eq!(fixed_updates_seen, 0);
assert_eq!(n_total_events, 1);
assert_eq!(n_current_events, 1);
assert_eq!(should_update, ShouldUpdateEvents::Waiting);
}
2 => {
assert_eq!(fixed_updates_seen, 1); // Time to trigger event updates
assert_eq!(n_total_events, 1);
assert_eq!(n_current_events, 1);
assert_eq!(should_update, ShouldUpdateEvents::Ready); // Prepping first update
}
3 => {
assert_eq!(fixed_updates_seen, 1);
assert_eq!(n_total_events, 1);
assert_eq!(n_current_events, 0); // First update has occurred
assert_eq!(should_update, ShouldUpdateEvents::Waiting);
}
4 => {
assert_eq!(fixed_updates_seen, 2); // Time to trigger the second update
assert_eq!(n_total_events, 1);
assert_eq!(n_current_events, 0);
assert_eq!(should_update, ShouldUpdateEvents::Ready); // Prepping second update
}
5 => {
assert_eq!(fixed_updates_seen, 2);
assert_eq!(n_total_events, 0); // Second update has occurred
assert_eq!(n_current_events, 0);
assert_eq!(should_update, ShouldUpdateEvents::Waiting);
}
_ => {
assert_eq!(n_total_events, 0); // No more events are sent
assert_eq!(n_current_events, 0);
}
}
}
}
}

251
vendor/bevy_time/src/real.rs vendored Normal file
View File

@@ -0,0 +1,251 @@
use bevy_platform::time::Instant;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use core::time::Duration;
use crate::time::Time;
/// Real time clock representing elapsed wall clock time.
///
/// A specialization of the [`Time`] structure. **For method documentation, see
/// [`Time<Real>#impl-Time<Real>`].**
///
/// It is automatically inserted as a resource by
/// [`TimePlugin`](crate::TimePlugin) and updated with time instants according
/// to [`TimeUpdateStrategy`](crate::TimeUpdateStrategy).[^disclaimer]
///
/// Note:
/// Using [`TimeUpdateStrategy::ManualDuration`](crate::TimeUpdateStrategy::ManualDuration)
/// allows for mocking the wall clock for testing purposes.
/// Besides this use case, it is not recommended to do this, as it will no longer
/// represent "wall clock" time as intended.
///
/// The [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values of this
/// clock should be used for anything which deals specifically with real time
/// (wall clock time). It will not be affected by relative game speed
/// adjustments, pausing or other adjustments.[^disclaimer]
///
/// The clock does not count time from [`startup()`](Time::startup) to
/// [`first_update()`](Time::first_update()) into elapsed, but instead will
/// start counting time from the first update call. [`delta()`](Time::delta) and
/// [`elapsed()`](Time::elapsed) will report zero on the first update as there
/// is no previous update instant. This means that a [`delta()`](Time::delta) of
/// zero must be handled without errors in application logic, as it may
/// theoretically also happen at other times.
///
/// [`Instant`]s for [`startup()`](Time::startup),
/// [`first_update()`](Time::first_update) and
/// [`last_update()`](Time::last_update) are recorded and accessible.
///
/// [^disclaimer]: When using [`TimeUpdateStrategy::ManualDuration`](crate::TimeUpdateStrategy::ManualDuration),
/// [`Time<Real>#impl-Time<Real>`] is only a *mock* of wall clock time.
///
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone, Default))]
pub struct Real {
startup: Instant,
first_update: Option<Instant>,
last_update: Option<Instant>,
}
impl Default for Real {
fn default() -> Self {
Self {
startup: Instant::now(),
first_update: None,
last_update: None,
}
}
}
impl Time<Real> {
/// Constructs a new `Time<Real>` instance with a specific startup
/// [`Instant`].
pub fn new(startup: Instant) -> Self {
Self::new_with(Real {
startup,
..Default::default()
})
}
/// Updates the internal time measurements.
///
/// Calling this method as part of your app will most likely result in
/// inaccurate timekeeping, as the [`Time`] resource is ordinarily managed
/// by the [`TimePlugin`](crate::TimePlugin).
pub fn update(&mut self) {
let instant = Instant::now();
self.update_with_instant(instant);
}
/// Updates time with a specified [`Duration`].
///
/// This method is provided for use in tests.
///
/// Calling this method as part of your app will most likely result in
/// inaccurate timekeeping, as the [`Time`] resource is ordinarily managed
/// by the [`TimePlugin`](crate::TimePlugin).
pub fn update_with_duration(&mut self, duration: Duration) {
let last_update = self.context().last_update.unwrap_or(self.context().startup);
self.update_with_instant(last_update + duration);
}
/// Updates time with a specified [`Instant`].
///
/// This method is provided for use in tests.
///
/// Calling this method as part of your app will most likely result in inaccurate timekeeping,
/// as the [`Time`] resource is ordinarily managed by the [`TimePlugin`](crate::TimePlugin).
pub fn update_with_instant(&mut self, instant: Instant) {
let Some(last_update) = self.context().last_update else {
let context = self.context_mut();
context.first_update = Some(instant);
context.last_update = Some(instant);
return;
};
let delta = instant - last_update;
self.advance_by(delta);
self.context_mut().last_update = Some(instant);
}
/// Returns the [`Instant`] the clock was created.
///
/// This usually represents when the app was started.
#[inline]
pub fn startup(&self) -> Instant {
self.context().startup
}
/// Returns the [`Instant`] when [`Self::update`] was first called, if it
/// exists.
///
/// This usually represents when the first app update started.
#[inline]
pub fn first_update(&self) -> Option<Instant> {
self.context().first_update
}
/// Returns the [`Instant`] when [`Self::update`] was last called, if it
/// exists.
///
/// This usually represents when the current app update started.
#[inline]
pub fn last_update(&self) -> Option<Instant> {
self.context().last_update
}
}
#[cfg(test)]
mod test {
use super::*;
// Waits until Instant::now() has increased.
//
// ```
// let previous = Instant::now();
// wait();
// assert!(Instant::now() > previous);
// ```
fn wait() {
let start = Instant::now();
while Instant::now() <= start {}
}
#[test]
fn test_update() {
let startup = Instant::now();
let mut time = Time::<Real>::new(startup);
assert_eq!(time.startup(), startup);
assert_eq!(time.first_update(), None);
assert_eq!(time.last_update(), None);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
wait();
time.update();
assert_ne!(time.first_update(), None);
assert_ne!(time.last_update(), None);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
wait();
time.update();
assert_ne!(time.first_update(), None);
assert_ne!(time.last_update(), None);
assert_ne!(time.last_update(), time.first_update());
assert_ne!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), time.delta());
wait();
let prev_elapsed = time.elapsed();
time.update();
assert_ne!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), prev_elapsed + time.delta());
}
#[test]
fn test_update_with_instant() {
let startup = Instant::now();
let mut time = Time::<Real>::new(startup);
wait();
let first_update = Instant::now();
time.update_with_instant(first_update);
assert_eq!(time.startup(), startup);
assert_eq!(time.first_update(), Some(first_update));
assert_eq!(time.last_update(), Some(first_update));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
wait();
let second_update = Instant::now();
time.update_with_instant(second_update);
assert_eq!(time.first_update(), Some(first_update));
assert_eq!(time.last_update(), Some(second_update));
assert_eq!(time.delta(), second_update - first_update);
assert_eq!(time.elapsed(), second_update - first_update);
wait();
let third_update = Instant::now();
time.update_with_instant(third_update);
assert_eq!(time.first_update(), Some(first_update));
assert_eq!(time.last_update(), Some(third_update));
assert_eq!(time.delta(), third_update - second_update);
assert_eq!(time.elapsed(), third_update - first_update);
}
#[test]
fn test_update_with_duration() {
let startup = Instant::now();
let mut time = Time::<Real>::new(startup);
time.update_with_duration(Duration::from_secs(1));
assert_eq!(time.startup(), startup);
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(1)));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
time.update_with_duration(Duration::from_secs(1));
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(2)));
assert_eq!(time.delta(), Duration::from_secs(1));
assert_eq!(time.elapsed(), Duration::from_secs(1));
time.update_with_duration(Duration::from_secs(1));
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(3)));
assert_eq!(time.delta(), Duration::from_secs(1));
assert_eq!(time.elapsed(), Duration::from_secs(2));
}
}

210
vendor/bevy_time/src/stopwatch.rs vendored Normal file
View File

@@ -0,0 +1,210 @@
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::{prelude::*, Reflect};
use core::time::Duration;
/// A Stopwatch is a struct that tracks elapsed time when started.
///
/// Note that in order to advance the stopwatch [`tick`](Stopwatch::tick) **MUST** be called.
/// # Examples
///
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
///
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // tick one second
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
///
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0)); // paused stopwatches don't tick
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
///
/// stopwatch.reset(); // reset the stopwatch
/// assert!(stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq)
)]
pub struct Stopwatch {
elapsed: Duration,
is_paused: bool,
}
impl Stopwatch {
/// Create a new unpaused `Stopwatch` with no elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let stopwatch = Stopwatch::new();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// assert_eq!(stopwatch.is_paused(), false);
/// ```
pub fn new() -> Self {
Default::default()
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs(1));
/// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1));
/// ```
///
/// # See Also
///
/// [`elapsed_secs`](Stopwatch::elapsed_secs) - if an `f32` value is desirable instead.
/// [`elapsed_secs_f64`](Stopwatch::elapsed_secs_f64) - if an `f64` is desirable instead.
#[inline]
pub fn elapsed(&self) -> Duration {
self.elapsed
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch, in seconds.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs(1));
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
///
/// # See Also
///
/// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
/// [`elapsed_secs_f64`](Stopwatch::elapsed_secs_f64) - if an `f64` is desirable instead.
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.elapsed().as_secs_f32()
}
/// Returns the elapsed time since the last [`reset`](Stopwatch::reset)
/// of the stopwatch, in seconds, as f64.
///
/// # See Also
///
/// [`elapsed`](Stopwatch::elapsed) - if a `Duration` is desirable instead.
/// [`elapsed_secs`](Stopwatch::elapsed_secs) - if an `f32` is desirable instead.
#[inline]
pub fn elapsed_secs_f64(&self) -> f64 {
self.elapsed().as_secs_f64()
}
/// Sets the elapsed time of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.set_elapsed(Duration::from_secs_f32(1.0));
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.elapsed = time;
}
/// Advance the stopwatch by `delta` seconds.
/// If the stopwatch is paused, ticking will not have any effect
/// on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(stopwatch.elapsed_secs(), 1.5);
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if !self.is_paused() {
self.elapsed = self.elapsed.saturating_add(delta);
}
self
}
/// Pauses the stopwatch. Any call to [`tick`](Stopwatch::tick) while
/// paused will not have any effect on the elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// assert!(stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn pause(&mut self) {
self.is_paused = true;
}
/// Unpauses the stopwatch. Resume the effect of ticking on elapsed time.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.pause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// stopwatch.unpause();
/// stopwatch.tick(Duration::from_secs_f32(1.0));
/// assert!(!stopwatch.is_paused());
/// assert_eq!(stopwatch.elapsed_secs(), 1.0);
/// ```
#[inline]
pub fn unpause(&mut self) {
self.is_paused = false;
}
/// Returns `true` if the stopwatch is paused.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut stopwatch = Stopwatch::new();
/// assert!(!stopwatch.is_paused());
/// stopwatch.pause();
/// assert!(stopwatch.is_paused());
/// stopwatch.unpause();
/// assert!(!stopwatch.is_paused());
/// ```
#[inline]
pub fn is_paused(&self) -> bool {
self.is_paused
}
/// Resets the stopwatch. The reset doesn't affect the paused state of the stopwatch.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut stopwatch = Stopwatch::new();
/// stopwatch.tick(Duration::from_secs_f32(1.5));
/// stopwatch.reset();
/// assert_eq!(stopwatch.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn reset(&mut self) {
self.elapsed = Default::default();
}
}

545
vendor/bevy_time/src/time.rs vendored Normal file
View File

@@ -0,0 +1,545 @@
use bevy_ecs::resource::Resource;
use core::time::Duration;
#[cfg(feature = "bevy_reflect")]
use {
bevy_ecs::reflect::ReflectResource,
bevy_reflect::{std_traits::ReflectDefault, Reflect},
};
/// A generic clock resource that tracks how much it has advanced since its
/// previous update and since its creation.
///
/// Multiple instances of this resource are inserted automatically by
/// [`TimePlugin`](crate::TimePlugin):
///
/// - [`Time<Real>`](crate::real::Real) tracks real wall-clock time elapsed.
/// - [`Time<Virtual>`](crate::virt::Virtual) tracks virtual game time that may
/// be paused or scaled.
/// - [`Time<Fixed>`](crate::fixed::Fixed) tracks fixed timesteps based on
/// virtual time.
/// - [`Time`] is a generic clock that corresponds to "current" or "default"
/// time for systems. It contains [`Time<Virtual>`](crate::virt::Virtual)
/// except inside the [`FixedMain`](bevy_app::FixedMain) schedule when it
/// contains [`Time<Fixed>`](crate::fixed::Fixed).
///
/// The time elapsed since the previous time this clock was advanced is saved as
/// [`delta()`](Time::delta) and the total amount of time the clock has advanced
/// is saved as [`elapsed()`](Time::elapsed). Both are represented as exact
/// [`Duration`] values with fixed nanosecond precision. The clock does not
/// support time moving backwards, but it can be updated with [`Duration::ZERO`]
/// which will set [`delta()`](Time::delta) to zero.
///
/// These values are also available in seconds as `f32` via
/// [`delta_secs()`](Time::delta_secs) and
/// [`elapsed_secs()`](Time::elapsed_secs), and also in seconds as `f64`
/// via [`delta_secs_f64()`](Time::delta_secs_f64) and
/// [`elapsed_secs_f64()`](Time::elapsed_secs_f64).
///
/// Since [`elapsed_secs()`](Time::elapsed_secs) will grow constantly and
/// is `f32`, it will exhibit gradual precision loss. For applications that
/// require an `f32` value but suffer from gradual precision loss there is
/// [`elapsed_secs_wrapped()`](Time::elapsed_secs_wrapped) available. The
/// same wrapped value is also available as [`Duration`] and `f64` for
/// consistency. The wrap period is by default 1 hour, and can be set by
/// [`set_wrap_period()`](Time::set_wrap_period).
///
/// # Accessing clocks
///
/// By default, any systems requiring current [`delta()`](Time::delta) or
/// [`elapsed()`](Time::elapsed) should use `Res<Time>` to access the default
/// time configured for the program. By default, this refers to
/// [`Time<Virtual>`](crate::virt::Virtual) except during the
/// [`FixedMain`](bevy_app::FixedMain) schedule when it refers to
/// [`Time<Fixed>`](crate::fixed::Fixed). This ensures your system can be used
/// either in [`Update`](bevy_app::Update) or
/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule depending on what is needed.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// #
/// fn ambivalent_system(time: Res<Time>) {
/// println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
/// }
/// ```
///
/// If your system needs to react based on real time (wall clock time), like for
/// user interfaces, it should use `Res<Time<Real>>`. The
/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will always
/// correspond to real time and will not be affected by pause, time scaling or
/// other tweaks.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// #
/// fn real_time_system(time: Res<Time<Real>>) {
/// println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
/// }
/// ```
///
/// If your system specifically needs to access fixed timestep clock, even when
/// placed in `Update` schedule, you should use `Res<Time<Fixed>>`. The
/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will
/// correspond to the latest fixed timestep that has been run.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// #
/// fn fixed_time_system(time: Res<Time<Fixed>>) {
/// println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
/// }
/// ```
///
/// Finally, if your system specifically needs to know the current virtual game
/// time, even if placed inside [`FixedUpdate`](bevy_app::FixedUpdate), for
/// example to know if the game is [`was_paused()`](Time::was_paused) or to use
/// [`effective_speed()`](Time::effective_speed), you can use
/// `Res<Time<Virtual>>`. However, if the system is placed in
/// [`FixedUpdate`](bevy_app::FixedUpdate), extra care must be used because your
/// system might be run multiple times with the same [`delta()`](Time::delta)
/// and [`elapsed()`](Time::elapsed) values as the virtual game time has not
/// changed between the iterations.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// #
/// fn fixed_time_system(time: Res<Time<Virtual>>) {
/// println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
/// println!("also the relative speed of the game is now {}", time.effective_speed());
/// }
/// ```
///
/// If you need to change the settings for any of the clocks, for example to
/// [`pause()`](Time::pause) the game, you should use `ResMut<Time<Virtual>>`.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// #
/// #[derive(Event)]
/// struct PauseEvent(bool);
///
/// fn pause_system(mut time: ResMut<Time<Virtual>>, mut events: EventReader<PauseEvent>) {
/// for ev in events.read() {
/// if ev.0 {
/// time.pause();
/// } else {
/// time.unpause();
/// }
/// }
/// }
/// ```
///
/// # Adding custom clocks
///
/// New custom clocks can be created by creating your own struct as a context
/// and passing it to [`new_with()`](Time::new_with). These clocks can be
/// inserted as resources as normal and then accessed by systems. You can use
/// the [`advance_by()`](Time::advance_by) or [`advance_to()`](Time::advance_to)
/// methods to move the clock forwards based on your own logic.
///
/// If you want to add methods for your time instance and they require access to
/// both your context and the generic time part, it's probably simplest to add a
/// custom trait for them and implement it for `Time<Custom>`.
///
/// Your context struct will need to implement the [`Default`] trait because
/// [`Time`] structures support reflection. It also makes initialization trivial
/// by being able to call `app.init_resource::<Time<Custom>>()`.
///
/// You can also replace the "generic" `Time` clock resource if the "default"
/// time for your game should not be the default virtual time provided. You can
/// get a "generic" snapshot of your clock by calling `as_generic()` and then
/// overwrite the [`Time`] resource with it. The default systems added by
/// [`TimePlugin`](crate::TimePlugin) will overwrite the [`Time`] clock during
/// [`First`](bevy_app::First) and [`FixedUpdate`](bevy_app::FixedUpdate)
/// schedules.
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use bevy_time::prelude::*;
/// # use bevy_platform::time::Instant;
/// #
/// #[derive(Debug)]
/// struct Custom {
/// last_external_time: Instant,
/// }
///
/// impl Default for Custom {
/// fn default() -> Self {
/// Self {
/// last_external_time: Instant::now(),
/// }
/// }
/// }
///
/// trait CustomTime {
/// fn update_from_external(&mut self, instant: Instant);
/// }
///
/// impl CustomTime for Time<Custom> {
/// fn update_from_external(&mut self, instant: Instant) {
/// let delta = instant - self.context().last_external_time;
/// self.advance_by(delta);
/// self.context_mut().last_external_time = instant;
/// }
/// }
/// ```
#[derive(Resource, Debug, Copy, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource, Default))]
pub struct Time<T: Default = ()> {
context: T,
wrap_period: Duration,
delta: Duration,
delta_secs: f32,
delta_secs_f64: f64,
elapsed: Duration,
elapsed_secs: f32,
elapsed_secs_f64: f64,
elapsed_wrapped: Duration,
elapsed_secs_wrapped: f32,
elapsed_secs_wrapped_f64: f64,
}
impl<T: Default> Time<T> {
const DEFAULT_WRAP_PERIOD: Duration = Duration::from_secs(3600); // 1 hour
/// Create a new clock from context with [`Self::delta`] and [`Self::elapsed`] starting from
/// zero.
pub fn new_with(context: T) -> Self {
Self {
context,
..Default::default()
}
}
/// Advance this clock by adding a `delta` duration to it.
///
/// The added duration will be returned by [`Self::delta`] and
/// [`Self::elapsed`] will be increased by the duration. Adding
/// [`Duration::ZERO`] is allowed and will set [`Self::delta`] to zero.
pub fn advance_by(&mut self, delta: Duration) {
self.delta = delta;
self.delta_secs = self.delta.as_secs_f32();
self.delta_secs_f64 = self.delta.as_secs_f64();
self.elapsed += delta;
self.elapsed_secs = self.elapsed.as_secs_f32();
self.elapsed_secs_f64 = self.elapsed.as_secs_f64();
self.elapsed_wrapped = duration_rem(self.elapsed, self.wrap_period);
self.elapsed_secs_wrapped = self.elapsed_wrapped.as_secs_f32();
self.elapsed_secs_wrapped_f64 = self.elapsed_wrapped.as_secs_f64();
}
/// Advance this clock to a specific `elapsed` time.
///
/// [`Self::delta()`] will return the amount of time the clock was advanced
/// and [`Self::elapsed()`] will be the `elapsed` value passed in. Cannot be
/// used to move time backwards.
///
/// # Panics
///
/// Panics if `elapsed` is less than `Self::elapsed()`.
pub fn advance_to(&mut self, elapsed: Duration) {
assert!(
elapsed >= self.elapsed,
"tried to move time backwards to an earlier elapsed moment"
);
self.advance_by(elapsed - self.elapsed);
}
/// Returns the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).
///
/// **Note:** The default modulus is one hour.
#[inline]
pub fn wrap_period(&self) -> Duration {
self.wrap_period
}
/// Sets the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).
///
/// **Note:** This will not take effect until the next update.
///
/// # Panics
///
/// Panics if `wrap_period` is a zero-length duration.
#[inline]
pub fn set_wrap_period(&mut self, wrap_period: Duration) {
assert!(!wrap_period.is_zero(), "division by zero");
self.wrap_period = wrap_period;
}
/// Returns how much time has advanced since the last [`update`](#method.update), as a
/// [`Duration`].
#[inline]
pub fn delta(&self) -> Duration {
self.delta
}
/// Returns how much time has advanced since the last [`update`](#method.update), as [`f32`]
/// seconds.
#[inline]
pub fn delta_secs(&self) -> f32 {
self.delta_secs
}
/// Returns how much time has advanced since the last [`update`](#method.update), as [`f64`]
/// seconds.
#[inline]
pub fn delta_secs_f64(&self) -> f64 {
self.delta_secs_f64
}
/// Returns how much time has advanced since [`startup`](#method.startup), as [`Duration`].
#[inline]
pub fn elapsed(&self) -> Duration {
self.elapsed
}
/// Returns how much time has advanced since [`startup`](#method.startup), as [`f32`] seconds.
///
/// **Note:** This is a monotonically increasing value. Its precision will degrade over time.
/// If you need an `f32` but that precision loss is unacceptable,
/// use [`elapsed_secs_wrapped`](#method.elapsed_secs_wrapped).
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.elapsed_secs
}
/// Returns how much time has advanced since [`startup`](#method.startup), as [`f64`] seconds.
#[inline]
pub fn elapsed_secs_f64(&self) -> f64 {
self.elapsed_secs_f64
}
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
/// the [`wrap_period`](#method.wrap_period), as [`Duration`].
#[inline]
pub fn elapsed_wrapped(&self) -> Duration {
self.elapsed_wrapped
}
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
/// the [`wrap_period`](#method.wrap_period), as [`f32`] seconds.
///
/// This method is intended for applications (e.g. shaders) that require an [`f32`] value but
/// suffer from the gradual precision loss of [`elapsed_secs`](#method.elapsed_secs).
#[inline]
pub fn elapsed_secs_wrapped(&self) -> f32 {
self.elapsed_secs_wrapped
}
/// Returns how much time has advanced since [`startup`](#method.startup) modulo
/// the [`wrap_period`](#method.wrap_period), as [`f64`] seconds.
#[inline]
pub fn elapsed_secs_wrapped_f64(&self) -> f64 {
self.elapsed_secs_wrapped_f64
}
/// Returns a reference to the context of this specific clock.
#[inline]
pub fn context(&self) -> &T {
&self.context
}
/// Returns a mutable reference to the context of this specific clock.
#[inline]
pub fn context_mut(&mut self) -> &mut T {
&mut self.context
}
/// Returns a copy of this clock as fully generic clock without context.
#[inline]
pub fn as_generic(&self) -> Time<()> {
Time {
context: (),
wrap_period: self.wrap_period,
delta: self.delta,
delta_secs: self.delta_secs,
delta_secs_f64: self.delta_secs_f64,
elapsed: self.elapsed,
elapsed_secs: self.elapsed_secs,
elapsed_secs_f64: self.elapsed_secs_f64,
elapsed_wrapped: self.elapsed_wrapped,
elapsed_secs_wrapped: self.elapsed_secs_wrapped,
elapsed_secs_wrapped_f64: self.elapsed_secs_wrapped_f64,
}
}
}
impl<T: Default> Default for Time<T> {
fn default() -> Self {
Self {
context: Default::default(),
wrap_period: Self::DEFAULT_WRAP_PERIOD,
delta: Duration::ZERO,
delta_secs: 0.0,
delta_secs_f64: 0.0,
elapsed: Duration::ZERO,
elapsed_secs: 0.0,
elapsed_secs_f64: 0.0,
elapsed_wrapped: Duration::ZERO,
elapsed_secs_wrapped: 0.0,
elapsed_secs_wrapped_f64: 0.0,
}
}
}
fn duration_rem(dividend: Duration, divisor: Duration) -> Duration {
// `Duration` does not have a built-in modulo operation
let quotient = (dividend.as_nanos() / divisor.as_nanos()) as u32;
dividend - (quotient * divisor)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_initial_state() {
let time: Time = Time::default();
assert_eq!(time.wrap_period(), Time::<()>::DEFAULT_WRAP_PERIOD);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.delta_secs(), 0.0);
assert_eq!(time.delta_secs_f64(), 0.0);
assert_eq!(time.elapsed(), Duration::ZERO);
assert_eq!(time.elapsed_secs(), 0.0);
assert_eq!(time.elapsed_secs_f64(), 0.0);
assert_eq!(time.elapsed_wrapped(), Duration::ZERO);
assert_eq!(time.elapsed_secs_wrapped(), 0.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
}
#[test]
fn test_advance_by() {
let mut time: Time = Time::default();
time.advance_by(Duration::from_millis(250));
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.delta_secs(), 0.25);
assert_eq!(time.delta_secs_f64(), 0.25);
assert_eq!(time.elapsed(), Duration::from_millis(250));
assert_eq!(time.elapsed_secs(), 0.25);
assert_eq!(time.elapsed_secs_f64(), 0.25);
time.advance_by(Duration::from_millis(500));
assert_eq!(time.delta(), Duration::from_millis(500));
assert_eq!(time.delta_secs(), 0.5);
assert_eq!(time.delta_secs_f64(), 0.5);
assert_eq!(time.elapsed(), Duration::from_millis(750));
assert_eq!(time.elapsed_secs(), 0.75);
assert_eq!(time.elapsed_secs_f64(), 0.75);
time.advance_by(Duration::ZERO);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.delta_secs(), 0.0);
assert_eq!(time.delta_secs_f64(), 0.0);
assert_eq!(time.elapsed(), Duration::from_millis(750));
assert_eq!(time.elapsed_secs(), 0.75);
assert_eq!(time.elapsed_secs_f64(), 0.75);
}
#[test]
fn test_advance_to() {
let mut time: Time = Time::default();
time.advance_to(Duration::from_millis(250));
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.delta_secs(), 0.25);
assert_eq!(time.delta_secs_f64(), 0.25);
assert_eq!(time.elapsed(), Duration::from_millis(250));
assert_eq!(time.elapsed_secs(), 0.25);
assert_eq!(time.elapsed_secs_f64(), 0.25);
time.advance_to(Duration::from_millis(750));
assert_eq!(time.delta(), Duration::from_millis(500));
assert_eq!(time.delta_secs(), 0.5);
assert_eq!(time.delta_secs_f64(), 0.5);
assert_eq!(time.elapsed(), Duration::from_millis(750));
assert_eq!(time.elapsed_secs(), 0.75);
assert_eq!(time.elapsed_secs_f64(), 0.75);
time.advance_to(Duration::from_millis(750));
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.delta_secs(), 0.0);
assert_eq!(time.delta_secs_f64(), 0.0);
assert_eq!(time.elapsed(), Duration::from_millis(750));
assert_eq!(time.elapsed_secs(), 0.75);
assert_eq!(time.elapsed_secs_f64(), 0.75);
}
#[test]
#[should_panic]
fn test_advance_to_backwards_panics() {
let mut time: Time = Time::default();
time.advance_to(Duration::from_millis(750));
time.advance_to(Duration::from_millis(250));
}
#[test]
fn test_wrapping() {
let mut time: Time = Time::default();
time.set_wrap_period(Duration::from_secs(3));
time.advance_by(Duration::from_secs(2));
assert_eq!(time.elapsed_wrapped(), Duration::from_secs(2));
assert_eq!(time.elapsed_secs_wrapped(), 2.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 2.0);
time.advance_by(Duration::from_secs(2));
assert_eq!(time.elapsed_wrapped(), Duration::from_secs(1));
assert_eq!(time.elapsed_secs_wrapped(), 1.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 1.0);
time.advance_by(Duration::from_secs(2));
assert_eq!(time.elapsed_wrapped(), Duration::ZERO);
assert_eq!(time.elapsed_secs_wrapped(), 0.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
time.advance_by(Duration::new(3, 250_000_000));
assert_eq!(time.elapsed_wrapped(), Duration::from_millis(250));
assert_eq!(time.elapsed_secs_wrapped(), 0.25);
assert_eq!(time.elapsed_secs_wrapped_f64(), 0.25);
}
#[test]
fn test_wrapping_change() {
let mut time: Time = Time::default();
time.set_wrap_period(Duration::from_secs(5));
time.advance_by(Duration::from_secs(8));
assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));
assert_eq!(time.elapsed_secs_wrapped(), 3.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);
time.set_wrap_period(Duration::from_secs(2));
assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));
assert_eq!(time.elapsed_secs_wrapped(), 3.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);
time.advance_by(Duration::ZERO);
// Time will wrap to modulo duration from full `elapsed()`, not to what
// is left in `elapsed_wrapped()`. This test of values is here to ensure
// that we notice if we change that behavior.
assert_eq!(time.elapsed_wrapped(), Duration::from_secs(0));
assert_eq!(time.elapsed_secs_wrapped(), 0.0);
assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
}
}

631
vendor/bevy_time/src/timer.rs vendored Normal file
View File

@@ -0,0 +1,631 @@
use crate::Stopwatch;
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::prelude::*;
use core::time::Duration;
/// Tracks elapsed time. Enters the finished state once `duration` is reached.
///
/// Non repeating timers will stop tracking and stay in the finished state until reset.
/// Repeating timers will only be in the finished state on each tick `duration` is reached or
/// exceeded, and can still be reset at any given point.
///
/// Paused timers will not have elapsed time increased.
///
/// Note that in order to advance the timer [`tick`](Timer::tick) **MUST** be called.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq)
)]
pub struct Timer {
stopwatch: Stopwatch,
duration: Duration,
mode: TimerMode,
finished: bool,
times_finished_this_tick: u32,
}
impl Timer {
/// Creates a new timer with a given duration.
///
/// See also [`Timer::from_seconds`](Timer::from_seconds).
pub fn new(duration: Duration, mode: TimerMode) -> Self {
Self {
duration,
mode,
..Default::default()
}
}
/// Creates a new timer with a given duration in seconds.
///
/// # Example
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// ```
pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {
Self {
duration: Duration::from_secs_f32(duration),
mode,
..Default::default()
}
}
/// Returns `true` if the timer has reached its duration.
///
/// For repeating timers, this method behaves identically to [`Timer::just_finished`].
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
///
/// let mut timer_once = Timer::from_seconds(1.0, TimerMode::Once);
/// timer_once.tick(Duration::from_secs_f32(1.5));
/// assert!(timer_once.finished());
/// timer_once.tick(Duration::from_secs_f32(0.5));
/// assert!(timer_once.finished());
///
/// let mut timer_repeating = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer_repeating.tick(Duration::from_secs_f32(1.1));
/// assert!(timer_repeating.finished());
/// timer_repeating.tick(Duration::from_secs_f32(0.8));
/// assert!(!timer_repeating.finished());
/// timer_repeating.tick(Duration::from_secs_f32(0.6));
/// assert!(timer_repeating.finished());
/// ```
#[inline]
pub fn finished(&self) -> bool {
self.finished
}
/// Returns `true` only on the tick the timer reached its duration.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(1.5));
/// assert!(timer.just_finished());
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert!(!timer.just_finished());
/// ```
#[inline]
pub fn just_finished(&self) -> bool {
self.times_finished_this_tick > 0
}
/// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
/// Will only equal `duration` when the timer is finished and non repeating.
///
/// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));
/// ```
#[inline]
pub fn elapsed(&self) -> Duration {
self.stopwatch.elapsed()
}
/// Returns the time elapsed on the timer as an `f32`.
/// See also [`Timer::elapsed`](Timer::elapsed).
#[inline]
pub fn elapsed_secs(&self) -> f32 {
self.stopwatch.elapsed_secs()
}
/// Returns the time elapsed on the timer as an `f64`.
/// See also [`Timer::elapsed`](Timer::elapsed).
#[inline]
pub fn elapsed_secs_f64(&self) -> f64 {
self.stopwatch.elapsed_secs_f64()
}
/// Sets the elapsed time of the timer without any other considerations.
///
/// See also [`Stopwatch::set`](Stopwatch::set).
///
/// #
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.set_elapsed(Duration::from_secs(2));
/// assert_eq!(timer.elapsed(), Duration::from_secs(2));
/// // the timer is not finished even if the elapsed time is greater than the duration.
/// assert!(!timer.finished());
/// ```
#[inline]
pub fn set_elapsed(&mut self, time: Duration) {
self.stopwatch.set_elapsed(time);
}
/// Returns the duration of the timer.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let timer = Timer::new(Duration::from_secs(1), TimerMode::Once);
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
#[inline]
pub fn duration(&self) -> Duration {
self.duration
}
/// Sets the duration of the timer.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);
/// timer.set_duration(Duration::from_secs(1));
/// assert_eq!(timer.duration(), Duration::from_secs(1));
/// ```
#[inline]
pub fn set_duration(&mut self, duration: Duration) {
self.duration = duration;
}
/// Returns the mode of the timer.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// assert_eq!(timer.mode(), TimerMode::Repeating);
/// ```
#[inline]
pub fn mode(&self) -> TimerMode {
self.mode
}
/// Sets the mode of the timer.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.set_mode(TimerMode::Once);
/// assert_eq!(timer.mode(), TimerMode::Once);
/// ```
#[doc(alias = "repeating")]
#[inline]
pub fn set_mode(&mut self, mode: TimerMode) {
if self.mode != TimerMode::Repeating && mode == TimerMode::Repeating && self.finished {
self.stopwatch.reset();
self.finished = self.just_finished();
}
self.mode = mode;
}
/// Advance the timer by `delta` seconds.
/// Non repeating timer will clamp at duration.
/// Repeating timer will wrap around.
/// Will not affect paused timers.
///
/// See also [`Stopwatch::tick`](Stopwatch::tick).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// let mut repeating = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.tick(Duration::from_secs_f32(1.5));
/// repeating.tick(Duration::from_secs_f32(1.5));
/// assert_eq!(timer.elapsed_secs(), 1.0);
/// assert_eq!(repeating.elapsed_secs(), 0.5);
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if self.paused() {
self.times_finished_this_tick = 0;
if self.mode == TimerMode::Repeating {
self.finished = false;
}
return self;
}
if self.mode != TimerMode::Repeating && self.finished() {
self.times_finished_this_tick = 0;
return self;
}
self.stopwatch.tick(delta);
self.finished = self.elapsed() >= self.duration();
if self.finished() {
if self.mode == TimerMode::Repeating {
self.times_finished_this_tick = self
.elapsed()
.as_nanos()
.checked_div(self.duration().as_nanos())
.map_or(u32::MAX, |x| x as u32);
self.set_elapsed(
self.elapsed()
.as_nanos()
.checked_rem(self.duration().as_nanos())
.map_or(Duration::ZERO, |x| Duration::from_nanos(x as u64)),
);
} else {
self.times_finished_this_tick = 1;
self.set_elapsed(self.duration());
}
} else {
self.times_finished_this_tick = 0;
}
self
}
/// Pauses the Timer. Disables the ticking of the timer.
///
/// See also [`Stopwatch::pause`](Stopwatch::pause).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed_secs(), 0.0);
/// ```
#[inline]
pub fn pause(&mut self) {
self.stopwatch.pause();
}
/// Unpauses the Timer. Resumes the ticking of the timer.
///
/// See also [`Stopwatch::unpause()`](Stopwatch::unpause).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.pause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// timer.unpause();
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.elapsed_secs(), 0.5);
/// ```
#[inline]
pub fn unpause(&mut self) {
self.stopwatch.unpause();
}
/// Returns `true` if the timer is paused.
///
/// See also [`Stopwatch::is_paused`](Stopwatch::is_paused).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// assert!(!timer.paused());
/// timer.pause();
/// assert!(timer.paused());
/// timer.unpause();
/// assert!(!timer.paused());
/// ```
#[inline]
pub fn paused(&self) -> bool {
self.stopwatch.is_paused()
}
/// Resets the timer. The reset doesn't affect the `paused` state of the timer.
///
/// See also [`Stopwatch::reset`](Stopwatch::reset).
///
/// Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(1.5));
/// timer.reset();
/// assert!(!timer.finished());
/// assert!(!timer.just_finished());
/// assert_eq!(timer.elapsed_secs(), 0.0);
/// ```
pub fn reset(&mut self) {
self.stopwatch.reset();
self.finished = false;
self.times_finished_this_tick = 0;
}
/// Returns the fraction of the timer elapsed time (goes from 0.0 to 1.0).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.fraction(), 0.25);
/// ```
#[inline]
pub fn fraction(&self) -> f32 {
if self.duration == Duration::ZERO {
1.0
} else {
self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
}
}
/// Returns the fraction of the timer remaining time (goes from 1.0 to 0.0).
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.fraction_remaining(), 0.75);
/// ```
#[inline]
pub fn fraction_remaining(&self) -> f32 {
1.0 - self.fraction()
}
/// Returns the remaining time in seconds
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::cmp::Ordering;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// let result = timer.remaining_secs().total_cmp(&1.5);
/// assert_eq!(Ordering::Equal, result);
/// ```
#[inline]
pub fn remaining_secs(&self) -> f32 {
self.remaining().as_secs_f32()
}
/// Returns the remaining time using Duration
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.remaining(), Duration::from_secs_f32(1.5));
/// ```
#[inline]
pub fn remaining(&self) -> Duration {
self.duration() - self.elapsed()
}
/// Returns the number of times a repeating timer
/// finished during the last [`tick`](Timer<T>::tick) call.
///
/// For non repeating-timers, this method will only ever
/// return 0 or 1.
///
/// # Examples
/// ```
/// # use bevy_time::*;
/// use std::time::Duration;
/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
/// timer.tick(Duration::from_secs_f32(6.0));
/// assert_eq!(timer.times_finished_this_tick(), 6);
/// timer.tick(Duration::from_secs_f32(2.0));
/// assert_eq!(timer.times_finished_this_tick(), 2);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.times_finished_this_tick(), 0);
/// ```
#[inline]
pub fn times_finished_this_tick(&self) -> u32 {
self.times_finished_this_tick
}
}
/// Specifies [`Timer`] behavior.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(
feature = "bevy_reflect",
derive(Reflect),
reflect(Default, Clone, PartialEq, Hash)
)]
pub enum TimerMode {
/// Run once and stop.
#[default]
Once,
/// Reset when finished.
Repeating,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_repeating_timer() {
let mut t = Timer::from_seconds(10.0, TimerMode::Once);
// Tick once, check all attributes
t.tick(Duration::from_secs_f32(0.25));
assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.elapsed_secs_f64(), 0.25);
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Once);
assert_eq!(t.fraction(), 0.025);
assert_eq!(t.fraction_remaining(), 0.975);
// Ticking while paused changes nothing
t.pause();
t.tick(Duration::from_secs_f32(500.0));
assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Once);
assert_eq!(t.fraction(), 0.025);
assert_eq!(t.fraction_remaining(), 0.975);
// Tick past the end and make sure elapsed doesn't go past 0.0 and other things update
t.unpause();
t.tick(Duration::from_secs_f32(500.0));
assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.elapsed_secs_f64(), 10.0);
assert!(t.finished());
assert!(t.just_finished());
assert_eq!(t.times_finished_this_tick(), 1);
assert_eq!(t.fraction(), 1.0);
assert_eq!(t.fraction_remaining(), 0.0);
// Continuing to tick when finished should only change just_finished
t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 10.0);
assert_eq!(t.elapsed_secs_f64(), 10.0);
assert!(t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.fraction(), 1.0);
assert_eq!(t.fraction_remaining(), 0.0);
}
#[test]
fn repeating_timer() {
let mut t = Timer::from_seconds(2.0, TimerMode::Repeating);
// Tick once, check all attributes
t.tick(Duration::from_secs_f32(0.75));
assert_eq!(t.elapsed_secs(), 0.75);
assert_eq!(t.elapsed_secs_f64(), 0.75);
assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.mode(), TimerMode::Repeating);
assert_eq!(t.fraction(), 0.375);
assert_eq!(t.fraction_remaining(), 0.625);
// Tick past the end and make sure elapsed wraps
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.elapsed_secs(), 0.25);
assert_eq!(t.elapsed_secs_f64(), 0.25);
assert!(t.finished());
assert!(t.just_finished());
assert_eq!(t.times_finished_this_tick(), 1);
assert_eq!(t.fraction(), 0.125);
assert_eq!(t.fraction_remaining(), 0.875);
// Continuing to tick should turn off both finished & just_finished for repeating timers
t.tick(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 1.25);
assert_eq!(t.elapsed_secs_f64(), 1.25);
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.fraction(), 0.625);
assert_eq!(t.fraction_remaining(), 0.375);
}
#[test]
fn times_finished_repeating() {
let mut t = Timer::from_seconds(1.0, TimerMode::Repeating);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(3.5));
assert_eq!(t.times_finished_this_tick(), 3);
assert_eq!(t.elapsed_secs(), 0.5);
assert_eq!(t.elapsed_secs_f64(), 0.5);
assert!(t.finished());
assert!(t.just_finished());
t.tick(Duration::from_secs_f32(0.2));
assert_eq!(t.times_finished_this_tick(), 0);
}
#[test]
fn times_finished_this_tick() {
let mut t = Timer::from_seconds(1.0, TimerMode::Once);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.times_finished_this_tick(), 1);
t.tick(Duration::from_secs_f32(0.5));
assert_eq!(t.times_finished_this_tick(), 0);
}
#[test]
fn times_finished_this_tick_repeating_zero_duration() {
let mut t = Timer::from_seconds(0.0, TimerMode::Repeating);
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.tick(Duration::from_secs(1));
assert_eq!(t.times_finished_this_tick(), u32::MAX);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.tick(Duration::from_secs(2));
assert_eq!(t.times_finished_this_tick(), u32::MAX);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
t.reset();
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.elapsed(), Duration::ZERO);
assert_eq!(t.fraction(), 1.0);
}
#[test]
fn times_finished_this_tick_precise() {
let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);
let duration = Duration::from_secs_f64(0.333);
// total duration: 0.333 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 0.666 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 0.999 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 1.332 => 34 times finished
t.tick(duration);
assert_eq!(t.times_finished_this_tick(), 34);
}
#[test]
fn paused() {
let mut t = Timer::from_seconds(10.0, TimerMode::Once);
t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.finished());
// A paused timer should change just_finished to false after a tick
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(t.finished());
}
#[test]
fn paused_repeating() {
let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);
t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.finished());
// A paused repeating timer should change finished and just_finished to false after a tick
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(!t.finished());
}
}

435
vendor/bevy_time/src/virt.rs vendored Normal file
View File

@@ -0,0 +1,435 @@
#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
use core::time::Duration;
use log::debug;
use crate::{real::Real, time::Time};
/// The virtual game clock representing game time.
///
/// A specialization of the [`Time`] structure. **For method documentation, see
/// [`Time<Virtual>#impl-Time<Virtual>`].**
///
/// Normally used as `Time<Virtual>`. It is automatically inserted as a resource
/// by [`TimePlugin`](crate::TimePlugin) and updated based on
/// [`Time<Real>`](Real). The virtual clock is automatically set as the default
/// generic [`Time`] resource for the update.
///
/// The virtual clock differs from real time clock in that it can be paused, sped up
/// and slowed down. It also limits how much it can advance in a single update
/// in order to prevent unexpected behavior in cases where updates do not happen
/// at regular intervals (e.g. coming back after the program was suspended a long time).
///
/// The virtual clock can be paused by calling [`pause()`](Time::pause) and
/// unpaused by calling [`unpause()`](Time::unpause). When the game clock is
/// paused [`delta()`](Time::delta) will be zero on each update, and
/// [`elapsed()`](Time::elapsed) will not grow.
/// [`effective_speed()`](Time::effective_speed) will return `0.0`. Calling
/// [`pause()`](Time::pause) will not affect value the [`delta()`](Time::delta)
/// value for the update currently being processed.
///
/// The speed of the virtual clock can be changed by calling
/// [`set_relative_speed()`](Time::set_relative_speed). A value of `2.0` means
/// that virtual clock should advance twice as fast as real time, meaning that
/// [`delta()`](Time::delta) values will be double of what
/// [`Time<Real>::delta()`](Time::delta) reports and
/// [`elapsed()`](Time::elapsed) will go twice as fast as
/// [`Time<Real>::elapsed()`](Time::elapsed). Calling
/// [`set_relative_speed()`](Time::set_relative_speed) will not affect the
/// [`delta()`](Time::delta) value for the update currently being processed.
///
/// The maximum amount of delta time that can be added by a single update can be
/// set by [`set_max_delta()`](Time::set_max_delta). This value serves a dual
/// purpose in the virtual clock.
///
/// If the game temporarily freezes due to any reason, such as disk access, a
/// blocking system call, or operating system level suspend, reporting the full
/// elapsed delta time is likely to cause bugs in game logic. Usually if a
/// laptop is suspended for an hour, it doesn't make sense to try to simulate
/// the game logic for the elapsed hour when resuming. Instead it is better to
/// lose the extra time and pretend a shorter duration of time passed. Setting
/// [`max_delta()`](Time::max_delta) to a relatively short time means that the
/// impact on game logic will be minimal.
///
/// If the game lags for some reason, meaning that it will take a longer time to
/// compute a frame than the real time that passes during the computation, then
/// we would fall behind in processing virtual time. If this situation persists,
/// and computing a frame takes longer depending on how much virtual time has
/// passed, the game would enter a "death spiral" where computing each frame
/// takes longer and longer and the game will appear to freeze. By limiting the
/// maximum time that can be added at once, we also limit the amount of virtual
/// time the game needs to compute for each frame. This means that the game will
/// run slow, and it will run slower than real time, but it will not freeze and
/// it will recover as soon as computation becomes fast again.
///
/// You should set [`max_delta()`](Time::max_delta) to a value that is
/// approximately the minimum FPS your game should have even if heavily lagged
/// for a moment. The actual FPS when lagged will be somewhat lower than this,
/// depending on how much more time it takes to compute a frame compared to real
/// time. You should also consider how stable your FPS is, as the limit will
/// also dictate how big of an FPS drop you can accept without losing time and
/// falling behind real time.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]
pub struct Virtual {
max_delta: Duration,
paused: bool,
relative_speed: f64,
effective_speed: f64,
}
impl Time<Virtual> {
/// The default amount of time that can added in a single update.
///
/// Equal to 250 milliseconds.
const DEFAULT_MAX_DELTA: Duration = Duration::from_millis(250);
/// Create new virtual clock with given maximum delta step [`Duration`]
///
/// # Panics
///
/// Panics if `max_delta` is zero.
pub fn from_max_delta(max_delta: Duration) -> Self {
let mut ret = Self::default();
ret.set_max_delta(max_delta);
ret
}
/// Returns the maximum amount of time that can be added to this clock by a
/// single update, as [`Duration`].
///
/// This is the maximum value [`Self::delta()`] will return and also to
/// maximum time [`Self::elapsed()`] will be increased by in a single
/// update.
///
/// This ensures that even if no updates happen for an extended amount of time,
/// the clock will not have a sudden, huge advance all at once. This also indirectly
/// limits the maximum number of fixed update steps that can run in a single update.
///
/// The default value is 250 milliseconds.
#[inline]
pub fn max_delta(&self) -> Duration {
self.context().max_delta
}
/// Sets the maximum amount of time that can be added to this clock by a
/// single update, as [`Duration`].
///
/// This is the maximum value [`Self::delta()`] will return and also to
/// maximum time [`Self::elapsed()`] will be increased by in a single
/// update.
///
/// This is used to ensure that even if the game freezes for a few seconds,
/// or is suspended for hours or even days, the virtual clock doesn't
/// suddenly jump forward for that full amount, which would likely cause
/// gameplay bugs or having to suddenly simulate all the intervening time.
///
/// If no updates happen for an extended amount of time, this limit prevents
/// having a sudden, huge advance all at once. This also indirectly limits
/// the maximum number of fixed update steps that can run in a single
/// update.
///
/// The default value is 250 milliseconds. If you want to disable this
/// feature, set the value to [`Duration::MAX`].
///
/// # Panics
///
/// Panics if `max_delta` is zero.
#[inline]
pub fn set_max_delta(&mut self, max_delta: Duration) {
assert_ne!(max_delta, Duration::ZERO, "tried to set max delta to zero");
self.context_mut().max_delta = max_delta;
}
/// Returns the speed the clock advances relative to your system clock, as [`f32`].
/// This is known as "time scaling" or "time dilation" in other engines.
#[inline]
pub fn relative_speed(&self) -> f32 {
self.relative_speed_f64() as f32
}
/// Returns the speed the clock advances relative to your system clock, as [`f64`].
/// This is known as "time scaling" or "time dilation" in other engines.
#[inline]
pub fn relative_speed_f64(&self) -> f64 {
self.context().relative_speed
}
/// Returns the speed the clock advanced relative to your system clock in
/// this update, as [`f32`].
///
/// Returns `0.0` if the game was paused or what the `relative_speed` value
/// was at the start of this update.
#[inline]
pub fn effective_speed(&self) -> f32 {
self.context().effective_speed as f32
}
/// Returns the speed the clock advanced relative to your system clock in
/// this update, as [`f64`].
///
/// Returns `0.0` if the game was paused or what the `relative_speed` value
/// was at the start of this update.
#[inline]
pub fn effective_speed_f64(&self) -> f64 {
self.context().effective_speed
}
/// Sets the speed the clock advances relative to your system clock, given as an [`f32`].
///
/// For example, setting this to `2.0` will make the clock advance twice as fast as your system
/// clock.
///
/// # Panics
///
/// Panics if `ratio` is negative or not finite.
#[inline]
pub fn set_relative_speed(&mut self, ratio: f32) {
self.set_relative_speed_f64(ratio as f64);
}
/// Sets the speed the clock advances relative to your system clock, given as an [`f64`].
///
/// For example, setting this to `2.0` will make the clock advance twice as fast as your system
/// clock.
///
/// # Panics
///
/// Panics if `ratio` is negative or not finite.
#[inline]
pub fn set_relative_speed_f64(&mut self, ratio: f64) {
assert!(ratio.is_finite(), "tried to go infinitely fast");
assert!(ratio >= 0.0, "tried to go back in time");
self.context_mut().relative_speed = ratio;
}
/// Stops the clock, preventing it from advancing until resumed.
#[inline]
pub fn pause(&mut self) {
self.context_mut().paused = true;
}
/// Resumes the clock if paused.
#[inline]
pub fn unpause(&mut self) {
self.context_mut().paused = false;
}
/// Returns `true` if the clock is currently paused.
#[inline]
pub fn is_paused(&self) -> bool {
self.context().paused
}
/// Returns `true` if the clock was paused at the start of this update.
#[inline]
pub fn was_paused(&self) -> bool {
self.context().effective_speed == 0.0
}
/// Updates the elapsed duration of `self` by `raw_delta`, up to the `max_delta`.
fn advance_with_raw_delta(&mut self, raw_delta: Duration) {
let max_delta = self.context().max_delta;
let clamped_delta = if raw_delta > max_delta {
debug!(
"delta time larger than maximum delta, clamping delta to {:?} and skipping {:?}",
max_delta,
raw_delta - max_delta
);
max_delta
} else {
raw_delta
};
let effective_speed = if self.context().paused {
0.0
} else {
self.context().relative_speed
};
let delta = if effective_speed != 1.0 {
clamped_delta.mul_f64(effective_speed)
} else {
// avoid rounding when at normal speed
clamped_delta
};
self.context_mut().effective_speed = effective_speed;
self.advance_by(delta);
}
}
impl Default for Virtual {
fn default() -> Self {
Self {
max_delta: Time::<Virtual>::DEFAULT_MAX_DELTA,
paused: false,
relative_speed: 1.0,
effective_speed: 1.0,
}
}
}
/// Advances [`Time<Virtual>`] and [`Time`] based on the elapsed [`Time<Real>`].
///
/// The virtual time will be advanced up to the provided [`Time::max_delta`].
pub fn update_virtual_time(current: &mut Time, virt: &mut Time<Virtual>, real: &Time<Real>) {
let raw_delta = real.delta();
virt.advance_with_raw_delta(raw_delta);
*current = virt.as_generic();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_default() {
let time = Time::<Virtual>::default();
assert!(!time.is_paused()); // false
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.max_delta(), Time::<Virtual>::DEFAULT_MAX_DELTA);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::ZERO);
}
#[test]
fn test_advance() {
let mut time = Time::<Virtual>::default();
time.advance_with_raw_delta(Duration::from_millis(125));
assert_eq!(time.delta(), Duration::from_millis(125));
assert_eq!(time.elapsed(), Duration::from_millis(125));
time.advance_with_raw_delta(Duration::from_millis(125));
assert_eq!(time.delta(), Duration::from_millis(125));
assert_eq!(time.elapsed(), Duration::from_millis(250));
time.advance_with_raw_delta(Duration::from_millis(125));
assert_eq!(time.delta(), Duration::from_millis(125));
assert_eq!(time.elapsed(), Duration::from_millis(375));
time.advance_with_raw_delta(Duration::from_millis(125));
assert_eq!(time.delta(), Duration::from_millis(125));
assert_eq!(time.elapsed(), Duration::from_millis(500));
}
#[test]
fn test_relative_speed() {
let mut time = Time::<Virtual>::default();
time.advance_with_raw_delta(Duration::from_millis(250));
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 1.0);
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.elapsed(), Duration::from_millis(250));
time.set_relative_speed_f64(2.0);
assert_eq!(time.relative_speed(), 2.0);
assert_eq!(time.effective_speed(), 1.0);
time.advance_with_raw_delta(Duration::from_millis(250));
assert_eq!(time.relative_speed(), 2.0);
assert_eq!(time.effective_speed(), 2.0);
assert_eq!(time.delta(), Duration::from_millis(500));
assert_eq!(time.elapsed(), Duration::from_millis(750));
time.set_relative_speed_f64(0.5);
assert_eq!(time.relative_speed(), 0.5);
assert_eq!(time.effective_speed(), 2.0);
time.advance_with_raw_delta(Duration::from_millis(250));
assert_eq!(time.relative_speed(), 0.5);
assert_eq!(time.effective_speed(), 0.5);
assert_eq!(time.delta(), Duration::from_millis(125));
assert_eq!(time.elapsed(), Duration::from_millis(875));
}
#[test]
fn test_pause() {
let mut time = Time::<Virtual>::default();
time.advance_with_raw_delta(Duration::from_millis(250));
assert!(!time.is_paused()); // false
assert!(!time.was_paused()); // false
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 1.0);
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.elapsed(), Duration::from_millis(250));
time.pause();
assert!(time.is_paused()); // true
assert!(!time.was_paused()); // false
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 1.0);
time.advance_with_raw_delta(Duration::from_millis(250));
assert!(time.is_paused()); // true
assert!(time.was_paused()); // true
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 0.0);
assert_eq!(time.delta(), Duration::ZERO);
assert_eq!(time.elapsed(), Duration::from_millis(250));
time.unpause();
assert!(!time.is_paused()); // false
assert!(time.was_paused()); // true
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 0.0);
time.advance_with_raw_delta(Duration::from_millis(250));
assert!(!time.is_paused()); // false
assert!(!time.was_paused()); // false
assert_eq!(time.relative_speed(), 1.0);
assert_eq!(time.effective_speed(), 1.0);
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.elapsed(), Duration::from_millis(500));
}
#[test]
fn test_max_delta() {
let mut time = Time::<Virtual>::default();
time.set_max_delta(Duration::from_millis(500));
time.advance_with_raw_delta(Duration::from_millis(250));
assert_eq!(time.delta(), Duration::from_millis(250));
assert_eq!(time.elapsed(), Duration::from_millis(250));
time.advance_with_raw_delta(Duration::from_millis(500));
assert_eq!(time.delta(), Duration::from_millis(500));
assert_eq!(time.elapsed(), Duration::from_millis(750));
time.advance_with_raw_delta(Duration::from_millis(750));
assert_eq!(time.delta(), Duration::from_millis(500));
assert_eq!(time.elapsed(), Duration::from_millis(1250));
time.set_max_delta(Duration::from_secs(1));
assert_eq!(time.max_delta(), Duration::from_secs(1));
time.advance_with_raw_delta(Duration::from_millis(750));
assert_eq!(time.delta(), Duration::from_millis(750));
assert_eq!(time.elapsed(), Duration::from_millis(2000));
time.advance_with_raw_delta(Duration::from_millis(1250));
assert_eq!(time.delta(), Duration::from_millis(1000));
assert_eq!(time.elapsed(), Duration::from_millis(3000));
}
}