> For the complete documentation index, see [llms.txt](https://docs.edgegap.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.edgegap.com/docs/sample-projects/unity-netcodes/photon-fusion-2-on-edgegap.md).

# Photon Fusion 2

Host a Unity Photon Fusion 2 game on Edgegap, following the Fusion 2 Asteroids sample from build through live deployment.

Explore how to host Unity projects on Edgegap using techniques from the [Fusion 2 Asteroids Sample](https://doc.photonengine.com/fusion/current/game-samples/fusion-asteroids).

{% embed url="<https://youtu.be/bg41HpPmE3E>" %}

## ✔️ Preparation

Before we start, you will need:

* [Unity 6](https://unity.com/releases/unity-6) - download using Unity Hub,
* [Fusion 2 Asteroids Sample project](https://github.com/edgegap/netcode-game-sample-photon-fusion2-asteroid) (modified for Edgegap).

## ⚡ Deploy and Connect

{% hint style="success" %}
See [Unity](/unity.md) for Unity to **learn how to build and customize servers**.
{% endhint %}

### 1. Deploy a Server on Edgegap

☑️ To get started, you'll need to [create a free account with Edgegap](https://app.edgegap.com/auth/register). No credit card required.

☑️ [Create a new app version for your application](https://app.edgegap.com/application-management/applications/fusion-2-asteroids-sample/versions/create), choose Fusion 2 Asteroids Sample.

☑️ Open your new project in Unity.

☑️ Navigate to Tools / Edgegap Hosting, then to step 6. Deploy a Server on Edgegap.

☑️ Press Deploy to cloud and [open your new deployment's details](https://app.edgegap.com/deployment-management/deployments/list).

☑️ Find your deployment's unique, one-time connection details:

* **Host URL** in format `780aa4260e83.pr.edgegap.net` .

✅ You may now proceed to the next step.

### 2. Connect from Editor

☑️ Open your new project in Unity.

☑️ Press the ▶️ Play button to start your game client.

☑️ Input Host URL (connection details from previous step) as room name.

☑️ Press Start Edgegap button to connect to your server.

☑️ Connect a second virtual Player with [Multiplayer Play Mode](https://docs-multiplayer.unity3d.com/mppm/current/about/) or [ParrelSync](https://github.com/VeriorPies/ParrelSync).

🙌 Congratulations on your first Deployment on Edgegap!

## ✏️ Customize Server Build

### Create an App on Photon

{% hint style="info" %}
To make the initial demo easy, we used a free tier Photon Cloud account owned by Edgegap.
{% endhint %}

☑️ [Create a free account with Photon](https://dashboard.photonengine.com/).

☑️ [Create an Application on Photon](https://dashboard.photonengine.com/app/create):

* `Multiplayer Game`,
* `Fusion` Photon SDK,
* `Fusion 2` SDK version.

☑️ Find your Application ID in format `85314a99-56fc-4ab3-ba26-50efca09f303` .

☑️ Input your ID in Photon Settings under Tools / Fusion / Fusion Hub (Alt + F).

### Integrate Fusion 2 Project with Edgegap

{% hint style="success" %}
See `EdgegapServerManager.cs`  script for an example integration of Fusion 2 with Edgegap.
{% endhint %}

{% hint style="warning" %}
Your `NetworkProjectConfig`  **must use `Peer Mode = Single` (Ded. Server)**, not `Multiple` (Client-Host)!
{% endhint %}

Game clients will connect to game servers through [Photon Fusion 2 ](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session)[Session (room) ](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session)[feature](https://doc.photonengine.com/fusion/current/manual/connection-and-matchmaking/matchmaking#creating-and-joining-a-game-session).

Your game server needs to register it's IP address and external port with the Session name, when calling `_runnerInstance.StartGame(StartGameArgs args)`:

* use `GameMode.Server`  to ensure your connection isn't relayed over Photon Cloud (adds latency),
* use `NetAddress.CreateFromIpPort` method from Fusion,
* provide server's IP address:\
  `Environment.GetEnvironmentVariable("ARBITRIUM_PUBLIC_IP")`
* provide server's external port:\
  `Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_EXTERNAL")`
  * this is the default port mapping name if you use our [Unity plugin to package your server](/unity.md).

Use Edgegap Deployment Host URL as a Session name:

* in game server, get it with\
  `$"{Environment.GetEnvironmentVariable("ARBITRIUM_REQUEST_ID")}.pr.edgegap.net"`
* when testing game client, get it from dashboard - deployment details page / Host URL
  * create deployments for testing from our [quickstart hosting plugin](/unity/developer-tools.md#dedicated-servers-quickstart-plugin),
* when live in game client, get it from [matchmaking / ticket assignment / fqdn](/learn/matchmaking/matchmaker-in-depth.md#player-api):
  * see also [getting started with Matchmaking](/learn/matchmaking.md).

{% tabs %}
{% tab title="Client Integration" %}
{% code title="EdgegapClientManager.cs" collapsedlinecount="10" %}

```csharp
// get fusion 2 network runner
_runnerInstance = FindFirstObjectByType<NetworkRunner>();
_runnerInstance.ProvideInput = true;

// get this value from Edgegap Matchmaker Assignment or Fusion 2 Sessions API
string sessionName = "<requestId.pr.edgegap.net>";

// find fusion 2 session (using session name)
var result = await _runnerInstance.StartGame(
    new StartGameArgs() {
        GameMode = GameMode.Client,
        SessionName = sessionName,
        ObjectProvider = _runnerInstance.GetComponent<NetworkObjectPoolDefault>(),
    }
);

// fusion will now fetch server IP and port based on session name and connect
```

{% endcode %}
{% endtab %}

{% tab title="Server Integration" %}
{% code title="EdgegapServerManager.cs" collapsedlinecount="10" %}

```csharp
// read edgegap injected environment variables
string requestId = Environment.GetEnvironmentVariable("ARBITRIUM_REQUEST_ID");
string listenPortStr = Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_INTERNAL");
string connectIP = Environment.GetEnvironmentVariable("ARBITRIUM_PUBLIC_IP");
string connectPort = Environment.GetEnvironmentVariable("ARBITRIUM_PORT_GAMEPORT_EXTERNAL");

// get fusion 2 network runner
_runnerInstance = FindFirstObjectByType<NetworkRunner>();
_runnerInstance.ProvideInput = true;

// parse the listen port as ushort expected by StartGame
ushort.TryParse(listenPortStr, out ushort listenPort);

// register fusion 2 session for clients to find it later (using session name)
var result = await _runnerInstance.StartGame(
    new StartGameArgs() {
        GameMode = GameMode.Server,
        SessionName = $"{requestId}.pr.edgegap.net",
        ObjectProvider = _runnerInstance.GetComponent<NetworkObjectPoolDefault>(),
        Address = NetAddress.Any(listenPort),
        CustomPublicAddress = NetAddress.CreateFromIpPort(connectIP, connectPort),
    }
);

// load initial scene
if (result.Ok && _runnerInstance.IsServer) {
    await _runnerInstance.LoadScene(sceneName);
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

### 🔌 Direct Connection

Fusion's `IP` and `Port` set the address your server **binds** to. The kit binds `0.0.0.0:7777`, Fusion finds the deployment's public address on its own, and clients connect **Direct** - even though Edgegap gives players a different [external port](/learn/orchestration/application-and-versions.md#port-mapping).

| Server `IP`                | Result                                                                                                                                           |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `0.0.0.0` (this kit)       | **Direct** on Edgegap.                                                                                                                           |
| `127.0.0.1`                | The server starts, but clients are **Relayed** through Photon, which adds latency. Nothing outside the container can reach the loopback address. |
| The deployment's public IP | The server **doesn't start** (`Failed to bind socket`), because the container doesn't own that address. Clients get `GameNotFound`.              |

## 🚨 Troubleshooting

<details>

<summary><code>MethodAccessException ... CheckInvokeRpc</code> in server log</summary>

* Your server was built with Mono. Rebuild it with IL2CPP.

</details>

<details>

<summary><code>No Toolchain found for host platform</code> or <code>No Linux sysroot found</code></summary>

* Linux toolchain packages are missing. Add Unity's Linux toolchain in Window / Package Manager / + / Add package by name:
  * Unity 6000.3 and later: `com.unity.toolchain.win-x86_64-linux` and `com.unity.sdk.linux-x86_64`,
  * earlier versions: `com.unity.toolchain.win-x86_64-linux-x86_64`.

</details>

<details>

<summary>Client gets <code>GameNotFound</code></summary>

* App Version or Photon region differs between client and server. Compare with the server's `photon_region=` log line.

</details>

<details>

<summary>Client gets <code>InvalidAuthentication</code></summary>

* Your Fusion App ID is wrong or missing in the build.

</details>

<details>

<summary>Tab overlay shows CT Relayed</summary>

* Your server binds `127.0.0.1`, see [Direct Connection](#direct-connection).

</details>
