This page is a work in progress and could be pending updates.

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.

Fusion implements a NAT Punchthrough algorithm which deals with all things required to establish a connection between the clients and the host. Therefore there is no need to set up port forwarding or firewall rules. This makes developing player hosted games straightforward.

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:

  1. The quality of the traffic over a residential network fluctuates - this depends on the Game Host's internet connection / subscription; and,
  2. 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.

Back To Top

Host Migration

In order to perform Host Migration, the project must meet a few pre-requisites:

  1. 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 the GameMode.Host or GameMode.AutoHostOrClient parameter.
  2. Enable Host Migration: as this is an optional feature and requires some manual work to implement, it is not enabled by default.
  3. 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.

Back To Top

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.

Add Fusion Stats
Host Migration Settings in the Network Project Config Asset

Back To Top

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:

  1. 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:
    1. A new Host is selected; and,
    2. 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.
  2. The INetworkRunnerCallbacks.OnHostMigration callback is invoked signaling the migration process has start.
    1. 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.
    2. 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 the HostMigrationToken which is available from the OnHostMigration callback. This step happens on both the new Host and all the Clients connecting to it. The SessionName and the GameMode to be used by a particular peer are both specified in the HostMigrationToken.
  3. 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 the StartGamrArgs.HostMigrationResume argument. This callback is invoked before the NetworkRunner is fully initialized thus allowing the developer to read the old Game State, re-create the old NetworkObject, and setup them as necessary before the simulation starts.

The following code snippet examplifies the steps explained above.

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
    }
  }
}

Back To Top

API Overview

  • INetworkRunnerCallbacks.OnHostMigration: invoked when the Host Migration must be performed.
  • HostMigrationToken: contains all the necessary information from the old NetworkRunner when performing the Host Migration and must be passed to the new NetworkRunner when calling StartGame.
  • HostMigrationToken.GameMode: new GameMode the local peer will start as it can be either Host or Client. Useful to show extra info to players.
  • StartGameArgs.HostMigrationToken: accepts the HostMigrationToken received as an argument in the INetworkRunnerCallbacks.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 of NetworkObjects 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 old NetworkObject into the newly created one.
  • NetworkBehaviour.CopyStateFrom: used to copy all the State from a specific NetworkBehaviour into one from a new NetworkObject.
  • ShutdownReason.HostMigration: this ShutdownReason can be used to signal why a NetworkRunner is being shutdown and to properly react to the host migration.


To Document Top