use crate::{
dispatch::Dispatcher, world::ResourceId, Accessor, AccessorCow, DynamicSystemData, RunningTime,
System, SystemData, World,
};
#[derive(Debug)]
pub struct BatchAccessor {
reads: Vec<ResourceId>,
writes: Vec<ResourceId>,
}
impl BatchAccessor {
pub fn new(reads: Vec<ResourceId>, writes: Vec<ResourceId>) -> Self {
BatchAccessor { reads, writes }
}
}
impl Accessor for BatchAccessor {
fn try_new() -> Option<Self> {
None
}
fn reads(&self) -> Vec<ResourceId> {
self.reads.clone()
}
fn writes(&self) -> Vec<ResourceId> {
self.writes.clone()
}
}
pub struct BatchUncheckedWorld<'a>(pub &'a World);
impl<'a> DynamicSystemData<'a> for BatchUncheckedWorld<'a> {
type Accessor = BatchAccessor;
fn setup(_accessor: &Self::Accessor, _world: &mut World) {}
fn fetch(_access: &Self::Accessor, world: &'a World) -> Self {
BatchUncheckedWorld(world)
}
}
pub trait BatchController<'a, 'b> {
type BatchSystemData: SystemData<'a>;
unsafe fn create(accessor: BatchAccessor, dispatcher: Dispatcher<'a, 'b>) -> Self;
}
pub struct DefaultBatchControllerSystem<'a, 'b> {
accessor: BatchAccessor,
dispatcher: Dispatcher<'a, 'b>,
}
impl<'a, 'b> BatchController<'a, 'b> for DefaultBatchControllerSystem<'a, 'b> {
type BatchSystemData = ();
unsafe fn create(accessor: BatchAccessor, dispatcher: Dispatcher<'a, 'b>) -> Self {
DefaultBatchControllerSystem {
accessor,
dispatcher,
}
}
}
impl<'a> System<'a> for DefaultBatchControllerSystem<'_, '_> {
type SystemData = BatchUncheckedWorld<'a>;
fn run(&mut self, data: Self::SystemData) {
self.dispatcher.dispatch(data.0);
}
fn running_time(&self) -> RunningTime {
RunningTime::VeryLong
}
fn accessor<'c>(&'c self) -> AccessorCow<'a, 'c, Self> {
AccessorCow::Ref(&self.accessor)
}
fn setup(&mut self, world: &mut World) {
self.dispatcher.setup(world);
}
}
unsafe impl<'a, 'b> Send for DefaultBatchControllerSystem<'a, 'b> {}
#[cfg(test)]
mod tests {
use crate::{
AccessorCow, BatchAccessor, BatchController, BatchUncheckedWorld, Dispatcher,
DispatcherBuilder, RunningTime, System, World, Write,
};
#[test]
fn test_setup() {
let mut dispatcher = DispatcherBuilder::new()
.with_batch::<CustomBatchControllerSystem>(
DispatcherBuilder::new()
.with(BuyTomatoSystem, "buy_tomato_system", &[])
.with(BuyPotatoSystem, "buy_potato_system", &[]),
"BatchSystemTest",
&[],
)
.build();
let mut world = World::empty();
dispatcher.setup(&mut world);
let potato_store = world.fetch::<PotatoStore>();
let tomato_store = world.fetch::<TomatoStore>();
assert!(!potato_store.is_store_open);
assert!(!tomato_store.is_store_open);
assert_eq!(potato_store.potato_count, 50);
assert_eq!(tomato_store.tomato_count, 50);
}
#[test]
fn test_parallel_batch_execution() {
let mut dispatcher = DispatcherBuilder::new()
.with(OpenStoresSystem, "open_stores_system", &[])
.with_batch::<CustomBatchControllerSystem>(
DispatcherBuilder::new()
.with(BuyTomatoSystem, "buy_tomato_system", &[])
.with(BuyPotatoSystem, "buy_potato_system", &[]),
"BatchSystemTest",
&[],
)
.with(CloseStoresSystem, "close_stores_system", &[])
.build();
let mut world = World::empty();
dispatcher.setup(&mut world);
{
let potato_store = world.fetch::<PotatoStore>();
let tomato_store = world.fetch::<TomatoStore>();
assert!(!potato_store.is_store_open);
assert!(!tomato_store.is_store_open);
assert_eq!(potato_store.potato_count, 50);
assert_eq!(tomato_store.tomato_count, 50);
}
for _i in 0..10 {
dispatcher.dispatch(&world);
}
{
let potato_store = world.fetch::<PotatoStore>();
let tomato_store = world.fetch::<TomatoStore>();
assert!(!potato_store.is_store_open);
assert!(!tomato_store.is_store_open);
assert_eq!(potato_store.potato_count, 50 - (1 * 3 * 10));
assert_eq!(tomato_store.tomato_count, 50 - (1 * 3 * 10));
}
}
#[test]
fn test_sequence_batch_execution() {
let mut dispatcher = DispatcherBuilder::new()
.with(OpenStoresSystem, "open_stores_system", &[])
.with_batch::<CustomBatchControllerSystem>(
DispatcherBuilder::new()
.with(BuyTomatoWalletSystem, "buy_tomato_system", &[])
.with(BuyPotatoWalletSystem, "buy_potato_system", &[]),
"BatchSystemTest",
&[],
)
.with(CloseStoresSystem, "close_stores_system", &[])
.build();
let mut world = World::empty();
dispatcher.setup(&mut world);
{
let potato_store = world.fetch::<PotatoStore>();
let tomato_store = world.fetch::<TomatoStore>();
let customer_wallet = world.fetch::<CustomerWallet>();
assert!(!potato_store.is_store_open);
assert!(!tomato_store.is_store_open);
assert_eq!(potato_store.potato_count, 50);
assert_eq!(tomato_store.tomato_count, 50);
assert_eq!(customer_wallet.cents_count, 2000);
}
for _i in 0..10 {
dispatcher.dispatch(&world);
}
{
let potato_store = world.fetch::<PotatoStore>();
let tomato_store = world.fetch::<TomatoStore>();
let customer_wallet = world.fetch::<CustomerWallet>();
assert!(!potato_store.is_store_open);
assert!(!tomato_store.is_store_open);
assert_eq!(potato_store.potato_count, 50 - (1 * 3 * 10));
assert_eq!(tomato_store.tomato_count, 50 - (1 * 3 * 10));
assert_eq!(customer_wallet.cents_count, 2000 - ((50 + 150) * 3 * 10));
}
}
#[derive(Debug, Clone, Copy)]
pub struct PotatoStore {
pub is_store_open: bool,
pub potato_count: i32,
}
impl Default for PotatoStore {
fn default() -> Self {
PotatoStore {
is_store_open: false,
potato_count: 50,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct TomatoStore {
pub is_store_open: bool,
pub tomato_count: i32,
}
impl Default for TomatoStore {
fn default() -> Self {
TomatoStore {
is_store_open: false,
tomato_count: 50,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct CustomerWallet {
pub cents_count: i32,
}
impl Default for CustomerWallet {
fn default() -> Self {
CustomerWallet { cents_count: 2000 }
}
}
pub struct OpenStoresSystem;
impl<'a> System<'a> for OpenStoresSystem {
type SystemData = (Write<'a, PotatoStore>, Write<'a, TomatoStore>);
fn run(&mut self, mut data: Self::SystemData) {
data.0.is_store_open = true;
data.1.is_store_open = true;
}
}
pub struct CloseStoresSystem;
impl<'a> System<'a> for CloseStoresSystem {
type SystemData = (Write<'a, PotatoStore>, Write<'a, TomatoStore>);
fn run(&mut self, mut data: Self::SystemData) {
data.0.is_store_open = false;
data.1.is_store_open = false;
}
}
pub struct BuyPotatoSystem;
impl<'a> System<'a> for BuyPotatoSystem {
type SystemData = (Write<'a, PotatoStore>);
fn run(&mut self, mut potato_store: Self::SystemData) {
assert!(potato_store.is_store_open);
potato_store.potato_count -= 1;
}
}
pub struct BuyTomatoSystem;
impl<'a> System<'a> for BuyTomatoSystem {
type SystemData = (Write<'a, TomatoStore>);
fn run(&mut self, mut tomato_store: Self::SystemData) {
assert!(tomato_store.is_store_open);
tomato_store.tomato_count -= 1;
}
}
pub struct BuyPotatoWalletSystem;
impl<'a> System<'a> for BuyPotatoWalletSystem {
type SystemData = (Write<'a, PotatoStore>, Write<'a, CustomerWallet>);
fn run(&mut self, (mut potato_store, mut customer_wallet): Self::SystemData) {
assert!(potato_store.is_store_open);
potato_store.potato_count -= 1;
customer_wallet.cents_count -= 50;
}
}
pub struct BuyTomatoWalletSystem;
impl<'a> System<'a> for BuyTomatoWalletSystem {
type SystemData = (Write<'a, TomatoStore>, Write<'a, CustomerWallet>);
fn run(&mut self, (mut tomato_store, mut customer_wallet): Self::SystemData) {
assert!(tomato_store.is_store_open);
tomato_store.tomato_count -= 1;
customer_wallet.cents_count -= 150;
}
}
pub struct CustomBatchControllerSystem<'a, 'b> {
accessor: BatchAccessor,
dispatcher: Dispatcher<'a, 'b>,
}
impl<'a, 'b> BatchController<'a, 'b> for CustomBatchControllerSystem<'a, 'b> {
type BatchSystemData = ();
unsafe fn create(accessor: BatchAccessor, dispatcher: Dispatcher<'a, 'b>) -> Self {
CustomBatchControllerSystem {
accessor,
dispatcher,
}
}
}
impl<'a> System<'a> for CustomBatchControllerSystem<'_, '_> {
type SystemData = BatchUncheckedWorld<'a>;
fn run(&mut self, data: Self::SystemData) {
for _i in 0..3 {
self.dispatcher.dispatch(data.0);
}
}
fn running_time(&self) -> RunningTime {
RunningTime::VeryLong
}
fn accessor<'c>(&'c self) -> AccessorCow<'a, 'c, Self> {
AccessorCow::Ref(&self.accessor)
}
fn setup(&mut self, world: &mut World) {
self.dispatcher.setup(world);
}
}
unsafe impl<'a, 'b> Send for CustomBatchControllerSystem<'a, 'b> {}
}