Skip to content

Commit

Permalink
Rework flows into lightweight handles and no sticking out references
Browse files Browse the repository at this point in the history
Fix a few internal issues
Update documentations
Cleanup prelude module
Reword documentation
  • Loading branch information
zakarumych committed Jul 10, 2024
1 parent 80485ee commit f41c637
Show file tree
Hide file tree
Showing 41 changed files with 1,353 additions and 829 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = ["proc-lib", "proc"]

[workspace.package]
version = "1.0.0-rc3"
version = "1.0.0-rc4"
edition = "2021"
authors = ["Zakarum <[email protected]>"]
license = "MIT OR Apache-2.0"
Expand Down Expand Up @@ -43,7 +43,7 @@ default = ["std", "scheduler", "flow"]
rayon = ["dep:rayon", "std"]

[dependencies]
edict-proc = { version = "=1.0.0-rc3", path = "proc" }
edict-proc = { version = "=1.0.0-rc4", path = "proc" }
amity = { version = "0.2.1", default-features = false, features = ["alloc"] }
hashbrown = { version = "0.14" }
smallvec = { version = "1.10", features = ["union"], default-features = false }
Expand Down
369 changes: 231 additions & 138 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion examples/flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ fn move_to_system(
}

async fn move_to(e: FlowEntity, target: Pos) {
e.poll_ref(move |mut e, cx| {
e.poll(move |mut e, cx| {
let Some(pos) = e.get::<&Pos>() else {
return Poll::Ready(());
};
Expand Down
4 changes: 2 additions & 2 deletions examples/system.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#![allow(dead_code)]

use edict::{
system::{system, ResNoSync},
system::{system, ResLocal},
view::View,
};

struct A(*mut u8);

#[system]
fn foo(_a: View<&u32>, _b: ResNoSync<A>) {}
fn foo(_a: View<&u32>, _b: ResLocal<A>) {}

fn main() {}
2 changes: 1 addition & 1 deletion proc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ proc-macro = true

[dependencies]
syn = "2.0"
edict-proc-lib = { version = "=1.0.0-rc3", path = "../proc-lib" }
edict-proc-lib = { version = "=1.0.0-rc4", path = "../proc-lib" }
24 changes: 12 additions & 12 deletions src/action/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl ActionSender {
/// Returns an iterator which encodes action to spawn entities
/// using bundles yielded from provided bundles iterator.
#[inline(always)]
pub fn spawn_batch<I>(&self, bundles: I) -> SpawnBatchChannel<I>
pub fn spawn_batch<I>(&self, bundles: I) -> SpawnBatchSender<I>
where
I: IntoIterator,
I::Item: ComponentBundle + Send + 'static,
Expand All @@ -121,7 +121,7 @@ impl ActionSender {
world.ensure_bundle_registered::<I::Item>();
});

SpawnBatchChannel {
SpawnBatchSender {
bundles,
sender: self,
}
Expand All @@ -130,12 +130,12 @@ impl ActionSender {
/// Returns an iterator which encodes action to spawn entities
/// using bundles yielded from provided bundles iterator.
#[inline(always)]
pub fn spawn_external_batch<I>(&self, bundles: I) -> SpawnBatchChannel<I>
pub fn spawn_external_batch<I>(&self, bundles: I) -> SpawnBatchSender<I>
where
I: IntoIterator,
I::Item: Bundle + Send + 'static,
{
SpawnBatchChannel {
SpawnBatchSender {
bundles,
sender: self,
}
Expand Down Expand Up @@ -282,12 +282,12 @@ impl ActionSender {
}

/// Spawning iterator. Produced by [`World::spawn_batch`].
pub struct SpawnBatchChannel<'a, I> {
pub struct SpawnBatchSender<'a, I> {
bundles: I,
sender: &'a ActionSender,
}

impl<B, I> SpawnBatchChannel<'_, I>
impl<B, I> SpawnBatchSender<'_, I>
where
I: Iterator<Item = B>,
B: Bundle + Send + 'static,
Expand All @@ -299,7 +299,7 @@ where
}
}

impl<B, I> Iterator for SpawnBatchChannel<'_, I>
impl<B, I> Iterator for SpawnBatchSender<'_, I>
where
I: Iterator<Item = B>,
B: Bundle + Send + 'static,
Expand All @@ -314,7 +314,7 @@ where

#[inline(always)]
fn nth(&mut self, n: usize) -> Option<()> {
// `SpawnBatchChannel` explicitly does NOT spawn entities that are skipped.
// `SpawnBatchSender` explicitly does NOT spawn entities that are skipped.
let bundle = self.bundles.nth(n)?;
Some(self.sender.spawn_external(bundle))
}
Expand Down Expand Up @@ -358,7 +358,7 @@ where
}
}

impl<B, I> ExactSizeIterator for SpawnBatchChannel<'_, I>
impl<B, I> ExactSizeIterator for SpawnBatchSender<'_, I>
where
I: ExactSizeIterator<Item = B>,
B: Bundle + Send + 'static,
Expand All @@ -369,7 +369,7 @@ where
}
}

impl<B, I> DoubleEndedIterator for SpawnBatchChannel<'_, I>
impl<B, I> DoubleEndedIterator for SpawnBatchSender<'_, I>
where
I: DoubleEndedIterator<Item = B>,
B: Bundle + Send + 'static,
Expand All @@ -382,7 +382,7 @@ where

#[inline(always)]
fn nth_back(&mut self, n: usize) -> Option<()> {
// `SpawnBatchChannel` explicitly does NOT spawn entities that are skipped.
// `SpawnBatchSender` explicitly does NOT spawn entities that are skipped.
let bundle = self.bundles.nth_back(n)?;
Some(self.sender.spawn_external(bundle))
}
Expand All @@ -404,7 +404,7 @@ where
}
}

