world resources to DB

This commit is contained in:
2026-01-12 04:32:38 +00:00
parent 557ffa7e53
commit d99c546499
9 changed files with 154 additions and 16 deletions

View File

@@ -37,7 +37,8 @@
"Bash(DATABASE_URL=../cursebreaker.db diesel migration:*)", "Bash(DATABASE_URL=../cursebreaker.db diesel migration:*)",
"Bash(DATABASE_URL=cursebreaker.db diesel migration:*)", "Bash(DATABASE_URL=cursebreaker.db diesel migration:*)",
"Bash(DATABASE_URL=../cursebreaker-parser/cursebreaker.db cargo run:*)", "Bash(DATABASE_URL=../cursebreaker-parser/cursebreaker.db cargo run:*)",
"Bash(identify:*)" "Bash(identify:*)",
"Bash(diesel migration revert:*)"
], ],
"additionalDirectories": [ "additionalDirectories": [
"/home/connor/repos/CBAssets/" "/home/connor/repos/CBAssets/"

View File

@@ -0,0 +1,59 @@
//! Example: Query world resources from the database
//!
//! Run with: cargo run --example query_world_resources
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use std::env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Connect to database
let database_url = env::var("DATABASE_URL").unwrap_or_else(|_| "../cursebreaker.db".to_string());
let mut conn = SqliteConnection::establish(&database_url)?;
// Use the schema
use cursebreaker_parser::schema::world_resources::dsl::*;
// Query all resources
#[derive(Queryable, Debug)]
struct WorldResource {
item_id: i32,
pos_x: f32,
pos_y: f32,
}
let results = world_resources
.limit(10)
.load::<WorldResource>(&mut conn)?;
println!("Found {} resources (showing first 10):", results.len());
println!();
for resource in results {
println!("Resource:");
println!(" Item ID: {}", resource.item_id);
println!(" Position: ({:.2}, {:.2})", resource.pos_x, resource.pos_y);
println!();
}
// Query all resources
println!("\n--- All world resources ---");
let all_results = world_resources
.load::<WorldResource>(&mut conn)?;
println!("Found {} total resources", all_results.len());
// Group by item_id
use std::collections::HashMap;
let mut item_counts: HashMap<i32, usize> = HashMap::new();
for resource in &all_results {
*item_counts.entry(resource.item_id).or_insert(0) += 1;
}
println!("\nResource counts by item ID:");
for (i_id, count) in item_counts {
println!(" Item {}: {} instances", i_id, count);
}
Ok(())
}

View File

@@ -0,0 +1 @@
DROP TABLE world_resources;

View File

@@ -0,0 +1,14 @@
-- World resources table - stores harvestable resources from Unity scenes
CREATE TABLE world_resources (
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
scene_name TEXT NOT NULL,
pos_x REAL NOT NULL,
pos_y REAL NOT NULL,
pos_z REAL NOT NULL,
object_name TEXT NOT NULL
);
CREATE INDEX idx_world_resources_item_id ON world_resources(item_id);
CREATE INDEX idx_world_resources_scene ON world_resources(scene_name);
CREATE INDEX idx_world_resources_position ON world_resources(pos_x, pos_z);

View File

@@ -0,0 +1,16 @@
-- Revert to original structure
DROP TABLE world_resources;
CREATE TABLE world_resources (
id INTEGER PRIMARY KEY,
item_id INTEGER NOT NULL,
scene_name TEXT NOT NULL,
pos_x REAL NOT NULL,
pos_y REAL NOT NULL,
pos_z REAL NOT NULL,
object_name TEXT NOT NULL
);
CREATE INDEX idx_world_resources_item_id ON world_resources(item_id);
CREATE INDEX idx_world_resources_scene ON world_resources(scene_name);
CREATE INDEX idx_world_resources_position ON world_resources(pos_x, pos_z);

View File

@@ -0,0 +1,13 @@
-- Drop the old table
DROP TABLE world_resources;
-- Recreate with simplified structure - no id, no scene_name, no object_name, only 2D coordinates
CREATE TABLE world_resources (
item_id INTEGER NOT NULL,
pos_x REAL NOT NULL,
pos_y REAL NOT NULL,
PRIMARY KEY (item_id, pos_x, pos_y)
) WITHOUT ROWID;
CREATE INDEX idx_world_resources_item_id ON world_resources(item_id);
CREATE INDEX idx_world_resources_position ON world_resources(pos_x, pos_y);

View File

@@ -5,13 +5,16 @@
//! - Parsing Unity scenes with type filtering //! - Parsing Unity scenes with type filtering
//! - Extracting Interactable_Resource components only //! - Extracting Interactable_Resource components only
//! - Computing world transforms //! - Computing world transforms
//! - Saving resource locations to the database
use cursebreaker_parser::InteractableResource; use cursebreaker_parser::InteractableResource;
use unity_parser::{UnityProject, TypeFilter}; use unity_parser::{UnityProject, TypeFilter};
use std::path::Path; use std::path::Path;
use unity_parser::log::DedupLogger; use unity_parser::log::DedupLogger;
use log::{info, error, LevelFilter}; use log::{info, error, warn, LevelFilter};
use std::env; use std::env;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
let logger = DedupLogger::new(); let logger = DedupLogger::new();
@@ -55,21 +58,45 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
unity_parser::compute_world_transforms(&mut scene.world, &scene.entity_map); unity_parser::compute_world_transforms(&mut scene.world, &scene.entity_map);
info!(" ✓ World transforms computed"); info!(" ✓ World transforms computed");
// Find all entities that have Interactable_Resource // Save resources to database
log::logger().flush(); info!("💾 Saving resources to database...");
let database_url = env::var("DATABASE_URL").unwrap_or_else(|_| "../cursebreaker.db".to_string());
let mut conn = SqliteConnection::establish(&database_url)?;
scene.world // Use diesel schema
.query_all::<(&InteractableResource, &unity_parser::WorldTransform, &unity_parser::GameObject)>() use cursebreaker_parser::schema::world_resources;
.for_each(|(resource, transform, object)| {
info!(" 📦 Resource: \"{}\"", object.name);
info!(" • typeId: {}", resource.type_id);
// Extract world position from WorldTransform // Clear the entire table (it's regenerated each run)
let world_pos = transform.position(); diesel::delete(world_resources::table).execute(&mut conn)?;
info!(" • Position: ({:.2}, {:.2}, {:.2})", world_pos.x, world_pos.y, world_pos.z);
log::logger().flush();
});
let mut resource_count = 0;
// Insert all resources in a transaction
conn.transaction::<_, diesel::result::Error, _>(|conn| {
scene.world
.query_all::<(&InteractableResource, &unity_parser::WorldTransform, &unity_parser::GameObject)>()
.for_each(|(resource, transform, object)| {
let world_pos = transform.position();
info!(" 📦 Resource: \"{}\"", object.name);
info!(" • typeId: {}", resource.type_id);
info!(" • Position: ({:.2}, {:.2})", world_pos.x, world_pos.z);
// Insert: store x and z (as y) coordinates only
let _ = diesel::insert_into(world_resources::table)
.values((
world_resources::item_id.eq(resource.type_id as i32),
world_resources::pos_x.eq(world_pos.x as f32),
world_resources::pos_y.eq(world_pos.z as f32),
))
.execute(conn);
resource_count += 1;
});
Ok(())
})?;
info!("✅ Saved {} resources to database", resource_count);
log::logger().flush(); log::logger().flush();
} }
Err(e) => { Err(e) => {

View File

@@ -155,6 +155,14 @@ diesel::table! {
} }
} }
diesel::table! {
world_resources (item_id, pos_x, pos_y) {
item_id -> Integer,
pos_x -> Float,
pos_y -> Float,
}
}
diesel::joinable!(crafting_recipe_items -> crafting_recipes (recipe_id)); diesel::joinable!(crafting_recipe_items -> crafting_recipes (recipe_id));
diesel::joinable!(crafting_recipe_items -> items (item_id)); diesel::joinable!(crafting_recipe_items -> items (item_id));
diesel::joinable!(crafting_recipes -> items (product_item_id)); diesel::joinable!(crafting_recipes -> items (product_item_id));
@@ -175,4 +183,5 @@ diesel::allow_tables_to_appear_in_same_query!(
quests, quests,
shops, shops,
traits, traits,
world_resources,
); );

View File

@@ -15,14 +15,12 @@ use serde_yaml::Mapping;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InteractableResource { pub struct InteractableResource {
pub max_health: i64,
pub type_id: i64, pub type_id: i64,
} }
impl UnityComponent for InteractableResource { impl UnityComponent for InteractableResource {
fn parse(yaml: &Mapping, _ctx: &ComponentContext) -> Option<Self> { fn parse(yaml: &Mapping, _ctx: &ComponentContext) -> Option<Self> {
Some(Self { 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), type_id: unity_parser::yaml_helpers::get_i64(yaml, "typeId").unwrap_or(0),
}) })
} }