The two global ACE objects
Add them to
server.cfg:
group.admin is just the conventional name — use whatever principal your server already has. If you run txAdmin, its administrators normally already sit in group.admin, so those two lines are usually all you need.
To give the builder role to someone who is not a full admin:
Framework groups and jobs
Config.Permissions.adminGroups (default { 'admin', 'superadmin' }) is checked against your framework’s group system, and Config.Permissions.adminJobs (empty by default) against the player’s job. Satisfying either is equivalent to holding nexdev_djsystem.admin.
Answers are cached for five seconds per player so framework lookups don’t run on every request, and cleared on disconnect. The server console always passes every check, so scheduled tasks and console commands are never blocked.
Layer 1 — the action tier
Each booth maps five actions to a tier:
The five actions:
This is how the shipped beach stage lets anyone add a track and hit the soundboard while keeping the transport controls for staff:
Layer 2 — the access mode
permissions.mode decides who counts as a DJ at that booth:
A global admin passes both layers everywhere.
soundboard = false on a booth disables the soundboard there for everyone, including admins. It’s a capability of the booth, not a permission.The per-booth ACE
Every booth carries its own ACE object inpermissions.ace. A booth whose mode is ace — or any, with the ACE populated — admits exactly the holders of that object.
The shipped nightclub is the worked example:
nexdev_djsystem.club_vanilla, nexdev_djsystem.club_bahama and nexdev_djsystem.beach_festival. They’re only consulted when the booth’s mode is ace or any, so declaring them costs nothing until you use them.
The DJ claim
Being allowed to DJ and currently holding the booth are two different things. The first is a permission; the second is a lock. Only one player holds the deck at a booth at a time. The claim is taken on the first control action and released on:- Closing the panel
- Disconnecting
- Walking out of range
- Dying
- An admin running
/djadmin kick Config.DJIdleTimeoutseconds without a control input (default 300)
What the panel is told
When a session opens, the server sends a resolved permission block and pushes an update whenever it changes. This is advisory only — the panel uses it to grey out buttons. Every request is re-checked on arrival, so a stale or forged permission block can only ever hide a button, never grant one.This is why a player whose job changed while their panel was open sees buttons that no longer work. The panel refreshes on a 30-second loop; the server is already correct. Reopening the panel resyncs it immediately.
Presence
WithConfig.RequirePresence = true (the default), every state-changing request also passes a proximity check against the booth anchor and all of its sound points, computed on the server from the player’s actual position.
You cannot control a booth from across the map, and you cannot control one you don’t have an open session at.
