Skip to content

Commit

Permalink
Merge pull request #4 from zaclegarssure/rework
Browse files Browse the repository at this point in the history
Huge rework, the syntax is clearer and simpler
  • Loading branch information
zaclegarssure authored Sep 27, 2023
2 parents f70c7b5 + 4ef4dc4 commit c5687d4
Show file tree
Hide file tree
Showing 22 changed files with 1,400 additions and 1,718 deletions.
48 changes: 25 additions & 23 deletions examples/basic.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
use std::time::Duration;

use bevy::prelude::*;
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
use corentin::prelude::*;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(Executor::new())
.add_systems(Startup, setup_coroutines)
.add_systems(Update, run_coroutines)
.add_plugins((DefaultPlugins, CorentinPlugin))
.add_systems(Startup, setup_scene)
.run();
}

fn setup_coroutines(mut executor: ResMut<Executor>) {
executor.add(|mut fib| async move {
let mut i = 0;
loop {
let dt = fib.next_tick().await;
println!("Last frame lasted for {}", dt.as_secs_f32());
fib.duration(Duration::from_secs(1)).await;
i += 1;
println!("This coroutine has started since {} seconds", i);
}
});
}
fn setup_scene(
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
mut commands: Commands,
) {
commands.spawn(Camera2dBundle::default());

fn run_coroutines(world: &mut World) {
world.resource_scope(|w, mut exec: Mut<Executor>| {
exec.tick(w);
})
// Circle
commands
.spawn(MaterialMesh2dBundle {
mesh: meshes.add(shape::Circle::new(50.).into()).into(),
material: materials.add(ColorMaterial::from(Color::PURPLE)),
transform: Transform::from_translation(Vec3::new(-150., 0., 0.)),
..default()
})
.add(coroutine(
|fib: Fib, mut transform: Wr<Transform>| async move {
loop {
let dt = fib.next_tick().await;
transform.get_mut().translation.x += 100.0 * dt.as_secs_f32();
}
},
));
}
70 changes: 27 additions & 43 deletions examples/read_write.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,30 @@
use std::time::Duration;

use bevy::prelude::*;
use corentin::{coroutine::PrimitiveVoid, prelude::*};

#[derive(Component)]
struct ExampleComponent(u32);

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.insert_resource(Executor::new())
.add_systems(Startup, setup_access)
.add_systems(Update, run_coroutines)
.run();
//App::new()
// .add_plugins((DefaultPlugins, CorentinPlugin))
// .add_systems(Startup, setup_access)
// .run();
}

fn setup_access(world: &mut World) {
world.resource_scope(|w, mut exec: Mut<Executor>| {
let e = w.spawn(ExampleComponent(0)).id();
exec.add_to_entity(e, move |mut fib, this| async move {
loop {
let mut b = fib
.duration(Duration::from_secs(1))
.then_grab::<&mut ExampleComponent>(this)
.await;
b.0 += 1;
}
});
exec.add(|mut fib| async move {
loop {
let c = fib
.change::<ExampleComponent>(e)
.then_grab::<&ExampleComponent>(e)
.await;
println!("Change detected, value is now {}", c.0);
}
});
})
}

fn run_coroutines(world: &mut World) {
world.resource_scope(|w, mut exec: Mut<Executor>| {
exec.tick(w);
})
}
//fn setup_scene(
// mut meshes: ResMut<Assets<Mesh>>,
// mut materials: ResMut<Assets<ColorMaterial>>,
// mut commands: Commands,
//) {
// commands.spawn(Camera2dBundle::default());
// let e = world.spawn((IsClicked(0))).id();
// coroutine(|fib: Fib, mut ex: R<IsClicked>| async move {
// loop {
// ex.on_change().await;
//
// }
// })
// .apply(e, world);
//
// coroutine(|_: Fib, ex: R<ExampleComponent>| async move {
// loop {
// ex.on_change().await;
// println!("Change detected, value is now {}", ex.get().0);
// }
// })
// .apply(e, world);
//}
39 changes: 39 additions & 0 deletions src/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use bevy::ecs::system::EntityCommand;
use bevy::utils::synccell::SyncCell;
use std::marker::PhantomData;

use bevy::prelude::{Entity, World};

use crate::coroutine::UninitCoroutine;
use crate::executor::Executor;

pub struct AddCoroutine<Marker, C> {
coro: C,
_phantom: PhantomData<Marker>,
}

