Matchmaking Guide

Getting into a room to play with (or against!) someone is very easy with Photon. There are basically three approaches: Either tell the server to find a matching room, follow a friend into their room, or get a list of rooms to let the user pick one.

We think, for modern games it's best to use quick and painless server-side matchmaking, so we discourage using room listings. Below, we will explain the options and things to keep in mind if you roll your own.

Content

Matchmaking Checklist

If you are having issues matching players, here is a quick checklist:

  • Verify that you are using same AppId in all clients.
  • Verify that clients are connected to the same Region. By design, Photon Cloud is region locked. Only players connected to same region can play with each other no matter what device or platform they're using.
  • Verify that you are using same AppVersion in all clients. More information can be found here.

  • Before trying to join a room by name, make sure that this room is created or use JoinOrCreateRoom.

  • If you are trying to join a random room, make sure to choose the same lobby (name and type) used when creating it.
  • If you are doing random matchmaking using room properties as a filter make sure to set the keys of those properties to be visible from the lobby when creating the room.
  • If you are doing random matchmaking with SQL filters make sure to set the reserved filtering properties keys used to be visible from the lobby. It is also important to relax the SQL filter with each random matchmaking attempt or create new rooms at some point after a number of failed attempts.
  • If you are implementing asynchronous matchmaking make sure to use webhooks with proper configuration (enable "AsyncJoin") or use AsyncRandomLobby.

Random Matchmaking

The workflow described here gets players into rooms without asking them to pick one (randomly) from a long list of rooms.

If you just want to get players into a room quickly, do the following:

  • Try "Join Random". This is an operation named OpJoinRandom or JoinRandomRoom, depending on the API / platform.
    • In best case, that's it. Your client will join a room successfully.
    • In worst case, no room is existing or no space is left in any room.
  • If this doesn't find a room instantly, create one!
    • If you never show room names (and why should you), don't make up a name. Let the server do this! Set null or "" as "room name" when calling OpCreateRoom. The room gets a guid which is unique.
    • Apply a value for "max players". This way, the server eventually stops adding players.
  • If your client is alone in the room (players == 1): Wait. Show a screen you're waiting for opponents.
  • When enough players are in the room, you might "start" the game. To keep new players out, "close" the room. The server stops filling up the room, even if it's not full yet.
    • Note: When you close the room, there is a short time where players maybe are already on the way in. Don't be surprised if someone joins even after closing.

Using this workflow, joining a game is a breeze for your players.

When you use this approach, check if your client API has a setting for "Auto-Join Lobby" (a parameter named "autoJoinLobby" or similar). Make sure it's false, to speed up your client's join workflow.

Not So Random Matchmaking

Totally random matchmaking is not always something players will enjoy. Sometimes you just want to play a certain map or mode (two versus two, etc.).

In Photon Cloud and Loadbalancing, you can set arbitrary "Custom Room Properties" and use them as filter in JoinRandom.

Room Properties and the Lobby

Room properties are synced to all players in the room and can be useful to keep track of the current map, round, start-time, etc. They are handled as Hashtable with string keys. Brief names are better, so use "gm" instead of "GameMode" as example.

By default, properties are not sent to the Master Server to keep things lean. To make "map" and "game mode" available for for matchmaking, you can set a list of "room properties shown in the lobby" when you create a room.

Note that "ai" has no value yet. It won't show up in the lobby until it's set in the game via Room.SetCustomProperties. When you change the values for "map" or "ai", they will be updated in the lobby with a short delay, too.

Keep the list short to make sure your clients performance doesn't suffer from loading the list.

Again: You don't have to join the lobby (and get the awfully long room list) to make use of roomPropsInLobby. When you set some for the lobby, they become available as filter, too.

Filtering Room Properties in Join Random

In OpJoinRandom, you could pass a Hashtable with expected room properties and max player value. These work as filters when the server selects a "fitting" room for you.

If you pass more filter properties, chances are lower that a room matches them. Better limit the options.

Make sure you never filter for properties that are not known to the lobby (see above).

Play with Your Friends

If your users communicate with friends (e.g. with Photon Chat), they can easily make up a room name and everyone just uses OpJoinOrCreateRoom to get into that room.

A unique room name could be composed (e.g.) as: "friendName1 + friendName2 + randomInteger".

To avoid anyone else joining, create the room invisible like so:

With JoinOrCreateRoom, the room gets created if it didn't exist. If it's full, the operation OpJoin fails, so check: OnOperationResponse.

You can also find a friend with OpFindFriends - if you got unique userIDs.

Publishing UserIDs in a Room

Photon uses a UserID in various places. For example, you can find friends only with a suitable UserID per player. We added an option to Photon, which makes the UserID of players known per room. For that, set RoomOptions.PublishUserId to true, when you create a room. The server will then provide the UserID and you can access it on the client via PhotonPlayer.UserId.

Notes:

  • UserIDs are broadcasted, with player properties, in the Photon join event.
  • The UserID for a client can be set in three ways:
    1. Client sets LoadBalancingClient.UserId before connecting.
    2. Returned by an external web service using Custom Authentication. It will override the value sent by the client.
    3. Photon will make up UserIDs (GUID) for users that don't explicitly set theirs.
  • Generally, UserIDs, are not intended to be displayed.

Matchmaking Slot Reservation

