Appearance
Appearance
MetaplayClient
The SDK-provided MetaplayClient
handles most of the details of game connection management, leaving just the high-level session lifecycle management for the game to implement.
The primary interactions between the game and MetaplayClient
are:
MetaplayClient.Initialize
to initialize the client SDK. The game can pass it various options, one of which is a reference to a IMetaplayLifecycleDelegate
.MetaplayClient.Connect
to start connecting to the backend.MetaplayClient.Update
on every frame to run its update loop.MetaplayClient
communicates session lifecycle events (such as session start and end) to the game via the IMetaplayLifecycleDelegate
.In a typical always-online game, the session lifecyle works roughly like this:
MetaplayClient
, and then tells it to start a connection. The game is initially in a loading screen or a similar starting scene. MetaplayClient.Initialize
, you can set MetaplayClientOptions.ConnectionOptions.ConnectionConfig
to configure various time limits and other connection options.MetaplayClient.Update
once per frame.MetaplayClient
informs the game when a about session lifecycle events, and the game reacts appropriately: MetaplayClient
to start a new connection.This is how the Metaplay sample projects like Idler and HelloWorld behave. This behavior is implemented in their ApplicationStateManager.cs
files, which can be used as a reference.
The IMetaplayLifecycleDelegate
methods OnSessionLost
and OnFailedToStartSession
are called on connection failures. They have a ConnectionLostEvent
parameter which describes the connection error.
The game code can use this parameter to:
AutoReconnectRecommended
), or whether to direct the player to download a client update (on ConnectionLostReason.ClientVersionTooOld
).Reason
member is an enum containing the error reason on a granularity appropriate for the player. This should be localized to a simple description - to get started, EnglishLocalizedReason
contains an English description. TechnicalErrorCode
to the player - although it's not directly useful to the player, the player can give the code to customer support and it can be helpful for diagnosing the problem using the List of Connection Error Codes.MetaplayClient
also routes the the error information to the IMetaplayClientAnalyticsDelegate
. See Handling Events in the Client for how to integrate client-side analytics.
During a game session, consider also checking MetaplayClient.ConnectionHealth
and showing an "unhealthy connection" indicator to the player if it returns ConnectionHealth.Unhealthy
. Unhealthy
means the connection is of degraded quality but the SDK will still try to recover it for a short time. The indicator provides an early warning to the player that the connection might be lost soon and the current game actions might not reach the server.
Metaplay handles the Unity application being paused and put to background automatically. If the game spends too long in paused state, the connection is terminated. By default, this time limit is ConnectionConfig.MaxSessionRetainingPauseDuration
.
However, there might be special cases where we expect the application to be paused temporarily, but longer than the Config.MaxSessionRetainingPauseDuration
. Such cases can be for example when application shows an Ad, opens a third-party customer service window, or displays Terms-of-Service or Privacy-policy using the device browser. In these cases, developer should call MetaplaySDK.OnApplicationAboutToBePaused(reason, TimeSpan)
just before the expected pause begins. This hints the MetaplaySDK to extend MaxSessionRetainingPauseDuration
temporarily, and helps the game keep the session alive during the pause. Note that session continuation cannot be guaranteed as background applications may be terminated on mobile devices at any time.
In the case that the same player account connects from a second device while they already had an active session on another device, the old session is forcefully terminated with the code 1500 (force_terminated_received_another_connection
) and the new connection replaces the old one. The client should handle the terminated session with an indication to the player why they were disconnected. Optionally, a reconnect button can be shown to allow easily switching back to the original device.
This table lists describes the meanings of the possible values of ConnectionLostEvent.TechnicalErrorCode
.
Fine-grained errors
In the C# code, if you need fine-grained information about the type of error, please examine ConnectionLostEvent
's TechnicalError
and ConnectionStats
instead of relying on TechnicalErrorCode
. TechnicalErrorCode
(and TechnicalErrorString
) is meant for providing a succinct and opaque code to the player for customer support purposes. Its values are based on SDK implementation details, and might change in SDK updates. Therefore it is safer to use the properly-typed TechnicalError
instead.
TechnicalErrorCode | TechnicalErrorString | Player-Facing Reason | Description |
---|---|---|---|
1000 | closed_session_init_done | ConnectionLost | Connection was lost (during session). |
1001 | closed_handshake_done | CouldNotConnect | Connection was lost (before session had been started). |
1002 | closed_handshake_not_done | CouldNotConnect | Connection was lost (before any packets had been received from the server). |
1003 | closed_probe_failed | NoInternetConnection | Connection was lost (no internet connection). |
1004 | closed_probe_missing | CouldNotConnect | Connection was lost (internet connectivity not known). |
1100 | timeout_connect_session_init_done | CouldNotConnect | Reconnection attempt timed out (during session). |
1101 | timeout_connect_handshake_done | CouldNotConnect | Reconnection attempt timed out (during session). |
1102 | timeout_connect_handshake_not_done | CouldNotConnect | Connection attempt timed out. |
1103 | timeout_connect_probe_failed | NoInternetConnection | Connection attempt timed out (no internet connection). |
1104 | timeout_connect_probe_missing | CouldNotConnect | Connection attempt timed out (internet connectivity not known). |
1112 | timeout_resource_fetch | CouldNotConnect | Resource download timed out. |
1113 | timeout_resource_fetch_probe_failed | NoInternetConnection | Resource download timed out (no internet connection). |
1114 | timeout_resource_fetch_probe_missing | CouldNotConnect | Resource download timed out (internet connectivity not known). |
1120 | timeout_stream_session_init_done | ConnectionLost | Connection timed out (during session). |
1121 | timeout_stream_handshake_done | ConnectionLost | Connection timed out (before session had been started). |
1122 | timeout_stream_handshake_not_done | ConnectionLost | Connection timed out (before any packets had been received from the server). |
1190 | timeout_unhandled | CouldNotConnect | Unknown kind of connection timeout. |
1200 | cluster_starting | ServerMaintenance | Backend is starting up and does not yet accept connections. |
1201 | cluster_shutting_down | ServerMaintenance | Backend is shutting down and does not accept connections anymore. |
1209 | cluster_not_ready_unhandled | ServerMaintenance | Backend is refusing connections for an unknown reason. |
1302 | resource_fetch_failed | CouldNotConnect | Failed to download resources. |
1303 | resource_fetch_failed_probe_failed | NoInternetConnection | Failed to download resources (no internet connection). |
1304 | resource_fetch_failed_probe_missing | CouldNotConnect | Failed to download resources (internet connectivity not known). |
1310 | resource_activation_failed | InternalError | Failed to apply resources. Likely a game config deserialization failure. |
1390 | resource_load_failed_unhandled | InternalError | Unknown kind of resource loading failure. |
1400 | failed_to_resume_session | ConnectionLost | Failed to resume a game session after a disconnect. Likely the session has already expired, or another device connected to the same player account. |
1500 | force_terminated_received_another_connection | ConnectionLost | Another client connected to the same player account. |
1501 | force_terminated_kicked_by_admin_action | ConnectionLost | A game admin performed an action that requires the client to reconnect. |
1502 | force_terminated_internal_server_error | InternalError | An internal server error occurred. |
1503 | force_terminated_unknown | InternalError | An unknown server error occurred. |
1504 | force_terminated_client_time_too_far_behind | ConnectionLost | The client-controlled PlayerModel timeline fell too far behind the real time. |
1505 | force_terminated_client_time_too_far_ahead | InternalError | The client-controlled PlayerModel timeline advanced too far beyond the real time. |
1506 | force_terminated_session_too_long | ConnectionLost | Maximum game session duration was exceeded (configured in server option Session:MaximumSessionLength ). |
1507 | force_terminated_player_banned | PlayerIsBanned | The player got banned during the game session. |
1508 | force_terminated_maintenance_mode_started | ServerMaintenance | Backend maintenance mode mode started during the game session. |
1509 | force_terminated_pause_deadline_exceeded | ConnectionLost | The client was paused for too long. |
1510 | force_terminated_game_config_updated | ServerMaintenance | The game config has been updated. |
1550 | force_terminated_unhandled | InternalError | The server terminated the session for an unknown reason. |
1599 | force_terminated_unhandled_invalid | InternalError | The server terminated the session for an unknown reason. |
1600 | protocol_error_unexpected_login_message | InternalError | The client detected a connection protocol violation by the server. |
1610 | protocol_error_missing_server_hello | InternalError | The client detected a connection protocol violation by the server. |
1620 | protocol_error_session_start_failed | InternalError | The server failed to initialize the session. Internal server error. |
1630 | protocol_error_session_protocol_error | InternalError | The client detected a connection protocol violation by the server. |
1690 | protocol_error_unhandled | InternalError | The client detected a connection protocol violation by the server. |
1700 | session_lost_in_background | ConnectionLost | The connection was lost while the client was paused. |
1800 | transport_watchdog_exceeded_session_init_done | InternalError | A connection watchdog timer was triggered. Internal client error. |
1801 | transport_watchdog_exceeded_handshake_done | InternalError | A connection watchdog timer was triggered. Internal client error. |
1802 | transport_watchdog_exceeded_handshake_not_done | InternalError | A connection watchdog timer was triggered. Internal client error. |
1803 | transport_watchdog_exceeded_probe_failed | InternalError | A connection watchdog timer was triggered. Internal client error. |
1804 | transport_watchdog_exceeded_probe_missing | InternalError | A connection watchdog timer was triggered. Internal client error. |
1810 | resetup_watchdog_exceeded | InternalError | A connection watchdog timer was triggered. Internal client error. |
1890 | connection_watchdog_exceeded_unhandled | InternalError | A connection watchdog timer was triggered. Internal client error. |
2000 | tls_error_unknown | CouldNotConnect | TLS error. |
2001 | tls_error_not_authenticated | CouldNotConnect | TLS error. |
2002 | tls_error_failure_while_authenticating | CouldNotConnect | TLS error. |
2003 | tls_error_not_encrypted | CouldNotConnect | TLS error. |
2009 | tls_error_unhandled | CouldNotConnect | TLS error. |
2100 | wire_protocol_version_client_too_old | ClientVersionTooOld | The client is too old for the server. |
2101 | wire_protocol_version_client_too_new | ServerMaintenance | The client is too new for the server. |
2109 | wire_protocol_version_invalid_mismatch | ServerMaintenance | Unexpected client-server version mismatch. |
2200 | invalid_game_magic | InternalError | The server reported a mismatching "game magic" code. The client likely connected to something that isn't a Metaplay game server, or belongs to a different game. |
2201 | project_name_mismatch | InternalError | The server reported a mismatching "project name". The client likely connected to a server that belongs to a different game. |
2300 | in_maintenance | ServerMaintenance | Backend is in maintenance. |
2400 | logic_version_client_too_old | ClientVersionTooOld | The client is too old for the server. |
2401 | logic_version_client_too_new | ServerMaintenance | The client is too new for the server. |
2402 | logic_version_invalid_mismatch | ServerMaintenance | Unexpected client-server version mismatch. |
2500 | commit_id_mismatch | InternalError | The client and the server have mismatching commit ids. This is only reported if the game has been configured to check for matching commit ids. |
2600 | wire_format_error_session_init_done | InternalError | The client failed to parse a packet that was sent by the server. |
2601 | wire_format_error_handshake_done | InternalError | The client failed to parse a packet that was sent by the server. |
2602 | wire_format_error_handshake_not_done | InternalError | The client failed to parse a packet that was sent by the server. |
2700 | no_network_connectivity | NoInternetConnection | No internet connection. |
2800 | device_storage_write_error | DeviceLocalStorageError | Could not write account credentials to the client device's storage. |
2801 | client_side_connection_error | InternalError | Unexpected client-side connection management error. |
2900 | player_is_banned | PlayerIsBanned | The player is banned. |
3000 | player_deserialization_failure | InternalError | The server-side persisted player state failed to be deserialized. |
3100 | login_protocol_version_client_too_old | ClientVersionTooOld | The client is too old for the server. |
3101 | login_protocol_version_client_too_new | ServerMaintenance | The client is too new for the server. |
3102 | login_protocol_version_invalid_mismatch | ServerMaintenance | Unexpected client-server version mismatch. |
8000 | social_authentication_force_reconnect | ConnectionLost | The client chose to switch to another player account via an external authentication mechanism (e.g. Facebook Login), and needs to reconnect to access the new player account. |
8100 | player_checksum_mismatch | InternalError | A player state mismatch was detected between the client and the server. |
8200 | client_terminated_connection | ConnectionLost | The client terminated the connection due to the usage of a debug/development utility which requires it. |
9000 | unknown | InternalError | Unknown error. |
9100 | unhandled | InternalError | Unknown error. |
MetaplayConnection
Directly INFO
You likely won't need to manage MetaplayConnection
directly if your game uses the SDK-provided MetaplayClient
class, as described in Managing the Session Lifecycle with MetaplayClient
. New games are intended to use MetaplayClient
, and this section is here mostly to support legacy integrations.
The network connection between the client and the Game Backend is managed by the MetaplayConnection
class. The aforementioned class handles credential management, connection lifecycle, reconnection attempts, session config management, timeouts, connection healthiness assessment, and other networking related tasks.
MetaplayConnection
exposes its state as following:
|
| Close()
V
+---------------+
| Not Connected | (initial state)
+-------+-------+
|
| Connect()
|
V
+----------------+
| Connecting |
+-------+----+---+
| |
| '--------.
V |
+---------------+ |
| Connected | |
+-------+-------+ |
| |
<DisconnectedFromServer> | .---------'
| |
V V
+---------------+
| Error |
+---------------+
💡 Pro tip
State transitions might not be observed in graph order as more than one transition might occur in a single update. However, MetaplayConnection.State
will remain constant between the calls to Update()
.
To integrate connection state management, the game will need to:
Connect()
. In always-online games; this happens very early in the app lifecycle, and is normally done in MetaplayClient
.Update()
every frame. Since this call dispatches all incoming messages, and hence potentially changes your game state, you should call it either as early or as late as possible to ensure state changes mid-frame.INFO
In the Example Idler Project, this is handled by setting the calling GameObject's (ApplicationStateManager) Execution Order to be the last in the project. In this project, the state changes, such as a loss of connection, cause a switch of the Unity Scene. As the scene changes happen after the end of the frame, it is natural to apply game state changes also at the end of the frame.
Connected
and Game Logic Models to become available before showing the game view. The Model states are delivered in SessionProtocol.SessionStartSuccess
message which the game must process. Normally this waiting happens during a loading screen.Connected
State if Connected.IsHealthy
is false.Error
states.MetaplayConnection
's Connection Errors A connection might fail or be interrupted for a wide variety of reasons. A user might walk out of WiFi range, or the backend might be running an incompatible version. In the case of lost WiFi, simply reconnecting in a moment will most likely succeed. In the case of an incompatible backend version, recovery is unlikely. We aim to make handling these various cases in your game as simple as possible.
💡 Pro tip
You can check Connected.IsHealthy
for an early warning that connection quality has been degraded and might be lost soon.
To make reacting to these errors easier, Metaplay networking errors are divided into transient and terminal errors:
Hence, a simple implementation would be:
Update()
{
...
if (connectionState is ConnectionStates.TransientError)
{
// Restart the game
ClearGameModels();
Connection.Reconnect();
GoToLoadingScreen();
}
else if (connectionState is ConnectionStates.TerminalError)
{
// Tell user something went wrong
ShowErrorMessage( ... );
}
}
Additionally, a DisconnectedFromServer
pseudo-message is emitted just after leaving the Connected state. It can be used to inspect the error before it is handled. For the duration of the message handler, the MetaplayConnection.State
is set to the error state.
💡 Pro tip
You can fine-tune timeout durations and other settings by modifying ConnectionConfig.Config
in your app start scene.
MetaplayConnection
's Errors Error type | Cause | Resolution |
---|---|---|
TransientError.Closed | Connection was closed unexpectedly. | Reconnect. |
TransientError.Timeout | Connection timed out. | Reconnect. |
TransientError.ClusterNotReady | The backend is currently in temporary state where it does not accept connections. | Reconnect. |
TransientError.ConfigFetchFailed | Config fetch task has thrown an exception. | Inspect exception. Reconnect. |
TransientError.FailedToResumeSession | Connection was lost unexpectedly and the backend rejected our session resume request. | Reconnect. |
TransientError.SessionForceTerminated | Session was terminated by the backend. Commonly caused by the same user connecting from another device. | Inform user. |
TransientError.SessionError | Session signaling was malformed or illegal. | Reconnect. |
TransientError.SessionLostInBackground | Session was lost while the game was in the background and game has just been resumed to the foreground. | Reconnect. |
TransientError.TlsError | Internal transient error in TLS layer. | Reconnect. |
TerminalError.WireFormatError | Packet framing or payload is corrupt or cannot be deserialized. | Cannot recover. Often caused by an incompatible backend version. Prompt user to update the game. |
TerminalError.InvalidGameMagic | The connected peer is not a game backend. | Cannot recover. Backend address is not correct. |
TerminalError.ProjectNameMismatchError | The connected peer is not a game backend. | Cannot recover. Backend address is not correct. |
TerminalError.WireProtocolVersionMismatch | The low-level communication protocol with the backend is incompatible with this client. | Prompt user to update the game. |
TerminalError.InMaintenance | The server is down for maintenance. | Inform user. |
TerminalError.VersionMismatch | Server and Client Logic versions are not compatible. | Prompt user to update the game. |
TerminalError.CommitIdMismatch | Commit Id Check is enabled and server and client are not built from the same commit. | Prompt user to update the game. |
TerminalError.NoNetworkConnectivity | Could not connect to the backend or to a test location on the CDN. | Inform user. |
TerminalError.PlayerIsBanned | Player has been banned. | Inform user. |
TerminalError.Unknown | Any other terminal error. | Inform user. |