meta files phase 3
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
//! ECS world building from Unity documents
|
||||
|
||||
use crate::model::RawDocument;
|
||||
use crate::parser::GuidResolver;
|
||||
use crate::types::{
|
||||
yaml_helpers, ComponentContext, FileID, GameObject, LinkingContext, PrefabInstanceComponent,
|
||||
RectTransform, Transform, TypeFilter, UnityComponent,
|
||||
@@ -19,11 +20,13 @@ use std::collections::HashMap;
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `documents`: Parsed Unity documents to build the world from
|
||||
/// - `guid_resolver`: Optional GUID resolver for resolving MonoBehaviour scripts to class names
|
||||
///
|
||||
/// # Returns
|
||||
/// A tuple of (World, FileID → Entity mapping)
|
||||
pub fn build_world_from_documents(
|
||||
documents: Vec<RawDocument>,
|
||||
guid_resolver: Option<&GuidResolver>,
|
||||
) -> Result<(World, HashMap<FileID, Entity>)> {
|
||||
// Create World builder with registered component types
|
||||
let mut builder = World::builder();
|
||||
@@ -51,7 +54,7 @@ pub fn build_world_from_documents(
|
||||
// PASS 2: Attach components to entities
|
||||
let type_filter = TypeFilter::parse_all();
|
||||
for doc in documents.iter().filter(|d| d.type_id != 1 && d.class_name != "GameObject") {
|
||||
attach_component(&mut world, doc, &linking_ctx, &type_filter)?;
|
||||
attach_component(&mut world, doc, &linking_ctx, &type_filter, guid_resolver)?;
|
||||
}
|
||||
|
||||
// PASS 3: Execute all deferred linking callbacks
|
||||
@@ -100,7 +103,8 @@ pub fn build_world_from_documents_into(
|
||||
// PASS 2: Attach components to entities
|
||||
let type_filter = TypeFilter::parse_all();
|
||||
for doc in documents.iter().filter(|d| d.type_id != 1 && d.class_name != "GameObject") {
|
||||
attach_component(world, doc, &linking_ctx, &type_filter)?;
|
||||
// Note: Prefab instantiation doesn't need GUID resolution (uses base prefab's components)
|
||||
attach_component(world, doc, &linking_ctx, &type_filter, None)?;
|
||||
}
|
||||
|
||||
// PASS 3: Execute all deferred linking callbacks
|
||||
@@ -125,6 +129,7 @@ fn spawn_game_object(world: &mut World, doc: &RawDocument) -> Result<Entity> {
|
||||
entity: None,
|
||||
linking_ctx: None,
|
||||
yaml,
|
||||
guid_resolver: None,
|
||||
};
|
||||
|
||||
let go = GameObject::parse(yaml, &ctx)
|
||||
@@ -142,6 +147,7 @@ fn attach_component(
|
||||
doc: &RawDocument,
|
||||
linking_ctx: &RefCell<LinkingContext>,
|
||||
type_filter: &TypeFilter,
|
||||
guid_resolver: Option<&GuidResolver>,
|
||||
) -> Result<()> {
|
||||
let yaml = doc
|
||||
.as_mapping()
|
||||
@@ -176,6 +182,7 @@ fn attach_component(
|
||||
entity: Some(entity),
|
||||
linking_ctx: Some(linking_ctx),
|
||||
yaml,
|
||||
guid_resolver,
|
||||
};
|
||||
|
||||
// Check type filter to see if we should parse this component
|
||||
@@ -209,6 +216,49 @@ fn attach_component(
|
||||
linking_ctx.borrow_mut().entity_map_mut().insert(doc.file_id, entity);
|
||||
}
|
||||
}
|
||||
"MonoBehaviour" => {
|
||||
// Extract m_Script GUID to resolve the actual class name
|
||||
if let Some(resolver) = guid_resolver {
|
||||
if let Some(script_ref) = yaml_helpers::get_external_ref(yaml, "m_Script") {
|
||||
// Resolve GUID to class name
|
||||
if let Some(class_name) = resolver.resolve_class_name(script_ref.guid.as_str()) {
|
||||
// Try to find a registered custom component with this class name
|
||||
let mut found_custom = false;
|
||||
for reg in inventory::iter::<crate::types::ComponentRegistration> {
|
||||
if reg.class_name == class_name {
|
||||
found_custom = true;
|
||||
// Parse and insert the component into the ECS world
|
||||
if (reg.parse_and_insert)(yaml, &ctx, world, entity) {
|
||||
// Successfully parsed and inserted
|
||||
linking_ctx.borrow_mut().entity_map_mut().insert(doc.file_id, entity);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found_custom {
|
||||
// GUID resolved but no registered component found
|
||||
eprintln!(
|
||||
"Warning: Skipping MonoBehaviour '{}' (no registered parser)",
|
||||
class_name
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// GUID not found in resolver
|
||||
eprintln!(
|
||||
"Warning: Could not resolve MonoBehaviour GUID: {}",
|
||||
script_ref.guid
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// No m_Script reference found
|
||||
eprintln!("Warning: MonoBehaviour missing m_Script reference");
|
||||
}
|
||||
} else {
|
||||
// No GUID resolver available
|
||||
eprintln!("Warning: Skipping MonoBehaviour (no GUID resolver available)");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Check if this is a registered custom component
|
||||
let mut found_custom = false;
|
||||
|
||||
@@ -81,8 +81,29 @@ fn detect_file_type(path: &Path) -> FileType {
|
||||
fn parse_scene(path: &Path, content: &str) -> Result<UnityFile> {
|
||||
let raw_documents = parse_raw_documents(content)?;
|
||||
|
||||
// Try to find Unity project root and build GUID resolver
|
||||
let guid_resolver = match find_project_root(path) {
|
||||
Ok(project_root) => {
|
||||
// Build GUID resolver from project
|
||||
match GuidResolver::from_project(&project_root) {
|
||||
Ok(resolver) => Some(resolver),
|
||||
Err(e) => {
|
||||
eprintln!("Warning: Failed to build GUID resolver: {}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Not part of a Unity project, or project root not found
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
// Build ECS world from documents
|
||||
let (world, entity_map) = crate::ecs::build_world_from_documents(raw_documents)?;
|
||||
let (world, entity_map) = crate::ecs::build_world_from_documents(
|
||||
raw_documents,
|
||||
guid_resolver.as_ref(),
|
||||
)?;
|
||||
|
||||
Ok(UnityFile::Scene(UnityScene::new(
|
||||
path.to_path_buf(),
|
||||
|
||||
@@ -70,6 +70,8 @@ pub struct ComponentContext<'a> {
|
||||
pub linking_ctx: Option<&'a RefCell<LinkingContext>>,
|
||||
/// The raw YAML mapping for this component (for extracting FileRefs)
|
||||
pub yaml: &'a Mapping,
|
||||
/// GUID resolver for resolving MonoBehaviour script GUIDs to class names
|
||||
pub guid_resolver: Option<&'a crate::parser::GuidResolver>,
|
||||
}
|
||||
|
||||
/// Trait for Unity components that can be parsed from YAML
|
||||
|
||||
@@ -524,6 +524,7 @@ impl PrefabResolver {
|
||||
entity: None,
|
||||
linking_ctx: None,
|
||||
yaml: mapping,
|
||||
guid_resolver: None,
|
||||
};
|
||||
|
||||
if let Some(component) = PrefabInstanceComponent::parse(mapping, &ctx) {
|
||||
|
||||
@@ -474,3 +474,81 @@ fn test_guid_resolution() {
|
||||
|
||||
println!("\n{}", "=".repeat(60));
|
||||
}
|
||||
|
||||
/// Test parsing PlaySFX components from actual scene file
|
||||
#[test]
|
||||
fn test_playsfx_parsing() {
|
||||
use cursebreaker_parser::UnityComponent;
|
||||
|
||||
/// PlaySFX component from VR_Horror_YouCantRun
|
||||
#[derive(Debug, Clone, UnityComponent)]
|
||||
#[unity_class("PlaySFX")]
|
||||
pub struct PlaySFX {
|
||||
#[unity_field("volume")]
|
||||
pub volume: f64,
|
||||
|
||||
#[unity_field("startTime")]
|
||||
pub start_time: f64,
|
||||
|
||||
#[unity_field("endTime")]
|
||||
pub end_time: f64,
|
||||
|
||||
#[unity_field("isLoop")]
|
||||
pub is_loop: bool,
|
||||
}
|
||||
|
||||
let project_path = match clone_test_project(&TestProject::VR_HORROR) {
|
||||
Ok(path) => path,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to clone project: {}", e);
|
||||
eprintln!("Skipping PlaySFX parsing test (git may not be available)");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!("\n{}", "=".repeat(60));
|
||||
println!("Testing PlaySFX Component Parsing");
|
||||
println!("{}", "=".repeat(60));
|
||||
|
||||
// Parse the 1F.unity scene that contains PlaySFX components
|
||||
let scene_path = project_path.join("Assets/Scenes/TEST/Final_1F/1F.unity");
|
||||
|
||||
if !scene_path.exists() {
|
||||
eprintln!("Scene file not found: {}", scene_path.display());
|
||||
return;
|
||||
}
|
||||
|
||||
println!("\n Parsing scene: {}", scene_path.display());
|
||||
|
||||
match cursebreaker_parser::UnityFile::from_path(&scene_path) {
|
||||
Ok(cursebreaker_parser::UnityFile::Scene(scene)) => {
|
||||
println!(" ✓ Scene parsed successfully");
|
||||
println!(" - Total entities: {}", scene.entity_map.len());
|
||||
|
||||
// Try to get PlaySFX components
|
||||
let playsfx_view = scene.world.borrow::<PlaySFX>();
|
||||
let mut found_count = 0;
|
||||
|
||||
for entity in scene.entity_map.values() {
|
||||
if playsfx_view.get(*entity).is_some() {
|
||||
found_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!(" ✓ Found {} PlaySFX component(s)", found_count);
|
||||
|
||||
assert!(
|
||||
found_count > 0,
|
||||
"Should find at least one PlaySFX component in 1F.unity"
|
||||
);
|
||||
}
|
||||
Ok(_) => {
|
||||
panic!("File was not parsed as a scene");
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Failed to parse scene: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
println!("\n{}", "=".repeat(60));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user