Documentation menu

Matchmaking

One call routes a player to the right server - capacity-aware, start-aware, and grouped - across your whole network.

findServer asks the network for the best server of a given type and hands you a target you can send the player to. The network tracks live presence and capacity for you, so you never count players or pick a server by hand.

findServer

Give it a player and a server type. You get back a result with the matched server (and, for instanced games, the matched world). It’s a network call, so it’s async.

Matchmaking.java
hive.matchmaking()
    .findServer(player.uuid(), player.name(), "skywars")
    .thenAccept(result -> {
      if (result.isSuccess()) {
        hive.transfer(player, result.matchedServer());
      } else {
        player.sendMessage("No game open right now - try again.");
      }
    });

The type string ("skywars", "lobby", …) is the serverType each server announces in the Server registry. Anything that registered with that type is a candidate.

How a server gets picked

Routing isn’t just “least full.” The network weighs two things in order, so games actually start and players land together rather than scattered one-per-server:

  1. Servers that still need players to start come first - and among those, the one that needs the fewest more to fire. This fills a waiting lobby to its minimum instead of seeding a second empty one.
  2. Otherwise, the most-populated joinable server - grouping players together so lobbies fill top-down.

A server is only ever a candidate if it’s joinable (accepting players, not full, in a state that allows joins). The player’s current server is skipped, so findServer never routes someone back to where they already are.

Instanced games: match a world

Minigames run many instances (worlds) inside one server. Ask for a world type and the network picks the best world across every matching server - same start-aware, group-first priority, one level deeper.

World.java
hive.matchmaking()
    .findServerWithWorld(player.uuid(), player.name(), "skywars", "ranked")
    .thenAccept(result -> {
      if (result.isSuccess()) {
        // matchedServer + matchedWorld - send the player to the instance
        hive.transfer(player, result.matchedServer(), result.matchedWorld());
      }
    });

This is the call the Minigame SDK sits on: a lobby asks for a skywars world, the network finds an instance that’s still filling, and the player drops straight into it.

Direct join

Sometimes you want a specific server or world - a “join my friend” button, a party leader pulling their group along. Direct join validates the target and tells you precisely why if it can’t.

DirectJoin.java
hive.matchmaking()
    .joinServer(player.uuid(), player.name(), "lobby-2")
    .thenAccept(result -> {
      if (result.isSuccess()) hive.transfer(player, result.matchedServer());
      else player.sendMessage(result.errorMessage());
    });

joinServerWorld(uuid, name, serverId, worldId) does the same for a specific instance.

Reading the result

Every call resolves to the same result object, success or not - you never get an exception for an ordinary “lobby’s full.” Branch on isSuccess(), then use the match; on failure, errorMessage() is human-readable and result() is a precise enum you can switch on.

FieldWhat it is
isSuccess()whether a target was found
matchedServer()the server to send the player to (ServerInfo)
matchedWorld()the instance, when matching by world (else null)
transferId()id tying this match to the resulting transfer
errorMessage()human-readable reason on failure
result()precise outcome enum (below)

Outcomes

Successful matches return SUCCESS. The failure cases tell you exactly what went wrong, so you can show the right message or fall back:

OutcomeMeaning
NO_SERVERS_AVAILABLEnothing of that type is registered
ALL_SERVERS_FULLevery server is full or not accepting players
SERVER_NOT_FOUND / WORLD_NOT_FOUNDdirect-join target doesn’t exist
SERVER_FULL / WORLD_FULLdirect-join target is full
SERVER_NOT_JOINABLE / WORLD_NOT_JOINABLEtarget isn’t accepting joins (check its state)
ALREADY_ON_SERVER / ALREADY_IN_WORLDplayer is already there
NO_WORLDS_AVAILABLEno world of that type across any server
ERRORinternal error during matchmaking

Where the numbers come from

You don’t feed matchmaking any state. Capacity, player counts, joinable state and “needs N to start” all come from the heartbeats each server sends to the Server registry - so a match always reflects live presence, not a stale snapshot. Your job is one call and the transfer.

Queues and skill/party-aware matching are rolling out alongside the Hytale beta. Need something specific now? Ask us - we answer fast.