Core API Reference

This page documents the types and functions available to plugin and script authors.
All of these are exported from jsr:@ursamu/ursamu.

Imports

// Functions and classes
import { addCmd, registerPluginRoute, mu, createObj, DBO, dbojs } from "jsr:@ursamu/ursamu";

// Types (import type — zero runtime cost)
import type { ICmd, IPlugin, IDBObj, IUrsamuSDK } from "jsr:@ursamu/ursamu";

IDBObj

Every object in the game database — players, rooms, exits, things — is an
IDBObj.

interface IDBObj {
  id: string;                    // Numeric DB ID, e.g. "1", "42"
  name?: string;                 // Display name (top-level shortcut to state.name)
  flags: Set<string>;            // Flag set, e.g. Set { "player", "connected" }
  location?: string;             // ID of containing room or object
  state: Record<string, unknown>; // All stored data — desc, stats, attrs, etc.
  contents: IDBObj[];            // Objects contained by this object
}
Field Description
id Numeric string, e.g. "1". Use this when calling DB methods or u.teleport().
flags Use flags.has("wizard"), not array methods.
state Arbitrary key-value store. Cast values on read: obj.state.gold as number.
contents Populated at query time — may be empty even if the object has contents depending on context.

ICmd

Passed to addCmd() to register a command.

interface ICmd {
  name:     string;
  pattern:  string | RegExp;
  lock?:    string;
  exec:     (u: IUrsamuSDK) => void | Promise<void>;
  help?:    string;
  hidden?:  boolean;
  category?: string;
}
Field Description
name Human-readable name; appears in help listings unless hidden is true.
pattern RegExp (or string converted to RegExp) matched against raw player input. Capture groups map to u.cmd.args[0], u.cmd.args[1], …
lock Lock expression evaluated before exec. If the check fails the command silently does nothing. See the Lock Expressions guide.
exec Async-safe — you can await inside freely.
help Shown when a player runs help <name>.
hidden Hides the command from help and @commands listings.
category Groups related commands together in listings.

Example

import { addCmd } from "jsr:@ursamu/ursamu";
import type { IUrsamuSDK } from "jsr:@ursamu/ursamu";

addCmd({
  name: "+greet",
  pattern: /^\+greet\s+(.+)/i,
  lock: "connected",
  exec: (u: IUrsamuSDK) => {
    u.send(`Hello, ${u.cmd.args[0]}!`);
  },
  help: "+greet <name>\nSays hello.",
});

IUrsamuSDK

The u object injected into every command’s exec() function and into
sandbox scripts. It provides everything the command needs — actor info, DB
access, messaging, and utility helpers.

interface IUrsamuSDK {
  state:    Record<string, unknown>;   // Shared state bag for the command run
  socketId?: string;                   // WebSocket ID of the connected player
  me:       IDBObj;                    // The actor who triggered the command
  here:     IDBObj & { broadcast(msg: string): void };  // Actor's current room
  target?:  IDBObj & { broadcast(msg: string): void };  // Optional pre-resolved target
  cmd:      { name: string; args: string[]; switches?: string[]; original?: string };
  // ... namespaces below
}

u.me / u.here

u.me.id          // "42"
u.me.name        // "Alice"
u.me.location    // room ID
u.me.flags       // Set<string>
u.me.state       // all stored player data
u.me.contents    // inventory

u.here.id        // room ID
u.here.name      // room name
u.here.state     // room data (desc, exits, etc.)
u.here.contents  // everyone and everything in the room
u.here.broadcast("Message to everyone in the room.");

Check flags:

u.me.flags.has("superuser")   // first player ever created
u.me.flags.has("admin")       // or "wizard" — equivalent
u.me.flags.has("builder")
u.me.flags.has("player")
u.me.flags.has("connected")   // currently online

u.db

Database operations. All methods are async.

u.db.search(query)

const results: IDBObj[] = await u.db.search(query);

query can be:

  • A string — searched against name, ID, and flags
  • An object — field filter, e.g. { flags: ["room"] }
// Find all rooms
const rooms = await u.db.search({ flags: ["room"] });

