Host Migration API
Overview
Photon Fusion supports several architectures; one of which is the Client Hosted topology - also called HostMode
in Fusion. HostMode
means one of the clients in the game session is also acting as the game's Host. A Client Host will create and serve a new Game Session from its local computer and let others connect to play together using its machine as the Game Session's Server.
The main advantage is also its main drawback, namely the lack of control over the infrastructure involved. A Game Session may fluctuate in quality or stop if:
- The quality of the traffic over a residential network fluctuates - this depends on the Game Host's internet connection / subscription; and,
- The host machine can goes due to a loss of power and because it was turned off (system updates or manually by the player).
Both of these points may result in a bad player experience; especially if the Game Session is just terminated as a Fusion game in HostMode
can only run in the presence of a Game Server.
To solve this problem, Fusion implements a Host Migration API / System which developers can integrate. Leveraging the Host Migration feature in Fusion will allow another Client-peer in the Game Session to take over as the game's Host and continue to serve the game session. This is great for the overall game experience and is a solid alternative for the infrastructure required to run headless instances in ServerMode
on dedicated servers.
NOTE: An example implementation can be found in the Fusion Host Migration Sample.
Host Migration
In order to perform Host Migration, the project must meet a few pre-requisites:
- Client-Host game: when starting Fusion, one of the peers must start the simulation as the host (server + player on the same machine). This can be done by starting the Fusion Simulation (
NetworkRunner.StartGame()
) using either theGameMode.Host
orGameMode.AutoHostOrClient
parameter. - Enable Host Migration: as this is an optional feature and requires some manual work to implement, it is not enabled by default.
- Prepare the game for migration: migrating a Host to another machine is not a trivial task, but Fusion makes it as simple as possible. The system gives access to whole game state from the old Host and does not enforce what goes in or out the migration process.
Enabling Host Migration
In order to enable Host Migration, open the NetworkProjectConfig
via the Fusion > Network Project Config
menu and check the following settings in the Config
section of the asset:
Enable Host Migration
: Checkbox to enable the Host Migration system.Host Migration Snapshot Interval
: the interval in seconds at which a snapshot sent to the Photon Cloud. A reasonable value will depend on the pace of the game; 30 second intervals is recommend for most. Only the latest snapshot is stored in the cloud, this means when the Host needs to be migrated and changes made to the game state between the last snapshot and the actual host migration will be lost.
Performing the Host Migration
The central entry point for the Host Migration is the INetworkRunnerCallbacks.OnHostMigration
callback. OnHostMigration()
will be invoked when the game needs to perform the Host Migration procedure.
Fusion exposes the old Host's game state as a list of NetworkObjects
which can be iterated over; it holds the data which will allow the new Host to rebuild the game state as close as possible to the previous one.
The Host Migration process consists of the following steps:
- When the Fusion Server Plugin (hosted in the Photon Cloud) detects the Host peer has left the session, it triggers the Host Migration process on all other peers. At this point:
- A new Host is selected; and,
- The last stored Game State received from the previous Host is sent to the new Host. N.B.: This snapshot is only sent to the new Host, no other peer will receive it.
- The
INetworkRunnerCallbacks.OnHostMigration
callback is invoked signaling the migration process has start.- The current Fusion NetworkRunner is still running and must be shutdown manually. This allows the developer to handle reset and / or setup necessary before performing the migration itself.
- A new Fusion
NetworkRunner
must be created in order to connect to the new Host and re-start the game; there is no support for re-using the old Runner. All the necessary information about the old game is carried by theHostMigrationToken
which is available from theOnHostMigration
callback. This step happens on both the new Host and all the Clients connecting to it. TheSessionName
and theGameMode
to be used by a particular peer are both specified in theHostMigrationToken
.
- The final step is the Game State re-creation. This step only happens on the new Host and consists of the Fusion
NetworkRunner
invoking the callback passed as theStartGamrArgs.HostMigrationResume
argument. This callback is invoked before theNetworkRunner
is fully initialized thus allowing the developer to read the old Game State, re-create the oldNetworkObject
, and setup them as necessary before the simulation starts.
The following code snippet examplifies the steps explained above.
C#
public class FusionInit : MonoBehaviour, INetworkRunnerCallbacks {
// other callbacks...
// Step 1.
// It happens on the Photon Cloud and there is no direct relation with the code on the peers.
// Step 2.
// OnHostMigration callback
public async void OnHostMigration(NetworkRunner runner, HostMigrationToken hostMigrationToken) {
// Step 2.1
// Shutdown the current Runner, this will not be used anymore. Perform any prior setup and tear down of the old Runner
// The new "ShutdownReason.HostMigration" can be used here to inform why it's being shut down in the "OnShutdown" callback
await runner.Shutdown(shutdownReason: ShutdownReason.HostMigration);
// Step 2.2
// Create a new Runner.
var newRunner = Instantiate(_runnerPrefab);
// setup the new runner...
// Start the new Runner using the "HostMigrationToken" and pass a callback ref in "HostMigrationResume".
StartGameResult result = await newRunner.StartGame(new StartGameArgs() {
// SessionName = SessionName, // ignored, peer never disconnects from the Photon Cloud
// GameMode = gameMode, // ignored, Game Mode comes with the HostMigrationToken
HostMigrationToken = hostMigrationToken, // contains all necessary info to restart the Runner
HostMigrationResume = HostMigrationResume, // this will be invoked to resume the simulation
// other args
});
// Check StartGameResult as usual
if (result.Ok == false) {
Debug.LogWarning(result.ShutdownReason);
} else {
Debug.Log("Done");
}
}
// Step 3.
// Resume Simulation on the new Runner
void HostMigrationResume(NetworkRunner runner) {
// Get a temporary reference for each NO from the old Host
foreach (var resumeNO in runner.GetResumeSnapshotNetworkObjects())
if (
// Extract any NetworkBehavior used to represent the position/rotation of the NetworkObject
// this can be either a NetworkTransform or a NetworkRigidBody, for example
resumeNO.TryGetBehaviour<NetworkPositionRotation>(out var posRot)) {
runner.Spawn(resumeNO, position: posRot.ReadPosition(), rotation: posRot.ReadRotation(), onBeforeSpawned: (runner, newNO) =>
{
// One key aspects of the Host Migration is to have a simple way of restoring the old NetworkObjects state
// If all state of the old NetworkObject is all what is necessary, just call the NetworkObject.CopyStateFrom
newNO.CopyStateFrom(resumeNO);
// and/or
// If only partial State is necessary, it is possible to copy it only from specific NetworkBehaviours
if (resumeNO.TryGetBehaviour<NetworkBehaviour>(out var myCustomNetworkBehaviour))
{
newNO.GetComponent<NetworkBehaviour>().CopyStateFrom(myCustomNetworkBehaviour);
}
});
}
}
}
public void OnShutdown(NetworkRunner runner, ShutdownReason shutdownReason) {
// Can check if the Runner is being shutdown because of the Host Migration
if (shutdownReason == ShutdownReason.HostMigration) {
// ...
} else {
// Or a normal Shutdown
}
}
}
API Overview
INetworkRunnerCallbacks.OnHostMigration
: invoked when the Host Migration must be performed.HostMigrationToken
: contains all the necessary information from the oldNetworkRunner
when performing the Host Migration and must be passed to the newNetworkRunner
when callingStartGame
.HostMigrationToken.GameMode
: newGameMode
the local peer will start as it can be eitherHost
orClient
. Useful to show extra info to players.StartGameArgs.HostMigrationToken
: accepts theHostMigrationToken
received as an argument in theINetworkRunnerCallbacks.OnHostMigration
callback.StartGameArgs.HostMigrationResume
: accepts a callback to be invoked when performing the game state re-creation on the new Host before the game simulation re-starts.NetworkRunner.GetResumeSnapshotNetworkObjects
: returns an iterable list ofNetworkObjects
exposing the last known State of the old Host. It can be used to check for what needs to be created again on the new Fusion Simulation.NetworkObject.CopyStateFrom
: used to copy all the State from an oldNetworkObject
into the newly created one.NetworkBehaviour.CopyStateFrom
: used to copy all the State from a specificNetworkBehaviour
into one from a newNetworkObject
.ShutdownReason.HostMigration
: thisShutdownReason
can be used to signal why aNetworkRunner
is being shutdown and to properly react to the host migration.