Files
osiris/docs/CREATING_NEW_OBJECTS.md
Timur Gordon 87c556df7a wip
2025-10-29 16:52:33 +01:00

6.1 KiB

Guide: Creating New OSIRIS Objects

This guide explains how to properly create new object types in OSIRIS that integrate with the Rhai scripting engine and context storage.

Step-by-Step Process

1. Create the Object Module

Create a new file in the appropriate directory under src/objects/:

  • src/objects/legal/ for legal objects
  • src/objects/money/ for financial objects
  • src/objects/heroledger/ for HeroLedger objects
  • etc.

2. Define the Object Struct

CRITICAL: The struct MUST derive crate::DeriveObject to automatically implement the Object trait.

use crate::store::BaseData;
use serde::{Deserialize, Serialize};

/// Your object description
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, crate::DeriveObject)]
pub struct YourObject {
    /// Base data for object storage (REQUIRED)
    pub base_data: BaseData,
    
    /// Your custom fields
    pub title: String,
    pub status: YourStatus,
    // ... other fields
}

Required traits:

  • Debug - for debugging
  • Clone - required by Object trait
  • Serialize, Deserialize - for JSON serialization
  • PartialEq - for comparisons
  • crate::DeriveObject - CRITICAL - auto-implements Object trait

Required field:

  • base_data: BaseData - MUST be present for object storage

3. Implement Constructor and Methods

impl YourObject {
    /// Create a new object
    pub fn new(id: u32) -> Self {
        let base_data = BaseData::with_id(id, String::new());
        Self {
            base_data,
            title: String::new(),
            status: YourStatus::default(),
            // ... initialize other fields
        }
    }
    
    /// Fluent builder methods
    pub fn title(mut self, title: impl ToString) -> Self {
        self.title = title.to_string();
        self
    }
    
    // ... other methods
}

4. Create Rhai Bindings Module

Create rhai.rs in the same directory:

use ::rhai::plugin::*;
use ::rhai::{CustomType, Dynamic, Engine, EvalAltResult, Module, TypeBuilder};

use super::{YourObject, YourStatus};

/// Register your modules with the Rhai engine
pub fn register_your_modules(parent_module: &mut Module) {
    // Register custom types
    parent_module.set_custom_type::<YourObject>("YourObject");
    parent_module.set_custom_type::<YourStatus>("YourStatus");
    
    // Merge functions
    let your_module = exported_module!(rhai_your_module);
    parent_module.merge(&your_module);
}

#[export_module]
mod rhai_your_module {
    use super::YourObject;
    use ::rhai::EvalAltResult;
    
    // Constructor
    #[rhai_fn(name = "new_your_object", return_raw)]
    pub fn new_your_object(id: i64) -> Result<YourObject, Box<EvalAltResult>> {
        Ok(YourObject::new(id as u32))
    }
    
    // Builder methods
    #[rhai_fn(name = "title", return_raw)]
    pub fn set_title(
        obj: YourObject,
        title: String,
    ) -> Result<YourObject, Box<EvalAltResult>> {
        Ok(obj.title(title))
    }
    
    // Getters
    #[rhai_fn(name = "title", pure)]
    pub fn get_title(obj: &mut YourObject) -> String {
        obj.title.clone()
    }
}

// CustomType implementations
impl CustomType for YourObject {
    fn build(mut builder: TypeBuilder<Self>) {
        builder.with_name("YourObject");
    }
}

impl CustomType for YourStatus {
    fn build(mut builder: TypeBuilder<Self>) {
        builder.with_name("YourStatus");
    }
}

5. Update Module Exports

In mod.rs of your object category:

pub mod your_object;
pub mod rhai;

pub use your_object::{YourObject, YourStatus};
pub use rhai::register_your_modules;

In src/objects/mod.rs:

pub mod your_category;

pub use your_category::{YourObject, YourStatus};

6. Register in Engine (CRITICAL STEP)

In src/engine.rs, add the save registration in the OsirisPackage definition:

def_package! {
    pub OsirisPackage(module) : StandardPackage {
        // ... existing registrations ...
        
        // Add your object's save method
        FuncRegistration::new("save")
            .set_into_module(module, |ctx: &mut OsirisContext, obj: crate::objects::YourObject| ctx.save_object(obj));
        
        // ... existing registrations ...
        
        // Register your modules
        register_your_modules(module);
    }
}

Also add the import at the top of engine.rs:

use crate::objects::your_category::rhai::register_your_modules;

7. Create Example Script

Create examples/engine/XX_your_object.rhai:

print("=== Your Object Example ===\n");

// Get context
let ctx = get_context(["alice", "bob"]);

// Create object
let obj = new_your_object(1)
    .title("Example Title");

print("Object created: " + obj.title());

// Store in context
ctx.save(obj);
print("Object stored");

print("\n=== Example Complete ===");

Checklist

Before considering your object complete, verify:

  • Struct derives crate::DeriveObject
  • Struct has base_data: BaseData field
  • Rhai module created with register_*_modules function
  • Custom types registered with set_custom_type
  • Module exported in mod.rs files
  • Save method registered in src/engine.rs
  • Module registration added to OsirisPackage in src/engine.rs
  • Example script created and tested
  • Example runs successfully with cargo run --example engine examples/engine/XX_your_object.rhai

Common Mistakes to Avoid

  1. Forgetting crate::DeriveObject - Without this, the Object trait won't be implemented
  2. Missing base_data field - Required for all storable objects
  3. Not registering save in engine.rs - The save method MUST be in engine.rs, not context.rs
  4. Not calling set_custom_type - Rhai won't recognize your type
  5. Not merging the exported module - Your functions won't be available

Example: Contract Object

See the Contract object implementation as a reference:

  • Struct: src/objects/legal/contract.rs
  • Rhai bindings: src/objects/legal/rhai.rs
  • Module exports: src/objects/legal/mod.rs and src/objects/mod.rs
  • Engine registration: src/engine.rs (line ~110 and ~138)
  • Example: examples/engine/12_contract.rhai