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 her room, or fetch a list of rooms to let the user pick one. All three variants are supported by Photon and you can even roll your own.

We think, for most games it's best to use a quick and simple matchmaking, so we suggest to use Random Matchmaking and maybe filters for skills, levels and such.


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. 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.
  • Verify that players have different UserIds. Players with same UserId cannot join the same room.
  • Before trying to join a room by name, make sure that this room is created. Alternatively 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.

Back To Top

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.

Back To Top

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.

Back To Top

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.

RoomOptions roomOptions = new RoomOptions();
roomOptions.CustomRoomPropertiesForLobby = { "map", "ai" };
roomOptions.CustomRoomProperties = new Hashtable() { { "map", 1 } };
roomOptions.MaxPlayers = expectedMaxPlayers;
lbClient.OpCreateRoom(roomName, roomOptions, typedLobby);

Note that "ai" has no value initially. It won't show up in the lobby until it's set in the room 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.

Back To Top

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.

Hashtable expectedCustomRoomProperties = new Hashtable() { { "map", 1 } };
lbClient.OpJoinRandomRoom(expectedCustomRoomProperties, expectedMaxPlayers);

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).

Back To Top

Matchmaking For Low CCU

For really good (skillbased) matchmaking, a game needs a couple hundred players online. With less players online, it will become harder to find a worthy opponent and at some point it makes sense to just accept almost any match.

You have to take this into account when you build a more elaborate matchmaking on the client side. To do so, the Photon Master Server provides the count of connected users, rooms and players (in a room), so you can adjust the client-driven matchmaking at runtime.

The LoadBalancingClient.RoomsCount should be a good, generic indicator of how busy the game currently is. You could obviously also fine tune the matchmaking by on how many players are not in a room (LoadBalancingClient.PlayersOnMasterCount). Whoever is not in a room might be looking for one.

For example, you could define a low CCU situation as less than 20 rooms. So, if LoadBalancingClient.RoomsCount < 20, your clients use no filtering and instead run the Random Matchmaking routine (try to find a room, create one and wait if that fails).

If you want to appeal to the patience of your players, you could indicate the potentially longer wait time. Obviously, you may come up with more than two tiers of skillbased-matchmaking.

Back To Top

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:

RoomOptions roomOptions = new RoomOptions();
roomOptions.IsVisible = false;
lbClient.JoinOrCreateRoom(nameEveryFriendKnows, roomOptions, typedLobby);

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.

Back To Top

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.


  • 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.

Back To Top

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).

// join room example
lbClient.OpJoinRoom(roomName, expectedUsers);
// create room example
lbClient.OpJoinOrCreateRoom(roomName, roomOptions, typedLobby, expectedUsers);
// join random room example
lbClient.OpJoinRandomRoom(expectedProperties, maxPlayers, expectedUsers, matchmakingType, typedLobby, sqlLobbyFilter, expectedUsers);
// create room example
lbClient.OpCreateRoom(roomName, roomOptions, typedLobby, expectedUsers);

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.

Back To Top

Example Use Case: Teams Matchmaking

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:

Try to find a random room:

lbClient.OpJoinRandomRoom(expectedProperties, maxPlayers, expectedUsers, matchmakingType, typedLobby, sqlLobbyFilter, teamMembersUserIds);

Create a new one if none found:

lbClient.OpCreateRoom(roomName, roomOptions, typedLobby, teamMembersUserIds);

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

lbClient.OpFindFriends(new string[1]{ leaderUserId });

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


Back To Top


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 can begin to exist when you specify a lobby in JoinLobby or CreateRoom or JoinOrCreateRoom.

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.

We encourage everyone to not join lobbies as the list of rooms is not useful in most cases:

  • there is no difference in terms of ping between the entries of the list
  • usually players are looking for a quick match
  • receiving rooms list adds an extra delay and consumes traffic
  • a long list with too much information can have a bad effect on the user experience

Instead, 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:

Back To Top

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.

Back To Top

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.


RoomOptions roomOptions = new RoomOptions();
roomOptions.MaxPlayers = expectedMaxPlayers;
// in this example, C0 might be 0 or 1 for the two (fictional) game modes
roomOptions.CustomRoomProperties = new ExitGames.Client.Photon.Hashtable() { { "C0", 1 } };
roomOptions.CustomRoomPropertiesForLobby = new string[] { "C0" }; // this makes "C0" available in the lobby
// let's create this room in SqlLobby "myLobby" explicitly
TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);
lbClient.OpCreateRoom(roomName, roomOptions, sqlLobby);

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.

TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);    // same as above
string sqlLobbyFilter = "C0 = 0";   // find a game with mode 0
lbClient.OpJoinRandomRoom(null, expectedMaxPlayers, matchmakingMode, sqlLobby, sqlLobbyFilter);
// other filter examples:
// "C0 = 1 OR C6 > 50"
// "C5 = \"Map2\" AND C2 > 10 AND C3 < 20"
// "C8 BETWEEN 0 AND 100"
// "C4 IN ('Map1', 'Map2', 'Map3')"

Back To Top

Custom Room Listing

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

TypedLobby sqlLobby = new TypedLobby("myLobby", LobbyType.SqlLobby);    // same as above
string sqlLobbyFilter = "C0 = 0";   // request games with mode 0
lbClient.OpGetGameList(sqlLobby, sqlLobbyFilter);

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

Back To Top

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".

Back To Top

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.

Back To Top

Lobby Types Comparison

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

Back To Top

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