use bevy::prelude::*;
use bevy::reflect::serde::ReflectSerializer;
use std::time::Duration;
use crate::config::CacheConfig;
use crate::error::CacheError;
use crate::manifest::CacheManifest;
pub(crate) enum PendingPayload {
Reflect {
value: Box<dyn Reflect>,
extension: String,
},
Bytes {
reader: Box<dyn std::io::Read + Send + Sync + 'static>,
extension: String,
},
}
pub(crate) struct PendingSaveEntry {
pub key: String,
pub max_age: Option<Duration>,
pub payload: PendingPayload,
}
#[derive(Resource, Default)]
pub struct CacheQueue {
pub(crate) queue: Vec<PendingSaveEntry>,
}
impl CacheQueue {
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn enqueue_reflect(
&mut self,
value: Box<dyn Reflect>,
key: impl Into<String>,
extension: impl Into<String>,
max_age: Option<Duration>,
) {
self.queue.push(PendingSaveEntry {
key: key.into(),
max_age,
payload: PendingPayload::Reflect {
value,
extension: extension.into(),
},
});
}
pub fn enqueue<R: std::io::Read + Send + Sync + 'static>(
&mut self,
key: impl Into<String>,
extension: impl Into<String>,
reader: R,
max_age: Option<Duration>,
) {
self.queue.push(PendingSaveEntry {
key: key.into(),
max_age,
payload: PendingPayload::Bytes {
reader: Box::new(reader),
extension: extension.into(),
},
});
}
}
fn serialize_reflect(
value: &dyn Reflect,
registry: &AppTypeRegistry,
) -> Result<Vec<u8>, CacheError> {
let registry = registry.read();
let serializer = ReflectSerializer::new(value.as_partial_reflect(), ®istry);
let pretty = ron::ser::PrettyConfig::default();
let serialized =
ron::ser::to_string_pretty(&serializer, pretty).map_err(CacheError::RonSerialize)?;
Ok(serialized.into_bytes())
}
pub fn process_pending_saves(world: &mut World) {
let queue = {
let mut pending = world
.get_resource_mut::<CacheQueue>()
.expect("CacheQueue resource should exist");
std::mem::take(&mut pending.queue)
};
if queue.is_empty() {
return;
}
let config = world
.get_resource::<CacheConfig>()
.expect("CacheConfig resource should exist")
.clone();
let registry = world
.get_resource::<AppTypeRegistry>()
.expect("AppTypeRegistry resource should exist")
.clone();
for entry in queue {
let PendingSaveEntry { key, max_age, payload } = entry;
let result = match payload {
PendingPayload::Reflect { value, extension } => {
serialize_reflect(value.as_ref(), ®istry)
.and_then(|bytes| {
let mut manifest = world
.get_resource_mut::<CacheManifest>()
.expect("CacheManifest resource should exist");
manifest.store(
&config,
&key,
&extension,
std::io::Cursor::new(bytes),
max_age,
)
})
}
PendingPayload::Bytes { mut reader, extension } => {
let mut manifest = world
.get_resource_mut::<CacheManifest>()
.expect("CacheManifest resource should exist");
manifest.store(
&config,
&key,
&extension,
&mut *reader,
max_age,
)
}
};
match result {
Ok(()) => {
tracing::debug!("Cached asset '{key}'");
}
Err(e) => {
tracing::error!("Failed to cache asset '{key}': {e}");
}
}
}
}