impl<B, I> FusedIterator for SpawnBatchChannel<'_, I>
impl<B, I> FusedIterator for SpawnBatchSender<'_, I>
where
I: FusedIterator<Item = B>,
B: Bundle + Send + 'static,
Expand Down
2 changes: 1 addition & 1 deletion src/action/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ tiny_fn::tiny_fn! {

pub use self::{
buffer::{ActionBuffer, ActionBufferSliceExt, LocalActionBuffer},
channel::{ActionSender, SpawnBatchChannel},
channel::{ActionSender, SpawnBatchSender},
encoder::{ActionEncoder, LocalActionEncoder, LocalSpawnBatch, SpawnBatch},
};

Expand Down
27 changes: 24 additions & 3 deletions src/dump/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
//! Provides world serialization integration with serialization crates.
//!
//! Supports
//! - [`serde`]
//! - [`nanoserde`]
//! - [`alkahest`]
#![cfg_attr(
feature = "serde",
doc = "- [`serde`] when \"serde\" feature is enabled"
)]
#![cfg_attr(
feature = "nanoserde",
doc = "- [`nanoserde`] when \"nanoserde\" feature is enabled"
)]
#![cfg_attr(
feature = "alkahest",
doc = "- [`alkahest`] when \"alkahest\" feature is enabled"
)]
#![cfg_attr(
not(feature = "serde"),
doc = "- `serde` when \"serde\" feature is enabled"
)]
#![cfg_attr(
not(feature = "nanoserde"),
doc = "- `nanoserde` when \"nanoserde\" feature is enabled"
)]
#![cfg_attr(
not(feature = "alkahest"),
doc = "- `alkahest` when \"alkahest\" feature is enabled"
)]
//!
//! Each can be enabled with a feature named as serialization crate.
Expand Down
12 changes: 4 additions & 8 deletions src/entity/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1126,13 +1126,9 @@ impl<'a> EntityRef<'a> {
self.world.has_component::<T>(loc)
}

/// Spawns a new flow for the entity.
#[cfg(feature = "flow")]
pub fn spawn_flow<F>(&mut self, f: F)
where
F: crate::flow::IntoEntityFlow,
{
let id = self.id;
self.world.spawn_flow_for(id, f);
/// Returns a reference to world.
#[inline(always)]
pub fn world(&mut self) -> &mut World {
self.world
}
}
54 changes: 47 additions & 7 deletions src/flow/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,21 +172,21 @@ where
}

/// Type that can be spawned as a flow for an entity.
/// It can be an async function or a closure inside `flow_fn!` macro.
/// It must accept [`Entity`] as the only argument.
/// It can be an async function or a closure
/// that accepts [`FlowEntity`] as the only argument.
///
/// # Example
///
/// ```
/// # use edict::{world::World, flow::{Entity, flow_fn}};
/// # use edict::{world::World, flow::FlowEntity};
///
/// let mut world = edict::world::World::new();
///
/// let e = world.spawn(()).id();
///
/// world.spawn_flow_for(e, flow_fn!(|mut e: Entity| {
/// world.spawn_flow_for(e, |e: FlowEntity| async move {
/// e.despawn();
/// }));
/// });
/// ```
#[diagnostic::on_unimplemented(
note = "Try `async fn(e: flow::Entity)` or `flow_fn!(|e: flow::Entity| {{ ... }})`"
Expand Down Expand Up @@ -235,13 +235,49 @@ impl FlowEntity {
FlowWorld::new()
}

