Connection and Regions

Connecting to the Cloud

Connecting establishes the connection to the Photon Cloud and completes once the handshake with the server is done. Optional connect options supply authentication values, a display name or a custom server address. The connection only makes progress while the client is serviced, as described in Client and Service Loop. In C++ and C, keep calling the service function every frame. In JavaScript, the client services itself on a timer by default.

The typical flow waits for the connect result and handles a failure:

C++
C
JavaScript

C++

Task<Result<void, ConnectError>> ConnectWithUserId(RealtimeClient& client)
{
    ConnectOptions options;
    options.Auth.UserId = PHOTON_STR("player-4711");

    Result<void, ConnectError> result = co_await client.Connect(options);
    if (result.IsErr())
    {
        // Inspect result.GetError() and back off or retry.
    }
    co_return result;
}

C

/* Authentication values are plain parameters on the connect call: userId,
   username, authType, authParameters and serverAddress. NULL means default. */
realtime_connect(client,
                 "player-4711", /* userId                             */
                 NULL,          /* username                           */
                 255,           /* authType: CustomAuthenticationType::None */
                 NULL,          /* authParameters                     */
                 NULL);         /* serverAddress: the Photon Cloud    */

/* The outcome arrives as ConnectResult; mode 0 is success. */
if (e->type == RealtimeEventType_ConnectResult && e->mode != 0)
{
    /* Inspect e->mode and back off or retry. */
}

/* There is no ConnectOptions struct over the C API: the fields that have
   an ABI equivalent are parameters of realtime_connect.
   TryUseDatagramEncryption and UseBackgroundSendReceiveThread are not
   among them and keep their defaults. */

JavaScript

import { isRealtimeError } from '@photonengine/realtime-core';

try {
    await client.connect({ auth: { userId: 'player-4711' } });
} catch (error) {
    if (!isRealtimeError(error)) {
        throw error;
    }
    // Inspect error.code and back off or retry.
}

Connecting while the client is already connected or connecting does not restart anything. The call fails immediately with the already-connected code, or with the operation-pending code while a connect is in flight, and the existing connection stays untouched.

Situation C++ C JavaScript
Already connected ConnectError::AlreadyConnected RealtimeConnectError_AlreadyConnected 'alreadyConnected'
Connect in flight ConnectError::OperationPending RealtimeConnectError_OperationPending 'operationPending'

After a successful connect the client is authenticated and ready for matchmaking. The server assigns a random user id when the authentication values do not supply one, and the client automatically joins the default lobby unless auto-join is disabled.

Connect Options

C++ C (realtime_connect() parameter) JavaScript Default Description
Auth userId, authType, authParameters auth empty User id and authentication provider data. See Authentication.
Username username username empty Display name shown to other players, independent of the user id.
ServerAddress serverAddress serverAddress empty Name server address to connect to. Empty targets the Photon Cloud.
TryUseDatagramEncryption none tryUseDatagramEncryption false Encrypts UDP traffic with DTLS when the server supports it. The web build connects over WebSocket, so it has no UDP traffic to encrypt.
UseBackgroundSendReceiveThread none none true Only takes effect on Nintendo Switch 1, where it moves the low-level socket send and receive onto a background thread.

Leave the server address empty to connect to the Photon Cloud. To reach a self-hosted Photon Server, set it to the address and port of your server's name server endpoint.

Connection States

The client reports its current connection state: disconnected, connecting, connected, joining a room, in a room, leaving a room or disconnecting. In C++ the state is a ConnectionState value such as ConnectionState::InRoom, in C a RealtimeConnectionState value such as RealtimeConnectionState_InRoom and in JavaScript a string such as 'inRoom'. For the common checks, the convenience queries answer directly without comparing state values.

Query C++ C JavaScript
Connection state GetState(), a ConnectionState realtime_state(), a RealtimeConnectionState client.state, a ConnectionState
Connected IsConnected() realtime_is_connected() client.isConnected
In a room IsInRoom() realtime_is_in_room() client.isInRoom
In a lobby IsInLobby() realtime_is_in_lobby() client.isInLobby

Disconnecting

Disconnecting shuts the connection down gracefully and completes once the connection is closed. If the client is in a room it leaves the room on the way out, so other players see a regular leave.

Not every disconnect is requested: timeouts, server-side kicks and network failures end the connection asynchronously. Subscribe to the disconnected callback (OnDisconnected in C++, the disconnected event in JavaScript) to react to these. The client also keeps the most recent disconnect cause to inspect after the fact (GetDisconnectCause() in C++, disconnectCause in JavaScript). The Errors and Disconnects page lists every cause.

Reconnecting

Reconnecting is a reconnect-and-rejoin, not a plain connect. It re-establishes the connection using the cached session from the previous connect and rejoins the room the client was last in, restoring the same player slot. A client that was not in a room before the disconnect has no room to return to: its reconnect fails with a connection error, and a plain connect is the way back.

