Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Loading and Updating Individual Objects

This chapter explains how to move one object definition into or out of a database. These operations are different from the startup import that creates a complete database from a source directory.

Before you continue: mooR does not watch files on disk. The functions in this chapter receive objdef text and apply it only when something explicitly calls them. To understand how a complete source directory becomes a new database, see Starting a MOO from Objdef Source.

The most important choice in this chapter is the difference between loading and reloading:

  • load_object can create an object or merge a definition into an existing object, using options to decide what happens when the database and definition differ.
  • reload_object replaces an existing object's definition. Properties and verbs missing from the supplied definition are removed.

If you only want to edit a verb or property in the running MOO, use the normal authoring tools instead. You do not need objdef loading for ordinary live programming.

If you already have a .moo file, the Meadow object browser can upload it to replace a selected object. The moor-emh administration tool can load files while the regular server is stopped.

How Individual Object Definitions Work

While command-line import/export handles entire databases, mooR also provides built-in functions for working with individual objects from within the MOO itself. This enables more surgical operations like cherry-picking specific objects, sharing individual utilities, or performing targeted updates.

Within object definition files and directories, each object is described as a structured text representation that includes all its properties, verbs, and metadata. When you work with individual objects using dump_object and load_object, you're working with pieces of this broader object definition format.

When you dump an object, you get a list of strings that completely describe that object in the same format used in object definition files. When you load that definition back, mooR can recreate the object exactly as it was, or merge it with existing objects according to your preferences.

Basic Usage

Dumping Objects

The dump_object function converts any object into its text representation:

// Dump a single object
definition = dump_object(#123);
// Returns a list of strings representing the object

// Save the definition for later use
player.my_object_backup = dump_object($my_widget);

dump_object returns text to the MOO program as a list of lines. It does not choose a filename or write directly to the server's filesystem. A client or administration tool can save those lines as a .moo file.

Loading Objects

The load_object function recreates objects from their text definitions:

load_object expects the definition as a list of text lines already in memory. It does not accept a filesystem path or search the source directory. Tools such as moor-emh can read a file and pass its contents to the loader.

// Load a simple object using the object ID from the dump
new_obj = load_object(definition);

// Create a new object with next available ID (ignoring dump's ID)
new_obj = load_object(definition, [], 0);

// Update an existing object
new_obj = load_object(definition, [], #456);

// Create an anonymous object
new_obj = load_object(definition, [], 1);

// Create a UUID-based object
new_obj = load_object(definition, [], 2);

// Load with options and object kind
new_obj = load_object(definition, [
    `constants -> [`MY_CONSTANT -> "value"]  // Set compilation constants
], 0);  // Create new with next ID

Reloading Objects

The reload_object function replaces an existing object with a new definition from objdef format. Properties and verbs not present in the new definition are removed.

Reloading is replacement, not synchronization. Anything on the existing object that is absent from the supplied definition is deleted. Export or dump the object first if you might need to recover its current contents.

obj reload_object(list object_lines [, map constants] [, obj target])
  • object_lines: A list of strings containing the objdef text for the object.
  • constants: (Optional) Map or alist of constant substitutions available during compilation.
  • target: (Optional) Object to replace. When omitted, uses the object ID from the objdef definition.

reload_object is wizard-only and returns the loaded object ID.

Parsing Constants

map parse_objdef_constants(str|list lines)

Parses constants from objdef content and returns a map of constant name to value. This is useful when you want to extract constants.moo definitions or validate constants before calling load_object.

Raises E_INVARG with a formatted error if parsing or compilation fails.

Complete Options Reference

The load_object function accepts an optional second argument: a map of options that controls how loading works. This map can contain any combination of the following options.

Note about Examples: The examples in this documentation use symbols (like 'dry_run), boolean values (true/ false), and maps (like ['key -> "value"]) which are mooR extensions. If your mooR instance is not configured with these extension features enabled, you can use strings ( "dry_run"), integers (1/0), and alists ({{"key", "value"}, ...}) instead throughout - they work identically.

The load_object function accepts up to three arguments:

load_object(definition)                    // Use object ID from dump
load_object(definition, options)           // Use object ID from dump with options
load_object(definition, options, object_kind) // Specify where to load

Third Parameter - Object Kind:

The optional third parameter specifies where to create/load the object:

ValueDescription
(omitted)Use the object ID from the dump file
0Create new object with next available ID (NextObjid)
1Create anonymous object
2Create UUID-based object (requires use_uuobjids)
Object IDLoad into the specified existing object

Options Map:

The second parameter is a map with the following options:

OptionTypeDefaultDescription
constantsMap[]Compilation constants available during verb compilation
conflict_modeSymbol'clobberHow to handle conflicts: 'clobber, 'skip, 'detect
dry_runBooleanfalseTest mode - don't make actual changes
return_conflictsBooleanfalseReturn detailed conflict information
overridesList{}Force specific entities to use clobber mode

