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

Coroutine
Polling

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 (on RealtimeClient) fires when the Photon room is entered.
  • OnFusionStart (on Client) fires synchronously inside a successful Start() 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:

Back to top