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_objectcan create an object or merge a definition into an existing object, using options to decide what happens when the database and definition differ.reload_objectreplaces 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:
| Value | Description |
|---|---|
| (omitted) | Use the object ID from the dump file |
0 | Create new object with next available ID (NextObjid) |
1 | Create anonymous object |
2 | Create UUID-based object (requires use_uuobjids) |
| Object ID | Load into the specified existing object |
Options Map:
The second parameter is a map with the following options:
| Option | Type | Default | Description |
|---|---|---|---|
constants | Map | [] | Compilation constants available during verb compilation |
conflict_mode | Symbol | 'clobber | How to handle conflicts: 'clobber, 'skip, 'detect |
dry_run | Boolean | false | Test mode - don't make actual changes |
return_conflicts | Boolean | false | Return detailed conflict information |
overrides | List | {} | 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 hasdescription = "A blue sphere" - Verb conflicts: The definition includes a
lookverb with different code than the existinglookverb - 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 flagsbuiltin_props- Built-in properties like name, descriptionparentage- 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 flagp- Programmer flagw- Wizard flagr- Read flagW- Write flag (capital W)f- Fertile flag
Example: "upw" means user, programmer, and wizard flags are set.
Property Flags
r- Read permissionw- Write permissionc- Chown permission
Example: "rw" means read and write permissions.
Verb Flags
r- Read permissionw- Write permissionx- Execute permissiond- 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