From 29813a46ef7934e71efce643cf4b814e5866aeaa Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 4 Jan 2026 13:59:47 +0000 Subject: [PATCH] split project --- Cargo.lock | 17 +++ Cargo.toml | 2 +- cursebreaker-parser/Cargo.toml | 10 ++ cursebreaker-parser/src/main.rs | 128 ++++++++++++++++++ .../src/types/interactable_resource.rs | 47 +++++++ cursebreaker-parser/src/types/mod.rs | 3 + resources_output.txt | 10 +- unity-parser/Cargo.toml | 3 + unity-parser/src/types/mod.rs | 4 +- unity-parser/src/types/unity_types/mod.rs | 4 + 10 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 cursebreaker-parser/Cargo.toml create mode 100644 cursebreaker-parser/src/main.rs create mode 100644 cursebreaker-parser/src/types/interactable_resource.rs create mode 100644 cursebreaker-parser/src/types/mod.rs diff --git a/Cargo.lock b/Cargo.lock index a04b159..ad3aa2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,16 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41e67cd8309bbd06cd603a9e693a784ac2e5d1e955f11286e355089fcab3047c" +[[package]] +name = "cursebreaker-parser" +version = "0.1.0" +dependencies = [ + "inventory", + "serde_yaml", + "sparsey", + "unity-parser", +] + [[package]] name = "diff" version = "0.1.13" @@ -242,6 +252,12 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "sparsey" version = "0.13.3" @@ -303,6 +319,7 @@ dependencies = [ "regex", "serde", "serde_yaml", + "smallvec", "sparsey", "thiserror", "unity-parser-macros", diff --git a/Cargo.toml b/Cargo.toml index 4edfb6a..b674277 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["unity-parser", "unity-parser-macros"] +members = ["unity-parser", "unity-parser-macros", "cursebreaker-parser"] resolver = "2" [workspace.package] diff --git a/cursebreaker-parser/Cargo.toml b/cursebreaker-parser/Cargo.toml new file mode 100644 index 0000000..0e3087d --- /dev/null +++ b/cursebreaker-parser/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cursebreaker-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] +unity-parser = { path = "../unity-parser" } +serde_yaml = "0.9" +inventory = "0.3" +sparsey = "0.13" diff --git a/cursebreaker-parser/src/main.rs b/cursebreaker-parser/src/main.rs new file mode 100644 index 0000000..2320ba6 --- /dev/null +++ b/cursebreaker-parser/src/main.rs @@ -0,0 +1,128 @@ +//! Parse Cursebreaker Resources from 10_3.unity Scene +//! +//! This example demonstrates: +//! 1. Parsing the Cursebreaker Unity project +//! 2. Finding Interactable_Resource components +//! 3. Extracting typeId and transform positions +//! 4. Writing resource data to an output file + +mod types; + +use types::InteractableResource; +use unity_parser::UnityFile; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +fn main() -> Result<(), Box> { + println!("🎮 Cursebreaker - Resource Parser"); + println!("{}", "=".repeat(70)); + println!(); + + let scene_path = Path::new("/home/connor/repos/CBAssets/_GameAssets/Scenes/Tiles/10_3.unity"); + + // Check if scene exists + if !scene_path.exists() { + eprintln!("❌ Error: Scene not found at {}", scene_path.display()); + return Err("Scene file not found".into()); + } + + println!("📁 Parsing scene: {}", scene_path.display()); + println!(); + + // Parse the scene + match UnityFile::from_path(&scene_path) { + Ok(UnityFile::Scene(scene)) => { + println!("✅ Scene parsed successfully!"); + println!(" Total entities: {}", scene.entity_map.len()); + println!(); + + // Get views for component types we need + let resource_view = scene.world.borrow::(); + let transform_view = scene.world.borrow::(); + let gameobject_view = scene.world.borrow::(); + + // Find all entities that have Interactable_Resource + let mut found_resources = Vec::new(); + + for entity in scene.entity_map.values() { + if let Some(resource) = resource_view.get(*entity) { + let transform = transform_view.get(*entity); + let game_object = gameobject_view.get(*entity); + + let name = game_object + .and_then(|go| go.name()) + .unwrap_or("(unnamed)"); + + let position = transform + .and_then(|t| t.local_position()) + .map(|p| (p.x, p.y, p.z)); + + found_resources.push((name.to_string(), resource.clone(), position)); + } + } + + println!("🔍 Found {} Interactable_Resource component(s)", found_resources.len()); + println!(); + + if !found_resources.is_empty() { + // Display resources in console + for (name, resource, position) in &found_resources { + println!(" 📦 Resource: \"{}\"", name); + println!(" • typeId: {}", resource.type_id); + println!(" • maxHealth: {}", resource.max_health); + if let Some((x, y, z)) = position { + println!(" • Position: ({:.2}, {:.2}, {:.2})", x, y, z); + } else { + println!(" • Position: (no transform)"); + } + println!(); + } + + // Write to output file + let output_path = "resources_output.txt"; + let mut output_file = File::create(output_path)?; + + writeln!(output_file, "Cursebreaker Resources - 10_3.unity Scene")?; + writeln!(output_file, "{}", "=".repeat(70))?; + writeln!(output_file)?; + writeln!(output_file, "Total resources found: {}", found_resources.len())?; + writeln!(output_file)?; + writeln!(output_file, "{}", "-".repeat(70))?; + writeln!(output_file)?; + + for (name, resource, position) in &found_resources { + writeln!(output_file, "Resource: {}", name)?; + writeln!(output_file, " TypeID: {}", resource.type_id)?; + writeln!(output_file, " MaxHealth: {}", resource.max_health)?; + if let Some((x, y, z)) = position { + writeln!(output_file, " Position: ({:.6}, {:.6}, {:.6})", x, y, z)?; + } else { + writeln!(output_file, " Position: N/A")?; + } + writeln!(output_file)?; + } + + writeln!(output_file, "{}", "=".repeat(70))?; + writeln!(output_file, "End of resource data")?; + + println!("📝 Resource data written to: {}", output_path); + println!(); + } + + println!("{}", "=".repeat(70)); + println!("✅ Parsing complete!"); + println!("{}", "=".repeat(70)); + } + Ok(_) => { + eprintln!("❌ Error: File is not a scene"); + return Err("Not a Unity scene file".into()); + } + Err(e) => { + eprintln!("❌ Parse error: {}", e); + return Err(Box::new(e)); + } + } + + Ok(()) +} diff --git a/cursebreaker-parser/src/types/interactable_resource.rs b/cursebreaker-parser/src/types/interactable_resource.rs new file mode 100644 index 0000000..697e0c2 --- /dev/null +++ b/cursebreaker-parser/src/types/interactable_resource.rs @@ -0,0 +1,47 @@ +/// Interactable_Resource component from Cursebreaker +/// +/// C# definition from Interactable_Resource.cs: +/// ```csharp +/// public class Interactable_Resource : Interactable +/// { +/// public int health; +/// public int maxHealth; +/// public int typeId; +/// // ... other fields +/// } +/// ``` +use unity_parser::{UnityComponent, ComponentContext, EcsInsertable}; +use serde_yaml::Mapping; + +#[derive(Debug, Clone)] +pub struct InteractableResource { + pub max_health: i64, + pub type_id: i64, +} + +impl UnityComponent for InteractableResource { + fn parse(yaml: &Mapping, _ctx: &ComponentContext) -> Option { + Some(Self { + max_health: unity_parser::yaml_helpers::get_i64(yaml, "maxHealth").unwrap_or(0), + type_id: unity_parser::yaml_helpers::get_i64(yaml, "typeId").unwrap_or(0), + }) + } +} + +impl EcsInsertable for InteractableResource { + fn insert_into_world(self, world: &mut sparsey::World, entity: sparsey::Entity) { + world.insert(entity, (self,)); + } +} + +// Register component with inventory +inventory::submit! { + unity_parser::ComponentRegistration { + type_id: 114, + class_name: "Interactable_Resource", + parse_and_insert: |yaml, ctx, world, entity| { + ::parse_and_insert(yaml, ctx, world, entity) + }, + register: |builder| builder.register::(), + } +} diff --git a/cursebreaker-parser/src/types/mod.rs b/cursebreaker-parser/src/types/mod.rs new file mode 100644 index 0000000..21249cc --- /dev/null +++ b/cursebreaker-parser/src/types/mod.rs @@ -0,0 +1,3 @@ +mod interactable_resource; + +pub use interactable_resource::InteractableResource; diff --git a/resources_output.txt b/resources_output.txt index bfcfe5b..b691925 100644 --- a/resources_output.txt +++ b/resources_output.txt @@ -5,15 +5,15 @@ Total resources found: 2 ---------------------------------------------------------------------- -Resource: HarvestableSpawner_2Copper Ore - TypeID: 2 - MaxHealth: 0 - Position: (1788.727173, 40.725288, 172.017670) - Resource: HarvestableSpawner_38Dandelions TypeID: 38 MaxHealth: 0 Position: (1746.709717, 44.599632, 299.696503) +Resource: HarvestableSpawner_2Copper Ore + TypeID: 2 + MaxHealth: 0 + Position: (1788.727173, 40.725288, 172.017670) + ====================================================================== End of resource data diff --git a/unity-parser/Cargo.toml b/unity-parser/Cargo.toml index 93647d4..63dc7b7 100644 --- a/unity-parser/Cargo.toml +++ b/unity-parser/Cargo.toml @@ -46,6 +46,9 @@ once_cell = "1.19" # Component registry for custom MonoBehaviours inventory = "0.3" +# Small vector optimization for materials list +smallvec = "1.13" + # Procedural macro for derive(UnityComponent) unity-parser-macros = { path = "../unity-parser-macros" } diff --git a/unity-parser/src/types/mod.rs b/unity-parser/src/types/mod.rs index e9cd4b8..d1a0b20 100644 --- a/unity-parser/src/types/mod.rs +++ b/unity-parser/src/types/mod.rs @@ -23,7 +23,7 @@ pub use reference::UnityReference; pub use type_filter::TypeFilter; pub use type_registry::{get_class_name, get_type_id}; pub use unity_types::{ - GameObject, PrefabInstance, PrefabInstanceComponent, PrefabModification, PrefabResolver, - RectTransform, Transform, + GameObject, MeshFilter, PrefabInstance, PrefabInstanceComponent, PrefabModification, + PrefabResolver, RectTransform, Renderer, Transform, }; pub use values::{Color, ExternalRef, FileRef, Quaternion, Vector2, Vector3}; diff --git a/unity-parser/src/types/unity_types/mod.rs b/unity-parser/src/types/unity_types/mod.rs index b91e528..fca9e4a 100644 --- a/unity-parser/src/types/unity_types/mod.rs +++ b/unity-parser/src/types/unity_types/mod.rs @@ -1,11 +1,15 @@ //! Unity-specific types (GameObjects, Transforms, PrefabInstances) pub mod game_object; +pub mod mesh_filter; pub mod prefab_instance; +pub mod renderer; pub mod transform; pub use game_object::GameObject; +pub use mesh_filter::MeshFilter; pub use prefab_instance::{ PrefabInstance, PrefabInstanceComponent, PrefabModification, PrefabResolver, }; +pub use renderer::Renderer; pub use transform::{RectTransform, Transform};