Sometimes, a player joins a room, knowing that a friend should join as well. With Slot Reservation, Photon can block a slot for specific users and take that into account for matchmaking. To reserve slots there is an expectedUsers parameter in the methods that get you in a room (JoinRoom, JoinOrCreateRoom, JoinRandomRoom and CreateRoom).

When you know someone should join, pass an array of UserIDs. For JoinRandomRoom, the server will attempt to find a room with enough slots for you and your expected players (plus all active and expected players already in the room). The server will update clients in a room with the current expectedUsers, should they change.

To support Slot Reservation, you need to enable publishing UserIDs inside rooms.

Example Use Case:

You can use this to support teams in matchmaking: The leader of a team does the actual matchmaking. He/She can join a room and reserve slots for all members.

The others don't have to do any matchmaking but instead repeatedly call:

When the leader arrives in a room, the FindFriends operation will reveal that room's name and everyone can join it.

Lobbies

Photon is organizing your rooms in so called "lobbies". There is a default lobby but your clients can create new ones on the fly. Lobbies begin to exist when you specify a lobby in CreateRoom.

Like rooms, lobbies can be joined. In a lobby, the clients only get the room list of that lobby when applicable. Nothing else. There is no way to communicate with others in a lobby.

By now, we encourage everyone to not join lobbies: Often clients just get a long list of room names and players pick one randomly to finally start playing. There is not a lot information in a long list of room names, which have all the same ping.

To give your players more control over the matchmaking, use filters for random matchmaking.

Multiple lobbies can still be useful, as they are also used in (server-side) random matchmaking.

Lobbies are identified using their name and type. The name can be any string, however there are only 3 types of lobbies. Each one has a unique capability which suits specific use cases:

Default Lobby Type

Nothing special about this type of lobbies. It is the most suited type for synchronous random matchmaking. Probably the less sophisticated and most used type.

For example, the recommended default lobby (TypedLobby.Default) has a null name and LobbyType.Default type.

While joined to a default lobby type, the client will receive periodic room list updates.

SQL Lobby Type

This lobby is similar to the default lobby type with two major differences:

  • SQL filtering in JoinRandomRoom replaces the default "expected lobby properties" filtering.
  • "Custom Room Listing".

This lobby type adds a more elaborate matchmaking filtering which could be used for a server-side skill-based matchmaking that's completely client-driven.

Internally, SQL-lobbies list rooms in a SQLite table with up to 10 special "filtering-properties". Currently, the naming of those is fixed as: "C0", "C1" up to "C9". Only integer-typed and string-typed values are allowed and once a value was assigned to any column in a specific lobby, this column is locked to values of that type. Despite the static naming, clients have to define which ones are needed in the lobby. Also, you can still use custom room properties, visible or invisible to the lobby, other than the "Cx" ones.

Example:

Queries can be sent in JoinRandomGame operation. The filtering queries are basically SQL WHERE conditions based on the "C0" .. "C9" values. Find the list of all SQLite supported operators and how to use them here.

Custom Room Listing

Client can also request a custom list of rooms from an SqlLobby using SQL-like queries.

This operation is asynchronous. The response will contain the returned rooms list if any.

Skill-based Matchmaking

You can use lobbies of the SQL-type to implement your own skill-based matchmaking.

First of all, each room gets a fixed skill that players should have to join it. This value should not change, or else it will basically invalidate any matching the players in it did before.

As usual, players should try to get into a room by JoinRandomRoom. The filter should be based on the user's skill. The client can easily filter for rooms of "skill +/- X".

JoinRandomRoom will get a response immediately as usual but if it didn't find a match right away, the client should wait a few seconds and then try again! You can do as many or few requests as you like. Best of all: The client can begin to relax the filter rule over time.

It's important to relax the filters after a moment. Granted: A room might be joined by a player with not-so-well-fitting skill but obviously no other room was a better fit and it's better to play with someone.

You can define a max deviation and a timeout. If no room was found, this client has to open a new room with the skill this user has. Then it has to wait for others doing the same.

Obviously, this workflow might take some time when few rooms are available. You can rescue your players by checking the "application stats" which tell you how many rooms are available. You can adjust the filters and the timing for "less than 100 rooms" and use different settings for "100 to 1000 rooms" and again for "even more".

Asynchronous Random Lobby Type

This lobby is similar to the default lobby type with two major differences:

  1. Room entries stay in the lobby list (available for matchmaking) for one hour after they are removed from game servers. Rooms need to be visible and open to be considered in the asynchronous matchmaking.
  2. Rooms lists are not sent to clients.

So it does not make sense to join lobbies with this type.

This type of lobby should be combined with webhooks or any other mean of persisting room states in order to get asynchronous (re)join fully working.

Lobby Types Comparison

LobbyType Periodic Rooms List Updates SQL Filtering Custom Room Properties Filtering TTL For Removed Rooms Entries (minutes)
Default X 0
SQL 0
Asynchronous X X 60

Other Matchmaking Options

If you want to roll your own matchmaking, please make sure that most of that is done server side. The clients don't have perfect information about how full rooms are as the room-list update frequency from server to client is low (~1..2 seconds).

When you have thousands of players, several will send their "join" requests at the very same time. If a room gets full quickly, your players will frequently fail to join rooms and matchmaking will take longer and longer.

On the other hand, the server can distribute players perfectly, preferring almost full rooms and respecting your filtering.

That said, using a room as matching place, before the actual gameplay starts, is in most cases not a good idea for popular games.

 To Document Top