├── .github └── workflows │ └── test-main.yml ├── .gitignore ├── .vscode └── settings.json ├── CHANGELOG.md ├── COPYING ├── Cargo.toml ├── README.md ├── examples ├── alive.rs ├── allocate.rs ├── batch.rs ├── borrow.rs ├── demo.rs ├── derive.rs ├── external.rs ├── flow.rs ├── relation.rs ├── schedule.rs └── system.rs ├── license ├── APACHE └── MIT ├── proc-lib ├── Cargo.toml └── src │ ├── component.rs │ ├── flow.rs │ ├── lib.rs │ ├── query.rs │ ├── relation.rs │ └── system.rs ├── proc ├── Cargo.toml └── src │ └── lib.rs ├── rust-toolchain.toml └── src ├── action ├── buffer.rs ├── channel.rs ├── encoder.rs └── mod.rs ├── archetype.rs ├── bundle.rs ├── component.rs ├── dump ├── alkahest.rs ├── mod.rs ├── nanoserde.rs ├── query.rs └── serde.rs ├── entity ├── allocator.rs ├── entity.rs ├── mod.rs └── set.rs ├── epoch.rs ├── flow ├── entity.rs ├── futures.rs ├── mod.rs ├── tls.rs └── world.rs ├── hash.rs ├── lib.rs ├── nostd.rs ├── prelude.rs ├── query ├── alt.rs ├── any_of.rs ├── boolean.rs ├── borrow │ ├── all.rs │ ├── any.rs │ ├── mod.rs │ └── one.rs ├── copied.rs ├── entities.rs ├── fetch.rs ├── filter.rs ├── mod.rs ├── modified │ ├── alt.rs │ ├── any_of.rs │ ├── copied.rs │ ├── mod.rs │ ├── read.rs │ ├── with.rs │ └── write.rs ├── one_of.rs ├── option.rs ├── read.rs ├── tuple.rs ├── with_epoch │ ├── mod.rs │ └── read.rs └── write.rs ├── relation ├── child_of.rs ├── components.rs ├── mod.rs └── query │ ├── filter_related.rs │ ├── filter_related_by.rs │ ├── filter_relates.rs │ ├── filter_relates_to.rs │ ├── iter.rs │ ├── mod.rs │ ├── related.rs │ ├── relates.rs │ ├── relates_exclusive.rs │ └── relates_to.rs ├── resources.rs ├── scheduler ├── mod.rs └── threaded.rs ├── system ├── func │ ├── action.rs │ ├── mod.rs │ ├── res.rs │ ├── state.rs │ ├── view.rs │ └── world.rs └── mod.rs ├── test.rs ├── view ├── borrow.rs ├── extend.rs ├── index.rs ├── iter.rs ├── mod.rs ├── one.rs └── relation.rs └── world ├── builder.rs ├── edges.rs ├── get.rs ├── insert.rs ├── mod.rs ├── relation.rs ├── remove.rs ├── resource.rs ├── spawn.rs └── view.rs /.github/workflows/test-main.yml: -------------------------------------------------------------------------------- 1 | name: test-main 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Run cargo test 16 | run: cargo test --all --all-features 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.formatOnSave": true, 3 | "rust-analyzer.cargo.noDefaultFeatures": true, 4 | "rust-analyzer.cargo.features": [ 5 | "scheduler", 6 | "flow", 7 | "std", 8 | "threaded-scheduler", 9 | "rayon-scheduler", 10 | ], 11 | "cSpell.words": [ 12 | "despawn", 13 | "despawned", 14 | "despawning", 15 | "HEAPLESS", 16 | "nanoserde", 17 | "reborrow" 18 | ] 19 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this 6 | 7 | ## [0.7.0] 8 | 9 | ### Changed 10 | 11 | Renames in Flow API 12 | `FlowEntity` -> `Entity` 13 | `FlowWorld` -> `World` 14 | 15 | ### Fixes 16 | 17 | Fixes unsoundness in flows. 18 | 19 | ## [0.6.0] 20 | 21 | **Huge API changes** 22 | 23 | ### Added 24 | 25 | Flow API for writing asynchronous tasks, both local for an entity and global for world. 26 | Flows run on the internal scheduler that can be invoked with system or function call. 27 | 28 | Entity API is now extended for both ergonomics and performance. 29 | 30 | Entity now can be represented by different types that implement traits from following hierarchy 31 | - `Entity` - must be implemented for all entity types. Provide access to `EntityId` and fallible location lookup. 32 | - `AliveEntity: Entity` - provides guarantee that entity is alive and has non-fallible lookup functionality. 33 | - `LocatedEntity: AliveEntity` - same as above but location is know without access to the `World`. 34 | 35 | This hierarchy is implemented by following types out of the box: 36 | - `EntityId` - weakest type of entity. Knows its ID, may be expired already. Implements `Entity`. 37 | This is the only kind of entity that does not hold lifetime from `World` borrow. 38 | - `EntityBound` - entity know to be alive with lifetime bound to `World` borrow. Implements `AliveEntity`. 39 | Still requires `World` to lookup its location, so no performance gain here. But allows to use that does not return `NoSuchEntity` error. 40 | This kind is produced when querying relations. Related entities are guaranteed to be alive, but their location is not known. 41 | - `EntityLoc` - entity know to be alive and contains its location. Implements `LocatedEntity`. Sped up entity's components access. 42 | Stores location of the entity (which can't be changed since `World` is borrowed immutably) so it can access components without lookup in ID -> Location mapping. 43 | This kind is produced by `Entities` query. So accessing other components not in query is faster. It is better to not include `Option<&T>` in query if 44 | component is rare or when actual use of the component is rare. 45 | And `Option<&mut T>` is also preventing marking components modified when not needed. 46 | - `EntityRef` - Same as above but also borrows `World` mutably to allow entity modification. 47 | Produced by `World::spawn` and `World::entity` methods. 48 | Allows performing entity modifications without looking up entity location each time. 49 | Note that it is impossible to hold `EntityLoc` to modify entity since modification requires mutable borrow of `World`. 50 | 51 | ### Changed 52 | 53 | Query API. Renamed `QueryRef` to `ViewValue` to better reflect its meaning. 54 | `QueryOneRef` is now `ViewOne`. 55 | Data borrowing kinds goes to type level. 56 | 57 | `StaticallyBorrowed` is created when data is borrowed externally - suitable for systems. 58 | `ViewValue<..., StaticallyBorrowed>` has `View` alias. 59 | 60 | `RuntimeBorrow` borrows data at runtime. 61 | `ViewValue<..., RuntimeBorrow>` has `ViewCell` alias. 62 | 63 | `View` can be created using mutable borrow of `World` and commonly used in systems. 64 | Scheduler ensures that no conflicting `View` instances exist at the same time. 65 | This means that pair of systems with conflicting `View`s will not run in parallel 66 | and system with two conflicting `View`s is invalid and causes panic on scheduling. 67 | `ViewCell` can be created using shared borrow of `World` methods. 68 | In a system two or more `ViewCell` can conflict. User should ensure that they are not used 69 | at the same time. For example with conflicting `a: ViewCell` and `b: ViewCell` in a system 70 | the user can use view `a`, then drop or `unlock` it and then use view `b`. 71 | 72 | Intrasystem conflict resolution: 73 | Static conflict happens when two or more views declare access to the same component 74 | and at least one of them needs write access. 75 | Dynamic conflict happens when two or more views try to borrow same component of the same archetype 76 | and at least one of them needs write access. 77 | 78 | `IntoSystem::into_system` will fail with panic if static conflict is detected for `ViewCell`. 79 | Dynamic conflict is detected at runtime for `View` with `RuntimeBorrow` and produces a panic. 80 | 81 | Previously all conflicts were resolved at runtime, but they take quite a few precious CPU cycles. 82 | 83 | ## [0.3.3] - 2023-01-08 84 | 85 | ### Added 86 | 87 | - public method to create `ActionEncoder` from `ActionBuffer`. 88 | 89 | ## [0.3.2] - 2023-01-07 90 | 91 | ### Added 92 | 93 | - `Modified` query 94 | - `Modified` filter 95 | 96 | ## [0.3.1] - 2023-01-07 97 | 98 | ### Added 99 | 100 | - `Copied` query to yield component copies instead of references. -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2021 The edict Project Developers 2 | 3 | Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be 6 | copied, modified, or distributed except according to those terms. 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["proc-lib", "proc"] 3 | 4 | [workspace.package] 5 | version = "1.0.0-rc7" 6 | edition = "2021" 7 | authors = ["Zakarum "] 8 | license = "MIT OR Apache-2.0" 9 | documentation = "https://docs.rs/edict" 10 | homepage = "https://github.com/zakarumych/edict" 11 | repository = "https://github.com/zakarumych/edict" 12 | readme = "README.md" 13 | description = "Powerful entity-component-system library" 14 | keywords = ["ecs", "entity"] 15 | categories = ["no-std", "game-development", "data-structures"] 16 | 17 | [package] 18 | name = "edict" 19 | version.workspace = true 20 | edition.workspace = true 21 | authors.workspace = true 22 | license.workspace = true 23 | documentation.workspace = true 24 | homepage.workspace = true 25 | repository.workspace = true 26 | readme.workspace = true 27 | description.workspace = true 28 | keywords.workspace = true 29 | categories.workspace = true 30 | 31 | [features] 32 | # Enables support for async executor with ECS access 33 | # Without "std" it requires extern "C" functions provided, see `nostd` module 34 | flow = [] 35 | 36 | # Enables built-in scheduler 37 | # Without "std" it requires extern "C" functions provided, see `nostd` module 38 | scheduler = [] 39 | 40 | std = ["serde?/std", "alkahest?/std", "dep:parking_lot"] 41 | default = ["std", "scheduler", "flow", "hashbrown/default"] 42 | 43 | threaded-scheduler = ["std", "scheduler"] 44 | rayon-scheduler = ["dep:rayon", "threaded-scheduler"] 45 | 46 | [dependencies] 47 | edict-proc = { version = "=1.0.0-rc7", path = "proc" } 48 | amity = { version = "0.6.3", default-features = false, features = ["alloc", "flip-queue"] } 49 | hashbrown = { version = "0.15", default-features = false, features = [ 50 | "default-hasher", 51 | "raw-entry", 52 | ] } 53 | smallvec = { version = "1.10", features = ["union"], default-features = false } 54 | tiny-fn = "0.1.7" 55 | atomicell = "0.2" 56 | rayon = { version = "1.7", optional = true } 57 | alkahest = { version = "0.3.0", optional = true, features = [ 58 | "alloc", 59 | "fixed32", 60 | ], default-features = false } 61 | nanoserde = { version = "0.2", optional = true, default-features = false, features = ["binary"] } 62 | serde = { version = "1.0", optional = true, default-features = false, features = [ 63 | "alloc", 64 | ] } 65 | parking_lot = { version = "0.12", optional = true } 66 | slab = { version = "0.4", default-features = false } 67 | lock_api = { version = "0.4", default-features = false } 68 | 69 | [dev-dependencies] 70 | alkahest-proc = { version = "0.3.0" } 71 | 72 | [[example]] 73 | name = "alive" 74 | required-features = [] 75 | 76 | [[example]] 77 | name = "allocate" 78 | required-features = ["scheduler", "std"] 79 | 80 | [[example]] 81 | name = "batch" 82 | required-features = [] 83 | 84 | [[example]] 85 | name = "borrow" 86 | required-features = [] 87 | 88 | [[example]] 89 | name = "demo" 90 | required-features = [] 91 | 92 | [[example]] 93 | name = "derive" 94 | required-features = [] 95 | 96 | [[example]] 97 | name = "external" 98 | required-features = [] 99 | 100 | [[example]] 101 | name = "flow" 102 | required-features = ["scheduler", "flow", "std"] 103 | 104 | [[example]] 105 | name = "relation" 106 | required-features = [] 107 | 108 | [[example]] 109 | name = "schedule" 110 | required-features = ["scheduler", "std"] 111 | 112 | [package.metadata.docs.rs] 113 | all-features = true 114 | -------------------------------------------------------------------------------- /examples/alive.rs: -------------------------------------------------------------------------------- 1 | use edict::{component::Component, query::Entities, world::World}; 2 | 3 | #[derive(Debug, Component)] 4 | struct A; 5 | 6 | #[derive(Debug, Component)] 7 | struct B; 8 | 9 | fn main() { 10 | let mut world = World::new(); 11 | 12 | world.spawn((A, B)); 13 | 14 | let bs = world.view::<&B>(); 15 | 16 | for (e, a) in world.view::<(Entities, &A)>() { 17 | if let Some(b) = bs.get(e) { 18 | println!("A {:?} has B {:?}", a, b); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/allocate.rs: -------------------------------------------------------------------------------- 1 | //! This example shows how to use entity reserving mechanism. 2 | 3 | use edict::{ 4 | action::ActionEncoder, component::Component, entity::EntityLoc, scheduler::Scheduler, 5 | world::World, 6 | }; 7 | 8 | #[derive(Component)] 9 | pub struct Foo; 10 | 11 | fn main() { 12 | let mut world = World::new(); 13 | let mut scheduler = Scheduler::new(); 14 | 15 | scheduler.add_system(allocate_system); 16 | scheduler.add_system(spawn_system); 17 | 18 | scheduler.run_sequential(&mut world); 19 | scheduler.run_sequential(&mut world); 20 | 21 | assert_eq!(world.view::<&Foo>().iter().count(), 4); 22 | } 23 | 24 | fn allocate_system(world: &World, mut encoder: ActionEncoder) { 25 | let entity = world.allocate(); 26 | encoder.insert(entity, Foo); 27 | } 28 | 29 | fn spawn_system(mut encoder: ActionEncoder) { 30 | let _id: EntityLoc = encoder.spawn((Foo,)); 31 | } 32 | -------------------------------------------------------------------------------- /examples/batch.rs: -------------------------------------------------------------------------------- 1 | use edict::prelude::*; 2 | 3 | #[derive(Debug, Component)] 4 | struct A; 5 | 6 | #[derive(Debug, Component)] 7 | struct B; 8 | 9 | fn main() { 10 | let mut world = World::new(); 11 | 12 | world.spawn_batch((0..256).map(|_| (A, B))).spawn_all(); 13 | 14 | for (a, b) in world.view::<(&A, &B)>().into_iter_batched(32) { 15 | for (a, b) in a.iter().zip(b.iter()) { 16 | println!("{:?} {:?}", a, b); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/borrow.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any, TypeId}, 3 | fmt::Display, 4 | }; 5 | 6 | use edict::{component::Component, query::Entities, world::World}; 7 | 8 | #[derive(Component)] 9 | #[edict(borrow(dyn Display))] 10 | struct A; 11 | 12 | impl Display for A { 13 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 14 | f.write_str("A") 15 | } 16 | } 17 | 18 | #[derive(Component)] 19 | #[edict(borrow(dyn Display))] 20 | struct B; 21 | 22 | impl Display for B { 23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 | f.write_str("B") 25 | } 26 | } 27 | 28 | fn main() { 29 | let mut world = World::new(); 30 | 31 | // Spawn pair of entities. 32 | let _ = world.spawn((A,)); 33 | let _ = world.spawn((B,)); 34 | 35 | // Spawn entity with both. 36 | let _ = world.spawn((A, B)); 37 | 38 | // Borrow any component that exposes `Display` trait. 39 | // Skips entities without such component. 40 | for display in world.new_view().borrow_any_mut::() { 41 | println!("{}", display); 42 | } 43 | 44 | // Borrow component with specific `TypeId` as `Any` trait object. 45 | // Current behavior is to panic if component with that type id is found 46 | // and it doesn't exposes `Any` trait. 47 | for a in world 48 | .new_view() 49 | .borrow_one::(TypeId::of::()) 50 | .iter() 51 | { 52 | println!("{}", (a as &dyn Any).downcast_ref::().unwrap()); 53 | } 54 | 55 | // Borrow all components that expose `Display` trait. 56 | // This query yields vector of `&dyn Display` trait objects for each entity. 57 | // Current behavior is to skip entities with no such components. 58 | for (e, a) in world 59 | .view::() 60 | .borrow_all::() 61 | .iter() 62 | { 63 | print!("{}", e); 64 | for a in a { 65 | print!(" {}", a); 66 | } 67 | println!(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | //! This example contains usage of the main features of Edict ECS. 2 | 3 | use edict::{component::Component, world::World}; 4 | 5 | /// Just a type. 6 | /// Being `'static` makes it a proper component type. 7 | #[derive(Debug, PartialEq, Eq, Component)] 8 | struct Foo; 9 | 10 | /// Another type. 11 | #[derive(Debug, PartialEq, Eq, Component)] 12 | struct Bar; 13 | 14 | /// Another type. 15 | #[derive(Debug, PartialEq, Eq, Component)] 16 | struct Baz; 17 | 18 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Component)] 19 | #[edict(where T: 'static)] 20 | struct Value(T); 21 | 22 | fn main() { 23 | // Create new World. 24 | let mut world = World::new(); 25 | 26 | // World doesn't not contain any entities yet. 27 | 28 | // Spawn new entity in the world and give it three components, 29 | // namely `Foo`, `Bar` and `Baz` components. 30 | // `World::spawn` takes a `Bundle` implementation. 31 | // Tuples of various size implement `Bundle` trait. 32 | // Using this method with tuple will cause all tuple elements 33 | // to be added as components to the entity. 34 | // 35 | // Take care to now try to add duplicate components in one bundle 36 | // as method will surely panic. 37 | let e = world.spawn((Foo, Bar, Baz)); 38 | 39 | // Entity can be used to access components in the `World`. 40 | // Note that query returns `Result` because entity may be already despawned 41 | // or not have a component. 42 | assert!(matches!(e.get::<&Foo>(), Some(&Foo))); 43 | 44 | // To add another component to the entity call `EntityRef::insert`. 45 | let e = e.insert(Value(0u32)).unwrap(); 46 | assert!(matches!(e.get::<&Value>(), Some(&Value(0)))); 47 | 48 | // If the component is already present in entity, the value is simply replaced. 49 | let e = e.insert(Value(1u32)).unwrap(); 50 | assert!(matches!(e.get::<&Value>(), Some(&Value(1)))); 51 | 52 | // To add few components at once user should call `World::insert_bundle`. 53 | // This is much more efficient than adding components one by one. 54 | let e = e.insert_bundle((Value(1u8), Value(2u16))).unwrap(); 55 | 56 | // Spawned entities are despawned using [`World::despawn`] methods. 57 | e.despawn(); 58 | 59 | let _ = world.spawn((Foo, Bar)); 60 | 61 | // Entities can be spawned in batches using iterators over bundles. 62 | // Each iterator element is treated as bundle of components 63 | // and spawned entities receive them. 64 | // 65 | // This is more efficient than spawning in loop, 66 | // especially if iterator size hints are more or less accurate 67 | // and not `(0, None)` 68 | // 69 | // `World::spawn_batch` returns an iterator with `Entity` for each entity created. 70 | let _entities: Vec<_> = world.spawn_batch((0..10u32).map(|i| (Value(i),))).collect(); 71 | 72 | // User may choose to not consume returned iterator, or consume it partially. 73 | // This would cause bundle iterator to not be consumed as well and entities will not be spawned. 74 | // 75 | // This allows using unbound iterators to produce entities and stop at any moment. 76 | // 77 | // Prefer using using bound iterators to allow edict to reserve space for all entities. 78 | let _entities: Vec<_> = world 79 | .spawn_batch((0u32..).map(|i| (Value(i),))) 80 | .take(10) 81 | .collect(); 82 | } 83 | -------------------------------------------------------------------------------- /examples/derive.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::{Borrow, BorrowMut}, 3 | fmt::Debug, 4 | }; 5 | 6 | use edict::{component::Component, world::World}; 7 | 8 | #[derive(Component, Debug)] 9 | #[edict(borrow(dyn Debug, u32, f32))] 10 | #[edict(on_drop = |_, e, _| println!("A {e:?} dropped"))] 11 | pub struct A { 12 | a: f32, 13 | } 14 | 15 | impl Borrow for A { 16 | fn borrow(&self) -> &u32 { 17 | &0 18 | } 19 | } 20 | 21 | impl Borrow for A { 22 | fn borrow(&self) -> &f32 { 23 | &self.a 24 | } 25 | } 26 | 27 | impl BorrowMut for A { 28 | fn borrow_mut(&mut self) -> &mut f32 { 29 | &mut self.a 30 | } 31 | } 32 | 33 | fn main() { 34 | let mut world = World::new(); 35 | let a = world.spawn((A { a: 1.0 },)).id(); 36 | 37 | assert_eq!( 38 | world 39 | .new_view_mut() 40 | .borrow_any::<&(dyn Debug + Sync)>() 41 | .into_iter() 42 | .count(), 43 | 1 44 | ); 45 | 46 | assert_eq!( 47 | world 48 | .new_view_mut() 49 | .borrow_any::<&(dyn Debug + Send + Sync)>() 50 | .into_iter() 51 | .count(), 52 | 1 53 | ); 54 | 55 | assert_eq!( 56 | world 57 | .new_view_mut() 58 | .borrow_any_mut::() 59 | .into_iter() 60 | .count(), 61 | 1 62 | ); 63 | 64 | assert_eq!( 65 | world 66 | .new_view_mut() 67 | .borrow_any_mut::() 68 | .into_iter() 69 | .count(), 70 | 1 71 | ); 72 | 73 | assert_eq!( 74 | world.new_view_mut().borrow_any::().into_iter().count(), 75 | 1 76 | ); 77 | 78 | assert_eq!( 79 | world 80 | .new_view_mut() 81 | .borrow_any_mut::() 82 | .into_iter() 83 | .count(), 84 | 0 85 | ); 86 | 87 | assert_eq!( 88 | world.new_view_mut().borrow_any::().into_iter().count(), 89 | 1 90 | ); 91 | 92 | assert_eq!( 93 | world 94 | .new_view_mut() 95 | .borrow_any_mut::() 96 | .into_iter() 97 | .count(), 98 | 1 99 | ); 100 | 101 | assert_eq!(world.despawn(a), Ok(())); 102 | } 103 | -------------------------------------------------------------------------------- /examples/external.rs: -------------------------------------------------------------------------------- 1 | //! This example contains usage of the main features of Edict ECS. 2 | 3 | use edict::world::World; 4 | 5 | /// Just a type. 6 | /// Being `'static` makes it a proper component type. 7 | #[derive(Debug, PartialEq, Eq)] 8 | struct Foo; 9 | 10 | /// Another type. 11 | #[derive(Debug, PartialEq, Eq)] 12 | struct Bar; 13 | 14 | /// Another type. 15 | #[derive(Debug, PartialEq, Eq)] 16 | struct Baz; 17 | 18 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] 19 | struct Value(T); 20 | 21 | fn main() { 22 | // Create new World. 23 | let mut world_builder = World::builder(); 24 | 25 | world_builder 26 | .register_external::() 27 | .on_drop_fn(|_, _, _| { 28 | println!("Foo dropped"); 29 | }); 30 | 31 | world_builder.register_external::(); 32 | world_builder.register_external::(); 33 | world_builder.register_external::>(); 34 | world_builder.register_external::>(); 35 | world_builder.register_external::>(); 36 | 37 | let mut world = world_builder.build(); 38 | 39 | // World doesn't not contain any entities yet. 40 | 41 | // Spawn new entity in the world and give it three components, 42 | // namely `Foo`, `Bar` and `Baz` components. 43 | // `World::spawn` takes a `Bundle` implementation. 44 | // Tuples of various size implement `Bundle` trait. 45 | // Using this method with tuple will cause all tuple elements 46 | // to be added as components to the entity. 47 | // 48 | // Take care to not include duplicate components in one bundle 49 | // as method will surely panic. 50 | let e = world.spawn_external((Foo, Bar, Baz)); 51 | 52 | // Entity can be used to access components in the `World`. 53 | // Note that query returns `Option` because entity may not have a component. 54 | assert!(matches!(e.get::<&Foo>(), Some(&Foo))); 55 | 56 | // To add another component to the entity call `World::insert`. 57 | let e = e.insert_external(Value(0u32)).unwrap(); 58 | assert!(matches!(e.get::<&Value>(), Some(&Value(0)))); 59 | 60 | // If the component is already present in entity, the value is simply replaced. 61 | let e = e.insert_external(Value(1u32)).unwrap(); 62 | assert!(matches!(e.get::<&Value>(), Some(&Value(1)))); 63 | 64 | // To add few components at once user should call `World::insert_bundle`. 65 | // This is much more efficient than adding components once by one. 66 | let e = e.insert_external_bundle((Value(1u8), Value(2u16))).unwrap(); 67 | 68 | // Spawned entities are despawned using [`World::despawn`] methods. 69 | e.despawn(); 70 | 71 | let _e = world.spawn_external((Foo, Bar)).id(); 72 | 73 | // Entities can be spawned in batches using iterators over bundles. 74 | // Each iterator element is treated as bundle of components 75 | // and spawned entities receive them. 76 | // 77 | // This is more efficient than spawning in loop, 78 | // especially if iterator size hints are more or less accurate 79 | // and not `(0, None)` 80 | // 81 | // `World::spawn_batch` returns an iterator with `Entity` for each entity created. 82 | let _entities: Vec<_> = world 83 | .spawn_batch_external((0..10u32).map(|i| (Value(i),))) 84 | .collect(); 85 | 86 | // User may choose to not consume returned iterator, or consume it partially. 87 | // This would cause bundle iterator to not be consumed as well and entities will not be spawned. 88 | // 89 | // This allows using unbound iterators to produce entities and stop at any moment. 90 | // 91 | // Prefer using using bound iterators to allow edict to reserve space for all entities. 92 | let _entities: Vec<_> = world 93 | .spawn_batch_external((0u32..).map(|i| (Value(i),))) 94 | .take(10) 95 | .collect(); 96 | } 97 | -------------------------------------------------------------------------------- /examples/flow.rs: -------------------------------------------------------------------------------- 1 | use std::task::{Poll, Waker}; 2 | 3 | use edict::{ 4 | action::ActionEncoder, 5 | component::Component, 6 | flow::{FlowEntity, Flows}, 7 | query::Entities, 8 | resources::Res, 9 | scheduler::Scheduler, 10 | view::View, 11 | world::World, 12 | }; 13 | use smallvec::SmallVec; 14 | 15 | const THRESHOLD: f32 = 0.0001; 16 | const THRESHOLD2: f32 = THRESHOLD * THRESHOLD; 17 | const SPEED: f32 = 0.1; 18 | 19 | #[derive(Clone, Copy, Debug, Component)] 20 | struct Pos { 21 | x: f32, 22 | y: f32, 23 | } 24 | 25 | impl Pos { 26 | fn distance(&self, other: Pos) -> f32 { 27 | self.distance2(other).sqrt() 28 | } 29 | 30 | fn distance2(&self, other: Pos) -> f32 { 31 | let dx = other.x - self.x; 32 | let dy = other.y - self.y; 33 | dx * dx + dy * dy 34 | } 35 | 36 | fn dir_to(&self, other: Pos) -> (f32, f32) { 37 | let dx = other.x - self.x; 38 | let dy = other.y - self.y; 39 | let distance = self.distance(other); 40 | (dx / distance, dy / distance) 41 | } 42 | } 43 | 44 | #[derive(Debug, Component)] 45 | struct MoveTo { 46 | target: Pos, 47 | speed: f32, 48 | wakers: SmallVec<[Waker; 2]>, 49 | } 50 | 51 | struct DeltaTime(f32); 52 | 53 | fn move_to_system( 54 | dt: Res, 55 | view: View<(Entities, &mut Pos, &mut MoveTo)>, 56 | mut encoder: ActionEncoder, 57 | ) { 58 | let dt = dt.0; 59 | 60 | for (entity, pos, move_to) in view { 61 | let step = dt * move_to.speed; 62 | 63 | let distance2 = pos.distance2(move_to.target); 64 | if distance2 <= step { 65 | pos.x = move_to.target.x; 66 | pos.y = move_to.target.y; 67 | 68 | for waker in move_to.wakers.drain(..) { 69 | waker.wake(); 70 | } 71 | encoder.drop::(entity); 72 | } else { 73 | let dir = pos.dir_to(move_to.target); 74 | pos.x += dir.0 * step; 75 | pos.y += dir.1 * step; 76 | } 77 | } 78 | } 79 | 80 | async fn move_to(e: FlowEntity, target: Pos) { 81 | e.poll(move |mut e, cx| { 82 | let Some(pos) = e.get::<&Pos>() else { 83 | return Poll::Ready(()); 84 | }; 85 | 86 | if pos.distance2(target) < THRESHOLD2 { 87 | return Poll::Ready(()); 88 | } 89 | 90 | match e.get_mut::<&mut MoveTo>() { 91 | None => { 92 | e.insert(MoveTo { 93 | target, 94 | speed: SPEED, 95 | wakers: smallvec::smallvec![cx.waker().clone()], 96 | }); 97 | Poll::Pending 98 | } 99 | Some(move_to) => { 100 | move_to.target = target; 101 | move_to.speed = SPEED; 102 | 103 | for waker in move_to.wakers.iter() { 104 | if waker.will_wake(cx.waker()) { 105 | return Poll::Pending; 106 | } 107 | } 108 | 109 | move_to.wakers.push(cx.waker().clone()); 110 | Poll::Pending 111 | } 112 | } 113 | }) 114 | .await; 115 | } 116 | 117 | #[derive(Component)] 118 | struct Finish; 119 | 120 | fn main() { 121 | let mut flows = Flows::new(); 122 | let mut world = World::new(); 123 | 124 | let e = world.spawn((Pos { x: 0.0, y: 1.0 },)).id(); 125 | 126 | let targets = [ 127 | Pos { x: 1.0, y: 0.0 }, 128 | Pos { x: 0.0, y: -1.0 }, 129 | Pos { x: -1.0, y: 0.0 }, 130 | ]; 131 | 132 | world.spawn_flow_for(e, move |e| async move { 133 | for target in targets { 134 | move_to(e, target).await; 135 | } 136 | let _ = e.insert(Finish); 137 | }); 138 | 139 | let mut scheduler = Scheduler::new(); 140 | scheduler.add_system(move_to_system); 141 | 142 | loop { 143 | world.insert_resource(DeltaTime(0.1)); 144 | 145 | flows.execute(&mut world); 146 | scheduler.run_sequential(&mut world); 147 | 148 | if world.try_has_component::(e).unwrap() { 149 | return; 150 | } 151 | 152 | println!("{:?}", world.get::<&Pos>(e).unwrap()); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /examples/relation.rs: -------------------------------------------------------------------------------- 1 | use edict::{ 2 | component::Component, 3 | epoch::EpochId, 4 | query::Entities, 5 | relation::{Relates, Relation}, 6 | world::WorldBuilder, 7 | }; 8 | 9 | #[derive(Component)] 10 | struct A; 11 | 12 | #[derive(Component)] 13 | struct B; 14 | 15 | #[derive(Component)] 16 | struct C; 17 | 18 | #[derive(Clone, Copy, Relation)] 19 | #[edict(exclusive, owned)] 20 | struct ChildOf; 21 | 22 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Relation)] 23 | struct Likes; 24 | 25 | #[derive(Clone, Copy, Debug, Relation)] 26 | #[edict(symmetric)] 27 | struct Enemy; 28 | 29 | #[derive(Clone, Copy, Debug, Relation)] 30 | #[edict(symmetric, owned)] 31 | struct LifeBound; 32 | 33 | fn main() { 34 | let mut world_builder = WorldBuilder::new(); 35 | world_builder 36 | .register_component::() 37 | .on_drop_fn(|a, entity, encoder| { 38 | a.on_drop(entity, encoder); 39 | println!("A dropped"); 40 | }); 41 | 42 | let mut world = world_builder.build(); 43 | 44 | let a = world.spawn((A,)).id(); 45 | let b = world.spawn(()).id(); 46 | 47 | world.insert_relation(a, ChildOf, b).unwrap(); 48 | 49 | for (e, ChildOf) in world.view::().relates_to::(b).iter() { 50 | println!("{} is child of {}", e, b); 51 | } 52 | 53 | world.despawn(b).unwrap(); 54 | 55 | assert_eq!(world.is_alive(a), false); 56 | 57 | let a = world.spawn(()).id(); 58 | let b = world.spawn(()).id(); 59 | let c = world.spawn(()).id(); 60 | 61 | world.insert_relation(a, Likes, b).unwrap(); 62 | world.insert_relation(a, Likes, c).unwrap(); 63 | 64 | let mut view = world.get::>(a).unwrap(); 65 | let first = view.next().unwrap(); 66 | assert_eq!(first.1, b); 67 | let second = view.next().unwrap(); 68 | assert_eq!(second.1, c); 69 | 70 | world.despawn(b).unwrap(); 71 | 72 | let mut view = world.get::>(a).unwrap(); 73 | let first = view.next().unwrap(); 74 | assert_eq!(first.1, c); 75 | 76 | let b = world.spawn(()).id(); 77 | 78 | world.insert_relation(a, Enemy, b).unwrap(); 79 | 80 | let q = world.view::().relates::(); 81 | for (e, enemies) in q.iter() { 82 | println!( 83 | "{} is enemy of {:?}", 84 | e, 85 | enemies.into_iter().collect::>() 86 | ); 87 | } 88 | drop(q); 89 | 90 | let _ = world.despawn(b); 91 | 92 | for (e, enemies) in world.view_mut::().relates::() { 93 | println!( 94 | "{} is enemy of {:?}", 95 | e, 96 | enemies.into_iter().collect::>() 97 | ); 98 | } 99 | 100 | let since = EpochId::start(); 101 | 102 | let view = world 103 | .view_mut::<(Entities, &A)>() 104 | .with::() 105 | .modified::<&C>(since) 106 | .relates_to::(b) 107 | .filter_relates_to::(c); 108 | 109 | for ((e, a), c, child_of) in view { 110 | let _ = (e, a, c, child_of); 111 | } 112 | 113 | let a = world.spawn((A,)).id(); 114 | let b = world.spawn((B,)).id(); 115 | 116 | world.insert_relation(a, LifeBound, b).unwrap(); 117 | 118 | world.despawn(a).unwrap(); 119 | assert_eq!(world.is_alive(b), false); 120 | } 121 | -------------------------------------------------------------------------------- /examples/schedule.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use edict::{ 4 | query::{BorrowAll, Modified, With, Without}, 5 | scheduler::Scheduler, 6 | system::State, 7 | view::{View, ViewCell}, 8 | world::World, 9 | }; 10 | use edict_proc::Component; 11 | 12 | #[derive(Clone, Copy, Component)] 13 | struct A; 14 | 15 | #[derive(Clone, Copy, Debug, Component)] 16 | #[edict(borrow(dyn Debug))] 17 | struct B; 18 | 19 | fn main() { 20 | let mut world = World::new(); 21 | let mut schedule = Scheduler::new(); 22 | 23 | world.spawn((A,)); 24 | world.spawn((A, B)); 25 | let c = world.spawn((B,)).id(); 26 | 27 | schedule.add_system(system_a); 28 | schedule.add_system(system_b); 29 | schedule.add_system(system_c); 30 | schedule.add_system(system_d); 31 | 32 | for i in 0..10 { 33 | println!("Loop: {i}"); 34 | 35 | world.get::<&mut B>(c).unwrap(); 36 | 37 | schedule.run_threaded(&mut world); 38 | 39 | #[cfg(feature = "rayon-scheduler")] 40 | schedule.run_rayon(&mut world); 41 | } 42 | } 43 | 44 | fn system_a( 45 | // `ViewCell` is required because `&mut A` conflicts with `BorrowAll` 46 | view: ViewCell<( 47 | &mut A, 48 | Option<&B>, 49 | Option>, 50 | )>, 51 | mut counter: State, 52 | ) { 53 | *counter += 1; 54 | println!("Counter: {}", *counter); 55 | for (&mut A, b, dbg) in view { 56 | println!("A + {:?} + {:?}", b, dbg); 57 | } 58 | } 59 | 60 | fn system_b(q: View>) { 61 | for &B in q.iter() { 62 | println!("Modified B"); 63 | } 64 | } 65 | 66 | fn system_c(v: View<&mut A>) { 67 | v.into_iter().for_each(|_| {}); 68 | } 69 | 70 | // `ViewCell` is required because v1 conflicts with v2 71 | fn system_d(v1: View<&mut A, With>, v2: View<&mut A, Without>) { 72 | v1.into_iter().for_each(|_| {}); 73 | v2.into_iter().for_each(|_| {}); 74 | } 75 | -------------------------------------------------------------------------------- /examples/system.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use edict::{ 4 | system::{system, ResLocal}, 5 | view::View, 6 | }; 7 | 8 | struct A(*mut u8); 9 | 10 | #[system] 11 | fn foo(_a: View<&u32>, _b: ResLocal) {} 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /license/APACHE: -------------------------------------------------------------------------------- 1 | Copyright 2021 The edict project developers 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /license/MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 The edict project developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /proc-lib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edict-proc-lib" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | documentation.workspace = true 8 | homepage.workspace = true 9 | repository.workspace = true 10 | readme.workspace = true 11 | description.workspace = true 12 | keywords.workspace = true 13 | categories.workspace = true 14 | 15 | [dependencies] 16 | proc-macro2 = "1.0" 17 | syn = { version = "2.0", features = ["full"] } 18 | quote = "1.0" 19 | proc-easy = { version = "0.4.0" } 20 | -------------------------------------------------------------------------------- /proc-lib/src/component.rs: -------------------------------------------------------------------------------- 1 | use proc_easy::EasyAttributes; 2 | use syn::spanned::Spanned; 3 | 4 | use crate::{merge_where_clauses, Borrow, Name, OnDrop, OnReplace, WhereClause}; 5 | 6 | proc_easy::easy_attributes! { 7 | @(edict) 8 | struct ComponentAttributes { 9 | name: Option, 10 | borrow: Option, 11 | on_drop: Option, 12 | on_replace: Option, 13 | where_clauses: Vec, 14 | } 15 | } 16 | 17 | pub fn derive( 18 | input: syn::DeriveInput, 19 | edict_path: &syn::Path, 20 | edict_namespace: &syn::Ident, 21 | ) -> syn::Result { 22 | let ident = &input.ident; 23 | 24 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 25 | let attributes = ComponentAttributes::parse_in(edict_namespace, &input.attrs, input.span())?; 26 | let where_clause = merge_where_clauses(where_clause, &attributes.where_clauses); 27 | 28 | let fn_name = attributes.name.map(|name| { 29 | let name = name.literal; 30 | Some(quote::quote! { 31 | #[inline] 32 | fn name() -> &'static str { 33 | #name 34 | } 35 | }) 36 | }); 37 | 38 | let on_drop = attributes.on_drop.map(|on_drop| { 39 | let on_drop = &on_drop.function; 40 | quote::quote! { 41 | #[allow(unused_variables)] 42 | #[inline] 43 | fn on_drop(&mut self, entity: #edict_path::entity::EntityId, encoder: #edict_path::action::LocalActionEncoder<'_>) { 44 | (#on_drop)(self, entity, encoder) 45 | } 46 | } 47 | }); 48 | 49 | let on_replace = attributes.on_replace.map(|on_replace|{ 50 | let on_replace = &on_replace.function; 51 | quote::quote! { 52 | #[allow(unused_variables)] 53 | #[inline] 54 | fn on_replace(&mut self, value: &Self, entity: #edict_path::entity::EntityId, encoder: #edict_path::action::LocalActionEncoder<'_>) -> bool { 55 | (#on_replace)(self, value, entity, encoder) 56 | } 57 | } 58 | } 59 | ); 60 | 61 | let insert_borrows = match attributes.borrow { 62 | None => None, 63 | Some(borrow) => { 64 | let mut insert_borrows = quote::quote!(); 65 | 66 | for target in borrow.targets.iter() { 67 | match target { 68 | syn::Type::TraitObject(trait_object) => { 69 | if trait_object.bounds.len() != 1 { 70 | return Err(syn::Error::new( 71 | target.span(), 72 | "Only dyn traits without markers and lifetimes are supported", 73 | )); 74 | } 75 | 76 | let bound = 77 | match &trait_object.bounds[0] { 78 | syn::TypeParamBound::Trait(bound) => bound, 79 | _ => return Err(syn::Error::new( 80 | target.span(), 81 | "Only dyn traits without markers and lifetimes are supported", 82 | )), 83 | }; 84 | 85 | insert_borrows.extend(quote::quote! { 86 | #edict_path::trait_borrow!(#ident as #bound => output); 87 | }); 88 | } 89 | _ => { 90 | insert_borrows.extend(quote::quote! { 91 | #edict_path::type_borrow!(#ident as #target => output); 92 | }); 93 | } 94 | }; 95 | } 96 | Some(insert_borrows) 97 | } 98 | }; 99 | 100 | let output = quote::quote! { 101 | impl #impl_generics #edict_path::component::Component for #ident #ty_generics 102 | #where_clause 103 | { 104 | #fn_name 105 | 106 | #on_drop 107 | 108 | #on_replace 109 | 110 | fn borrows() -> #edict_path::private::Vec<#edict_path::component::ComponentBorrow> { 111 | let mut output = Vec::new(); 112 | output.push(#edict_path::component::ComponentBorrow::auto::()); 113 | #edict_path::trait_borrow!(Self as #edict_path::component::Value => output); 114 | #edict_path::trait_borrow!(Self as #edict_path::private::Any => output); 115 | #insert_borrows 116 | output 117 | } 118 | } 119 | }; 120 | 121 | Ok(output) 122 | } 123 | -------------------------------------------------------------------------------- /proc-lib/src/flow.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use syn::spanned::Spanned; 3 | 4 | pub fn flow_fn(closure: syn::ExprClosure, edict_path: &syn::Path) -> syn::Result { 5 | if closure.inputs.len() != 1 { 6 | return Err(syn::Error::new( 7 | closure.span(), 8 | "expected a closure with exactly one argument", 9 | )); 10 | } 11 | 12 | match closure.output { 13 | syn::ReturnType::Default => {} 14 | _ => { 15 | return Err(syn::Error::new( 16 | closure.output.span(), 17 | "expected a closure with no return type", 18 | )); 19 | } 20 | } 21 | 22 | let arg = &closure.inputs[0]; 23 | let body = closure.body; 24 | 25 | Ok(quote::quote! { 26 | unsafe { 27 | #edict_path::flow::FlowClosure::new(move |token| async move { 28 | #[allow(unused)] 29 | let #arg = #edict_path::flow::FlowContext::cx(&token); 30 | { 31 | #body 32 | } 33 | }) 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /proc-lib/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | mod component; 4 | // mod query; 5 | mod relation; 6 | mod system; 7 | 8 | mod kw { 9 | proc_easy::easy_token!(name); 10 | proc_easy::easy_token!(borrow); 11 | proc_easy::easy_token!(on_drop); 12 | proc_easy::easy_token!(on_target_drop); 13 | proc_easy::easy_token!(on_replace); 14 | proc_easy::easy_token!(exclusive); 15 | proc_easy::easy_token!(symmetric); 16 | proc_easy::easy_token!(owned); 17 | } 18 | 19 | proc_easy::easy_argument_value! { 20 | struct Name { 21 | kw: kw::name, 22 | literal: syn::LitStr, 23 | } 24 | } 25 | 26 | proc_easy::easy_argument! { 27 | struct Borrow { 28 | kw: kw::borrow, 29 | targets: proc_easy::EasyParenthesized>, 30 | } 31 | } 32 | 33 | proc_easy::easy_argument! { 34 | struct OnDrop { 35 | kw: kw::on_drop, 36 | eq: syn::Token![=], 37 | function: syn::Expr, 38 | } 39 | } 40 | 41 | proc_easy::easy_argument! { 42 | struct OnReplace { 43 | kw: kw::on_replace, 44 | eq: syn::Token![=], 45 | function: syn::Expr, 46 | } 47 | } 48 | 49 | proc_easy::easy_argument! { 50 | struct OnTargetDrop { 51 | kw: kw::on_target_drop, 52 | eq: syn::Token![=], 53 | function: syn::Expr, 54 | } 55 | } 56 | 57 | proc_easy::easy_argument! { 58 | struct Exclusive { 59 | kw: kw::exclusive, 60 | } 61 | } 62 | 63 | proc_easy::easy_argument! { 64 | struct Owned { 65 | kw: kw::owned, 66 | } 67 | } 68 | 69 | proc_easy::easy_argument! { 70 | struct WhereClause { 71 | kw: syn::Token![where], 72 | predicates: proc_easy::EasyTerminated, 73 | } 74 | } 75 | 76 | fn merge_where_clauses( 77 | where_clause: Option<&syn::WhereClause>, 78 | additional: &[WhereClause], 79 | ) -> Option { 80 | match where_clause { 81 | None if additional.is_empty() => None, 82 | None => { 83 | let mut predicates = syn::punctuated::Punctuated::new(); 84 | 85 | for where_clause in additional { 86 | for predicate in where_clause.predicates.iter() { 87 | predicates.push(predicate.clone()); 88 | } 89 | } 90 | 91 | Some(syn::WhereClause { 92 | where_token: additional[0].kw, 93 | predicates, 94 | }) 95 | } 96 | Some(where_clause) => { 97 | let mut predicates = where_clause.predicates.clone(); 98 | 99 | for where_clause in additional { 100 | for predicate in where_clause.predicates.iter() { 101 | predicates.push(predicate.clone()); 102 | } 103 | } 104 | 105 | Some(syn::WhereClause { 106 | where_token: where_clause.where_token, 107 | predicates, 108 | }) 109 | } 110 | } 111 | } 112 | 113 | pub fn derive_component( 114 | item: TokenStream, 115 | edict_path: &syn::Path, 116 | edict_namespace: &syn::Ident, 117 | ) -> TokenStream { 118 | match syn::parse2(item).and_then(|input| component::derive(input, edict_path, edict_namespace)) 119 | { 120 | Ok(output) => output.into(), 121 | Err(err) => err.to_compile_error().into(), 122 | } 123 | } 124 | 125 | pub fn derive_relation( 126 | item: TokenStream, 127 | edict_path: &syn::Path, 128 | edict_namespace: &syn::Ident, 129 | ) -> TokenStream { 130 | match syn::parse2(item).and_then(|input| relation::derive(input, edict_path, edict_namespace)) { 131 | Ok(output) => output.into(), 132 | Err(err) => err.to_compile_error().into(), 133 | } 134 | } 135 | 136 | // pub fn derive_query( 137 | // item: TokenStream, 138 | // edict_path: &syn::Path, 139 | // edict_namespace: &syn::Ident, 140 | // ) -> TokenStream { 141 | // match syn::parse2(item).and_then(|input| query::derive(input, edict_path, edict_namespace)) { 142 | // Ok(output) => output.into(), 143 | // Err(err) => err.to_compile_error().into(), 144 | // } 145 | // } 146 | 147 | pub fn system(item: syn::ItemFn, edict_path: &syn::Path) -> syn::Result { 148 | system::system(item, edict_path) 149 | } 150 | -------------------------------------------------------------------------------- /proc-lib/src/query.rs: -------------------------------------------------------------------------------- 1 | use syn::spanned::Spanned; 2 | 3 | pub fn derive( 4 | input: syn::DeriveInput, 5 | edict_path: &syn::Path, 6 | edict_namespace: &syn::Ident, 7 | ) -> syn::Result { 8 | let vis = &input.vis; 9 | let ident: &proc_macro2::Ident = &input.ident; 10 | let query_ident = quote::format_ident!("{}Query", ident); 11 | 12 | match input.data { 13 | syn::Data::Union(data) => Err(syn::Error::new_spanned( 14 | data.union_token, 15 | "Deriving `Query` is not supported for unions", 16 | )), 17 | syn::Data::Struct(data) => match data.fields { 18 | syn::Fields::Unit => Err(syn::Error::new_spanned( 19 | data.semi_token, 20 | "Deriving `Query` is not supported for unit structs", 21 | )), 22 | syn::Fields::Named(fields) => { 23 | let query_fields = fields.named.iter().map(|f| { 24 | let vis = &f.vis; 25 | let ident = f.ident.as_ref().unwrap(); 26 | let ty = &f.ty; 27 | 28 | let query_ty = 29 | quote::quote_spanned! { ty.span() => <#ty as #edict_path::query::AsQuery>::Query }; 30 | quote::quote_spanned! { f.span() => #vis #ident: #query_ty } 31 | }); 32 | 33 | let field_vis = fields.named.iter().map(|f| { 34 | &f.vis 35 | }).collect::>(); 36 | 37 | let field_names = fields.named.iter().map(|f| { 38 | f.ident.as_ref().unwrap() 39 | }).collect::>(); 40 | 41 | let field_types = fields.named.iter().map(|f| { 42 | &f.ty 43 | }).collect::>(); 44 | 45 | Ok(quote::quote! { 46 | struct #query_ident { 47 | #(#field_vis #query_fields,)* 48 | } 49 | 50 | impl #edict_path::query::AsQuery for #ident { 51 | type Query = #query_ident; 52 | } 53 | 54 | impl #edict_path::query::DefaultQuery for #ident { 55 | fn default_query() -> #query_ident { 56 | #query_ident{#( 57 | #field_names: <#field_types as #edict_path::query::DefaultQuery>::default_query(), 58 | )*} 59 | } 60 | } 61 | 62 | impl #edict_path::query::AsQuery for #query_ident { 63 | type Query = Self; 64 | } 65 | 66 | impl #edict_path::query::IntoQuery for #query_ident { 67 | fn into_query(self) -> Self { 68 | self 69 | } 70 | } 71 | 72 | impl #edict_path::query::DefaultQuery for #query_ident { 73 | fn default_query() -> #query_ident { 74 | #query_ident(#( 75 | <#field_types as #edict_path::query::DefaultQuery>::default_query(), 76 | )*) 77 | } 78 | } 79 | 80 | impl #edict_path::query::Query for #query_ident { 81 | type Item<'a> = 82 | } 83 | }) 84 | } 85 | syn::Fields::Unnamed(fields) => { 86 | let query_fields = fields.unnamed.iter().map(|f| { 87 | let vis = &f.vis; 88 | let ty = &f.ty; 89 | 90 | quote::quote_spanned! { ty.span() => #vis <#ty as #edict_path::query::AsQuery>::Query } 91 | }); 92 | 93 | let field_vis = fields.unnamed.iter().map(|f| { 94 | &f.vis 95 | }).collect::>(); 96 | 97 | let field_indices = (0..fields.unnamed.len()).collect::>(); 98 | 99 | let field_types = fields.unnamed.iter().map(|f| { 100 | &f.ty 101 | }).collect::>(); 102 | 103 | Ok(quote::quote! { 104 | #vis struct #query_ident( 105 | #(#field_vis #query_fields,)* 106 | ) 107 | 108 | impl #edict_path::query::AsQuery for #ident { 109 | type Query = #query_ident; 110 | } 111 | 112 | impl #edict_path::query::DefaultQuery for #ident { 113 | fn default_query() -> #query_ident { 114 | #query_ident(#( 115 | <#field_types as #edict_path::query::DefaultQuery>::default_query(), 116 | )*) 117 | } 118 | } 119 | 120 | impl #edict_path::query::AsQuery for #query_ident { 121 | type Query = Self; 122 | } 123 | 124 | impl #edict_path::query::IntoQuery for #query_ident { 125 | fn into_query(self) -> Self { 126 | self 127 | } 128 | } 129 | 130 | impl #edict_path::query::DefaultQuery for #query_ident { 131 | fn default_query() -> #query_ident { 132 | #query_ident(#( 133 | <#field_types as #edict_path::query::DefaultQuery>::default_query(), 134 | )*) 135 | } 136 | } 137 | }) 138 | } 139 | }, 140 | syn::Data::Enum(data) => { 141 | let query_variants = data.variants.iter().map(|v| { 142 | match &v.fields { 143 | syn::Fields::Unit => Err(syn::Error::new_spanned( 144 | &v.ident, 145 | "Deriving `Query` is not supported for unit structs", 146 | )), 147 | syn::Fields::Named(fields) => { 148 | let query_fields = fields.named.iter().map(|f| { 149 | let vis = &f.vis; 150 | let ident = f.ident.as_ref().unwrap(); 151 | let ty = &f.ty; 152 | 153 | let query_ty = 154 | quote::quote_spanned! { ty.span() => <#ty as #edict_path::query::AsQuery>::Query }; 155 | quote::quote_spanned! { f.span() => #vis #ident: #query_ty } 156 | }); 157 | 158 | Ok(quote::quote! { 159 | struct #query_ident { 160 | #(#query_fields,)* 161 | } 162 | }) 163 | } 164 | syn::Fields::Unnamed(fields) => { 165 | let query_fields = fields.unnamed.iter().map(|f| { 166 | let vis = &f.vis; 167 | let ty = &f.ty; 168 | 169 | quote::quote_spanned! { ty.span() => #vis <#ty as #edict_path::query::AsQuery>::Query } 170 | }); 171 | 172 | Ok(quote::quote! { 173 | struct #query_ident( 174 | #(#query_fields,)* 175 | ) 176 | }) 177 | } 178 | } 179 | }).collect::, _>>()?; 180 | 181 | Ok(quote::quote! { 182 | #vis enum #query_ident { 183 | #(#query_variants,)* 184 | } 185 | }) 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /proc-lib/src/relation.rs: -------------------------------------------------------------------------------- 1 | use proc_easy::EasyAttributes; 2 | use syn::spanned::Spanned; 3 | 4 | use crate::{kw, merge_where_clauses, Name, OnDrop, OnReplace, OnTargetDrop, WhereClause}; 5 | 6 | proc_easy::easy_attributes! { 7 | @(edict) 8 | struct RelationAttributes { 9 | name: Option, 10 | exclusive: Option, 11 | symmetric: Option, 12 | owned: Option, 13 | on_drop: Option, 14 | on_replace: Option, 15 | on_target_drop: Option, 16 | where_clauses: Vec, 17 | } 18 | } 19 | 20 | pub fn derive( 21 | input: syn::DeriveInput, 22 | edict_path: &syn::Path, 23 | edict_namespace: &syn::Ident, 24 | ) -> syn::Result { 25 | let ident = &input.ident; 26 | 27 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 28 | let attributes = RelationAttributes::parse_in(edict_namespace, &input.attrs, input.span())?; 29 | let where_clause = merge_where_clauses(where_clause, &attributes.where_clauses); 30 | 31 | let exclusive = attributes 32 | .exclusive 33 | .map(|_| quote::quote! { const EXCLUSIVE: bool = true; }); 34 | 35 | let symmetric = attributes 36 | .symmetric 37 | .map(|_| quote::quote! { const SYMMETRIC: bool = true; }); 38 | 39 | let owned = attributes 40 | .owned 41 | .map(|_| quote::quote! { const OWNED: bool = true; }); 42 | 43 | let fn_name = attributes.name.map(|name| { 44 | let name = name.literal; 45 | Some(quote::quote! { 46 | #[inline] 47 | fn name() -> &'static str { 48 | #name 49 | } 50 | }) 51 | }); 52 | 53 | let on_drop = attributes.on_drop.map(|on_drop| { 54 | let on_drop = &on_drop.function; 55 | quote::quote! { 56 | #[allow(unused_variables)] 57 | #[inline] 58 | fn on_drop(&mut self, entity: #edict_path::entity::EntityId, target: #edict_path::entity::EntityId, encoder: #edict_path::action::LocalActionEncoder<'_>) { 59 | (#on_drop)(self, entity, target, encoder) 60 | } 61 | } 62 | }); 63 | 64 | let on_replace = attributes.on_replace.map(|on_replace|{ 65 | let on_replace = &on_replace.function; 66 | quote::quote! { 67 | #[allow(unused_variables)] 68 | #[inline] 69 | fn on_replace(&mut self, value: &Self, entity: #edict_path::entity::EntityId, target: #edict_path::entity::EntityId, new_target: #edict_path::entity::EntityId, encoder: #edict_path::action::LocalActionEncoder<'_>) -> bool { 70 | (#on_replace)(self, value, entity, target, new_target, encoder) 71 | } 72 | } 73 | } 74 | ); 75 | 76 | let on_target_drop = attributes.on_target_drop.map(|on_target_drop| { 77 | let on_target_drop = &on_target_drop.function; 78 | quote::quote! { 79 | #[allow(unused_variables)] 80 | #[inline] 81 | fn on_target_drop(entity: #edict_path::entity::EntityId, target: #edict_path::entity::EntityId, encoder: #edict_path::action::LocalActionEncoder<'_>) { 82 | (#on_target_drop)(entity, target, encoder) 83 | } 84 | } 85 | }); 86 | 87 | let mut output = quote::quote! { 88 | impl #impl_generics #edict_path::relation::Relation for #ident #ty_generics 89 | #where_clause 90 | { 91 | #exclusive 92 | 93 | #symmetric 94 | 95 | #owned 96 | 97 | #fn_name 98 | 99 | #on_drop 100 | 101 | #on_replace 102 | 103 | #on_target_drop 104 | } 105 | }; 106 | 107 | if attributes.exclusive.is_some() { 108 | output.extend(quote::quote! { 109 | impl #impl_generics #edict_path::relation::ExclusiveRelation for #ident #ty_generics 110 | #where_clause 111 | {} 112 | }); 113 | } 114 | 115 | Ok(output) 116 | } 117 | -------------------------------------------------------------------------------- /proc-lib/src/system.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use syn::spanned::Spanned; 3 | 4 | pub fn system(mut item: syn::ItemFn, edict_path: &syn::Path) -> syn::Result { 5 | if item.sig.generics.type_params().count() != 0 { 6 | return Err(syn::Error::new_spanned( 7 | item.sig.generics, 8 | "functions with type parameters may not be used with `#[system]` attribute", 9 | )); 10 | } 11 | 12 | if item.sig.asyncness.is_some() { 13 | return Err(syn::Error::new_spanned( 14 | item.sig.fn_token, 15 | "async functions may not be used with `#[system]` attribute", 16 | )); 17 | } 18 | 19 | if item.sig.unsafety.is_some() { 20 | return Err(syn::Error::new_spanned( 21 | item.sig.unsafety, 22 | "unsafe functions may not be used with `#[system]` attribute", 23 | )); 24 | } 25 | 26 | if item.sig.output != syn::ReturnType::Default { 27 | return Err(syn::Error::new_spanned( 28 | item.sig.output, 29 | "functions with return types may not be used with `#[system]` attribute", 30 | )); 31 | } 32 | 33 | let mut checks = Vec::new(); 34 | 35 | // let where_clause = item.sig.generics.make_where_clause(); 36 | 37 | for arg in item.sig.inputs.iter() { 38 | match arg { 39 | syn::FnArg::Receiver(_) => { 40 | return Err(syn::Error::new_spanned( 41 | arg, 42 | "methods may not be used with `#[system]` attribute", 43 | )); 44 | } 45 | 46 | syn::FnArg::Typed(arg) => { 47 | let ty = &*arg.ty; 48 | checks.push(syn::parse_quote_spanned!(ty.span() => { 49 | #edict_path::private::is_fn_arg::<#ty>(); 50 | // #edict_path::private::is_fn_system(|_: #ty| {}); 51 | })); 52 | 53 | // where_clause 54 | // .predicates 55 | // .push(syn::parse_quote_spanned!(ty.span() => #ty: #edict_path::private::FnArg)); 56 | } 57 | } 58 | } 59 | 60 | let ident = &item.sig.ident; 61 | 62 | checks.push( 63 | syn::parse_quote_spanned!(ident.span() => #edict_path::private::is_fn_system(#ident);), 64 | ); 65 | 66 | checks.append(&mut item.block.stmts); 67 | item.block.stmts = checks; 68 | 69 | Ok(quote::quote!(#item)) 70 | } 71 | -------------------------------------------------------------------------------- /proc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "edict-proc" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | documentation.workspace = true 8 | homepage.workspace = true 9 | repository.workspace = true 10 | readme.workspace = true 11 | description.workspace = true 12 | keywords.workspace = true 13 | categories.workspace = true 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | syn = "2.0" 20 | edict-proc-lib = { version = "=1.0.0-rc7", path = "../proc-lib" } 21 | -------------------------------------------------------------------------------- /proc/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::str::FromStr; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | #[proc_macro_derive(Component, attributes(edict))] 6 | pub fn derive_component(item: TokenStream) -> TokenStream { 7 | let path: syn::Path = syn::parse_quote!(edict); 8 | edict_proc_lib::derive_component(item.into(), &path, path.get_ident().unwrap()).into() 9 | } 10 | 11 | #[proc_macro_derive(Relation, attributes(edict))] 12 | pub fn derive_relation(item: TokenStream) -> TokenStream { 13 | let path: syn::Path = syn::parse_quote!(edict); 14 | edict_proc_lib::derive_relation(item.into(), &path, path.get_ident().unwrap()).into() 15 | } 16 | 17 | /// This attribute adds checks for system functions. 18 | /// Only applicable to function items. 19 | /// 20 | /// Generates compilation error if function has type parameters, 21 | /// is async, unsafe, has return type or is a method. 22 | /// 23 | /// Checks that all function arguments are valid system arguments. 24 | /// 25 | /// And finally checks that function is a system. 26 | #[proc_macro_attribute] 27 | pub fn system(attr: TokenStream, item: TokenStream) -> TokenStream { 28 | if !attr.is_empty() { 29 | return TokenStream::from_str( 30 | "::core::compile_error!(\"`#[system]` attribute doesn't take any arguments\");", 31 | ) 32 | .unwrap(); 33 | } 34 | 35 | let item = syn::parse_macro_input!(item as syn::ItemFn); 36 | 37 | let path: syn::Path = syn::parse_quote!(edict); 38 | match edict_proc_lib::system(item, &path) { 39 | Ok(output) => output.into(), 40 | Err(err) => err.to_compile_error().into(), 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | -------------------------------------------------------------------------------- /src/action/buffer.rs: -------------------------------------------------------------------------------- 1 | use alloc::collections::VecDeque; 2 | 3 | use crate::world::World; 4 | 5 | use super::{encoder::LocalActionEncoder, ActionEncoder, ActionFn, LocalActionFn}; 6 | 7 | /// Buffer with all actions recorded by [`ActionEncoder`]. 8 | #[derive(Default)] 9 | #[repr(transparent)] 10 | pub struct ActionBuffer { 11 | actions: VecDeque>, 12 | } 13 | 14 | impl ActionBuffer { 15 | /// Returns new empty action buffer. 16 | #[inline] 17 | pub fn new() -> Self { 18 | Self { 19 | actions: VecDeque::new(), 20 | } 21 | } 22 | 23 | #[inline] 24 | pub(super) fn actions(&mut self) -> &mut VecDeque> { 25 | &mut self.actions 26 | } 27 | 28 | /// Returns an encoder that records actions into this buffer. 29 | /// 30 | /// Actions should be executed on the same [`World`], 31 | /// otherwise entity ids will not refer to the correct entities. 32 | #[inline] 33 | pub fn encoder<'a>(&'a mut self, world: &'a World) -> ActionEncoder<'a> { 34 | ActionEncoder::new(self, world.entities()) 35 | } 36 | 37 | /// Executes recorded actions onto the [`World`]. 38 | /// Iterates through all recorded actions and executes them one by one. 39 | /// Executed actions may trigger component hooks. 40 | /// Hooks record actions into the same buffer. 41 | /// 42 | /// After execution buffer is empty. 43 | /// Actions recorded during execution are executed as well. 44 | /// 45 | /// An infinite recursion is possible if a hook records an action that 46 | /// transitively triggers the same hook again. 47 | /// 48 | /// Returns `true` if at least one action was executed. 49 | #[inline] 50 | pub fn execute(&mut self, world: &mut World) -> bool { 51 | if self.actions.is_empty() { 52 | return false; 53 | } 54 | 55 | while let Some(fun) = self.actions.pop_front() { 56 | fun.call(world); 57 | } 58 | 59 | true 60 | } 61 | } 62 | 63 | /// Buffer with all actions recorded by [`ActionEncoder`]. 64 | #[derive(Default)] 65 | #[repr(transparent)] 66 | pub struct LocalActionBuffer { 67 | actions: VecDeque>, 68 | } 69 | 70 | impl LocalActionBuffer { 71 | /// Returns new empty action buffer. 72 | #[inline] 73 | pub fn new() -> Self { 74 | Self { 75 | actions: VecDeque::new(), 76 | } 77 | } 78 | 79 | #[inline] 80 | pub(super) fn actions(&mut self) -> &mut VecDeque> { 81 | &mut self.actions 82 | } 83 | 84 | /// Returns an encoder that records actions into this buffer. 85 | /// 86 | /// Actions should be executed on the same [`World`], 87 | /// otherwise entity ids will not refer to the correct entities. 88 | #[inline] 89 | pub fn encoder<'a>(&'a mut self, world: &'a World) -> LocalActionEncoder<'a> { 90 | LocalActionEncoder::new(self, world.entities()) 91 | } 92 | 93 | /// Executes recorded actions onto the [`World`]. 94 | /// Iterates through all recorded actions and executes them one by one. 95 | /// Executed actions may trigger component hooks. 96 | /// Hooks record actions into the same buffer. 97 | /// 98 | /// After execution buffer is empty. 99 | /// Actions recorded during execution are executed as well. 100 | /// 101 | /// An infinite recursion is possible if a hook records an action that 102 | /// transitively triggers the same hook again. 103 | /// 104 | /// Returns `true` if at least one action was executed. 105 | #[inline] 106 | pub fn execute(&mut self, world: &mut World) -> bool { 107 | if self.actions.is_empty() { 108 | return false; 109 | } 110 | 111 | while let Some(fun) = self.actions.pop_front() { 112 | fun.call(world); 113 | } 114 | 115 | true 116 | } 117 | 118 | /// Executes recorded actions onto the [`World`]. 119 | /// Iterates through all recorded actions and executes them one by one. 120 | /// Executed actions may trigger component hooks. 121 | /// Hooks record actions into the same buffer. 122 | /// 123 | /// After execution buffer is empty. 124 | /// Actions recorded during execution are executed as well. 125 | /// 126 | /// An infinite recursion is possible if a hook records an action that 127 | /// transitively triggers the same hook again. 128 | /// 129 | /// Returns `true` if at least one action was executed. 130 | #[inline] 131 | pub(crate) fn pop(&mut self) -> Option> { 132 | self.actions.pop_front() 133 | } 134 | 135 | // /// Temporary take this buffer. 136 | // #[inline] 137 | // pub(crate) fn take(&mut self) -> Self { 138 | // Self { 139 | // actions: std::mem::take(&mut self.actions), 140 | // } 141 | // } 142 | 143 | // /// Put back temporary taken buffer. 144 | // #[inline] 145 | // pub(crate) fn put(&mut self, tmp: Self) { 146 | // debug_assert!(self.actions.is_empty()); 147 | // self.actions = tmp.actions; 148 | // } 149 | } 150 | 151 | /// Extension trait for slice of [`ActionBuffer`]s. 152 | pub trait ActionBufferSliceExt { 153 | /// Execute all action encoders from the slice. 154 | /// Returns `true` if at least one action was executed. 155 | fn execute_all(&mut self, world: &mut World) -> bool; 156 | } 157 | 158 | impl ActionBufferSliceExt for [ActionBuffer] { 159 | #[inline] 160 | fn execute_all(&mut self, world: &mut World) -> bool { 161 | self.iter_mut() 162 | .fold(false, |acc, encoder| acc | encoder.execute(world)) 163 | } 164 | } 165 | 166 | impl ActionBufferSliceExt for [LocalActionBuffer] { 167 | #[inline] 168 | fn execute_all(&mut self, world: &mut World) -> bool { 169 | self.iter_mut() 170 | .fold(false, |acc, encoder| acc | encoder.execute(world.local())) 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/action/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains definitions for action recording. 2 | //! Actions can be recorded into [`ActionEncoder`] and executed later onto the [`World`]. 3 | //! Two primary use cases for actions are: 4 | //! * Deferring [`World`] mutations when [`World`] is borrowed immutably, like in most [`Systems`] 5 | //! * Generating actions in custom component drop-glue. 6 | //! 7 | //! [`Systems`]: edict::system::System 8 | 9 | use crate::world::World; 10 | 11 | mod buffer; 12 | mod channel; 13 | mod encoder; 14 | 15 | tiny_fn::tiny_fn! { 16 | pub(crate) struct ActionFn = FnOnce(world: &mut World) | + Send; 17 | pub(crate) struct LocalActionFn = FnOnce(world: &mut World); 18 | } 19 | 20 | pub use self::{ 21 | buffer::{ActionBuffer, ActionBufferSliceExt, LocalActionBuffer}, 22 | channel::{ActionSender, SpawnBatchSender}, 23 | encoder::{ActionEncoder, LocalActionEncoder, LocalSpawnBatch, SpawnBatch}, 24 | }; 25 | 26 | pub(crate) use self::channel::ActionChannel; 27 | -------------------------------------------------------------------------------- /src/dump/nanoserde.rs: -------------------------------------------------------------------------------- 1 | //! World serialization with [`nanoserde`]. 2 | 3 | use core::convert::Infallible; 4 | 5 | pub use nanoserde::{DeBin, DeBinErr, SerBin}; 6 | 7 | use crate::{action::ActionEncoder, component::Component, query::SendImmutableQuery}; 8 | 9 | use super::{ 10 | DumpSet, DumpSlot, Dumper, EntityDump, LoadSet, LoadSlot, Loader, Mark, WorldDump, WorldLoad, 11 | }; 12 | 13 | /// Dumps world using [`nanoserde::SerBin`]. 14 | pub struct DumperBin<'a>(pub &'a mut Vec); 15 | 16 | /// Loads world using [`nanoserde::DeBin`]. 17 | pub struct LoaderBin<'a> { 18 | offset: usize, 19 | buf: &'a [u8], 20 | } 21 | 22 | impl<'a> LoaderBin<'a> { 23 | /// Creates new bincode loader from buffer. 24 | pub fn new(buf: &'a [u8]) -> Self { 25 | Self { offset: 0, buf } 26 | } 27 | } 28 | 29 | macro_rules! dumper { 30 | () => {}; 31 | ($($a:ident)+) => { 32 | #[allow(non_snake_case)] 33 | impl<'a $(, $a)+> Dumper<($($a,)+)> for DumperBin<'a> 34 | where 35 | $($a: SerBin + Sync + 'static,)+ 36 | { 37 | type Error = Infallible; 38 | fn dump(&mut self, entity: EntityDump, slots: ($(DumpSlot<'_, $a>,)+)) -> Result<(), Infallible> { 39 | entity.0.ser_bin(self.0); 40 | let ($($a,)+) = slots; 41 | $( 42 | match $a { 43 | DumpSlot::Skipped => {} 44 | DumpSlot::Component($a) => $a.ser_bin(self.0), 45 | } 46 | )+ 47 | Ok(()) 48 | } 49 | } 50 | 51 | impl<'a $(, $a)+, Fi> SerBin for WorldDump<'a, ($($a,)+), Fi> 52 | where 53 | Fi: SendImmutableQuery + Copy, 54 | $($a: SerBin + Sync + 'static,)+ 55 | { 56 | fn ser_bin(&self, buf: &mut Vec) { 57 | self.dump_bin(buf); 58 | } 59 | } 60 | 61 | impl<'a $(, $a)+, Fi> WorldDump<'a, ($($a,)+), Fi> 62 | where 63 | Fi: SendImmutableQuery + Copy, 64 | $($a: SerBin + Sync + 'static,)+ 65 | { 66 | fn dump_bin(&self, buf: &mut Vec) { 67 | let result = <($($a,)+) as DumpSet>::dump_world(self.world, self.filter, self.epoch, &mut DumperBin(buf)); 68 | match result { 69 | Ok(()) => {} 70 | Err(never) => match never {}, 71 | } 72 | } 73 | } 74 | 75 | #[allow(non_snake_case)] 76 | impl<'a $(, $a)+> Loader<($($a,)+)> for LoaderBin<'a> 77 | where 78 | $($a: DeBin + Component + Send + 'static,)+ 79 | { 80 | /// Type of possible errors that can occur during deserialization. 81 | type Error = DeBinErr; 82 | 83 | /// Returns next entity to load. 84 | fn next(&mut self) -> Result, DeBinErr> { 85 | if self.offset == self.buf.len() { 86 | return Ok(None); 87 | } 88 | let idxs = <[u64;3]>::de_bin(&mut self.offset, self.buf)?; 89 | Ok(Some(EntityDump(idxs))) 90 | } 91 | 92 | /// Loads entity with provided component slots. 93 | fn load(&mut self, slots: &mut ($(LoadSlot<'_, $a>,)+)) -> Result<(), DeBinErr> { 94 | let ($($a,)+) = slots; 95 | $( 96 | match $a { 97 | LoadSlot::Skipped => {} 98 | LoadSlot::Missing => { 99 | let comp: $a = $a::de_bin(&mut self.offset, self.buf)?; 100 | *$a = LoadSlot::Created(comp); 101 | } 102 | LoadSlot::Existing(comp) => { 103 | **comp = $a::de_bin(&mut self.offset, self.buf)?; 104 | } 105 | LoadSlot::Created(_) => unreachable!(), 106 | } 107 | )+ 108 | Ok(()) 109 | } 110 | } 111 | 112 | impl<'a $(, $a)+, Ma> WorldLoad<'a, ($($a,)+), Ma> 113 | where 114 | Ma: Mark, 115 | $($a: Component + DeBin + Send + 'static,)+ 116 | { 117 | /// Loads world from buffer using [`nanoserde::DeBin`]. 118 | pub fn load_bin(&self, actions: &mut ActionEncoder, buf: &[u8]) -> Result<(), DeBinErr> { 119 | <($($a,)+) as LoadSet>::load_world(self.world, self.marker, actions, &mut LoaderBin::new(buf)) 120 | } 121 | } 122 | }; 123 | } 124 | 125 | for_tuple!(dumper); 126 | 127 | #[test] 128 | fn test_dump() { 129 | use ::nanoserde::{DeBin, SerBin}; 130 | 131 | use super::NoMark; 132 | use crate::{action::ActionBuffer, epoch::EpochId, world::World}; 133 | 134 | let mut world = World::new(); 135 | 136 | #[derive(Component, Debug, PartialEq, Eq, SerBin, DeBin)] 137 | struct Foo; 138 | 139 | #[derive(Component, Debug, PartialEq, Eq, SerBin, DeBin)] 140 | struct Bar(u32); 141 | 142 | #[derive(Component, Debug, PartialEq, Eq, SerBin, DeBin)] 143 | struct Baz(String); 144 | 145 | let foo = world.spawn((Foo,)).id(); 146 | let bar = world.spawn((Bar(42),)).id(); 147 | let baz = world.spawn((Baz("qwerty".into()),)).id(); 148 | 149 | let foo_bar = world.spawn((Foo, Bar(11))).id(); 150 | let foo_baz = world.spawn((Foo, Baz("asdfgh".into()))).id(); 151 | let bar_baz = world.spawn((Bar(23), Baz("zxcvbn".into()))).id(); 152 | let foo_bar_baz = world.spawn((Foo, Bar(155), Baz("123456".into()))).id(); 153 | 154 | type Set = (Foo, Bar, Baz); 155 | 156 | let data = WorldDump::::new(&mut world, (), EpochId::start()).serialize_bin(); 157 | 158 | let mut world2 = World::new(); 159 | 160 | let mut buffer = ActionBuffer::new(); 161 | let mut actions = buffer.encoder(&world2); 162 | 163 | WorldLoad::::new(&world2, NoMark) 164 | .load_bin(&mut actions, &data) 165 | .unwrap(); 166 | buffer.execute(&mut world2); 167 | 168 | assert_eq!( 169 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(foo), 170 | Ok((Some(&Foo), None, None)) 171 | ); 172 | 173 | assert_eq!( 174 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(bar), 175 | Ok((None, Some(&Bar(42)), None)) 176 | ); 177 | 178 | assert_eq!( 179 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(baz), 180 | Ok((None, None, Some(&Baz("qwerty".into())))) 181 | ); 182 | 183 | assert_eq!( 184 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(foo_bar), 185 | Ok((Some(&Foo), Some(&Bar(11)), None)) 186 | ); 187 | 188 | assert_eq!( 189 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(foo_baz), 190 | Ok((Some(&Foo), None, Some(&Baz("asdfgh".into())))) 191 | ); 192 | 193 | assert_eq!( 194 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(bar_baz), 195 | Ok((None, Some(&Bar(23)), Some(&Baz("zxcvbn".into())))) 196 | ); 197 | 198 | assert_eq!( 199 | world2.get::<(Option<&Foo>, Option<&Bar>, Option<&Baz>)>(foo_bar_baz), 200 | Ok((Some(&Foo), Some(&Bar(155)), Some(&Baz("123456".into())))) 201 | ); 202 | } 203 | -------------------------------------------------------------------------------- /src/dump/query.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | entity::EntityLoc, 7 | epoch::EpochId, 8 | query::{ 9 | AsQuery, Entities, EntitiesFetch, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, 10 | WriteAlias, 11 | }, 12 | type_id, Access, 13 | }; 14 | 15 | /// Query result per component. 16 | pub enum DumpItem<'a, T> { 17 | /// Component is missing. 18 | Missing, 19 | 20 | /// Component is present and modified. 21 | Modified(&'a T), 22 | 23 | /// Component is present and unmodified. 24 | Unmodified, 25 | } 26 | 27 | impl<'a, T> Clone for DumpItem<'a, T> { 28 | fn clone(&self) -> Self { 29 | *self 30 | } 31 | } 32 | 33 | impl<'a, T> Copy for DumpItem<'a, T> {} 34 | 35 | /// Query for fetching components to serialize. 36 | pub(super) struct DumpQuery { 37 | after_epoch: EpochId, 38 | marker: PhantomData T>, 39 | } 40 | 41 | impl Clone for DumpQuery { 42 | #[inline] 43 | fn clone(&self) -> Self { 44 | *self 45 | } 46 | } 47 | 48 | impl Copy for DumpQuery {} 49 | 50 | impl DumpQuery { 51 | #[inline] 52 | pub fn new(after_epoch: EpochId) -> Self { 53 | DumpQuery { 54 | after_epoch, 55 | marker: PhantomData, 56 | } 57 | } 58 | } 59 | 60 | pub(super) struct DumpFetch<'a, T> { 61 | after_epoch: EpochId, 62 | ptr: Option>, 63 | entity_epochs: NonNull, 64 | marker: PhantomData<&'a [T]>, 65 | } 66 | 67 | unsafe impl<'a, T> Fetch<'a> for DumpFetch<'a, T> 68 | where 69 | T: 'a, 70 | { 71 | type Item = DumpItem<'a, T>; 72 | 73 | #[inline] 74 | fn dangling() -> Self { 75 | DumpFetch { 76 | after_epoch: EpochId::start(), 77 | ptr: None, 78 | entity_epochs: NonNull::dangling(), 79 | marker: PhantomData, 80 | } 81 | } 82 | 83 | #[inline] 84 | unsafe fn get_item(&mut self, idx: u32) -> DumpItem<'a, T> { 85 | match self.ptr { 86 | None => DumpItem::Missing, 87 | Some(ptr) => { 88 | let epoch = unsafe { *self.entity_epochs.as_ptr().add(idx as usize) }; 89 | if epoch.after(self.after_epoch) { 90 | DumpItem::Modified(unsafe { &*ptr.as_ptr().add(idx as usize) }) 91 | } else { 92 | DumpItem::Unmodified 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | macro_rules! impl_dump_query { 100 | () => { 101 | /* Don't implement for empty tuple */ 102 | }; 103 | ($($a:ident)*) => { 104 | impl<$($a),*> AsQuery for DumpQuery<($($a,)*)> 105 | where 106 | $($a: 'static,)* 107 | { 108 | type Query = Self; 109 | } 110 | 111 | impl<$($a),*> IntoQuery for DumpQuery<($($a,)*)> 112 | where 113 | $($a: 'static,)* 114 | { 115 | fn into_query(self) -> Self { 116 | self 117 | } 118 | } 119 | 120 | #[allow(unused_parens)] 121 | #[allow(non_snake_case)] 122 | unsafe impl<$($a),*> Query for DumpQuery<($($a,)*)> 123 | where 124 | $($a: 'static,)* 125 | { 126 | type Item<'a> = (EntityLoc<'a>, ($(DumpItem<'a, $a>),*)); 127 | type Fetch<'a> = (EntitiesFetch<'a>, ($(DumpFetch<'a, $a>),*)); 128 | 129 | const MUTABLE: bool = false; 130 | const FILTERS_ENTITIES: bool = true; 131 | 132 | #[inline] 133 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 134 | match comp.id() { 135 | $(id if id == type_id::<$a>() => Ok(Some(Access::Read)),)* 136 | _ => Ok(None), 137 | } 138 | } 139 | 140 | #[inline] 141 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 142 | false $(|| archetype.has_component(type_id::<$a>()))* 143 | } 144 | 145 | #[inline] 146 | unsafe fn access_archetype(&self, archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 147 | $( 148 | if archetype.has_component(type_id::<$a>()) { 149 | f(type_id::<$a>(), Access::Read); 150 | } 151 | )* 152 | } 153 | 154 | #[inline] 155 | unsafe fn fetch<'a>( 156 | &self, 157 | arch_idx: u32, 158 | archetype: &'a Archetype, 159 | epoch: EpochId, 160 | ) -> (EntitiesFetch<'a>, ($(DumpFetch<'a, $a>),*)) { 161 | let ($($a,)*) = ($( 162 | match archetype.component(type_id::<$a>()) { 163 | None => DumpFetch { 164 | after_epoch: self.after_epoch, 165 | ptr: None, 166 | entity_epochs: NonNull::dangling(), 167 | marker: PhantomData, 168 | }, 169 | Some(component) => { 170 | let data = unsafe { component.data() }; 171 | DumpFetch { 172 | after_epoch: self.after_epoch, 173 | ptr: Some(data.ptr.cast()), 174 | entity_epochs: unsafe { unsafe{ NonNull::new_unchecked(data.entity_epochs.as_ptr() as *mut EpochId) } }, 175 | marker: PhantomData, 176 | } 177 | } 178 | },)*); 179 | (unsafe { Entities.fetch(arch_idx, archetype, epoch) }, ($($a),*)) 180 | } 181 | } 182 | 183 | unsafe impl<$($a),*> ImmutableQuery for DumpQuery<($($a,)*)> 184 | where 185 | $($a: 'static,)* 186 | {} 187 | 188 | #[allow(unused_parens)] 189 | #[allow(non_snake_case)] 190 | unsafe impl<$($a),*> SendQuery for DumpQuery<($($a,)*)> 191 | where 192 | $($a: 'static,)* 193 | { 194 | } 195 | }; 196 | } 197 | 198 | for_tuple!(impl_dump_query); 199 | -------------------------------------------------------------------------------- /src/dump/serde.rs: -------------------------------------------------------------------------------- 1 | //! World serialization with [`serde`]. 2 | 3 | use serde::ser::{Serialize, SerializeSeq, Serializer}; 4 | 5 | use crate::query::SendImmutableQuery; 6 | 7 | use super::{DumpSet, DumpSlot, Dumper, EntityDump, WorldDump}; 8 | 9 | /// Wrapper for `serde::ser::SerializeSeq` that implements `Dumper`. 10 | pub struct SerdeDumper<'a, S>(pub &'a mut S); 11 | 12 | struct SerializeDump([u64; 3], T); 13 | 14 | macro_rules! dumper { 15 | () => {}; 16 | ($($a:ident)+) => { 17 | #[allow(non_snake_case)] 18 | impl<'a $(, $a)+> Serialize for SerializeDump<($(DumpSlot<'a, $a>,)+)> 19 | where 20 | $($a: Serialize,)+ 21 | { 22 | fn serialize(&self, serializer: S) -> Result 23 | where 24 | S: Serializer, 25 | { 26 | let mut seq = serializer.serialize_seq(None)?; 27 | seq.serialize_element(&self.0)?; 28 | let ($($a,)+) = &self.1; 29 | $( 30 | match $a { 31 | DumpSlot::Skipped => {} 32 | DumpSlot::Component($a) => seq.serialize_element($a)?, 33 | } 34 | )+ 35 | seq.end() 36 | } 37 | } 38 | 39 | impl<'a $(, $a)+, Se> Dumper<($($a,)+)> for SerdeDumper<'_, Se> 40 | where 41 | $($a: Serialize + Sync + 'static,)+ 42 | Se: SerializeSeq, 43 | { 44 | type Error = Se::Error; 45 | fn dump(&mut self, entity: EntityDump, slots: ($(DumpSlot<'_, $a>,)+)) -> Result<(), Se::Error> { 46 | self.0.serialize_element(&SerializeDump(entity.0, slots)) 47 | } 48 | } 49 | 50 | impl<'a $(, $a)+, Fi> Serialize for WorldDump<'a, ($($a,)+), Fi> 51 | where 52 | Fi: SendImmutableQuery + Copy, 53 | $($a: Serialize + Sync + 'static,)+ 54 | { 55 | fn serialize(&self, serializer: S) -> Result 56 | where 57 | S: Serializer, 58 | { 59 | let mut seq = serializer.serialize_seq(None)?; 60 | let mut dumper = SerdeDumper(&mut seq); 61 | <($($a,)+) as DumpSet>::dump_world(self.world, self.filter, self.epoch, &mut dumper)?; 62 | seq.end() 63 | } 64 | } 65 | }; 66 | } 67 | 68 | for_tuple!(dumper); 69 | -------------------------------------------------------------------------------- /src/entity/mod.rs: -------------------------------------------------------------------------------- 1 | //! Entity references. 2 | //! 3 | //! Alive, weak and raw ids. 4 | 5 | pub use self::{ 6 | allocator::{IdRange, IdRangeAllocator, OneRangeAllocator}, 7 | entity::{AliveEntity, Entity, EntityBound, EntityId, EntityLoc, EntityRef}, 8 | set::{EntitySet, Location}, 9 | }; 10 | 11 | mod allocator; 12 | mod entity; 13 | mod set; 14 | -------------------------------------------------------------------------------- /src/entity/set.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::{ 3 | fmt, 4 | sync::atomic::{AtomicU64, Ordering}, 5 | }; 6 | 7 | use hashbrown::{hash_map::Entry, HashMap}; 8 | 9 | use crate::cold; 10 | 11 | use super::{ 12 | allocator::{IdAllocator, IdRangeAllocator}, 13 | EntityId, EntityLoc, 14 | }; 15 | 16 | /// Entity location in archetypes. 17 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 18 | pub struct Location { 19 | /// Archetype index. 20 | pub arch: u32, 21 | 22 | /// Index within archetype. 23 | pub idx: u32, 24 | } 25 | 26 | impl Location { 27 | /// Creates a new location instance. 28 | pub fn new(arch_idx: u32, idx: u32) -> Self { 29 | Location { 30 | arch: arch_idx, 31 | idx, 32 | } 33 | } 34 | 35 | /// Creates a new location instance with empty archetype index. 36 | pub fn empty(idx: u32) -> Self { 37 | Location { arch: 0, idx } 38 | } 39 | 40 | /// Creates a new location instance with reserved archetype index. 41 | pub fn reserved(idx: u32) -> Self { 42 | Location { 43 | arch: u32::MAX, 44 | idx, 45 | } 46 | } 47 | } 48 | 49 | /// Collection of entities with mapping to their location. 50 | /// 51 | /// User typically interacts with `World` that contains `EntitySet` internally. 52 | /// When API needs `EntitySet`, `World::entities()` method is used to get it. 53 | pub struct EntitySet { 54 | map: HashMap, 55 | id_allocator: IdAllocator, 56 | reserve_counter: AtomicU64, 57 | } 58 | 59 | impl fmt::Debug for EntitySet { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | f.debug_struct("Entities") 62 | .field("entities", &self.map) 63 | .finish_non_exhaustive() 64 | } 65 | } 66 | 67 | impl EntitySet { 68 | /// Creates a new entity set. 69 | pub fn new() -> Self { 70 | EntitySet { 71 | map: HashMap::new(), 72 | id_allocator: IdAllocator::new(), 73 | reserve_counter: AtomicU64::new(0), 74 | } 75 | } 76 | 77 | /// Creates a new entity set with custom ID range allocator. 78 | /// 79 | /// Custom range allocator is typically used to create 80 | /// entity sets that allocate IDs without collisions. 81 | pub fn with_allocator(id_allocator: Box) -> Self { 82 | EntitySet { 83 | map: HashMap::new(), 84 | id_allocator: IdAllocator::with_range_allocator(id_allocator), 85 | reserve_counter: AtomicU64::new(0), 86 | } 87 | } 88 | 89 | /// Spawns entity with new ID in specified archetype. 90 | /// Calls provided closure to place entity into archetype and acquire index. 91 | /// Returns entity ID and location. 92 | #[inline] 93 | pub fn spawn(&mut self, arch: u32, f: impl FnOnce(EntityId) -> u32) -> (EntityId, Location) { 94 | let Some(id) = self.id_allocator.next() else { 95 | panic!("Entity id allocator is exhausted"); 96 | }; 97 | let id = EntityId::new(id); 98 | let loc = Location { arch, idx: f(id) }; 99 | let old = self.map.insert(id.bits(), loc); 100 | debug_assert!(old.is_none()); 101 | (id, loc) 102 | } 103 | 104 | /// Spawns entity with specified ID in specified archetype. 105 | /// If entity with specified ID already exists, returns `false` and its location. 106 | /// Otherwise calls provided closure to place entity into archetype and acquire index. 107 | /// And then returns `true` and entity location. 108 | #[inline] 109 | pub fn spawn_at( 110 | &mut self, 111 | id: EntityId, 112 | arch: u32, 113 | f: impl FnOnce() -> u32, 114 | ) -> (bool, Location) { 115 | match self.map.entry(id.bits()) { 116 | Entry::Occupied(entry) => (false, *entry.get()), 117 | Entry::Vacant(entry) => { 118 | let loc = Location { arch, idx: f() }; 119 | entry.insert(loc); 120 | (true, loc) 121 | } 122 | } 123 | } 124 | 125 | /// Allocate entity ID without spawning. 126 | /// Entity is place into "reserved" archetype at index `u32::MAX`. 127 | /// Allocated entities must be spawned with `spawn_allocated` 128 | /// before any other entity is spawned. 129 | #[inline] 130 | pub fn alloc(&self) -> EntityLoc<'_> { 131 | let idx = self.reserve_counter.fetch_add(1, Ordering::Relaxed); 132 | 133 | if idx >= u32::MAX as u64 { 134 | self.reserve_counter.fetch_sub(1, Ordering::Relaxed); 135 | panic!("Too much entity ids reserved"); 136 | } 137 | 138 | match self.id_allocator.reserve(idx) { 139 | None => { 140 | self.reserve_counter.fetch_sub(1, Ordering::Relaxed); 141 | panic!("Too much entity ids reserved"); 142 | } 143 | Some(id) => EntityLoc::from_parts(EntityId::new(id), Location::reserved(idx as u32)), 144 | } 145 | } 146 | 147 | /// Spawns all allocated entities. 148 | #[inline] 149 | pub fn spawn_allocated(&mut self, mut f: impl FnMut(EntityId) -> u32) { 150 | let reserved = core::mem::replace(self.reserve_counter.get_mut(), 0); 151 | if reserved == 0 { 152 | return; 153 | } 154 | 155 | cold(); 156 | unsafe { 157 | self.id_allocator.flush_reserved(reserved, |id| { 158 | self.map.insert( 159 | id.get(), 160 | Location { 161 | arch: 0, 162 | idx: f(EntityId::new(id)), 163 | }, 164 | ); 165 | }); 166 | } 167 | } 168 | 169 | /// Despawns entity with specified ID. 170 | #[inline] 171 | pub fn despawn(&mut self, id: EntityId) -> Option { 172 | self.map.remove(&id.bits()) 173 | } 174 | 175 | /// Set location for entity with specified ID. 176 | #[inline] 177 | pub fn set_location(&mut self, id: EntityId, loc: Location) { 178 | self.map.insert(id.bits(), loc); 179 | } 180 | 181 | /// Returns location for entity with specified ID. 182 | #[inline] 183 | pub fn get_location(&self, id: EntityId) -> Option { 184 | match self.map.get(&id.bits()) { 185 | None => { 186 | let reserved = self.reserve_counter.load(Ordering::Acquire); 187 | let Some(idx) = self.id_allocator.reserved(id.non_zero()) else { 188 | return None; 189 | }; 190 | if idx >= reserved { 191 | return None; 192 | } 193 | Some(Location::reserved(idx as u32)) 194 | } 195 | Some(loc) => Some(*loc), 196 | } 197 | } 198 | 199 | /// Returns location for entity with specified ID. 200 | #[inline] 201 | pub fn is_alive(&self, id: EntityId) -> bool { 202 | if self.map.contains_key(&id.bits()) { 203 | return true; 204 | } 205 | let reserved = self.reserve_counter.load(Ordering::Acquire); 206 | let Some(idx) = self.id_allocator.reserved(id.non_zero()) else { 207 | return false; 208 | }; 209 | idx < reserved 210 | } 211 | 212 | /// Reserves capacity for at least `additional` more 213 | /// entities to be inserted. 214 | pub fn reserve(&mut self, additional: u32) { 215 | self.map.reserve(additional as usize); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/epoch.rs: -------------------------------------------------------------------------------- 1 | //! This module contains `EpochCounter` and `EpochId` types used for change detection. 2 | 3 | use core::{ 4 | cell::Cell, 5 | fmt::{self, Debug}, 6 | sync::atomic::{AtomicU64, Ordering}, 7 | }; 8 | 9 | /// Monotonically incremented epoch counter. 10 | /// It is assumed that underlying value cannot overflow in any reasonable amount of time. 11 | /// For this purpose only increment operation is possible and counter starts with 0. 12 | /// If incremented every nanosecond the counter will overflow in 14'029 years. 13 | /// Before this inevitable event happens, update to new version of this library with 128 bit counter is required. 14 | /// Which shall be released before year 14000 to give at least 29 years for migration. 15 | pub struct EpochCounter { 16 | value: AtomicU64, 17 | } 18 | 19 | impl EpochCounter { 20 | /// Returns new epoch counter. 21 | pub const fn new() -> Self { 22 | EpochCounter { 23 | value: AtomicU64::new(0), 24 | } 25 | } 26 | 27 | /// Returns current epoch id. 28 | #[inline] 29 | pub fn current(&self) -> EpochId { 30 | EpochId { 31 | value: self.value.load(Ordering::Relaxed), 32 | } 33 | } 34 | 35 | /// Returns current epoch id. 36 | /// But faster. 37 | #[inline] 38 | pub fn current_mut(&mut self) -> EpochId { 39 | EpochId { 40 | value: *self.value.get_mut(), 41 | } 42 | } 43 | 44 | /// Bumps to the next epoch and returns new epoch id. 45 | #[inline] 46 | pub fn next(&self) -> EpochId { 47 | let old = self.value.fetch_add(1, Ordering::Relaxed); 48 | debug_assert!(old < u64::MAX); 49 | EpochId { value: old + 1 } 50 | } 51 | 52 | /// Bumps to the next epoch and returns new epoch id. 53 | /// But faster 54 | #[inline] 55 | pub fn next_mut(&mut self) -> EpochId { 56 | let value = self.value.get_mut(); 57 | debug_assert!(*value < u64::MAX); 58 | *value += 1; 59 | EpochId { value: *value } 60 | } 61 | 62 | /// Bumps to the next epoch and returns new epoch id if `cond` is true. 63 | /// Otherwise returns current epoch id. 64 | #[inline] 65 | pub fn next_if(&self, cond: bool) -> EpochId { 66 | if cond { 67 | self.next() 68 | } else { 69 | self.current() 70 | } 71 | } 72 | } 73 | 74 | /// Epoch identifier. 75 | #[derive(Clone, Copy, PartialEq, Eq)] 76 | #[repr(transparent)] 77 | pub struct EpochId { 78 | value: u64, 79 | } 80 | 81 | impl Default for EpochId { 82 | fn default() -> Self { 83 | EpochId::start() 84 | } 85 | } 86 | 87 | impl Debug for EpochId { 88 | #[inline] 89 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result { 90 | ::fmt(&self.value, f) 91 | } 92 | } 93 | 94 | impl EpochId { 95 | /// Returns id of starting epoch. 96 | #[inline] 97 | pub const fn start() -> Self { 98 | EpochId { value: 0 } 99 | } 100 | 101 | /// Returns true if this epoch comes strictly before the `other`. 102 | #[inline] 103 | pub const fn before(&self, other: EpochId) -> bool { 104 | self.value < other.value 105 | } 106 | 107 | /// Returns true if this epoch comes strictly after the `other`. 108 | #[inline] 109 | pub const fn after(&self, other: EpochId) -> bool { 110 | self.value > other.value 111 | } 112 | 113 | /// Updates epoch id to later of this and the `other`. 114 | #[inline] 115 | pub fn update(&mut self, other: EpochId) { 116 | self.value = self.value.max(other.value); 117 | } 118 | 119 | /// Bumps epoch to specified one. 120 | /// Assumes this epoch is strictly before epoch `to`. 121 | #[inline] 122 | pub fn bump(&mut self, to: EpochId) { 123 | debug_assert!( 124 | self.before(to), 125 | "`EpochId::bump` must be used only for older epochs" 126 | ); 127 | *self = to; 128 | } 129 | 130 | /// Bumps epoch to specified one. 131 | /// Assumes this epoch is before epoch `to` or the same. 132 | #[inline] 133 | pub fn bump_again(&mut self, to: EpochId) { 134 | debug_assert!( 135 | !self.after(to), 136 | "`EpochId::bump` must be used only for older epochs" 137 | ); 138 | *self = to; 139 | } 140 | 141 | /// Bumps epoch to specified one. 142 | /// Assumes this epoch is strictly before epoch to. 143 | #[inline] 144 | pub fn bump_cell(cell: &Cell, to: EpochId) { 145 | debug_assert!( 146 | !cell.get().after(to), 147 | "`EpochId::bump_cell` must be used only for older or same epochs" 148 | ); 149 | cell.set(to); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/flow/futures.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | convert::Infallible, 3 | future::Future, 4 | pin::Pin, 5 | task::{Context, Poll, Waker}, 6 | }; 7 | 8 | use smallvec::SmallVec; 9 | 10 | use crate::component::Component; 11 | 12 | use super::FlowEntity; 13 | 14 | /// Future that yields control to the executor once. 15 | pub struct YieldNow { 16 | yielded: bool, 17 | } 18 | 19 | impl YieldNow { 20 | /// Create a new instance of [`YieldNow`] future. 21 | pub const fn new() -> Self { 22 | YieldNow { yielded: false } 23 | } 24 | } 25 | 26 | impl Future for YieldNow { 27 | type Output = (); 28 | 29 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 30 | let me = self.get_mut(); 31 | if !me.yielded { 32 | me.yielded = true; 33 | cx.waker().wake_by_ref(); 34 | Poll::Pending 35 | } else { 36 | Poll::Ready(()) 37 | } 38 | } 39 | } 40 | 41 | /// Yield control to the executor once. 42 | /// Useful for implementing waiting in loops 43 | /// when there's no where to add a [`Waker`]. 44 | #[macro_export] 45 | macro_rules! yield_now { 46 | () => { 47 | $crate::flow::YieldNow::new().await 48 | }; 49 | } 50 | 51 | /// Component to wake all wakers bound to this entity when it's dropped. 52 | pub struct WakeOnDrop { 53 | wakers: SmallVec<[Waker; 4]>, 54 | } 55 | 56 | impl WakeOnDrop { 57 | /// Create a new instance of [`WakeOnDrop`] component. 58 | pub fn new() -> Self { 59 | WakeOnDrop { 60 | wakers: SmallVec::new(), 61 | } 62 | } 63 | 64 | /// Add a waker to the list of wakers to wake when this component is dropped. 65 | /// 66 | /// If waker bound to the same task is already in the list, it will not be added again. 67 | pub fn add_waker(&mut self, waker: &Waker) { 68 | for elem in &mut self.wakers { 69 | if elem.will_wake(waker) { 70 | return; 71 | } 72 | } 73 | self.wakers.push(waker.clone()); 74 | } 75 | 76 | /// Remove a waker from the list of wakers to wake when this component is dropped. 77 | pub fn remove_waker(&mut self, waker: &Waker) { 78 | if let Some(idx) = self.wakers.iter().position(|elem| elem.will_wake(waker)) { 79 | self.wakers.swap_remove(idx); 80 | } 81 | } 82 | } 83 | 84 | impl Drop for WakeOnDrop { 85 | fn drop(&mut self) { 86 | // Wake all flows bound to this entity to 87 | // allow them to terminate. 88 | for waker in self.wakers.drain(..) { 89 | waker.wake(); 90 | } 91 | } 92 | } 93 | 94 | impl Component for WakeOnDrop { 95 | #[inline] 96 | fn name() -> &'static str { 97 | "WakeOnDrop" 98 | } 99 | } 100 | 101 | impl FlowEntity { 102 | /// Waits until the entity is despawned. 103 | pub async fn wait_despawned(self) { 104 | let r = self 105 | .try_poll(|_e, _cx| { 106 | Poll::::Pending // `try_poll` will resolve to None when entity is despawned. 107 | }) 108 | .await; 109 | 110 | match r { 111 | Ok(never) => match never {}, 112 | Err(_) => return, 113 | } 114 | } 115 | 116 | /// Waits until the entity gets a component. 117 | /// Never resolves if the entity is despawned. 118 | pub async fn wait_has_component(self) 119 | where 120 | T: 'static, 121 | { 122 | self.poll_view::<&T, _, _>(|_, _cx| Poll::Ready(())).await; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/flow/tls.rs: -------------------------------------------------------------------------------- 1 | //! Provides a thread-local storage for the current world pointer. 2 | 3 | use core::{marker::PhantomData, ptr::NonNull}; 4 | 5 | use crate::world::WorldLocal; 6 | 7 | #[cfg(not(feature = "std"))] 8 | use crate::nostd::flow::{ 9 | edict_get_flow_world_tls, edict_reset_flow_world_tls, edict_set_flow_world_tls, 10 | }; 11 | 12 | #[cfg(feature = "std")] 13 | std::thread_local! { 14 | static WORLD_TLS: std::cell::Cell>> = const { std::cell::Cell::new(None) }; 15 | } 16 | 17 | /// Guard for setting and resetting the current world pointer. 18 | pub struct WorldGuard<'a> { 19 | this: NonNull, 20 | prev: Option>, 21 | marker: PhantomData<&'a mut WorldLocal>, 22 | } 23 | 24 | impl<'a> WorldGuard<'a> { 25 | /// Sets the current world pointer. 26 | /// Keeps previous pointer and resets it back on drop. 27 | pub fn new(world: &'a mut WorldLocal) -> Self { 28 | let this = NonNull::from(world); 29 | 30 | #[cfg(feature = "std")] 31 | let prev = WORLD_TLS.with(|cell| cell.replace(Some(this))); 32 | 33 | #[cfg(not(feature = "std"))] 34 | let prev = unsafe { edict_set_flow_world_tls(this.cast()).map(NonNull::cast) }; 35 | 36 | WorldGuard { 37 | this, 38 | prev, 39 | marker: PhantomData, 40 | } 41 | } 42 | } 43 | 44 | impl Drop for WorldGuard<'_> { 45 | fn drop(&mut self) { 46 | #[cfg(feature = "std")] 47 | WORLD_TLS.with(|cell| { 48 | let top = cell.replace(self.prev); 49 | debug_assert_eq!(top, Some(self.this)); 50 | }); 51 | 52 | #[cfg(not(feature = "std"))] 53 | unsafe { 54 | edict_reset_flow_world_tls(self.prev.map(NonNull::cast), self.this.cast()); 55 | } 56 | } 57 | } 58 | 59 | /// Returns the current world reference. 60 | /// 61 | /// # Safety 62 | /// 63 | /// `WorldGuard` must exist. 64 | /// 65 | /// Returns reference with unbound lifetime. 66 | /// The caller is responsible to ensure that the reference 67 | /// is not used after current `Guard` is dropped. 68 | pub(super) unsafe fn get_world_mut<'a>() -> &'a mut WorldLocal { 69 | #[cfg(feature = "std")] 70 | let world = WORLD_TLS.with(|cell| cell.get()); 71 | 72 | #[cfg(not(feature = "std"))] 73 | let world = unsafe { edict_get_flow_world_tls() }.map(NonNull::cast); 74 | 75 | unsafe { world.unwrap().as_mut() } 76 | } 77 | -------------------------------------------------------------------------------- /src/hash.rs: -------------------------------------------------------------------------------- 1 | use core::hash::{BuildHasher, Hasher}; 2 | 3 | #[derive(Default)] 4 | pub struct NoOpHasher { 5 | value: u64, 6 | } 7 | 8 | impl NoOpHasher { 9 | pub fn new() -> Self { 10 | NoOpHasher { value: 0 } 11 | } 12 | } 13 | 14 | impl Hasher for NoOpHasher { 15 | #[inline] 16 | fn write(&mut self, bytes: &[u8]) { 17 | let mut b = [0; 8]; 18 | let c = 8.min(bytes.len()); 19 | b[..c].copy_from_slice(&bytes[..c]); 20 | self.value = u64::from_ne_bytes(b); 21 | } 22 | 23 | #[inline] 24 | fn write_u64(&mut self, i: u64) { 25 | self.value = i; 26 | } 27 | 28 | #[inline] 29 | fn write_u128(&mut self, i: u128) { 30 | self.value = i as u64; 31 | } 32 | 33 | #[inline] 34 | fn write_usize(&mut self, i: usize) { 35 | self.value = i as u64; 36 | } 37 | 38 | #[inline] 39 | fn finish(&self) -> u64 { 40 | self.value 41 | } 42 | } 43 | 44 | #[derive(Default)] 45 | pub struct NoOpHasherBuilder; 46 | 47 | impl BuildHasher for NoOpHasherBuilder { 48 | type Hasher = NoOpHasher; 49 | 50 | #[inline] 51 | fn build_hasher(&self) -> NoOpHasher { 52 | NoOpHasher::new() 53 | } 54 | } 55 | 56 | const MUL_HASH_CONST_64: u64 = 11400714819323198485; 57 | 58 | pub struct MulHasher { 59 | value: u64, 60 | } 61 | 62 | impl MulHasher { 63 | #[inline] 64 | pub fn new() -> Self { 65 | MulHasher { value: 0 } 66 | } 67 | } 68 | 69 | impl Hasher for MulHasher { 70 | #[inline] 71 | fn write(&mut self, bytes: &[u8]) { 72 | for chunk in bytes.chunks(8) { 73 | let mut b = [0; 8]; 74 | b[..chunk.len()].copy_from_slice(chunk); 75 | self.value ^= u64::from_ne_bytes(b); 76 | } 77 | } 78 | 79 | #[inline] 80 | fn write_u64(&mut self, i: u64) { 81 | self.value = self.value.wrapping_mul(MUL_HASH_CONST_64).wrapping_add(i); 82 | } 83 | 84 | #[inline] 85 | fn write_u128(&mut self, i: u128) { 86 | self.write_u64(i as u64); 87 | self.write_u64((i >> 64) as u64); 88 | } 89 | 90 | #[inline] 91 | fn write_usize(&mut self, i: usize) { 92 | self.write_u64(i as u64); 93 | } 94 | 95 | #[inline] 96 | fn finish(&self) -> u64 { 97 | // Most significant bits are better than leas significant ones. 98 | self.value.wrapping_mul(MUL_HASH_CONST_64).swap_bytes() 99 | } 100 | } 101 | 102 | pub struct MulHasherBuilder; 103 | 104 | impl BuildHasher for MulHasherBuilder { 105 | type Hasher = MulHasher; 106 | 107 | fn build_hasher(&self) -> MulHasher { 108 | MulHasher::new() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/nostd.rs: -------------------------------------------------------------------------------- 1 | //! This module contains submodules with extern function declarations. 2 | //! 3 | //! It is only relevant when "std" feature is disabled. 4 | //! 5 | //! When submodule's feature is enabled, it is required to supply implementation for each function. 6 | //! Otherwise linker error may occur. 7 | 8 | /// Declares extern functions required when "flow" feature is enabled without "std". 9 | #[cfg(feature = "flow")] 10 | pub mod flow { 11 | use core::ptr::NonNull; 12 | 13 | unsafe extern "C" { 14 | /// Sets the current world pointer in thread-local storage. 15 | /// Returns previous pointer if any. 16 | /// 17 | /// # Safety 18 | /// 19 | /// Pointer to the world will be stored in thread-local storage 20 | /// and returned by [`edict_get_flow_world_tls`]. 21 | /// At which point it can be accessed mutably, thus world passed to this function 22 | /// should not be accessed otherwise. 23 | /// 24 | /// Up until the thread-local storage is reset using [`edict_reset_flow_world_tls`]. 25 | pub unsafe fn edict_set_flow_world_tls(world: NonNull) -> Option>; 26 | 27 | /// Returns the current world pointer in thread-local storage. 28 | /// 29 | /// # Safety 30 | /// 31 | /// This function should be safe to call. 32 | /// 33 | /// Returned pointer must remain valid until [`edict_reset_flow_world_tls`] is called to reset it. 34 | pub unsafe fn edict_get_flow_world_tls() -> Option>; 35 | 36 | /// Resets the current world pointer in thread-local storage. 37 | /// 38 | /// # Safety 39 | /// 40 | /// This function should be safe to call if the following conditions are met: 41 | /// 42 | /// - The `prev` pointer is the value returned by `edict_set_flow_world_tls` when the `world` was passed in on this thread. 43 | /// - The `world` pointer must be the current world pointer in thread-local storage. 44 | /// i.e. for each call to [`edict_reset_flow_world_tls`] made after the call that set the `world` pointer, 45 | /// [`edict_reset_flow_world_tls`] call was made. 46 | pub unsafe fn edict_reset_flow_world_tls(prev: Option>, world: NonNull); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/prelude.rs: -------------------------------------------------------------------------------- 1 | //! A prelude module. Reexports types and traits, enough to start using [`edict`] 2 | #[doc(no_inline)] 3 | pub use crate::{ 4 | action::{ 5 | ActionBuffer, ActionEncoder, ActionSender, LocalActionBuffer, LocalActionEncoder, 6 | LocalSpawnBatch, SpawnBatch, SpawnBatchSender, 7 | }, 8 | bundle::EntityBuilder, 9 | component::Component, 10 | entity::{EntityId, EntityRef}, 11 | query::{Alt, Entities, Modified, Query, With, Without}, 12 | relation::{ChildOf, Related, Relates, RelatesExclusive, RelatesTo, Relation}, 13 | resources::{Res, ResMut}, 14 | system::{ResLocal, ResMutLocal, State, System}, 15 | view::{View, ViewCell, ViewCellIter, ViewIter, ViewMut, ViewOne, ViewRef}, 16 | world::{World, WorldBuilder}, 17 | EntityError, NoSuchEntity, 18 | }; 19 | -------------------------------------------------------------------------------- /src/query/alt.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | any::TypeId, 3 | cell::Cell, 4 | marker::PhantomData, 5 | ops::{Deref, DerefMut}, 6 | ptr::NonNull, 7 | }; 8 | 9 | use crate::{ 10 | archetype::{chunk_idx, Archetype}, 11 | component::ComponentInfo, 12 | epoch::EpochId, 13 | system::QueryArg, 14 | type_id, 15 | }; 16 | 17 | use super::{Access, AsQuery, DefaultQuery, Fetch, IntoQuery, Query, SendQuery, WriteAlias}; 18 | 19 | /// Item type that [`Alt`] yields. 20 | /// Wraps `&mut T` and implements [`DerefMut`] to `T`. 21 | /// Bumps component epoch on dereference. 22 | #[derive(Debug)] 23 | pub struct RefMut<'a, T: ?Sized> { 24 | pub(super) component: &'a mut T, 25 | pub(super) entity_epoch: &'a mut EpochId, 26 | pub(super) chunk_epoch: &'a Cell, 27 | pub(super) archetype_epoch: &'a Cell, 28 | pub(super) epoch: EpochId, 29 | } 30 | 31 | impl Deref for RefMut<'_, T> { 32 | type Target = T; 33 | 34 | #[inline] 35 | fn deref(&self) -> &T { 36 | &*self.component 37 | } 38 | } 39 | 40 | impl DerefMut for RefMut<'_, T> { 41 | #[inline] 42 | fn deref_mut(&mut self) -> &mut T { 43 | self.entity_epoch.bump_again(self.epoch); 44 | EpochId::bump_cell(&self.chunk_epoch, self.epoch); 45 | EpochId::bump_cell(&self.archetype_epoch, self.epoch); 46 | self.component 47 | } 48 | } 49 | 50 | /// [`Fetch`] type for the [`Alt`] query. 51 | pub struct FetchAlt<'a, T> { 52 | epoch: EpochId, 53 | ptr: NonNull, 54 | entity_epochs: NonNull, 55 | chunk_epochs: NonNull>, 56 | archetype_epoch: NonNull>, 57 | marker: PhantomData<&'a [T]>, 58 | } 59 | 60 | unsafe impl<'a, T> Fetch<'a> for FetchAlt<'a, T> 61 | where 62 | T: 'a, 63 | { 64 | type Item = RefMut<'a, T>; 65 | 66 | #[inline] 67 | fn dangling() -> Self { 68 | FetchAlt { 69 | epoch: EpochId::start(), 70 | ptr: NonNull::dangling(), 71 | entity_epochs: NonNull::dangling(), 72 | chunk_epochs: NonNull::dangling(), 73 | archetype_epoch: NonNull::dangling(), 74 | marker: PhantomData, 75 | } 76 | } 77 | 78 | #[inline] 79 | unsafe fn touch_chunk(&mut self, chunk_idx: u32) { 80 | let chunk_epoch = unsafe { &mut *self.chunk_epochs.as_ptr().add(chunk_idx as usize) }; 81 | debug_assert!((*chunk_epoch).get().before(self.epoch)); 82 | } 83 | 84 | #[inline] 85 | unsafe fn get_item(&mut self, idx: u32) -> RefMut<'a, T> { 86 | let archetype_epoch = unsafe { &mut *self.archetype_epoch.as_ptr() }; 87 | let chunk_epoch = unsafe { &mut *self.chunk_epochs.as_ptr().add(chunk_idx(idx) as usize) }; 88 | let entity_epoch = unsafe { &mut *self.entity_epochs.as_ptr().add(idx as usize) }; 89 | 90 | debug_assert!(entity_epoch.before(self.epoch)); 91 | 92 | RefMut { 93 | component: unsafe { &mut *self.ptr.as_ptr().add(idx as usize) }, 94 | entity_epoch, 95 | chunk_epoch, 96 | archetype_epoch, 97 | epoch: self.epoch, 98 | } 99 | } 100 | } 101 | 102 | marker_type! { 103 | /// Query that yields wrapped mutable reference to specified component 104 | /// for each entity that has that component. 105 | /// 106 | /// Skips entities that don't have the component. 107 | /// 108 | /// Works almost as `&mut T` does. 109 | /// However, it does not updates entity epoch 110 | /// unless returned reference wrapper is dereferenced. 111 | pub struct Alt; 112 | } 113 | 114 | impl AsQuery for Alt 115 | where 116 | T: 'static, 117 | { 118 | type Query = Self; 119 | } 120 | 121 | impl IntoQuery for Alt 122 | where 123 | T: 'static, 124 | { 125 | #[inline] 126 | fn into_query(self) -> Self { 127 | self 128 | } 129 | } 130 | 131 | impl DefaultQuery for Alt 132 | where 133 | T: 'static, 134 | { 135 | #[inline] 136 | fn default_query() -> Self { 137 | Alt 138 | } 139 | } 140 | 141 | impl QueryArg for Alt 142 | where 143 | T: Send + 'static, 144 | { 145 | #[inline] 146 | fn new() -> Self { 147 | Alt 148 | } 149 | } 150 | 151 | unsafe impl Query for Alt 152 | where 153 | T: 'static, 154 | { 155 | type Item<'a> = RefMut<'a, T>; 156 | type Fetch<'a> = FetchAlt<'a, T>; 157 | 158 | const MUTABLE: bool = true; 159 | 160 | #[inline] 161 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 162 | if comp.id() == type_id::() { 163 | Ok(Some(Access::Write)) 164 | } else { 165 | Ok(None) 166 | } 167 | } 168 | 169 | #[inline] 170 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 171 | archetype.has_component(type_id::()) 172 | } 173 | 174 | #[inline] 175 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 176 | f(type_id::(), Access::Write) 177 | } 178 | 179 | #[inline] 180 | unsafe fn fetch<'a>( 181 | &self, 182 | _arch_idx: u32, 183 | archetype: &'a Archetype, 184 | epoch: EpochId, 185 | ) -> FetchAlt<'a, T> { 186 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 187 | debug_assert_eq!(component.id(), type_id::()); 188 | let data = unsafe { component.data_mut() }; 189 | debug_assert!(data.epoch.before(epoch)); 190 | 191 | FetchAlt { 192 | epoch, 193 | ptr: data.ptr.cast(), 194 | entity_epochs: unsafe { NonNull::new_unchecked(data.entity_epochs.as_mut_ptr()) }, 195 | chunk_epochs: unsafe { NonNull::new_unchecked(data.chunk_epochs.as_mut_ptr()) }.cast(), 196 | archetype_epoch: NonNull::from(&mut data.epoch).cast(), 197 | marker: PhantomData, 198 | } 199 | } 200 | } 201 | 202 | unsafe impl SendQuery for Alt where T: Send + 'static {} 203 | -------------------------------------------------------------------------------- /src/query/any_of.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | boolean::{BooleanQuery, OrOp}, 3 | read::Read, 4 | write::Write, 5 | IntoQuery, 6 | }; 7 | 8 | marker_type! { 9 | /// A query adaptor parameterized by a tuple of queries. 10 | /// Yields a tuple of items from each query wrapped in `Option`. 11 | /// Yields `None` for queries that do not match the entity. 12 | /// Skips if no queries match the entity. 13 | pub struct AnyOf; 14 | } 15 | 16 | macro_rules! any_of { 17 | () => { /* Don't implement for empty tuple */ }; 18 | ($($a:ident)+) => { 19 | impl<$($a),+> IntoQuery for AnyOf<($(&$a,)+)> 20 | where 21 | $($a: Sync + 'static,)+ 22 | { 23 | type Query = BooleanQuery<($(Read<$a>,)+), OrOp>; 24 | 25 | fn into_query(self) -> Self::Query { 26 | BooleanQuery::from_tuple(($(Read::<$a>,)*)) 27 | } 28 | } 29 | 30 | impl<$($a),+> IntoQuery for AnyOf<($(&mut $a,)+)> 31 | where 32 | $($a: Send + 'static,)+ 33 | { 34 | type Query = BooleanQuery<($(Write<$a>,)+), OrOp>; 35 | 36 | fn into_query(self) -> Self::Query { 37 | BooleanQuery::from_tuple(($(Write::<$a>,)*)) 38 | } 39 | } 40 | }; 41 | } 42 | 43 | for_tuple!(any_of); 44 | -------------------------------------------------------------------------------- /src/query/borrow/mod.rs: -------------------------------------------------------------------------------- 1 | mod all; 2 | mod any; 3 | mod one; 4 | 5 | pub use self::{all::*, any::*, one::*}; 6 | -------------------------------------------------------------------------------- /src/query/copied.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, epoch::EpochId, system::QueryArg, type_id, 5 | }; 6 | 7 | use super::{ 8 | Access, AsQuery, DefaultQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, WriteAlias, 9 | }; 10 | 11 | /// [`Fetch`] type for the `&T` query. 12 | 13 | pub struct FetchCpy<'a, T> { 14 | ptr: NonNull, 15 | marker: PhantomData<&'a [T]>, 16 | } 17 | 18 | unsafe impl<'a, T> Fetch<'a> for FetchCpy<'a, T> 19 | where 20 | T: Copy + 'a, 21 | { 22 | type Item = T; 23 | 24 | #[inline] 25 | fn dangling() -> Self { 26 | FetchCpy { 27 | ptr: NonNull::dangling(), 28 | marker: PhantomData, 29 | } 30 | } 31 | 32 | #[inline] 33 | unsafe fn get_item(&mut self, idx: u32) -> T { 34 | unsafe { *self.ptr.as_ptr().add(idx as usize) } 35 | } 36 | } 37 | 38 | marker_type! { 39 | /// Query for fetching a copy of a component. 40 | /// Borrows component immutably and yields a copy. 41 | /// Prefer this over `&T` or [`Read`] for small `Copy` types. 42 | /// 43 | /// [`Read`]: crate::query::Read 44 | pub struct Cpy; 45 | } 46 | 47 | impl AsQuery for Cpy 48 | where 49 | T: Copy + 'static, 50 | { 51 | type Query = Self; 52 | } 53 | 54 | impl IntoQuery for Cpy 55 | where 56 | T: Copy + 'static, 57 | { 58 | #[inline] 59 | fn into_query(self) -> Self { 60 | self 61 | } 62 | } 63 | 64 | impl DefaultQuery for Cpy 65 | where 66 | T: Copy + 'static, 67 | { 68 | #[inline] 69 | fn default_query() -> Self { 70 | Cpy 71 | } 72 | } 73 | 74 | impl QueryArg for Cpy 75 | where 76 | T: Copy + Sync + 'static, 77 | { 78 | #[inline] 79 | fn new() -> Self { 80 | Cpy 81 | } 82 | } 83 | 84 | unsafe impl Query for Cpy 85 | where 86 | T: Copy + 'static, 87 | { 88 | type Item<'a> = T; 89 | type Fetch<'a> = FetchCpy<'a, T>; 90 | 91 | const MUTABLE: bool = false; 92 | 93 | #[inline] 94 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 95 | if comp.id() == type_id::() { 96 | Ok(Some(Access::Read)) 97 | } else { 98 | Ok(None) 99 | } 100 | } 101 | 102 | #[inline] 103 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 104 | archetype.has_component(type_id::()) 105 | } 106 | 107 | #[inline] 108 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 109 | f(type_id::(), Access::Read) 110 | } 111 | 112 | #[inline] 113 | unsafe fn fetch<'a>( 114 | &self, 115 | _arch_idx: u32, 116 | archetype: &'a Archetype, 117 | _epoch: EpochId, 118 | ) -> FetchCpy<'a, T> { 119 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 120 | debug_assert_eq!(component.id(), type_id::()); 121 | 122 | let data = unsafe { component.data() }; 123 | 124 | FetchCpy { 125 | ptr: data.ptr.cast(), 126 | marker: PhantomData, 127 | } 128 | } 129 | } 130 | 131 | unsafe impl ImmutableQuery for Cpy where T: Copy + 'static {} 132 | 133 | unsafe impl SendQuery for Cpy where T: Sync + Copy + 'static {} 134 | -------------------------------------------------------------------------------- /src/query/entities.rs: -------------------------------------------------------------------------------- 1 | use core::any::TypeId; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | entity::{EntityId, EntityLoc, Location}, 7 | system::QueryArg, 8 | }; 9 | 10 | use super::{ 11 | Access, AsQuery, BatchFetch, DefaultQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, 12 | WriteAlias, 13 | }; 14 | 15 | /// [`Fetch`] type for the [`Entities`] query. 16 | pub struct EntitiesFetch<'a> { 17 | archetype: u32, 18 | entities: &'a [EntityId], 19 | } 20 | 21 | unsafe impl<'a> Fetch<'a> for EntitiesFetch<'a> { 22 | type Item = EntityLoc<'a>; 23 | 24 | #[inline] 25 | fn dangling() -> Self { 26 | EntitiesFetch { 27 | archetype: u32::MAX, 28 | entities: &[], 29 | } 30 | } 31 | 32 | #[inline] 33 | unsafe fn get_item(&mut self, idx: u32) -> EntityLoc<'a> { 34 | let id = unsafe { *self.entities.get_unchecked(idx as usize) }; 35 | EntityLoc::from_parts(id, Location::new(self.archetype, idx)) 36 | } 37 | } 38 | 39 | unsafe impl<'a> BatchFetch<'a> for EntitiesFetch<'a> { 40 | type Batch = &'a [EntityId]; 41 | 42 | #[inline] 43 | unsafe fn get_batch(&mut self, start: u32, end: u32) -> &'a [EntityId] { 44 | debug_assert!(end >= start); 45 | 46 | unsafe { &*self.entities.get_unchecked(start as usize..end as usize) } 47 | } 48 | } 49 | 50 | marker_type! { 51 | /// Queries entity ids. 52 | pub struct Entities; 53 | } 54 | 55 | impl AsQuery for Entities { 56 | type Query = Self; 57 | } 58 | 59 | impl IntoQuery for Entities { 60 | #[inline] 61 | fn into_query(self) -> Self { 62 | self 63 | } 64 | } 65 | 66 | impl DefaultQuery for Entities { 67 | #[inline] 68 | fn default_query() -> Self { 69 | Entities 70 | } 71 | } 72 | 73 | impl QueryArg for Entities { 74 | #[inline] 75 | fn new() -> Self { 76 | Entities 77 | } 78 | } 79 | 80 | unsafe impl Query for Entities { 81 | type Fetch<'a> = EntitiesFetch<'a>; 82 | type Item<'a> = EntityLoc<'a>; 83 | 84 | const MUTABLE: bool = false; 85 | 86 | #[inline] 87 | fn component_access(&self, _comp: &ComponentInfo) -> Result, WriteAlias> { 88 | Ok(None) 89 | } 90 | 91 | #[inline] 92 | fn visit_archetype(&self, _archetype: &Archetype) -> bool { 93 | true 94 | } 95 | 96 | #[inline] 97 | unsafe fn access_archetype(&self, _archetype: &Archetype, _f: impl FnMut(TypeId, Access)) {} 98 | 99 | #[inline] 100 | unsafe fn fetch<'a>( 101 | &self, 102 | arch_idx: u32, 103 | archetype: &'a Archetype, 104 | _epoch: crate::epoch::EpochId, 105 | ) -> EntitiesFetch<'a> { 106 | EntitiesFetch { 107 | archetype: arch_idx, 108 | entities: archetype.entities(), 109 | } 110 | } 111 | 112 | #[inline] 113 | fn reserved_entity_item<'a>(&self, id: EntityId, idx: u32) -> Option> 114 | where 115 | EntityId: 'a, 116 | { 117 | Some(EntityLoc::from_parts(id, Location::reserved(idx))) 118 | } 119 | } 120 | 121 | unsafe impl ImmutableQuery for Entities {} 122 | unsafe impl SendQuery for Entities {} 123 | -------------------------------------------------------------------------------- /src/query/modified/any_of.rs: -------------------------------------------------------------------------------- 1 | use crate::query::{ 2 | any_of::AnyOf, boolean::BooleanQuery, boolean::OrOp, read::Read, write::Write, IntoQuery, 3 | }; 4 | 5 | use super::Modified; 6 | 7 | macro_rules! any_of { 8 | () => { /* Don't implement for empty tuple */ }; 9 | ($($a:ident)+) => { 10 | impl<$($a),+> IntoQuery for Modified> 11 | where 12 | $($a: Sync + 'static,)+ 13 | { 14 | type Query = BooleanQuery<($(Modified>,)+), OrOp>; 15 | 16 | fn into_query(self) -> Self::Query { 17 | BooleanQuery::from_tuple(($(Modified::>::new(self.after_epoch),)*)) 18 | } 19 | } 20 | 21 | impl<$($a),+> IntoQuery for Modified,)+)>> 22 | where 23 | $($a: Sync + 'static,)+ 24 | { 25 | type Query = BooleanQuery<($(Modified>,)+), OrOp>; 26 | 27 | fn into_query(self) -> Self::Query { 28 | BooleanQuery::from_tuple(($(Modified::>::new(self.after_epoch),)*)) 29 | } 30 | } 31 | 32 | impl<$($a),+> IntoQuery for Modified> 33 | where 34 | $($a: Send + 'static,)+ 35 | { 36 | type Query = BooleanQuery<($(Modified>,)+), OrOp>; 37 | 38 | fn into_query(self) -> Self::Query { 39 | BooleanQuery::from_tuple(($(Modified::>::new(self.after_epoch),)*)) 40 | } 41 | } 42 | 43 | impl<$($a),+> IntoQuery for Modified,)+)>> 44 | where 45 | $($a: Send + 'static,)+ 46 | { 47 | type Query = BooleanQuery<($(Modified>,)+), OrOp>; 48 | 49 | fn into_query(self) -> Self::Query { 50 | BooleanQuery::from_tuple(($(Modified::>::new(self.after_epoch),)*)) 51 | } 52 | } 53 | }; 54 | } 55 | 56 | for_tuple!(any_of); 57 | -------------------------------------------------------------------------------- /src/query/modified/mod.rs: -------------------------------------------------------------------------------- 1 | mod alt; 2 | // mod any_of; 3 | mod copied; 4 | mod read; 5 | mod with; 6 | mod write; 7 | 8 | use crate::epoch::EpochId; 9 | 10 | pub use self::{ 11 | alt::ModifiedFetchAlt, copied::ModifiedFetchCopied, read::ModifiedFetchRead, 12 | with::ModifiedFetchWith, write::ModifiedFetchWrite, 13 | }; 14 | 15 | /// Query over modified component. 16 | /// 17 | /// Should be used as either [`Modified<&T>`], [`Modified<&mut T>`] 18 | /// or [`Modified>`]. 19 | /// 20 | /// This is tracking query that uses epoch lower bound to filter out entities with unmodified components. 21 | #[derive(Clone, Copy, Debug)] 22 | pub struct Modified { 23 | after_epoch: EpochId, 24 | query: T, 25 | } 26 | 27 | impl Modified { 28 | /// Creates new `Modified` query. 29 | /// Uses provided `after_epoch` id to skip components that are last modified not after this epoch. 30 | pub fn new(after_epoch: EpochId) -> Self 31 | where 32 | T: Default, 33 | { 34 | Modified { 35 | after_epoch, 36 | query: T::default(), 37 | } 38 | } 39 | 40 | /// Epoch id threshold for this query. 41 | pub fn after_epoch(&self) -> EpochId { 42 | self.after_epoch 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/query/modified/with.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | epoch::EpochId, 7 | query::{ 8 | filter::With, Access, AsQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, 9 | WriteAlias, 10 | }, 11 | system::QueryArg, 12 | type_id, 13 | world::World, 14 | }; 15 | 16 | use super::Modified; 17 | 18 | /// [`Fetch`] type for the [`Modified<&T>`] query. 19 | pub struct ModifiedFetchWith<'a, T> { 20 | after_epoch: EpochId, 21 | entity_epochs: NonNull, 22 | chunk_epochs: NonNull, 23 | marker: PhantomData<&'a [T]>, 24 | } 25 | 26 | unsafe impl<'a, T> Fetch<'a> for ModifiedFetchWith<'a, T> 27 | where 28 | T: 'a, 29 | { 30 | type Item = (); 31 | 32 | #[inline] 33 | fn dangling() -> Self { 34 | ModifiedFetchWith { 35 | after_epoch: EpochId::start(), 36 | entity_epochs: NonNull::dangling(), 37 | chunk_epochs: NonNull::dangling(), 38 | marker: PhantomData, 39 | } 40 | } 41 | 42 | #[inline] 43 | unsafe fn visit_chunk(&mut self, chunk_idx: u32) -> bool { 44 | let chunk_epoch = unsafe { *self.chunk_epochs.as_ptr().add(chunk_idx as usize) }; 45 | chunk_epoch.after(self.after_epoch) 46 | } 47 | 48 | #[inline] 49 | unsafe fn visit_item(&mut self, idx: u32) -> bool { 50 | let epoch = unsafe { *self.entity_epochs.as_ptr().add(idx as usize) }; 51 | epoch.after(self.after_epoch) 52 | } 53 | 54 | #[inline] 55 | unsafe fn get_item(&mut self, _: u32) {} 56 | } 57 | 58 | impl AsQuery for Modified> 59 | where 60 | T: 'static, 61 | { 62 | type Query = Self; 63 | } 64 | 65 | impl IntoQuery for Modified> 66 | where 67 | T: 'static, 68 | { 69 | fn into_query(self) -> Self { 70 | self 71 | } 72 | } 73 | 74 | impl QueryArg for Modified> 75 | where 76 | T: 'static, 77 | { 78 | #[inline] 79 | fn new() -> Self { 80 | Modified { 81 | after_epoch: EpochId::start(), 82 | query: With, 83 | } 84 | } 85 | 86 | #[inline] 87 | fn after(&mut self, world: &World) { 88 | self.after_epoch = world.epoch(); 89 | } 90 | } 91 | 92 | unsafe impl Query for Modified> 93 | where 94 | T: 'static, 95 | { 96 | type Item<'a> = (); 97 | type Fetch<'a> = ModifiedFetchWith<'a, T>; 98 | 99 | const MUTABLE: bool = false; 100 | 101 | #[inline] 102 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 103 | self.query.component_access(comp) 104 | } 105 | 106 | #[inline] 107 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 108 | match archetype.component(type_id::()) { 109 | None => false, 110 | Some(component) => unsafe { 111 | debug_assert_eq!(self.query.visit_archetype(archetype), true); 112 | debug_assert_eq!(component.id(), type_id::()); 113 | true 114 | }, 115 | } 116 | } 117 | 118 | #[inline] 119 | unsafe fn visit_archetype_late(&self, archetype: &Archetype) -> bool { 120 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 121 | let data = unsafe { component.data() }; 122 | data.epoch.after(self.after_epoch) 123 | } 124 | 125 | #[inline] 126 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 127 | f(type_id::(), Access::Read) 128 | } 129 | 130 | #[inline] 131 | unsafe fn fetch<'a>( 132 | &self, 133 | _arch_idx: u32, 134 | archetype: &'a Archetype, 135 | _epoch: EpochId, 136 | ) -> ModifiedFetchWith<'a, T> { 137 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 138 | let data = unsafe { component.data() }; 139 | 140 | debug_assert!(data.epoch.after(self.after_epoch)); 141 | 142 | ModifiedFetchWith { 143 | after_epoch: self.after_epoch, 144 | entity_epochs: unsafe { 145 | NonNull::new_unchecked(data.entity_epochs.as_ptr() as *mut EpochId) 146 | }, 147 | chunk_epochs: unsafe { 148 | NonNull::new_unchecked(data.chunk_epochs.as_ptr() as *mut EpochId) 149 | }, 150 | marker: PhantomData, 151 | } 152 | } 153 | } 154 | 155 | unsafe impl ImmutableQuery for Modified> where T: 'static {} 156 | unsafe impl SendQuery for Modified> where T: 'static {} 157 | -------------------------------------------------------------------------------- /src/query/one_of.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! one_of { 3 | (@IntoQuery $q:ty) => { 4 | <$q as $crate::query::IntoQuery>::Query 5 | }; 6 | // (@IntoQuery &'a mut $a:ty) => { 7 | // $crate::query::Write<$a> 8 | // }; 9 | (@Fetch $q:ty) => { 10 | <<$q as $crate::query::IntoQuery>::Query as $crate::query::Query>::Fetch<'a> 11 | }; 12 | // (@Fetch &'a mut $a:ty) => { 13 | // $crate::query::FetchWrite<'a, $a> 14 | // }; 15 | (@MUTABLE $q:ty) => { 16 | <<$q as $crate::query::IntoQuery>::Query as $crate::query::Query>::MUTABLE 17 | }; 18 | // (@MUTABLE &'a mut $a:ty) => { 19 | // true 20 | // }; 21 | (@DANGLING_FETCH $v:ident, $($rest:ident,)*) => { 22 | OneOfFetch::$v($crate::query::Fetch::dangling()) 23 | }; 24 | 25 | ($vis:vis $name:ident <'a> { 26 | $($v:ident ( $q:ty )),+ $(,)? 27 | }) => { 28 | $vis enum $name<'a> { 29 | $($v($q),)+ 30 | } 31 | 32 | #[allow(non_snake_case)] 33 | #[allow(unused_parens)] 34 | const _: () = { 35 | 36 | $vis enum OneOfFetch<'a> { 37 | $($v( $crate::one_of!(@Fetch $q) ),)+ 38 | } 39 | 40 | #[allow(unused_parens)] 41 | #[allow(non_snake_case)] 42 | unsafe impl Fetch<'a> for OneOfFetch<'a> { 43 | type Item = $name<'a>; 44 | 45 | #[inline] 46 | fn dangling() -> Self { 47 | $crate::one_of!(@DANGLING_FETCH $($v,)+) 48 | } 49 | 50 | #[inline] 51 | unsafe fn visit_chunk(&mut self, chunk_idx: u32) -> bool { 52 | match self { 53 | $(OneOfFetch::$v($v) => unsafe { 54 | $crate::query::Fetch::visit_chunk($v, chunk_idx) 55 | },)+ 56 | } 57 | } 58 | 59 | /// Checks if item with specified index must be visited or skipped. 60 | #[inline] 61 | unsafe fn visit_item(&mut self, idx: u32) -> bool { 62 | match self { 63 | $(OneOfFetch::$v($v) => unsafe { 64 | $crate::query::Fetch::visit_item($v, idx) 65 | },)+ 66 | } 67 | } 68 | 69 | /// Notifies this fetch that it visits a chunk. 70 | #[inline] 71 | unsafe fn touch_chunk(&mut self, chunk_idx: u32) { 72 | match self { 73 | $(OneOfFetch::$v($v) => unsafe { 74 | $crate::query::Fetch::touch_chunk($v, chunk_idx) 75 | },)+ 76 | } 77 | } 78 | 79 | #[inline] 80 | unsafe fn get_item(&mut self, idx: u32) -> $name<'a> { 81 | match self { 82 | $(OneOfFetch::$v($v) => unsafe { 83 | $name::$v($crate::query::Fetch::get_item($v, idx)) 84 | },)+ 85 | } 86 | } 87 | } 88 | 89 | // $vis struct OneOf; 90 | 91 | // impl $crate::query::IntoQuery for $name<'_> { 92 | // type Query = OneOf; 93 | // } 94 | 95 | // impl DefaultQuery for $name<'_> { 96 | // #[inline] 97 | // fn default_query() -> OneOf { 98 | // OneOf 99 | // } 100 | // } 101 | 102 | // impl $crate::query::QueryArg for $name<'_> { 103 | // #[inline] 104 | // fn new() -> OneOf { 105 | // OneOf 106 | // } 107 | // } 108 | 109 | // #[allow(non_snake_case)] 110 | // #[allow(unused_parens)] 111 | // impl $crate::query::Query for OneOf { 112 | // type Item<'a> = $name<'a>; 113 | // type Fetch<'a> = OneOfFetch<'a>, 114 | 115 | // const MUTABLE: bool = $($crate::one_of!(@MUTABLE $q) ||)+ false; 116 | // const FILTERS_ENTITIES: bool = false; 117 | 118 | // fn component_access(&self, ty: core::any::TypeId) -> Result, WriteAlias> { 119 | // $( 120 | // let $v = $crate::one_of!(@IntoQuery &'a $q); 121 | // match $crate::query::Query::component_access(&$v, ty)? { 122 | // None => {}, 123 | // Some(access) => return Ok(Some(access)), 124 | // }; 125 | // )* 126 | // Ok(None) 127 | // } 128 | 129 | // #[inline] 130 | // fn visit_archetype(&self, archetype: &Archetype) -> bool { 131 | // $(let $v = $crate::one_of!(@IntoQuery &'a $q);)+ 132 | // $( $crate::query::Query::visit_archetype(&$v, archetype) )||+ 133 | // } 134 | 135 | // #[inline] 136 | // unsafe fn access_archetype(&self, archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 137 | // $( 138 | // let $v = $crate::one_of!(@IntoQuery &'a $q); 139 | // if $crate::query::Query::visit_archetype(&$v, archetype) { 140 | // $crate::query::Query::access_archetype(&$v, archetype, &mut f); 141 | // return; 142 | // } 143 | // )+ 144 | // } 145 | 146 | // #[inline] 147 | // unsafe fn fetch<'a>(&self, arch_idx: u32, archetype: &'a Archetype, epoch: EpochId) -> OneOfFetch<'a> { 148 | // $(let $v = $crate::one_of!(@IntoQuery &'a $q);)+ 149 | // $( 150 | // if $crate::query::Query::visit_archetype(&$v, archetype) { 151 | // return OneOfFetch::$v($crate::query::Query::fetch(&$v, arch_idx, archetype, epoch)); 152 | // } 153 | // )+ 154 | // unsafe { 155 | // core::hint::unreachable_unchecked() 156 | // } 157 | // } 158 | 159 | // #[inline] 160 | // fn reserved_entity_item<'a>(&self, _id: EntityId, _idx: u32) -> Option<$name<'a>> { 161 | // None 162 | // } 163 | // } 164 | }; 165 | }; 166 | } 167 | -------------------------------------------------------------------------------- /src/query/option.rs: -------------------------------------------------------------------------------- 1 | use core::any::TypeId; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, entity::EntityId, epoch::EpochId, 5 | system::QueryArg, 6 | }; 7 | 8 | use super::{ 9 | Access, AsQuery, BatchFetch, DefaultQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, 10 | WriteAlias, 11 | }; 12 | 13 | unsafe impl<'a, T> Fetch<'a> for Option 14 | where 15 | T: Fetch<'a>, 16 | { 17 | type Item = Option; 18 | 19 | /// Returns `Fetch` value that must not be used. 20 | fn dangling() -> Self { 21 | None 22 | } 23 | 24 | /// Checks if chunk with specified index must be visited or skipped. 25 | #[inline] 26 | unsafe fn visit_chunk(&mut self, chunk_idx: u32) -> bool { 27 | if let Some(fetch) = self { 28 | unsafe { fetch.visit_chunk(chunk_idx) } 29 | } else { 30 | true 31 | } 32 | } 33 | 34 | /// Notifies this fetch that it visits a chunk. 35 | #[inline] 36 | unsafe fn touch_chunk(&mut self, chunk_idx: u32) { 37 | if let Some(fetch) = self { 38 | unsafe { 39 | fetch.touch_chunk(chunk_idx); 40 | } 41 | } 42 | } 43 | 44 | /// Checks if item with specified index must be visited or skipped. 45 | #[inline] 46 | unsafe fn visit_item(&mut self, idx: u32) -> bool { 47 | if let Some(fetch) = self { 48 | unsafe { fetch.visit_item(idx) } 49 | } else { 50 | true 51 | } 52 | } 53 | 54 | /// Returns fetched item at specified index. 55 | unsafe fn get_item(&mut self, idx: u32) -> Option { 56 | match self { 57 | None => None, 58 | Some(fetch) => Some(unsafe { fetch.get_item(idx) }), 59 | } 60 | } 61 | } 62 | 63 | unsafe impl<'a, T> BatchFetch<'a> for Option 64 | where 65 | T: BatchFetch<'a>, 66 | { 67 | type Batch = Option; 68 | 69 | /// Returns fetched item at specified index. 70 | unsafe fn get_batch(&mut self, start: u32, end: u32) -> Option { 71 | match self { 72 | None => None, 73 | Some(fetch) => Some(unsafe { fetch.get_batch(start, end) }), 74 | } 75 | } 76 | } 77 | 78 | #[derive(Clone, Copy, Debug)] 79 | pub struct OptionQuery(pub T); 80 | 81 | impl AsQuery for Option 82 | where 83 | T: AsQuery, 84 | { 85 | type Query = OptionQuery; 86 | } 87 | 88 | impl DefaultQuery for Option 89 | where 90 | T: DefaultQuery, 91 | { 92 | #[inline] 93 | fn default_query() -> OptionQuery { 94 | OptionQuery(T::default_query()) 95 | } 96 | } 97 | 98 | impl AsQuery for OptionQuery 99 | where 100 | T: AsQuery, 101 | { 102 | type Query = OptionQuery; 103 | } 104 | 105 | impl IntoQuery for OptionQuery 106 | where 107 | T: IntoQuery, 108 | { 109 | fn into_query(self) -> OptionQuery { 110 | OptionQuery(self.0.into_query()) 111 | } 112 | } 113 | 114 | impl DefaultQuery for OptionQuery 115 | where 116 | T: DefaultQuery, 117 | { 118 | #[inline] 119 | fn default_query() -> OptionQuery { 120 | OptionQuery(T::default_query()) 121 | } 122 | } 123 | 124 | impl QueryArg for OptionQuery 125 | where 126 | T: QueryArg, 127 | { 128 | #[inline] 129 | fn new() -> Self { 130 | OptionQuery(T::new()) 131 | } 132 | } 133 | 134 | unsafe impl Query for OptionQuery 135 | where 136 | T: Query, 137 | { 138 | type Item<'a> = Option>; 139 | type Fetch<'a> = Option>; 140 | 141 | const MUTABLE: bool = T::MUTABLE; 142 | 143 | #[inline] 144 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 145 | self.0.component_access(comp) 146 | } 147 | 148 | #[inline] 149 | fn visit_archetype(&self, _: &Archetype) -> bool { 150 | true 151 | } 152 | 153 | #[inline] 154 | unsafe fn access_archetype(&self, archetype: &Archetype, f: impl FnMut(TypeId, Access)) { 155 | if self.0.visit_archetype(archetype) { 156 | unsafe { 157 | self.0.access_archetype(archetype, f); 158 | } 159 | } 160 | } 161 | 162 | #[inline] 163 | unsafe fn fetch<'a>( 164 | &self, 165 | arch_idx: u32, 166 | archetype: &'a Archetype, 167 | epoch: EpochId, 168 | ) -> Option> { 169 | if self.0.visit_archetype(archetype) && unsafe { self.0.visit_archetype_late(archetype) } { 170 | Some(unsafe { self.0.fetch(arch_idx, archetype, epoch) }) 171 | } else { 172 | None 173 | } 174 | } 175 | 176 | fn reserved_entity_item<'a>(&self, id: EntityId, idx: u32) -> Option>> { 177 | Some(self.0.reserved_entity_item(id, idx)) 178 | } 179 | } 180 | 181 | unsafe impl ImmutableQuery for OptionQuery where T: ImmutableQuery {} 182 | unsafe impl SendQuery for OptionQuery where T: SendQuery {} 183 | -------------------------------------------------------------------------------- /src/query/read.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, epoch::EpochId, system::QueryArg, type_id, 5 | Access, 6 | }; 7 | 8 | use super::{ 9 | AsQuery, BatchFetch, DefaultQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, 10 | WriteAlias, 11 | }; 12 | 13 | /// [`Fetch`] type for the `&T` query. 14 | 15 | pub struct FetchRead<'a, T> { 16 | ptr: NonNull, 17 | marker: PhantomData<&'a [T]>, 18 | } 19 | 20 | unsafe impl Send for FetchRead<'_, T> where T: Sync {} 21 | 22 | unsafe impl<'a, T> Fetch<'a> for FetchRead<'a, T> 23 | where 24 | T: 'a, 25 | { 26 | type Item = &'a T; 27 | 28 | #[inline] 29 | fn dangling() -> Self { 30 | FetchRead { 31 | ptr: NonNull::dangling(), 32 | marker: PhantomData, 33 | } 34 | } 35 | 36 | #[inline] 37 | unsafe fn get_item(&mut self, idx: u32) -> &'a T { 38 | unsafe { &*self.ptr.as_ptr().add(idx as usize) } 39 | } 40 | } 41 | 42 | unsafe impl<'a, T> BatchFetch<'a> for FetchRead<'a, T> 43 | where 44 | T: 'a, 45 | { 46 | type Batch = &'a [T]; 47 | 48 | #[inline] 49 | unsafe fn get_batch(&mut self, start: u32, end: u32) -> &'a [T] { 50 | debug_assert!(end >= start); 51 | 52 | let count = end - start; 53 | unsafe { 54 | core::slice::from_raw_parts(self.ptr.as_ptr().add(start as usize), count as usize) 55 | } 56 | } 57 | } 58 | 59 | marker_type! { 60 | /// Query for reading component. 61 | pub struct Read; 62 | } 63 | 64 | impl AsQuery for &T 65 | where 66 | T: 'static, 67 | { 68 | type Query = Read; 69 | } 70 | 71 | impl DefaultQuery for &T 72 | where 73 | T: 'static, 74 | { 75 | #[inline] 76 | fn default_query() -> Read { 77 | Read 78 | } 79 | } 80 | 81 | impl AsQuery for Read 82 | where 83 | T: 'static, 84 | { 85 | type Query = Self; 86 | } 87 | 88 | impl IntoQuery for Read 89 | where 90 | T: 'static, 91 | { 92 | #[inline] 93 | fn into_query(self) -> Self { 94 | self 95 | } 96 | } 97 | 98 | impl DefaultQuery for Read 99 | where 100 | T: 'static, 101 | { 102 | #[inline] 103 | fn default_query() -> Read { 104 | Read 105 | } 106 | } 107 | 108 | impl QueryArg for Read 109 | where 110 | T: Sync + 'static, 111 | { 112 | #[inline] 113 | fn new() -> Read { 114 | Read 115 | } 116 | } 117 | 118 | unsafe impl Query for Read 119 | where 120 | T: 'static, 121 | { 122 | type Item<'a> = &'a T; 123 | type Fetch<'a> = FetchRead<'a, T>; 124 | 125 | const MUTABLE: bool = false; 126 | 127 | #[inline] 128 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 129 | if comp.id() == type_id::() { 130 | Ok(Some(Access::Read)) 131 | } else { 132 | Ok(None) 133 | } 134 | } 135 | 136 | #[inline] 137 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 138 | archetype.has_component(type_id::()) 139 | } 140 | 141 | #[inline] 142 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 143 | f(type_id::(), Access::Read) 144 | } 145 | 146 | #[inline] 147 | unsafe fn fetch<'a>( 148 | &self, 149 | _arch_idx: u32, 150 | archetype: &'a Archetype, 151 | _epoch: EpochId, 152 | ) -> FetchRead<'a, T> { 153 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 154 | debug_assert_eq!(component.id(), type_id::()); 155 | 156 | let data = unsafe { component.data() }; 157 | 158 | FetchRead { 159 | ptr: data.ptr.cast(), 160 | marker: PhantomData, 161 | } 162 | } 163 | } 164 | 165 | unsafe impl ImmutableQuery for Read where T: 'static {} 166 | unsafe impl SendQuery for Read where T: Sync + 'static {} 167 | -------------------------------------------------------------------------------- /src/query/with_epoch/mod.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, epoch::EpochId, system::QueryArg, type_id, 5 | }; 6 | 7 | use super::{ 8 | fetch::Fetch, Access, AsQuery, DefaultQuery, ImmutableQuery, IntoQuery, Query, SendQuery, 9 | WriteAlias, 10 | }; 11 | 12 | mod read; 13 | 14 | /// Fetch for [`EpochOf`] epochs. 15 | pub struct FetchEpoch<'a> { 16 | entity_epochs: NonNull, 17 | marker: PhantomData<&'a [EpochId]>, 18 | } 19 | 20 | unsafe impl<'a> Fetch<'a> for FetchEpoch<'a> { 21 | type Item = EpochId; 22 | 23 | #[inline] 24 | fn dangling() -> Self { 25 | FetchEpoch { 26 | entity_epochs: NonNull::dangling(), 27 | marker: PhantomData, 28 | } 29 | } 30 | 31 | #[inline] 32 | unsafe fn get_item(&mut self, idx: u32) -> EpochId { 33 | unsafe { *self.entity_epochs.as_ptr().add(idx as usize) } 34 | } 35 | } 36 | 37 | marker_type! { 38 | /// Query for fetching epochs of a component. 39 | pub struct EpochOf; 40 | } 41 | 42 | impl AsQuery for EpochOf 43 | where 44 | T: 'static, 45 | { 46 | type Query = Self; 47 | } 48 | 49 | impl IntoQuery for EpochOf 50 | where 51 | T: 'static, 52 | { 53 | #[inline] 54 | fn into_query(self) -> Self { 55 | self 56 | } 57 | } 58 | 59 | impl DefaultQuery for EpochOf 60 | where 61 | T: 'static, 62 | { 63 | #[inline] 64 | fn default_query() -> Self { 65 | EpochOf 66 | } 67 | } 68 | 69 | impl QueryArg for EpochOf 70 | where 71 | T: 'static, 72 | { 73 | #[inline] 74 | fn new() -> Self { 75 | EpochOf 76 | } 77 | } 78 | 79 | unsafe impl Query for EpochOf 80 | where 81 | T: 'static, 82 | { 83 | type Item<'a> = EpochId; 84 | type Fetch<'a> = FetchEpoch<'a>; 85 | 86 | const MUTABLE: bool = false; 87 | 88 | #[inline] 89 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 90 | if comp.id() == type_id::() { 91 | Ok(Some(Access::Read)) 92 | } else { 93 | Ok(None) 94 | } 95 | } 96 | 97 | #[inline] 98 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 99 | archetype.has_component(type_id::()) 100 | } 101 | 102 | #[inline] 103 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 104 | f(type_id::(), Access::Read); 105 | } 106 | 107 | #[inline] 108 | unsafe fn fetch<'a>( 109 | &self, 110 | _arch_idx: u32, 111 | archetype: &'a Archetype, 112 | _epoch: EpochId, 113 | ) -> FetchEpoch<'a> { 114 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 115 | let data = unsafe { component.data() }; 116 | 117 | FetchEpoch { 118 | entity_epochs: unsafe { 119 | NonNull::new_unchecked(data.entity_epochs.as_ptr() as *mut EpochId) 120 | }, 121 | marker: PhantomData, 122 | } 123 | } 124 | } 125 | 126 | unsafe impl ImmutableQuery for EpochOf where T: 'static {} 127 | unsafe impl SendQuery for EpochOf where T: 'static {} 128 | 129 | #[derive(Clone, Copy, Debug, Default)] 130 | #[repr(transparent)] 131 | pub struct WithEpoch(pub T); 132 | -------------------------------------------------------------------------------- /src/query/with_epoch/read.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | epoch::EpochId, 7 | query::{ 8 | Access, AsQuery, DefaultQuery, Fetch, ImmutableQuery, IntoQuery, Query, Read, SendQuery, 9 | WriteAlias, 10 | }, 11 | system::QueryArg, 12 | type_id, 13 | }; 14 | 15 | use super::WithEpoch; 16 | 17 | /// [`Fetch`] type for the [`WithEpoch<&T>`] query. 18 | pub struct WithEpochFetchRead<'a, T> { 19 | ptr: NonNull, 20 | epochs: NonNull, 21 | marker: PhantomData<&'a [T]>, 22 | } 23 | 24 | unsafe impl<'a, T> Fetch<'a> for WithEpochFetchRead<'a, T> 25 | where 26 | T: 'a, 27 | { 28 | type Item = (&'a T, EpochId); 29 | 30 | #[inline] 31 | fn dangling() -> Self { 32 | WithEpochFetchRead { 33 | ptr: NonNull::dangling(), 34 | epochs: NonNull::dangling(), 35 | marker: PhantomData, 36 | } 37 | } 38 | 39 | #[inline] 40 | unsafe fn get_item(&mut self, idx: u32) -> (&'a T, EpochId) { 41 | let epoch = unsafe { *self.epochs.as_ptr().add(idx as usize) }; 42 | let item = unsafe { &*self.ptr.as_ptr().add(idx as usize) }; 43 | (item, epoch) 44 | } 45 | } 46 | 47 | impl AsQuery for WithEpoch<&T> 48 | where 49 | T: 'static, 50 | { 51 | type Query = WithEpoch>; 52 | } 53 | 54 | impl DefaultQuery for WithEpoch<&T> 55 | where 56 | T: 'static, 57 | { 58 | fn default_query() -> Self::Query { 59 | WithEpoch(Read) 60 | } 61 | } 62 | 63 | impl AsQuery for WithEpoch> 64 | where 65 | T: 'static, 66 | { 67 | type Query = Self; 68 | } 69 | 70 | impl IntoQuery for WithEpoch> 71 | where 72 | T: 'static, 73 | { 74 | fn into_query(self) -> Self { 75 | self 76 | } 77 | } 78 | 79 | impl DefaultQuery for WithEpoch> 80 | where 81 | T: 'static, 82 | { 83 | fn default_query() -> Self { 84 | WithEpoch(Read) 85 | } 86 | } 87 | 88 | impl QueryArg for WithEpoch> 89 | where 90 | T: Sync + 'static, 91 | { 92 | fn new() -> Self { 93 | WithEpoch(Read) 94 | } 95 | } 96 | 97 | unsafe impl Query for WithEpoch> 98 | where 99 | T: 'static, 100 | { 101 | type Item<'a> = (&'a T, EpochId); 102 | type Fetch<'a> = WithEpochFetchRead<'a, T>; 103 | 104 | const MUTABLE: bool = false; 105 | const FILTERS_ENTITIES: bool = false; 106 | 107 | #[inline] 108 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 109 | self.0.component_access(comp) 110 | } 111 | 112 | #[inline] 113 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 114 | self.0.visit_archetype(archetype) 115 | } 116 | 117 | #[inline] 118 | unsafe fn access_archetype(&self, archetype: &Archetype, f: impl FnMut(TypeId, Access)) { 119 | unsafe { 120 | self.0.access_archetype(archetype, f); 121 | } 122 | } 123 | 124 | #[inline] 125 | unsafe fn fetch<'a>( 126 | &self, 127 | _arch_idx: u32, 128 | archetype: &'a Archetype, 129 | _epoch: EpochId, 130 | ) -> WithEpochFetchRead<'a, T> { 131 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 132 | debug_assert_eq!(component.id(), type_id::()); 133 | 134 | let data = unsafe { component.data() }; 135 | 136 | WithEpochFetchRead { 137 | ptr: data.ptr.cast(), 138 | epochs: unsafe { NonNull::new_unchecked(data.entity_epochs.as_ptr() as *mut EpochId) }, 139 | marker: PhantomData, 140 | } 141 | } 142 | } 143 | 144 | unsafe impl ImmutableQuery for WithEpoch> where T: 'static {} 145 | unsafe impl SendQuery for WithEpoch> where T: Sync + 'static {} 146 | -------------------------------------------------------------------------------- /src/query/write.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, epoch::EpochId, system::QueryArg, type_id, 5 | }; 6 | 7 | use super::{ 8 | Access, AsQuery, BatchFetch, DefaultQuery, Fetch, IntoQuery, Query, SendQuery, WriteAlias, 9 | }; 10 | 11 | /// [`Fetch`] type for the `&mut T` query. 12 | pub struct FetchWrite<'a, T> { 13 | ptr: NonNull, 14 | entity_epochs: NonNull, 15 | chunk_epochs: NonNull, 16 | epoch: EpochId, 17 | marker: PhantomData<&'a mut [T]>, 18 | } 19 | 20 | unsafe impl Send for FetchWrite<'_, T> where T: Send {} 21 | 22 | unsafe impl<'a, T> Fetch<'a> for FetchWrite<'a, T> 23 | where 24 | T: 'a, 25 | { 26 | type Item = &'a mut T; 27 | 28 | #[inline] 29 | fn dangling() -> Self { 30 | FetchWrite { 31 | ptr: NonNull::dangling(), 32 | entity_epochs: NonNull::dangling(), 33 | chunk_epochs: NonNull::dangling(), 34 | epoch: EpochId::start(), 35 | marker: PhantomData, 36 | } 37 | } 38 | 39 | #[inline] 40 | unsafe fn touch_chunk(&mut self, chunk_idx: u32) { 41 | let chunk_epoch = unsafe { &mut *self.chunk_epochs.as_ptr().add(chunk_idx as usize) }; 42 | chunk_epoch.bump(self.epoch); 43 | } 44 | 45 | #[inline] 46 | unsafe fn get_item(&mut self, idx: u32) -> &'a mut T { 47 | let entity_epoch = unsafe { &mut *self.entity_epochs.as_ptr().add(idx as usize) }; 48 | entity_epoch.bump(self.epoch); 49 | 50 | unsafe { &mut *self.ptr.as_ptr().add(idx as usize) } 51 | } 52 | } 53 | 54 | unsafe impl<'a, T> BatchFetch<'a> for FetchWrite<'a, T> 55 | where 56 | T: 'a, 57 | { 58 | type Batch = &'a mut [T]; 59 | 60 | #[inline] 61 | unsafe fn get_batch(&mut self, start: u32, end: u32) -> &'a mut [T] { 62 | debug_assert!(end >= start); 63 | 64 | let count = end - start; 65 | unsafe { 66 | core::slice::from_raw_parts_mut(self.ptr.as_ptr().add(start as usize), count as usize) 67 | } 68 | } 69 | } 70 | 71 | marker_type! { 72 | /// Query for writing a component. 73 | pub struct Write; 74 | } 75 | 76 | impl AsQuery for &mut T 77 | where 78 | T: 'static, 79 | { 80 | type Query = Write; 81 | } 82 | 83 | impl DefaultQuery for &mut T 84 | where 85 | T: 'static, 86 | { 87 | #[inline] 88 | fn default_query() -> Write { 89 | Write 90 | } 91 | } 92 | 93 | impl AsQuery for Write 94 | where 95 | T: 'static, 96 | { 97 | type Query = Self; 98 | } 99 | 100 | impl IntoQuery for Write 101 | where 102 | T: 'static, 103 | { 104 | #[inline] 105 | fn into_query(self) -> Write { 106 | Write 107 | } 108 | } 109 | 110 | impl DefaultQuery for Write 111 | where 112 | T: 'static, 113 | { 114 | #[inline] 115 | fn default_query() -> Write { 116 | Write 117 | } 118 | } 119 | 120 | impl QueryArg for Write 121 | where 122 | T: Send + 'static, 123 | { 124 | #[inline] 125 | fn new() -> Write { 126 | Write 127 | } 128 | } 129 | 130 | unsafe impl Query for Write 131 | where 132 | T: 'static, 133 | { 134 | type Item<'a> = &'a mut T; 135 | type Fetch<'a> = FetchWrite<'a, T>; 136 | 137 | const MUTABLE: bool = true; 138 | 139 | #[inline] 140 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 141 | if comp.id() == type_id::() { 142 | Ok(Some(Access::Write)) 143 | } else { 144 | Ok(None) 145 | } 146 | } 147 | 148 | #[inline] 149 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 150 | archetype.has_component(type_id::()) 151 | } 152 | 153 | #[inline] 154 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 155 | f(type_id::(), Access::Write) 156 | } 157 | 158 | #[inline] 159 | unsafe fn fetch<'a>( 160 | &self, 161 | _arch_idx: u32, 162 | archetype: &'a Archetype, 163 | epoch: EpochId, 164 | ) -> FetchWrite<'a, T> { 165 | let component = unsafe { archetype.component(type_id::()).unwrap_unchecked() }; 166 | debug_assert_eq!(component.id(), type_id::()); 167 | 168 | let data = unsafe { component.data_mut() }; 169 | data.epoch.bump(epoch); 170 | 171 | FetchWrite { 172 | ptr: data.ptr.cast(), 173 | entity_epochs: unsafe { NonNull::new_unchecked(data.entity_epochs.as_mut_ptr()) }, 174 | chunk_epochs: unsafe { NonNull::new_unchecked(data.chunk_epochs.as_mut_ptr()) }, 175 | epoch, 176 | marker: PhantomData, 177 | } 178 | } 179 | } 180 | 181 | unsafe impl SendQuery for Write where T: Send + 'static {} 182 | -------------------------------------------------------------------------------- /src/relation/child_of.rs: -------------------------------------------------------------------------------- 1 | use super::{ExclusiveRelation, Relation}; 2 | 3 | /// Child -> Parent relation. 4 | /// Children can have only one parent. So this relation is exclusive. 5 | /// Children should be despawned when parent is despawned. So this relation is owned. 6 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 7 | pub struct ChildOf; 8 | 9 | impl Relation for ChildOf { 10 | const EXCLUSIVE: bool = true; 11 | const OWNED: bool = true; 12 | const SYMMETRIC: bool = false; 13 | } 14 | 15 | impl ExclusiveRelation for ChildOf {} 16 | -------------------------------------------------------------------------------- /src/relation/mod.rs: -------------------------------------------------------------------------------- 1 | //! [`Relation`] is like [`Component`] but they are bound to a pair of entities. 2 | //! 3 | //! [`Component`]: crate::component::Component 4 | 5 | use crate::{action::LocalActionEncoder, entity::EntityId}; 6 | 7 | pub use edict_proc::Relation; 8 | 9 | pub use self::{ 10 | child_of::ChildOf, 11 | query::{ 12 | FetchFilterRelatedBy, FetchRelatedRead, FetchRelatedWith, FetchRelatedWrite, 13 | FetchRelatesExclusiveRead, FetchRelatesExclusiveWith, FetchRelatesExclusiveWrite, 14 | FetchRelatesRead, FetchRelatesToRead, FetchRelatesToWrite, FetchRelatesWith, 15 | FetchRelatesWrite, FilterFetchRelatesTo, FilterRelated, FilterRelatedBy, FilterRelates, 16 | FilterRelatesTo, Related, Relates, RelatesExclusive, RelatesTo, RelationIter, 17 | RelationReadIter, RelationWriteIter, 18 | }, 19 | }; 20 | 21 | pub(crate) use self::components::{OriginComponent, TargetComponent}; 22 | 23 | mod child_of; 24 | mod components; 25 | mod query; 26 | 27 | /// Trait that must be implemented for types to be 28 | /// used as relation components. 29 | /// 30 | /// Relation components are special in a way that they are bound to 31 | /// a pair of entities, not just one. 32 | /// One entity is called "origin" and the other is called "target". 33 | /// 34 | /// Relation components are used to connect two entities together. 35 | /// For example [`ChildOf`] relation component is used to connect 36 | /// child entity ("origin") to parent entity ("target"). 37 | /// 38 | /// Relation components are dropped when either of the "origin" or "target" 39 | /// is dropped. Appropriate hook method is called when this happens. 40 | /// `on_drop` is called when relation is dropped from "origin" entity. 41 | /// `on_target_drop` is called when "target" entity is dropped. 42 | pub trait Relation: Copy + 'static { 43 | /// If `true` then relation can be added only once to an entity. 44 | /// If another exclusive relation is added to the same entity, 45 | /// then the old one is removed. 46 | /// `on_replace` is called when this happens. 47 | /// 48 | /// Non-exclusive relations is replaced only if re-added 49 | /// with same target. 50 | /// 51 | /// When using `#[derive(Relation)]` add `#[edict(exclusive)]` attribute to set this to true. 52 | const EXCLUSIVE: bool = false; 53 | 54 | /// If `true` then when relation is added to an entity 55 | /// it is also added to the target in reverse direction. 56 | /// 57 | /// When using `#[derive(Relation)]` add `#[edict(symmetric)]` attribute to set this to true. 58 | const SYMMETRIC: bool = false; 59 | 60 | /// If `true` then origin entity in relation is "owned" by the target. 61 | /// This means that when last target is dropped, entity is despawned. 62 | /// 63 | /// When using `#[derive(Relation)]` add `#[edict(owned)]` attribute to set this to true. 64 | const OWNED: bool = false; 65 | 66 | /// Returns name of the relation type. 67 | /// 68 | /// Can be overridden to provide custom name. 69 | #[inline] 70 | #[must_use] 71 | fn name() -> &'static str { 72 | core::any::type_name::() 73 | } 74 | 75 | /// Method that is called when relation is re-inserted. 76 | /// For non-exclusive relations this happens when relation is re-inserted with the same 77 | /// origin-target entity pair. 78 | /// For exclusive relations this happens when relation is re-inserted with 79 | /// origin that has relation of this type with any target. 80 | /// 81 | /// If returns `true`, `on_drop` will be called. 82 | /// 83 | /// Does nothing by default and returns `true`, causing `on_drop` to be called. 84 | #[inline] 85 | fn on_replace( 86 | old_value: &mut Self, 87 | new_value: &Self, 88 | origin: EntityId, 89 | old_target: EntityId, 90 | new_target: EntityId, 91 | encoder: LocalActionEncoder, 92 | ) -> bool { 93 | let _ = old_value; 94 | let _ = new_value; 95 | let _ = origin; 96 | let _ = old_target; 97 | let _ = new_target; 98 | let _ = encoder; 99 | 100 | true 101 | } 102 | 103 | /// Hook that is called when relation is dropped 104 | /// via [`World::drop_relation`](crate::world::World::drop_relation) or similar method 105 | /// or is replaced and [`Relation::on_replace`] returns `true`. 106 | #[inline] 107 | fn on_drop(self, origin: EntityId, target: EntityId, encoder: LocalActionEncoder) { 108 | let _ = origin; 109 | let _ = target; 110 | let _ = encoder; 111 | } 112 | 113 | /// Hook that is called when origin is despawned. 114 | #[inline] 115 | fn on_origin_drop(origin: EntityId, targets: &[(EntityId, Self)], encoder: LocalActionEncoder) { 116 | let _ = origin; 117 | let _ = targets; 118 | let _ = encoder; 119 | } 120 | 121 | /// Hook that is called when target is despawned. 122 | #[inline] 123 | fn on_target_drop(origins: &[(EntityId, Self)], target: EntityId, encoder: LocalActionEncoder) { 124 | let _ = origins; 125 | let _ = target; 126 | let _ = encoder; 127 | } 128 | } 129 | 130 | /// Sub-trait for exclusive relations. 131 | /// It should be implemented for relations that specify `EXCLUSIVE = true`, 132 | /// to enable use of `RelatesExclusive` query. 133 | /// Implementing it for relation with `EXCLUSIVE = false` will cause 134 | /// compilation error or runtime panic. 135 | /// 136 | /// `Relation` derive macro implements this trait automatically. 137 | pub trait ExclusiveRelation: Relation { 138 | #[doc(hidden)] 139 | const ASSERT_EXCLUSIVE: () = assert!(Self::EXCLUSIVE); 140 | } 141 | -------------------------------------------------------------------------------- /src/relation/query/filter_related.rs: -------------------------------------------------------------------------------- 1 | use core::any::TypeId; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | epoch::EpochId, 7 | query::{ 8 | AsQuery, DefaultQuery, ImmutableQuery, IntoQuery, Query, SendQuery, UnitFetch, WriteAlias, 9 | }, 10 | relation::{OriginComponent, Relation, TargetComponent}, 11 | system::QueryArg, 12 | type_id, Access, 13 | }; 14 | 15 | marker_type! { 16 | /// Filters targets of relation. 17 | pub struct FilterRelated; 18 | } 19 | 20 | impl AsQuery for FilterRelated 21 | where 22 | R: Relation, 23 | { 24 | type Query = Self; 25 | } 26 | 27 | impl IntoQuery for FilterRelated 28 | where 29 | R: Relation, 30 | { 31 | #[inline] 32 | fn into_query(self) -> Self { 33 | self 34 | } 35 | } 36 | 37 | impl DefaultQuery for FilterRelated 38 | where 39 | R: Relation, 40 | { 41 | #[inline] 42 | fn default_query() -> Self { 43 | FilterRelated 44 | } 45 | } 46 | 47 | impl QueryArg for FilterRelated 48 | where 49 | R: Relation, 50 | { 51 | #[inline] 52 | fn new() -> Self { 53 | FilterRelated 54 | } 55 | } 56 | 57 | unsafe impl Query for FilterRelated 58 | where 59 | R: Relation, 60 | { 61 | type Item<'a> = (); 62 | type Fetch<'a> = UnitFetch; 63 | 64 | const MUTABLE: bool = false; 65 | 66 | #[inline] 67 | fn component_access(&self, _comp: &ComponentInfo) -> Result, WriteAlias> { 68 | Ok(None) 69 | } 70 | 71 | #[inline] 72 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 73 | if R::SYMMETRIC { 74 | archetype.has_component(type_id::>()) 75 | } else { 76 | archetype.has_component(type_id::>()) 77 | } 78 | } 79 | 80 | #[inline] 81 | unsafe fn access_archetype(&self, _archetype: &Archetype, _f: impl FnMut(TypeId, Access)) {} 82 | 83 | #[inline] 84 | unsafe fn fetch(&self, _: u32, _: &Archetype, _: EpochId) -> UnitFetch { 85 | UnitFetch::new() 86 | } 87 | } 88 | 89 | unsafe impl ImmutableQuery for FilterRelated where R: Relation {} 90 | unsafe impl SendQuery for FilterRelated where R: Relation {} 91 | -------------------------------------------------------------------------------- /src/relation/query/filter_related_by.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | entity::EntityId, 7 | epoch::EpochId, 8 | query::{AsQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, WriteAlias}, 9 | relation::{OriginComponent, Relation, TargetComponent}, 10 | type_id, Access, 11 | }; 12 | 13 | /// Fetch for the `FilterRelatedBy` query. 14 | pub struct FetchFilterRelatedBy<'a, R: Relation> { 15 | origin: EntityId, 16 | ptr: NonNull, 17 | marker: PhantomData<&'a R>, 18 | } 19 | 20 | unsafe impl<'a, R> Fetch<'a> for FetchFilterRelatedBy<'a, R> 21 | where 22 | R: Relation, 23 | { 24 | type Item = (); 25 | 26 | #[inline] 27 | fn dangling() -> Self { 28 | FetchFilterRelatedBy { 29 | origin: EntityId::dangling(), 30 | ptr: NonNull::dangling(), 31 | marker: PhantomData, 32 | } 33 | } 34 | 35 | #[inline] 36 | unsafe fn visit_item(&mut self, idx: u32) -> bool { 37 | if R::SYMMETRIC { 38 | let origin_component = unsafe { 39 | &*self 40 | .ptr 41 | .cast::>() 42 | .as_ptr() 43 | .add(idx as usize) 44 | }; 45 | origin_component 46 | .targets() 47 | .iter() 48 | .any(|r| r.0 == self.origin) 49 | } else { 50 | let target_component = unsafe { 51 | &*self 52 | .ptr 53 | .cast::>() 54 | .as_ptr() 55 | .add(idx as usize) 56 | }; 57 | target_component 58 | .origins() 59 | .iter() 60 | .any(|r| r.0 == self.origin) 61 | } 62 | } 63 | 64 | #[inline] 65 | unsafe fn get_item(&mut self, _: u32) -> () {} 66 | } 67 | 68 | /// Filters targets of relation with specified origin. 69 | pub struct FilterRelatedBy { 70 | origin: EntityId, 71 | phantom: PhantomData R>, 72 | } 73 | 74 | impl_debug!(FilterRelatedBy { origin }); 75 | impl_copy!(FilterRelatedBy); 76 | 77 | impl FilterRelatedBy { 78 | /// Returns relation filter bound to one specific origin. 79 | pub const fn new(origin: EntityId) -> Self { 80 | FilterRelatedBy { 81 | origin, 82 | phantom: PhantomData, 83 | } 84 | } 85 | } 86 | 87 | impl AsQuery for FilterRelatedBy 88 | where 89 | R: Relation, 90 | { 91 | type Query = Self; 92 | } 93 | 94 | impl IntoQuery for FilterRelatedBy 95 | where 96 | R: Relation, 97 | { 98 | #[inline] 99 | fn into_query(self) -> Self::Query { 100 | self 101 | } 102 | } 103 | 104 | unsafe impl Query for FilterRelatedBy 105 | where 106 | R: Relation, 107 | { 108 | type Item<'a> = (); 109 | type Fetch<'a> = FetchFilterRelatedBy<'a, R>; 110 | 111 | const MUTABLE: bool = false; 112 | const FILTERS_ENTITIES: bool = true; 113 | 114 | #[inline] 115 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 116 | if R::SYMMETRIC { 117 | if comp.id() == type_id::>() { 118 | Ok(Some(Access::Read)) 119 | } else { 120 | Ok(None) 121 | } 122 | } else { 123 | if comp.id() == type_id::>() { 124 | Ok(Some(Access::Read)) 125 | } else { 126 | Ok(None) 127 | } 128 | } 129 | } 130 | 131 | #[inline] 132 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 133 | if R::SYMMETRIC { 134 | archetype.has_component(type_id::>()) 135 | } else { 136 | archetype.has_component(type_id::>()) 137 | } 138 | } 139 | 140 | #[inline] 141 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 142 | if R::SYMMETRIC { 143 | f(type_id::>(), Access::Read) 144 | } else { 145 | f(type_id::>(), Access::Read) 146 | } 147 | } 148 | 149 | #[inline] 150 | unsafe fn fetch<'a>( 151 | &self, 152 | _arch_idx: u32, 153 | archetype: &'a Archetype, 154 | _epoch: EpochId, 155 | ) -> FetchFilterRelatedBy<'a, R> { 156 | if R::SYMMETRIC { 157 | let component = unsafe { 158 | archetype 159 | .component(type_id::>()) 160 | .unwrap_unchecked() 161 | }; 162 | debug_assert_eq!(component.id(), type_id::>()); 163 | 164 | let data = unsafe { component.data() }; 165 | 166 | FetchFilterRelatedBy { 167 | origin: self.origin, 168 | ptr: data.ptr.cast(), 169 | marker: PhantomData, 170 | } 171 | } else { 172 | let component = unsafe { 173 | archetype 174 | .component(type_id::>()) 175 | .unwrap_unchecked() 176 | }; 177 | debug_assert_eq!(component.id(), type_id::>()); 178 | 179 | let data = unsafe { component.data() }; 180 | 181 | FetchFilterRelatedBy { 182 | origin: self.origin, 183 | ptr: data.ptr.cast(), 184 | marker: PhantomData, 185 | } 186 | } 187 | } 188 | } 189 | 190 | unsafe impl ImmutableQuery for FilterRelatedBy where R: Relation {} 191 | unsafe impl SendQuery for FilterRelatedBy where R: Relation {} 192 | -------------------------------------------------------------------------------- /src/relation/query/filter_relates.rs: -------------------------------------------------------------------------------- 1 | use core::any::TypeId; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | epoch::EpochId, 7 | query::{ 8 | AsQuery, DefaultQuery, ImmutableQuery, IntoQuery, Query, SendQuery, UnitFetch, WriteAlias, 9 | }, 10 | relation::{OriginComponent, Relation}, 11 | system::QueryArg, 12 | type_id, Access, 13 | }; 14 | 15 | marker_type! { 16 | /// Filters origins of relation. 17 | pub struct FilterRelates; 18 | } 19 | 20 | impl AsQuery for FilterRelates 21 | where 22 | R: Relation, 23 | { 24 | type Query = Self; 25 | } 26 | 27 | impl IntoQuery for FilterRelates 28 | where 29 | R: Relation, 30 | { 31 | #[inline] 32 | fn into_query(self) -> Self { 33 | self 34 | } 35 | } 36 | 37 | impl DefaultQuery for FilterRelates 38 | where 39 | R: Relation, 40 | { 41 | #[inline] 42 | fn default_query() -> Self { 43 | FilterRelates 44 | } 45 | } 46 | 47 | impl QueryArg for FilterRelates 48 | where 49 | R: Relation, 50 | { 51 | #[inline] 52 | fn new() -> Self { 53 | FilterRelates 54 | } 55 | } 56 | 57 | unsafe impl Query for FilterRelates 58 | where 59 | R: Relation, 60 | { 61 | type Item<'a> = (); 62 | type Fetch<'a> = UnitFetch; 63 | 64 | const MUTABLE: bool = false; 65 | 66 | #[inline] 67 | fn component_access(&self, _comp: &ComponentInfo) -> Result, WriteAlias> { 68 | Ok(None) 69 | } 70 | 71 | #[inline] 72 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 73 | archetype.has_component(type_id::>()) 74 | } 75 | 76 | #[inline] 77 | unsafe fn access_archetype(&self, _archetype: &Archetype, _f: impl FnMut(TypeId, Access)) {} 78 | 79 | #[inline] 80 | unsafe fn fetch(&self, _: u32, _: &Archetype, _: EpochId) -> UnitFetch { 81 | UnitFetch::new() 82 | } 83 | } 84 | 85 | unsafe impl ImmutableQuery for FilterRelates where R: Relation {} 86 | unsafe impl SendQuery for FilterRelates where R: Relation {} 87 | -------------------------------------------------------------------------------- /src/relation/query/filter_relates_to.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, marker::PhantomData, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | component::ComponentInfo, 6 | entity::EntityId, 7 | epoch::EpochId, 8 | query::{AsQuery, Fetch, ImmutableQuery, IntoQuery, Query, SendQuery, WriteAlias}, 9 | relation::{OriginComponent, Relation}, 10 | type_id, Access, 11 | }; 12 | 13 | /// Fetch for the `RelatesTo` query. 14 | pub struct FilterFetchRelatesTo<'a, R: Relation> { 15 | target: EntityId, 16 | ptr: NonNull>, 17 | marker: PhantomData<&'a OriginComponent>, 18 | } 19 | 20 | unsafe impl<'a, R> Fetch<'a> for FilterFetchRelatesTo<'a, R> 21 | where 22 | R: Relation, 23 | { 24 | type Item = (); 25 | 26 | #[inline] 27 | fn dangling() -> Self { 28 | FilterFetchRelatesTo { 29 | target: EntityId::dangling(), 30 | ptr: NonNull::dangling(), 31 | marker: PhantomData, 32 | } 33 | } 34 | 35 | #[inline] 36 | unsafe fn visit_item(&mut self, idx: u32) -> bool { 37 | let origin_component = unsafe { &*self.ptr.as_ptr().add(idx as usize) }; 38 | origin_component 39 | .targets() 40 | .iter() 41 | .any(|origin| origin.0 == self.target) 42 | } 43 | 44 | #[inline] 45 | unsafe fn get_item(&mut self, _: u32) -> () {} 46 | } 47 | 48 | /// Filters origins of relation with specified target. 49 | pub struct FilterRelatesTo { 50 | target: EntityId, 51 | phantom: PhantomData R>, 52 | } 53 | 54 | impl_debug!(FilterRelatesTo { target }); 55 | impl_copy!(FilterRelatesTo); 56 | 57 | impl FilterRelatesTo { 58 | /// Returns relation filter bound to one specific target. 59 | pub const fn new(target: EntityId) -> Self { 60 | FilterRelatesTo { 61 | target, 62 | phantom: PhantomData, 63 | } 64 | } 65 | } 66 | 67 | impl AsQuery for FilterRelatesTo 68 | where 69 | R: Relation, 70 | { 71 | type Query = Self; 72 | } 73 | 74 | impl IntoQuery for FilterRelatesTo 75 | where 76 | R: Relation, 77 | { 78 | fn into_query(self) -> Self::Query { 79 | self 80 | } 81 | } 82 | 83 | unsafe impl Query for FilterRelatesTo 84 | where 85 | R: Relation, 86 | { 87 | type Item<'a> = (); 88 | type Fetch<'a> = FilterFetchRelatesTo<'a, R>; 89 | 90 | const MUTABLE: bool = false; 91 | const FILTERS_ENTITIES: bool = true; 92 | 93 | #[inline] 94 | fn component_access(&self, comp: &ComponentInfo) -> Result, WriteAlias> { 95 | if comp.id() == type_id::>() { 96 | Ok(Some(Access::Read)) 97 | } else { 98 | Ok(None) 99 | } 100 | } 101 | 102 | #[inline] 103 | fn visit_archetype(&self, archetype: &Archetype) -> bool { 104 | archetype.has_component(type_id::>()) 105 | } 106 | 107 | #[inline] 108 | unsafe fn access_archetype(&self, _archetype: &Archetype, mut f: impl FnMut(TypeId, Access)) { 109 | f(type_id::>(), Access::Read) 110 | } 111 | 112 | #[inline] 113 | unsafe fn fetch<'a>( 114 | &self, 115 | _arch_idx: u32, 116 | archetype: &'a Archetype, 117 | _epoch: EpochId, 118 | ) -> FilterFetchRelatesTo<'a, R> { 119 | let component = unsafe { 120 | archetype 121 | .component(type_id::>()) 122 | .unwrap_unchecked() 123 | }; 124 | debug_assert_eq!(component.id(), type_id::>()); 125 | 126 | let data = unsafe { component.data() }; 127 | 128 | FilterFetchRelatesTo { 129 | target: self.target, 130 | ptr: data.ptr.cast(), 131 | marker: PhantomData, 132 | } 133 | } 134 | } 135 | 136 | unsafe impl ImmutableQuery for FilterRelatesTo where R: Relation {} 137 | unsafe impl SendQuery for FilterRelatesTo where R: Relation {} 138 | -------------------------------------------------------------------------------- /src/relation/query/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::entity::{EntityBound, EntityId}; 2 | 3 | /// Iterator over relations of a given type on one entity. 4 | #[derive(Clone)] 5 | pub struct RelationIter<'a, R> { 6 | iter: core::slice::Iter<'a, (EntityId, R)>, 7 | } 8 | 9 | impl<'a, R> RelationIter<'a, R> { 10 | /// Creates a new iterator over relations of a given type on one entity. 11 | #[inline] 12 | pub fn new(relations: &'a [(EntityId, R)]) -> Self { 13 | RelationIter { 14 | iter: relations.iter(), 15 | } 16 | } 17 | } 18 | 19 | impl<'a, R> Iterator for RelationIter<'a, R> { 20 | type Item = EntityBound<'a>; 21 | 22 | #[inline] 23 | fn size_hint(&self) -> (usize, Option) { 24 | self.iter.size_hint() 25 | } 26 | 27 | #[inline] 28 | fn next(&mut self) -> Option> { 29 | let origin = self.iter.next()?; 30 | Some(EntityBound::new(origin.0)) 31 | } 32 | 33 | #[inline] 34 | fn nth(&mut self, n: usize) -> Option> { 35 | let origin = self.iter.nth(n)?; 36 | Some(EntityBound::new(origin.0)) 37 | } 38 | 39 | #[inline] 40 | fn fold(self, init: B, mut f: F) -> B 41 | where 42 | Self: Sized, 43 | F: FnMut(B, Self::Item) -> B, 44 | { 45 | self.iter 46 | .fold(init, |acc, origin| f(acc, EntityBound::new(origin.0))) 47 | } 48 | } 49 | 50 | impl<'a, R> DoubleEndedIterator for RelationIter<'a, R> { 51 | #[inline] 52 | fn next_back(&mut self) -> Option> { 53 | let origin = self.iter.next_back()?; 54 | Some(EntityBound::new(origin.0)) 55 | } 56 | 57 | #[inline] 58 | fn nth_back(&mut self, n: usize) -> Option> { 59 | let origin = self.iter.nth_back(n)?; 60 | Some(EntityBound::new(origin.0)) 61 | } 62 | 63 | #[inline] 64 | fn rfold(self, init: B, mut f: F) -> B 65 | where 66 | Self: Sized, 67 | F: FnMut(B, Self::Item) -> B, 68 | { 69 | self.iter 70 | .rfold(init, |acc, origin| f(acc, EntityBound::new(origin.0))) 71 | } 72 | } 73 | 74 | impl<'a, R> ExactSizeIterator for RelationIter<'a, R> { 75 | #[inline] 76 | fn len(&self) -> usize { 77 | self.iter.len() 78 | } 79 | } 80 | 81 | /// Iterator over relations of a given type on one entity. 82 | #[derive(Clone)] 83 | pub struct RelationReadIter<'a, R> { 84 | iter: core::slice::Iter<'a, (EntityId, R)>, 85 | } 86 | 87 | impl<'a, R> RelationReadIter<'a, R> { 88 | /// Creates a new iterator over relations of a given type on one entity. 89 | #[inline] 90 | pub fn new(relations: &'a [(EntityId, R)]) -> Self { 91 | RelationReadIter { 92 | iter: relations.iter(), 93 | } 94 | } 95 | } 96 | 97 | impl<'a, R> Iterator for RelationReadIter<'a, R> { 98 | type Item = (&'a R, EntityBound<'a>); 99 | 100 | #[inline] 101 | fn size_hint(&self) -> (usize, Option) { 102 | self.iter.size_hint() 103 | } 104 | 105 | #[inline] 106 | fn next(&mut self) -> Option<(&'a R, EntityBound<'a>)> { 107 | let origin = self.iter.next()?; 108 | Some((&origin.1, EntityBound::new(origin.0))) 109 | } 110 | 111 | #[inline] 112 | fn nth(&mut self, n: usize) -> Option<(&'a R, EntityBound<'a>)> { 113 | let origin = self.iter.nth(n)?; 114 | Some((&origin.1, EntityBound::new(origin.0))) 115 | } 116 | 117 | #[inline] 118 | fn fold(self, init: B, mut f: F) -> B 119 | where 120 | Self: Sized, 121 | F: FnMut(B, Self::Item) -> B, 122 | { 123 | self.iter.fold(init, |acc, origin| { 124 | f(acc, (&origin.1, EntityBound::new(origin.0))) 125 | }) 126 | } 127 | } 128 | 129 | impl<'a, R> DoubleEndedIterator for RelationReadIter<'a, R> { 130 | #[inline] 131 | fn next_back(&mut self) -> Option<(&'a R, EntityBound<'a>)> { 132 | let origin = self.iter.next_back()?; 133 | Some((&origin.1, EntityBound::new(origin.0))) 134 | } 135 | 136 | #[inline] 137 | fn nth_back(&mut self, n: usize) -> Option<(&'a R, EntityBound<'a>)> { 138 | let origin = self.iter.nth_back(n)?; 139 | Some((&origin.1, EntityBound::new(origin.0))) 140 | } 141 | 142 | #[inline] 143 | fn rfold(self, init: B, mut f: F) -> B 144 | where 145 | Self: Sized, 146 | F: FnMut(B, Self::Item) -> B, 147 | { 148 | self.iter.rfold(init, |acc, origin| { 149 | f(acc, (&origin.1, EntityBound::new(origin.0))) 150 | }) 151 | } 152 | } 153 | 154 | impl<'a, R> ExactSizeIterator for RelationReadIter<'a, R> { 155 | #[inline] 156 | fn len(&self) -> usize { 157 | self.iter.len() 158 | } 159 | } 160 | 161 | /// Iterator over relations of a given type on one entity. 162 | pub struct RelationWriteIter<'a, R> { 163 | iter: core::slice::IterMut<'a, (EntityId, R)>, 164 | } 165 | 166 | impl<'a, R> RelationWriteIter<'a, R> { 167 | /// Creates a new iterator over relations of a given type on one entity. 168 | #[inline] 169 | pub fn new(relations: &'a mut [(EntityId, R)]) -> Self { 170 | RelationWriteIter { 171 | iter: relations.iter_mut(), 172 | } 173 | } 174 | } 175 | 176 | impl<'a, R> Iterator for RelationWriteIter<'a, R> { 177 | type Item = (&'a mut R, EntityBound<'a>); 178 | 179 | #[inline] 180 | fn size_hint(&self) -> (usize, Option) { 181 | self.iter.size_hint() 182 | } 183 | 184 | #[inline] 185 | fn next(&mut self) -> Option<(&'a mut R, EntityBound<'a>)> { 186 | let origin = self.iter.next()?; 187 | Some((&mut origin.1, EntityBound::new(origin.0))) 188 | } 189 | 190 | #[inline] 191 | fn nth(&mut self, n: usize) -> Option<(&'a mut R, EntityBound<'a>)> { 192 | let origin = self.iter.nth(n)?; 193 | Some((&mut origin.1, EntityBound::new(origin.0))) 194 | } 195 | 196 | #[inline] 197 | fn fold(self, init: B, mut f: F) -> B 198 | where 199 | Self: Sized, 200 | F: FnMut(B, Self::Item) -> B, 201 | { 202 | self.iter.fold(init, |acc, origin| { 203 | f(acc, (&mut origin.1, EntityBound::new(origin.0))) 204 | }) 205 | } 206 | } 207 | 208 | impl<'a, R> DoubleEndedIterator for RelationWriteIter<'a, R> { 209 | #[inline] 210 | fn next_back(&mut self) -> Option<(&'a mut R, EntityBound<'a>)> { 211 | let origin = self.iter.next_back()?; 212 | Some((&mut origin.1, EntityBound::new(origin.0))) 213 | } 214 | 215 | #[inline] 216 | fn nth_back(&mut self, n: usize) -> Option<(&'a mut R, EntityBound<'a>)> { 217 | let origin = self.iter.nth_back(n)?; 218 | Some((&mut origin.1, EntityBound::new(origin.0))) 219 | } 220 | 221 | #[inline] 222 | fn rfold(self, init: B, mut f: F) -> B 223 | where 224 | Self: Sized, 225 | F: FnMut(B, Self::Item) -> B, 226 | { 227 | self.iter.rfold(init, |acc, origin| { 228 | f(acc, (&mut origin.1, EntityBound::new(origin.0))) 229 | }) 230 | } 231 | } 232 | 233 | impl<'a, R> ExactSizeIterator for RelationWriteIter<'a, R> { 234 | #[inline] 235 | fn len(&self) -> usize { 236 | self.iter.len() 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /src/relation/query/mod.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! This module contains queries for relations. 3 | //! 4 | //! Naming rules. 5 | //! 6 | //! `*Relates*` - matches origins of relations. 7 | //! `*Related*` - matches targets of relations. 8 | //! `*RelatesTo` - contains specific relation target. 9 | //! `*RelatedBy` - contains specific relation origin. 10 | //! 11 | //! # Queries 12 | //! 13 | //! [`Relates`] - matches relation origins and fetches slice of relation instances and targets. 14 | //! [`RelatesExclusive`] - matches relation origins and fetches exclusive relation instance and target. 15 | //! [`RelatesTo`] - matches relation origin with specified target and fetches relation instance. 16 | //! [`Related`] - matches relation targets and fetches slice of origins. 17 | //! 18 | //! # Filters 19 | //! 20 | //! [`FilterRelates`] - filters relation targets. 21 | //! [`FilterRelatesTo`] - filters relations targets with specified origin. 22 | //! [`FilterNotRelates`] - filters entities that are not relation targets. 23 | //! [`FilterNotRelatesTo`] - filters entities that are not relation targets with specified origin. 24 | //! 25 | //! [`FilterRelated`] - filters relation targets. 26 | //! [`FilterRelatedBy`] - filters relations targets with specified origin. 27 | //! [`FilterNotRelated`] - filters entities that are not relation targets. 28 | //! [`FilterNotRelatedBy`] - filters entities that are not relation targets with specified origin. 29 | 30 | mod filter_related; 31 | mod filter_related_by; 32 | mod filter_relates; 33 | mod filter_relates_to; 34 | mod iter; 35 | mod related; 36 | mod relates; 37 | mod relates_exclusive; 38 | mod relates_to; 39 | 40 | pub use self::{ 41 | filter_related::FilterRelated, 42 | filter_related_by::{FetchFilterRelatedBy, FilterRelatedBy}, 43 | filter_relates::FilterRelates, 44 | filter_relates_to::{FilterFetchRelatesTo, FilterRelatesTo}, 45 | iter::{RelationIter, RelationReadIter, RelationWriteIter}, 46 | related::{FetchRelatedRead, FetchRelatedWith, FetchRelatedWrite, Related}, 47 | relates::{FetchRelatesRead, FetchRelatesWith, FetchRelatesWrite, Relates}, 48 | relates_exclusive::{ 49 | FetchRelatesExclusiveRead, FetchRelatesExclusiveWith, FetchRelatesExclusiveWrite, 50 | RelatesExclusive, 51 | }, 52 | relates_to::{FetchRelatesToRead, FetchRelatesToWrite, RelatesTo}, 53 | }; 54 | -------------------------------------------------------------------------------- /src/scheduler/mod.rs: -------------------------------------------------------------------------------- 1 | //! Built-in scheduling for systems. 2 | //! 3 | //! [`Scheduler`] has following properties: 4 | //! * Separates execution of conflicting systems temporally. 5 | //! * Executes non-conflicting systems in parallel on available worker threads. 6 | //! * Conflicting systems are executed in order of their registration. 7 | //! That means that system defines implicit dependency on all systems with with there's conflict. 8 | //! * In case of write-to-read conflict, reading system that is added later is guaranteed 9 | //! to observe modifications made by writing system that was added before. 10 | //! * In case of read-to-write conflict, reading system that is added before is guaranteed 11 | //! to NOT observe modifications made by writing system that was added later. 12 | //! * In case of write-to-write conflict, writing system that is added before is guaranteed 13 | //! to NOT observe modifications made by writing system that was added later. 14 | //! And writing system that is added later is guaranteed 15 | //! to observe modifications made by writing system that was added before. 16 | //! 17 | 18 | use alloc::{boxed::Box, vec::Vec}; 19 | use core::{ 20 | cell::UnsafeCell, 21 | ops::{Deref, DerefMut}, 22 | ptr::NonNull, 23 | }; 24 | 25 | use amity::ring_buffer::RingBuffer; 26 | 27 | use crate::{ 28 | action::ActionBuffer, 29 | system::{IntoSystem, System}, 30 | world::World, 31 | }; 32 | 33 | #[cfg(feature = "threaded-scheduler")] 34 | mod threaded; 35 | 36 | #[cfg(feature = "threaded-scheduler")] 37 | pub use self::threaded::ScopedExecutor; 38 | 39 | /// Scheduler that starts systems in order of their registration. 40 | /// And executes as many non-conflicting systems in parallel as possible. 41 | /// 42 | /// # Example 43 | /// 44 | /// ``` 45 | /// # #[cfg(feature = "threaded-scheduler")] 46 | /// # { 47 | /// # use edict::{world::World, resources::Res, scheduler::Scheduler, system::IntoSystem}; 48 | /// 49 | /// let mut world = World::new(); 50 | /// let mut scheduler = Scheduler::new(); 51 | /// 52 | /// scheduler.add_system(|| {}); 53 | /// scheduler.add_system(|world: &mut World| { 54 | /// println!("{}", world.with_resource::(|| 0)); 55 | /// }); 56 | /// scheduler.add_system(|world: &World| { 57 | /// assert_eq!(0, *world.expect_resource::()); 58 | /// }); 59 | /// scheduler.add_system(|res: Res| { 60 | /// assert_eq!(0, *res); 61 | /// }); 62 | /// 63 | /// scheduler.run_threaded(&mut world); 64 | /// # } 65 | /// ``` 66 | pub struct Scheduler { 67 | systems: Vec, 68 | action_buffers: RingBuffer, 69 | 70 | #[cfg(feature = "threaded-scheduler")] 71 | schedule_cache_id: Option, 72 | } 73 | 74 | struct SyncUnsafeCell { 75 | inner: UnsafeCell, 76 | } 77 | 78 | impl SyncUnsafeCell { 79 | pub fn new(value: T) -> Self { 80 | SyncUnsafeCell { 81 | inner: UnsafeCell::new(value), 82 | } 83 | } 84 | } 85 | 86 | unsafe impl Sync for SyncUnsafeCell {} 87 | 88 | impl Deref for SyncUnsafeCell { 89 | type Target = UnsafeCell; 90 | 91 | fn deref(&self) -> &UnsafeCell { 92 | &self.inner 93 | } 94 | } 95 | 96 | impl DerefMut for SyncUnsafeCell { 97 | fn deref_mut(&mut self) -> &mut UnsafeCell { 98 | &mut self.inner 99 | } 100 | } 101 | 102 | struct ScheduledSystem { 103 | system: SyncUnsafeCell>, 104 | 105 | #[cfg(feature = "threaded-scheduler")] 106 | threaded: self::threaded::ThreadedSystem, 107 | } 108 | 109 | impl Scheduler { 110 | /// Creates new empty scheduler. 111 | pub fn new() -> Self { 112 | Scheduler { 113 | systems: Vec::new(), 114 | action_buffers: RingBuffer::new(), 115 | 116 | #[cfg(feature = "threaded-scheduler")] 117 | schedule_cache_id: None, 118 | } 119 | } 120 | 121 | /// Adds system to the scheduler. 122 | pub fn add_system(&mut self, system: impl IntoSystem) { 123 | self.add_boxed_system(Box::new(system.into_system())); 124 | } 125 | 126 | /// Adds system to the scheduler. 127 | pub fn add_boxed_system(&mut self, system: Box) { 128 | self.systems.push(ScheduledSystem { 129 | #[cfg(feature = "threaded-scheduler")] 130 | threaded: self::threaded::ThreadedSystem::new(system.is_local()), 131 | 132 | system: SyncUnsafeCell::new(system), 133 | }); 134 | 135 | #[cfg(feature = "threaded-scheduler")] 136 | { 137 | self.schedule_cache_id = None; 138 | } 139 | } 140 | 141 | /// Runs all systems in the scheduler sequentially. 142 | pub fn run_sequential(&mut self, world: &mut World) { 143 | use crate::action::ActionBufferSliceExt; 144 | 145 | for system in &mut self.systems { 146 | let system = system.system.inner.get_mut(); 147 | unsafe { 148 | system.run_unchecked(NonNull::from(&mut *world), &mut self.action_buffers); 149 | } 150 | } 151 | 152 | let (front, back) = self.action_buffers.as_mut_slices(); 153 | front.execute_all(world); 154 | back.execute_all(world); 155 | } 156 | } 157 | 158 | #[cfg(test)] 159 | mod test { 160 | 161 | use super::*; 162 | 163 | use crate::system::State; 164 | 165 | #[test] 166 | fn test() { 167 | let mut world = World::new(); 168 | 169 | let mut scheduler = Scheduler::new(); 170 | scheduler.add_system(|mut q: State| { 171 | *q = 11; 172 | }); 173 | 174 | scheduler.run_sequential(&mut world); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/system/func/action.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, ptr::NonNull}; 2 | 3 | use crate::{ 4 | action::{ActionBuffer, ActionEncoder}, 5 | archetype::Archetype, 6 | component::ComponentInfo, 7 | system::{Access, ActionBufferQueue}, 8 | world::World, 9 | }; 10 | 11 | use super::{FnArg, FnArgState}; 12 | 13 | impl FnArg for ActionEncoder<'_> { 14 | type State = ActionEncoderState; 15 | } 16 | 17 | /// [`FnArgState`] for `ActionEncoder` argument. 18 | #[derive(Default)] 19 | pub struct ActionEncoderState { 20 | buffer: Option, 21 | } 22 | 23 | unsafe impl FnArgState for ActionEncoderState { 24 | type Arg<'a> = ActionEncoder<'a>; 25 | 26 | fn new() -> Self { 27 | ActionEncoderState { buffer: None } 28 | } 29 | 30 | #[inline] 31 | fn is_local(&self) -> bool { 32 | false 33 | } 34 | 35 | #[inline] 36 | fn world_access(&self) -> Option { 37 | None 38 | } 39 | 40 | #[inline] 41 | fn visit_archetype(&self, _archetype: &Archetype) -> bool { 42 | false 43 | } 44 | 45 | #[inline] 46 | fn borrows_components_at_runtime(&self) -> bool { 47 | false 48 | } 49 | 50 | #[inline] 51 | fn component_access(&self, _comp: &ComponentInfo) -> Option { 52 | None 53 | } 54 | 55 | #[inline] 56 | fn resource_type_access(&self, _ty: TypeId) -> Option { 57 | None 58 | } 59 | 60 | #[inline] 61 | unsafe fn get_unchecked<'a>( 62 | &'a mut self, 63 | world: NonNull, 64 | queue: &mut dyn ActionBufferQueue, 65 | ) -> ActionEncoder<'a> { 66 | debug_assert!(self.buffer.is_none()); 67 | let buffer = self.buffer.get_or_insert_with(|| queue.get()); 68 | ActionEncoder::new(buffer, unsafe { world.as_ref() }.entities()) 69 | } 70 | 71 | #[inline] 72 | unsafe fn flush_unchecked( 73 | &mut self, 74 | _world: NonNull, 75 | queue: &mut dyn ActionBufferQueue, 76 | ) { 77 | if let Some(buffer) = self.buffer.take() { 78 | queue.flush(buffer); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/system/func/state.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | any::TypeId, 3 | ops::{Deref, DerefMut}, 4 | ptr::NonNull, 5 | }; 6 | 7 | use crate::{ 8 | archetype::Archetype, 9 | component::ComponentInfo, 10 | system::{Access, ActionBufferQueue}, 11 | world::World, 12 | }; 13 | 14 | use super::{FnArg, FnArgState}; 15 | 16 | /// State for function systems. 17 | /// Value inside [`State`] is preserved between system runs. 18 | /// 19 | /// The difference between [`ResMut`] and [`State`] 20 | /// is that [`State`] is not stored in the [`World`] 21 | /// and is not shared between [`System`]s. 22 | /// Instead each [`System`] gets its own cached instance of [`State`] 23 | /// which is automatically initialized with [`Default`] 24 | /// on first access. 25 | /// 26 | /// [`ResMut`]: crate::resources::ResMut 27 | /// [`System`]: crate::system::System 28 | #[repr(transparent)] 29 | pub struct State<'a, T> { 30 | value: &'a mut T, 31 | } 32 | 33 | impl<'a, T> From<&'a mut T> for State<'a, T> { 34 | fn from(value: &'a mut T) -> Self { 35 | State { value } 36 | } 37 | } 38 | 39 | /// [`FnArgState`] for [`State`] argument. 40 | #[derive(Default)] 41 | #[repr(transparent)] 42 | pub struct StateState { 43 | value: T, 44 | } 45 | 46 | impl Deref for State<'_, T> { 47 | type Target = T; 48 | 49 | #[inline] 50 | fn deref(&self) -> &T { 51 | &self.value 52 | } 53 | } 54 | 55 | impl DerefMut for State<'_, T> { 56 | #[inline] 57 | fn deref_mut(&mut self) -> &mut T { 58 | &mut self.value 59 | } 60 | } 61 | 62 | impl FnArg for State<'_, T> 63 | where 64 | T: Default + Send + 'static, 65 | { 66 | type State = StateState; 67 | } 68 | 69 | unsafe impl FnArgState for StateState 70 | where 71 | T: Default + Send + 'static, 72 | { 73 | type Arg<'a> = State<'a, T>; 74 | 75 | #[inline] 76 | fn new() -> Self { 77 | Self::default() 78 | } 79 | 80 | #[inline] 81 | fn is_local(&self) -> bool { 82 | false 83 | } 84 | 85 | #[inline] 86 | fn world_access(&self) -> Option { 87 | None 88 | } 89 | 90 | #[inline] 91 | fn visit_archetype(&self, _archetype: &Archetype) -> bool { 92 | false 93 | } 94 | 95 | #[inline] 96 | fn borrows_components_at_runtime(&self) -> bool { 97 | false 98 | } 99 | 100 | #[inline] 101 | fn component_access(&self, _comp: &ComponentInfo) -> Option { 102 | None 103 | } 104 | 105 | #[inline] 106 | fn resource_type_access(&self, _ty: TypeId) -> Option { 107 | None 108 | } 109 | 110 | #[inline] 111 | unsafe fn get_unchecked<'a>( 112 | &'a mut self, 113 | _world: NonNull, 114 | _queue: &mut dyn ActionBufferQueue, 115 | ) -> State<'a, T> { 116 | State { 117 | value: &mut self.value, 118 | } 119 | } 120 | } 121 | 122 | #[test] 123 | fn test_state_system() { 124 | use alloc::vec::Vec; 125 | 126 | use super::{IntoSystem, System}; 127 | 128 | fn bar(mut state: State) { 129 | *state = *state + 1; 130 | #[cfg(feature = "std")] 131 | println!("{}", *state); 132 | } 133 | 134 | let mut system = bar.into_system(); 135 | 136 | let world = World::new(); 137 | let mut encoders = Vec::new(); 138 | 139 | unsafe { 140 | system.run_unchecked(NonNull::from(&world), &mut encoders); 141 | system.run_unchecked(NonNull::from(&world), &mut encoders); 142 | system.run_unchecked(NonNull::from(&world), &mut encoders); 143 | system.run_unchecked(NonNull::from(&world), &mut encoders); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/system/func/world.rs: -------------------------------------------------------------------------------- 1 | use core::{any::TypeId, ptr::NonNull}; 2 | 3 | use crate::{ 4 | archetype::Archetype, component::ComponentInfo, system::ActionBufferQueue, world::World, Access, 5 | }; 6 | 7 | use super::{FnArg, FnArgState}; 8 | 9 | #[derive(Default)] 10 | pub struct WorldReadState; 11 | 12 | impl FnArg for &World { 13 | type State = WorldReadState; 14 | } 15 | 16 | unsafe impl FnArgState for WorldReadState { 17 | type Arg<'a> = &'a World; 18 | 19 | #[inline] 20 | fn new() -> Self { 21 | Self::default() 22 | } 23 | 24 | #[inline] 25 | fn is_local(&self) -> bool { 26 | false 27 | } 28 | 29 | #[inline] 30 | fn world_access(&self) -> Option { 31 | Some(Access::Read) 32 | } 33 | 34 | #[inline] 35 | fn visit_archetype(&self, _archetype: &Archetype) -> bool { 36 | true 37 | } 38 | 39 | #[inline] 40 | fn borrows_components_at_runtime(&self) -> bool { 41 | true 42 | } 43 | 44 | #[inline] 45 | fn component_access(&self, _comp: &ComponentInfo) -> Option { 46 | Some(Access::Write) 47 | } 48 | 49 | #[inline] 50 | fn resource_type_access(&self, _ty: TypeId) -> Option { 51 | Some(Access::Write) 52 | } 53 | 54 | #[inline] 55 | unsafe fn get_unchecked<'a>( 56 | &'a mut self, 57 | world: NonNull, 58 | _queue: &mut dyn ActionBufferQueue, 59 | ) -> &'a World { 60 | // Safety: Declares read. 61 | unsafe { world.as_ref() } 62 | } 63 | } 64 | 65 | #[derive(Default)] 66 | pub struct WorldWriteState; 67 | 68 | impl FnArg for &mut World { 69 | type State = WorldWriteState; 70 | } 71 | 72 | unsafe impl FnArgState for WorldWriteState { 73 | type Arg<'a> = &'a mut World; 74 | 75 | #[inline] 76 | fn new() -> Self { 77 | Self::default() 78 | } 79 | 80 | #[inline] 81 | fn is_local(&self) -> bool { 82 | true 83 | } 84 | 85 | #[inline] 86 | fn world_access(&self) -> Option { 87 | Some(Access::Write) 88 | } 89 | 90 | #[inline] 91 | fn visit_archetype(&self, _archetype: &Archetype) -> bool { 92 | true 93 | } 94 | 95 | #[inline] 96 | fn borrows_components_at_runtime(&self) -> bool { 97 | false 98 | } 99 | 100 | #[inline] 101 | fn component_access(&self, _comp: &ComponentInfo) -> Option { 102 | Some(Access::Write) 103 | } 104 | 105 | #[inline] 106 | fn resource_type_access(&self, _ty: TypeId) -> Option { 107 | Some(Access::Write) 108 | } 109 | 110 | #[inline] 111 | unsafe fn get_unchecked<'a>( 112 | &'a mut self, 113 | mut world: NonNull, 114 | _queue: &mut dyn ActionBufferQueue, 115 | ) -> &'a mut World { 116 | // Safety: Declares write. 117 | unsafe { world.as_mut() } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/view/one.rs: -------------------------------------------------------------------------------- 1 | use core::mem::MaybeUninit; 2 | 3 | use crate::{ 4 | archetype::Archetype, 5 | entity::{AliveEntity, EntityId, Location}, 6 | epoch::EpochCounter, 7 | query::{AsQuery, ImmutableQuery, Query, QueryItem}, 8 | world::World, 9 | }; 10 | 11 | use super::{expect_match, get_at, BorrowState, RuntimeBorrowState}; 12 | 13 | /// A view over [`World`] that may be used to access specific components 14 | /// of one entity. 15 | #[must_use] 16 | pub struct ViewOneValue<'a, Q: Query, F: Query> { 17 | query: Q, 18 | filter: F, 19 | 20 | // Init if loc.arch != u32::MAX 21 | archetype: MaybeUninit<&'a Archetype>, 22 | id: EntityId, 23 | loc: Location, 24 | borrow: RuntimeBorrowState, 25 | epochs: &'a EpochCounter, 26 | } 27 | 28 | impl<'a, Q: Query, F: Query> Drop for ViewOneValue<'a, Q, F> { 29 | #[inline] 30 | fn drop(&mut self) { 31 | self.unlock() 32 | } 33 | } 34 | 35 | impl<'a, Q: Query, F: Query> ViewOneValue<'a, Q, F> { 36 | /// Unlocks runtime borrows. 37 | /// Allows usage of conflicting views. 38 | /// 39 | /// Borrows are automatically unlocked when the view is dropped. 40 | /// This method is necessary only if caller wants to keep the view 41 | /// to reuse it later. 42 | #[inline] 43 | pub fn unlock(&self) { 44 | if self.loc.arch == u32::MAX { 45 | return; 46 | } 47 | 48 | // Safety: archetype is init if loc.arch != u32::MAX 49 | let archetype = unsafe { self.archetype.assume_init() }; 50 | 51 | self.borrow 52 | .release(self.query, self.filter, core::slice::from_ref(archetype)) 53 | } 54 | } 55 | 56 | /// View for single entity. 57 | pub type ViewOne<'a, Q, F = ()> = ViewOneValue<'a, ::Query, ::Query>; 58 | 59 | impl<'a, Q, F> ViewOneValue<'a, Q, F> 60 | where 61 | Q: Query, 62 | F: Query, 63 | { 64 | /// Creates a new view over a single entity. 65 | #[inline] 66 | pub fn new(world: &'a World, entity: impl AliveEntity, query: Q, filter: F) -> Self { 67 | let loc = entity.locate(world.entities()); 68 | let mut archetype = MaybeUninit::uninit(); 69 | 70 | if loc.arch != u32::MAX { 71 | archetype.write(&world.archetypes()[loc.arch as usize]); 72 | } 73 | 74 | ViewOneValue { 75 | query: query, 76 | filter: filter, 77 | archetype, 78 | id: entity.id(), 79 | loc, 80 | borrow: RuntimeBorrowState::new(), 81 | epochs: world.epoch_counter(), 82 | } 83 | } 84 | } 85 | 86 | impl<'a, Q, F> ViewOneValue<'a, Q, F> 87 | where 88 | Q: Query, 89 | F: Query, 90 | { 91 | /// Fetches data that matches the view's query and filter 92 | /// from a bound entity. 93 | /// 94 | /// Returns none if entity does not match the view's query and filter. 95 | #[inline] 96 | pub fn get_mut(&mut self) -> Option> { 97 | if self.loc.arch == u32::MAX { 98 | return Query::reserved_entity_item(&self.query, self.id, self.loc.idx); 99 | } 100 | 101 | // Safety: archetype is init if loc.arch != u32::MAX 102 | let archetype = unsafe { self.archetype.assume_init() }; 103 | 104 | // Ensure to borrow view's data. 105 | self.borrow 106 | .acquire(self.query, self.filter, core::slice::from_ref(archetype)); 107 | 108 | unsafe { get_at(self.query, self.filter, self.epochs, archetype, self.loc) } 109 | } 110 | 111 | /// Fetches data that matches the view's query and filter 112 | /// from a bound entity. 113 | /// 114 | /// # Panics 115 | /// 116 | /// Panics if entity does not match the view's query and filter. 117 | #[inline] 118 | #[track_caller] 119 | pub fn expect_mut(&mut self) -> QueryItem { 120 | expect_match(self.get_mut()) 121 | } 122 | 123 | /// Fetches data that matches the view's query and filter 124 | /// from a bound entity. 125 | /// 126 | /// Calls provided closure with fetched data if entity matches query and filter. 127 | /// Otherwise, returns `None`. 128 | #[inline] 129 | pub fn map_mut(&mut self, f: Fun) -> Option 130 | where 131 | Fun: FnOnce(QueryItem) -> R, 132 | { 133 | if self.loc.arch == u32::MAX { 134 | let item = Query::reserved_entity_item(&self.query, self.id, self.loc.idx); 135 | return match item { 136 | Some(item) => Some(f(item)), 137 | None => None, 138 | }; 139 | } 140 | 141 | // Safety: archetype is init if loc.arch != u32::MAX 142 | let archetype = unsafe { self.archetype.assume_init() }; 143 | 144 | // Ensure to borrow view's data. 145 | self.borrow.with(self.query, self.filter, archetype, || { 146 | let item = unsafe { get_at(self.query, self.filter, self.epochs, archetype, self.loc) }; 147 | match item { 148 | Some(item) => Some(f(item)), 149 | None => None, 150 | } 151 | }) 152 | } 153 | } 154 | 155 | impl<'a, Q, F> ViewOneValue<'a, Q, F> 156 | where 157 | Q: ImmutableQuery, 158 | F: ImmutableQuery, 159 | { 160 | /// Fetches data that matches the view's query and filter 161 | /// from a bound entity. 162 | /// 163 | /// Returns none if entity does not match the view's query and filter. 164 | #[inline] 165 | pub fn get(&self) -> Option> { 166 | if self.loc.arch == u32::MAX { 167 | return Query::reserved_entity_item(&self.query, self.id, self.loc.idx); 168 | } 169 | 170 | // Safety: archetype is init if loc.arch != u32::MAX 171 | let archetype = unsafe { self.archetype.assume_init() }; 172 | 173 | // Ensure to borrow view's data. 174 | self.borrow 175 | .acquire(self.query, self.filter, core::slice::from_ref(archetype)); 176 | 177 | unsafe { get_at(self.query, self.filter, self.epochs, archetype, self.loc) } 178 | } 179 | 180 | /// Fetches data that matches the view's query and filter 181 | /// from a bound entity. 182 | /// 183 | /// Returns none if entity does not match the view's query and filter. 184 | #[inline] 185 | #[track_caller] 186 | pub fn expect(&self) -> QueryItem { 187 | expect_match(self.get()) 188 | } 189 | 190 | /// Fetches data that matches the view's query and filter 191 | /// from a bound entity. 192 | /// 193 | /// Calls provided closure with fetched data if entity matches query and filter. 194 | /// Otherwise, returns `None`. 195 | #[inline] 196 | pub fn map(&self, f: Fun) -> Option 197 | where 198 | Fun: FnOnce(QueryItem) -> R, 199 | { 200 | if self.loc.arch == u32::MAX { 201 | let item = Query::reserved_entity_item(&self.query, self.id, self.loc.idx); 202 | return match item { 203 | Some(item) => Some(f(item)), 204 | None => None, 205 | }; 206 | } 207 | 208 | // Safety: archetype is init if loc.arch != u32::MAX 209 | let archetype = unsafe { self.archetype.assume_init() }; 210 | 211 | // Ensure to borrow view's data. 212 | self.borrow.with(self.query, self.filter, archetype, || { 213 | let item = unsafe { get_at(self.query, self.filter, self.epochs, archetype, self.loc) }; 214 | match item { 215 | Some(item) => Some(f(item)), 216 | None => None, 217 | } 218 | }) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/view/relation.rs: -------------------------------------------------------------------------------- 1 | use core::cell::Cell; 2 | 3 | use crate::{archetype::Archetype, query::Query, relation::Relation}; 4 | -------------------------------------------------------------------------------- /src/world/builder.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use core::{cell::UnsafeCell, marker::PhantomData}; 3 | 4 | use crate::{ 5 | action::{ActionChannel, LocalActionBuffer}, 6 | bundle::{Bundle, ComponentBundle}, 7 | component::{ 8 | Component, ComponentInfo, ComponentInfoRef, ComponentRegistry, ExternalDropHook, 9 | ExternalSetHook, 10 | }, 11 | entity::{EntitySet, IdRangeAllocator}, 12 | resources::Resources, 13 | }; 14 | 15 | use super::{ 16 | assert_bundle_registered, ensure_bundle_registered, ArchetypeSet, Edges, EpochCounter, World, 17 | }; 18 | 19 | /// Builder for [`World`] value. 20 | /// 21 | /// [`WorldBuilder`] allows to perform setup before building [`World`] value. 22 | /// That otherwise would be impossible. 23 | /// For example [`WorldBuilder::register_component`] allows customization of registered components. 24 | pub struct WorldBuilder { 25 | registry: ComponentRegistry, 26 | range_alloc: Option>, 27 | } 28 | 29 | impl WorldBuilder { 30 | /// Returns new [`WorldBuilder`] value. 31 | #[must_use] 32 | pub const fn new() -> WorldBuilder { 33 | WorldBuilder { 34 | registry: ComponentRegistry::new(), 35 | range_alloc: None, 36 | } 37 | } 38 | 39 | /// Returns newly created [`World`] with configuration copied from this [`WorldBuilder`]. 40 | #[must_use] 41 | pub fn build(self) -> World { 42 | let entities = match self.range_alloc { 43 | None => EntitySet::new(), 44 | Some(range_alloc) => EntitySet::with_allocator(range_alloc), 45 | }; 46 | 47 | World { 48 | epoch: EpochCounter::new(), 49 | entities, 50 | archetypes: ArchetypeSet::new(), 51 | edges: Edges::new(), 52 | resources: Resources::new(), 53 | registry: self.registry, 54 | action_buffer: UnsafeCell::new(LocalActionBuffer::new()), 55 | action_channel: ActionChannel::new(), 56 | 57 | #[cfg(feature = "flow")] 58 | new_flows: UnsafeCell::new(crate::flow::NewFlows::new()), 59 | } 60 | } 61 | 62 | /// Registers new component type using raw [`ComponentInfo`]. 63 | pub fn register_raw(&mut self, info: ComponentInfo) { 64 | self.registry.register_raw(info); 65 | } 66 | 67 | /// Registers new component type and allows modifying it. 68 | pub fn register_component(&mut self) -> ComponentInfoRef<'_, T> 69 | where 70 | T: Component, 71 | { 72 | self.registry.register_component::() 73 | } 74 | 75 | /// Registers new component type and allows modifying it. 76 | pub fn register_external( 77 | &mut self, 78 | ) -> ComponentInfoRef<'_, T, ExternalDropHook, ExternalSetHook> 79 | where 80 | T: 'static, 81 | { 82 | self.registry.register_external::() 83 | } 84 | 85 | /// Sets custom ID range allocator to be used by the [`World`]. 86 | /// Replaces previously set allocator. 87 | /// If no allocator is set, no range allocator is used 88 | /// and [`World`] will allocate sequentially all IDs in range [1..=u64::MAX]. 89 | /// 90 | /// If allocator is set, [`World`] will allocate IDs from ranges provided by the allocator. 91 | /// If allocator is exhausted, allocating new entities will panic. 92 | pub fn with_id_range_allocator(mut self, range_alloc: Box) -> Self { 93 | self.range_alloc = Some(range_alloc); 94 | self 95 | } 96 | } 97 | 98 | impl World { 99 | /// Returns new instance of [`World`]. 100 | /// Created [`World`] instance contains no entities. 101 | #[inline] 102 | pub fn new() -> Self { 103 | Self::builder().build() 104 | } 105 | 106 | /// Returns new instance of [`WorldBuilder`]. 107 | /// This allows pre-register component types and override their behavior. 108 | #[inline] 109 | pub const fn builder() -> WorldBuilder { 110 | WorldBuilder::new() 111 | } 112 | 113 | /// Explicitly registers component type. 114 | /// 115 | /// Unlike [`WorldBuilder::register_component`] method, this method does not return reference to component configuration, 116 | /// once [`World`] is created overriding component behavior is not possible. 117 | /// 118 | /// Component types are implicitly registered on first use by most methods. 119 | /// This method is only needed if you want to use component type using 120 | /// [`World::insert_external`], [`World::insert_external_bundle`] or [`World::spawn_external`]. 121 | pub fn ensure_component_registered(&mut self) 122 | where 123 | T: Component, 124 | { 125 | self.registry.ensure_component_registered::(); 126 | } 127 | 128 | /// Explicitly registers external type. 129 | /// 130 | /// Unlike [`WorldBuilder::register_external`] method, this method does not return reference to component configuration, 131 | /// once [`World`] is created overriding component behavior is not possible. 132 | /// 133 | /// External component types are not implicitly registered on first use. 134 | /// This method is needed if you want to use component type with 135 | /// [`World::insert_external`], [`World::insert_external_bundle`] or [`World::spawn_external`]. 136 | pub fn ensure_external_registered(&mut self) 137 | where 138 | T: 'static, 139 | { 140 | self.registry.ensure_external_registered::(); 141 | } 142 | 143 | /// Explicitly registers bundle of component types. 144 | /// 145 | /// This method is only needed if you want to use bundle of component types using 146 | /// [`World::insert_external_bundle`] or [`World::spawn_external`]. 147 | pub fn ensure_bundle_registered(&mut self) 148 | where 149 | B: ComponentBundle, 150 | { 151 | ensure_bundle_registered(&mut self.registry, &PhantomData::); 152 | } 153 | 154 | /// Asserts that all components from the bundle are registered. 155 | pub fn assert_bundle_registered(&self) 156 | where 157 | B: Bundle, 158 | { 159 | assert_bundle_registered(&self.registry, &PhantomData::); 160 | } 161 | } 162 | --------------------------------------------------------------------------------