2024-03-09 05:25:13 +00:00
|
|
|
use std::{any::{Any, TypeId}, marker::PhantomData, sync::{Arc, RwLock}};
|
2023-09-12 18:25:33 +00:00
|
|
|
|
2024-03-08 05:20:29 +00:00
|
|
|
use lyra_ecs::Component;
|
2024-03-09 05:25:13 +00:00
|
|
|
use crate::{loader::LoaderError, lyra_engine};
|
2023-09-12 18:25:33 +00:00
|
|
|
use uuid::Uuid;
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
use crate::ResourceStorage;
|
|
|
|
|
2024-03-09 05:25:13 +00:00
|
|
|
/// A trait that that each resource type should implement.
|
|
|
|
pub trait ResourceData: Send + Sync + Any + 'static {
|
|
|
|
fn as_any(&self) -> &dyn Any;
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
|
|
|
fn type_id(&self) -> TypeId;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: Send + Sync + Any + 'static> ResourceData for T {
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_id(&self) -> TypeId {
|
|
|
|
TypeId::of::<T>()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 18:25:33 +00:00
|
|
|
pub enum ResourceState {
|
|
|
|
Loading,
|
2024-03-09 05:25:13 +00:00
|
|
|
Error(Arc<LoaderError>),
|
|
|
|
Ready(Box<dyn ResourceData>),
|
2023-09-12 18:25:33 +00:00
|
|
|
}
|
|
|
|
|
2024-01-15 23:07:47 +00:00
|
|
|
pub struct ResourceDataRef<'a, T> {
|
|
|
|
guard: std::sync::RwLockReadGuard<'a, Resource<T>>,
|
2023-09-12 18:25:33 +00:00
|
|
|
}
|
|
|
|
|
2024-03-09 05:25:13 +00:00
|
|
|
impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> {
|
2024-01-15 23:07:47 +00:00
|
|
|
type Target = T;
|
2023-09-29 17:00:33 +00:00
|
|
|
|
2024-01-15 23:07:47 +00:00
|
|
|
fn deref(&self) -> &Self::Target {
|
2024-03-09 05:25:13 +00:00
|
|
|
match &self.guard.state {
|
|
|
|
ResourceState::Ready(d) => {
|
|
|
|
// for some reason, if I didn't use `.as_ref`, the downcast would fail.
|
|
|
|
let d = d.as_ref().as_any();
|
|
|
|
d.downcast_ref::<T>().unwrap()
|
|
|
|
},
|
|
|
|
_ => unreachable!() // ResHandler::data_ref shouldn't allow this to run
|
|
|
|
}
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
pub struct Resource<T> {
|
2024-01-15 23:07:47 +00:00
|
|
|
pub(crate) version: usize,
|
|
|
|
pub(crate) state: ResourceState,
|
|
|
|
uuid: Uuid,
|
2024-03-09 05:25:13 +00:00
|
|
|
path: Option<String>,
|
2024-01-16 04:22:21 +00:00
|
|
|
pub(crate) is_watched: bool,
|
2024-03-09 05:25:13 +00:00
|
|
|
_marker: PhantomData<T>,
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A handle to a resource.
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// This struct has an inner [`RwLock`] to the resource data, so most methods may be blocking.
|
|
|
|
/// However, the only times it will be blocking is if another thread is reloading the resource
|
|
|
|
/// and has a write lock on the data. This means that most of the time, it is not blocking.
|
2024-03-08 05:20:29 +00:00
|
|
|
#[derive(Component)]
|
|
|
|
pub struct ResHandle<T: 'static> {
|
2024-01-15 23:07:47 +00:00
|
|
|
pub(crate) data: Arc<RwLock<Resource<T>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Clone for ResHandle<T> {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self { data: self.data.clone() }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-09 05:25:13 +00:00
|
|
|
impl<T: ResourceData> ResHandle<T> {
|
|
|
|
pub fn new_loading(path: Option<&str>) -> Self {
|
|
|
|
let res_version = Resource {
|
|
|
|
version: 0,
|
|
|
|
path: path.map(str::to_string),
|
|
|
|
state: ResourceState::Loading,
|
|
|
|
uuid: Uuid::new_v4(),
|
|
|
|
is_watched: false,
|
|
|
|
_marker: PhantomData::<T>
|
|
|
|
};
|
|
|
|
|
|
|
|
Self {
|
|
|
|
data: Arc::new(RwLock::new(res_version)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-12 18:25:33 +00:00
|
|
|
/// Create the resource with data, its assumed the state is `Ready`
|
2024-03-09 05:25:13 +00:00
|
|
|
pub fn new_ready(path: Option<&str>, data: T) -> Self {
|
2024-01-15 23:07:47 +00:00
|
|
|
let res_version = Resource {
|
|
|
|
version: 0,
|
2024-03-09 05:25:13 +00:00
|
|
|
path: path.map(str::to_string),
|
|
|
|
state: ResourceState::Ready(Box::new(data)),
|
2024-01-15 23:07:47 +00:00
|
|
|
uuid: Uuid::new_v4(),
|
2024-01-16 04:22:21 +00:00
|
|
|
is_watched: false,
|
2024-03-09 05:25:13 +00:00
|
|
|
_marker: PhantomData::<T>
|
2024-01-15 23:07:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Self {
|
|
|
|
data: Arc::new(RwLock::new(res_version)),
|
2023-09-12 18:25:33 +00:00
|
|
|
}
|
|
|
|
}
|
2024-01-15 23:07:47 +00:00
|
|
|
|
2024-01-16 04:22:21 +00:00
|
|
|
/// Returns a boolean indicating if this resource's path is being watched.
|
|
|
|
pub fn is_watched(&self) -> bool {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
|
|
|
d.is_watched
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a boolean indicating if this resource is loaded
|
2024-01-15 23:07:47 +00:00
|
|
|
pub fn is_loaded(&self) -> bool {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
2024-03-09 05:25:13 +00:00
|
|
|
matches!(d.state, ResourceState::Ready(_))
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the current state of the resource.
|
2024-03-09 05:25:13 +00:00
|
|
|
/* pub fn state(&self) -> &ResourceState {
|
2024-01-15 23:07:47 +00:00
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
2024-03-09 05:25:13 +00:00
|
|
|
&d.state
|
|
|
|
} */
|
2024-01-15 23:07:47 +00:00
|
|
|
|
|
|
|
/// Returns the uuid of the resource.
|
|
|
|
pub fn uuid(&self) -> Uuid {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
|
|
|
d.uuid
|
|
|
|
}
|
|
|
|
|
2024-03-09 05:25:13 +00:00
|
|
|
pub fn path(&self) -> Option<String> {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
|
|
|
d.path.clone()
|
|
|
|
}
|
|
|
|
|
2024-01-15 23:07:47 +00:00
|
|
|
/// Retrieves the current version of the resource. This gets incremented when the resource
|
|
|
|
/// is reloaded.
|
|
|
|
pub fn version(&self) -> usize {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
|
|
|
d.version
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the data in the resource
|
2024-03-08 05:20:29 +00:00
|
|
|
pub fn data_ref<'a>(&'a self) -> Option<ResourceDataRef<'a, T>> {
|
2024-01-15 23:07:47 +00:00
|
|
|
if self.is_loaded() {
|
|
|
|
let d = self.data.read().expect("Resource mutex was poisoned!");
|
|
|
|
Some(ResourceDataRef {
|
|
|
|
guard: d
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2024-02-23 21:38:38 +00:00
|
|
|
}
|
2024-01-15 23:07:47 +00:00
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
impl<T: Send + Sync + 'static> ResourceStorage for ResHandle<T> {
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
|
|
self
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
|
|
|
self
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
fn as_arc_any(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
|
|
|
|
self.clone()
|
2024-01-15 23:07:47 +00:00
|
|
|
}
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
fn as_box_any(self: Box<Self>) -> Box<dyn Any + Send + Sync> {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_watched(&self, watched: bool) {
|
|
|
|
let mut w = self.data.write().unwrap();
|
|
|
|
w.is_watched = watched;
|
|
|
|
}
|
|
|
|
|
|
|
|
fn version(&self) -> usize {
|
|
|
|
self.version()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn uuid(&self) -> Uuid {
|
|
|
|
self.uuid()
|
|
|
|
}
|
|
|
|
|
2024-03-09 05:25:13 +00:00
|
|
|
fn path(&self) -> Option<String> {
|
|
|
|
self.path()
|
|
|
|
}
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
fn is_watched(&self) -> bool {
|
|
|
|
self.is_watched()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_loaded(&self) -> bool {
|
|
|
|
self.is_loaded()
|
|
|
|
}
|
2024-03-09 05:25:13 +00:00
|
|
|
|
|
|
|
fn set_state(&self, new: ResourceState) {
|
|
|
|
let mut d = self.data.write().expect("Resource mutex was poisoned!");
|
|
|
|
d.state = new;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-02-23 21:38:38 +00:00
|
|
|
}
|