Connection

Overview

Fusion uses the Photon Cloud (or a self-hosted Photon server) for transport.
The connection lifecycle is managed through PhotonMatchmaking::RealtimeClient, which provides an async Task<Result<T>> API.
The SharedMode::Client coordinates Fusion state on top of this connection.

Two-Layer Construction

Fusion's networking is split into two objects that you construct independently.
The RealtimeClient handles Photon transport, rooms and matchmaking.
The SharedMode::Client handles Fusion state, objects and RPCs.

Step 1: RealtimeClient

C++

PhotonMatchmaking::ClientConstructOptions options;
options.appId      = PHOTON_STR("your-app-id");
options.appVersion = PHOTON_STR("1.0");

auto realtimeClient = std::make_unique<PhotonMatchmaking::RealtimeClient>(options);

ClientConstructOptions

Field Type Default Purpose
appId StringType -- Photon application ID from the Dashboard
appVersion StringType -- Client version for room isolation
protocol ConnectionProtocol Default UDP or TCP
useAlternativePorts bool false Use alternative port range
regionSelectionMode RegionSelectionMode Default How region is chosen
autoLobbyStats bool false Auto-request lobby statistics
serialization SerializationProtocol Protocol1_8 Wire format version
disconnectTimeoutMs optional<int> -- Override disconnect timeout
pingIntervalMs optional<int> -- Override ping interval
enableCrc optional<bool> -- CRC checking
sentCountAllowance optional<int> -- Outgoing command buffer
quickResendAttempts optional<uint8_t> -- Fast retransmit count
limitOfUnreliableCommands optional<int> -- Unreliable queue cap

Step 2: SharedMode::Client

C++

auto fusionClient = std::make_unique<SharedMode::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

Async Flow with Task<Result<T>>

All connection and room operations return Task<Result<T>>, which is a C++20 coroutine type.
Tasks complete asynchronously as RealtimeClient::Service() is called each frame.

ConnectOptions

C++

PhotonMatchmaking::ConnectOptions connectOpts;
connectOpts.auth.userId = PHOTON_STR("player-123");
connectOpts.username    = PHOTON_STR("PlayerName");
connectOpts.serverType  = PhotonMatchmaking::ServerType::NameServer;
Field Type Default Purpose
auth AuthenticationValues -- User ID, custom auth type, parameters, data
username StringType -- Display name
serverType ServerType NameServer Which server to connect to
serverAddress StringType -- Custom server address
tryUseDatagramEncryption bool false Encrypt UDP packets
useBackgroundSendReceiveThread bool true Background I/O thread

AuthenticationValues

C++

struct AuthenticationValues {
    PhotonCommon::StringType userId;
    CustomAuthenticationType type;  // None, Custom, Steam, Facebook, etc.
    PhotonCommon::StringType parameters;
    std::variant<std::monostate, std::vector<uint8_t>, StringType, PropertyMap> data;
};

Connection Sequence

  • Coroutine
  • Polling

C++

// 1. Connect to name server
auto connectResult = co_await realtimeClient->Connect(connectOpts);
if (connectResult.IsErr()) { /* handle error */ }

// 2. Select region (or use best region)
auto regionResult = co_await realtimeClient->SelectRegion(PHOTON_STR("us"));
if (regionResult.IsErr()) { /* handle error */ }

// 3. Join or create room
PhotonMatchmaking::CreateRoomOptions roomOpts;
roomOpts.maxPlayers = 8;
roomOpts.plugins    = { PHOTON_STR("Fusion3Plugin") };

auto roomResult = co_await realtimeClient->JoinOrCreateRoom(
    PHOTON_STR("my-room"), roomOpts);
if (roomResult.IsErr()) { /* handle error */ }

PhotonMatchmaking::MutableRoomView room = std::move(roomResult).GetValue();

C++

auto task = realtimeClient->Connect(connectOpts);

// In frame loop:
realtimeClient->Service(true);
if (task.IsReady()) {
    auto result = task.Get();
    // proceed...
}

Room Operations

All room operations require IsConnected() == true and return Task<Result<MutableRoomView>> (except LeaveRoom which returns Task<Result<void>>).

