Developer API
On this page(6)
SnProxyUtility exposes a public developer API for other Velocity plugins: queue events and a read-only query service. The API lives in the com.sn.proxyutility.api package inside the plugin jar. There is no separate artifact.
Only com.sn.proxyutility.api is a stable, kept contract. Everything else in the jar is obfuscated and internal. Do not call it.
Getting the jar
-
Download the latest release jar.
-
Install it into your local Maven repository:
mvn install:install-file -Dfile=SnProxyUtility-<version>.jar -DgroupId=com.sn \ -DartifactId=SnProxyUtility -Dversion=<version> -Dpackaging=jar -
Depend on it with
providedscope. Never shade it.
Quick start
Declare the dependency in your velocity-plugin.json, or in the dependencies of your @Plugin annotation:
"dependencies": [
{ "id": "snproxyutility", "optional": true }
]Resolve the API when you need it:
SnProxyUtilityAPI api = SnProxyUtilityProvider.get();
if (api != null) {
api.getQueuePosition(player.getUniqueId())
.ifPresent(place -> player.sendMessage(Component.text("Position " + place.position())));
}SnProxyUtility publishes the API when it handles ProxyInitializeEvent and withdraws it on shutdown. Resolve the reference when you need it. Do not cache it.
Master switch
API events can be disabled by the proxy owner with api-events.enabled: false in config.yml. The query service stays available either way.
Events
Events are plain Velocity events. Subscribe to them through the proxy EventManager like any proxy event. Velocity delivers them on its event executor, never on a netty thread.
Cancellable events fire before the action. They use the Velocity ResultedEvent model: deny the result to abort the action.
| Event | Fired when | Cancel effect |
|---|---|---|
QueueJoinEvent | Before a player joins a server's queue: /queue, an intercepted direct connection, an auto-reconnect rejoin, or a queue bypass. Not fired when nothing would change. | The player is not queued and not moved out of a queue they already wait in. SnProxyUtility sends no message, so tell the player yourself. |
Notification events fire after the fact. They cannot be cancelled.
| Event | Fired when | Thread |
|---|---|---|
QueueLeaveEvent | After a player left a queue with /leavequeue. Other ways out of a queue do not fire it. | Velocity event executor (async) |
Payload getters:
| Event | Getters |
|---|---|
QueueJoinEvent | getPlayer(), getServer() (Velocity server name), isReconnect(), getResult() |
QueueLeaveEvent | getPlayer(), getServer() (Velocity server name) |
Listen like any Velocity event:
@Subscribe
public void onQueueJoin(QueueJoinEvent event) {
if (event.getServer().equalsIgnoreCase("event") && !event.getPlayer().hasPermission("my.event")) {
event.setResult(ResultedEvent.GenericResult.denied());
event.getPlayer().sendMessage(Component.text("The event server is closed."));
}
}Event payloads are read-only. The result is the only thing a listener can change.
Query service
Every method reads in-memory state, never blocks and is safe from any thread. Server names match without regard to case. Results are immutable snapshots.
| Method | Returns | Notes |
|---|---|---|
getApiVersion() | String | The API contract version. |
isModuleEnabled(String moduleId) | boolean | Use the MODULE_RESTART, MODULE_MOTD, MODULE_COMMAND_BLOCKER and MODULE_QUEUE constants. |
getQueuedServers() | List<String> | Servers with a queue, in queues.yml order. Empty while the queue module is off. |
getQueue(String server) | Optional<QueueView> | Empty when the server has no queue or the queue module is off. |
getQueuePosition(UUID player) | Optional<QueuePositionView> | Empty when the player waits in no queue. |
getNextRestart() | Optional<Instant> | The next planned restart, including a manual countdown. Empty when none is planned or the restart module is off. |
Views:
| View | Components |
|---|---|
QueueView | server, displayName, status, lanes, size |
QueuePositionView | server, displayName, lane, position, total, status |
status is one of OPEN, WHITELIST, FULL, OFFLINE or PAUSED. lane and position start at 1. Position 1 of every lane leaves with the next batch.
Versioning
Call getApiVersion() for the API contract version. It is independent of the plugin version. Additions bump the minor component. Existing members are never removed or changed; deprecated members keep working.