Object Kind (Third Parameter)

The third parameter to load_object controls where the object is created or loaded. This parameter is optional and has different behaviors depending on the value:

Using Object ID from Dump (default):

// When omitted, use the object ID specified in the dump
new_obj = load_object(definition);
new_obj = load_object(definition, [`conflict_mode -> `skip]);

Create New Numbered Object (0):

// Allocate next available object ID, ignoring dump's ID
new_obj = load_object(definition, [], 0);

// With options
copy = load_object(dump_object($widget), [`constants -> my_constants], 0);

When to use 0 (NextObjid):

  • Duplicating an object within the same database
  • Importing objects that might have conflicting IDs
  • Creating instances from a template definition
  • Sharing object packages between different MOO servers

Create Anonymous Object (1):

// Create anonymous object (requires anonymous_objects feature)
anon_obj = load_object(definition, [], 1);

Anonymous objects don't have traditional object IDs and are used for temporary or transient data that shouldn't persist in the main object hierarchy.

Create UUID-Based Object (2):

// Create UUID-based object (requires use_uuobjids configuration)
uuid_obj = load_object(definition, [], 2);

UUID-based objects use universally unique identifiers, useful for distributed systems or when object IDs need to be globally unique.

Load into Existing Object:

// Update an existing object with new definition
load_object(definition, [], #123);

// With options
load_object(new_widget_def, [`conflict_mode -> `skip], $my_widget);

When to load into existing object:

  • Updating an existing object with a new version
  • Applying a template to an existing object
  • Restoring an object from a backup
  • Syncing an object with an external definition

Compilation Constants

Option: constants Type: Map Default: Empty map

Provide constants that will be available when resolving object references in property values:

load_object(definition, [
    `constants -> [
        `THING -> #789,
        `ROOM -> #456,
        `PLAYER -> #123,
        `WIZARD -> #3,
    ]
]);

These constants are used to resolve symbolic object references in property values, similar to the constants.moo file in object definition directories. They allow object definitions to use readable names instead of hardcoded object numbers.

Conflict Handling

What is a Conflict?

A conflict occurs when you try to load an object definition that contains data that differs from what already exists in the database. For example:

  • Property conflicts: The object definition sets description = "A red ball" but the existing object has description = "A blue sphere"
  • Verb conflicts: The definition includes a look verb with different code than the existing look verb
  • Flag conflicts: The definition specifies different object flags (like wizard/programmer status) than currently set
  • Ownership conflicts: The definition assigns different owners to properties or verbs

Why Conflicts Matter

Conflicts are important because they represent potential data loss or unintended changes:

  • User customizations: Players may have customized descriptions or properties that you don't want to overwrite
  • Site-specific modifications: Your MOO may have local changes to core objects that should be preserved
  • Version differences: Loading an older object definition might downgrade newer functionality
  • Security implications: Changing ownership or permissions could create security vulnerabilities

Option: conflict_mode Type: Symbol Default: clobber Values: clobber, skip, detect

Controls what happens when the definition conflicts with existing object data:

// Overwrite everything (default) - DESTROYS existing conflicting data
load_object(definition, [`conflict_mode -> `clobber]);

// Skip conflicting parts, only add new properties/verbs - PRESERVES existing data
load_object(definition, [`conflict_mode -> `skip]);

// Don't make changes, just report what conflicts exist - SAFE inspection
load_object(definition, [`conflict_mode -> `detect]);

When to Use Each Mode:

  • clobber: When you want to completely replace objects with canonical versions (fresh installs, reverting changes)
  • skip: When adding new functionality while preserving existing customizations (package updates, safe installs)
  • detect: When you need to understand what would change before deciding how to proceed (conflict analysis, impact assessment)

Dry Run Mode

Option: dry_run Type: Boolean Default: false

Test what would happen without actually making changes:

// See what conflicts would occur
result = load_object(definition, [
    `dry_run -> true,
    `return_conflicts -> true
]);
// Examine result[2] for conflict details

Selective Overrides

Option: overrides Type: List of {object, entity} pairs Default: Empty list

Force specific parts to be overwritten even in skip mode:

load_object(definition, [
    `conflict_mode -> `skip,
    `overrides -> [
        {#123, {'property_value, 'description}},
        {#123, {'verb_program, {'look, 'l}}},
        {#456, 'object_flags}
    ]
]);

Available entity types (see Entity Reference below for complete details):

  • object_flags - Object permission flags
  • builtin_props - Built-in properties like name, description
  • parentage - Parent/child relationships
  • {'property_def, name} - Property definition
  • {'property_value, name} - Property value
  • {'property_flag, name} - Property permissions
  • {'verb_def, {names}} - Verb definition (names is list like {'look, 'l})
  • {'verb_program, {names}} - Verb code

Detailed Results

Option: return_conflicts Type: Boolean Default: false

Get detailed information about the loading process:

result = load_object(definition, [`return_conflicts -> true]);
// result[1]: success (boolean)
// result[2]: conflicts (list of conflict details)
// result[3]: loaded objects (list of object numbers)