// Find by name fragment
const matches = await u.db.search("Town Square");

u.db.create(template)

const obj: IDBObj = await u.db.create({
  name: "Magic Sword",
  flags: new Set(["thing"]),
  location: u.me.id,
  state: { desc: "A gleaming sword.", damage: 5 },
  contents: [],
});

u.db.modify(id, op, data)

Updates fields on an object. The op argument must be one of "$set", "$unset", or "$inc" — any other value is rejected.

// Merge one field (preferred — precise, no full-object rewrite)
await u.db.modify(u.me.id, "$set", { "data.gold": 100 });

// Replace full state (always spread to preserve other fields)
await u.db.modify(u.me.id, "$set", { data: { ...u.me.state, gold: 100 } });

// Increment a number field
await u.db.modify(u.me.id, "$inc", { "data.deaths": 1 });

// Remove a field
await u.db.modify(u.me.id, "$unset", { "data.tempFlag": "" });

// Update name or location (these are top-level fields)
await u.db.modify(obj.id, "$set", { name: "New Name" });
await u.db.modify(obj.id, "$set", { location: destinationId });

u.db.destroy(id)

await u.db.destroy(obj.id);

u.util

Utility helpers.

u.util.target(actor, query, global?)

Resolves a name or #id reference to an IDBObj. Searches the actor’s
inventory then the current room. Pass global: true to search the whole DB.

const obj = await u.util.target(u.me, u.cmd.args[0]);
if (!obj) return u.send("I don't see that here.");

u.util.displayName(obj, actor)

Returns the display name of obj as seen by actor, applying moniker
substitutions if set.

u.send(`You see ${u.util.displayName(target, u.me)}.`);

u.util.stripSubs(str)

Strips MUSH color codes (%cX, %n, %r, %t, %b) and raw ANSI escapes.
Useful for measuring the true display length of a string.

const plain = u.util.stripSubs("%chBold text%cn");
// → "Bold text"

u.util.center(str, length, filler?)

Centers str within length characters, optionally padding with filler.

u.send(u.util.center("TITLE", 78, "-"));
// → "--------------------------------TITLE---------------------------------"

u.util.ljust(str, length, filler?) / u.util.rjust(str, length, filler?)

Left-pads or right-pads a string.

u.send(u.util.ljust("Name", 20) + u.util.rjust("100", 10));

u.util.sprintf(format, ...args)

Printf-style formatting.

u.send(u.util.sprintf("%-20s %5d gp", player.name, gold));

u.cmd

Populated by the command parser before exec is called.

u.cmd.name       // "look"
u.cmd.args       // string[] — capture groups from the pattern RegExp
u.cmd.switches   // string[] — e.g. ["quiet"] from "@set/quiet ..."
u.cmd.original   // The raw input string the player typed

u.auth

Authentication helpers for scripts that need to verify or change passwords.

// Verify a password
const ok: boolean = await u.auth.verify(u.me.name!, "mypassword");

// Hash a password (bcrypt)
const hashed: string = await u.auth.hash("newpassword");

// Change a player's password (use carefully — no confirmation prompt)
await u.auth.setPassword(u.me.id, "newpassword");

// Log in a player (rarely needed in scripts)
await u.auth.login(u.me.id);

u.sys

Server administration methods. Most are locked behind the wizard or
superuser flag in system scripts.

// Set a config value (only keys whitelisted in the engine are accepted)
await u.sys.setConfig("server.name", "My Game");

// Disconnect a player by socket ID
await u.sys.disconnect(socketId);

// Server uptime in milliseconds
const ms: number = await u.sys.uptime();

// Reboot or shut down (DANGEROUS — confirms nothing)
await u.sys.reboot();
await u.sys.shutdown();

// In-game calendar
const t: IGameTime = await u.sys.gameTime();
// t.year, t.month (1-12), t.day (1-28), t.hour (0-23), t.minute (0-59)

await u.sys.setGameTime({ year: 1340, month: 6, day: 15, hour: 8, minute: 0 });

u.chan

Channel management. Players can join/leave channels and admins can create and
configure them.

