> 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-simple-fps.md).

# Photon Fusion 2 Simple FPS

Host Photon's Simple FPS sample (Photon Fusion 2) on Edgegap dedicated servers, one deployment per match, using the Edgegap Simple FPS kit.

Explore how to host Unity projects on Edgegap using techniques from the [Simple FPS Multiplayer Sample](https://assetstore.unity.com/packages/templates/tutorials/simple-fps-multiplayer-photon-fusion-272941). Players join a server from the sample's **Party** menu by typing the deployment's address and Photon region.

{% hint style="info" %}
Our [Edgegap Simple FPS kit](https://github.com/edgegap/fusion-simplefps-edgegap) is a set of files you copy into your Simple FPS project. It **doesn't change or include any Photon licensed code**, so you get Simple FPS from the Unity Asset Store under your own license.
{% endhint %}

This sample shows:

* **Simple FPS as a dedicated server** - one deployment per match, [placed close to players](/learn/orchestration/deployments.md#server-placement).
* **Party menu join** - the Join card takes your deployment FQDN and a Photon region code, **Create Game** is unchanged.
* **Direct connections** - the server binds `0.0.0.0` on port `7777`, so clients connect directly.
* **One project for both sides** - server and client builds share the same scene list.
* **Server self-stop** - when the server process exits on its own, the start script stops the deployment.

{% hint style="success" %}
See [Photon Fusion 2](/docs/sample-projects/unity-netcodes/photon-fusion-2-on-edgegap.md) for a **general guide to Photon Fusion 2 on Edgegap**. See the kit's [README](https://github.com/edgegap/fusion-simplefps-edgegap) for the full list of files and test results.
{% endhint %}

## ✔️ Preparation

Before we start, you will need:

* [Simple FPS Multiplayer (Photon Fusion)](https://assetstore.unity.com/packages/templates/tutorials/simple-fps-multiplayer-photon-fusion-272941) - download and import into a new Unity project.
  * This sample brings its own Project Settings.
* Edgegap Simple FPS kit - [download from GitHub](https://github.com/edgegap/fusion-simplefps-edgegap),
* Unity 6000.3 with Linux build modules below, and **Windows Build Support (IL2CPP)** for clients (requires Visual Studio's C++ workload),
* Photon Fusion App ID - find it in your [Photon dashboard](https://dashboard.photonengine.com).

{% hint style="info" %}
Open your project and install server build tools by following [Unity](/unity.md) preparation steps.
{% endhint %}

This kit was tested with Simple FPS 2.1.1, Fusion 2.1.1 (build 2177) and Unity 6000.3.11f1 on Windows.

{% hint style="warning" %}
Your server **must use IL2CPP** (the sample's default). Mono servers fail at runtime unrecoverably.
{% endhint %}

☑️ To build Linux IL2CPP on Windows, 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`.

## ⚡ Deploy and Connect

### 1. Add the Kit to Your Project

☑️ Copy `Assets/EdgegapSimpleFPS`, `edgegap/`, `scripts/`, `.dockerignore` and `.gitattributes` from the kit to your project root, next to `Assets/`.

☑️ Paste your Fusion App ID in Tools / Fusion / Realtime Settings.

{% hint style="info" %}
Clients and server **must use the same App ID, App Version and Photon region**, otherwise they won't find each other.
{% endhint %}

{% hint style="warning" %}
The Asset Store package also ships a `PhotonAppSettings` asset in `Assets/AssetStoreTools/Resources/` with the publisher's App ID. **Put your App ID there too, or delete that copy.**
{% endhint %}

☑️ Set **Peer Mode** to `Single` in Tools / Fusion / Network Project Config.

☑️ Run Tools / Edgegap Simple FPS / Check Project and fix any reported issues.

☑️ Run Tools / Edgegap Simple FPS / Set Up Project. It only changes scene and build settings, and is safe to run again:

* Deathmatch: `FusionBootstrap` becomes `EdgegapFusionBootstrap` (keeping its settings), and Start Mode becomes **User Interface**,
* Startup: Party menu's `FusionMenuUIParty` becomes `EdgegapPartyMenu` (the menu prefab instance is unpacked to allow this),
* Join card's code field accepts up to 30 characters (letters, digits and dots), and gets a new **Region** field next to it,
* build profile is set to Startup (0), then Deathmatch (1).

✅ You may now proceed to the next step.

### 2. Build and Upload the Server

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

☑️ Build and upload your server with the Edgegap plugin or from a terminal:

{% tabs %}
{% tab title="Edgegap Plugin (Recommended)" %}
☑️ Open Tools / Edgegap Server Hosting. In step **3. Containerize Your Server**, click **Path To Dockerfile** and select `edgegap/Dockerfile`. Keep **Relative Build Path** at `Builds/EdgegapServer`, then Containerize and upload.

☑️ Keep port `7777` / UDP when the plugin creates your app version.
{% endtab %}

{% tab title="Terminal (for Agents)" %}
☑️ Find your [Container Registry](/learn/advanced-features/edgegap-container-registry.md#getting-your-credentials) project name and credentials, then log in with `docker login registry.edgegap.com`.

☑️ Build your server, then package and push the image (Git Bash works on Windows):

{% code collapsedlinecount="10" %}

```bash
scripts/build.sh server
scripts/package.sh registry.edgegap.com/<your-registry-project>/simplefps <new-tag>
```

{% endcode %}

`package.sh` prints the new tag it pushed.

☑️ [Create an application](https://app.edgegap.com/application-management/applications/list) and a version for your image. You can also use the kit's `examples/app-version.json` with `POST /v1/app/{app}/version`.
{% endtab %}
{% endtabs %}

The first IL2CPP server build takes about 13 minutes.

{% hint style="danger" %}
:x: **DO NOT - overwrite existing tags or use `latest` tag** - this will deploy an outdated cache.\
:white\_check\_mark: **DO - always increase your version tag** and deploy the new build.
{% endhint %}

You can also build from editor with Tools / Edgegap Simple FPS / Build Linux Server, which builds the same output to `Builds/EdgegapServer`. See [Unity](/unity.md) to learn more about building Unity servers.

{% hint style="warning" %}
If you package your server with the Edgegap Unity plugin, set its **Path To Dockerfile** to `edgegap/Dockerfile`. The plugin's default Dockerfile prints the environment, **which includes your deployment's delete token**.
{% endhint %}

☑️ [Create an application](https://app.edgegap.com/application-management/applications/list) and a version with:

* your image and tag,
* your Container Registry username and token,
* resources 1 vCPU and 2 GB to start,
* [port](/learn/orchestration/application-and-versions.md#port-mapping) `7777`, protocol **UDP**, with port check off.

✅ You may now proceed to the next step.

### 3. Test Locally

☑️ Build a local image and run it with the variables Edgegap would inject:

{% code collapsedlinecount="10" %}

```bash
scripts/package.sh
scripts/run-local.sh
```

{% endcode %}

☑️ Find log line `[edgegap-simplefps] server started: session=f68e011bfb01.pr.edgegap.net photon_region=<code>` and join from a client with that session and region, as in the next step.

{% hint style="info" %}
Local clients connect **Relayed**, as a clients on the same machine can't reach Docker Desktop's NAT address directly. On Edgegap, the same image connects **Direct**.
{% endhint %}

✅ You may now proceed to the next step.

### 4. Deploy and Connect

☑️ Start a deployment from the plugin, [dashboard](https://app.edgegap.com/deployment-management/deployments/list), or `POST /v2/deployments`. Pass your players' public IPs to [deploy close to them](/learn/orchestration/deployments.md#server-placement).

☑️ Find `server started: session=<fqdn> photon_region=<code>` in your deployment's logs.

☑️ Build a client with `scripts/build.sh client` (or your usual Windows build), launch it and click **Party Menu** on the main menu.

☑️ On the Join card, type your deployment FQDN (e.g. `f68e011bfb01.pr.edgegap.net`) in the code field, and region code (e.g. `cae`) in the **Region** field, then click **Join**.

{% hint style="info" %}
If your client doesn't connect, your region most likely doesn't match the server log.
{% endhint %}

🙌 Congratulations on your first Deployment on Edgegap!

## 🔄 Match Lifecycle

1. Server build skips the Startup menu and loads Deathmatch.
2. `EdgegapFusionBootstrap` starts a Fusion server on `ARBITRIUM_PORT_GAMEPORT_INTERNAL` (`7777` by default), bound to `0.0.0.0`, and names the session after the deployment FQDN, `<ARBITRIUM_REQUEST_ID>.pr.edgegap.net`.
3. Server logs the session name and Photon region, which players type into the Party menu's Join card and connect Direct to the deployment.
4. When the server process exits on its own, `edgegap/start-server.sh` calls `ARBITRIUM_DELETE_URL` to stop the deployment.

{% hint style="info" %}
The start script never prints the environment, so **your delete token stays out of logs**.
{% endhint %}

See [Deployments](/learn/orchestration/deployments.md) to learn more.

## ⚙️ Environment Variables

| Variable                                         | Set by                      | Effect                                                                                                                                                         |
| ------------------------------------------------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ARBITRIUM_REQUEST_ID`                           | Edgegap                     | The session name becomes `<request id>.pr.edgegap.net`, the deployment's FQDN.                                                                                 |
| `ARBITRIUM_PORT_GAMEPORT_INTERNAL`               | Edgegap                     | The UDP port the server binds (`7777` by default). It binds `0.0.0.0`, so clients connect Direct.                                                              |
| `PHOTON_REGION`                                  | You (version or deployment) | A fixed Photon region, for example `us`. When it isn't set, the server uses the region with the best ping from where it runs. This override is not tested yet. |
| `ARBITRIUM_DELETE_URL`, `ARBITRIUM_DELETE_TOKEN` | Edgegap                     | When the server process exits on its own, the start script uses them to stop the deployment.                                                                   |

See [injected variables](/learn/orchestration/deployments.md#injected-environment-variables) to learn more.

## 🔌 Direct Connection

Simple FPS'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`.              |

## 🎬 Server Scenes

Fusion tells clients which scene to load by **build index**. A server built with Deathmatch only would have Deathmatch at index 0, while clients have Startup at index 0. Clients then never find the game objects and throw `NullReferenceException` in `Player.FixedUpdateNetwork` every tick. That's why both builds use Startup (0) and Deathmatch (1), and the server skips Startup.

For the same reason, the server marks Deathmatch **active on load**, like the sample's own host. Without it, clients load Deathmatch but keep Startup active, with the same exception.

## ⚠️ Known Issues

* Playing a match to its end, and whether the server exits after a match, are not tested yet - see the kit's [README](https://github.com/edgegap/fusion-simplefps-edgegap).
* `PHOTON_REGION` override is not tested yet.

## 🚨 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, see [Preparation](#preparation).

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