> 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/megacity-metro.md).

# Unity ECS Megacity Metro

Host Unity's Megacity Metro, a Netcode for Entities shooter for up to 150 players, on Edgegap with dedicated servers, matchmaking and backfill.

Explore how to host Unity DOTS projects on Edgegap using techniques from the [Megacity Metro Sample](https://github.com/Unity-Technologies/megacity-metro), a mobile and desktop shooter built on Netcode for Entities, with up to 150 players per server.

Players press **Find Match**, the [Edgegap Matchmaker](/learn/matchmaking.md) starts a dedicated server near them, and players searching later join running servers with [backfill](/learn/matchmaking/matchmaker-in-depth.md#backfill-match). The server stops its own deployment when nobody is playing.

This sample shows:

* **Unity 6000.1.0f1** project on **Netcode for Entities** (DOTS), built as a Linux dedicated server,
* **direct connect** from a desktop client to a deployment, for testing without a matchmaker,
* **Edgegap Matchmaker** integration with [Edgegap Unity SDK](https://github.com/edgegap/edgegap-unity-sdk) - client queues a ticket and connects to the assigned server,
* **backfill** - running servers keep open seats in the matchmaker, so new players join them instead of starting new servers, up to 150 players,
* **server self-stop** - the server stops its own deployment when nobody joins, or after everyone has left.

The game's netcode is unchanged. Without a matchmaker URL, outside an Edgegap deployment and without a direct-connect target, the game behaves like upstream.

{% hint style="info" %}
Verified on Edgegap with Windows desktop clients: server deployment, direct connect, and matchmaking with backfill (3 players on one server). Mobile release builds hide the Connect option, so phones use **Find Match**.
{% endhint %}

{% hint style="success" %}
See [EDGEGAP.md](https://github.com/edgegap/megacity-metro/blob/master/EDGEGAP.md) in the sample repository for the full list of Edgegap files and server settings.
{% endhint %}

## ✔️ Preparation

Before we start, you will need:

* Unity `6000.1.0f1` - [download using Unity Hub](https://unity.com/releases/unity-6), with modules below and your client platform's module (Windows Build Support (IL2CPP) for the tested client),
* Megacity Metro Sample project (modified for Edgegap) - [download from GitHub](https://github.com/edgegap/megacity-metro) with [Git LFS](https://git-lfs.com/) (about 415 MB of LFS content).

<details>

<summary>Install Unity Linux Build Support Modules</summary>

* Use Unity Hub to select tab **Installs**, access **Settings** and **Add Modules** for each Unity version you intend to use with Edgegap platform:

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FylRG4r8orenZrw5ijjpJ%2Fimage.png?alt=media&amp;token=fb981825-8a15-4c07-9180-0f79a6a77a91" alt=""><figcaption></figcaption></figure>

* Scroll down to select and install the following Unity modules:
  * **Linux Build Support (IL2CPP),**
    * **Linux Build Support (Mono),**
    * **Linux Dedicated Server Build Support**

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2FuXtyqoLhBOk8CZJ4WdzA%2Fimage.png?alt=media&amp;token=19e956ed-a731-420d-b911-130c334794fc" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><a href="https://www.docker.com/products/docker-desktop/">Install Docker Desktop (or Docker CLI)</a></summary>

* [Install Docker Desktop from the official source](https://www.docker.com/products/docker-desktop/) (no account required).
* Restart your computer after completing the installation.

</details>

## ⚡ Deploy and Connect

### 1. Build and Push

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

☑️ Build in editor plugin in  or with CLI:

{% tabs %}
{% tab title="Unity Servers Quickstart Plugin (Recommended)" %}
☑️ Select Tools / Edgegap / Build Linux Dedicated Server.

☑️ Complete Edgegap Plugin step Containerize and Upload.
{% endtab %}

{% tab title="Command-Line Interface (for Agents)" %}
☑️ Close your project in Unity Editor, then build the Linux dedicated server from the project root:

{% code collapsedlinecount="10" %}

```bash
Unity -batchmode -quit -buildTarget Linux64 -standaloneBuildSubtarget Server -projectPath . -executeMethod EdgegapServerBuild.Build -logFile build.log
```

{% endcode %}

{% hint style="warning" %}
Keep `-buildTarget Linux64 -standaloneBuildSubtarget Server`, otherwise the Entities content build fails with `Unable to build with the current configuration`.
{% endhint %}

☑️ Find your [Container Registry](/learn/advanced-features/edgegap-container-registry.md#getting-your-credentials) project name and credentials, then build and push your image:

{% code collapsedlinecount="10" %}

```bash
docker login registry.edgegap.com
docker build --platform linux/amd64 -t registry.edgegap.com/<project>/megacity-metro:<tag> .
docker push registry.edgegap.com/<project>/megacity-metro:<tag>
```

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

Your build is saved to `Builds/EdgegapServer/` (about 3.4 GB, mostly Burst debug information and IL2CPP backup folder). The sample's `.dockerignore` excludes `*_BurstDebugInformation_DoNotShip` and `*_BackUpThisFolder_ButDontShipItWithYourGame`, so your image is about 537 MB.

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

Your server listens on `0.0.0.0:7979` over UDP. Pass extra server arguments in `UNITY_COMMANDLINE_ARGS` environment variable on your app version.

✅ You may now proceed to the next step.

### 2. Create an App Version

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

* your `registry.edgegap.com/<project>/megacity-metro:<tag>` image,
* your Container Registry username and token, otherwise deployments fail to pull the image,
* resources 1 vCPU and 2 GB memory (tested, an idle server used about 18% CPU and 323 MB),
* [port](/learn/orchestration/application-and-versions.md#port-mapping) `7979`, named `gameport`, protocol **UDP**.

✅ You may now proceed to the next step.

### 3. Connect Directly

Let's verify your server works before adding a matchmaker.

☑️ [Deploy your app version](https://app.edgegap.com/deployment-management/deployments/list) from plugin or dashboard. Your server log shows `Running on Edgegap ... skipping UGS sessions`.

☑️ [Open your new deployment's details](https://app.edgegap.com/deployment-management/deployments/list) and find your unique, one-time connection details:

* **Host URL** in format `780aa4260e83.pr.edgegap.net` (or IP),
* **External Port** of `gameport` in format `30854` (5 digits, not `7979`).

☑️ Start a desktop client with these details:

{% code collapsedlinecount="10" %}

```bash
<client executable> -edgegapConnect <host>:<external port>
```

{% endcode %}

You can also set `EDGEGAP_CONNECT=<host>:<external port>` before launching your client. With a direct-connect target, your client skips Unity Gaming Services (UGS) sign-in, so no UGS project is needed. **Find Match** connects to that target.

☑️ Find `[Edgegap] Connected players: 1` in your server log once your client joins.

✅ You may now proceed to the next step.

### 4. Create a Matchmaker

☑️ Set `application.name` and `application.version` to your application and version in the sample's `matchmaker-config.json`.

The config has one `metro` profile with one team of 2 to 150 players. Two players searching together get a server about 10 seconds after queuing, and a lone player gets one after 30 seconds. This suits testing, raise the minimum for real matches.

☑️ Open [Matchmaker](https://app.edgegap.com/matchmaker-management-v2/matchmakers/list) page, click **Create Matchmaker**, upload `matchmaker-config.json`, then click **Create and Start**.

☑️ Copy your matchmaker's **API URL** and **Auth Token** once it shows **ONLINE**.

* **Free Cluster (shared)** to test all features and explore synergies with your design,
  * shuts down after 3 hours automatically, requiring restart to continue testing.
* **Private Cluster** **(dedicated)** to ensure a stable environment for your production needs,
  * pick your region and get 24/7 support for live games to release with confidence.

Restarting your matchmaker from dashboard keeps its API URL and Auth Token.

✅ You may now proceed to the next step.

### 5. Find a Match

☑️ Paste your API URL and Auth Token into `Assets/Resources/EdgegapMatchmakerSettings.asset`.

{% hint style="danger" %}
**Do not integrate Edgegap API endpoints in game client, as your API token provides unlimited access to your account. See** [Integration](/docs/api/integration.md) **for secure client-facing API endpoints and functions.**
{% endhint %}

{% hint style="info" %}
Matchmaking Auth Token is scoped to your matchmaker only, so it's safe to ship in game clients.
{% endhint %}

☑️ Rebuild your server and clients - clients read this asset to find matches, servers read it to create backfills. Push your new server image and point your app version and `matchmaker-config.json` to it.

Override the asset at launch with `-edgegapMatchmakerUrl`, `-edgegapMatchmakerToken` and `-edgegapMatchmakerProfile` (or `EDGEGAP_MM_URL`, `EDGEGAP_MM_TOKEN`, `EDGEGAP_MM_PROFILE`).

☑️ Start two clients and click **Find Match** in both. Status goes from `Searching...` to `Match found, starting server...`, then your client connects.

☑️ Start a third client and click **Find Match** while the match is running. It joins the same server through a backfill.

🙌 Congratulations on your first matchmade Deployment on Edgegap!

## 🔄 Match Lifecycle

1. Client queues a solo ticket with `metro` profile. Its `backfill_group_size` attribute is `["new", "1"]` - a new match, or one seat on a running server.
2. Matchmaker starts a deployment near the players, or matches the ticket with a backfill from a running server.
3. Client connects to the server's IP and external port, then sends its ticket ID to the server.
4. On matchmaker deployments, the server reads the match from [`MM_*` variables](/learn/matchmaking/matchmaker-in-depth.md#injected-variables) and gives each matched player 60 seconds to connect. It keeps up to 3 backfills open while seats are free, and frees a seat when its player leaves or never arrives.
5. Server closes its backfills and stops its deployment with `ARBITRIUM_DELETE_URL` - 300 seconds after start if no player ever connected, or 60 seconds after the last player left.

Change these limits with environment variables on your app version:

* `EDGEGAP_MAX_PLAYERS` - seats per server (default `150`),
* `EDGEGAP_MAX_OPEN_BACKFILLS` - backfills open at once (default `3`),
* `EDGEGAP_FIRST_PLAYER_TIMEOUT_S` - seconds before a server nobody joined stops itself (default `300`),
* `EDGEGAP_EMPTY_TIMEOUT_S` - seconds after the last player left before the server stops itself (default `60`).

{% hint style="warning" %}
If a deployment is stopped from outside (dashboard, API, or max duration), the server exits without closing its backfills. **Until they expire** (`ticket_expiration_period`, 30 seconds), **they can send a player to the stopped server.** That player's connection times out, and their next **Find Match** works.
{% endhint %}

See [Matchmaking](/learn/matchmaking.md), [In-Depth Look](/learn/matchmaking/matchmaker-in-depth.md) and [Deployments](/learn/orchestration/deployments.md) to learn more.

## ⚠️ Known Issues

* Android and iOS clients are not tested yet, nor is a windowed playtest on desktop.
* Server logs connections with unknown ticket IDs, but still lets them in (no ticket validation yet).
* Vivox and UGS sign-in are untouched. Edgegap Matchmaker and direct-connect paths don't use them.
