Server
Last updated on
The server-only OPX.* runtime — public events, chat commands and aliases, answers and refusals, the readiness gate, sessions, routing buckets, tunables and the schema.
Everything on this page is nil in the client VM. Nothing here is reachable from another resource; outside code uses the creator exports.
Server values
| Name | Type | Notes |
|---|---|---|
OPX.Booted | boolean | false until the server boot has run every phase, then true whatever the outcome. |
OPX.BootError | string? | Why the server is degraded: 'no database', 'schema failed: <table>' or 'module failed: <id>'. Set once, never cleared. A database that comes back later needs a resource restart. |
OPX.Sessions | table<integer, table> | Live sessions by player id: source, userId, displayName, connectedAt, gateSession, plus departing, heldAt, released when set. Read it; create and drop sessions only with the session functions. |
Public events
OPX.Publish
Raises a public server event for every resource on the host: TriggerEvent(name, source, payload). See Public server events.
OPX.Publish(name, source, payload)Parameters
- name:
string— must start withopx:on:(anything else raises) - source:
integer?— the player id, ornil - payload:
table— a plain table built for the event, never a live record
Returns
boolean— a refusal by the host is logged once per name and answeredfalse; it never raises
OPX.Publish('opx:on:thing:done', source, { citizenId = citizenId, amount = 50 })Commands
Register chat commands here, not with RegisterCommand. The host checks the ACL right command.<name> before the handler of a restricted command runs.
OPX.Command.Register
Registers a chat command, plus its alias from COMMAND_ALIASES if any (a refused alias is logged and skipped).
OPX.Command.Register(name, opts, handler)Parameters
- name:
string - opts?:
table- restricted?:
boolean— gate oncommand.<name> - help?:
string— locale key - params?:
{ name, help?, optional? }[]— passed through as written - cooldownMs?:
number— per-player window; a hit answerserror.tooFast - key?:
string— cooldown key, defaultcommand.<name>; share it with other doors into the same operation
- restricted?:
- handler:
function(source, args, raw)
Raises on a duplicate name (case-blind) or a cooldownMs that is not a number >= 0.
OPX.Command.Register('opx.thing.reset', {
restricted = true,
help = 'thing.command.reset',
params = { { name = 'target', help = 'thing.param.target' } },
cooldownMs = 2000,
}, function(source, args, raw)
OPX.CommandNotice(source, raw, 'success', locale('thing.reset'))
end)OPX.Command.Suggestions
Commands this player may run. An aliased command is listed under its alias. help is resolved through the locale now.
OPX.Command.Suggestions(source)Parameters
- source:
integer
Returns
{ name, help?, params }[]— sorted by name
OPX.Command.Known
Whether a command or alias is registered. Case-blind. An alias answers its command's restricted flag.
OPX.Command.Known(name)Parameters
- name:
string— command or alias
Returns
- registered:
boolean - restricted:
boolean
OPX.Command.Aliases
The aliases that were actually registered.
OPX.Command.Aliases()Returns
table<alias, command>- count:
integer
Aliases and the ACL
- An alias is registered unrestricted on the host, then checks the ACL right of the full command name itself (
command.opx.admin.self.noclipfornoclip). Grant only the long name inacl.jsonc. - The console (source
0) is always allowed. - A host without
Open77.aclmakes every restricted alias refuse witherror.noPermission, while the long name still works. - An alias may not start with
opx., must be 1–64 characters of letters, digits,_ . : -, and must not be taken by another resource. - The shipped list (
chars,duty,withdraw,noclip,goto, …) is on COMMAND_ALIASES.
Answers, refusals and cooldowns
OPX.Notify
Sends a toast through Open77.notifications.send (the platform's open77_notifications package), titled SERVER_NAME, at NOTIFY_POSITION. Takes no icon.
OPX.Notify(source, message, kind, durationMs)Parameters
- source:
integer - message:
string - kind?:
'info' | 'success' | 'warning' | 'error'— defaultinfo - durationMs?:
integer— default 5000
Returns
- ok:
boolean - reason?:
string—'duplicate'(an identical toast to the same player within 2 s),'invalid_source','no_notifications'(noOpen77.notificationson this host),'notification_refused', or the host's own refusal
The title is cut to 96 bytes and the message to 384 bytes, on a character boundary (the package refuses longer ones whole). A toast the host refused is not remembered, so an identical retry goes out.
OPX.NotifyLocale
OPX.Notify with the text of a locale key (through RefusalKey). A kind of 'error' or 'warning' is replaced by OPX.Result.Kind(key, kind).
OPX.NotifyLocale(source, key, params, kind)Parameters
- source:
integer - key:
string - params?:
table - kind?:
string
Returns
- ok:
boolean - reason?:
string
OPX.NotifyLocale(source, 'thing.done', { count = 3 }, 'success')OPX.RefusalKey
code if it is a locale key that exists, else 'error.unavailable' (and a warning in the log).
OPX.RefusalKey(code)Parameters
- code:
any
Returns
string
OPX.Refuse
Sends opx:net:runtime:notify to one player: a toast with the text of RefusalKey(code). Its kind is OPX.Result.Kind(code, 'error'): warning for too fast, rate limited or busy, else error. Does nothing for source <= 0. One refusal is one toast: do not follow it with NotifyLocale.
OPX.Refuse(source, code, operation, icon, params)Parameters
- source:
integer - code:
string— a locale key - operation?:
string— which request was refused; default'unknown'. Not shown in the toast. - icon?:
string— a name fromOPX.Glyphs; the client drops an unknown one - params?:
table— the sentence's{placeholders}. Keys match[%w_]+, values are strings or numbers, at most 8 entries, strings cut to 64 bytes; anything else is dropped. The client checks them again.
if OPX.Cooling(source, 'thing.use', 1000) then
return OPX.Refuse(source, 'error.tooFast', 'thing.use') -- a warning toast
end
return OPX.Refuse(source, 'thing.blocked', 'thing.use', nil, { place = spot.label })OPX.CommandResult
Answers a command that reads something back (a list, a dump). Sends opx:net:runtime:commandResult with kind = 'info' (accepted) or 'error'; the chat module draws it. Newlines are kept; cut to 8192 bytes on a character boundary (the cut is logged). Source nil/0 prints to the console.
OPX.CommandResult(source, accepted, message)Parameters
- source:
integer? - accepted:
boolean - message:
string
OPX.CommandNotice
One-line toast saying what a command did. Sends opx:net:runtime:commandAnswer. Source nil/0 prints.
OPX.CommandNotice(source, raw, kind, message, toasted, icon)Parameters
- source:
integer? - raw:
string?— the typed command line - kind:
'success' | 'warning' | 'error' - message:
string— stripped of control characters and cut to 240 characters - toasted?:
boolean—true: the action already showed a toast, so the client shows none - icon?:
string— dropped unless inOPX.Glyphs
OPX.CommandNotice(source, raw, 'error', locale('weather.error.unknownPreset'))OPX.Cooling
true if key was used by this player less than everyMs ago; otherwise records the use and answers false. The window is per player and per operation, shared by every door into it. The console is never cooled. Not a security check.
OPX.Cooling(source, key, everyMs)Parameters
- source:
integer - key:
string - everyMs:
integer
Returns
boolean
OPX.ForgetCooldowns
Drops a player's cooldowns and toast dedupe windows. The core also does this on disconnect.
OPX.ForgetCooldowns(source)Parameters
- source:
integer
Readiness gate
No player may be moved, spawned, killed or respawned before their gate opens. The core takes part in the gate at load, so every new connection is held in its name. The character module drives entry with these functions. Settings: ENTRY.
OPX.Gate.Participate
Declares the core's participation with the ENTRY.GATE_MS liveness interval. Called once at load. Logs a warning if no resource emits open77:session:gameplayReady.
OPX.Gate.Participate()Returns
boolean—falsewhen the host has no readiness gate
OPX.Gate.Hold
Takes the hold and stores its session token. Raises opx:in:gate:held.
OPX.Gate.Hold(source, reason)Parameters
- source:
integer - reason?:
string
Returns
boolean—falsewith no session or when the host refuses
OPX.Gate.Release
Releases the hold. Raises opx:in:gate:released.
OPX.Gate.Release(source, note, expectedUserId)Parameters
- source:
integer - note?:
string— reaches every resource as theonPlayerReadydetail, prefixedopx_infinity: - expectedUserId?:
string— refuses if the slot now belongs to another account
Returns
boolean—trueon a host with no readiness API
OPX.Gate.IsReady
Whether the gate is open. A host read that raises counts as open.
OPX.Gate.IsReady(source)Parameters
- source:
integer
Returns
boolean
OPX.Gate.Watch
Gives up on a player at a deadline. One thread per player.
OPX.Gate.Watch(source, timeoutMs, onGiveUp)Parameters
- source:
integer - timeoutMs?:
integer— clamped belowENTRY.GATE_MS; defaultENTRY.WATCH_MS - onGiveUp?:
function(source)— called at the deadline; returningfalsestops the watch and leaves the hold to the caller, otherwise the core releases with notewatch-timeout
Returns
boolean— started
Stops early if the slot empties, changes account or is released. A release the host refuses is retried once a second, up to 5 times, asking the host for a fresh session token each time; onGiveUp still runs only once. After the fifth refusal the journal says [gate] gave up releasing <id> after 5 refused attempts.
Sessions
A session is a connected machine, keyed by the account the host vouches for. The character module loads a character onto it.
OPX.UserIdOf
The durable account id (GetPlayerIdentifier).
OPX.UserIdOf(playerId)Parameters
- playerId:
integer
Returns
string?—nilonce the player has gone
OPX.DisplayNameOf
The player's verified display name (GetPlayerName).
OPX.DisplayNameOf(playerId)Parameters
- playerId:
integer
Returns
string?
OPX.EnsureSession
The session on a slot, created if needed. If the slot now belongs to another account, the old session is dropped first.
OPX.EnsureSession(playerId)Parameters
- playerId:
integer | string
Returns
table?—nilwhen the host vouches for no account
OPX.ForgetSession
Marks the session departing, raises opx:in:session:forgotten, then drops it. The core does this itself one frame after a disconnect if no module did.
OPX.ForgetSession(playerId)Parameters
- playerId:
integer
OPX.SessionHolds
Whether the slot still belongs to the account the session was made for.
OPX.SessionHolds(playerId)Parameters
- playerId:
integer
Returns
boolean
Routing buckets
Each player can wait in their own selection bucket (ENTRY.BUCKET.BASE + playerId) before a character is placed. A bucket move writes no position and is allowed while the gate is closed. Settings: ENTRY.BUCKET.
OPX.Buckets.IsSelection
Whether the id is in BASE+1 .. BASE+65535, even with isolation off.
OPX.Buckets.IsSelection(bucket)Parameters
- bucket:
integer
Returns
boolean
OPX.Buckets.PlacementOf
The bucket a stored position places someone in. A selection bucket or an invalid id reads as WORLD.
OPX.Buckets.PlacementOf(stored)Parameters
- stored:
integer
Returns
integer
OPX.Buckets.Move
Moves one player.
OPX.Buckets.Move(source, bucket, why)Parameters
- source:
integer - bucket:
integer - why?:
string
Returns
boolean—trueif already there
OPX.Buckets.Isolate
Moves a player into their own selection bucket and applies the population and lockdown settings to it. Do not call it for a player with a loaded character.
OPX.Buckets.Isolate(source, why, expectedUserId)Parameters
- source:
integer - why?:
string - expectedUserId?:
string
Returns
boolean—falsewhen isolation is off, there is no session, the session is departing, or the account does not match
OPX.Buckets.Release
Moves a player out of a selection bucket into WORLD. Leaves any other bucket alone (true, false). On resource stop the core releases every session.
OPX.Buckets.Release(source, why, expectedUserId)Parameters
- source:
integer - why?:
string - expectedUserId?:
string
Returns
- ok:
boolean - moved:
boolean
Tunables
Values an operator can change from the host's tunables panel while the server runs. A change raises opx:in:tune:changed with the key.
OPX.Tune.Declare
Adds a module's tunables. Call from Init. Raises on a key declared twice, or after publishing.
OPX.Tune.Declare(block)Parameters
- block:
table<key, spec>— a spec is what the host panel takes;valueis the default
OPX.Tune.Declare({
['thing.sweepMs'] = { value = 1000, type = 'integer', min = 250, max = 600000 },
})OPX.Tune.Publish
Declares every collected key to the host in one call. Boot calls it between Api and Start. On a host with no panel, or a refusal, every key reads its default.
OPX.Tune.Publish()Returns
boolean— the panel is live
OPX.Tune.Get
The live value, or the declared default.
OPX.Tune.Get(key)Parameters
- key:
string
Returns
any
OPX.Tune.Number
The value as a finite number, never below floor. Answers floor for an unknown key or a bad value. Read it at the moment of use; do not store it.
OPX.Tune.Number(key, floor)Parameters
- key:
string - floor:
number
Returns
number
OPX.Scheduler.Every('thing.sweep', function() return OPX.Tune.Number('thing.sweepMs', 250) end, sweep)OPX.Tune.Known
Whether anyone declared the key.
OPX.Tune.Known(key)Parameters
- key:
string
Returns
boolean
Schema
OPX.Schema.Add
Adds CREATE TABLE IF NOT EXISTS statements, in foreign-key order. Call from Init.
OPX.Schema.Add(list)Parameters
- list:
string[]
OPX.Schema.AddColumns
Adds columns a table that already exists may be missing (a column in a CREATE TABLE only reaches a fresh install). Applied by OPX.Schema.Apply after every table and before the indexes, through OPX.Storage.EnsureColumn. Unlike an index, a column that cannot be added fails the schema. Call from Init.
OPX.Schema.AddColumns(list)Parameters
- list:
table[]- TABLE:
string - COLUMN:
string - DEFINITION:
string— SQL type and options
- TABLE:
OPX.Schema.AddColumns({
{ TABLE = 'opx77_crafting_orders', COLUMN = 'token',
DEFINITION = 'VARCHAR(40) CHARACTER SET ascii COLLATE ascii_bin NULL DEFAULT NULL' },
}) -- what crafting does, from M.Storage.COLUMNSOPX.Schema.AddIndexes
Adds indexes a table that already exists may be missing. Applied by OPX.Schema.Apply after every table, through OPX.Storage.EnsureIndex. A failure is a logged warning, not a boot failure. Call from Init.
OPX.Schema.AddIndexes(list)Parameters
- list:
table[]- TABLE:
string - NAME:
string— index name - COLUMNS:
string[]
- TABLE:
OPX.Schema.AddIndexes({
{ TABLE = 'opx77_characters', NAME = 'idx_opx77_characters_seen', COLUMNS = { 'deleted_at', 'last_logged_out', 'citizen_id' } },
}) -- what character does, from M.Storage.INDEXESOPX.Schema.Apply
Runs every statement through OPX.Storage.ApplySchema, stopping at the first failure, then ensures every column (a failure stops it) and every index. Boot calls it before Start (not at all with no database); a failure sets OPX.BootError.
OPX.Schema.Apply()Returns
- ok:
boolean - failedTable?:
string
Shared
The OPX.* runtime on both sides — module registry and lifecycle, contracts, event names, time, the scheduler, runtime values and the boot order.
Client
The client-only OPX.* runtime — the WebUI bridge and focus stack, the runtime's own toasts, notes to the server journal, OPX.Lib and the client scheduler loop.