Connection and Matchmaking
Overview
Fusion uses the Photon Cloud (or a self-hosted Photon server) for transport.
The connection lifecycle is managed through RealtimeCore::Matchmaking::RealtimeClient, which provides an async Task<Result<T>> API.
The FusionCore::Client coordinates Fusion state on top of this connection.
This page covers the matchmaking basics a Fusion Core integration needs: constructing the two clients, connecting, joining a room and starting Fusion. Deep matchmaking topics — lobbies, random matchmaking, SQL filters, friends, player properties — are covered by the Realtime Core documentation. See Advanced Matchmaking Topics below for direct links.
Two-Layer Construction
Fusion's networking is split into two objects that you construct independently.
The RealtimeClient handles Photon transport, rooms and matchmaking.
The FusionCore::Client handles Fusion state, objects and RPCs.
Step 1: RealtimeClient
C++
RealtimeCore::Matchmaking::ClientConstructOptions options;
options.AppId = PHOTON_STR("your-app-id");
options.AppVersion = PHOTON_STR("1.0");
auto realtimeClient = std::make_unique<RealtimeCore::Matchmaking::RealtimeClient>(options);
AppId and AppVersion are the only required fields.
The full ClientConstructOptions field list is documented in the Realtime API reference, and configuration details in the Realtime Core configuration manual.
Step 2: FusionCore::Client
C++
auto fusionClient = std::make_unique<FusionCore::Client>(*realtimeClient);
The Client constructor takes a reference to the RealtimeClient and internally subscribes to its events via Broadcaster.
No network I/O happens until you connect.
Connecting
All connection and room operations return Task<Result<T>>, a C++20 coroutine type.
Tasks complete asynchronously while the realtime client is serviced — call RealtimeClient::Service() every frame until Fusion is running (see Complete Connection Flow).
Task<T>::Get() does not block; call it only after IsReady() returns true.
See Result and Task for the API and Asynchronous Operations for the full coroutine guide.
C++
RealtimeCore::Matchmaking::ConnectOptions connectOpts;
connectOpts.Auth.UserId = PHOTON_STR("player-123");
connectOpts.Username = PHOTON_STR("PlayerName");
| Field | Type | Default | Purpose |
|---|---|---|---|
Auth |
AuthenticationValues |
-- | User ID and custom authentication data |
Username |
StringType |
-- | Display name |
ServerAddress |
StringType |
-- | Custom server address (self-hosted) |
TryUseDatagramEncryption |
bool |
false |
Encrypt UDP packets |
UseBackgroundSendReceiveThread |
bool |
true |
Background I/O thread |
Custom authentication providers and multi-step auth are covered in Authentication.
Connection Sequence
C++
// 1. Connect
auto connectResult = co_await realtimeClient->Connect(connectOpts);
if (connectResult.IsErr()) { /* handle error */ }
// 2. Optionally select an explicit region (otherwise best region is used)
auto regionResult = co_await realtimeClient->SelectRegion(PHOTON_STR("us"));
if (regionResult.IsErr()) { /* handle error */ }
// 3. Join or create a room
RealtimeCore::Matchmaking::CreateRoomOptions roomOpts;
roomOpts.MaxPlayers = 8;
auto roomResult = co_await realtimeClient->JoinOrCreateRoom(
PHOTON_STR("my-room"), roomOpts);
if (roomResult.IsErr()) { /* handle error */ }
RealtimeCore::Matchmaking::MutableRoomView room = std::move(roomResult).GetValue();
C++
// Task<T> has no default constructor -- create it where you start the operation.
auto task = realtimeClient->Connect(connectOpts);
// In the frame loop:
realtimeClient->Service(true);
if (task.IsReady()) {
auto result = task.Get();
// proceed...
}
Region selection modes and region listings are covered in Connection and Regions.
Joining a Room
The core room operations return Task<Result<MutableRoomView>> (LeaveRoom returns Task<Result<void>>) and require IsConnected() == true.
| Method | Behavior |
|---|---|
CreateRoom(name, createOptions) |
Create a new room. Fails if the name exists. |
JoinRoom(name, joinOptions) |
Join an existing room by name. |
JoinOrCreateRoom(name, createOptions, joinOptions) |
Join if it exists, otherwise create. Most common. |
LeaveRoom(willComeBack) |
Leave the current room. |
GetCurrentRoom() |
std::optional<MutableRoomView> for the joined room. |
For a Fusion session, MaxPlayers is usually the only option you need:
C++
RealtimeCore::Matchmaking::CreateRoomOptions roomOpts;
roomOpts.MaxPlayers = 8;
auto roomResult = co_await realtimeClient->JoinOrCreateRoom(PHOTON_STR("my-room"), roomOpts);
The Fusion server plugin is registered on the server under the name Fusion3.Plugin and is bound to rooms by the server's plugin configuration.
You do not set CreateRoomOptions::Plugins in a standard Fusion deployment.
Set it only when your server setup requires explicit plugin selection.
The full CreateRoomOptions field list lives in the Realtime API reference.
Random matchmaking (JoinRandomRoom, JoinRandomOrCreateRoom, MatchmakingOptions), room listings and lobby selection are covered in Lobbies and Matchmaking and SQL Lobby Matchmaking.
Rejoining with JoinRoomOptions::Rejoin and player TTLs are covered in Rooms and Players.
Room Properties
Once in a room, MutableRoomView reads and writes room state:
C++
auto room = realtimeClient->GetCurrentRoom();
if (room) {
auto name = room->GetName();
int count = room->GetPlayerCount();
// Write one custom property and read it back later via GetCustomProperties()
room->SetProperty(PHOTON_STR("map"),
RealtimeCore::Common::StringType{PHOTON_STR("arena")});
}
The property keys fusion_config, fusion_map_data and fusion_sdk_version are reserved by the Fusion plugin — see Starting Fusion below.
The full MutableRoomView surface and custom-property semantics are covered in Rooms and Players and Custom Properties.
Connection State
C++
enum class ConnectionState : uint8_t {
Disconnected,
Connecting,
Connected,
JoiningRoom,
InRoom,
LeavingRoom,
Disconnecting
};
State Queries
C++
ConnectionState realtimeClient->GetState();
bool realtimeClient->IsConnected();
bool realtimeClient->IsInRoom();
bool realtimeClient->IsInLobby();
DisconnectCause realtimeClient->GetDisconnectCause();
The DisconnectCause values and error handling patterns are documented in Errors and Disconnects.
Key Callbacks
The RealtimeClient exposes broadcasters for connection lifecycle events.
Broadcaster::Subscribe() returns a Subscription token; store it in a SubscriptionBag (or ScopedSubscription) so it unsubscribes automatically on destruction:
C++
RealtimeCore::Common::SubscriptionBag subs;
subs += realtimeClient->OnDisconnected.Subscribe([](DisconnectCause cause) {
// Handle disconnect
});
subs += realtimeClient->OnRoomJoined.Subscribe([]() {
// Room entered
});
subs += realtimeClient->OnPlayerJoined.Subscribe([](const PlayerView& player) {
// Handle player join
});
The broadcasters most Fusion integrations need:
| Broadcaster | Signature |
|---|---|
OnDisconnected |
void(DisconnectCause) |
OnError |
void(ErrorCode, StringViewType) |
OnRoomJoined |
void() |
OnRoomLeft |
void() |
OnPlayerJoined |
void(const PlayerView&) |
OnPlayerLeft |
void(int playerNumber, bool isInactive) |
OnMasterClientChanged |
void(int newId, int oldId) |
The complete broadcaster list is in the Realtime API reference; subscription mechanics are covered in Callbacks and Subscriptions.
Starting Fusion
After joining a room, call Client::Start() to initialize Fusion state replication:
C++
if (!fusionClient->Start()) {
// Precondition failure -- see the error log for the cause.
}
Start() returns bool.
It reads the fusion_config and fusion_map_data room properties from the current room, applies the configuration and the initial map state, fires OnFusionStart, and then sends the version handshake to the server plugin.
It returns false when no room is joined or when the required room properties are missing or malformed — which typically means the room was created without the Fusion plugin bound.
Fusion Room Properties
The Fusion plugin injects its reserved room properties when the room is created:
| Property | Content |
|---|---|
fusion_config |
JSON session configuration, merged from the plugin defaults |
fusion_map_data |
Base64 initial map state in FusionMapStateBuilder format |
fusion_sdk_version |
Optional 20-byte packed SDK version |
fusion_config selects the simulation mode via its "SimulationMode" field ("Shared" or "Authority") and sets "ClientSendRate" and "AuthoritySendRate" (default 30).
Query the active mode at any time with fusionClient->GetSimulationMode() — see Modes and Topologies.
A room creator can override configuration defaults by seeding fusion_config through CreateRoomOptions::CustomProperties.
The plugin merges the override on top of its defaults at room creation, gated by the server's AllowConfigOverride setting:
C++
RealtimeCore::Matchmaking::CreateRoomOptions roomOpts;
roomOpts.MaxPlayers = 8;
roomOpts.CustomProperties = {
{ RealtimeCore::Common::StringType{FusionCore::FusionRoomProperties::CONFIG},
RealtimeCore::Common::StringType{PHOTON_STR(R"({"SimulationMode":"Authority"})")} }
};
The helper namespaces FusionRoomProperties, FusionConfigDefaults, FusionMapStateBuilder and FusionSdkVersionBuilder (header FusionRoomProperties.h) are documented in the Types API reference.
OnFusionStart vs OnRoomJoined
OnRoomJoined(onRealtimeClient) fires when the Photon room is entered.OnFusionStart(onClient) fires synchronously inside a successfulStart()call, after the configuration and initial map state are applied and before the version handshake is sent.
Subscribe before calling Start(), and wait for OnFusionStart before creating objects or sending RPCs:
C++
subs += fusionClient->OnFusionStart.Subscribe([&]() {
// Fusion is ready -- create objects, load scene, etc.
});
OnForcedDisconnect
C++
fusionClient->OnForcedDisconnect.Subscribe(
[](RealtimeCore::Common::StringType message, FusionCore::ForcedDisconnectReason reason) {
// Server forced us out
});
Fires when the server plugin forcibly disconnects the client (e.g., version mismatch, ban).
The reason is a ForcedDisconnectReason (declared in ForcedDisconnect.h) — see the Types API reference.
Fusion-Level Queries
Once Fusion is running:
C++
bool fusionClient->IsRunning(); // Connection active AND config applied
bool fusionClient->HasRealtimeClient(); // RealtimeClient reference still valid
bool fusionClient->IsMasterClient(); // Local client is room master
PlayerId fusionClient->LocalPlayerId(); // Local player ID
int32_t fusionClient->PlayerCount(); // Number of players
double fusionClient->GetRtt(); // Notify-layer round-trip time (seconds, 0 before Start)
FusionCore::Client::GetSdkVersion(); // static -- SDK version info
Shutdown
C++
fusionClient->Shutdown();
Shutdown() performs the full Fusion teardown: it clears prediction and input state, releases deferred events and closes the Notify connection.
Stop() only closes the Notify connection; use it when Fusion may be restarted in the same room.
After Shutdown(), destroy the Client instance before the RealtimeClient.
To disconnect from Photon cleanly, also call:
C++
co_await realtimeClient->Disconnect();
Complete Connection Flow
C++
// 1. Construct
auto realtimeClient = std::make_unique<RealtimeCore::Matchmaking::RealtimeClient>(constructOpts);
auto fusionClient = std::make_unique<FusionCore::Client>(*realtimeClient);
// 2. Subscribe to Fusion events
RealtimeCore::Common::SubscriptionBag subs;
subs += fusionClient->OnFusionStart.Subscribe([&]() {
// Ready to create objects
create_scene_objects();
});
subs += fusionClient->OnObjectReady.Subscribe([](FusionCore::ObjectRoot* obj) {
// Remote object ready
});
// 3. Connect + join room (coroutine or polling)
co_await realtimeClient->Connect(connectOpts);
co_await realtimeClient->JoinOrCreateRoom(PHOTON_STR("my-room"), roomOpts);
// 4. Start Fusion
if (!fusionClient->Start()) { /* room lacks the Fusion plugin -- abort */ }
// 5. Frame loop
while (running) {
if (fusionClient->IsRunning()) {
// UpdateFrameBegin services the realtime client internally.
fusionClient->UpdateFrameBegin(delta);
sync_inbound();
// ... game simulation ...
sync_outbound();
fusionClient->UpdateFrameEnd();
} else {
// Not in a room yet -- pump the connection manually.
realtimeClient->Service(true);
}
}
// 6. Shutdown
fusionClient->Shutdown();
co_await realtimeClient->Disconnect();
See Frame Loop for the frame sequence details.
Advanced Matchmaking Topics
Fusion Core shares its matchmaking layer with Realtime Core. The complete matchmaking documentation lives there:
- Connection and Regions -- region selection, reconnecting
- Rooms and Players -- full room options, players, master client, rejoin
- Lobbies and Matchmaking -- lobbies, room lists, random matchmaking
- SQL Lobby Matchmaking -- filter-based matchmaking
- Custom Properties -- room and player properties in depth
- Authentication -- custom auth providers
- Errors and Disconnects -- DisconnectCause, ErrorCode, recovery
- Asynchronous Operations -- Task, Result, coroutines
- Callbacks and Subscriptions -- Broadcaster and Subscription mechanics
- Friends, Statistics, Configuration
Related
- Frame Loop -- The frame sequence
- Architecture -- Layered design and namespaces
- Objects -- Object creation after OnFusionStart
- Realtime API -- RealtimeClient reference