use crate::{
app::SystemStage,
assets::deps::Dependencies,
ecs::plugin::Plugin,
ecs::resources::Resources,
ecs::system::{Commands, Res},
};
pub trait LazyResource<B>: 'static + Send + Sync + Sized {
type Deps<'a>: Dependencies<'a>;
fn construct<'a>(backend: &B, deps: &Self::Deps<'a>) -> Option<Self>;
}
pub struct LazyResourcePlugin<B, T: LazyResource<B>> {
_marker: std::marker::PhantomData<(B, T)>,
}
impl<B, T: LazyResource<B>> LazyResourcePlugin<B, T> {
pub fn new() -> Self {
Self {
_marker: std::marker::PhantomData,
}
}
}
impl<B, T> Plugin for LazyResourcePlugin<B, T>
where
B: 'static + Send + Sync,
T: LazyResource<B>,
{
fn build(&self, app: &mut crate::prelude::App) {
app.provides::<T>();
app.add_system(SystemStage::AssetSyncDeps, construct_resource::<B, T>);
}
}
fn construct_resource<B, T>(
mut commands: Commands,
backend: Option<Res<B>>,
existing: Option<Res<T>>,
world: &hecs::World,
resources: &Resources,
) where
B: 'static + Send + Sync,
T: LazyResource<B>,
{
if existing.is_some() {
return;
}
let Some(backend) = backend else {
tracing::trace!(
"{}: waiting on backend to construct resource",
std::any::type_name::<T>()
);
return;
};
let Some(deps) = T::Deps::try_gather(world, resources) else {
tracing::trace!(
"{}: waiting on dependencies to construct resource",
std::any::type_name::<T>()
);
return;
};
match T::construct(&backend, &deps) {
Some(value) => {
commands.insert_resource(value);
}
None => {
tracing::trace!(
"{}: construct() returned None, will retry next tick",
std::any::type_name::<T>()
);
}
}
}