From a747e3c00dc1d6f987cadc282a03a41cf5cbd358 Mon Sep 17 00:00:00 2001 From: Dmitry Stepanov Date: Tue, 1 Apr 2025 23:14:30 +0300 Subject: [PATCH] tidy up resource header content - moved resource uuid to ResourceState::Ok --- fyrox-graph/src/lib.rs | 6 +- fyrox-impl/src/engine/mod.rs | 2 +- fyrox-impl/src/material/mod.rs | 8 +- fyrox-impl/src/scene/mod.rs | 4 +- fyrox-impl/src/scene/sound/mod.rs | 2 +- fyrox-impl/src/scene/tilemap/mod.rs | 22 +++- fyrox-resource/src/graph.rs | 35 ++---- fyrox-resource/src/lib.rs | 52 ++++---- fyrox-resource/src/manager.rs | 180 ++++++++++------------------ fyrox-resource/src/state.rs | 78 +++++++++--- fyrox-resource/src/untyped.rs | 125 ++++++++----------- fyrox-texture/src/lib.rs | 2 +- 12 files changed, 240 insertions(+), 276 deletions(-) diff --git a/fyrox-graph/src/lib.rs b/fyrox-graph/src/lib.rs index f3eb7dd66..bea1f40e4 100644 --- a/fyrox-graph/src/lib.rs +++ b/fyrox-graph/src/lib.rs @@ -1144,7 +1144,7 @@ pub trait SceneGraph: BaseSceneGraph { let mut nodes_to_delete = Vec::new(); for (_, node) in self.traverse_iter(instance_root) { if let Some(resource) = node.resource() { - let kind = resource.kind().clone(); + let kind = resource.kind(); if let Some(model) = resource.state().data() { if !model .graph() @@ -1182,7 +1182,7 @@ pub trait SceneGraph: BaseSceneGraph { // Step 2. Look for missing nodes and create appropriate instances for them. let mut model = resource.state(); - let model_kind = model.kind().clone(); + let model_kind = model.kind(); if let Some(data) = model.data() { let resource_graph = data.graph(); @@ -1279,7 +1279,7 @@ pub trait SceneGraph: BaseSceneGraph { for node in self.linear_iter_mut() { if let Some(model) = node.resource() { let mut header = model.state(); - let model_kind = header.kind().clone(); + let model_kind = header.kind(); if let Some(data) = header.data() { let resource_graph = data.graph(); diff --git a/fyrox-impl/src/engine/mod.rs b/fyrox-impl/src/engine/mod.rs index 5f49a024e..64d5dc270 100644 --- a/fyrox-impl/src/engine/mod.rs +++ b/fyrox-impl/src/engine/mod.rs @@ -2563,7 +2563,7 @@ impl Engine { let mut state = self.resource_manager.state(); for resource in state.resources().iter() { let data = resource.0.lock(); - if let ResourceState::Ok(ref data) = data.state { + if let ResourceState::Ok { ref data, .. } = data.state { data.as_reflect(&mut |reflect| { if reflect.assembly_name() == plugin_assembly_name { resources_to_reload.insert(resource.clone()); diff --git a/fyrox-impl/src/material/mod.rs b/fyrox-impl/src/material/mod.rs index d99ab938d..61147691a 100644 --- a/fyrox-impl/src/material/mod.rs +++ b/fyrox-impl/src/material/mod.rs @@ -1232,14 +1232,14 @@ impl MaterialResourceExtension for MaterialResource { let material_state = self.header(); let kind = material_state.kind.clone(); match material_state.state { - ResourceState::Pending { .. } => MaterialResource::new_pending(Uuid::new_v4(), kind), + ResourceState::Pending { .. } => MaterialResource::new_pending(kind), ResourceState::LoadError { ref error, .. } => { - MaterialResource::new_load_error(Uuid::new_v4(), kind.clone(), error.clone()) + MaterialResource::new_load_error(kind.clone(), error.clone()) } - ResourceState::Ok(ref material) => MaterialResource::new_ok( + ResourceState::Ok { ref data, .. } => MaterialResource::new_ok( Uuid::new_v4(), kind, - ResourceDataAsAny::as_any(&**material) + ResourceDataAsAny::as_any(&**data) .downcast_ref::() .unwrap() .clone(), diff --git a/fyrox-impl/src/scene/mod.rs b/fyrox-impl/src/scene/mod.rs index e42584852..cb3e224c7 100644 --- a/fyrox-impl/src/scene/mod.rs +++ b/fyrox-impl/src/scene/mod.rs @@ -355,7 +355,9 @@ impl SceneLoader { .filter(|res| { let state = self.resource_manager.state(); let registry = state.resource_registry.lock(); - registry.uuid_to_path(res.resource_uuid()) == Some(&path) + res.resource_uuid() + .and_then(|uuid| registry.uuid_to_path(uuid)) + == Some(&path) }) .cloned() .collect::>(); diff --git a/fyrox-impl/src/scene/sound/mod.rs b/fyrox-impl/src/scene/sound/mod.rs index 5db206927..d531495be 100644 --- a/fyrox-impl/src/scene/sound/mod.rs +++ b/fyrox-impl/src/scene/sound/mod.rs @@ -457,7 +457,7 @@ impl NodeTrait for Sound { Some(buffer) => { let header = buffer.header(); match header.state { - ResourceState::Pending { .. } | ResourceState::Ok(_) => Ok(()), + ResourceState::Pending { .. } | ResourceState::Ok { .. } => Ok(()), ResourceState::LoadError { ref error, .. } => { match &error.0 { None => Err("Sound buffer is failed to load, the reason is unknown!" diff --git a/fyrox-impl/src/scene/tilemap/mod.rs b/fyrox-impl/src/scene/tilemap/mod.rs index 4d2f9b4e4..e14b5317e 100644 --- a/fyrox-impl/src/scene/tilemap/mod.rs +++ b/fyrox-impl/src/scene/tilemap/mod.rs @@ -562,8 +562,12 @@ impl TileBook { pub fn path(&self, registry: &ResourceRegistry) -> Option { match self { TileBook::Empty => None, - TileBook::TileSet(r) => registry.uuid_to_path_buf(r.header().resource_uuid), - TileBook::Brush(r) => registry.uuid_to_path_buf(r.header().resource_uuid), + TileBook::TileSet(r) => r + .resource_uuid() + .and_then(|uuid| registry.uuid_to_path_buf(uuid)), + TileBook::Brush(r) => r + .resource_uuid() + .and_then(|uuid| registry.uuid_to_path_buf(uuid)), } } /// True if the resource is external and its `change_count` is not zero. @@ -585,8 +589,11 @@ impl TileBook { TileBook::Empty => Ok(()), TileBook::TileSet(r) => { if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() { - let result = - r.save(®istry.uuid_to_path_buf(r.header().resource_uuid).unwrap()); + let result = r.save( + ®istry + .uuid_to_path_buf(r.resource_uuid().unwrap()) + .unwrap(), + ); if result.is_ok() { r.data_ref().change_flag.reset(); } @@ -597,8 +604,11 @@ impl TileBook { } TileBook::Brush(r) => { if r.header().kind.is_external() && r.data_ref().change_flag.needs_save() { - let result = - r.save(®istry.uuid_to_path_buf(r.header().resource_uuid).unwrap()); + let result = r.save( + ®istry + .uuid_to_path_buf(r.resource_uuid().unwrap()) + .unwrap(), + ); if result.is_ok() { r.data_ref().change_flag.reset(); } diff --git a/fyrox-resource/src/graph.rs b/fyrox-resource/src/graph.rs index 28e998820..dbcb680f0 100644 --- a/fyrox-resource/src/graph.rs +++ b/fyrox-resource/src/graph.rs @@ -41,8 +41,8 @@ impl ResourceGraphNode { let mut dependent_resources = FxHashSet::default(); let header = resource.0.lock(); - if let ResourceState::Ok(ref resource_data) = header.state { - (**resource_data).as_reflect(&mut |entity| { + if let ResourceState::Ok { ref data, .. } = header.state { + (**data).as_reflect(&mut |entity| { collect_used_resources(entity, &mut dependent_resources); }); } @@ -131,16 +131,9 @@ mod test { #[test] fn resource_graph_node_pretty_print() { let mut s = String::new(); - let mut node = ResourceGraphNode::new(&UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); - let node2 = ResourceGraphNode::new(&UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + let mut node = + ResourceGraphNode::new(&UntypedResource::new_pending(ResourceKind::External)); + let node2 = ResourceGraphNode::new(&UntypedResource::new_pending(ResourceKind::External)); node.children.push(node2); node.pretty_print(1, &mut s); @@ -169,18 +162,13 @@ mod test { #[test] fn resource_dependency_pretty_print() { - let mut graph = ResourceDependencyGraph::new(&UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + let mut graph = + ResourceDependencyGraph::new(&UntypedResource::new_pending(ResourceKind::External)); graph .root .children .push(ResourceGraphNode::new(&UntypedResource::new_pending( - Uuid::new_v4(), ResourceKind::External, - Uuid::default(), ))); let s = graph.pretty_print(); @@ -189,18 +177,13 @@ mod test { #[test] fn resource_dependency_for_each() { - let mut graph = ResourceDependencyGraph::new(&UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + let mut graph = + ResourceDependencyGraph::new(&UntypedResource::new_pending(ResourceKind::External)); graph .root .children .push(ResourceGraphNode::new(&UntypedResource::new_pending( - Uuid::new_v4(), ResourceKind::External, - Uuid::default(), ))); let mut uuids = Vec::new(); diff --git a/fyrox-resource/src/lib.rs b/fyrox-resource/src/lib.rs index a9e6e5e72..7abeedf3a 100644 --- a/fyrox-resource/src/lib.rs +++ b/fyrox-resource/src/lib.rs @@ -125,12 +125,12 @@ impl ResourceHeaderGuard<'_, T> where T: TypedResourceData, { - pub fn kind(&self) -> &ResourceKind { - &self.guard.kind + pub fn kind(&self) -> ResourceKind { + self.guard.kind } pub fn data(&mut self) -> Option<&mut T> { - if let ResourceState::Ok(ref mut data) = self.guard.state { + if let ResourceState::Ok { ref mut data, .. } = self.guard.state { ResourceDataAsAny::as_any_mut(&mut **data).downcast_mut::() } else { None @@ -138,7 +138,7 @@ where } pub fn data_ref(&self) -> Option<&T> { - if let ResourceState::Ok(ref data) = self.guard.state { + if let ResourceState::Ok { ref data, .. } = self.guard.state { ResourceDataAsAny::as_any(&**data).downcast_ref::() } else { None @@ -184,14 +184,15 @@ where let mut untyped = UntypedResource::default(); if untyped.visit(name, visitor).is_ok() { let untyped_data_type = untyped.type_uuid(); - if untyped_data_type == ::type_uuid() { + if untyped_data_type == Some(::type_uuid()) { self.untyped = untyped; return Ok(()); } else { Log::err(format!( "Unable to deserialize untyped resource into its typed \ version, because types do not match! Untyped resource has \ - {untyped_data_type} type, but the required type is {}", + {:?} type, but the required type is {}", + untyped_data_type, ::type_uuid(), )) } @@ -242,13 +243,9 @@ where { /// Creates new resource in pending state. #[inline] - pub fn new_pending(resource_uuid: Uuid, kind: ResourceKind) -> Self { + pub fn new_pending(kind: ResourceKind) -> Self { Self { - untyped: UntypedResource::new_pending( - resource_uuid, - kind, - ::type_uuid(), - ), + untyped: UntypedResource::new_pending(kind), phantom: PhantomData, } } @@ -264,14 +261,9 @@ where /// Creates new resource in error state. #[inline] - pub fn new_load_error(resource_uuid: Uuid, kind: ResourceKind, error: LoadError) -> Self { + pub fn new_load_error(kind: ResourceKind, error: LoadError) -> Self { Self { - untyped: UntypedResource::new_load_error( - resource_uuid, - kind, - error, - ::type_uuid(), - ), + untyped: UntypedResource::new_load_error(kind, error), phantom: PhantomData, } } @@ -315,7 +307,7 @@ where /// Returns true if the resource is fully loaded and ready for use. #[inline] pub fn is_ok(&self) -> bool { - matches!(self.untyped.0.lock().state, ResourceState::Ok(_)) + matches!(self.untyped.0.lock().state, ResourceState::Ok { .. }) } /// Returns true if the resource is failed to load. @@ -342,6 +334,11 @@ where self.untyped.kind() } + #[inline] + pub fn resource_uuid(&self) -> Option { + self.untyped.resource_uuid() + } + /// Sets a new kind of the resource. #[inline] pub fn set_path(&mut self, new_kind: ResourceKind) { @@ -407,7 +404,10 @@ where { #[inline] fn from(untyped: UntypedResource) -> Self { - assert_eq!(untyped.type_uuid(), ::type_uuid()); + assert_eq!( + untyped.type_uuid(), + Some(::type_uuid()) + ); Self { untyped, phantom: Default::default(), @@ -456,7 +456,7 @@ where #[inline] pub fn as_loaded_ref(&self) -> Option<&T> { match self.guard.state { - ResourceState::Ok(ref data) => ResourceDataAsAny::as_any(&**data).downcast_ref(), + ResourceState::Ok { ref data, .. } => ResourceDataAsAny::as_any(&**data).downcast_ref(), _ => None, } } @@ -464,7 +464,7 @@ where #[inline] pub fn as_loaded_mut(&mut self) -> Option<&mut T> { match self.guard.state { - ResourceState::Ok(ref mut data) => { + ResourceState::Ok { ref mut data, .. } => { ResourceDataAsAny::as_any_mut(&mut **data).downcast_mut() } _ => None, @@ -492,7 +492,7 @@ where self.guard.kind ) } - ResourceState::Ok(ref data) => data.fmt(f), + ResourceState::Ok { ref data, .. } => data.fmt(f), } } } @@ -517,7 +517,7 @@ where self.guard.kind ) } - ResourceState::Ok(ref data) => ResourceDataAsAny::as_any(&**data) + ResourceState::Ok { ref data, .. } => ResourceDataAsAny::as_any(&**data) .downcast_ref() .expect("Type mismatch!"), } @@ -543,7 +543,7 @@ where header.kind ) } - ResourceState::Ok(ref mut data) => ResourceDataAsAny::as_any_mut(&mut **data) + ResourceState::Ok { ref mut data, .. } => ResourceDataAsAny::as_any_mut(&mut **data) .downcast_mut() .expect("Type mismatch!"), } diff --git a/fyrox-resource/src/manager.rs b/fyrox-resource/src/manager.rs index 674abf5e7..f8228269f 100644 --- a/fyrox-resource/src/manager.rs +++ b/fyrox-resource/src/manager.rs @@ -32,7 +32,6 @@ use crate::{ parking_lot::{Mutex, MutexGuard}, task::TaskPool, watcher::FileSystemWatcher, - TypeUuidProvider, }, entry::{TimedEntry, DEFAULT_RESOURCE_LIFETIME}, event::{ResourceEvent, ResourceEventBroadcaster}, @@ -327,11 +326,6 @@ impl ResourceManager { /// Actual resource state can be fetched by [`Resource::state`] method. If you know for sure that the resource /// is already loaded, then you can use [`Resource::data_ref`] to obtain a reference to the actual resource data. /// Keep in mind, that this method will panic if the resource non in `Ok` state. - /// - /// ## Panic - /// - /// This method will panic, if type UUID of `T` does not match the actual type UUID of the resource. If this - /// is undesirable, use [`Self::try_request`] instead. pub fn request(&self, path: impl AsRef) -> Resource where T: TypedResourceData, @@ -342,28 +336,6 @@ impl ResourceManager { } } - /// The same as [`Self::request`], but returns [`None`] if type UUID of `T` does not match the actual type UUID - /// of the resource. - /// - /// ## Panic - /// - /// This method does not panic. - pub fn try_request(&self, path: impl AsRef) -> Option> - where - T: TypedResourceData, - { - let untyped = self.state().request(path); - let actual_type_uuid = untyped.type_uuid(); - if actual_type_uuid == ::type_uuid() { - Some(Resource { - untyped, - phantom: PhantomData::, - }) - } else { - None - } - } - /// Same as [`Self::request`], but returns untyped resource. pub fn request_untyped

(&self, path: P) -> UntypedResource where @@ -389,12 +361,14 @@ impl ResourceManager { F: FnMut(&mut dyn ResourceData, &Path) -> bool, { let path = path.as_ref().to_owned(); - let resource_uuid = resource.resource_uuid(); + let resource_uuid = resource + .resource_uuid() + .ok_or(ResourceRegistrationError::InvalidState)?; let mut state = self.state(); if let Some(resource) = state.find(resource_uuid) { let resource_state = resource.0.lock(); - if let ResourceState::Ok(_) = resource_state.state { + if let ResourceState::Ok { .. } = resource_state.state { return Err(ResourceRegistrationError::AlreadyRegistered); } } @@ -403,14 +377,16 @@ impl ResourceManager { let mut header = resource.0.lock(); header.kind.make_external(); - if let ResourceState::Ok(ref mut data) = header.state { + if let ResourceState::Ok { + ref mut data, + resource_uuid, + .. + } = header.state + { if !on_register(&mut **data, path.as_ref()) { Err(ResourceRegistrationError::UnableToRegister) } else { - state - .resource_registry - .lock() - .register(header.resource_uuid, path); + state.resource_registry.lock().register(resource_uuid, path); drop(header); state.push(resource); Ok(()) @@ -426,12 +402,16 @@ impl ResourceManager { resource: UntypedResource, new_path: impl AsRef, ) -> Result<(), FileError> { + let resource_uuid = resource + .resource_uuid() + .ok_or_else(|| FileError::Custom("Unable to move non-loaded resource!".to_string()))?; + let new_path = new_path.as_ref().to_owned(); let io = self.state().resource_io.clone(); let registry = self.state().resource_registry.clone(); let existing_path = registry .lock() - .uuid_to_path(resource.resource_uuid()) + .uuid_to_path(resource_uuid) .map(|path| path.to_path_buf()) .ok_or_else(|| FileError::Custom("Cannot move embedded resource!".to_string()))?; @@ -604,10 +584,10 @@ impl ResourceManagerState { if resource.value.use_count() <= 1 { resource.time_to_live -= dt; if resource.time_to_live <= 0.0 { - if let Some(path) = self - .resource_registry - .lock() - .uuid_to_path(resource.0.lock().resource_uuid) + let registry = self.resource_registry.lock(); + let resource_uuid = resource.resource_uuid(); + if let Some(path) = + resource_uuid.and_then(|resource_uuid| registry.uuid_to_path(resource_uuid)) { Log::info(format!( "Resource {} destroyed because it is not used anymore!", @@ -671,16 +651,21 @@ impl ResourceManagerState { pub fn find(&self, uuid: Uuid) -> Option<&UntypedResource> { self.resources .iter() - .find(|entry| entry.value.resource_uuid() == uuid) + .find(|entry| entry.value.resource_uuid() == Some(uuid)) .map(|entry| &entry.value) } pub fn find_by_path(&self, path: &Path) -> Option<&UntypedResource> { let registry = self.resource_registry.lock(); - self.resources - .iter() - .find(|entry| registry.uuid_to_path(entry.value.resource_uuid()) == Some(path)) - .map(|entry| &entry.value) + self.resources.iter().find_map(|entry| { + let header = entry.value.0.lock(); + if let ResourceState::Ok { resource_uuid, .. } = header.state { + if registry.uuid_to_path(resource_uuid) == Some(path) { + return Some(&entry.value); + } + } + None + }) } /// Returns total amount of resources in the container. @@ -718,7 +703,7 @@ impl ResourceManagerState { /// Returns total amount of completely loaded resources. pub fn count_loaded_resources(&self) -> usize { self.resources.iter().fold(0, |counter, resource| { - if let ResourceState::Ok(_) = resource.0.lock().state { + if let ResourceState::Ok { .. } = resource.0.lock().state { counter + 1 } else { counter @@ -743,11 +728,13 @@ impl ResourceManagerState { self.find_or_load(ResourcePath::Explicit(path.as_ref().to_path_buf())) } + /// Tries to load a resource by a unique identifier. The identifier must not be a zero-uuid, + /// otherwise this method will panic! pub fn request_by_uuid(&mut self, resource_uuid: Uuid) -> UntypedResource { if let Some(built_in_resource) = self .built_in_resources .values() - .find(|r| r.resource.resource_uuid() == resource_uuid) + .find(|r| r.resource.resource_uuid() == Some(resource_uuid)) { return built_in_resource.resource.clone(); } @@ -763,14 +750,12 @@ impl ResourceManagerState { match header.state { ResourceState::Pending { ref path, .. } | ResourceState::LoadError { ref path, .. } => path == path_to_search, - ResourceState::Ok(_) => match path_to_search { + ResourceState::Ok { resource_uuid, .. } => match path_to_search { ResourcePath::Explicit(fs_path) => { - self.resource_registry - .lock() - .uuid_to_path(header.resource_uuid) + self.resource_registry.lock().uuid_to_path(resource_uuid) == Some(fs_path) } - ResourcePath::Implicit(uuid) => &header.resource_uuid == uuid, + ResourcePath::Implicit(uuid) => &resource_uuid == uuid, }, } }) @@ -781,8 +766,7 @@ impl ResourceManagerState { match self.find_by_resource_path(&path) { Some(existing) => existing.clone(), None => { - let resource = - UntypedResource::new_pending(Uuid::nil(), ResourceKind::External, Uuid::nil()); + let resource = UntypedResource::new_pending(ResourceKind::External); self.spawn_loading_task(path, resource.clone(), false); self.push(resource.clone()); resource @@ -791,6 +775,10 @@ impl ResourceManagerState { } fn spawn_loading_task(&self, path: ResourcePath, resource: UntypedResource, reload: bool) { + if let ResourcePath::Implicit(ref uuid) = path { + assert_ne!(*uuid, Uuid::nil()); + } + let event_broadcaster = self.event_broadcaster.clone(); let loaders = self.loaders.clone(); let registry = self.resource_registry.clone(); @@ -843,11 +831,12 @@ impl ResourceManagerState { // Separate scope to keep mutex locking time at minimum. { let mut mutex_guard = resource.0.lock(); - mutex_guard.resource_uuid = - registry.lock().path_to_uuid(&fs_path).unwrap(); - mutex_guard.type_uuid = data.type_uuid(); + let resource_uuid = registry.lock().path_to_uuid(&fs_path).unwrap(); assert!(mutex_guard.kind.is_external()); - mutex_guard.state.commit(ResourceState::Ok(data)); + mutex_guard.state.commit(ResourceState::Ok { + data, + resource_uuid, + }); } event_broadcaster.broadcast_loaded_or_reloaded(resource, reload); @@ -886,8 +875,8 @@ impl ResourceManagerState { drop(header); self.spawn_loading_task(path, resource, true) } - ResourceState::Ok(_) => { - let path = ResourcePath::Implicit(header.resource_uuid); + ResourceState::Ok { resource_uuid, .. } => { + let path = ResourcePath::Implicit(resource_uuid); drop(header); self.spawn_loading_task(path, resource, true) } @@ -941,7 +930,7 @@ impl ResourceManagerState { if let Some(position) = self .resources .iter() - .position(|entry| entry.value.resource_uuid() == uuid) + .position(|entry| entry.value.resource_uuid() == Some(uuid)) { self.resources.remove(position); } @@ -1012,17 +1001,8 @@ mod test { let cx = ResourceWaitContext { resources: vec![ - UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - ), - UntypedResource::new_load_error( - Uuid::new_v4(), - ResourceKind::External, - Default::default(), - Uuid::default(), - ), + UntypedResource::new_pending(ResourceKind::External), + UntypedResource::new_load_error(ResourceKind::External, Default::default()), ], }; assert!(!cx.is_all_loaded()); @@ -1062,16 +1042,10 @@ mod test { assert_eq!(state.count_registered_resources(), 0); assert_eq!(state.len(), 0); - state.push(UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + state.push(UntypedResource::new_pending(ResourceKind::External)); state.push(UntypedResource::new_load_error( - Uuid::new_v4(), ResourceKind::External, Default::default(), - Uuid::default(), )); state.push(UntypedResource::new_ok( Uuid::new_v4(), @@ -1091,16 +1065,10 @@ mod test { assert_eq!(state.loading_progress(), 100); - state.push(UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + state.push(UntypedResource::new_pending(ResourceKind::External)); state.push(UntypedResource::new_load_error( - Uuid::new_v4(), ResourceKind::External, Default::default(), - Uuid::default(), )); state.push(UntypedResource::new_ok( Uuid::new_v4(), @@ -1118,9 +1086,7 @@ mod test { assert!(state.find_by_path(Path::new("foo.txt")).is_none()); let path = PathBuf::from("test.txt"); - let type_uuid = Uuid::default(); - let resource = - UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, type_uuid); + let resource = UntypedResource::new_pending(ResourceKind::External); state.push(resource.clone()); assert_eq!(state.find_by_path(&path), Some(&resource)); @@ -1132,14 +1098,8 @@ mod test { assert_eq!(state.resources(), Vec::new()); - let r1 = - UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, Uuid::default()); - let r2 = UntypedResource::new_load_error( - Uuid::new_v4(), - ResourceKind::External, - Default::default(), - Uuid::default(), - ); + let r1 = UntypedResource::new_pending(ResourceKind::External); + let r2 = UntypedResource::new_load_error(ResourceKind::External, Default::default()); let r3 = UntypedResource::new_ok(Uuid::new_v4(), ResourceKind::Embedded, Stub {}); state.push(r1.clone()); state.push(r2.clone()); @@ -1153,11 +1113,7 @@ mod test { fn resource_manager_state_destroy_unused_resources() { let mut state = new_resource_manager(); - state.push(UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::default(), - )); + state.push(UntypedResource::new_pending(ResourceKind::External)); assert_eq!(state.len(), 1); state.destroy_unused_resources(); @@ -1168,14 +1124,8 @@ mod test { fn resource_manager_state_request() { let mut state = new_resource_manager(); let path = PathBuf::from("test.txt"); - let type_uuid = Uuid::default(); - let resource = UntypedResource::new_load_error( - Uuid::new_v4(), - ResourceKind::External, - Default::default(), - type_uuid, - ); + let resource = UntypedResource::new_load_error(ResourceKind::External, Default::default()); state.push(resource.clone()); let res = state.request(path); @@ -1185,7 +1135,6 @@ mod test { let res = state.request(path.clone()); assert_eq!(res.kind(), ResourceKind::External); - assert_eq!(res.type_uuid(), type_uuid); assert!(!res.is_loading()); } @@ -1194,12 +1143,7 @@ mod test { let mut state = new_resource_manager(); state.loaders.lock().set(Stub {}); - let resource = UntypedResource::new_load_error( - Uuid::new_v4(), - ResourceKind::External, - Default::default(), - Uuid::default(), - ); + let resource = UntypedResource::new_load_error(ResourceKind::External, Default::default()); state.push(resource.clone()); assert!(!state.try_reload_resource_from_path(Path::new("foo.txt"))); @@ -1231,10 +1175,8 @@ mod test { fn resource_manager_register() { let manager = ResourceManager::new(Arc::new(Default::default())); let path = PathBuf::from("test.txt"); - let type_uuid = Uuid::default(); - let resource = - UntypedResource::new_pending(Uuid::new_v4(), ResourceKind::External, type_uuid); + let resource = UntypedResource::new_pending(ResourceKind::External); let res = manager.register(resource.clone(), path.clone(), |_, _| true); assert!(res.is_err()); diff --git a/fyrox-resource/src/state.rs b/fyrox-resource/src/state.rs index 8dbf9f6f0..f5fc3c59f 100644 --- a/fyrox-resource/src/state.rs +++ b/fyrox-resource/src/state.rs @@ -20,11 +20,13 @@ //! A module that handles resource states. +use crate::untyped::ResourceKind; use crate::{ core::{reflect::prelude::*, uuid::Uuid, visitor::prelude::*}, manager::ResourceManager, ResourceData, ResourceLoadError, }; +use fyrox_core::warn; use std::path::PathBuf; use std::{ ops::{Deref, DerefMut}, @@ -108,6 +110,13 @@ impl Default for ResourcePath { /// Resources do not store their paths to respective files in the file system, instead resource only /// stores their unique identifiers (UUID). Use [`crate::registry::ResourceRegistry`] to get a path /// associated with the resource uuid. +/// +/// ## UUID +/// +/// Resource UUID is available only if the resource is fully loaded. This is because there's no way +/// to get the UUID earlier: the UUID is stored in a metadata file which exists only if the resource +/// is present. It is somewhat possible to get a UUID when a resource is failed to load, but not in +/// 100% cases. #[derive(Debug, Reflect)] pub enum ResourceState { /// Resource is loading from external resource or in the queue to load. @@ -130,7 +139,12 @@ pub enum ResourceState { error: LoadError, }, /// Actual resource data when it is fully loaded. - Ok(Box), + Ok { + /// Unique id of the resource. + resource_uuid: Uuid, + /// Actual data of the resource. + data: Box, + }, } impl Default for ResourceState { @@ -150,12 +164,20 @@ impl Drop for ResourceState { } } -impl Visit for ResourceState { - fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult { +impl ResourceState { + pub fn visit(&mut self, kind: ResourceKind, name: &str, visitor: &mut Visitor) -> VisitResult { if visitor.is_reading() { let mut type_uuid = Uuid::default(); type_uuid.visit("TypeUuid", visitor)?; + let mut resource_uuid = Uuid::default(); + if resource_uuid.visit("ResourceUuid", visitor).is_err() { + warn!( + "A resource of type {type_uuid} has no uuid! It looks like a resource in \ + the old format; trying to read it..." + ); + } + let resource_manager = visitor.blackboard.get::().expect( "Resource data constructor container must be \ provided when serializing resources!", @@ -167,25 +189,43 @@ impl Visit for ResourceState { .try_create(&type_uuid) { drop(resource_manager_state); - instance.visit(name, visitor)?; - *self = Self::Ok(instance); - Ok(()) + + if kind == ResourceKind::Embedded { + instance.visit(name, visitor)?; + } + + *self = Self::Ok { + resource_uuid, + data: instance, + }; } else { - Err(VisitError::User(format!( + return Err(VisitError::User(format!( "There's no constructor registered for type {type_uuid}!" - ))) + ))); } - } else if let Self::Ok(instance) = self { - instance.visit(name, visitor)?; + + Ok(()) + } else if let Self::Ok { + resource_uuid, + data, + } = self + { + resource_uuid.visit("ResourceUuid", visitor)?; + + let mut type_uuid = data.type_uuid(); + type_uuid.visit("TypeUuid", visitor)?; + + if kind == ResourceKind::Embedded { + data.visit(name, visitor)?; + } + Ok(()) } else { // Do not save other variants, because they're needed only for runtime purposes. Ok(()) } } -} -impl ResourceState { /// Creates new resource in pending state. #[inline] pub fn new_pending() -> Self { @@ -203,8 +243,11 @@ impl ResourceState { /// Creates new resource in ok (resource with data) state. #[inline] - pub fn new_ok(data: T) -> Self { - Self::Ok(Box::new(data)) + pub fn new_ok(resource_uuid: Uuid, data: T) -> Self { + Self::Ok { + resource_uuid, + data: Box::new(data), + } } /// Checks whether the resource is still loading or not. @@ -240,8 +283,11 @@ impl ResourceState { } /// Changes internal state to [`ResourceState::Ok`] - pub fn commit_ok(&mut self, data: T) { - self.commit(ResourceState::Ok(Box::new(data))) + pub fn commit_ok(&mut self, resource_uuid: Uuid, data: T) { + self.commit(ResourceState::Ok { + resource_uuid, + data: Box::new(data), + }) } /// Changes internal state to [`ResourceState::LoadError`]. diff --git a/fyrox-resource/src/untyped.rs b/fyrox-resource/src/untyped.rs index 93559396e..ef23aad62 100644 --- a/fyrox-resource/src/untyped.rs +++ b/fyrox-resource/src/untyped.rs @@ -87,7 +87,7 @@ fn guess_uuid(region: &mut RegionGuard) -> Uuid { } /// Kind of a resource. It defines how the resource manager will treat a resource content on serialization. -#[derive(Default, Reflect, Debug, Visit, Clone, PartialEq, Eq, Hash)] +#[derive(Default, Reflect, Debug, Visit, Copy, Clone, PartialEq, Eq, Hash)] pub enum ResourceKind { /// The content of embedded resources will be fully serialized. #[default] @@ -155,10 +155,6 @@ impl Display for ResourceKind { /// its kind, etc. #[derive(Reflect, Debug)] pub struct ResourceHeader { - /// Unique id of a resource. - pub resource_uuid: Uuid, - /// UUID of the internal data type. - pub type_uuid: Uuid, /// Kind of the resource. See [`ResourceKind`] for more info. pub kind: ResourceKind, /// Actual state of the resource. See [`ResourceState`] for more info. @@ -216,7 +212,11 @@ impl Visit for ResourceHeader { instance.visit("Details", &mut region)?; - self.state = ResourceState::Ok(instance); + self.state = ResourceState::Ok { + data: instance, + // The old format does not contain an uuid. + resource_uuid: Uuid::nil(), + }; return Ok(()); } else { @@ -232,27 +232,18 @@ impl Visit for ResourceHeader { } return Ok(()); - } - } - - if region.is_reading() { - let mut old_kind = OldResourceKind::Embedded; - if old_kind.visit("Kind", &mut region).is_ok() { - if let OldResourceKind::External(path) = old_kind { - self.old_format_path = Some(path); + } else { + let mut old_kind = OldResourceKind::Embedded; + if old_kind.visit("Kind", &mut region).is_ok() { + if let OldResourceKind::External(path) = old_kind { + self.old_format_path = Some(path); + } } } } self.kind.visit("Kind", &mut region)?; - self.type_uuid.visit("TypeUuid", &mut region)?; - - // Backward compatibility. - let _ = self.resource_uuid.visit("ResourceUuid", &mut region); - - if self.kind == ResourceKind::Embedded { - self.state.visit("State", &mut region)?; - } + self.state.visit(self.kind, "State", &mut region)?; Ok(()) } @@ -306,20 +297,21 @@ impl Visit for UntypedResource { self.0.visit(name, visitor)?; // Try to restore the shallow handle on deserialization for external resources. - if visitor.is_reading() && !self.is_embedded() { - let resource_manager = visitor - .blackboard - .get::() - .expect("Resource manager must be available when deserializing resources!"); - + if visitor.is_reading() { let inner_lock = self.0.lock(); - let resource_uuid = inner_lock.resource_uuid; - if let Some(old_format_path) = inner_lock.old_format_path.clone() { - drop(inner_lock); - self.0 = resource_manager.request_untyped(old_format_path).0; - } else { - drop(inner_lock); - self.0 = resource_manager.request_by_uuid(resource_uuid).0; + if inner_lock.kind == ResourceKind::External { + let resource_manager = visitor + .blackboard + .get::() + .expect("Resource manager must be available when deserializing resources!"); + + if let Some(old_format_path) = inner_lock.old_format_path.clone() { + drop(inner_lock); + self.0 = resource_manager.request_untyped(old_format_path).0; + } else if let ResourceState::Ok { resource_uuid, .. } = inner_lock.state { + drop(inner_lock); + self.0 = resource_manager.request_by_uuid(resource_uuid).0; + } } } @@ -330,9 +322,7 @@ impl Visit for UntypedResource { impl Default for UntypedResource { fn default() -> Self { Self(Arc::new(Mutex::new(ResourceHeader { - resource_uuid: Default::default(), kind: Default::default(), - type_uuid: Default::default(), state: ResourceState::new_load_error( Default::default(), LoadError::new("Default resource state of unknown type."), @@ -364,11 +354,9 @@ impl Hash for UntypedResource { impl UntypedResource { /// Creates new untyped resource in pending state using the given path and type uuid. - pub fn new_pending(resource_uuid: Uuid, kind: ResourceKind, type_uuid: Uuid) -> Self { + pub fn new_pending(kind: ResourceKind) -> Self { Self(Arc::new(Mutex::new(ResourceHeader { - resource_uuid, kind, - type_uuid, state: ResourceState::new_pending(), old_format_path: None, }))) @@ -381,37 +369,36 @@ impl UntypedResource { T: ResourceData, { Self(Arc::new(Mutex::new(ResourceHeader { - resource_uuid, kind, - type_uuid: data.type_uuid(), - state: ResourceState::new_ok(data), + state: ResourceState::new_ok(resource_uuid, data), old_format_path: None, }))) } /// Creates new untyped resource in error state. - pub fn new_load_error( - resource_uuid: Uuid, - kind: ResourceKind, - error: LoadError, - type_uuid: Uuid, - ) -> Self { + pub fn new_load_error(kind: ResourceKind, error: LoadError) -> Self { Self(Arc::new(Mutex::new(ResourceHeader { - resource_uuid, kind, - type_uuid, state: ResourceState::new_load_error(Default::default(), error), old_format_path: None, }))) } - pub fn resource_uuid(&self) -> Uuid { - self.0.lock().resource_uuid + pub fn resource_uuid(&self) -> Option { + let header = self.0.lock(); + match header.state { + ResourceState::Ok { resource_uuid, .. } => Some(resource_uuid), + _ => None, + } } /// Returns actual unique type id of underlying resource data. - pub fn type_uuid(&self) -> Uuid { - self.0.lock().type_uuid + pub fn type_uuid(&self) -> Option { + let header = self.0.lock(); + match header.state { + ResourceState::Ok { ref data, .. } => Some(data.type_uuid()), + _ => None, + } } /// Returns true if the resource is still loading. @@ -439,7 +426,7 @@ impl UntypedResource { /// Returns path of the untyped resource. pub fn kind(&self) -> ResourceKind { - self.0.lock().kind.clone() + self.0.lock().kind } /// Set a new path for the untyped resource. @@ -454,7 +441,7 @@ impl UntypedResource { ResourceState::Pending { .. } | ResourceState::LoadError { .. } => { Err("Unable to save unloaded resource!".into()) } - ResourceState::Ok(ref mut data) => data.save(path), + ResourceState::Ok { ref mut data, .. } => data.save(path), } } @@ -463,7 +450,7 @@ impl UntypedResource { where T: TypedResourceData, { - if self.type_uuid() == ::type_uuid() { + if self.type_uuid() == Some(::type_uuid()) { Some(Resource { untyped: self.clone(), phantom: PhantomData::, @@ -481,10 +468,9 @@ impl UntypedResource { } /// Changes internal state to [`ResourceState::Ok`] - pub fn commit_ok(&self, data: T) { + pub fn commit_ok(&self, resource_uuid: Uuid, data: T) { let mut guard = self.0.lock(); - guard.type_uuid = data.type_uuid(); - guard.state.commit_ok(data); + guard.state.commit_ok(resource_uuid, data); } /// Changes internal state to [`ResourceState::LoadError`]. @@ -505,7 +491,7 @@ impl Future for UntypedResource { Poll::Pending } ResourceState::LoadError { ref error, .. } => Poll::Ready(Err(error.clone())), - ResourceState::Ok(_) => Poll::Ready(Ok(self.clone())), + ResourceState::Ok { .. } => Poll::Ready(Ok(self.clone())), } } } @@ -569,11 +555,7 @@ mod test { #[test] fn untyped_resource_try_cast() { let r = UntypedResource::default(); - let r2 = UntypedResource::new_pending( - Uuid::new_v4(), - ResourceKind::External, - Uuid::from_u128(0xa1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8u128), - ); + let r2 = UntypedResource::new_pending(ResourceKind::External); assert!(r.try_cast::().is_some()); assert!(r2.try_cast::().is_none()); @@ -587,18 +569,17 @@ mod test { let mut cx = task::Context::from_waker(&waker); let mut r = UntypedResource(Arc::new(Mutex::new(ResourceHeader { - resource_uuid: Default::default(), kind: ResourceKind::External, - type_uuid: Uuid::default(), - state: ResourceState::Ok(Box::new(stub)), + state: ResourceState::Ok { + data: Box::new(stub), + resource_uuid: Uuid::new_v4(), + }, old_format_path: None, }))); assert!(Pin::new(&mut r).poll(&mut cx).is_ready()); let mut r = UntypedResource(Arc::new(Mutex::new(ResourceHeader { - resource_uuid: Default::default(), kind: ResourceKind::External, - type_uuid: Uuid::default(), state: ResourceState::LoadError { path: Default::default(), error: Default::default(), diff --git a/fyrox-texture/src/lib.rs b/fyrox-texture/src/lib.rs index 46e106b5d..a63367225 100644 --- a/fyrox-texture/src/lib.rs +++ b/fyrox-texture/src/lib.rs @@ -790,7 +790,7 @@ impl TextureResourceExtension for TextureResource { } fn deep_clone(&self) -> Self { - let kind = self.header().kind.clone(); + let kind = self.header().kind; let data = self.data_ref().clone(); Resource::new_ok(Uuid::new_v4(), kind, data) }