Reconnecting is only valid while the client is disconnected. Calling it in any other state fails immediately with the already-connected code and leaves the existing connection untouched.

The reconnect operation completes when the reconnect handshake completes, and it reports the same error codes as connecting. Restoring the player slot additionally requires that the room was created with a non-zero player time-to-live and that the player is still marked inactive, the same rule that governs rejoining a room by name, described in Rooms and Players.

A typical recovery flow watches for a timeout and starts the reconnect:

C++
C
JavaScript

C++

bool timedOut = false;
auto subscription = client.OnDisconnected.Subscribe([&timedOut](DisconnectCause cause) {
    timedOut = (cause == DisconnectCause::TimeoutDisconnect);
});

// In the game loop:
client.Service();
if (timedOut)
{
    timedOut = false;
    client.Reconnect(); // Fire and forget: Service() keeps driving the reconnect.
}

C

int timedOut = 0;

/* In the game loop, inside the polled batch: `e` is one polled event. */
if (e->type == RealtimeEventType_Disconnected)
{
    /* `origin` is the DisconnectCause, another enum from RealtimeCAPI.h. */
    timedOut = (e->origin == RealtimeDisconnectCause_TimeoutDisconnect);
}

/* After the batch, never from inside it. */
if (timedOut)
{
    timedOut = 0;
    realtime_reconnect(client); /* realtime_service keeps driving it */
}

/* realtime_disconnect_cause returns the same cause after the fact, so a
   handler that only needs the latest cause can read it instead of tracking
   the event. Starting the reconnect after the batch rather than inside it
   keeps the handler out of the pump, as the C API page requires. */

JavaScript

client.on('disconnected', ({ cause }) => {
    if (cause === 'timeoutDisconnect') {
        client.reconnect().catch((error) => {
            console.warn('reconnect failed:', error);
        });
    }
});

Starting the reconnect from inside the handler is safe: the client is already disconnected when the disconnected event fires, and the pump drives the reconnect from there.

Regions

The Photon Cloud is split into geographic regions, each served by its own cluster. Matchmaking is per region: only players connected to the same region can see each other's rooms and meet in matches.

Region Selection Modes

The selection mode is fixed when the client is created, through ClientConstructOptions.RegionSelection in C++ and the regionSelection option of createRealtimeClient() in JavaScript.

Mode C++ JavaScript Behavior
Default RegionSelectionMode::Default 'default' Connects to the first region in the region list.
Select RegionSelectionMode::Select 'select' Lets you choose the region during connect.
Best RegionSelectionMode::Best none Pings the regions during connect and picks the lowest-latency one.

The web build cannot ping regions, so the JavaScript client has no best mode. A client created through the C API always uses the default mode.

Listing Available Regions

Listing the available regions (AvailableRegions() in C++, availableRegions() in JavaScript) returns one entry per region. Each entry carries the region code (such as "eu" or "us") and the server address.

Selecting a Region Manually

Manual selection is a coordinated flow, not a sequential one. In select mode, start listing the regions and connecting together, wait for the region list, select a region and only then wait for the original connect to complete. The two operations resume each other through the name-server handshake, so neither completes without the other.

With the client created in select mode, the whole flow fits in a few lines:

C++
C
JavaScript

C++

Task<Result<void>> ConnectToChosenRegion(RealtimeClient& client)
{
    Task<Result<std::vector<RegionInfo>, RegionError>> regionsTask = client.AvailableRegions();
    Task<Result<void, ConnectError>>                   connectTask = client.Connect();

    Result<std::vector<RegionInfo>, RegionError> regions = co_await regionsTask;
    if (regions.IsErr())
    {
        co_return Result<void>::Err(regions.GetError());
    }

    // Pick the region to use, for example a code saved from a previous session.
    RealtimeCore::Common::StringType chosen = regions.GetValue().front().Code;

    Result<void, RegionError> selected = co_await client.SelectRegion(chosen);
    if (selected.IsErr())
    {
        co_return selected;
    }

    co_return co_await connectTask;
}

Manual region selection is not available over the C API.

RegionSelectionMode is fixed to Default mode and connects to the first region the name server returns.

JavaScript

// The client was created with regionSelection: 'select'.
const regionsPromise = client.availableRegions();
const connected = client.connect();

const regions = await regionsPromise;

// Pick the region to use, for example a code saved from a previous session.
const chosen = regions[0].code;

await client.selectRegion(chosen);
await connected;

Best Region

Best mode pings the available regions during connect and picks the lowest-latency one automatically. It needs no extra calls and no UI, which makes it the right default for most games. The JavaScript client has no best mode; it connects in default mode or lets you choose in select mode.

The client reports the region it actually connected to (GetBestRegion() in C++, bestRegion in JavaScript). It is populated only by a successful connect and is empty before that; listing the available regions alone does not populate it.

Pinging every region takes time, so cache the result to make later connects faster. Persist the region code after the first successful connect, then create future clients in select mode and select the saved code to skip the ping pass.

Last updated on

Back to top