Files
cursebreaker-parser-rust/README.md
2026-01-03 14:51:31 +00:00

404 lines
13 KiB
Markdown

# Cursebreaker Unity Parser
A high-performance Rust library for parsing Unity project files (.unity scenes, .prefab prefabs, and .asset files) with automatic MonoBehaviour component discovery and ECS integration.
[![Rust](https://img.shields.io/badge/rust-1.70%2B-orange.svg)](https://www.rust-lang.org/)
[![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](LICENSE)
**⚠️ Work in Progress**: This library is under active development. APIs may change.
## Features
### Core Parsing
- **Multi-format support**: Parse `.unity` scenes, `.prefab` prefabs, and `.asset` files
- **ECS integration**: Automatically builds [Sparsey](https://github.com/LechintanTudor/sparsey) ECS worlds from scenes
- **Type-safe**: Strong typing for Unity primitives (Vector3, Quaternion, Color, etc.)
- **Fast**: Efficient YAML parsing with minimal allocations
### Component System
- **Derive macro**: `#[derive(UnityComponent)]` for automatic component parsing
- **Auto-registration**: Components register themselves via [inventory](https://github.com/dtolnay/inventory)
- **GUID resolution**: Automatically resolves MonoBehaviour script GUIDs to class names
- **Prefab resolution**: Resolves prefab GUIDs for nested prefab references
- **Type filtering**: Selectively parse only the components you need
### Advanced Features
- **Prefab instantiation**: Clone and modify prefab instances (basic support)
- **Reference resolution**: FileID → Entity mapping
- **Regex filtering**: Parse only files matching specific patterns
- **Transform hierarchies**: Parent-child relationships preserved
- **Memory efficient**: 128-bit GUIDs stored as u128 (16 bytes vs ~56 bytes for String)
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
unity_parser = { path = "unity-parser" }
sparsey = "0.13" # For ECS queries
```
## Quick Start
### Parse a Unity Scene
```rust
use unity_parser::UnityFile;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = UnityFile::from_path("Scene.unity")?;
match file {
UnityFile::Scene(scene) => {
println!("Scene with {} entities", scene.entity_map.len());
// Query transforms
let transforms = scene.world.borrow::<unity_parser::Transform>();
for (file_id, entity) in &scene.entity_map {
if let Some(transform) = transforms.get(*entity) {
println!("Entity {} at {:?}", file_id, transform.local_position());
}
}
}
UnityFile::Prefab(prefab) => {
println!("Prefab with {} documents", prefab.documents.len());
}
UnityFile::Asset(asset) => {
println!("Asset with {} documents", asset.documents.len());
}
}
Ok(())
}
```
### Parse a Prefab
```rust
use unity_parser::UnityFile;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let file = UnityFile::from_path("Player.prefab")?;
if let UnityFile::Prefab(prefab) = file {
// Find all GameObjects
let game_objects = prefab.get_documents_by_class("GameObject");
for doc in game_objects {
if let Some(mapping) = doc.as_mapping() {
if let Some(name) = mapping.get("m_Name").and_then(|v| v.as_str()) {
println!("GameObject: {}", name);
}
}
}
// Find documents by type ID
let transforms = prefab.get_documents_by_type(4); // Transform = type 4
println!("Found {} Transforms", transforms.len());
}
Ok(())
}
```
### Define Custom Components
**⚠️ Note**: The derive macro currently has namespace issues and may not compile. Manual implementation is recommended until fixed.
```rust
use unity_parser::{UnityComponent, ComponentContext};
use serde_yaml::Mapping;
#[derive(Debug, Clone)]
pub struct PlaySFX {
pub volume: f64,
pub start_time: f64,
pub end_time: f64,
pub is_loop: bool,
}
// Manual implementation (recommended until macro is fixed)
impl UnityComponent for PlaySFX {
fn parse(yaml: &Mapping, _ctx: &ComponentContext) -> Option<Self> {
Some(Self {
volume: unity_parser::yaml_helpers::get_f64(yaml, "volume").unwrap_or(1.0),
start_time: unity_parser::yaml_helpers::get_f64(yaml, "startTime").unwrap_or(0.0),
end_time: unity_parser::yaml_helpers::get_f64(yaml, "endTime").unwrap_or(0.0),
is_loop: unity_parser::yaml_helpers::get_bool(yaml, "isLoop").unwrap_or(false),
})
}
}
```
## GUID Resolution
The parser automatically resolves Unity MonoBehaviour GUIDs to class names:
```
Unity Scene File Rust Code
───────────────── ─────────
MonoBehaviour: Custom component
m_Script: in ECS World
guid: 091c537... ──────────>
volume: 1.0
isLoop: 0
```
### How It Works
1. **Scan**: Parser scans Unity project's `Assets/` for `*.cs.meta` files
2. **Build Map**: Extracts GUIDs from `.meta` files
3. **Extract Class**: Parses `.cs` files to get `class Name : MonoBehaviour`
4. **Resolve**: Maps GUID → Class Name → Registered Component
5. **Parse**: Automatically parses MonoBehaviour YAML into components
The GUID resolver builds automatically when parsing scenes if a Unity project root is detected.
## Type Filtering
Parse only specific Unity types and MonoBehaviours for better performance:
```rust
use unity_parser::{TypeFilter, parse_unity_file_filtered};
use std::collections::HashSet;
// Parse only Transforms and GameObjects
let mut types = HashSet::new();
types.insert("Transform".to_string());
types.insert("GameObject".to_string());
let filter = TypeFilter::with_unity_types(types);
let file = parse_unity_file_filtered(
Path::new("Scene.unity"),
None, // No regex filter
Some(&filter)
)?;
```
## Regex Path Filtering
Parse only files matching specific patterns:
```rust
use regex::Regex;
use unity_parser::parse_unity_file_filtered;
// Only parse production scenes
let filter = Regex::new(r"Assets/Scenes/Production/")?;
let scene = parse_unity_file_filtered(
Path::new("Assets/Scenes/Production/Level1.unity"),
Some(&filter),
None
)?;
```
## Supported Unity Types
### Built-in Components
- ✅ GameObject
- ✅ Transform
- ✅ RectTransform
- ✅ PrefabInstance
### Value Types
- ✅ Vector2, Vector3
- ✅ Quaternion
- ✅ Color
- ✅ FileID, GUID
- ✅ ExternalRef, FileRef
### Custom Components
- ✅ Any `MonoBehaviour` via manual `UnityComponent` implementation
- ⚠️ `#[derive(UnityComponent)]` macro (has namespace bugs, not recommended)
## Architecture
### Component Flow
```
Unity Scene File
Raw YAML Documents
┌─────────────────────┐
│ GUID Resolution │ ← .meta files + .cs files
│ (MonoBehaviour) │ (Script GUID → Class Name)
└─────────────────────┘
┌─────────────────────┐
│ Prefab GUID Res. │ ← .prefab.meta files
│ (Nested Prefabs) │ (Prefab GUID → Prefab Path)
└─────────────────────┘
┌─────────────────────┐
│ Component Registry │ ← UnityComponent trait impls
│ (inventory) │
└─────────────────────┘
┌─────────────────────┐
│ ECS World │ ← Sparsey entities & components
│ (Transform, etc) │
└─────────────────────┘
```
### Memory Efficiency
**GUID Storage**:
- Old approach: `String` (24 bytes stack + 32 bytes heap = 56 bytes)
- Current: `u128` (16 bytes on stack)
- **3.5x memory reduction** for GUID storage
**GUID Comparison**:
- Old: O(n) string comparison (32 characters)
- New: O(1) integer comparison
- **Significant speedup** for HashMap lookups
## Project Structure
```
cursebreaker-parser-rust/
├── unity-parser/ # Main library crate
│ ├── src/
│ │ ├── ecs/ # ECS world building
│ │ │ └── builder.rs
│ │ ├── model/ # UnityFile, Scene, Prefab, Asset
│ │ │ └── mod.rs
│ │ ├── parser/ # YAML parsing & GUID resolution
│ │ │ ├── guid_resolver.rs # Script GUID → Class Name
│ │ │ ├── prefab_guid_resolver.rs # Prefab GUID → Path
│ │ │ ├── meta.rs # .meta file parsing
│ │ │ ├── yaml.rs # YAML document splitting
│ │ │ └── mod.rs
│ │ ├── project/ # ⚠️ OUTDATED - needs refactoring
│ │ │ └── mod.rs
│ │ ├── types/ # Unity types & components
│ │ │ ├── unity_types/
│ │ │ │ ├── game_object.rs
│ │ │ │ ├── transform.rs
│ │ │ │ ├── prefab_instance.rs
│ │ │ │ └── mod.rs
│ │ │ ├── component.rs # UnityComponent trait & helpers
│ │ │ ├── guid.rs # 128-bit GUID type
│ │ │ ├── ids.rs # FileID, LocalID
│ │ │ ├── reference.rs # UnityReference enum
│ │ │ ├── type_filter.rs # TypeFilter for selective parsing
│ │ │ ├── values.rs # Vector3, Quaternion, Color, etc.
│ │ │ └── mod.rs
│ │ ├── error.rs # Error types
│ │ ├── macros.rs
│ │ ├── property/
│ │ └── lib.rs
│ ├── examples/
│ │ ├── basic_parsing.rs
│ │ ├── custom_component.rs
│ │ ├── ecs_integration.rs
│ │ ├── find_playsfx.rs
│ │ ├── parse_resources.rs
│ │ └── parse_resource_prefabs.rs
│ ├── tests/
│ └── Cargo.toml
├── unity-parser-macros/ # Proc macro crate (⚠️ has bugs)
│ ├── src/
│ │ └── lib.rs
│ └── Cargo.toml
├── Cargo.toml # Workspace config
└── README.md
```
## Known Issues
### Critical Issues
1. **`unity-parser/src/project/mod.rs` is OUTDATED**
- Built for old architecture before `UnityFile` enum refactor
- References non-existent `UnityDocument` type (should be `RawDocument`)
- Module is disabled in lib.rs until refactored
2. **Derive macro namespace mismatch**
- `unity-parser-macros` uses `unity_parser` namespace
- Actual crate name is `unity_parser::` (underscore, not hyphen)
- Manual `UnityComponent` implementation recommended
3. **Placeholder values in Cargo.toml**
- Author and repository fields need updating
### Minor Issues
1. Disabled example/test files may reference outdated APIs
2. Some examples may have incorrect YAML access patterns
## Running Examples
```bash
# Parse basic Unity file
cargo run --example basic_parsing
# Custom component parsing (requires Unity project)
cargo run --example custom_component
# ECS integration showcase
cargo run --example ecs_integration
# Find PlaySFX components (requires CBAssets project)
cargo run --example find_playsfx
```
## Testing
```bash
# Unit tests
cargo test --lib
# Integration tests
cargo test --test integration_tests
# All tests
cargo test
```
## Roadmap
### Completed
- ✅ Phase 1: GUID Resolution (Script GUID → Class Name)
- ✅ Phase 2: MonoBehaviour Parser
- ✅ Phase 3: Prefab GUID Resolution
- ✅ Type filtering for selective parsing
- ✅ Regex path filtering
- ✅ Basic prefab instantiation
### In Progress / Needs Work
- 🔧 Refactor `project` module for new architecture
- 🔧 Update disabled example/test files
- 🔧 Fix example code YAML access patterns
### Future Enhancements
- [ ] Full prefab modification system
- [ ] Persistent GUID cache for instant loading
- [ ] Watch mode for live Unity project monitoring
- [ ] More built-in Unity component types
- [ ] Better error messages with line numbers
- [ ] Parallel processing support
- [ ] Cross-platform path handling improvements
## Contributing
Contributions welcome! Areas needing help:
- **Documentation**: API docs, more examples, tutorials
- **Testing**: Integration tests with real Unity projects
- **Performance**: Optimize YAML parsing, parallel processing
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
## Acknowledgments
- **Unity Technologies**: For the YAML-based file format
- **Sparsey**: ECS library for component storage
- **serde_yaml**: YAML parsing foundation
- **inventory**: Compile-time component registration