Matchmaking API
Introduction
One key requirement while creating a multiplayer game is to easily be able to match players together with similar skills, level, or who want to play the same game type or map, making the overall experience in-game as enjoyable as possible.
For this purpose, Photon Fusion exposes a set of API calls that can be used to create the best experience for players looking for the perfect match.
Photon Fusion works with the Photon Cloud transparently, so most of the interactions with the Photon backend services are done automatically.
This page describes the Fusion Matchmaking API, which is used to create a Game Session
optionally with custom properties that can be used by players to filter/join the best Session
based on their desired gameplay experience.
Glossary
In order to fully understand the API, below are described some terms used along this document that are directly related to the correct usage of the Matchmaking API
.
Game Session
: Or justSession
, is where Players meet to play a match or communicate. This is what gets published in the Photon Cloud and is made available so other clients can search, filter, and join a particular game. Communication outside of anySession
is not possible and any client can only be active in oneSession
.Game Sessions
have the following characteristics: they can be created and joined by name, haveCustom Properties
, a maximum player count, can be hidden (not shown in aLobby
) or visible, and can be closed (no one can join) or open. InPUN
andPhoton Realtime
, it was formerly called aRoom
.Lobby
: Is a virtual container or "list" ofSessions
. It is possible to use multiple lobbies to split the sessions between different game types, for example, as it is basically a way to have aGame Session
listed within an arbitrary set of sessions. Clients can't communicate in theLobby
and they never know that another client is also in the lobby. A client can only be in aLobby
, aGame Session
or neither.
Creating and Joining a Game Session
The Game Session
creation and joining are two parts of the same procedure, and the rules are simple:
1. If there is no Session
with the specified SessionName
, a new one will be created (not in all cases as explained below) with that SessionName
.
2. If there is already a Session with that SessionName
, the peer will join it.
In terms of API, all of this is done automatically when a new Fusion Simulation is started.
Below are the main arguments that can be used to customize the Session
when creating a new one or used as a filter when looking for a Session
to join:
C#
NetworkRunner.StartGame(new StartGameArgs {
// ...
// Arguments related to the Matchmaking between peers
SessionName = [string],
SessionProperties = [Dictionary<string, SessionProperty>],
CustomLobbyName = [string],
EnableClientSessionCreation = [bool],
PlayerCount = [int],
IsOpen = [bool],
IsVisible = [bool],
MatchmakingMode = [MatchmakingMode],
});
All the matchmaking related arguments are optional and the default values each one will take are described below:
- SessionName: the
Game Session
'sName
orID
, it identifies the session on thePhoton Cloud
and it must be unique within a Region. If no name is set, Fusion will generate a randomGUID
to identify the Session. - SessionProperties: the
Session
'sCustom Properties
are a way to include metadata on yourGame Session
, like the game mode/type or the current map, for example. Keep in mind that all properties are always published to theLobby
when creating theSession
and those properties can be used as matching filters when a peer is joining a randomSession
(read more below). As a suggestion, always try to keep theProperty Keys
as short as possible in order to minimize traffic. By default, theSession Custom Properties
are empty and no extra information is included. - CustomLobbyName: this argument is used to set a custom
Lobby Name
with which theSession
will be associated. By default, Fusion already separates aSession
based on theGameMode
(ClientServer Lobby
when starting inHost
,Server
, orClient
, andShared Lobby
when starting inShared
game mode). - EnableClientSessionCreation: this flag changes which
Client
peer types can create a newGame Session
or only join one. See below for more info. - PlayerCount: defines the maximum number of clients that can join a
Session
. This parameter is only used when creating a newSession
and by default, it takes the value from theDefault Players
field on theNetworkProjectConfig/Simulation
. - IsOpen: defines if the
Game Session
being created (when applicable), will be set as open for any other players to join. See Getting and Updating the Game Session information for more info. - IsVisible: defines if the
Game Session
being created (when applicable), will be visible to other players. See Getting and Updating the Game Session information for more info. - MatchmakingMode: defines the matchmaking mode used when trying to join a Random
Game Session
.- FillRoom: Fills up rooms (oldest first) to get players together as fast as possible. Default.
- SerialMatching: Distributes players across available rooms sequentially but takes filters into account. Without filter, rooms get players evenly distributed.
- RandomMatching: Joins a (fully) random room. Expected properties must match, but aside from this, any available room might be selected.
It is possible to create and join a Game Session
, either at random or with a specific SessionName
(useful for Game Invites, for example), and also enable Session
filtering using custom properties, in order to only join games with specific configurations.
This already provides a lot of flexibility when managing the sessions.
The following table summarizes how Fusion handles the Game Session
creation and joining, as it depends on the SessionName
, GameMode
, and whether EnableClientSessionCreation
is enabled or not when starting the simulation.
GameMode | Session Name | |||||
---|---|---|---|---|---|---|
Valid | Empty/Null | |||||
Server/Host | Create or Join specific Session | Create or Join Session with Random ID | ||||
EnableClientSessionCreation | ||||||
Null (default) | True | False | Null (default) | True | False | |
Client | Join Session | Create or Join Session | Join Session | Join Random Session | Join Random or Create | Join Random Session |
Shared | Create or Join Session | Create or Join Session | Join Session | Join Random or Create | Join Random or Create | Join Random Session |
AutoHostOrClient | Create or Join Session | Create or Join Session | Join Session | Join Random or Create | Join Random or Create | Join Random Session |
Getting and Updating the Game Session information
Fusion provides a lot of information about the currently connected Game Session
, such as its Name
and Region
.
This data is directly available in NetworkRunner
through the SessionInfo
property.
Below are listed all available fields of the SessionInfo
type:
| Property | Description |
| --- | --- |
| IsValid [bool{get}]
| Signals if the SessionInfo
is ready for read/write. |
| Name [string{get}]
| the Session Name
. |
| Region [string{get}]
| the currently connected Region
. |
| Properties [Dictionary<string, SessionProperty>{get}]
| A read-only dictionary with the current Session Custom Properties
. In order to update these properties, just use the SessionInfo.UpdateCustomProperties(Dictionary<string, SessionProperty>)
method and pass a new set of properties. |
| IsVisible [bool{get,set}]
| Signals if the Session
is Visible
on Lobby
. Making a Session
invisible is simply a matter of changing this property. |
| IsOpen [bool{get,set}]
| Signals if the Session
is Open
to join. In order to close or open a Session
, just alter this property. |
| PlayerCount [int{get}]
| The current number of Players
in the Session
. Only available in the Lobby. |
| MaxPlayers [int{get}]
| Maximum number
of peers that can join the Session
, this value also includes a slot for the Server/Host
peer. Only available in the Lobby. |
Keep in mind that the Session
information and especially the Custom Properties
, should be used for Matchmaking purposes only and never to synchronize game state information with the game clients, for example. We highly discourage such uses.
If you need to exchange information session-wide that is related to gameplay, Fusion offers plenty of options, like having global NetworkObject
instances or using RPC
s for one-shot data.
The NetworkRunner
also provides some other Session
and Photon Cloud
related properties that can be used in-game, like:
| Property | Description |
|---|---|
| NetworkRunner.IsCloudReady
| Signals if the Local Peer is connected to Photon Cloud
and is able to Create/Join a Room or join a Lobby. |
| NetworkRunner.UserId
| Holds the UserId
associated with the Local Peer after it gets authenticated. This information comes from the Authentication Service used by your application. |
| NetworkRunner.AuthenticationValues
| Holds a reference to the AuthenticationValues
used to authenticate the local peer when starting Fusion. |
| NetworkRunner.CurrentConnectionType
| Describes the current Connection Type used by the peer, either a Direct
or Relayed
connection with the remote Server
. Keep in mind that in SharedMode
, the clients always connect via relay. |
| NetworkRunner.NATType
| When the NAT Punchthrough System is enabled, Fusion will try to determine the current NAT Type of the current network where the local peer is running, this property exposes that information. NAT Types can be: Invalid
, UdpBlocked
, OpenInternet
, FullCone
or Symmetric
. |
| NetworkRunner.IsSharedModeMasterClient
| A boolean flag that indicates if the local peer is also the Master Client
of a Shared Game Session
. This is only valid when running in SharedMode
and can be used to determine on which peer certain actions should happen based on the differentiation between other clients and the Master Client
. |
Another related field is the NetworkRunner.LobbyInfo
, that exposes information about the current Lobby
the peer is connected to (keep in mind that a peer can only be in a Lobby
, a Game Session
or disconnected). Here we list the main properties of the LobbyInfo
:
| Property | Description |
|---|---|
| IsValid [bool{get}]
| Signals if the LobbyInfo
is ready for read/write. |
| Name [string{get}]
| Contains the name of the current Lobby. |
Game Session Browser
Game Session browsers were popular in the 90s and early 2000s. The fundamental design is still valid but nowadays their main purpose (finding a suitable game and joining it quickly) has been replaced with Matchmaking.
There are multiple reasons why Game Session browsers are discouraged:
- there are security concerns;
- retrieving a list of active Game Sessions is a performance-heavy operation for the servers;
- the lists are always specific to the region to which a player is currently connected; and,
- most importantly, it is an antiquated design pattern and better UX options are available today.
The up-to-date alternatives offered by Fusion Matchmaking API are:
- To fill / join a Game Session quickly: Join a random open room.
- To join a certain type of match: Join a random open room while also filtering using the Game Session properties.
- To have specific players join up and play together: Create an invite code and / or join a Game Session by name.
Unless the game has an abundance of community servers running which have their own custom / modded modes and maps, there is little to no reason to request a full list of rooms and make them available to the user through a Game Session browser.
Examples of API usage
Join a Random Session
To join any session, in cases where players just want to quickly join a Game, simply start the NetworkRunner
and let it find an available Game Session
without any extra parameters:
C#
public async Task StartPlayer(NetworkRunner runner) {
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.AutoHostOrClient, // or GameMode.Shared
});
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
This way, the local peer will start and connect to a random Game Session
. If none can be found, it will create a new one with a random Session Name
(because it is using the GameMode.AutoHostOrClient
). This is also valid if starting the NetworkRunner
using the GameMode.Shared
, for sessions in Shared Mode.
Starting a new Game Session with Custom Properties
In this example a Host
will create a Game Session
with some custom properties, so that later, Clients
can filter the Sessions
using those properties.
C#
// Some predefined types used as values for the Game Session Properties
public enum GameType : int {
FreeForAll,
Team,
Timed
}
public enum GameMap : int {
Forest,
City,
Desert
}
// Utility method to start a Host using a defined GameMap and GameType
public async Task StartHost(NetworkRunner runner, GameMap gameMap, GameType gameType) {
var customProps = new Dictionary<string, SessionProperty>();
customProps["map"] = (int)gameMap;
customProps["type"] = (int)gameType;
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Host,
SessionProperties = customProps,
});
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
The sample code shows the use of Enums
for the Custom Properties
values of the Game Session
, but this is just one way to add meaning to the values.
Calling the runner.StartGame
as a Host
(GameMode = GameMode.Host
) is enough to start a new session with a Random Name
(as the SessionName
argument was not passed) and by using the SessionProperties
argument, Fusion will include those properties in the Session
.
Join a Random Session with Filters
Considering the example code above, here is how to start a Client
that will join any Game Session
on any GameMap
, but with a specific GameType
.
The startup code is basically the same, just the GameMode
that is now set to GameMode.Client
, and the customProps
contains only the type
key with the desired gameType
value.
C#
public async Task StartClient(NetworkRunner runner, GameType gameType) {
var customProps = new Dictionary<string, SessionProperty>() {
{ "type", (int)gameType }
};
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Client,
SessionProperties = customProps,
});
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
That is enough to make your client join a random Session
with that specific GameType
.
Join a Session from a Lobby
Another way to find the right Game Session
is to provide a list of Sessions
, allowing the player to choose one to join.
Joining a Lobby
is the way to go in this case, although we highly suggest avoiding this method if it is not strictly necessary.
For most game types, joining a Session
based on property filters is the best way, but Fusion makes Session
listing pretty easy too.
Instead of using the usual flow and starting Fusion as described above, the Session
listing follows a slightly different flow:
- Join a Lobby: using a Fusion Runner reference, just call
NetworkRunner.JoinSessionLobby([SessionLobby], [string])
in order to make the peer connect to the Photon Cloud and join a specificLobby
. This method receives two arguments:SessionLobby
: which can be one of the following values:ClientServer
to join the defaultClientServer Lobby
;Shared
to join the defaultShared Lobby
; and,Custom
, used in conjunction with a customLobbyName
.
LobbyName
: this should be aCustom Lobby Name
used when creating aGame Session
previously.
C#
// Utility method to Join the ClientServer Lobby
public async Task JoinLobby(NetworkRunner runner) {
var result = await runner.JoinSessionLobby(SessionLobby.ClientServer);
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
And as described before, it is possible for a Client
to join the ClientServer
, Shared
or Custom
lobbies this way.
Just as an example, it is shown below how a Server/Host can create a Session
in a custom Lobby
:
C#
public async Task StartHost(NetworkRunner runner) {
var result = await runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Host,
CustomLobbyName = "MyCustomLobby"
});
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
And how a Client
can join that custom Lobby
:
C#
// Utility method to Join a Custom Lobby
public async Task JoinLobby(NetworkRunner runner) {
var result = await runner.JoinSessionLobby(SessionLobby.Custom, "MyCustomLobby");
if (result.Ok) {
// all good
} else {
Debug.LogError($"Failed to Start: {result.ShutdownReason}");
}
}
Get a list of Game Sessions: when working with Fusion, one of the main API entry points is the
INetworkRunnerCallbacks
, a special interface that Fusion uses to surface a series of different events, including the list of sessions from aLobby
. TheOnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList)
callback will be invoked with the complete list of sessions from the Lobby every time the sessions change, either by the creation/removal of sessions or when the properties of a session are updated. TheSessionInfo
is of the same type described above. The list can then be shown, filtered, ordered, etc.Join a Session: with a selected
Session
to join, Fusion can be started using the usualNetworkRunner.StartGame()
, but in this case, theSessionName
used to start the client must be the one from the session. That way, theClient
will join that particularGame Session
.- Select the right
GameMode
as usual, as the peer is joining theSession
it must be eitherGameMode.Client
orGameMode.Shared
modes. - The
SessionName
field must be set toSessionInfo.Name
, as this is the identifier of theGame Session
. - All other parameters are optional and should be initialized accordingly.
- Select the right
C#
public class MyBehaviour : Fusion.Behaviour, INetworkRunnerCallbacks {
// other callbacks...
// Receive the List of Sessions from the current Lobby
public void OnSessionListUpdated(NetworkRunner runner, List<SessionInfo> sessionList) {
Debug.Log($"Session List Updated with {sessionList.Count} session(s)");
// Example
// Join first session from the list
// Check if there are any Sessions to join
if (sessionList.Count > 0) {
// Get first Session from the list
var session = sessionList[0];
Debug.Log($"Joining {session.Name}");
// Join
runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Client, // Client GameMode, could be Shared as well
SessionName = session.Name, // Session to Join
// ...
});
}
// OR
// Example
// Search the list for a Session with a specific Property
// Store the target session
SessionInfo session = null;
foreach (var sessionItem in sessionList) {
// Check for a specific Custom Property
if (sessionItem.Properties.TryGetValue("type", out var propertyType) && propertyType.IsInt) {
var gameType = (int)propertyType.PropertyValue;
// Check for the desired Game Type
if (gameType == 1) {
// Store the session info
session = sessionItem;
break;
}
}
}
// Check if there is any valid session
if (session != null) {
Debug.Log($"Joining {session.Name}");
// Join
runner.StartGame(new StartGameArgs() {
GameMode = GameMode.Client, // Client GameMode, could be Shared as well
SessionName = session.Name, // Session to Join
// ...
});
}
}
}
Setup Dedicated Server Game Mode
Running Photon Fusion in Server Mode is pretty straightforward, but depending on the network characteristics of the hosting machine where the server is running, it might be necessary to set it up differently.
For that reason, this document shows the three main ways to start Fusion with GameMode.Server
.
- Full Automatic: The peer starts and binds to any available port given by the OS to the socket layer. This will work in most scenarios where a pre-defined port is not necessary. The STUN Protocol is used to resolve the Public EndPoint (
IP:Port
) of the peer. This is mostly suited for common networks that do not block ports by default, like domestic internet connections.
C#
NetworkRunner.StartGame(new StartGameArgs() {
GameMode = GameMode.Server
// other args
});
- Pre-defined Local EndPoint & Automatic Public EndPoint: Similar to case 1, but here a particular Local EndPoint is set to be used by the Server. The peer will fail to start if any other service is bound to that same port, so make sure it is available. In the example code, the Server tries to bind to the EndPoint
192.168.0.10:27015
, which must be an IP address associated with one of the host machine's network cards and an available port. If there is no need to specify a local IP but only the port, useNetAddress.Any(27015)
(meaning0.0.0.0:27015
) instead. This is suited for scenarios with a restricted list of available ports that are open for communication on the host's firewall, so it is necessary to use one of these specific ports.
C#
NetworkRunner.StartGame(new StartGameArgs() {
GameMode = GameMode.Server,
Address = NetAddress.CreateFromIpPort("192.168.0.10", 27015) // or NetAddress.Any(27015)
// other args
});
- Pre-defined Local & Public EndPoint: Extending case 2, both the Local Endpoint and Public Endpoint of the peer can be set. Fusion completely bypasses the use of the STUN Protocol for Public EndPoint discovery and just relays the one passed as an argument. In the example, the peer binds to the same Local EndPoint as in case 2, but also uses the specific Public EndPoint
10.0.0.1:27030
. TheCustomPublicAddress
argument is intended for scenarios where the hosting service already provides the mapping between Local and Public EndPoints.
C#
NetworkRunner.StartGame(new StartGameArgs() {
GameMode = GameMode.Server,
Address = NetAddress.CreateFromIpPort("192.168.0.10", 27015),
CustomPublicAddress = NetAddress.CreateFromIpPort("10.0.0.1", 27030)
// other args
});
Back to top