// Join a channel (alias is the local shorthand, e.g. "pub")
await u.chan.join("Public", "pub");

// Leave by alias
await u.chan.leave("pub");

// List all channels the actor is a member of
const channels = await u.chan.list();

// Admin — create a channel
await u.chan.create("Staff", { header: "%ch[STAFF]%cn", hidden: true });

// Admin — destroy a channel
await u.chan.destroy("Staff");

// Admin — update channel settings
await u.chan.set("Public", { header: "%ch[PUB]%cn", masking: false });

// Get recent channel messages
const history = await u.chan.history("public");        // last 20 messages (default)
const history = await u.chan.history("public", 50);    // last 50 messages
// → [{ id: string, playerName: string, message: string, timestamp: number }, ...]

u.bb

Bulletin board access.

// List all boards
const boards = await u.bb.listBoards();
// → [{ id, name, description, order, postCount, newCount }, ...]

// List posts on a board
const posts = await u.bb.listPosts(boardId);
// → [{ id, num, subject, authorName, date, edited? }, ...]

// Read a post (by board + post number)
const post = await u.bb.readPost(boardId, postNum);
// → { id, subject, body, authorName, date, edited? } | null

// Post a message
await u.bb.post(boardId, "Subject line", "Body text.");

// Edit a post you authored
await u.bb.editPost(boardId, postNum, "New body text.");

// Delete a post (owner or admin)
await u.bb.deletePost(boardId, postNum);

// Count unread posts
const total: number = await u.bb.totalNewCount();

u.events

Emit custom events and register handlers by attribute name.

// Emit an event (other scripts can listen)
await u.events.emit("game:levelup", { playerId: u.me.id, level: 5 });

// Register a listener (stores a script key to call when the event fires)
const handlerId = await u.events.on("game:levelup", "scripts/levelup-handler");

u.attr

Reads soft-coded &ATTR values stored on objects.

// Returns the string value, or null if not set
const bio: string | null = await u.attr.get(u.me.id, "FINGER-INFO");
const desc = await u.attr.get(objectId, "SHORT-DESC");

// Attribute names are case-insensitive
const val = await u.attr.get(objectId, "onenter");   // same as "ONENTER"

u.mail (sandbox scripts only)

Mail system access. Available in system/scripts/ sandbox context.
For native addCmd handlers, import mail from the database service directly.

// Send a message
await u.mail.send({
  from:    `#${u.me.id}`,           // dbref format: "#42"
  to:      [`#${recipientId}`],     // array of dbrefs
  cc:      [`#${ccId}`],            // optional
  subject: "Guild meeting",
  message: "Tonight at 8pm.",
  read:    false,
  date:    Date.now(),
});

// Notify recipient in-game
u.send(`%chMAIL:%cn You have new mail from ${u.util.displayName(u.me, u.me)}.`, recipientId);

// Read inbox
const inbox = await u.mail.read({ to: { $in: [`#${u.me.id}`] } });
inbox.sort((a, b) => b.date - a.date);

// Delete
await u.mail.delete(messageId);

// Update (e.g. mark as read)
await u.mail.modify({ id: messageId }, "$set", { read: true });

IMail shape:

{
  id?:      string;
  from:     string;     // "#42"
  to:       string[];   // ["#7", "#8"]
  cc?:      string[];
  bcc?:     string[];
  subject:  string;
  message:  string;
  read:     boolean;
  date:     number;     // Date.now() timestamp
}

u.eval

Evaluates a stored &ATTR as a script and returns its output as a string.

// Evaluate &FORMULA on object #42
const result = await u.eval("#42", "FORMULA", ["arg1", "arg2"]);
u.send(`Result: ${result}`);

// Evaluate an attribute on the actor
const score = await u.eval(u.me.id, "SCORE-FORMULA");

u.forceAs

Executes a command as another object. Requires wizard or admin privilege — enforce
this in your script; the SDK does not check automatically.

if (!u.me.flags.has("wizard") && !u.me.flags.has("admin") && !u.me.flags.has("superuser")) {
  u.send("Permission denied.");
  return;
}

