SDK Cookbook
Every method available on the u object, organized by namespace, with working examples pulled from real system scripts.
Context Objects
These are injected automatically — you don’t call them, you read them.
u.me — The Actor
The player or object that triggered the script.
u.me.id // "42"
u.me.name // "Alice" (raw DB name)
u.me.location // "1" (room ID)
u.me.flags // Set<string> — e.g. Set { "player", "connected", "wizard" }
u.me.state // Record<string,unknown> — all player data (desc, attrs, etc.)
u.me.contents // IDBObj[] — objects in actor's inventory
// Check permission level
u.me.flags.has("superuser") // highest — created at first boot
u.me.flags.has("admin") // or "wizard" — both level 9
u.me.flags.has("player")
u.me.flags.has("connected") // currently logged in
// Read stored data
const desc = u.me.state.desc as string || "You see nothing special.";
const gold = u.me.state.gold as number || 0;
const level = u.me.state.level as number || 1;
u.here — The Room
The room the actor is currently in. Has an extra .broadcast() method.
u.here.id // room ID
u.here.name // room name
u.here.state // room data (desc, exits, etc.)
u.here.contents // IDBObj[] — everyone and everything in the room
// Send to everyone in the room
u.here.broadcast("The lights flicker.");
// Send to everyone in the room (with options)
u.here.broadcast("An explosion rocks the building!", { source: "event" });
u.target — Optional Target
Set when the parser resolves a target from the command args. May be undefined.
if (!u.target) {
u.send("No target found.");
return;
}
u.target.id
u.target.name
u.target.flags
u.target.broadcast("Something pokes you.");
u.cmd — The Command
u.cmd.name // "look" — the script name that was matched
u.cmd.original // "@look north" — full string the player typed
u.cmd.args // ["north", "north"] — [rawArgString, ...splitArgs]
u.cmd.switches // ["edit"] from "@bbpost/edit ..." (or undefined)
// Common args pattern:
const arg = (u.cmd.args[0] || "").trim();
// Common switches pattern:
const sw = u.cmd.switches || [];
if (sw.includes("verbose")) { /* ... */ }
u.state — Reactive Script State
Per-execution scratch space. Changes are sent back to the server as patches.
// Read initial state (usually empty for one-shot scripts)
const prev = u.state.lastRan as number | undefined;
// Write state (patches are sent back automatically via Proxy)
u.state.lastRan = Date.now();
Messaging
u.send(message, target?, options?)
Send a message to a player.
// To the actor
u.send("You pick up the coin.");
// To a specific player by ID
u.send("Alice picks up a coin.", aliceId);
// With options
u.send("Disconnecting...", undefined, { quit: true }); // triggers disconnect
// MUSH color codes work everywhere
u.send("%ch%cgYou level up!%cn");
u.send(`%ch%cr[ERROR]%cn ${message}`);
u.broadcast(message, options?)
Send to all connected players on the server. Use sparingly.
u.broadcast("The server will reboot in 5 minutes.");
u.broadcast(`%ch%cy[ANNOUNCE]%cn ${announcement}`);
u.here.broadcast(message, options?)
Send to everyone in the actor’s current room.
u.here.broadcast(`${u.util.displayName(u.me, u.me)} arrives.`);
Database (u.db)
u.db.search(query)
Returns an array of IDBObj. Query can be a string (target lookup) or an object.
// By name (case-insensitive)
const results = await u.db.search({ "data.name": /alice/i });
// By flag
const wizards = await u.db.search({ flags: /wizard/i });
// All connected players
const online = await u.db.search({ flags: /connected/i });
// Objects in a specific room
const contents = await u.db.search({ location: u.here.id });
// Players in a room (combined)
const players = await u.db.search({ location: u.here.id, flags: /player/i });
// OR query
const found = await u.db.search({
$or: [
{ "data.name": /alice/i },
{ "data.alias": /alice/i }
]
});
u.db.create(template)
const sword = await u.db.create({
flags: new Set(["thing", "weapon"]),
location: u.me.id, // in actor's inventory
state: {
name: "Iron Sword",
description: "A basic iron sword.",
damage: 5,
owner: u.me.id
}
});
u.send(`You create ${sword.state.name as string} (#${sword.id}).`);
u.db.modify(id, op, data)
// $set — merge data fields (always spread existing state)
await u.db.modify(u.me.id, "$set", {
data: { ...u.me.state, gold: (u.me.state.gold as number || 0) + 100 }
});
// $set on any object
await u.db.modify(objectId, "$set", {
data: { ...targetObj.state, locked: true }
});
u.db.destroy(id)
await u.db.destroy(objectId);
u.send(`Object #${objectId} destroyed.`);
Target Resolution (u.util.target)
The standard way to resolve player input into an object.
// Accepts: name, ID, alias, "me", "here", partial match
const target = await u.util.target(u.me, inputString);
if (!target) {
u.send("I can't find that.");
return;
}
The lookup searches:
me/herekeywords- Actor’s inventory (by name/alias)
- Current room contents (by name/alias)
- All objects globally (if
globalparam istrue)
Flags (u.setFlags)
await u.setFlags(target.id, "dark"); // add "dark" flag
await u.setFlags(target.id, "!dark"); // remove "dark" flag
await u.setFlags(target.id, "wizard"); // grant wizard (superuser required)
// Or by object reference
await u.setFlags(u.me, "builder");
Permission Check (u.canEdit)
if (!u.canEdit(u.me, target)) {
u.send("Permission denied.");
return;
}
// proceed with edit
Returns true if the actor owns the target, or is admin/wizard/superuser.
Locks (u.checkLock)
// Evaluate a lock expression against the actor
const canPass = await u.checkLock(target.id, "player+");
const isWiz = await u.checkLock(u.me.id, "wizard");
// Lock syntax examples:
// "player+" — must have player flag
// "wizard|admin" — wizard OR admin
// "!dark" — must NOT have dark flag
// "#42" — must be object #42
Movement (u.teleport)
// Move any object to a destination room
u.teleport(u.me.id, destinationId);
// Move an object from inventory to a room
u.teleport(objectId, u.here.id);
Execute Commands (u.execute, u.force)
// Run a command as the actor (goes through full command pipeline)
u.execute("look");
u.execute(`say I just arrived!`);
// Force a command (bypasses some permission checks)
u.force("@set me=dark");
u.force(`@teleport #${objectId}=#${roomId}`);
Text Storage (u.text)
Named text blobs stored in the database. Useful for MOTD, templates, help pages.
// Read
const motd = await u.text.read("motd");
if (motd) u.send(motd);
// Write
await u.text.set("motd", "Server maintenance tonight at midnight EST.");
// Clear
await u.text.set("motd", "");
Bulletin Boards (u.bb)
// List boards with unread counts (for current player)
const boards = await u.bb.listBoards();
// → [{ id, name, description, order, postCount, newCount }, ...]
// List posts on a board
const posts = await u.bb.listPosts("announcements");
// → [{ id, num, subject, authorName, date, edited? }, ...]
// Read a specific post by number
const post = await u.bb.readPost("announcements", 3);
if (post) {
u.send(`Subject: ${post.subject}`);
u.send(`By: ${post.authorName}`);
u.send(post.body);
}
// Post to a board
await u.bb.post("announcements", "Game Night Friday!", "Join us at 8pm EST.");
// Edit a post (author or admin)
await u.bb.editPost("announcements", 3, "Updated body text.");
// Delete a post
await u.bb.deletePost("announcements", 3);
// Admin: create a board
await u.bb.createBoard("General", { description: "General discussion", order: 1 });
// Admin: destroy a board (also deletes all posts)
await u.bb.destroyBoard("old-board");
// Mark all posts on a board as read (for current player)
await u.bb.markRead("announcements");
// Unread counts
const boardNew = await u.bb.newPostCount("announcements"); // for one board
const totalNew = await u.bb.totalNewCount(); // across all boards
Mail (u.mail)
// Send
await u.mail.send({
id: crypto.randomUUID(),
from: `#${u.me.id}`,
to: [`#${recipientId}`],
cc: [`#${ccId}`], // optional
subject: "Guild Meeting",
message: "Meeting tomorrow at 8pm in the hall.",
read: false,
date: Date.now()
});
// Notify recipient (send them an in-game message)
u.send(`%chMAIL:%cn You have new mail from ${u.util.displayName(u.me, u.me)}.`, recipientId);
// Read (query your inbox)
const inbox = await u.mail.read({ to: { $in: [`#${u.me.id}`] } });
inbox.sort((a, b) => a.date - b.date);
const unread = inbox.filter(m => !m.read);
// Delete
await u.mail.delete(messageId);
Channels (u.chan)
// Join (adds to player's channel list + socket room)
await u.chan.join("public", "pub");
// Leave by alias
await u.chan.leave("pub");
// List all channels
const channels = await u.chan.list();
// Admin: create
await u.chan.create("events", {
header: "[EVENTS]",
lock: "player+",
hidden: false
});
// Admin: modify
await u.chan.set("public", {
header: "[PUB]",
masking: true // hide speaker names
});
// Admin: destroy
await u.chan.destroy("events");
Attribute Triggers (u.trigger)
Run a stored &ATTR as a sandbox script, with the enactor as u.me.
// Fire &ONENTER on the room with no args
await u.trigger(u.here.id, "ONENTER");
// Fire &USE on an object, passing args
await u.trigger(objectId, "USE", ["open"]);
// Fire &ACONNECT on the player
await u.trigger(u.me.id, "ACONNECT");
Authentication (u.auth)
// Verify credentials (returns true/false)
const valid = await u.auth.verify(name, password);
// Complete login (sets connected flag, joins socket rooms, fires hooks)
await u.auth.login(playerId);
// Hash a password
const hashed = await u.auth.hash(plaintext);
// Change password
await u.auth.setPassword(playerId, newPassword);
System (u.sys)
Wizard/admin only (enforced in each script — the SDK does not enforce it itself).
// Set a runtime config value
await u.sys.setConfig("game.masterRoom", "42");
// Disconnect a socket (by socket ID, not player ID)
await u.sys.disconnect(socketId);
// Server uptime in milliseconds
const ms = await u.sys.uptime();
const h = Math.floor(ms / 3600000);
const m = Math.floor((ms % 3600000) / 60000);
u.send(`Uptime: ${h}h ${m}m`);
// Reboot (exit code 75 — daemon restart loop restarts the server)
await u.sys.reboot();
// Shutdown (exit code 0)
await u.sys.shutdown();
// Pull latest code from git and restart (runs git pull, then exits 75)
await u.sys.update(); // pull origin/main
await u.sys.update("develop"); // pull a specific branch
Events (u.events)
Server-wide pub/sub system.
// Emit an event with data
await u.events.emit("player.died", {
id: u.me.id,
cause: "fell off a cliff"
});
// Subscribe a stored attribute to an event
// The attribute value is a script that receives the event data
const subId = await u.events.on("player.died", "DEATH_HANDLER");
Formatting (u.util)
Alignment
// Left-pad to width (optional fill character)
u.util.ljust("text", 20) // "text "
u.util.ljust("text", 20, ".") // "text................"
// Right-align
u.util.rjust("100", 20) // " 100"
// Center
u.util.center("Title", 40, "-") // "-------------------Title------------------"
u.util.center("Title", 40) // " Title "
sprintf
u.util.sprintf("%-10s %5d", "Alice", 1200) // "Alice 1200"
u.util.sprintf("%.2f%%", 98.6) // "98.60%"
u.util.sprintf("%05d", 42) // "00042"
template
Multi-row column formatter. Uppercase keys are bracketed ([value]), lowercase are unbracketed.
const out = u.util.template(
"[NNN] [TTTTTTTTTTTTTTTTTTTT] rrrrr",
{
N: { value: "42", align: "right" },
T: { value: "Post title", align: "left" },
r: { value: "Alice", align: "left" }
}
);
// → "[ 42] [Post title ] Alice"
stripSubs
// Remove all MUSH codes and ANSI escapes
u.util.stripSubs("%ch%crDanger!%cn") // → "Danger!"
u.util.stripSubs("%n waves.") // → " waves."
// Use before validation or DB storage
const clean = u.util.stripSubs(userInput).trim();
if (!clean) { u.send("Value cannot be empty."); return; }
displayName
// Returns moniker (if set) → name → "Unknown"
u.util.displayName(u.me, u.me) // "Alice" or moniker
u.util.displayName(target, u.me) // target's display name as seen by actor
Attributes (u.attr)
u.attr.get(id, name)
Reads a soft-coded &ATTR value stored on any game object. Attribute names are
case-insensitive. Returns null if the attribute is not set.
// Read your own short description
const shortDesc = await u.attr.get(u.me.id, "SHORT-DESC");
if (shortDesc) u.send(`Short desc: ${shortDesc}`);
// Read another object's attribute
const bio = await u.attr.get(targetId, "FINGER-INFO");
// Case-insensitive
const val = await u.attr.get(objectId, "onenter"); // same as "ONENTER"
Evaluate Attribute (u.eval)
u.eval(targetId, attr, args?)
Evaluates a stored &ATTR as a script and returns the output as a string.
This is the script equivalent of @trigger that also captures output.
// Run &FORMULA on object #42 with args
const result = await u.eval("#42", "FORMULA", ["5", "10"]);
u.send(`Formula result: ${result}`);
// Run an attribute on the actor
const score = await u.eval(u.me.id, "SCORE-FORMULA");
u.send(`Your score: ${score}`);
Force As Another Object (u.forceAs)
u.forceAs(targetId, command)
Executes a command as another object (NPC, room, etc.). The SDK does not enforce
privilege — always check flags in your script before calling.
// Guard: admin/wizard only
const isAdmin = u.me.flags.has("admin") || u.me.flags.has("wizard") || u.me.flags.has("superuser");
if (!isAdmin) { u.send("Permission denied."); return; }
// Make an NPC speak
await u.forceAs(npcId, "say Welcome to the Inn!");
// Make a room run a command
await u.forceAs(roomId, "@trigger me/ONRESET");
Game Time (u.sys.gameTime)
u.sys.gameTime() / u.sys.setGameTime(t)
Read and write the in-game calendar. Useful for seasonal events, timestamps,
and roleplay-relevant date display.
// Read current game time
const t = await u.sys.gameTime();
// t.year: number, t.month: 1-12, t.day: 1-28, t.hour: 0-23, t.minute: 0-59
const monthNames = ["", "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
u.send(`Today is ${monthNames[t.month]} ${t.day}, Year ${t.year} — ${t.hour}:${String(t.minute).padStart(2,"0")}`);
// Advance the calendar (admin only)
if (!u.me.flags.has("wizard") && !u.me.flags.has("admin")) {
u.send("Permission denied."); return;
}
await u.sys.setGameTime({ year: t.year + 1, month: 1, day: 1, hour: 0, minute: 0 });
u.broadcast("A new year has begun!");
Channel History (u.chan.history)
u.chan.history(name, limit?)
Fetches recent messages from a channel. Default limit is 20.
// Last 20 messages on the public channel
const history = await u.chan.history("public");
// → [{ id: string, playerName: string, message: string, timestamp: number }, ...]
// Last 50 messages
const history = await u.chan.history("staff", 50);
// Display as a log
const lines = history.map(e => {
const date = new Date(e.timestamp).toLocaleTimeString();
return `[${date}] ${e.playerName}: ${e.message}`;
});
u.send(lines.join("\r\n"));
Mail — Modify (u.mail.modify)
u.mail.modify(query, op, update)
Updates mail records matching query. Used for marking messages read/unread or
other bulk updates. Available in sandbox scripts only.
// Mark a specific message as read
await u.mail.modify({ id: messageId }, "$set", { read: true });
// Mark all your messages as read
await u.mail.modify(
{ to: { $in: [`#${u.me.id}`] } },
"$set",
{ read: true }
);