impl<Marker: Send + 'static, C: Send + 'static> EntityCommand for AddCoroutine<Marker, C>
where
C: UninitCoroutine<Marker>,
{
fn apply(self, id: Entity, world: &mut World) {
world.resource_scope::<Executor, ()>(|w, mut executor| {
if let Some(coroutine) = self.coro.init(id, w) {
executor.add(SyncCell::new(Box::pin(coroutine)));
}
});
}
}

pub fn coroutine<M, C>(coro: C) -> AddCoroutine<M, C> {
AddCoroutine::new(coro)
}

impl<M, C> AddCoroutine<M, C> {
pub fn new(coro: C) -> Self {
Self {
coro,
_phantom: PhantomData,
}
}
}
156 changes: 156 additions & 0 deletions src/coroutine/coro_param/component.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
use std::marker::PhantomData;

use bevy::prelude::{Component, Entity, World};

use crate::coroutine::{
observable::{ObservableId, OnChange},
CoroAccess, CoroWrites, SourceId,
};

use super::{CoroParam, ParamContext, RdGuard, WrGuard};

/// A readonly reference to a [`Component`] from the owning [`Entity`].
///
/// Note that a Coroutine with such parameter will be canceled if the entity does not have the
/// relevent component.
pub struct Rd<T: Component> {
context: ParamContext,
_phantom: PhantomData<T>,
}

impl<T: Component> CoroParam for Rd<T> {
fn init(context: ParamContext, world: &mut World, access: &mut CoroAccess) -> Option<Self> {
let id = world
.component_id::<T>()
.unwrap_or(world.init_component::<T>());

if !access.add_read(SourceId::Entity(context.owner), id) {
return None;
}

Some(Self {
context,
_phantom: PhantomData,
})
}

fn is_valid(owner: Entity, world: &World) -> bool {
match world.get_entity(owner) {
Some(e) => e.contains::<T>(),
_ => false,
}
}
}

impl<T: Component> Rd<T> {
/// Return the current value of the [`Component`]. The result ([`InGuard`]) cannot be held
/// accros any await.
pub fn get(&self) -> RdGuard<'_, T> {
unsafe {
RdGuard::new(
self.context
.world_window
.world_cell()
.get_entity(self.context.owner)
.unwrap()
.get::<T>()
.unwrap(),
)
}
}

/// Yields and resume when the `Component` is mutated.
///
/// Note that it integrates with the regular change detection of Bevy, meaning that the
/// coroutine will be resumed, if a [`System`] mutates the value.
pub fn on_change(&self) -> OnChange<'_> {
unsafe {
OnChange::new(
&self.context,
ObservableId::Component(
self.context.owner,
self.context.world_window.component_id::<T>(),
),
)
}
}
}

/// A read-write exclusive reference to a [`Component`] from the owning [`Entity`].
///
/// Note that a Coroutine with such parameter will be canceled if the entity does not have the
/// relevent component.
pub struct Wr<T: Component> {
_phantom: PhantomData<T>,
context: ParamContext,
}

impl<T: Component> CoroParam for Wr<T> {
fn init(context: ParamContext, w: &mut World, access: &mut CoroAccess) -> Option<Self> {
let id = w.component_id::<T>().unwrap_or(w.init_component::<T>());

if !access.add_write(SourceId::Entity(context.owner), id) {
return None;
}

Some(Self {
_phantom: PhantomData,
context,
})
}

fn is_valid(owner: Entity, world: &World) -> bool {
match world.get_entity(owner) {
Some(e) => e.contains::<T>(),
_ => false,
}
}
}

impl<T: Component> Wr<T> {
pub fn get(&self) -> RdGuard<'_, T> {
let value = unsafe {
self.context
.world_window
.world_cell()
.get_entity(self.context.owner)
.unwrap()
.get::<T>()
.unwrap()
};

RdGuard::new(value)
}

pub fn get_mut(&mut self) -> WrGuard<'_, T> {
unsafe {
let cell = self.context.world_window.world_cell();
let c_id = cell.components().component_id::<T>().unwrap();
cell.get_resource_mut::<CoroWrites>()
.unwrap()
.0
// TODO fix write
.push_back((self.context.owner, c_id));

let value = cell
.get_entity(self.context.owner)
.unwrap()
.get_mut::<T>()
.unwrap();

WrGuard::new(value)
}
}

pub fn on_change(&self) -> OnChange<'_> {
unsafe {
OnChange::new(
&self.context,
ObservableId::Component(
self.context.owner,
self.context.world_window.component_id::<T>(),
),
)
}
}
}
Loading

0 comments on commit c5687d4

Please sign in to comment.