Paper / Spigot
Reports player counts and arrivals, exposes atlas() and setStatus(), delivers messages on the Bukkit scheduler, and owns orphan safety shutdown.
Java API · 1.0
The Atlas Java SDK gives every Minecraft server and proxy one view of the whole network. Read projected state from memory, make authoritative claims asynchronously, and keep running when the control plane is far away.
try (AtlasClient atlas = AtlasClient.connect()) {
atlas.awaitReady(Duration.ofSeconds(5));
// Local: no network call.
int online = atlas.playerCount("squidgame");
// Central: returns immediately.
atlas.reserveSlot("squidgame", playerId)
.thenAccept(slot -> connect(slot.serverId()));
}
Get started
Atlas requires Java 17 or newer. Add the SDK artifact supplied with your Atlas installation to your plugin build. Platform adapters for Paper, Velocity, and BungeeCord are shipped separately.
An Atlas-managed process receives ATLAS_SDK_SOCKET and ATLAS_SERVER_ID from its local agent. Do not put server IDs or control-plane credentials in your plugin.
com.atlasggatlas-sdk0.1.017+<dependency>
<groupId>com.atlasgg</groupId>
<artifactId>atlas-sdk</artifactId>
<version>0.1.0</version>
</dependency>
Use the Atlas artifact repository or SDK bundle provided with your account.
Get started
Call AtlasClient.connect() during plugin startup. It opens the local Unix socket and identifies the current Atlas server using agent-provided environment values.
The client initially has an empty registry. If “not loaded yet” must be different from “the network is empty,” wait for the first projection with awaitReady.
A process started outside the agent will fail clearly because its socket path and server identity are absent.
private AtlasClient atlas;
public void start() throws IOException {
atlas = AtlasClient.connect();
atlas.awaitReady(Duration.ofSeconds(5));
}
public void stop() throws IOException {
if (atlas != null) atlas.close();
}
Get started
Method shape tells you what the call costs. Synchronous query methods read the SDK’s in-process projection. Methods that need authoritative agreement return CompletableFuture. Fire-and-forget mutations can throw IOException if they cannot reach the local agent.
T method()servers(), playerCount()Advisory state on hot pathsCompletableFuture<T>reserveSlot(), locate()Placement and central truthvoid … throws IOExceptionsetStatus(), send()Reports and best-effort deliveryCore API
Every query below is thread-safe and reads memory in the current process. Values describe the whole Atlas network, not just the current node, and are normally about one second behind.
playerCount(group)Players across every server in a group.availableSlots(group)Advisory free slots across joinable servers.networkPlayerCount()True total from proxy groups, without double-counting backends.servers(group)All projected servers in one group.servers()All projected servers in the network.routableServers(group)Ready, accepting, addressable servers.self()This server’s view, or null before the projection.Seeing one available slot does not make it yours. Call reserveSlot before moving a player.
int players = atlas.playerCount("lobby");
int capacity = atlas.availableSlots("lobby");
List<ServerView> targets =
atlas.routableServers("lobby");
for (ServerView server : targets) {
logger.info("%s — %d/%d".formatted(
server.getServerId(),
server.getPlayerCount(),
server.getSlotCapacity()));
}
Core API
Atlas does not inspect your game. Report the facts that drive placement and lifecycle decisions: current occupancy, successful arrivals, and your game status.
Status names are yours. Your group configuration decides whether a value such as INGAME stops new placements and starts replacement capacity.
playerArrived releases the short-lived hold and reports the authoritative backend count in one operation.
// Match begins: remain healthy, stop taking players.
atlas.setStatus("INGAME");
// A reserved player reaches the backend.
int count = Bukkit.getOnlinePlayers().size();
atlas.playerArrived(player.getUniqueId(), count);
// Match ends: restore ordinary joinability.
atlas.clearStatus();
Core API
onEvent derives events from the same projection queries read. Delivery is at-most-once, not durable, and updates may be coalesced. Treat events as a prompt to inspect state—not as a ledger.
ServerStarting, ServerReady, ServerStopping, ServerStopped, ServerStatusChanged, and ServerJoinabilityChanged.
PlayerJoinedNetwork, PlayerLeftNetwork, and PlayerSwitchedServer.
Payments, punishments, and inventory writes need durable application state. Atlas lifecycle events can be missed during a disconnect.
atlas.onEvent(event -> {
// Callback runs on the SDK reader thread.
Bukkit.getScheduler().runTask(plugin, () -> {
if (event instanceof
AtlasEvent.ServerReady ready) {
announce("Ready: "
+ ready.server().getServerId());
}
});
});
Core API
atlas.player(uuid) returns a handle whether the player is online or not. Presence stays central, so location and transfer methods are asynchronous.
locate()Returns proxy, server, group, node, and placement time.isOnline()Checks for a live presence record.transfer(group)Reserves capacity, then asks the holding proxy to move.transferTo(server)Moves to an exact server without creating a reservation.send(payload)Fire-and-forget delivery to the current holder.Messages perform no location lookup. The server holding the player owns a live subscription; when that connection disappears, the route disappears with it.
AtlasPlayer player = atlas.player(playerId);
player.locate().thenCompose(presence -> {
if (!presence.online()) {
return CompletableFuture.failedFuture(
new IllegalStateException("offline"));
}
return player.transfer("squidgame");
}).exceptionally(error -> {
logger.warning(error.getMessage());
return null;
});
player.send("The next match is ready.");
Core API
Use atlas.players(…) instead of looping over individual players. A party request considers everyone in one central operation, so separate reservations cannot scatter the group by accident.
ALL_OR_NOTHINGOne server fits everyone, or the request fails.PREFER_TOGETHERTry one server first, then split across the best options.SPLITPlace each member using the group strategy.A completed party transfer may still contain unplaced players. Always inspect isComplete(), placed(), and unplaced().
AtlasParty party = atlas.players(memberIds);
party.transfer("squidgame",
PartyPolicy.PREFER_TOGETHER)
.thenAccept(result -> {
if (!result.isComplete()) {
result.unplaced().forEach((id, reason) ->
logger.warning(id + ": " + reason));
}
});
Reliability
The client reconnects indefinitely with capped exponential backoff and jitter. After reconnecting, it identifies itself again and replays held players, player count, and status.
Requests already in flight fail with a retryable AtlasConnectionException. Issue a fresh request after the connection-state callback reports true.
The Paper adapter can shut down a backend safely after a sustained agent outage. The default grace is five minutes, followed by a 30-second player warning and save. Set ATLAS_ORPHAN_SHUTDOWN_GRACE_SECONDS to at least 120 seconds, or use 0/off to disable it.
atlas.onConnectionStateChanged(connected -> {
logger.info("Atlas agent: "
+ (connected ? "connected" : "reconnecting"));
});
atlas.onDisconnectedFor(Duration.ofMinutes(2),
() -> scheduler.execute(this::enterSafeMode));
if (atlas.isConnected()) {
submitPlacement();
}
Reliability
AtlasException means the agent or control plane rejected an operation. Inspect code() and retryable(). AtlasConnectionException is an I/O failure on the local agent connection and is always safe to retry after reconnection.
Async failures normally arrive wrapped by CompletionException. Unwrap the cause before classifying it.
INVALID_ARGUMENTNOT_FOUNDCONFLICTUNAVAILABLETIMEOUTSEQUENCE_GAPINTERNAL
atlas.reserveSlot(group, playerId)
.whenComplete((slot, failure) -> {
if (failure == null) {
connect(slot.serverId());
return;
}
Throwable cause = failure.getCause() == null
? failure : failure.getCause();
if (cause instanceof AtlasException e
&& e.retryable()) {
enqueueForLater(playerId);
}
});
Reliability
Event, message, connection, and transfer callbacks run on the SDK reader/reconnect thread. Hand platform work to Bukkit, Velocity, or BungeeCord’s scheduler before touching their APIs.
Do not block a callback. It shares the connection path that delivers events and completes claim replies.
atlas.onPlayerMessage((playerId, payload) ->
Bukkit.getScheduler().runTask(plugin, () -> {
Player player = Bukkit.getPlayer(playerId);
if (player != null) {
player.sendMessage(new String(payload,
StandardCharsets.UTF_8));
}
})
);
Reference
The packaged adapters implement the platform-specific bridge around the same Java client.
Reports player counts and arrivals, exposes atlas() and setStatus(), delivers messages on the Bukkit scheduler, and owns orphan safety shutdown.
Synchronizes projected backends, resolves initial placement, reports presence, executes cross-network transfers, and publishes the Atlas player count in pings.
Provides the same placement, presence, transfer, messaging, and ping behavior through BungeeCord’s scheduler and event model.
Reference
ServerViewProjected protobuf view: IDs, group and node, lifecycle state, players, capacity, status, joinability, address, port, kind, and observation time.
SlotreservationId, serverId, expiresAtUnixMs, plus expiresAt() and isExpired().
PlayerPresenceonline, proxyId, serverId, groupId, nodeId, and sinceUnixMs.
JoinPlacementThe selected groupId, a live slot, and whether the player’s remembered group was used.
PartyPlacementPrimary server, expiry, per-player slots, and per-player failure reasons. Helpers report completeness and togetherness.
TransferRequestThe player, target server, and optional reservation that a proxy adapter must execute on its platform thread.
Reference
static connect()awaitReady(Duration)static connect(Path, String)playerCount(String)availableSlots(String)networkPlayerCount()servers(String)servers()routableServers(String)self()setStatus(String)clearStatus()setPlayerCount(int)playerArrived(UUID[, int])player(UUID)players(Collection|UUID…)holdPlayer(UUID)releasePlayer(UUID)onPlayerMessage(BiConsumer)onPlayerTransfer(Function)reportPresence(…)reportPlayerLeft(UUID, String)reserveSlot(String, UUID)reserveParty(…)resolveJoin(UUID)onEvent(Consumer)isConnected()onConnectionStateChanged(Consumer)onDisconnectedFor(Duration, Runnable)close()uuid()locate()isOnline()transfer(String)transferTo(ServerView)send(byte[])send(String)members()reserve(String[, PartyPolicy])transfer(String[, PartyPolicy])Generated protocol types live under com.atlasgg.protocol.v1. Application-facing helpers live under com.atlasgg.sdk.
reserveSlot or a concept such as events.