use std::{any::Any, marker::PhantomData, panic, path::PathBuf, sync::Mutex};
use amethyst::{
self,
core::{transform::TransformBundle, EventReader, RunNowDesc, SystemBundle, SystemDesc},
ecs::prelude::*,
error::Error,
input::{BindingTypes, InputBundle},
prelude::*,
shred::Resource,
ui::UiBundle,
utils::application_root_dir,
window::ScreenDimensions,
StateEventReader,
};
use derivative::Derivative;
use lazy_static::lazy_static;
use crate::{
CustomDispatcherStateBuilder, FunctionState, GameUpdate, SequencerState,
SystemDescInjectionBundle, SystemInjectionBundle, ThreadLocalInjectionBundle,
};
type BundleAddFn = Box<
dyn FnOnce(
GameDataBuilder<'static, 'static>,
) -> Result<GameDataBuilder<'static, 'static>, Error>,
>;
type FnResourceAdd = Box<dyn FnMut(&mut World) + Send>;
type FnSetup = Box<dyn FnOnce(&mut World) + Send>;
type FnState<T, E> = Box<dyn FnOnce() -> Box<dyn State<T, E>>>;
pub const SCREEN_WIDTH: u32 = 800;
pub const SCREEN_HEIGHT: u32 = 600;
pub const HIDPI: f64 = 1.;
lazy_static! {
static ref RENDY_MEMORY_MUTEX: Mutex<()> = Mutex::new(());
}
#[derive(Derivative, Default)]
#[derivative(Debug)]
pub struct AmethystApplication<T, E, R>
where
E: Send + Sync + 'static,
{
#[derivative(Debug = "ignore")]
bundle_add_fns: Vec<BundleAddFn>,
#[derivative(Debug = "ignore")]
resource_add_fns: Vec<FnResourceAdd>,
#[derivative(Debug = "ignore")]
setup_fns: Vec<FnSetup>,
#[derivative(Debug = "ignore")]
state_fns: Vec<FnState<T, E>>,
state_data: PhantomData<(T, E, R)>,
}
impl AmethystApplication<GameData<'static, 'static>, StateEvent, StateEventReader> {
pub fn blank() -> AmethystApplication<GameData<'static, 'static>, StateEvent, StateEventReader>
{
AmethystApplication {
bundle_add_fns: Vec::new(),
resource_add_fns: Vec::new(),
setup_fns: Vec::new(),
state_fns: Vec::new(),
state_data: PhantomData,
}
}
pub fn ui_base<B: BindingTypes>(
) -> AmethystApplication<GameData<'static, 'static>, StateEvent, StateEventReader> {
AmethystApplication::blank()
.with_bundle(TransformBundle::new())
.with_ui_bundles::<B>()
.with_resource(ScreenDimensions::new(SCREEN_WIDTH, SCREEN_HEIGHT, HIDPI))
}
pub fn assets_dir() -> Result<PathBuf, Error> {
Ok(application_root_dir()?.join("assets"))
}
}
impl<E, R> AmethystApplication<GameData<'static, 'static>, E, R>
where
E: Clone + Send + Sync + 'static,
R: Default,
{
pub fn build(self) -> Result<CoreApplication<'static, GameData<'static, 'static>, E, R>, Error>
where
for<'b> R: EventReader<'b, Event = E>,
{
let params = (
self.bundle_add_fns,
self.resource_add_fns,
self.setup_fns,
self.state_fns,
);
Self::build_internal(params)
}
#[allow(unknown_lints, clippy::type_complexity)]
fn build_internal(
(bundle_add_fns, resource_add_fns, setup_fns, state_fns): (
Vec<BundleAddFn>,
Vec<FnResourceAdd>,
Vec<FnSetup>,
Vec<FnState<GameData<'static, 'static>, E>>,
),
) -> Result<CoreApplication<'static, GameData<'static, 'static>, E, R>, Error>
where
for<'b> R: EventReader<'b, Event = E>,
{
let game_data = bundle_add_fns.into_iter().fold(
Ok(GameDataBuilder::default()),
|game_data: Result<GameDataBuilder<'_, '_>, Error>, function: BundleAddFn| {
game_data.and_then(function)
},
)?;
let mut states = Vec::<Box<dyn State<GameData<'static, 'static>, E>>>::new();
state_fns
.into_iter()
.rev()
.for_each(|state_fn| states.push(state_fn()));
Self::build_application(
SequencerState::new(states),
game_data,
resource_add_fns,
setup_fns,
)
}
fn build_application<S>(
first_state: S,
game_data: GameDataBuilder<'static, 'static>,
resource_add_fns: Vec<FnResourceAdd>,
setup_fns: Vec<FnSetup>,
) -> Result<CoreApplication<'static, GameData<'static, 'static>, E, R>, Error>
where
S: State<GameData<'static, 'static>, E> + 'static,
for<'b> R: EventReader<'b, Event = E>,
{
let assets_dir =
AmethystApplication::assets_dir().expect("Failed to get default assets dir.");
let mut application_builder = CoreApplication::build(assets_dir, first_state)?;
{
let world = &mut application_builder.world;
for mut function in resource_add_fns {
function(world);
}
for function in setup_fns {
function(world);
}
}
application_builder.build(game_data)
}
pub fn run(self) -> Result<(), Error>
where
for<'b> R: EventReader<'b, Event = E>,
{
let params = (
self.bundle_add_fns,
self.resource_add_fns,
self.setup_fns,
self.state_fns,
);
let application = Mutex::new(Self::build_internal(params)?);
panic::catch_unwind(move || {
application
.lock()
.expect("Expected to get application lock")
.run()
})
.map_err(Self::box_any_to_error)
}
pub fn run_isolated(self) -> Result<(), Error>
where
for<'b> R: EventReader<'b, Event = E>,
{
let _guard = RENDY_MEMORY_MUTEX.lock().unwrap();
self.run()
}
fn box_any_to_error(error: Box<dyn Any + Send>) -> Error {
if let Some(inner) = error.downcast_ref::<&str>() {
Error::from_string((*inner).to_string())
} else if let Some(inner) = error.downcast_ref::<String>() {
Error::from_string(inner.clone())
} else {
Error::from_string(
"Unable to detect additional information from test failure.\n\
Please inspect the test output for clues.",
)
}
}
}
impl<T, E, R> AmethystApplication<T, E, R>
where
T: GameUpdate + 'static,
E: Send + Sync + 'static,
R: 'static,
{
pub fn with_custom_event_type<Evt, Rdr>(self) -> AmethystApplication<T, Evt, Rdr>
where
Evt: Send + Sync + 'static,
for<'b> Rdr: EventReader<'b, Event = Evt>,
{
if !self.state_fns.is_empty() {
panic!(
"`{}` must be invoked **before** any other `.with_*()` \
functions calls.",
stringify!(with_custom_event_type::<E>())
);
}
AmethystApplication {
bundle_add_fns: self.bundle_add_fns,
resource_add_fns: self.resource_add_fns,
setup_fns: self.setup_fns,
state_fns: Vec::new(),
state_data: PhantomData,
}
}
pub fn with_bundle<B>(mut self, bundle: B) -> Self
where
B: SystemBundle<'static, 'static> + Send + 'static,
{
self.bundle_add_fns
.push(Box::new(|game_data: GameDataBuilder<'static, 'static>| {
game_data.with_bundle(bundle)
}));
self
}
pub fn with_bundle_fn<FnBundle, B>(mut self, bundle_function: FnBundle) -> Self
where
FnBundle: FnOnce() -> B + Send + 'static,
B: SystemBundle<'static, 'static> + 'static,
{
self.bundle_add_fns.push(Box::new(
move |game_data: GameDataBuilder<'static, 'static>| {
game_data.with_bundle(bundle_function())
},
));
self
}
pub fn with_ui_bundles<B: BindingTypes>(self) -> Self {
self.with_bundle(InputBundle::<B>::new())
.with_bundle(UiBundle::<B>::new())
}
pub fn with_resource<Res>(mut self, resource: Res) -> Self
where
Res: Resource,
{
let mut resource_opt = Some(resource);
self.resource_add_fns
.push(Box::new(move |world: &mut World| {
let resource = resource_opt.take();
if let Some(resource) = resource {
world.insert(resource);
}
}));
self
}
pub fn with_state<S, FnStateLocal>(mut self, state_fn: FnStateLocal) -> Self
where
S: State<T, E> + 'static,
FnStateLocal: FnOnce() -> S + Send + Sync + 'static,
{
let closure = move || Box::new((state_fn)()) as Box<dyn State<T, E>>;
self.state_fns.push(Box::new(closure));
self
}
pub fn with_system<S, N>(self, system: S, name: N, deps: &[N]) -> Self
where
S: for<'sys_local> System<'sys_local> + Send + 'static,
N: Into<String> + Clone,
{
let name = Into::<String>::into(name);
let deps = deps
.iter()
.map(Clone::clone)
.map(Into::<String>::into)
.collect::<Vec<String>>();
self.with_bundle_fn(move || SystemInjectionBundle::new(system, name, deps))
}
pub fn with_system_desc<SD, S, N>(self, system_desc: SD, name: N, deps: &[N]) -> Self
where
SD: SystemDesc<'static, 'static, S> + Send + Sync + 'static,
S: for<'sys_local> System<'sys_local> + Send + 'static,
N: Into<String> + Clone,
{
let name = Into::<String>::into(name);
let deps = deps
.iter()
.map(Clone::clone)
.map(Into::<String>::into)
.collect::<Vec<String>>();
self.with_bundle_fn(move || SystemDescInjectionBundle::new(system_desc, name, deps))
}
pub fn with_thread_local<RNDesc, RN>(self, run_now_desc: RNDesc) -> Self
where
RNDesc: RunNowDesc<'static, 'static, RN> + Send + Sync + 'static,
RN: for<'sys_local> RunNow<'sys_local> + Send + 'static,
{
self.with_bundle_fn(move || ThreadLocalInjectionBundle::new(run_now_desc))
}
pub fn with_system_single<S, N>(self, system: S, name: N, deps: &[N]) -> Self
where
S: for<'sys_local> System<'sys_local> + Send + Sync + 'static,
N: Into<String> + Clone,
{
let name = Into::<String>::into(name);
let deps = deps
.iter()
.map(Clone::clone)
.map(Into::<String>::into)
.collect::<Vec<String>>();
self.with_state(move || {
CustomDispatcherStateBuilder::new()
.with_system(system, name, deps)
.build()
})
}
pub fn with_system_desc_single<SD, S, N>(self, system_desc: SD, name: N, deps: &[N]) -> Self
where
SD: SystemDesc<'static, 'static, S> + Send + Sync + 'static,
S: for<'sys_local> System<'sys_local> + Send + Sync + 'static,
N: Into<String> + Clone,
{
let name = Into::<String>::into(name);
let deps = deps
.iter()
.map(Clone::clone)
.map(Into::<String>::into)
.collect::<Vec<String>>();
self.with_state(move || {
CustomDispatcherStateBuilder::new()
.with_system_desc(system_desc, name, deps)
.build()
})
}
pub fn with_fn<F>(self, func: F) -> Self
where
F: FnOnce(&mut World) + Send + Sync + 'static,
{
self.with_state(move || FunctionState::new(func))
}
pub fn with_setup<F>(mut self, setup_fn: F) -> Self
where
F: FnOnce(&mut World) + Send + 'static,
{
self.setup_fns.push(Box::new(setup_fn));
self
}
pub fn with_effect<F>(self, effect_fn: F) -> Self
where
F: FnOnce(&mut World) + Send + Sync + 'static,
{
self.with_fn(effect_fn)
}
pub fn with_assertion<F>(self, assertion_fn: F) -> Self
where
F: FnOnce(&mut World) + Send + Sync + 'static,
{
self.with_fn(assertion_fn)
}
}
#[cfg(test)]
mod test {
use std::marker::PhantomData;
use amethyst::{
assets::{Asset, AssetStorage, Handle, Loader, ProcessingState, Processor},
core::{bundle::SystemBundle, SystemDesc},
derive::SystemDesc,
ecs::prelude::*,
error::Error,
prelude::*,
ui::FontAsset,
window::ScreenDimensions,
};
use super::AmethystApplication;
use crate::{EffectReturn, FunctionState, PopState};
#[test]
fn bundle_build_is_ok() -> Result<(), Error> {
AmethystApplication::blank().with_bundle(BundleZero).run()
}
#[test]
fn load_multiple_bundles() -> Result<(), Error> {
AmethystApplication::blank()
.with_bundle(BundleZero)
.with_bundle(BundleOne)
.run()
}
#[test]
fn assertion_when_resource_is_added_succeeds() -> Result<(), Error> {
let assertion_fn = |world: &mut World| {
world.read_resource::<ApplicationResource>();
world.read_resource::<ApplicationResourceNonDefault>();
};
AmethystApplication::blank()
.with_bundle(BundleZero)
.with_bundle(BundleOne)
.with_assertion(assertion_fn)
.run()
}
#[test]
#[should_panic(expected = "Tried to fetch resource of type `ApplicationResource`")]
fn assertion_when_resource_is_not_added_should_panic() {
let assertion_fn = |world: &mut World| {
world.read_resource::<ApplicationResource>();
};
AmethystApplication::blank()
.with_assertion(assertion_fn)
.run()
.unwrap();
}
#[test]
fn assertion_switch_with_loading_state_with_add_resource_succeeds() -> Result<(), Error> {
let state_fns = || {
let assertion_fn = |world: &mut World| {
world.read_resource::<LoadResource>();
};
let assertion_state = FunctionState::new(assertion_fn);
LoadingState::new(assertion_state)
};
AmethystApplication::blank().with_state(state_fns).run()
}
#[test]
fn assertion_push_with_loading_state_with_add_resource_succeeds() -> Result<(), Error> {
let state_fns = || LoadingState::new(PopState);
let assertion_fn = |world: &mut World| {
world.read_resource::<LoadResource>();
};
AmethystApplication::blank()
.with_state(state_fns)
.with_assertion(assertion_fn)
.run()
}
#[test]
#[should_panic(expected = "Tried to fetch resource of type `LoadResource`")]
fn assertion_switch_with_loading_state_without_add_resource_should_panic() {
let state_fns = || {
let assertion_fn = |world: &mut World| {
world.read_resource::<LoadResource>();
};
SwitchState::new(FunctionState::new(assertion_fn))
};
AmethystApplication::blank()
.with_state(state_fns)
.run()
.unwrap();
}
#[test]
#[should_panic(expected = "Tried to fetch resource of type `LoadResource`")]
fn assertion_push_with_loading_state_without_add_resource_should_panic() {
let state_fns = || SwitchState::new(PopState);
let assertion_fn = |world: &mut World| {
world.read_resource::<LoadResource>();
};
AmethystApplication::blank()
.with_state(state_fns)
.with_assertion(assertion_fn)
.run()
.unwrap();
}
#[test]
fn game_data_must_update_before_assertion() -> Result<(), Error> {
let effect_fn = |world: &mut World| {
let handles = vec![
AssetZeroLoader::load(world, AssetZero(10)).unwrap(),
AssetZeroLoader::load(world, AssetZero(20)).unwrap(),
];
world.insert::<Vec<AssetZeroHandle>>(handles);
};
let assertion_fn = |world: &mut World| {
let asset_translation_zero_handles = world.read_resource::<Vec<AssetZeroHandle>>();
let store = world.read_resource::<AssetStorage<AssetZero>>();
assert_eq!(
Some(&AssetZero(10)),
store.get(&asset_translation_zero_handles[0])
);
assert_eq!(
Some(&AssetZero(20)),
store.get(&asset_translation_zero_handles[1])
);
};
AmethystApplication::blank()
.with_bundle(BundleAsset)
.with_effect(effect_fn)
.with_assertion(assertion_fn)
.run()
}
#[test]
fn execution_order_is_setup_state_effect_assertion() -> Result<(), Error> {
struct Setup;
let setup_fns = |world: &mut World| world.insert(Setup);
let state_fns = || {
LoadingState::new(FunctionState::new(|world: &mut World| {
world.read_resource::<Setup>();
}))
};
let effect_fn = |world: &mut World| {
world.read_resource::<LoadResource>();
let handles = vec![AssetZeroLoader::load(world, AssetZero(10)).unwrap()];
world.insert(handles);
};
let assertion_fn = |world: &mut World| {
let asset_translation_zero_handles = world.read_resource::<Vec<AssetZeroHandle>>();
let store = world.read_resource::<AssetStorage<AssetZero>>();
assert_eq!(
Some(&AssetZero(10)),
store.get(&asset_translation_zero_handles[0])
);
};
AmethystApplication::blank()
.with_bundle(BundleAsset)
.with_setup(setup_fns)
.with_state(state_fns)
.with_effect(effect_fn)
.with_assertion(assertion_fn)
.run()
}
#[test]
fn base_application_can_load_ui() -> Result<(), Error> {
let assertion_fn = |world: &mut World| {
world.read_resource::<AssetStorage<FontAsset>>();
world.read_resource::<ScreenDimensions>();
};
AmethystApplication::ui_base::<amethyst::input::StringBindings>()
.with_assertion(assertion_fn)
.run()
}
#[test]
fn with_system_runs_system_every_tick() -> Result<(), Error> {
let effect_fn = |world: &mut World| {
let entity = world.create_entity().with(ComponentZero(0)).build();
world.insert(EffectReturn(entity));
};
fn get_component_zero_value(world: &mut World) -> i32 {
let entity = world.read_resource::<EffectReturn<Entity>>().0;
let component_zero_storage = world.read_storage::<ComponentZero>();
let component_zero = component_zero_storage
.get(entity)
.expect("Entity should have a `ComponentZero` component.");
component_zero.0
};
AmethystApplication::blank()
.with_system(SystemEffect, "system_effect", &[])
.with_effect(effect_fn)
.with_assertion(|world| assert_eq!(1, get_component_zero_value(world)))
.with_assertion(|world| assert_eq!(2, get_component_zero_value(world)))
.run()
}
#[test]
fn with_system_invoked_twice_should_not_panic() {
AmethystApplication::blank()
.with_system(SystemZero, "zero", &[])
.with_system(SystemOne, "one", &["zero"]);
}
#[test]
fn with_system_single_runs_system_once() -> Result<(), Error> {
let assertion_fn = |world: &mut World| {
let entity = world.read_resource::<EffectReturn<Entity>>().0;
let component_zero_storage = world.read_storage::<ComponentZero>();
let component_zero = component_zero_storage
.get(entity)
.expect("Entity should have a `ComponentZero` component.");
assert_eq!(1, component_zero.0);
};
AmethystApplication::blank()
.with_setup(|world| {
world.register::<ComponentZero>();
let entity = world.create_entity().with(ComponentZero(0)).build();
world.insert(EffectReturn(entity));
})
.with_system_single(SystemEffect, "system_effect", &[])
.with_assertion(assertion_fn)
.with_assertion(assertion_fn)
.run()
}
#[test]
fn with_setup_invoked_twice_should_run_in_specified_order() -> Result<(), Error> {
AmethystApplication::blank()
.with_setup(|world| {
world.insert(ApplicationResource);
})
.with_setup(|world| {
world.read_resource::<ApplicationResource>();
})
.run()
}
#[test]
fn with_effect_invoked_twice_should_run_in_the_specified_order() -> Result<(), Error> {
AmethystApplication::blank()
.with_effect(|world| {
world.insert(ApplicationResource);
})
.with_effect(|world| {
world.read_resource::<ApplicationResource>();
})
.run()
}
#[test]
fn with_assertion_invoked_twice_should_run_in_the_specified_order() -> Result<(), Error> {
AmethystApplication::blank()
.with_assertion(|world| {
world.insert(ApplicationResource);
})
.with_assertion(|world| {
world.read_resource::<ApplicationResource>();
})
.run()
}
#[test]
fn with_state_invoked_twice_should_run_in_the_specified_order() -> Result<(), Error> {
AmethystApplication::blank()
.with_state(|| {
FunctionState::new(|world| {
world.insert(ApplicationResource);
})
})
.with_state(|| {
FunctionState::new(|world| {
world.read_resource::<ApplicationResource>();
})
})
.run()
}
#[test]
fn with_state_invoked_after_with_resource_should_work() -> Result<(), Error> {
AmethystApplication::blank()
.with_resource(ApplicationResource)
.with_state(|| {
FunctionState::new(|world| {
world.read_resource::<ApplicationResource>();
})
})
.run()
}
#[test]
fn setup_runs_before_system() -> Result<(), Error> {
AmethystApplication::blank()
.with_setup(|world| world.insert(ApplicationResourceNonDefault))
.with_system(SystemNonDefault, "", &[])
.run()
}
#[cfg(feature = "test_audio")]
mod audio_test {
use amethyst::{
assets::AssetStorage,
audio::{AudioBundle, Source},
error::Error,
};
use super::AmethystApplication;
#[test]
fn audio_zero() -> Result<(), Error> {
AmethystApplication::blank()
.with_bundle(AudioBundle::default())
.with_assertion(|world| {
world.read_resource::<AssetStorage<Source>>();
})
.run()
}
#[test]
fn audio_one() -> Result<(), Error> {
AmethystApplication::blank()
.with_bundle(AudioBundle::default())
.with_assertion(|world| {
world.read_resource::<AssetStorage<Source>>();
})
.run()
}
#[test]
fn audio_two() -> Result<(), Error> {
AmethystApplication::blank()
.with_bundle_fn(|| AudioBundle::default())
.with_assertion(|world| {
world.read_resource::<AssetStorage<Source>>();
})
.run()
}
#[test]
fn audio_three() -> Result<(), Error> {
AmethystApplication::blank()
.with_bundle_fn(|| AudioBundle::default())
.with_assertion(|world| {
world.read_resource::<AssetStorage<Source>>();
})
.run()
}
}
#[derive(Debug, Default)]
struct ApplicationResource;
#[derive(Debug)]
struct ApplicationResourceNonDefault;
#[derive(Debug)]
struct LoadResource;
struct LoadingState<'a, 'b, S, E>
where
S: State<GameData<'a, 'b>, E> + 'static,
E: Send + Sync + 'static,
{
next_state: Option<S>,
state_data: PhantomData<dyn State<GameData<'a, 'b>, E>>,
}
impl<'a, 'b, S, E> LoadingState<'a, 'b, S, E>
where
S: State<GameData<'a, 'b>, E> + 'static,
E: Send + Sync + 'static,
{
fn new(next_state: S) -> Self {
LoadingState {
next_state: Some(next_state),
state_data: PhantomData,
}
}
}
impl<'a, 'b, S, E> State<GameData<'a, 'b>, E> for LoadingState<'a, 'b, S, E>
where
S: State<GameData<'a, 'b>, E> + 'static,
E: Send + Sync + 'static,
{
fn update(&mut self, data: StateData<'_, GameData<'_, '_>>) -> Trans<GameData<'a, 'b>, E> {
data.data.update(&data.world);
data.world.insert(LoadResource);
Trans::Switch(Box::new(self.next_state.take().unwrap()))
}
}
struct SwitchState<S, T, E>
where
S: State<T, E>,
E: Send + Sync + 'static,
{
next_state: Option<S>,
state_data: PhantomData<(T, E)>,
}
impl<S, T, E> SwitchState<S, T, E>
where
S: State<T, E>,
E: Send + Sync + 'static,
{
fn new(next_state: S) -> Self {
SwitchState {
next_state: Some(next_state),
state_data: PhantomData,
}
}
}
impl<S, T, E> State<T, E> for SwitchState<S, T, E>
where
S: State<T, E> + 'static,
E: Send + Sync + 'static,
{
fn update(&mut self, _data: StateData<'_, T>) -> Trans<T, E> {
Trans::Switch(Box::new(self.next_state.take().unwrap()))
}
}
#[derive(Debug, SystemDesc)]
struct SystemZero;
impl<'s> System<'s> for SystemZero {
type SystemData = ();
fn run(&mut self, _: Self::SystemData) {}
}
#[derive(Debug, SystemDesc)]
struct SystemOne;
type SystemOneData<'s> = Read<'s, ApplicationResource>;
impl<'s> System<'s> for SystemOne {
type SystemData = SystemOneData<'s>;
fn run(&mut self, _: Self::SystemData) {}
}
#[derive(Debug, SystemDesc)]
#[system_desc(insert(ApplicationResourceNonDefault))]
struct SystemNonDefault;
type SystemNonDefaultData<'s> = ReadExpect<'s, ApplicationResourceNonDefault>;
impl<'s> System<'s> for SystemNonDefault {
type SystemData = SystemNonDefaultData<'s>;
fn run(&mut self, _: Self::SystemData) {}
}
#[derive(Debug, SystemDesc)]
struct SystemEffect;
type SystemEffectData<'s> = WriteStorage<'s, ComponentZero>;
impl<'s> System<'s> for SystemEffect {
type SystemData = SystemEffectData<'s>;
fn run(&mut self, mut component_zero_storage: Self::SystemData) {
for mut component_zero in (&mut component_zero_storage).join() {
component_zero.0 += 1
}
}
}
#[derive(Debug)]
struct BundleZero;
impl<'a, 'b> SystemBundle<'a, 'b> for BundleZero {
fn build(
self,
_world: &mut World,
builder: &mut DispatcherBuilder<'a, 'b>,
) -> Result<(), Error> {
builder.add(SystemZero, "system_zero", &[]);
Ok(())
}
}
#[derive(Debug)]
struct BundleOne;
impl<'a, 'b> SystemBundle<'a, 'b> for BundleOne {
fn build(
self,
world: &mut World,
builder: &mut DispatcherBuilder<'a, 'b>,
) -> Result<(), Error> {
builder.add(SystemOne, "system_one", &["system_zero"]);
builder.add(SystemNonDefault.build(world), "system_non_default", &[]);
Ok(())
}
}
#[derive(Debug)]
struct BundleAsset;
impl<'a, 'b> SystemBundle<'a, 'b> for BundleAsset {
fn build(
self,
_world: &mut World,
builder: &mut DispatcherBuilder<'a, 'b>,
) -> Result<(), Error> {
builder.add(
Processor::<AssetZero>::new(),
"asset_translation_zero_processor",
&[],
);
Ok(())
}
}
#[derive(Debug, PartialEq)]
struct AssetZero(u32);
impl Asset for AssetZero {
const NAME: &'static str = "amethyst_test::AssetZero";
type Data = Self;
type HandleStorage = VecStorage<Handle<Self>>;
}
impl Component for AssetZero {
type Storage = DenseVecStorage<Self>;
}
impl From<AssetZero> for Result<ProcessingState<AssetZero>, Error> {
fn from(asset_translation_zero: AssetZero) -> Result<ProcessingState<AssetZero>, Error> {
Ok(ProcessingState::Loaded(asset_translation_zero))
}
}
type AssetZeroHandle = Handle<AssetZero>;
struct AssetZeroLoader;
impl AssetZeroLoader {
fn load(
world: &World,
asset_translation_zero: AssetZero,
) -> Result<AssetZeroHandle, Error> {
let loader = world.read_resource::<Loader>();
Ok(loader.load_from_data(
asset_translation_zero,
(),
&world.read_resource::<AssetStorage<AssetZero>>(),
))
}
}
struct ComponentZero(pub i32);
impl Component for ComponentZero {
type Storage = DenseVecStorage<Self>;
}
}