use serde::{de::DeserializeOwned, Serialize};
use bevy_app::{App, Plugin, PostUpdate};
use bevy_ecs::prelude::*;
use crate::PkvStore;
pub struct PersistentResourcePlugin<T> {
_phantom: std::marker::PhantomData<T>,
factory: Option<Box<dyn Fn() -> T + Send + Sync + 'static>>,
}
impl<T> PersistentResourcePlugin<T>
where
T: Resource + Serialize + DeserializeOwned + Default + Send + Sync + 'static,
{
pub fn new() -> Self {
Self {
_phantom: std::marker::PhantomData,
factory: Some(Box::new(|| T::default())),
}
}
}
impl<T> PersistentResourcePlugin<T>
where
T: Resource + Serialize + DeserializeOwned + Send + Sync + 'static,
{
pub fn with_default<F>(factory: F) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
Self {
_phantom: std::marker::PhantomData,
factory: Some(Box::new(factory)),
}
}
}
impl<T> Default for PersistentResourcePlugin<T>
where
T: Resource + Serialize + DeserializeOwned + Default + Send + Sync + 'static,
{
fn default() -> Self {
Self::new()
}
}
impl<T> Plugin for PersistentResourcePlugin<T>
where
T: Resource + Serialize + DeserializeOwned + Send + Sync + 'static,
{
fn build(&self, app: &mut App) {
let key = std::any::type_name::<T>();
let pkv = app.world_mut().resource_mut::<PkvStore>();
let resource = pkv.get::<T>(key).unwrap_or_else(|_| {
self.factory
.as_ref()
.expect("PersistentResourcePlugin should always have a factory function")(
)
});
app.insert_resource(resource);
app.add_systems(PostUpdate, save_resource::<T>.run_if(resource_changed::<T>));
}
}
fn save_resource<T>(resource: Res<T>, mut pkv: ResMut<PkvStore>)
where
T: Resource + Serialize + DeserializeOwned + Send + Sync + 'static,
{
let key = std::any::type_name::<T>();
if let Err(e) = pkv.set(key, &*resource) {
eprintln!("Failed to persist resource: {:?}", e);
}
}
pub trait PersistentResourceAppExtensions {
fn init_persistent_resource<T>(&mut self) -> &mut Self
where
T: Resource + Serialize + DeserializeOwned + Default + Send + Sync + 'static;
fn init_persistent_resource_with<T, F>(&mut self, factory: F) -> &mut Self
where
T: Resource + Serialize + DeserializeOwned + Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static;
}
impl PersistentResourceAppExtensions for App {
fn init_persistent_resource<T>(&mut self) -> &mut Self
where
T: Resource + Serialize + DeserializeOwned + Default + Send + Sync + 'static,
{
self.add_plugins(PersistentResourcePlugin::<T>::new())
}
fn init_persistent_resource_with<T, F>(&mut self, factory: F) -> &mut Self
where
T: Resource + Serialize + DeserializeOwned + Send + Sync + 'static,
F: Fn() -> T + Send + Sync + 'static,
{
self.add_plugins(PersistentResourcePlugin::<T>::with_default(factory))
}
}