Entity Reference

When working with the overrides option, you specify entities using symbol-based identifiers. Each entity type targets a specific part of an object's data:

Object-Level Entities

Object Flags - 'object_flags

  • Description: Object permission flags (user, programmer, wizard, fertile, readable, writeable)
  • Example: {#123, 'object_flags}
  • Use case: Changing an object's basic permissions

Built-in Properties - 'builtin_props

  • Description: Built-in object properties like name, location, owner, parent
  • Example: {#123, 'builtin_props}
  • Use case: Updating core object metadata

Parentage - 'parentage

  • Description: Parent-child inheritance relationships
  • Example: {#123, 'parentage}
  • Use case: Changing which object this inherits from

Property-Level Entities

Property Definition - 'property_def

  • Description: Complete property definition (creates new property with permissions and initial value)
  • Format: {'property_def, property_name} (list with type symbol and property name)
  • Example: {#123, {'property_def, 'description}}
  • Use case: Adding or completely replacing a property definition

Property Value - 'property_value

  • Description: Just the value of a property (preserves existing permissions)
  • Format: {'property_value, property_name} (list with type symbol and property name)
  • Example: {#123, {'property_value, 'description}}
  • Use case: Updating content while keeping permissions

Property Flags - 'property_flag

  • Description: Just the permissions flags of a property (preserves existing value)
  • Format: {'property_flag, property_name} (list with type symbol and property name)
  • Example: {#123, {'property_flag, 'description}}
  • Use case: Changing who can read/write a property

Verb-Level Entities

Verb Definition - 'verb_def

  • Description: Complete verb definition (names, permissions, argument spec)
  • Format: {'verb_def, {verb_names}} (list with type symbol and list of verb names)
  • Example: {#123, {'verb_def, {'look, 'l, 'examine}}}
  • Use case: Adding new verb or changing verb metadata
  • ⚠️ Important: Verb identity is determined by the complete set of names. If you add or remove aliases, it becomes a different verb.

Verb Program - 'verb_program

  • Description: Just the code/program of a verb (preserves existing definition)
  • Format: {'verb_program, {verb_names}} (list with type symbol and list of verb names)
  • Example: {#123, {'verb_program, {'look, 'l}}}
  • Use case: Updating verb code while keeping permissions
  • ⚠️ Important: Must specify the exact same names as the existing verb to update it.

Entity Usage Examples

// Force update just the description property value, skip everything else
load_object(definition, [
    `conflict_mode -> `skip,
    `overrides -> [
        {$my_object, {'property_value, 'description}}
    ]
]);

// Update verb code but preserve existing permissions
load_object(definition, [
    `conflict_mode -> `skip,
    `overrides -> [
        {$my_object, {'verb_program, {'main_function, 'main}}}
    ]
]);

// Complex selective update preserving user customizations
load_object(package_update, [
    `conflict_mode -> `skip,           // Preserve existing data by default
    `overrides -> [
        // Force update these core components
        {$package_obj, {'verb_program, {'init, 'initialize}}},
        {$package_obj, {'property_value, 'version}},
        {$package_obj, 'object_flags}   // Simple entities are just symbols
    ]
], $package_obj);  // Load into existing package object

Entity Selection Tips

Property vs Value vs Flag:

  • Use {'property_def, name} when adding completely new properties
  • Use {'property_value, name} when updating content but preserving permissions
  • Use {'property_flag, name} when changing access control but preserving content

Verb Def vs Program:

  • Use {'verb_def, {names}} when changing verb names, permissions, or argument specifications
  • Use {'verb_program, {names}} when just updating the code

Important: Verb Name Behavior

Verb Identity is Based on Complete Name Sets

When working with verbs, it's crucial to understand that verb identity is determined by the complete set of names, not individual name matches. This has significant implications:

Scenario: Adding Aliases

// Existing verb in database: {"look", "l"}

// Objdef file contains:
verb "look l examine" (this none none) owner: WIZARD flags: "rxd"
  player:tell("You look around.");
endverb

// Result: Creates a NEW verb with all three names
// The old {"look", "l"} verb remains unchanged
// You now have TWO verbs that respond to "look" and "l"!

Scenario: Removing Aliases

// Existing verb in database: {"look", "l", "examine"}

// Objdef file contains:
verb "look l" (this none none) owner: WIZARD flags: "rxd"
  player:tell("You look around.");
endverb

// Result: Creates a NEW verb with just two names
// The old {"look", "l", "examine"} verb remains unchanged
// You now have TWO verbs with overlapping names!

Why This Behavior Exists

The loader cannot determine intent when verb names change:

  • Did you want to add an alias to the existing verb?
  • Did you want to create a new verb that happens to share some names?
  • Did you want to rename the verb entirely?

Since the intent is ambiguous, the loader treats different name sets as different verbs.

Best Practices for Verb Management

Option 1: Manual Verb Updates

// To add an alias to an existing verb:
// 1. Use your MOO's verb management commands to modify the existing verb in-world
// 2. Then export to capture the change in objdef format

Option 2: Target Object Strategy

// Load into a temporary object first, then manually copy verbs
temp_obj = load_object(definition);
// Manually copy/update verbs as needed
// Then recycle temp_obj

Conflict Detection for Verbs

The conflict detection system will warn you about name overlaps:

result = load_object(definition, [`conflict_mode -> `detect, `return_conflicts -> true]);
// Check result[2] for verb conflicts before proceeding

This behavior ensures data safety at the cost of requiring more explicit management of verb aliases.

Practical Scenarios

Package Installation

Installing a new package that might conflict with existing objects:

// First, check for conflicts
result = load_object(package_def, [
    `dry_run -> true,
    `return_conflicts -> true
]);

if (result[1])
    // No conflicts, safe to install
    load_object(package_def);
else
    // Handle conflicts - perhaps ask user what to do
    player:tell("Package conflicts with: ", result[2]);
endif

Safe Updates

Updating an object while preserving user customizations:

// Update only the core functionality, skip user properties
load_object(new_version, [
    `conflict_mode -> `skip,
    `overrides -> [
        {$my_object, {'verb_program, {'main_function}}},
        {$my_object, {'property_value, 'version}}
    ]
], $my_object);  // Load into existing object

Database Migration

Moving objects between servers with different configurations:

// Export from source server
definitions = [];
for obj in (objects_to_migrate)
    definitions[obj] = dump_object(obj);
endfor

// Import on target server with appropriate constants
for obj in (keys(definitions))
    load_object(definitions[obj], [
        `constants -> [`THING -> #789, `ROOM -> #456, `PLAYER -> #123],
        `conflict_mode -> `skip  // Don't overwrite existing customizations
    ]);  // Uses object ID from dump by default
endfor

Conflict Resolution

Handling conflicts intelligently:

// Check what conflicts exist
result = load_object(new_package, [
    `conflict_mode -> `detect,
    `return_conflicts -> true
]);

// Process each conflict
for conflict in (result[2])
    obj = conflict[1];
    conflict_type = conflict[2];

    if (conflict_type == {'property_value, 'description})
        // Ask user whether to keep old or use new description
        // Note: :choose is a hypothetical verb to prompt the user
        choice = player:choose("Keep existing description?");
        // Handle based on choice...
    endif
endfor

Flag String Formats

When working with object and property flags in conflict reports or entity specifications, mooR uses readable string formats:

Object Flags

  • u - User flag
  • p - Programmer flag
  • w - Wizard flag
  • r - Read flag
  • W - Write flag (capital W)
  • f - Fertile flag

Example: "upw" means user, programmer, and wizard flags are set.

Property Flags

  • r - Read permission
  • w - Write permission
  • c - Chown permission

Example: "rw" means read and write permissions.

Verb Flags

  • r - Read permission
  • w - Write permission
  • x - Execute permission
  • d - Debug permission

Example: "rwx" means read, write, and execute permissions.

Best Practices

Version Control

Always include version information in your object definitions:

// Set version property before dumping
obj.version = "2.1.0";
obj.last_updated = time();
definition = dump_object(obj);

Backup Before Loading

Create backups before making significant changes:

backup = dump_object($important_object);
// Store backup somewhere safe
result = load_object(new_definition, [], $important_object);
if (!result[1])
    // Restore from backup if needed
    load_object(backup, [], $important_object);
endif

Test in Development First

Always test packages in a development environment:

// Load into test objects first
test_result = load_object(package, [
    `return_conflicts -> true
], $test_object);

// Only proceed to production if tests pass
if (test_passes(test_result))
    load_object(package, [], $production_object);
endif

Error Handling

The load_object function can return various errors. Always check the result:

try
    result = load_object(definition, options);
    if (typeof(result) == TYPE_LIST && !result[1])
        // Loading failed, check conflicts
        player:tell("Load failed due to conflicts: ", result[2]);
    else
        // Success
        player:tell("Object loaded successfully: #", result);
    endif
except error (E_INVARG)
    player:tell("Invalid object definition format");
except error (E_PERM)
    player:tell("Permission denied - wizard access required");
except error (ANY)
    player:tell("Unexpected error: ", error[2]);
endtry