Developer Api
Developer API
Everything here is server-side.
n4_queue parity
Same names and arguments, so migrating is a rename. See
Migrating from n4_queue for the one behaviour difference.
| Export | Returns |
|---|---|
queueList() |
Array of waiting players, in order. Adds tier and score to n4's fields. |
queueSize() |
Number waiting. |
connectingList() |
Map keyed by identifier of players past the queue but not yet loaded. |
connectingSize() |
How many of those there are. |
addPrio(identifier, points, category?, opts?) |
New row id, or nil. |
removePrio(id) |
true if a row was deleted. |
local id = exports['rivo_queue']:addPrio('discord:1234…', 2500, 'tebex', {
label = 'Gold',
tier = 'vip',
expiresAt = os.time() + 30 * 86400,
})
opts is an addition; n4's three-argument form still works.
Priority
| Export | Returns |
|---|---|
listPrio() |
Every stored row, expired included. |
prunePrio() |
Deletes expired rows now; returns the count. |
getQueueInfo(identifier) |
Position, points, rows, tier, waited, estimate. nil if not queued. |
getPosition(identifier) |
Position, or nil. |
Bans and blocks
| Export | Returns |
|---|---|
addBan(identifier, reason?, expiresAt?) |
Row id. Omit expiresAt for permanent. |
removeBan(id) |
true if lifted. |
listBans() |
Every ban row. |
getBans(identifier) |
Live bans for one identifier. Non-empty means banned right now. |
blockIdentifier(identifier, seconds, reason?) |
Temporary, memory-only. |
unblockIdentifier(identifier) |
Lifts it. |
listBlocks() |
Active blocks with remaining time. |
A ban refuses the player's next connection. It does not remove anyone
already on the server; use your admin resource for that.
Server state
| Export | Returns |
|---|---|
getSlots() |
max, online, connecting, admitting, held, reserved, free, queued. |
getMetrics() |
Counters, denials by gate, peaks, wait average and p95, admits/hour. |
getGraceHolds() |
Players whose place or slot is being held after a disconnect. |
getAdmitInterval() |
Average seconds between admissions, or nil. |
getMaintenance() |
Current maintenance state. |
setMaintenance(enabled, tiers?, seconds?, reason?) |
Turns it on or off. |
storageBackend() |
'json' or 'mysql'. |
discordStatus() |
Cache size, rate-limit state, whether the token was rejected. |
Use getSlots() when the server looks empty but nobody can get in.connecting and held are slots that GetPlayers() cannot see.
Events
Every internal event is also fired as a normal server event, so you can
subscribe without depending on this resource:
AddEventHandler('rivo_queue:denied', function(data)
print(('%s was refused by the %s gate'):format(data.name, data.gate))
end)
| Event | Payload |
|---|---|
rivo_queue:queued |
name, identifier, position, total, points, tier |
rivo_queue:admitted |
name, identifier, waited, position, points, tier, bypassed |
rivo_queue:joined |
name, identifier, points, tier, waited |
rivo_queue:left |
name, identifier, reason, waited, position |
rivo_queue:denied |
name, identifier, gate, reason |
rivo_queue:flagged |
name, identifier, matched, reason |
rivo_queue:admin |
action, by, detail |
rivo_queue:milestone |
kind (queueSize | longWait), value, player |
Payloads are copies. Changing one does not affect queue state.
Writing a priority source
A source takes a player's identifiers and returns the priority rows that apply
to them.
There are two forms. When calling from another resource, name an export. A
plain function does not survive being passed across a resource boundary
intact, and a named export keeps working if either resource restarts.
-- from another resource: name your export
exports['rivo_queue']:registerPrioritySource('tebex', 200, 'my_store', 'queuePriorityFor')
-- and in my_store:
exports('queuePriorityFor', function(ctx)
return { { points = 5000, label = 'Donator', category = 'tebex' } }
end)
The callback form is fine inside a single resource:
exports['rivo_queue']:registerPrioritySource('tebex', 200, function(ctx)
local rows = {}
for _, identifier in ipairs(ctx.all) do
local pkg = MyStore.packageFor(identifier)
if pkg then
rows[#rows + 1] = {
points = pkg.points,
label = pkg.name,
category = 'tebex',
tier = pkg.tier,
expiresAt = pkg.expiresAt,
}
end
end
return rows
end)
ctx contains:
| Field | |
|---|---|
src |
The connecting player's temporary source. |
name |
Their name. |
identifiers |
Map: { license = 'license:…', discord = 'discord:…', … } |
all |
Flat array of every identifier they presented. |
primary |
The one the queue tracks them by. |
Return an array of rows. points is required and must be above zero, or zero
when the row names a tier. Everything else is optional. Expired rows are dropped for you.
Things to know:
Match on ctx.all, not just ctx.primary. A row keyed on discord: should
still apply to a license-tracked player.
Your source may yield. HTTP calls and database queries are fine. It runs
inside the connecting player's own coroutine, so a slow source only delays that
player, not the queue.
Errors fail open. If your source errors, it is skipped with a console
warning and the player connects without those points. If a lookup times out,
return {} rather than guessing.
The second argument is the order sources are asked in. Lower runs first. It only
matters when Config.priorityMode = 'max' and two sources tie.
Built-in orders: config 10, store 20, discord 30, ace 40 (ACE and txAdmin).
prioritySources() lists what is registered.