Skip to main content

Photon Fusion

Photon Fusion on Arbitrium#

This guide will help you create a headless server on Edgegap for a Unity project using Photon Fusion as its networking solution.

We will use the sample project Tanknarok from Photon Fusion as an example.

Because Edgegap works best when it knows where all the players are (so it can pick a server in the optimal location) the Tanknarok game will first start in client-hosted mode and then switch to the Edgegap server once all the players have joined. This is because Tanknarok has a playable lobby that runs before the match starts where players are able to join and leave at will. So the flow of logic is as follow:

  1. Players connect to the same room using AutoHostOrClient mode. Which automatically picks the first player in the room as the host.
  2. All players send their IP address to the host when they join.
  3. Once the players ready-up, the host uses these IP addresses to start an Edgegap server version of the game with a randomized room code (using a guid).
  4. When the host sees that the Edgegap instance has finished deploying, it tells the other players about the new room code.
  5. All players, including the host, disconnect from their current room and reconnect in Client mode to the new room being hosted by the Edgegap server.
  6. When the Edgegap server sees all expected players have joined, it starts the match.
  7. When the Edgegap server sees that everyone has left the match, it shuts itself down.

Account setup#

To run this application, you will need to have both a Photon account and an account with Edgegap. First, you will need to make a Fusion app on the Photon dashboard and set the app id in the PhotonAppSettings scriptable object file. Then, you will need to add an application and create an API token on the dashboard. You will use these to set the App Name, Version, and Api Token in the EdgegapConfig scriptable object file.

note

When making the Edgegap app, this program requires 512 CPU units and 512 MB of memory.

There are also two plugins to help with development: Newtonsoft Json for better Json serialization and ParrelSync for easier testing with multiple unity editors.

Step 1: Server mode setup#

You can grab the modified Tanknarok project on GitHub. You will also need to import the Photon Fusion SDK into the project.

The Photon Fusion Tanknarok project needed to be slightly altered to work as a server. This mostly involved automatically starting the match and setting the mode.

  1. Modified GameLauncher.cs to launch on start with GameMode.Server.
  2. Modified FusionLauncher.cs to call _spawnWorldCallback once game is running instead of relying on InstantiatePlayer (since no players are instantiated for server).
  3. Skipping the lobby scene if it’s a server and going straight to the match when all the players have joined.

This has already been completed in the modified Tanknarok project available our GitHub.

Dockerfile#

The docker file is pretty basic. We’re just using an ubuntu base, copying over the compiled linux server build, setting the permissions on the file, and running it.

Step 2: The first client#

The initial client takes on a role of host and of managing the Edgegap server. It does this through the EdgegapManager.cs file, which in turn uses the EdgegapAPIInterface.cs file, which wraps up most of the functionality surrounding interacting with Edgegap.

In GameManager.cs, the OnAllPlayersReady function for the host will deploy an Edgegap server instead of starting the match. It does this based on the IP addresses of all the players. Unity coroutines allow these actions to happen asynchronously. The Deploy method takes a callback function, which is fed the name of the new room the server will use. A Photon RPC function is sent to signal to all the clients to switch over.

if (EdgegapManager.EdgegapPreServerMode)
{
//if host, spin up new edgegap server
//once server is ready, connect to it
FindObjectOfType<GameLauncher>().ShowMatchStartingUI();
if (Object.HasStateAuthority)
{
string[] ips = PlayerManager.allPlayers.Select(p => p.ipAddress).ToArray();
StartCoroutine(EdgegapManager.Instance.Deploy(ips, RpcOnEdgegapServerReady));
}
}
else
{
LoadLevel(_levelManager.GetRandomLevelIndex(), -1);
}

The switch over happens in an empty scene named TransferringScene.

Inside EdgegapManager, a randomized room code is created and passed, along with the player count as Environment variables to the newly created instance through the deploy API. This is combined with the status api calls that start checking once a second to see when the instance is done deploying. You could also use the webhook option.

/// <summary>
/// Calls the Edgegap API to deploy a server
/// </summary>
/// <param name="clientIpList">A list of client IP addresses</param>
/// <param name="OnDeployed">A callback method with the session name as a parameter</param>
public IEnumerator Deploy(string[] clientIpList, Action<string> OnDeployed = null)
{
var roomCode = Guid.NewGuid().ToString();
yield return EdgegapAPIInterface.RequestDeployment(clientIpList,
new Dictionary<string, string> {
{ "room_code", roomCode },
{ "player_count", clientIpList.Length.ToString() } });
yield return EdgegapAPIInterface.WaitForDeployment();
OnDeployed(roomCode);
}

Step 3: Joining players#

Besides switching over to the new Edgegap server when told to, joining players also need to tell the host player what their IP address is. Since Photon doesn’t provide this information, an RPC call in GameManager called ShareIpAddress is called from the Player.cs class in the Spawned method.

The host then stores these IP addresses in the player class for all the players based on who the source of the RPC message was:

[Rpc(sources:RpcSources.All, RpcTargets.StateAuthority)]
public void RpcShareIpAddress(string ipAddress, RpcInfo info = default)
{
if (info.Source == PlayerRef.None)
{
// source will be None for local player
Player.local.ipAddress = ipAddress;
return;
}
foreach (var player in PlayerManager.allPlayers)
{
if (player.playerID == info.Source.PlayerId)
{
player.ipAddress = ipAddress;
break;
}
}
}

Step 4: Cleaning up#

Back in the server, the modified GameManager.cs keeps track of the number of players in the match in the main Update function. It both checks for when the target number of players have joined, so it can start the match, but also determines when it’s time to shut itself down. If the game has been running for over a minute and there aren’t any players left, it calls the Edgegap API to stop its own deployment.

if (EdgegapManager.IsServer())
{
if (GameManager.playState == GameManager.PlayState.LOBBY)
{
// if we're in the lobby and all players have joined, start the match
if (PlayerManager.allPlayers.Count >= EdgegapManager.ExpectedPlayerCount)
{
OnAllPlayersReady();
}
}
if(Time.time > 60 && PlayerManager.allPlayers.Count == 0
&& GameManager.playState != GameManager.PlayState.TRANSITION)
{
// if it's been over a minute and there's no one here, stop the server
Debug.LogWarning("Shutting Down");
StartCoroutine(EdgegapAPIInterface.StopDeploymentFromServer());
GameManager.playState = PlayState.TRANSITION;
}
}

You now have a Photon Fusion project available to deploy on demand!