Automated matchmaker

In the previous section, we learned how players can manually create and join lobbies.

Players may also join a "matchmaking queue" and be automatically placed in a lobby!

Joining and leaving the matchmaking queue

A player can join the matchmaking queue like this:

var matchmaker_ticket

func join_matchmaker_queue():
    var result = await W4GD.matchmaker.join_matchmaker_queue({
        # Arbitrary properties used to group players together.
        game_mode = 'battle-royale',
        region = 'eu',
    if result.is_error():
        print("ERROR: ", result.message)

    matchmaker_ticket = result.get_data()

func _on_matchmaker_matched(p_lobby_id):
    var result = await W4GD.matchmaker.get_lobby(p_lobby_id).async()
    if result.is_error():
        print("ERROR: ", result.message)

    var lobby = result.get_data()

    print("Matched into lobby with ID: ",

They are given a "matchmaker ticket" which represents their position in the queue. This ticket can be used to later leave the queue:

func leave_matchmaker_queue():
    if matchmaker_ticket == null:

    await W4GD.matchmaker.leave_matchmaker_queue(matchmaker_ticket).async()
    matchmaker_ticket = null

Matchmaking profiles

A "matchmaking profile" represents a type of lobby that can be automatically created out of players in the matchmaking queue.

You can create one in the W4 Cloud dashboard by:

  1. Going to Multiplayer -> Matchmaker

  2. Clicking the "+ New Matchmaking Profile" button in the upper-right corner

  3. Filling out the form!


Here's a quick run down of what the form fields mean:


A human-readable name of the profile to help you keep them organized. Players won't see these names.


A JSON object that describes which players this profile applies to.

For example, if you wanted to make matches from all the players that put {"game_mode": "battle-royale"} when joining the queue, you'd use a query like this:

    "constraints": {
        "ticket.game_mode": {
            "value": "battle-royale",
            "op": "="

We'll cover more advanced queries below!

Minimum number of players

The minimum number of players required to make a lobby.

Maximum number of players

The maximum number of players to put in a lobby.


If enabled, players will be assigned to lobbies that were previously created by the automated matchmaker (so long as they haven't been sealed yet).


Progressive lobbies aren't implemented yet!

Lobby properties

A JSON object that will be used as the properties for the new lobby that gets created.

As discussed in Lobby basics, these are arbitrary values that can be used by your game.

However, in combination with W4 Cloud Dedicated Servers, there are special properties that can be used to control which region the dedicated server for the new lobby will be created in. See the Dedicated Servers section for more information.

Default lobby state

The state the new lobby will be created in. You can set this to "Sealed" to start the match immediately, or use one of the lower states to keep the lobby open for a bit before starting.

Private lobby

If enabled, the new lobby will be private, which means it can't be found by players that aren't already in it.

WebRTC (peer-to-peer)

If enabled, this will be a WebRTC (peer-to-peer) lobby, which can use the WebRTC signalling server built into the matchmaker. See Peer-to-peer via WebRTC.

Queries based user properties

As we saw in the example in the previous section, constraints in matchmaking queries can be based on the properties on a player's matchmaker ticket.

However, they can also be based on information related to the user in the database. This can be useful for properties of the player that you may not necessarily want to trust from the game client.

For example, let's say you want to group players based on their rank. If you allow the game client to specify the player's rank in the matchmaker ticket, a player could hack their client in order to claim they are in a higher or lower rank.

But if the player's rank is instead stored on a table in the database that the player can't update, you can make a constraint that "joins" with that table to check their rank.

For example, consider this query:

    "joins": {
        "user_rank": {
            "schema": "public",
            "table": "user_rank",
            "column": "id"
    "constraints": {
        "ticket.mode": {
            "value": "battle-royale",
            "op": "="
        "user_rank.rank": {
            "value": [0, 3],
            "op": "between"

This presumes that there is a table in your database called user_rank, with two columns:

  • id: The user ID (a UUID)

  • rank: The users rank (an integer)

The above query will apply to players who both (1) put {"game_mode": "battle-royale"} when joining the matchmaking queue, AND who have a rank between 0 and 3 (inclusive) per the user_rank table.


Multiple constraints will always be AND'ed together.

List of all constraint operators




Equal to


Greater than


Greater than or equal to


Less than


Less than or equal to


Greater than


not equal to


Between two values, inclusive, meaning between 0 and 3 would match 0, 1, 2 or 3. It's case-insensitive.

How does it work?

The automated matchmaking is performed by an app called "cyklotron" that's internal to the W4 Cloud.

Here are some details of its implementation that may be helpful:

  • It will always attempt to match the highest number of players in a single lobby. So, if a profile has a player minimum of 2 and a maximum of 4, and there are 4 matching players in the queue, it will make one lobby with all 4 players, rather than two with 2 players each.

  • If more than the maximum number of players in the queue match a profile, it will favor the players that have been in the queue longer.

  • Players that match multiple profiles will only ever be assigned to one lobby. Database locking is used to achieve this.

  • Profiles are processed in parallel. If multiple profiles apply to the same players, processing of those profiles will slow down due to database locking. For best matchmaking performance, use multiple, mutually exclusive profiles when possible.


    Parallel profile processing isn't implemented yet.