From 929c6c6ae9f715f573272690c72ea3a1582d8e31 Mon Sep 17 00:00:00 2001 From: b-guild <148972828+b-guild@users.noreply.github.com> Date: Mon, 13 Oct 2025 02:49:27 -0700 Subject: [PATCH 1/5] Improved load error reporting for FormattedText --- fyrox-ui/src/formatted_text.rs | 53 +++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 4 deletions(-) diff --git a/fyrox-ui/src/formatted_text.rs b/fyrox-ui/src/formatted_text.rs index 74734a25e..49d7e105a 100644 --- a/fyrox-ui/src/formatted_text.rs +++ b/fyrox-ui/src/formatted_text.rs @@ -29,9 +29,12 @@ use crate::{ HorizontalAlignment, VerticalAlignment, }; use fyrox_core::log::Log; -use fyrox_resource::state::LoadError; +use fyrox_resource::state::{LoadError, ResourceState}; pub use run::*; -use std::ops::{Range, RangeBounds}; +use std::{ + ops::{Range, RangeBounds}, + path::PathBuf, +}; use strum_macros::{AsRefStr, EnumString, VariantNames}; use textwrapper::*; @@ -804,14 +807,56 @@ impl FormattedText { true } + pub fn are_fonts_loading(&self) -> bool { + if !self.font.is_loading() { + return true; + } + for run in self.runs.iter() { + if let Some(font) = run.font() { + if !font.is_loading() { + return true; + } + } + } + false + } + + pub fn font_load_error_list(&self) -> Vec<(PathBuf, LoadError)> { + let mut list = vec![]; + if let ResourceState::LoadError { path, error } = &self.font.header().state { + list.push((path.clone(), error.clone())); + } + for run in self.runs.iter() { + if let Some(font) = run.font() { + if let ResourceState::LoadError { path, error } = &font.header().state { + list.push((path.clone(), error.clone())); + } + } + } + list + } + + pub fn font_loading_summary(&self) -> String { + use std::fmt::Write; + let mut result = String::default(); + writeln!(result, "Primary font: {:?}", self.font.header().state).unwrap(); + for run in self.runs.iter() { + if let Some(font) = run.font() { + writeln!(result, "Run {:?}: {:?}", run.range, font.header().state).unwrap(); + } + } + result + } + pub fn build(&mut self) -> Vector2 { let mut lines = std::mem::take(&mut self.lines); lines.clear(); // Fail early if any font is not available. if !self.are_fonts_loaded() { Log::err(format!( - "Text failed to build due to unloaded fonts. {:?}", - self.text() + "Text failed to build due to unloaded fonts. {:?}.\n{}", + self.text(), + self.font_loading_summary(), )); return Vector2::default(); } From 75b873d72bc869a0ab1de4135a92378ed84cf5bf Mon Sep 17 00:00:00 2001 From: b-guild <148972828+b-guild@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:09:09 -0700 Subject: [PATCH 2/5] Making resource handles UUID-based instead of path-based. --- editor/src/asset/preview/cache.rs | 2 +- editor/src/asset/selection.rs | 8 +- editor/src/asset/selector.rs | 2 +- editor/src/lib.rs | 16 + editor/src/plugins/material/editor.rs | 4 +- fyrox-core/src/visitor/mod.rs | 10 + fyrox-graph/src/lib.rs | 3 + fyrox-impl/src/engine/executor.rs | 3 + fyrox-impl/src/engine/mod.rs | 72 +- fyrox-impl/src/renderer/cache/texture.rs | 10 +- fyrox-impl/src/resource/model/mod.rs | 16 +- fyrox-impl/src/scene/base.rs | 6 +- fyrox-impl/src/scene/dim2/physics.rs | 6 +- fyrox-impl/src/scene/graph/mod.rs | 50 +- fyrox-impl/src/scene/graph/physics.rs | 6 +- fyrox-impl/src/scene/mesh/mod.rs | 7 +- fyrox-impl/src/scene/mesh/surface.rs | 6 +- fyrox-impl/src/scene/mod.rs | 7 +- fyrox-impl/src/scene/node/mod.rs | 2 +- fyrox-impl/src/scene/sound/mod.rs | 5 +- fyrox-material/src/lib.rs | 22 +- fyrox-resource/src/builtin.rs | 11 +- fyrox-resource/src/graph.rs | 9 +- fyrox-resource/src/lib.rs | 145 +-- fyrox-resource/src/manager.rs | 1095 +++++++++++++++------- fyrox-resource/src/registry.rs | 202 ++-- fyrox-resource/src/state.rs | 217 ++--- fyrox-resource/src/untyped.rs | 741 ++++++++++----- fyrox-sound/src/buffer/generic.rs | 3 + fyrox-ui/src/file_browser/mod.rs | 10 +- fyrox-ui/src/formatted_text.rs | 4 +- fyrox-ui/src/lib.rs | 14 +- 32 files changed, 1709 insertions(+), 1005 deletions(-) diff --git a/editor/src/asset/preview/cache.rs b/editor/src/asset/preview/cache.rs index a2023aa70..de05a3656 100644 --- a/editor/src/asset/preview/cache.rs +++ b/editor/src/asset/preview/cache.rs @@ -77,7 +77,7 @@ impl AssetPreviewCache { generated_counter: &mut usize, engine: &mut Engine, ) -> Option { - let resource_uuid = resource.resource_uuid()?; + let resource_uuid = resource.resource_uuid(); if let (false, Some(cached_preview)) = (force_update, self.container.get(&resource_uuid)) { return Some(cached_preview.clone()); diff --git a/editor/src/asset/selection.rs b/editor/src/asset/selection.rs index b91409086..0986c4814 100644 --- a/editor/src/asset/selection.rs +++ b/editor/src/asset/selection.rs @@ -59,7 +59,7 @@ struct SaveResourceCommand { impl SaveResourceCommand { fn save(&self) { - let mut guard = self.resource.0.safe_lock(); + let mut guard = self.resource.lock(); if let Some(data) = guard.state.data_mut() { Log::verify(data.save(&self.path)); } @@ -163,7 +163,7 @@ impl SelectionContainer for AssetSelection { block_on(self.resource_manager.request_untyped(&resource.path)) { if !self.resource_manager.is_built_in_resource(&resource) { - let guard = resource.0.safe_lock(); + let guard = resource.lock(); if let Some(data) = guard.state.data_ref() { callback(&*data.0 as &dyn Reflect, false) } @@ -205,7 +205,7 @@ impl SelectionContainer for AssetSelection { let resource2 = resource.clone(); if !self.resource_manager.is_built_in_resource(&resource) { if let Some(command) = make_command(args, move |_| { - let mut guard = resource.0.safe_lock(); + let mut guard = resource.lock(); let data = &mut **guard.state.data_mut()?; // SAFETY: This is safe, because the closure owns its own copy of // resource strong ref, and the entity getter is used only once per @@ -264,7 +264,7 @@ impl SelectionContainer for AssetSelection { path.to_string(), value, move |_| { - let mut guard = resource.0.safe_lock(); + let mut guard = resource.lock(); let data = &mut **guard.state.data_mut()?; // SAFETY: This is safe, because the closure owns its own copy of // resource strong ref, and the entity getter is used only once per diff --git a/editor/src/asset/selector.rs b/editor/src/asset/selector.rs index 223ff8994..00579fe8c 100644 --- a/editor/src/asset/selector.rs +++ b/editor/src/asset/selector.rs @@ -337,7 +337,7 @@ impl<'a> AssetSelectorBuilder<'a> { .collect::>(); supported_resource_paths.extend(state.built_in_resources.values().filter_map(|res| { - let resource_state = res.resource.0.safe_lock(); + let resource_state = res.resource.lock(); if self .asset_types .contains(&resource_state.state.data_ref()?.type_uuid()) diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 2295225ca..063c21754 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -2313,6 +2313,9 @@ impl Editor { let current_working_directory = std::env::current_dir().unwrap(); if current_working_directory != working_directory { + Log::info(format!( + "Changing working directory to {working_directory:?}" + )); std::env::set_current_dir(working_directory.clone()).unwrap(); self.engine.resource_manager.update_or_load_registry(); self.reload_settings(); @@ -2964,7 +2967,19 @@ impl Editor { self.engine.destroy_graphics_context().unwrap(); } + fn on_focus_changed(&mut self) { + if self.focused { + self.engine + .resource_manager + .state() + .process_filesystem_events(); + } + } + pub fn run(mut self, event_loop: EventLoop<()>) { + Log::info("Initializing resource registry."); + self.engine.resource_manager.update_or_load_registry(); + for_each_plugin!(self.plugins => on_start(&mut self)); event_loop @@ -3041,6 +3056,7 @@ impl Editor { } WindowEvent::Focused(focused) => { self.focused = *focused; + self.on_focus_changed(); } WindowEvent::Moved(new_position) => { // Allow the window to go outside the screen bounds by a little. This diff --git a/editor/src/plugins/material/editor.rs b/editor/src/plugins/material/editor.rs index 31547169c..2a5c93588 100644 --- a/editor/src/plugins/material/editor.rs +++ b/editor/src/plugins/material/editor.rs @@ -218,9 +218,10 @@ pub struct MaterialFieldEditorBuilder { } fn make_name(resource_manager: &ResourceManager, material: &MaterialResource) -> String { + let resource_uuid = material.resource_uuid(); let header = material.header(); match header.state { - ResourceState::Ok { resource_uuid, .. } => { + ResourceState::Ok { .. } => { if let Some(path) = resource_manager .state() .resource_registry @@ -241,6 +242,7 @@ fn make_name(resource_manager: &ResourceManager, material: &MaterialResource) -> ) } } + ResourceState::Unloaded => "Material not loading".into(), ResourceState::LoadError { ref error, .. } => { format!("Loading failed: {error:?}") } diff --git a/fyrox-core/src/visitor/mod.rs b/fyrox-core/src/visitor/mod.rs index 6b614b739..e5f567519 100644 --- a/fyrox-core/src/visitor/mod.rs +++ b/fyrox-core/src/visitor/mod.rs @@ -628,6 +628,16 @@ impl Visitor { self.version } + /// Returns true of a region with the given name exists as a child of the current node. + /// When reading, `has_region` returning true means that `enter_region` will succeed. + /// When writing, `has_region` returning true means that `enter_region` will fail. + pub fn has_region(&self, name: &str) -> bool { + let node = self.nodes.borrow(self.current_node); + node.children + .iter() + .any(|child| self.nodes.borrow(*child).name == name) + } + /// If [Visitor::is_reading], find a node with the given name that is a child of the current /// node, and return a Visitor for the found node. Return an error if no node with that name exists. /// diff --git a/fyrox-graph/src/lib.rs b/fyrox-graph/src/lib.rs index 8448f7ccf..d6818a747 100644 --- a/fyrox-graph/src/lib.rs +++ b/fyrox-graph/src/lib.rs @@ -1273,6 +1273,9 @@ pub trait SceneGraph: BaseSceneGraph { instances } + // Iterate through every node and try to find a corresponding node in the parent resource for that node. + // If a node has a parent resource but no correspoding node in the parent resource, then delete the node. + // Otherwise, use reflection to make the unmodified variables of the node match the values in the corresponding node. fn restore_original_handles_and_inherit_properties( &mut self, ignored_types: &[TypeId], diff --git a/fyrox-impl/src/engine/executor.rs b/fyrox-impl/src/engine/executor.rs index dd0a8ac2a..e4ba49c2d 100644 --- a/fyrox-impl/src/engine/executor.rs +++ b/fyrox-impl/src/engine/executor.rs @@ -207,6 +207,9 @@ impl Executor { /// Runs the executor - starts your game. pub fn run(self) { + Log::info("Initializing resource registry."); + self.engine.resource_manager.update_or_load_registry(); + let engine = self.engine; let event_loop = self.event_loop; let throttle_threshold = self.throttle_threshold; diff --git a/fyrox-impl/src/engine/mod.rs b/fyrox-impl/src/engine/mod.rs index a4d35bb3f..12bb3e29d 100644 --- a/fyrox-impl/src/engine/mod.rs +++ b/fyrox-impl/src/engine/mod.rs @@ -40,7 +40,6 @@ use crate::{ manager::{ResourceManager, ResourceWaitContext}, state::ResourceState, untyped::{ResourceKind, UntypedResource}, - Resource, }, core::{ algebra::Vector2, @@ -51,6 +50,7 @@ use crate::{ reflect::Reflect, task::TaskPool, variable::try_inherit_properties, + SafeLock, }, engine::{error::EngineError, task::TaskPoolHandler}, event::Event, @@ -360,6 +360,7 @@ struct LoadingScene { } struct SceneLoadingResult { + uuid: Uuid, path: PathBuf, result: Result<(Scene, Vec), VisitError>, } @@ -399,6 +400,7 @@ impl AsyncSceneLoader { let sender = self.sender.clone(); let serialization_context = self.serialization_context.clone(); let resource_manager = self.resource_manager.clone(); + let uuid = resource_manager.find::(&path).resource_uuid(); // Aquire the resource IO from the resource manager let io = resource_manager.resource_io(); @@ -415,12 +417,14 @@ impl AsyncSceneLoader { Ok((loader, data)) => { let scene = loader.finish().await; Log::verify(sender.send(SceneLoadingResult { + uuid, path, result: Ok((scene, data)), })); } Err(e) => { Log::verify(sender.send(SceneLoadingResult { + uuid, path, result: Err(e), })); @@ -1059,7 +1063,7 @@ impl ResourceGraphVertex { pub fn resolve(&self) { Log::info(format!( - "Resolving {:?} resource from dependency graph...", + "Resolving {} resource from dependency graph...", self.resource.resource_uuid() )); @@ -1351,7 +1355,7 @@ pub(crate) fn initialize_resource_manager_loaders( state.constructors_container.add::(); state.constructors_container.add::