await u.forceAs(npcId, "say Welcome, traveler!");
await u.forceAs(roomId, "look");

Top-level methods

These are direct properties on u, not namespaced.

u.send(message, target?, options?)

Sends message to the actor, or to target (DB ID) if provided.

u.send("You say, \"Hello!\"");
u.send("Whispered message.", otherPlayerId);

u.broadcast(message, options?)

Sends message to everyone in the actor’s current room.

u.broadcast(`${u.me.name} waves.`);

u.setFlags(target, flags)

Sets or clears flags on a target. Prefix with ! to remove.

await u.setFlags(u.me.id, "builder");      // add "builder"
await u.setFlags(u.me.id, "!builder");     // remove "builder"
await u.setFlags(obj.id, "dark");

u.checkLock(target, lock)

Evaluates a lock expression against a target. Returns true if the lock passes.

const canEnter = await u.checkLock(u.me, "builder|wizard");

See the Lock Expressions guide for syntax.

u.teleport(target, destination)

Moves target (DB ID) to destination (DB ID).

await u.teleport(u.me.id, "1");  // send actor to room #1

u.force(command)

Executes command as if the actor typed it.

u.force("look");

u.execute(command)

Executes command as the server (no actor context).

u.execute("@pemit #3=Server message.");

u.trigger(target, attr, args?)

Triggers an attribute script on target.

await u.trigger(room.id, "onEnter", [u.me.id]);

u.canEdit(actor, target)

Returns true if actor has permission to edit target.

const ok = await u.canEdit(u.me, target);
if (!ok) return u.send("Permission denied.");

IPlugin

The interface your plugin’s exported object must satisfy.

interface IPlugin {
  name:         string;
  version:      string;
  description?: string;
  init?:        () => boolean | Promise<boolean>;
  remove?:      () => void   | Promise<void>;
}
import type { IPlugin } from "jsr:@ursamu/ursamu";
import "./commands.ts";

export const plugin: IPlugin = {
  name:        "my-plugin",
  version:     "1.0.0",
  description: "Does cool things.",
  init: async () => {
    // Startup logic — seed data, connect to external services, etc.
    return true;   // return false to abort loading
  },
};

Exported functions

These are the top-level exports you import from jsr:@ursamu/ursamu.

addCmd(...cmds: ICmd[])

Registers one or more commands. Safe to call at module level.

import { addCmd } from "jsr:@ursamu/ursamu";

addCmd(
  { name: "cmd1", pattern: /^cmd1$/i, exec: (u) => u.send("one") },
  { name: "cmd2", pattern: /^cmd2$/i, exec: (u) => u.send("two") },
);

registerPluginRoute(prefix, handler)

Registers a custom HTTP route handled by your plugin.

import { registerPluginRoute } from "jsr:@ursamu/ursamu";

registerPluginRoute("/api/v1/my-plugin", async (req, userId) => {
  return Response.json({ ok: true, userId });
});

handler signature: (req: Request, userId: string | null) => Promise<Response>

mu(config?)

Starts the UrsaMU engine. Called once in your game’s src/main.ts. Returns
a Deno Deno.HttpServer instance.

import { mu } from "jsr:@ursamu/ursamu";
await mu();

createObj(template)

Creates a new object directly in the DB. Useful in startup scripts or
migrations that run outside a command handler (where u.db.create() isn’t
available).

import { createObj } from "jsr:@ursamu/ursamu";

const room = await createObj({
  name: "The Void",
  flags: new Set(["room"]),
  state: { desc: "An empty room." },
  contents: [],
});

DBO

The raw Deno KV database wrapper. Use dbojs for most game-data access — DBO
is for low-level or plugin-specific storage.

import { DBO } from "jsr:@ursamu/ursamu";

const db = new DBO<{ score: number }>("server.highscores");
await db.create({ score: 100 });
const all = await db.all();

dbojs

The game-object database accessor. Lets you query IDBObj records directly.

import { dbojs } from "jsr:@ursamu/ursamu";

const players = await dbojs.queryAll((o) => o.flags.has("player"));
const room    = await dbojs.queryOne((o) => o.id === "1");