{"id":15138006,"url":"https://github.com/mintlu8/bevy_defer","last_synced_at":"2025-05-07T07:12:46.828Z","repository":{"id":227554622,"uuid":"771598765","full_name":"mintlu8/bevy_defer","owner":"mintlu8","description":"A simple asynchronous runtime for executing async coroutines in the bevy engine.","archived":false,"fork":false,"pushed_at":"2024-09-08T15:50:05.000Z","size":497,"stargazers_count":50,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-10-29T21:02:01.109Z","etag":null,"topics":["async","bevy","coroutine","defer","futures","reactivity","rust","ui"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mintlu8.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-13T15:39:52.000Z","updated_at":"2024-10-21T20:56:07.000Z","dependencies_parsed_at":"2024-04-02T12:38:18.134Z","dependency_job_id":"635a3646-b73f-4b5a-af17-600a3e2381a4","html_url":"https://github.com/mintlu8/bevy_defer","commit_stats":{"total_commits":230,"total_committers":2,"mean_commits":115.0,"dds":0.004347826086956497,"last_synced_commit":"be7e7756e78949bc02d53b1e70ecaa1bf0ca3f01"},"previous_names":["mintlu8/bevy_defer"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mintlu8%2Fbevy_defer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mintlu8%2Fbevy_defer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mintlu8%2Fbevy_defer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mintlu8%2Fbevy_defer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mintlu8","download_url":"https://codeload.github.com/mintlu8/bevy_defer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243606856,"owners_count":20318314,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","bevy","coroutine","defer","futures","reactivity","rust","ui"],"created_at":"2024-09-26T07:04:17.025Z","updated_at":"2025-03-14T16:05:56.992Z","avatar_url":"https://github.com/mintlu8.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Bevy Defer\n\n[![Crates.io](https://img.shields.io/crates/v/bevy_defer.svg)](https://crates.io/crates/bevy_defer)\n[![Docs](https://docs.rs/bevy_defer/badge.svg)](https://docs.rs/bevy_defer/latest/bevy_defer/)\n[![Bevy tracking](https://img.shields.io/badge/Bevy%20tracking-released%20version-lightblue)](https://bevyengine.org/learn/book/plugin-development/)\n\nA simple asynchronous runtime for executing async coroutines.\n\n## Motivation\n\n`bevy_defer` is an async runtime for bevy with world access. Think of a future\nin `bevy_defer` as a `Command` that can take as long as it wants to complete.\nThey can even wait for each other or pause for a few seconds,\nwhich is pretty powerful!\n\n* So who needs `bevy_defer`?\n\n`bevy_defer` is not for every use case as it is somewhat opposite to `ecs`s\ndesign goal. If your game is a lot of simple conditionals,\n`ecs` likely suits your need just fine.\nBut if you have a game that does a lot of sequential actions or use a lot of\ncomplicated state machines, `bevy_defer`\nmight help you make your life easier.\n\n## Common use cases\n\n* Powerful Abstractions\n\n`Pin\u003cBox\u003cdyn Future\u003e\u003e` in `bevy_defer` is the strongest possible abstraction\nin bevy. Put that on a button's `OnClick` and it can do anything you want,\nmuch more powerful than a command or a system.\n\n* Turn based gameplay and event orchestration\n\nIn turn based games, you generally want to sequence actions after the player's input.\nin `bevy_defer` this is incredibly simple:\n\n```rust, ignore\nmove_to(position).await;\nlet damage = attack(enemy).await;\nshow_damage(damage).await;\n```\n\n* UI reactivity\n\n`bevy_defer` provides an alternative model to handle reactivity using\nasync rust.\n\n## Getting Started\n\nFirst add `AsyncPlugin`\n\n```rust, ignore\napp.add_plugins(AsyncPlugin::default_settings())\n```\n\nIf you want to react to events, states, or other things,\nadd their corresponding `react_to` functions.\n\n```rust, ignore\napp.react_to_state::\u003cPlayerAlive\u003e();\napp.react_to_event::\u003cPlayerAttack\u003e();\n```\n\n### Spawning a Task\n\nYou can spawn a task onto `bevy_defer`\nfrom `World`, `App`, `Commands`, `AsyncWorld` or `AsyncExecutor`.\n\nHere is an example:\n\n```rust, ignore\ncommands.spawn_task(|| async move {\n    AsyncWorld.sleep(500.0).await;\n    println!(\"Hello, World!\");\n    Ok(())\n});\n```\n\n### Accessing the world\n\nThe entry point of all world access is `AsyncWorld`,\nfor example a `Component` can be accessed by\n\n```rust, ignore\nlet translation = AsyncWorld\n    .entity(entity)\n    .component::\u003cTransform\u003e()\n    .get(|t| {\n        t.translation\n    }).unwrap()\n```\n\nThis is similar to `World` but all validation are\ndeferred to the access function.\n\n* New in `0.13`: the `fetch!` macro can be used:\n\n```rust, ignore\nlet translation = fetch!(entity, Transform).get(|t| t.translation).unwrap()\n```\n\nTo set some data:\n\n```rust, ignore\nlet richard = AsyncWorld.entity(richard_entity);\nrichard.component::\u003cHP\u003e().get_mut(|hp| hp.set(500))?;\n```\n\nThis works for all the bevy things you expect, `Resource`, `Query`, etc.\nSee the `access` module and the `AsyncAccess` trait for more detail.\n\nYou can add extension methods to these accessors via `Deref` if you own the\nunderlying types. See the `access::deref` module for more detail. The\n`async_access` derive macro can be useful for adding method to\nasync accessors.\n\n### Run Systems\n\nYou might have noticed `AsyncSystemParam` does not exist.\nIn order to run more complicated logic, use one of the one-shot\nsystem based API, like:\n\n```rust, ignore\nAsyncWorld.run_cached_system(my_system)\nAsyncWorld.run_cached_system_with_input(my_system2, 4)\n```\n\n### Event Orchestration\n\n* Coroutines\n\n`AsyncWorld.yield_now()` yields execution for the current frame,\nwith default settings this makes code like this run once per frame.\n\n```rust, ignore\nloop {\n    transform.set(|t| t.translation.x += delta_time)?;\n    AsyncWorld.yield_now().await\n}\n```\n\n* Pausing\n\n`AsyncWorld.sleep(4.0)` pauses the future for `4` seconds,\nwhile `AsyncWorld.sleep_frames(4)` pauses the future for `4` frames.\n\n* Concurrency\n\nUse `futures::join!` or `futures_lite::zip` to achieve concurrency:\n\n```rust, ignore\n// Do both at the same time.\njoin! {\n    dance(),\n    play_music(),\n}\n```\n\n* Cancellation\n\nUse `futures::select!` or `futures_lite::or` to achieve cancellation:\n\n```rust, ignore\n// Dance for no more than 5 seconds.\nselect! {\n    _ = dance() =\u003e (),\n    _ = sleep(5.0) =\u003e (),\n}\n```\n\n## Bridging Sync and Async\n\nCommunicating between sync and async can be daunting for new users. See\nthis amazing tokio article: \u003chttps://tokio.rs/tokio/topics/bridging\u003e.\n\nCommunicating from sync to async is simple, async code can provide channels\nto sync code and `await` on them, pausing the task.\nOnce sync code sends data through the channel, it will\nwake and resume the corresponding task.\n\nCommunicating from async to sync require more thought.\nThis usually means mutating the world or sending an event\nin an async function,\nthen a system can listen for that particular change in sync code.\n\n```rust,ignore\nasync {\n    fetch!(entity, IsJumping).set(|j| *j == true);\n    AsyncWold.send_event(Jump(entity))\n}\n\npub fn jump_system(query: Query\u003cName, Changed\u003cIsJumping\u003e\u003e) {\n    for name in \u0026query {\n        println!(\"{} is jumping!\", name);\n    }\n}\n```\n\n`States` is particularly powerful for this type of communication:\n\n```rust, ignore\nAsyncWorld.set_state(GameState::Loading);\n```\n\nThe core principle is async code should help sync code to\ndo less work, and vice versa!\n\n## Comparison with `bevy_tasks`\n\n`bevy_tasks` has no direct world access, which makes it difficult to write game\nlogic in it.\n\nThe core idea behind `bevy_defer` is simple:\n\n```rust, ignore\n// Pseudocode\nstatic WORLD_CELL: Mutex\u003c\u0026mut World\u003e;\n\nfn run_async_executor(world: \u0026mut World) {\n    let executor = world.get_executor();\n    WORLD_CELL.set(world);\n    executor.run();\n    WORLD_CELL.clear();\n}\n```\n\nFutures spawned onto the executor can access the `World`\nvia access functions, similar to how database transaction works:\n\n```rust, ignore\nWORLD_CELL.with(|world: \u0026mut World| {\n    world.entity(entity).get::\u003cTransform\u003e().clone()\n})\n```\n\nAs long as no references can be borrowed from the world,\nand the executor is single threaded, this is perfectly sound!\n\n## Implementation Details\n\n`bevy_defer` uses a single threaded runtime that always runs on bevy's main thread inside the main schedule,\nthis is ideal for simple game logic, wait heavy or IO heavy tasks, but CPU heavy tasks should not be run in `bevy_defer`. Unlike multithreaded runtimes, single threaded runtimes are more vulnerable to\nblocking tasks, so pay extra attention to this.\n\n`AsyncWorld::unblock` is an abstraction for using `AsyncComputeTaskPool` to run cpu intensive tasks. for blocking io, checkout crates like `async-fs` which are more suited for these tasks.\n\n## Versions\n\n| bevy | bevy_defer         |\n|------|--------------------|\n| 0.12 | 0.1                |\n| 0.13 | 0.2-0.11           |\n| 0.14 | 0.12               |\n| 0.15 | 0.13-latest        |\n\n## License\n\nLicense under either of\n\nApache License, Version 2.0 (LICENSE-APACHE or \u003chttp://www.apache.org/licenses/LICENSE-2.0\u003e)\nMIT license (LICENSE-MIT or \u003chttp://opensource.org/licenses/MIT\u003e)\nat your option.\n\n## Contribution\n\nContributions are welcome!\n\nUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmintlu8%2Fbevy_defer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmintlu8%2Fbevy_defer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmintlu8%2Fbevy_defer/lists"}