Connection
App ID and Fusion Settings
Project-wide Fusion configuration can be found in Project Settings > Fusion Settings in the Unreal editor. The most important fields are AppId and AppVersion. The App ID identifies the project on the Photon Cloud and AppVersion segments players by client build so incompatible versions cannot share rooms.
Get an App ID for the project from the Photon Dashboard and paste it into the App ID field as shown in the Quick Start Guide.
Connecting to Photon
Every connection and room operation goes through UFusionOnlineSubsystem, a UGameInstanceSubsystem reached the standard Unreal way: GameInstance->GetSubsystem<UFusionOnlineSubsystem>(). The subsystem owns the underlying UFusionClient, the PhotonMatchmaking::RealtimeClient, and the lifecycle of the Fusion session.
In Blueprints, the entry point is the latent node "Connect To Photon", which maps to ConnectToPhoton(FFusionConnectOptions, WorldContextObject) and returns a UFusionConnectToPhotonAsync with OnSuccess and OnFailure multicast pins. That node is a convenience wrapper built for Blueprint use; under the hood it drives PhotonMatchmaking::RealtimeClient::Connect. From C++ you call that matchmaking API directly and skip the latent node entirely.
C++
// Create a standalone matchmaking client. AppId / AppVersion / region mode come
// from Fusion Settings. It is NOT on the subsystem yet.
FFusionConnectOptions ConnectOpts;
ConnectOpts.RegionSelectionMode = EFusionRegionSelectionMode::Best;
UFusionRealtimeClient* RealtimeClient = UFusionRealtimeClient::CreateRealtimeClient(ConnectOpts);
PhotonMatchmaking::RealtimeClient* Client = RealtimeClient->GetClient();
PhotonMatchmaking::ConnectOptions RtOpts;
RtOpts.auth.userId = reinterpret_cast<const PhotonCommon::CharType*>(
StringCast<UTF8CHAR>(*FGuid::NewGuid().ToString()).Get());
// Leave RtOpts.serverAddress empty for the Photon Cloud NameServer.
Client->Connect(RtOpts); // returns a Task; you can discard it (see below)
// Pump the client every tick until it reports connected — nothing else drives
// the connection while the client is standalone:
// RealtimeClient->Service();
// if (RealtimeClient->IsConnected()) { /* connected */ }
The one hard requirement is that something calls Service() on the client every tick, that is what advances the connection and dispatches incoming events. While the client is standalone you pump it yourself, once you hand it to the subsystem UFusionOnlineSubsystem::WorldTick does it for you.
For Blueprint projects, wire the "Connect To Photon" latent node's OnSuccess pin into "Join Or Create Room" with a Make FFusionRoomOptions literal feeding the room options.
Joining or Creating a Room
Once connected, the subsystem exposes five room entry points. CreateRoom, JoinOrCreateRoom, and JoinOrCreateRandomRoom all take an FFusionRoomOptions struct describing the room to create. JoinRoom(RoomName) and JoinRandomRoom join an existing room without creating one, the by-name variant requires the exact room name, the random variant picks any open and visible room.
C++
PhotonMatchmaking::CreateRoomOptions RoomOpts;
RoomOpts.maxPlayers = 8;
const auto RoomName = StringCast<UTF8CHAR>(TEXT("Match-42"));
Client->JoinOrCreateRoom(
reinterpret_cast<const PhotonCommon::CharType*>(RoomName.Get()), RoomOpts);
// Keep calling RealtimeClient->Service() each tick. Once the client is in the
// room (RealtimeClient->IsInRoom(), or the OnRoomJoined broadcaster fires),
// hand it to the subsystem and start the Fusion session. The subsystem now
// owns the client, so WorldTick takes over servicing it.
OnlineSubsystem->RealtimeClient = RealtimeClient;
OnlineSubsystem->StartFusionSession(); // constructs the UFusionClient, begins replication
If you would rather not call Service() yourself, the inverse also works: assign the client to OnlineSubsystem->RealtimeClient up front so WorldTick services it from the start, and simply defer StartFusionSession() until the room is joined.
The room entry points map one-to-one onto RealtimeClient methods: JoinOrCreateRoom, JoinRandomOrCreateRoom, CreateRoom, JoinRoom(name), and JoinRandomRoom. The FFusionRoomOptions struct used by the Blueprint nodes is just a wrapper, FFusionRoomOptions::ToCreateRoomOptions() converts it to the PhotonMatchmaking::CreateRoomOptions these methods take, so C++ code can build either one.
For Blueprints, the convenience node "Connect And Join Room" (ConnectAndJoinRoom) chains connect, join, and Fusion start in a single action. The C++ equivalent is the two snippets above: connect (servicing the client), join, then assign the in-room client to the subsystem and call StartFusionSession().
Async Actions
Every connect and room call returns a UFusionRoomActionBase subclass, a Blueprint latent action that exposes OnSuccess and OnFailure multicast delegates as exec pins in BP and as FMulticastDynamicDelegate members in C++. OnFailure carries an EFusionActionFailureCodes value describing the reason. These actions exist for Blueprint use, C++ code that wants to avoid them uses the PhotonMatchmaking::RealtimeClient directly, as shown in Connecting to Photon above.
| Async class | Subsystem method | Wraps |
|---|---|---|
UFusionConnectToPhotonAsync |
ConnectToPhoton |
Initial cloud connection |
UFusionDisconnectFromPhotonAsync |
DisconnectFromPhoton |
Full disconnect from the cloud |
UFusionJoinOrCreateRoomAsync |
JoinOrCreateRoom, JoinOrCreateRandomRoom |
Join a matching room or create one |
UFusionCreateRoomAsync |
CreateRoom |
Create a new room unconditionally |
UFusionJoinRoomAsync |
JoinRoom, JoinRandomRoom |
Join an existing room |
UFusionLeaveRoomAsync |
LeaveRoom |
Leave the current room, stay connected |
UFusionConnectAndJoinRoomAsync |
ConnectAndJoinRoom |
Connect plus join in one action |
EFusionActionFailureCodes covers the practical failure cases: TimeOut, InvalidRegion, ClientAlreadyExists, Disconnected, and the already-in-that-state codes Connecting, JoiningRoom, and InRoom which fire when the requested action is redundant because the client has already reached that state (for example, calling connect while already connected, or join/create while already in a room).
Connection Options and Regions
FFusionConnectOptions carries two fields. Region is a region code string such as "us" or "eu", see Regions for the full list. RegionSelectionMode is an EFusionRegionSelectionMode enum that decides how the Region field is interpreted.
| Mode | Behavior |
|---|---|
Default |
Use the first available region. The Region string is ignored. |
Select |
Connect to the region named in the Region string. |
Best |
Ping every available region, pick the lowest-RTT one, and cache the result. |
C++
FFusionConnectOptions ConnectOpts;
ConnectOpts.Region = TEXT("us");
ConnectOpts.RegionSelectionMode = EFusionRegionSelectionMode::Select;
For production, ship with Best on first launch so the player lands on the lowest-RTT region without any UI. Persist the chosen region and switch to Select with that value on subsequent launches to skip the ping pass.
Room Options
FFusionRoomOptions describes the room being created or joined.
| Field | Type | Effect |
|---|---|---|
RoomName |
FString |
Unique name within the App ID. Required for invite-style flows; auto-generated when joining randomly. |
MaxPlayers |
uint8 |
Maximum concurrent players in the room. |
bIsOpen |
bool |
If false, the room rejects new joins. Set false to seal a match in progress. |
bIsVisible |
bool |
If false, the room does not appear in random-join results. Invite-only rooms set this false. |
EmptyTTL |
uint8 (seconds) |
How long the room survives after the last player leaves. Converted to ms internally. |
InitialWorld |
TSoftObjectPtr<UWorld> |
World to load when the local player joins. Skips a separate ChangeWorld call on the Master Client. |
C++
FFusionRoomOptions RoomOpts;
RoomOpts.RoomName = TEXT("Match-42");
RoomOpts.MaxPlayers = 8;
RoomOpts.EmptyTTL = 0;
RoomOpts.InitialWorld = LobbyWorldPtr;
Set InitialWorld whenever the room creator already knows the starting map. The local client loads that world automatically as it joins, and the Master Client does not need to issue a separate ChangeWorld call after OnSuccess.
Leaving the Room and Reconnecting
LeaveRoom(WorldContextObject) returns a UFusionLeaveRoomAsync and after OnSuccess the client is back in EFusionStatus::Connected. It can call JoinOrCreateRoom again without re-running ConnectToPhoton. For a full teardown of the Photon connection, use DisconnectFromPhoton, which returns the client to EFusionStatus::None.
Connection Status and Events
Status() returns the current EFusionStatus. The normal lifecycle traces None → Connecting → Connected → JoiningRoom → InRoom → LeavingRoom, with Disconnected and Error reachable from any non-terminal state. The transitions are introduced in Concepts.
| Query | Returns |
|---|---|
IsConnected() |
True when the client has reached at least Connected. |
IsInRoom() |
True when the client is fully in a room (InRoom). |
IsJoiningOrInRoom() |
True for both JoiningRoom and InRoom — useful for "session is forming or formed" gates. |
IsMasterClient() |
True when this client is the elected Master Client of the current room. |
PlayerCount() |
Current number of players in the room. |
GetLocalPlayerId() |
Stable int32 id of the local player for this session. |
GetRtt() |
Round-trip time to the Photon relay, in milliseconds. |
CurrentRoomInfo(Name, Players) |
Room name and current player count as out params. |