Method Behavior
CreateRoom(name, createOpts) Create a new room. Fails if name exists.
JoinRoom(name, joinOpts) Join existing room by name.
JoinOrCreateRoom(name, createOpts, joinOpts) Join if exists, otherwise create. Most common.
JoinRandomRoom(matchmakingOpts) Join any available room matching filters.
JoinRandomOrCreateRoom(createOpts, matchmakingOpts) Random join with create fallback.
LeaveRoom(willComeBack, sendAuthCookie) Leave current room.

CreateRoomOptions

Field Type Default
isVisible bool true
isOpen bool true
maxPlayers uint8_t 0 (unlimited)
customProperties PropertyMap {}
lobbyProperties vector<StringType> {}
playerTtlMs int 0
emptyRoomTtlMs int 0
suppressRoomEvents bool false
publishUserId bool false
plugins vector<StringType> {}
expectedUsers vector<StringType> {}

JoinRoomOptions

Field Type Default
rejoin bool false
cacheSliceIndex int 0
expectedUsers vector<StringType> {}

MatchmakingOptions

Field Type Default
filter PropertyMap {}
maxPlayers uint8_t 0
mode MatchmakingMode FillRoom
sqlFilter StringType {}
expectedUsers vector<StringType> {}

MutableRoomView

Once in a room, MutableRoomView provides read and write access to room state:

C++

auto room = realtimeClient->GetCurrentRoom();
if (room) {
    auto name = room->GetName();
    int count = room->GetPlayerCount();
    auto& players = room->GetPlayers();
    int master = room->GetMasterClientId();

    // Mutations
    room->SetOpen(false);
    room->SetMaxPlayers(4);
    room->SetProperties({{ PHOTON_STR("map"), PropertyValue(PHOTON_STR("arena")) }});
}

ConnectionState

C++

enum class ConnectionState : uint8_t {
    Disconnected,
    Connecting,
    Connected,
    JoiningRoom,
    InRoom,
    LeavingRoom,
    Disconnecting
};
From To Trigger
Disconnected Connecting Connect() called
Connecting Connected Connection established
Connecting Disconnected Connection error
Connected JoiningRoom Join/Create room called
JoiningRoom InRoom Room entered
JoiningRoom Disconnected Join error
InRoom LeavingRoom LeaveRoom() called
InRoom Disconnecting Timeout or error
LeavingRoom Disconnected Leave complete
Disconnecting Disconnected Disconnect complete

State Queries

C++

ConnectionState realtimeClient->GetState();
bool realtimeClient->IsConnected();
bool realtimeClient->IsInRoom();
bool realtimeClient->IsInLobby();
DisconnectCause realtimeClient->GetDisconnectCause();

DisconnectCause

C++

enum class DisconnectCause : int {
    None = 0,
    DisconnectByServerUserLimit = 1,
    ExceptionOnConnect = 2,
    DisconnectByServer = 3,
    DisconnectByServerLogic = 4,
    TimeoutDisconnect = 5,
    Exception = 6,
    InvalidAuthentication = 7,
    MaxCCUReached = 8,
    InvalidRegion = 9,
    OperationNotAllowedInCurrentState = 10,
    CustomAuthenticationFailed = 11,
    ClientVersionTooOld = 12,
    ClientVersionInvalid = 13,
    DashboardVersionInvalid = 14,
    AuthenticationTicketExpired = 15,
    DisconnectByOperationLimit = 16
};

RealtimeClient Broadcasters

The RealtimeClient exposes broadcasters for connection lifecycle events.
Subscribe using Broadcaster::Subscribe() and manage lifetime with SubscriptionBag:

C++

PhotonCommon::SubscriptionBag subs;

subs += realtimeClient->OnDisconnected.Subscribe([](DisconnectCause cause) {
    // Handle disconnect
});

subs += realtimeClient->OnPlayerJoined.Subscribe([](const PlayerView& player) {
    // Handle player join
});

subs += realtimeClient->OnPlayerLeft.Subscribe([](int playerNumber, bool isInactive) {
    // Handle player leave
});