/// Access entity reference in the world with closure.
/// Returns closure result.
///
/// # Panics
///
/// If entity is not alive the closure will not be called and the method will panic.
/// Use [`FlowEntity::try_map`] to handle entity not alive case.
#[inline(always)]
pub fn map<F, R>(self, f: F) -> R
where
F: FnOnce(EntityRef) -> R,
{
match self.try_map(f) {
Ok(r) => r,
Err(NoSuchEntity) => entity_not_alive(),
}
}

/// Returns a future that will poll the closure with entity reference.
/// The future will resolve to closure result in [`Poll::Ready`].
/// The closure may use task context to register wakers.
///
/// If entity is not alive the future will not poll closure and never resolve.
#[inline(always)]
pub fn poll_ref<F, R>(self, f: F) -> PollEntityRef<F>
pub fn try_map<F, R>(self, f: F) -> Result<R, NoSuchEntity>
where
F: FnOnce(EntityRef) -> R,
{
// Safety: world reference does not escape this scope.
let world = unsafe { get_flow_world() };

let e = world.entity(self.id)?;
Ok(f(e))
}

/// Returns a future that will poll the closure with entity reference.
/// The future will resolve to closure result in [`Poll::Ready`].
/// The closure may use task context to register wakers.
///
/// If entity is not alive the future will not poll closure and never resolve.
/// Use [`FlowEntity::try_poll`] to handle entity not alive case.
#[inline(always)]
pub fn poll<F, R>(self, f: F) -> PollEntityRef<F>
where
F: FnMut(EntityRef, &mut Context) -> Poll<R>,
{
Expand All @@ -258,7 +294,7 @@ impl FlowEntity {
///
/// The closure may use task context to register wakers.
#[inline(always)]
pub fn try_poll_ref<F, R>(self, f: F) -> TryPollEntityRef<F>
pub fn try_poll<F, R>(self, f: F) -> TryPollEntityRef<F>
where
F: FnMut(EntityRef, &mut Context) -> Poll<R>,
{
Expand All @@ -275,6 +311,8 @@ impl FlowEntity {
/// The closure may use task context to register wakers.
///
/// If entity is not alive the future will not poll closure and never resolve.
/// Use [`FlowEntity::try_poll_view`] to handle entity not alive case.
///
/// Future will not poll closure and resolve until query is satisfied.
pub fn poll_view<Q, F, R>(self, f: F) -> PollEntityView<Q::Query, F>
where
Expand Down Expand Up @@ -313,6 +351,8 @@ impl FlowEntity {
/// The closure may use task context to register wakers.
///
/// If entity is not alive the future will not poll closure and never resolve.
/// Use [`FlowEntity::try_poll_view_with`] to handle entity not alive case.
///
/// Future will not poll closure and resolve until query is satisfied.
pub fn poll_view_with<Q, F, R>(self, query: Q, f: F) -> PollEntityView<Q::Query, F>
where
Expand Down
43 changes: 22 additions & 21 deletions src/flow/futures.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::{
convert::Infallible,
future::Future,
pin::Pin,
task::{Context, Poll, Waker},
Expand Down Expand Up @@ -97,27 +98,27 @@ impl Component for WakeOnDrop {
}
}

/// Waits until the entity is despawned.
pub async fn wait_despawned(entity: FlowEntity) {
enum Never {}

let r = entity
.try_poll_ref(|_e, _cx| {
Poll::<Never>::Pending // `try_poll_ref` will resolve to None when entity is despawned.
})
.await;

match r {
Ok(never) => match never {},
Err(_) => return,
impl FlowEntity {
/// Waits until the entity is despawned.
pub async fn wait_despawned(self) {
let r = self
.try_poll(|_e, _cx| {
Poll::<Infallible>::Pending // `try_poll` will resolve to None when entity is despawned.
})
.await;

match r {
Ok(never) => match never {},
Err(_) => return,
}
}
}

/// Waits until the entity gets a component.
/// Never resolves if the entity is despawned.
pub async fn wait_has_component<T>(entity: FlowEntity)
where
T: 'static,
{
entity.poll_view::<&T, _, _>(|_, _cx| Poll::Ready(())).await;
/// Waits until the entity gets a component.
/// Never resolves if the entity is despawned.
pub async fn wait_has_component<T>(self)
where
T: 'static,
{
self.poll_view::<&T, _, _>(|_, _cx| Poll::Ready(())).await;
}
}
Loading

0 comments on commit f41c637

Please sign in to comment.