9.2 KiB
9.2 KiB
XML Parsing in Cursebreaker Parser
This document describes the XML parsing functionality added to the cursebreaker-parser project.
Overview
The parser now supports loading game data from Cursebreaker's XML files and storing them in efficient data structures for runtime access and SQL database serialization.
Features
- ✅ Parse Items, NPCs, Quests, and Harvestables XML files with full attribute and nested element support
- ✅ In-memory databases with fast lookups by ID, name, and various filters
- ✅ JSON serialization for SQL database storage
- ✅ Type-safe data structures with serde support
- ✅ Easy-to-use API with query methods
- ✅ Cross-referencing support between different data types
Quick Start
Loading Items
use cursebreaker_parser::ItemDatabase;
let item_db = ItemDatabase::load_from_xml("Data/XMLs/Items/Items.xml")?;
println!("Loaded {} items", item_db.len());
Querying Items
// Get by ID
if let Some(item) = item_db.get_by_id(150) {
println!("Found: {}", item.name);
}
// Get by category
let bows = item_db.get_by_category("bow");
// Get by slot
let weapons = item_db.get_by_slot("weapon");
// Get by skill requirement
let magic_items = item_db.get_by_skill("magic");
// Get all items
for item in item_db.all_items() {
println!("{}: {}", item.id, item.name);
}
SQL Serialization
// Prepare items for SQL insertion
let sql_data = item_db.prepare_for_sql();
for (id, name, json_data) in sql_data {
// INSERT INTO items (id, name, data) VALUES (?, ?, ?)
// Use your preferred SQL library to insert
}
Data Structures
Item
The main Item struct contains all item attributes from the XML:
pub struct Item {
// Required
pub id: i32,
pub name: String,
// Optional attributes
pub level: Option<i32>,
pub description: Option<String>,
pub price: Option<i32>,
pub slot: Option<String>,
pub category: Option<String>,
pub skill: Option<String>,
// ... many more fields
// Nested elements
pub stats: Vec<ItemStat>,
pub crafting_recipes: Vec<CraftingRecipe>,
pub animations: Option<AnimationSet>,
pub generate_rules: Vec<GenerateRule>,
}
ItemStat
Represents item statistics:
pub struct ItemStat {
// Damage
pub damagephysical: Option<i32>,
pub damagemagical: Option<i32>,
pub damageranged: Option<i32>,
// Accuracy
pub accuracyphysical: Option<i32>,
pub accuracymagical: Option<i32>,
pub accuracyranged: Option<i32>,
// Resistance
pub resistancephysical: Option<i32>,
pub resistancemagical: Option<i32>,
pub resistanceranged: Option<i32>,
// Core stats
pub health: Option<i32>,
pub mana: Option<i32>,
pub manaregen: Option<i32>,
pub healing: Option<i32>,
// Harvesting
pub harvestingspeedwoodcutting: Option<i32>,
}
Example Programs
Run the demos to see all features in action:
# Items only
cargo run --example item_database_demo
# All game data (Items, NPCs, Quests, Harvestables)
cargo run --example game_data_demo
Loading NPCs
use cursebreaker_parser::NpcDatabase;
let npc_db = NpcDatabase::load_from_xml("Data/XMLs/Npcs/NPCInfo.xml")?;
println!("Loaded {} NPCs", npc_db.len());
Querying NPCs
// Get by ID
if let Some(npc) = npc_db.get_by_id(1) {
println!("Found: {}", npc.name);
}
// Get hostile NPCs
let hostile = npc_db.get_hostile();
// Get interactable NPCs
let interactable = npc_db.get_interactable();
// Get NPCs by tag
let undead = npc_db.get_by_tag("Undead");
// Get shopkeepers
let shops = npc_db.get_shopkeepers();
Loading Quests
use cursebreaker_parser::QuestDatabase;
let quest_db = QuestDatabase::load_from_xml("Data/XMLs/Quests/Quests.xml")?;
println!("Loaded {} quests", quest_db.len());
Querying Quests
// Get by ID
if let Some(quest) = quest_db.get_by_id(1) {
println!("Quest: {}", quest.name);
println!("Phases: {}", quest.phase_count());
}
// Get main quests
let main_quests = quest_db.get_main_quests();
// Get side quests
let side_quests = quest_db.get_side_quests();
// Get hidden quests
let hidden = quest_db.get_hidden_quests();
Loading Harvestables
use cursebreaker_parser::HarvestableDatabase;
let harvestable_db = HarvestableDatabase::load_from_xml("Data/XMLs/Harvestables/HarvestableInfo.xml")?;
println!("Loaded {} harvestables", harvestable_db.len());
Querying Harvestables
// Get by type ID
if let Some(harvestable) = harvestable_db.get_by_typeid(1) {
println!("Found: {}", harvestable.name);
}
// Get by skill
let woodcutting = harvestable_db.get_by_skill("Woodcutting");
let mining = harvestable_db.get_by_skill("mining");
let fishing = harvestable_db.get_by_skill("Fishing");
// Get trees (harvestables with tree=1)
let trees = harvestable_db.get_trees();
// Get by tool requirement
let hatchet_nodes = harvestable_db.get_by_tool("hatchet");
let pickaxe_nodes = harvestable_db.get_by_tool("pickaxe");
// Get by level range
let beginner = harvestable_db.get_by_level_range(1, 10);
let advanced = harvestable_db.get_by_level_range(50, 100);
Cross-referencing Data
// Find items rewarded by quests
for quest in quest_db.all_quests() {
for reward in &quest.rewards {
if let Some(item_id) = reward.item {
if let Some(item) = item_db.get_by_id(item_id) {
println!("Quest '{}' rewards: {}", quest.name, item.name);
}
}
}
}
// Find NPCs that give quests
for npc in npc_db.all_npcs() {
if !npc.questmarkers.is_empty() {
println!("NPC '{}' has {} quest markers", npc.name, npc.questmarkers.len());
}
}
// Find items that drop from harvestables
for harvestable in harvestable_db.all_harvestables() {
for drop in &harvestable.drops {
if let Some(item) = item_db.get_by_id(drop.id) {
println!("'{}' drops: {} (rate: {})",
harvestable.name, item.name, drop.droprate.unwrap_or(0));
}
}
}
Statistics from XML Files
When loaded from /home/connor/repos/CBAssets/Data/XMLs/:
Items.xml
- Total Items: 1,360
- Weapons: 166
- Armor: 148
- Consumables: 294
- Trinkets: 59
- Bows: 18
- Magic Items: 76
NPCs/NPCInfo.xml
- Total NPCs: 1,242
- Hostile NPCs: 328
- Interactable NPCs: 512
- Undead: 71
- Predators: 13
- Quest Givers: 108
Quests/Quests.xml
- Total Quests: 108
- Main Quests: 19
- Side Quests: 89
- Hidden Quests: 2
- Unique Quest Reward Items: 70
Harvestables/HarvestableInfo.xml
- Total Harvestables: 96
- Trees: 9
- Woodcutting: 10
- Mining: 11
- Fishing: 11
- Alchemy: 50
- Level 1-10: 31
- Level 11-50: 37
- Level 51-100: 28
- Unique Items from Harvestables: 98
File Structure
cursebreaker-parser/
├── src/
│ ├── lib.rs # Library exports
│ ├── main.rs # Main binary (Unity + XML parsing)
│ ├── types/
│ │ ├── mod.rs
│ │ ├── item.rs # Item data structures
│ │ ├── npc.rs # NPC data structures
│ │ ├── quest.rs # Quest data structures
│ │ ├── harvestable.rs # Harvestable data structures
│ │ └── interactable_resource.rs
│ ├── xml_parser.rs # XML parsing logic (all types)
│ ├── item_database.rs # ItemDatabase for runtime access
│ ├── npc_database.rs # NpcDatabase for runtime access
│ ├── quest_database.rs # QuestDatabase for runtime access
│ └── harvestable_database.rs # HarvestableDatabase for runtime access
└── examples/
├── item_database_demo.rs # Items usage example
└── game_data_demo.rs # Full game data example
Dependencies Added
quick-xml = "0.37" # XML parsing
serde = { version = "1.0", features = ["derive"] } # Serialization
serde_json = "1.0" # JSON serialization
diesel = { version = "2.2", features = ["sqlite"], optional = true } # SQL (optional)
thiserror = "1.0" # Error handling
Completed Features
- ✅ Items (
/XMLs/Items/Items.xml) - ✅ NPCs (
/XMLs/Npcs/NPCInfo.xml) - ✅ Quests (
/XMLs/Quests/Quests.xml) - ✅ Harvestables (
/XMLs/Harvestables/HarvestableInfo.xml)
Future Enhancements
The same pattern can be extended to parse other XML files:
- Loot tables (
/XMLs/Loot/*.xml) - Maps (
/XMLs/Maps/*.xml) - Dialogue (
/XMLs/Dialogue/*.xml) - Events (
/XMLs/Events/*.xml) - Achievements (
/XMLs/Achievements/*.xml) - Traits (
/XMLs/Traits/*.xml) - Shops (
/XMLs/Shops/*.xml)
Each follows the same pattern:
- Define data structures in
src/types/ - Create parser in
src/xml_parser.rs - Create database wrapper for runtime access
- Add to
lib.rsexports
Integration with Unity Parser
The main binary (src/main.rs) demonstrates integration of both systems:
- Load game data from XML files (Items, etc.)
- Parse Unity scenes for game objects
- Cross-reference data (e.g., item IDs in loot spawners)
This creates a complete game data pipeline from source files to runtime.