Skip to content

Commit

Permalink
Merge amethyst#141
Browse files Browse the repository at this point in the history
141: Move methods from `specs::WorldExt` to `World` r=torkleyy a=torkleyy

Also bumped to 0.9.0 due to recent breaking change (amethyst#138).

Co-authored-by: Thomas Schaller <[email protected]>
  • Loading branch information
bors[bot] and torkleyy committed May 27, 2019
2 parents d7f1261 + e2c3d47 commit 318be6b
Show file tree
Hide file tree
Showing 6 changed files with 213 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "shred"
version = "0.8.1"
version = "0.9.0"
authors = ["torkleyy <[email protected]>"]
description = """
Dispatches systems in parallel which need read access to some resources,
Expand Down
4 changes: 2 additions & 2 deletions src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl Error for InvalidBorrow {
///
/// Access the value via `std::ops::Deref` (e.g. `*val`)
#[derive(Debug)]
pub struct Ref<'a, T: 'a + ?Sized> {
pub struct Ref<'a, T: ?Sized + 'a> {
flag: &'a AtomicUsize,
value: &'a T,
}
Expand Down Expand Up @@ -130,7 +130,7 @@ impl<'a, T: ?Sized> Clone for Ref<'a, T> {
///
/// Access the value via `std::ops::DerefMut` (e.g. `*val`)
#[derive(Debug)]
pub struct RefMut<'a, T: 'a + ?Sized> {
pub struct RefMut<'a, T: ?Sized + 'a> {
flag: &'a AtomicUsize,
value: &'a mut T,
}
Expand Down
10 changes: 4 additions & 6 deletions src/dispatch/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,8 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> {
///
/// Thread-local systems are dispatched in-order.
///
/// Same as
/// [`add_thread_local()`](struct.DispatcherBuilder.html#method.
/// add_thread_local), but returns `self` to enable method chaining.
/// Same as [DispatcherBuilder::add_thread_local], but returns `self` to
/// enable method chaining.
pub fn with_thread_local<T>(mut self, system: T) -> Self
where
T: for<'c> RunNow<'c> + 'b,
Expand Down Expand Up @@ -217,9 +216,8 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> {
/// Thread-local systems are not affected by barriers;
/// they're always executed at the end.
///
/// Same as
/// [`add_barrier()`](struct.DispatcherBuilder.html#method.add_barrier),
/// but returns `self` to enable method chaining.
/// Same as [DispatcherBuilder::add_barrier], but returns `self` to enable
/// method chaining.
pub fn with_barrier(mut self) -> Self {
self.add_barrier();

Expand Down
7 changes: 2 additions & 5 deletions src/dispatch/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,13 @@ impl<'a, 'b> Dispatcher<'a, 'b> {
///
/// This function automatically redirects to
///
/// * [`dispatch_par`] in case it is supported
/// * [`dispatch_seq`] otherwise
/// * [Dispatcher::dispatch_par] in case it is supported
/// * [Dispatcher::dispatch_seq] otherwise
///
/// and runs `dispatch_thread_local` afterwards.
///
/// Please note that this method assumes that no resource
/// is currently borrowed. If that's the case, it panics.
///
/// [`dispatch_par`]: struct.Dispatcher.html#method.dispatch_par
/// [`dispatch_seq`]: struct.Dispatcher.html#method.dispatch_seq
pub fn dispatch(&mut self, world: &World) {
#[cfg(feature = "parallel")]
self.dispatch_par(world);
Expand Down
3 changes: 1 addition & 2 deletions src/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::{any::TypeId, marker::PhantomData};
use hashbrown::HashMap;
use mopa::Any;

use crate::{Resource, World, ResourceId};
use crate::{Resource, ResourceId, World};

/// This implements `Send` and `Sync` unconditionally.
/// (the trait itself doesn't need to have these bounds and the
Expand Down Expand Up @@ -436,7 +436,6 @@ mod tests {
table.register(&ImplementorA(125));
table.register(&ImplementorB(111111));


{
let mut iter = table.iter(&mut world);
assert_eq!(iter.next().unwrap().method1(), 3);
Expand Down
205 changes: 203 additions & 2 deletions src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ use std::{
use hashbrown::HashMap;
use mopa::Any;

use crate::cell::{Ref, RefMut, TrustCell};
use crate::{
cell::{Ref, RefMut, TrustCell},
SystemData,
};

use self::entry::create_entry;

Expand Down Expand Up @@ -158,9 +161,25 @@ impl ResourceId {
}
}

/// A resource container, which provides methods to access to
/// A [Resource] container, which provides methods to insert, access and manage
/// the contained resources.
///
/// Many methods take `&self` which works because everything
/// is stored with **interior mutability**. In case you violate
/// the borrowing rules of Rust (multiple reads xor one write),
/// you will get a panic.
///
/// # Use with Specs
///
/// If you're using this from the Specs ECS library, there are two things to be
/// aware of:
///
/// 1. There are many utility methods Specs provides. To use them, you need to
/// import `specs::WorldExt`.
/// 2. You should not use [World::empty], but rather `specs::WorldExt::new`. The
/// latter can simply be called using `World::new()`, as long as `WorldExt`
/// is imported.
///
/// # Resource Ids
///
/// Resources are identified by `ResourceId`s, which consist of a `TypeId`.
Expand Down Expand Up @@ -242,6 +261,134 @@ impl World {
create_entry(self.resources.entry(ResourceId::new::<R>()))
}

/// Gets `SystemData` `T` from the `World`. This can be used to retrieve
/// data just like in [System](crate::System)s.
///
/// This will not setup the system data, i.e. resources fetched here must
/// exist already.
///
/// # Examples
///
/// ```
/// # use shred::*;
/// # #[derive(Default)] struct Timer; #[derive(Default)] struct AnotherResource;
///
/// // NOTE: If you use Specs, use `World::new` instead.
/// let mut world = World::empty();
/// world.insert(Timer);
/// world.insert(AnotherResource);
/// let system_data: (Read<Timer>, Read<AnotherResource>) = world.system_data();
/// ```
///
/// # Panics
///
/// * Panics if `T` is already borrowed in an incompatible way.
pub fn system_data<'a, T>(&'a self) -> T
where
T: SystemData<'a>,
{
SystemData::fetch(&self)
}

/// Sets up system data `T` for fetching afterwards.
///
/// Most `SystemData` implementations will insert a sensible default value,
/// by implementing [SystemData::setup]. However, it is not guaranteed to
/// do that; if there is no sensible default, `setup` might not do anything.
///
/// # Examples
///
/// ```
/// use shred::{Read, World};
///
/// #[derive(Default)]
/// struct MyCounter(u32);
///
/// // NOTE: If you use Specs, use `World::new` instead.
/// let mut world = World::empty();
/// assert!(!world.has_value::<MyCounter>());
///
/// // `Read<MyCounter>` requires a `Default` implementation, and uses
/// // that to initialize the resource
/// world.setup::<Read<MyCounter>>();
/// assert!(world.has_value::<MyCounter>());
/// ```
///
/// Here's another example, showing the case where no resource gets
/// initialized:
///
/// ```
/// use shred::{ReadExpect, World};
///
/// struct MyCounter(u32);
///
/// // NOTE: If you use Specs, use `World::new` instead.
/// let mut world = World::empty();
///
/// world.setup::<ReadExpect<MyCounter>>();
/// ```
pub fn setup<'a, T: SystemData<'a>>(&mut self) {
T::setup(self);
}

/// Executes `f` once, right now and with the specified system data.
///
/// This sets up the system data `f` expects, fetches it and then
/// executes `f`. This is essentially like a one-time [System].
///
/// This is especially useful if you either need a lot of system data or,
/// with Specs, if you want to build an entity and for that you need to
/// access resources first - just fetching the resources and building
/// the entity would cause a double borrow.
///
/// **Calling this method is equivalent to:**
///
/// ```
/// # use shred::*;
/// # struct MySystemData; impl MySystemData { fn do_something(&self) {} }
/// # impl<'a> SystemData<'a> for MySystemData {
/// # fn fetch(res: &World) -> Self { MySystemData }
/// # fn reads() -> Vec<ResourceId> { vec![] }
/// # fn writes() -> Vec<ResourceId> { vec![] }
/// # fn setup(res: &mut World) {}
/// # }
/// # let mut world = World::empty();
/// {
/// // note the extra scope
/// world.setup::<MySystemData>();
/// let my_data: MySystemData = world.system_data();
/// my_data.do_something();
/// }
/// ```
///
/// # Examples
///
/// ```
/// # use shred::*;
/// // NOTE: If you use Specs, use `World::new` instead.
/// let mut world = World::empty();
///
/// #[derive(Default)]
/// struct MyRes {
/// field: i32,
/// }
///
/// world.exec(|(mut my_res,): (Write<MyRes>,)| {
/// assert_eq!(my_res.field, 0);
/// my_res.field = 5;
/// });
///
/// assert_eq!(world.fetch::<MyRes>().field, 5);
/// ```
pub fn exec<'a, F, R, T>(&'a mut self, f: F) -> R
where
F: FnOnce(T) -> R,
T: SystemData<'a>,
{
self.setup::<T>();
f(self.system_data())
}

/// Fetches the resource with the specified type `T` or panics if it doesn't
/// exist.
///
Expand Down Expand Up @@ -468,6 +615,60 @@ mod tests {
);
}

#[test]
fn system_data() {
let mut world = World::empty();

world.insert(5u32);
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 5);
}

#[test]
fn setup() {
let mut world = World::empty();

world.insert(5u32);
world.setup::<Read<u32>>();
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 5);

world.remove::<u32>();
world.setup::<Read<u32>>();
let x = *world.system_data::<Read<u32>>();
assert_eq!(x, 0);
}

#[test]
fn exec() {
let mut world = World::empty();

world.exec(|(float, boolean): (Read<f32>, Read<bool>)| {
assert_eq!(*float, 0.0);
assert_eq!(*boolean, false);
});

world.exec(|(mut float, mut boolean): (Write<f32>, Write<bool>)| {
*float = 4.3;
*boolean = true;
});

world.exec(|(float, boolean): (Read<f32>, ReadExpect<bool>)| {
assert_eq!(*float, 4.3);
assert_eq!(*boolean, true);
});
}

#[test]
#[should_panic]
fn exec_panic() {
let mut world = World::empty();

world.exec(|(_float, _boolean): (Write<f32>, Write<bool>)| {
panic!();
});
}

#[test]
#[should_panic]
fn invalid_fetch_by_id0() {
Expand Down

0 comments on commit 318be6b

Please sign in to comment.