admin
Last updated on
The staff tool — a menu, rows on the target eye and 56 restricted commands to move, heal, arm, equip, moderate and audit players.
Staff press F9 (or type /opx.admin) to open a menu that finds a player, goes to them, heals or moves them, gives them a car, a weapon or an item, kicks or bans them, and reads the audit. The same actions are rows on the target eye and chat commands. Every action is a restricted command, so the server's ACL decides who may run what. Nothing is stored in the database.
| Side | both |
| Requires | character (hard) |
| Optional | menu, form, target, inventory, vehicles, downed, prompts, diagnostics, vehiclekeys — each adds one feature; appearance is read for the wardrobe command |
| Contract | admin v1 — server, client |
| Config | config/admin.lua (shared) |
| Data | none. Locations saved in game are kept with OPX.Carry |
Granting access
The module checks no permission itself: the host resolves command.<name> against acl.jsonc before any handler runs. The menu and the eye only grey out or hide what the ACL would refuse.
command.opx.adminopens the menu. Holding it is what makes someone "staff" forIsStaffand for the badge on name tags.command.opx.admin.*covers every action. Only a rule ending in.*is a prefix, so it does not cover the opener: an operator role needs both.- Narrower families work too, e.g.
command.opx.admin.player.*. Thecharacter.*commands are separate on purpose:listshows one account's characters,findsearches everyone who ever played,rename/deletechange rows that outlive a session. - The menu and eye also run other modules' commands, named in
LINKS:opx.players,opx.where,opx.job,opx.gang,opx.money,opx.save,opx.inventory.open,opx.inventory.holders,opx.weather.set,opx.weather.next,opx.weather.freeze,opx.time,opx.time.freeze. Grant those too (e.g.command.opx.weather.*,command.opx.time,command.opx.time.*). - The eye hides what the menu greys. The menu draws a refused row and marks it; the eye does not register it at all, so a missing grant looks like a missing feature. The client writes the dropped grants to the server journal, e.g.
[admin] target rows, player 3: … this ACL does not grant: opx.inventory.open opx.weather.set— each name is acommand.<name>to add. - A short alias is gated on the long command's right, never on its own name.
LINKS.DOORLOCKadds Manage door locks to the World screen; it runs/opx.doorlockand needscommand.opx.doorlock(doorlock).
Raw rights
Three ACL rights are not commands: command.* grants none of them, * grants all. Grant them explicitly.
| Right | Effect |
|---|---|
opx.admin.immune | Shields the holder from every harmful staff action: kick, ban, kill, health, armour, freeze, model, bring, send, tp, observe, wardrobe, holster, bag remove/clear, weapon remove/ammo, character rename/delete. The operator gets target_protected; the attempt is audited. The console and acting on yourself are never stopped. |
opx.admin.override | Acts on an immune player anyway. |
opx.admin.vehicle.anywhere | Vehicle commands that take a typed id reach any live vehicle. Without it the vehicle must carry the operator, or be in their instance within VEHICLES.REACH; else vehicle_out_of_reach (audited). |
"senior": ["command.opx.admin", "command.opx.admin.*", "opx.admin.immune", "opx.admin.override"]What a grant does not imply
- Bag commands on weapon items need the weapon command too.
inventory.giveof a weapon needsweapon.give(of rounds:weapon.giveammo);inventory.removeneedsweapon.remove(rounds:weapon.ammo);inventory.clearchecks every weapon and round first and clears nothing if one is refused. Refusal:weapon_not_granted. player.observeis spectating, not flight. Withoutself.noclip, its noclip and hidden body end afterPLACEMENT.OBSERVE_MS(2 min) and the operator is told.player.health 0is a kill: it needs the kill command (kill_not_granted).
Commands
56 restricted commands (the opener + 55 actions). The right is always command. + the full name.
- target
playerId|me: a server id, orme/selffor the caller. holderplayerId|me|citizenId: also a citizen id, online or offline. - Switches take
on/off(alsotrue/false,1/0,yes/no); left out, they toggle. - "In game": the console gets
in_game_only. - Moving, reviving, healing, killing and spawning for a player need that player's readiness gate open (
not_incarnated,gate_closed,gate_unreadable). A move is a kill + respawn at the new point, health set toPLACEMENT.HEALTH, with a grace window; if the respawn is refused, the player is revived where they stood. - Per-operator cooldown per command: 400 ms for actions, 1000 ms for reads (
RATE+ tunables);too_fastinside it. The console has none. - Every action writes an audit line; the target gets a toast unless they are the caller.
- One heavy request per operator. Every bag, weapon and character command, the key cut and every menu list run on that operator's single worker. A typed command arriving while it is busy answers
busy; a menu list waits its turn (only the newest ask per list is kept). A worker silent for 30 s releases the slot. - Every list on the menu (roster, bag, saved places, characters, ammo, catalogues) is paged, 20 rows a page. Redraws run on their own thread, so a search typed in a form comes back filtered.
Menu
Self
Player
| Command | Access | Arguments | Effect |
|---|---|---|---|
/opx.admin.player.goto | ACL command.opx.admin.player.goto | <playerId> | In game. Caller beside the player (PLACEMENT.BESIDE), in their bucket. Alias goto. |
/opx.admin.player.bring | ACL command.opx.admin.player.bring | <playerId> | In game. Player beside the caller. Alias bring. |
/opx.admin.player.tp | ACL command.opx.admin.player.tp | <playerId|me> <x> <y> <z> [heading] | To coordinates, each within ±1,000,000. Alias tp. |
/opx.admin.player.send | ACL command.opx.admin.player.send | <playerId|me> <location> | To a saved location (configured or added in game). |
/opx.admin.player.observe | ACL command.opx.admin.player.observe | <playerId> | In game. Caller PLACEMENT.OBSERVE_HEIGHT m above the player, noclip on. Without self.noclip the noclip ends after PLACEMENT.OBSERVE_MS. Alias spectate. |
/opx.admin.player.heal | ACL command.opx.admin.player.heal | <playerId|me> | Health to max. |
/opx.admin.player.revive | ACL command.opx.admin.player.revive | <playerId|me> | Through downed's Revive when it runs (a player not down still succeeds), else the host's revive. |
/opx.admin.player.god | ACL command.opx.admin.player.god | <playerId|me> [on|off] | God mode. |
/opx.admin.player.freeze | ACL command.opx.admin.player.freeze | <playerId> [on|off] | Holds a player still. Released when the module stops. Alias freeze. |
/opx.admin.player.kill | ACL command.opx.admin.player.kill | <playerId> | Kills; the caller is recorded as killer. |
/opx.admin.player.health | ACL command.opx.admin.player.health | <playerId|me> <points> | Sets health, capped at the player's max. 0 also needs player.kill (kill_not_granted). |
/opx.admin.player.armor | ACL command.opx.admin.player.armor | <playerId|me> <points> (0–10000) | Sets armour. |
/opx.admin.player.model | ACL command.opx.admin.player.model | <playerId> [ped|off] | self.model for another player. |
/opx.admin.player.wardrobe | ACL command.opx.admin.player.wardrobe | <playerId|me> | Opens the fitting room through appearance's OpenWardrobe; appearance_unavailable without that contract. |
Character
| Command | Access | Arguments | Effect |
|---|---|---|---|
/opx.admin.character.list | ACL command.opx.admin.character.list | <playerId|me> | In game, read. Every character of a connected player's account: citizen id, name, gender, created. * = being played, > = active. |
/opx.admin.character.find | ACL command.opx.admin.character.find | <name|citizenId> | In game, read. Searches every character through FindCharacters: citizen id, name, account, last logout. * = in play, + = account online. |
/opx.admin.character.rename | ACL command.opx.admin.character.rename | <citizenId> <firstName> <lastName> | In game. Through RenameCharacter. |
/opx.admin.character.delete | ACL command.opx.admin.character.delete | <citizenId> | In game. Through RemoveCharacter. Warns the player holding it, if online. |
Character delete and bag clear cannot be undone
opx.admin.character.delete removes the character row through character; opx.admin.inventory.clear empties the bag through inventory. Neither asks for confirmation when typed, and neither has an alias.
Moderation
Vehicle
vehicleId|near: a live vehicle id, or near = the vehicle the caller sits in, else the nearest in the caller's bucket within VEHICLES.NEAR_RADIUS. A typed id must carry the caller or be within VEHICLES.REACH in their instance, unless they hold opx.admin.vehicle.anywhere.
Inventory
Need the inventory contract (inventory_unavailable). Counts 1..INVENTORY.MAX_COUNT. On a weapon or ammo item they also need the matching weapon command (grant limits).
Weapon
Weapons are inventory items, so these need inventory too. A weapon is named by its item, with or without weapon_; ammunition by its own item.
| Command | Access | Arguments | Effect |
|---|---|---|---|
/opx.admin.weapon.give | ACL command.opx.admin.weapon.give | <holder> <weapon> [ammo] [count] | One weapon, unloaded; with an ammo item also count of it (default INVENTORY.DEFAULT_AMMO). Ammo refused → weapon taken back. Alias addweapon. |
/opx.admin.weapon.giveammo | ACL command.opx.admin.weapon.giveammo | <holder> <ammo> [count] | Ammunition (default DEFAULT_AMMO). Alias addammo. |
/opx.admin.weapon.ammo | ACL command.opx.admin.weapon.ammo | <holder> [ammo|all] [count] | count of one ammo item, or of every ammo type already in the bag (all, default). |
/opx.admin.weapon.remove | ACL command.opx.admin.weapon.remove | <holder> <weapon|all> | Every unit of one weapon, or every weapon. Alias delweapon. |
/opx.admin.weapon.holster | ACL command.opx.admin.weapon.holster | <playerId|me> | Puts the drawn weapon away through inventory's HolsterWeapon, which reads its rounds back into the bag. Answers admin.done.holstered. not_armed: no weapon drawn. holster_unavailable: contract missing or the call failed. |
/opx.admin.weapon.read | ACL command.opx.admin.weapon.read | <holder> | Read. Weapons in the bag with slot, serial, rounds; marks the one drawn. |
World
Read
Configuration
config/admin.lua sets OPX.Config.MODULES.admin. Shared script — every client downloads it, keep no secret in it. Tunable keys are defaults of a live panel entry (ADMIN_*), read when used; the rest are read at start.
| Key | Default | Effect |
|---|---|---|
enabled | true | false switches the module off. |
KEYS | { MENU = 'F9', SPEED_UP = 'PAGEUP', SPEED_DOWN = 'PAGEDOWN' } | Default key mappings, rebindable in the pause menu; false registers none. Speed keys change noclip speed. |
RATE | { ACTION_MS = 400, READ_MS = 1000, REFRESH_MS = 750 } | Per-operator cooldowns: actions, reads, menu list refreshes. Tunable ADMIN_RATE_ACTION_MS, _READ_MS, _REFRESH_MS. |
AUDIT_ENTRIES | 200 | In-memory audit ring size, 10–2000. Tunable. |
TOAST_MS | 6000 | How long a target's toast stays up. Tunable. |
PLACEMENT | { HEALTH = 1.0, GRACE_MS = 5000, BESIDE = { X = 1.5, Y = 0.0, Z = 0.0 }, OBSERVE_HEIGHT = 2.0, OBSERVE_MS = 120000 } | How a moved player lands: health fraction, respawn grace (tunable), goto/bring offset, observe height. OBSERVE_MS (5000–3600000): how long an observe keeps noclip for an operator without self.noclip. |
NOCLIP | { SPEED = 40.0, MIN_SPEED = 1.0, MAX_SPEED = 500.0, STEP = 0.15, SEND_AFTER_MS = 500, PROMPTS = true, EFFECT = '', EFFECT_SECONDS = 1.5, SOUND = '', HIDE_BODY = true } | Client. Speed range and step; delay before a key-chosen speed is sent (never < RATE.ACTION_MS + 100); on-screen prompts; VFX alias and sound on toggle (empty = none); whether the body is hidden while flying (honoured by the server too; the client always reports noclip off). |
TARGET | { DISTANCE = 10.0 } | Reach of the staff eye rows, 1–12 m. |
MODELS | { RESET_ON_DEATH = true, DURATION_MS = 0 } | A worn ped: death gives the own body back; lifetime (0 = until removed, ≤ one day). |
COMBAT | { PVP = true } | PvP at server start. |
TAGS | DISTANCE = 25.0, FADE_START = 0.55, HEAD_LIFT = 0.35, HEAD_OFFSET_Z = 2.05, UPDATE_MS = 250, REFRESH_MS = 2000, MAX = 32, OWN = false, HIDE_IN_FIRST_PERSON = false, TECHNICAL = true, USERNAME = true, CITIZEN = true, BADGE = true, COLORS = { TEXT = '#F2F6F8', ACCENT = '#FCEE0A', STAFF = '#22D8E2', BACKGROUND = '#0A1220' } | Name tags. REFRESH_MS = the server's list interval (tunable ADMIN_TAGS_REFRESH_MS). The rest is client drawing: reach, fade, height, cap, own tag, id square, account, citizen id, staff badge, colours. |
ANNOUNCE | { DURATION_MS = 12000, CHAT = true, MAX_CHARACTERS = 240, COOLDOWN_MS = 10000, STINGER = { OPEN = 'announce-open.mp3', CLOSE = 'announce-close.mp3', VOLUME = 0.8 } } | Lifetime (tunable), chat copy, length cap, least time between two announcements from any operator (0..600000; 0 = off; console exempt), clips before/after. A clip is a bare file name in the resource's audio/; '' turns it off, an invalid name is dropped. |
BAN_DURATIONS | { '1h', '1d', '7d', '30d', 'perm' } | Offered by the ban form. A typed ban takes any valid duration. |
VEHICLES | { SPAWN_OFFSET = { X = 3.0, Y = 0.0, Z = 0.25 }, AV_LIFT = 1.2, PER_OWNER = 8, NEAR_RADIUS = 30.0, REACH = 100.0, OCCUPIED_REPAIRS = { glass = true, body = true, lights = true, tires = true, visual = true }, FLAGS = { 'locked', 'engineOn', 'lightsOn', 'invulnerable' } } | Spawn offset, AV lift, cap per player (tunable), near radius (tunable), REACH = metres a typed vehicle id may be from the operator (1–10000) without opx.admin.vehicle.anywhere, repairs allowed with people aboard, flags vehicle.flag accepts. AVs are recognised by AV_PREFIXES in config/shared.lua. |
INVENTORY | { MAX_COUNT = 10000, DEFAULT_AMMO = 60 } | Largest give/removal count (tunable); ammo count when none typed. |
DOORS | { MAX_PER_BUCKET = 256, SWEEP_MS = 2000, SCAN_MS = 1000, SCAN_RADIUS = 80 } | Staff door states, only without open77_doors: per-bucket cap, server bucket sweep, client scan interval and radius (≤ 100 m). |
LINKS | PLAYERS = 'opx.players', WHERE = 'opx.where', JOB = 'opx.job', GANG = 'opx.gang', MONEY = 'opx.money', SAVE = 'opx.save', INVENTORY_OPEN = 'opx.inventory.open', INVENTORY_HOLDERS = 'opx.inventory.holders', WEATHER_SET = 'opx.weather.set', WEATHER_NEXT = 'opx.weather.next', WEATHER_FREEZE = 'opx.weather.freeze', TIME = 'opx.time', TIME_FREEZE = 'opx.time.freeze', DOORLOCK = 'opx.doorlock' | Other modules' commands the menu and eye run; false removes the row. In the access map, so greyed by the same ACL. |
WEATHER_PRESETS | { 'sunny', 'lightclouds', 'cloudy', 'rain', 'heavyclouds', 'fog', 'pollution', 'sandstorm' } | Offered on the sky screen and the eye. Must be NAMEs from the weather config. |
TIMES | { '06:00', '09:00', '12:00', '17:30', '20:30', '23:00', '03:00' } | Offered on the time screen. |
LOCATIONS | 11 rows (watson, heights, coast, stoop, northside, junction, underpass, dealer, racegrid, lab, arena) | Configured destinations { NAME, LABEL, X, Y, Z, HEADING }; NAME must be a slug. /opx.admin.self.pos copies a row in this shape. |
Spawn list: modules/admin/data/vehicles.lua (277 rows in classes, Air first). Ped list: modules/admin/data/peds.lua (250 ordinary human rigs). A malformed row is warned at boot and skipped.
Refusal codes
Locale key admin.error.<camelCase> (e.g. bad_target → admin.error.badTarget). Typed-input codes show as warnings, the rest as errors.
| Code | Meaning |
|---|---|
in_game_only | Needs a player, not the console. |
too_fast | Inside the operator's cooldown. |
failed | The handler raised; the log names it. |
busy | The operator's previous heavy request is still running. |
no_target, bad_target, not_connected, self_target, console_has_no_player | Target missing, malformed, offline, the caller where not allowed, or me from the console. |
target_protected | The target holds opx.admin.immune and the operator not opx.admin.override. Audited. |
kill_not_granted | player.health 0 without the kill command. |
not_incarnated, gate_closed, gate_unreadable | The player's body cannot be acted on yet. |
no_position | The host has no position for the player. |
kill_refused, respawn_refused, refused | The host refused; its reason is shown. |
bad_coordinates, bad_switch, bad_duration, empty_text | Malformed argument. |
unknown_vehicle, vehicle_cap, no_vehicle, occupied, unsafe_repair, bad_scope, unknown_flag, not_ours, vehicles_unavailable, keys_unavailable, vehicle_out_of_reach | Vehicle commands. vehicle_out_of_reach: typed id beyond VEHICLES.REACH or in another instance. vehicle_cap counts the cars of the operator on this connection only: cars left by an operator who logged off belong to nobody (cleanup still finds them), so the next player given that id is not capped by them. |
unknown_weapon, unknown_ammo, not_armed, holster_unavailable, give_partial, weapon_not_granted | Weapon commands. weapon_not_granted: a bag command on a weapon or ammo item without the weapon command. not_armed: holster target has no weapon drawn. holster_unavailable: no HolsterWeapon on the inventory contract, or it failed. give_partial: weapon given, ammo refused, weapon could not be taken back. |
inventory_unavailable, bad_holder, no_character, unknown_citizen, unknown_item, bad_count, not_enough, bag_no_room, bag_too_heavy | Inventory or character contract refusals. |
unknown_location, bad_location_name, seeded_location | Locations. |
bad_door, door_limit, doors_networked | Doors. |
combat_unavailable | No Open77.combat.setFriendlyFire on the host. |
unknown_ped, models_unavailable | Ped models. models_unavailable: no players.setModel native. As of 2.31.13+op77.78 it is in no published server build, so self.model and player.model always answer this today (one server log line). |
appearance_unavailable | player.wardrobe without the appearance contract. |
bad_name, characters_unavailable, search_short, bad_request | Character commands. |