subs += realtimeClient->OnMasterClientChanged.Subscribe([](int newId, int oldId) {
    // Handle master migration
});

subs += realtimeClient->OnError.Subscribe([](ErrorCode code, StringViewType msg) {
    // Handle error
});
Broadcaster Signature
OnDisconnected void(DisconnectCause)
OnError void(ErrorCode, StringViewType)
OnRoomPropertiesChanged void(const PropertyMap&)
OnRoomListUpdated void(const vector<RoomListing>&)
OnMasterClientChanged void(int newId, int oldId)
OnPlayerJoined void(const PlayerView&)
OnPlayerLeft void(int playerNumber, bool isInactive)
OnPlayerPropertiesChanged void(int playerNumber, const PropertyMap&)
OnEvent void(uint8_t eventCode, int senderId, span<const uint8_t> data)
OnDirectMessage void(int senderId, span<const uint8_t> data, bool isRelay)
OnRoomJoined void()
OnRoomLeft void()
OnWarning void(int warningCode)

Starting Fusion

After connecting and joining a room, call Client::Start() to initialize Fusion state replication:

C++

fusionClient->Start();

Start() creates the internal Notify::Connection and begins the Fusion protocol handshake with the server plugin.
The OnFusionStart broadcaster fires when Fusion is ready:

C++

subs += fusionClient->OnFusionStart.Subscribe([&]() {
    // Fusion is ready -- create objects, load scene, etc.
});

OnFusionStart vs OnRoomJoined

  • OnRoomJoined (on RealtimeClient) fires when the Photon room is entered.
  • OnFusionStart (on Client) fires when the Fusion protocol handshake completes and the server plugin config is received.

You must wait for OnFusionStart before creating objects or sending RPCs.

OnForcedDisconnect

C++

fusionClient->OnForcedDisconnect.Subscribe([](std::string message) {
    // Server forced us out
});

Fires when the server plugin forcibly disconnects the client (e.g., version mismatch, ban).

Fusion-Level Queries

Once Fusion is running:

C++

bool fusionClient->IsRunning();          // Has config AND connection active
bool fusionClient->IsMasterClient();     // Local client is room master
PlayerId fusionClient->LocalPlayerId();  // Local player ID
int32_t fusionClient->PlayerCount();     // Number of players
double fusionClient->GetRtt();           // Round-trip time (seconds)
SdkVersion fusionClient->GetSdkVersion(); // SDK version info

Shutdown

C++

fusionClient->Shutdown();

Full teardown.
Destroys all Fusion objects, closes the Notify connection and releases internal resources.
After Shutdown(), the Client instance should be destroyed.
To disconnect from Photon cleanly, also call:

C++

co_await realtimeClient->Disconnect();

Complete Connection Flow

C++

// 1. Construct
auto realtimeClient = std::make_unique<PhotonMatchmaking::RealtimeClient>(constructOpts);
auto fusionClient   = std::make_unique<SharedMode::Client>(*realtimeClient);

// 2. Subscribe to Fusion events
PhotonCommon::SubscriptionBag subs;
subs += fusionClient->OnFusionStart.Subscribe([&]() {
    // Ready to create objects
    create_scene_objects();
});
subs += fusionClient->OnObjectReady.Subscribe([](SharedMode::ObjectRoot* obj) {
    // Remote object ready
});

// 3. Connect + join room (coroutine or polling)
co_await realtimeClient->Connect(connectOpts);
co_await realtimeClient->SelectRegion(PHOTON_STR("us"));
co_await realtimeClient->JoinOrCreateRoom(PHOTON_STR("my-room"), roomOpts);

// 4. Start Fusion
fusionClient->Start();

// 5. Frame loop
while (running) {
    realtimeClient->Service(true);

    if (fusionClient->IsRunning()) {
        sync_outbound();
        fusionClient->UpdateFrameEnd();
        fusionClient->UpdateFrameBegin(delta);
        sync_inbound();
    }
}

// 6. Shutdown
fusionClient->Shutdown();
co_await realtimeClient->Disconnect();
Back to top