> 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/unity-netcode-on-edgegap.md).

# Unity NGO

Host a Unity Netcode for GameObjects project on Edgegap, following the NGO Boss Room sample from build through live deployment.

Explore how to host Unity projects on Edgegap using techniques from the [NGO Boss Room Sample](https://unity.com/demos/small-scale-coop-sample).

{% hint style="info" %}
This sample **does not require any Unity Gaming Services (UGS)**, Multiplay, or Relays to run.
{% endhint %}

{% embed url="<https://youtu.be/8Qi-wXswjkw>" %}

## ✔️ Preparation

Before we start, you will need:

* Unity 6 - [download using Unity Hub](https://unity.com/releases/unity-6),
* Unity NGO \[Netcode for Game Objects] Boss Room Sample project (modified for Edgegap):
  * [download from Github](https://github.com/edgegap/netcode-sample-unity-ngo-bossroom).

## ⚡ Deploy and Connect

### 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/unity-ngo-bossroom-sample/versions/create), choose NGO Boss Room Sample.

☑️ [Deploy a server with your NGO Boss Room Sample app version](https://app.edgegap.com/deployment-management/deployments/list).

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

* **External Port**, for example `30854` (randomly chosen).

☑️ Navigate to tab Logs and find (CTRL+F) your unique, one-time connection details:

* `ARBITRIUM_PUBLIC_IP` , for example IPv4: `172.234.244.38` .

✅ You may now proceed to the next step.

### 2. Connect from Editor

☑️ Open your new project in Unity.

☑️ Verify that you have opened scene: `Assets/Scenes/MainMenu.unity`.

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

* press **START WITH DIRECT IP** button,
* select tab **JOIN WITH IP.**

☑️ Input connection details from previous step.

☑️ Press **JOIN** 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!

## 🎯 Matchmaking (Optional)

Match players with [Edgegap Matchmaker](/learn/matchmaking.md) - it groups players, Edgegap starts a server near them, and every client connects to it automatically. Matchmaking is disabled by default, enable it once direct connection works.

{% hint style="success" %}
Matchmaking scripts live in `Assets/Edgegap/` and add themselves at runtime, so **Boss Room's scenes and prefabs are not modified**. See [EDGEGAP.md](https://github.com/edgegap/netcode-sample-unity-ngo-bossroom/blob/main/EDGEGAP.md) for the full list of files and command-line options.
{% endhint %}

### 1. 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 `casual` profile for 2 to 8 players. It fills up to 8 players when it can, and its [expansion](/learn/matchmaking/matchmaker-in-depth.md#rule-expansion) at 15 seconds lets it start with fewer (at least 2).

☑️ 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**.

✅ You may now proceed to the next step.

### 2. Find a Match

☑️ Select `Assets/Edgegap/Resources/EdgegapSettings.asset` and tick **Use Edgegap Matchmaker**.

☑️ Paste your **API URL** into **Matchmaker Url** and **Auth Token** into **Matchmaker Auth Token**.

{% 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 client and start it twice. To run two copies on one PC, give each its own profile: `Game.exe -AuthProfile p1` and `Game.exe -AuthProfile p2`.

☑️ Click **Find Match** in the panel on the left of the main menu in both clients.

Status goes through searching, match found and joining, then both players land in Character Select on the same server. Pick heroes and click **Ready** to enter the dungeon together.

🙌 Congratulations on your first matchmade Deployment on Edgegap!

{% hint style="warning" %}
Keep an expansion stage in your matchmaker config. Without one, the matchmaker only makes a [partial match](/learn/matchmaking/matchmaker-in-depth.md#partial-matches) when tickets expire (5 minutes).
{% endhint %}

## 🛑 Server Self-Stop

With or without matchmaking, your server stops its own deployment so idle servers don't cost money:

* when nobody joins within 120 seconds,
* 30 seconds after the last player leaves.

Change these timeouts with **First Player Timeout Seconds** and **Empty Server Timeout Seconds** in `EdgegapSettings.asset`. If the stop request fails, the server quits instead.

## ✏️ Customize Server Build

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

### Run as Dedicated Server

For this sample to run as dedicated server, we made the following changes:

{% tabs %}
{% tab title="Server Initialization" %}
New script (added to your `MainMenu` scene in a new empty `GameObject`):

<pre class="language-csharp" data-title="Assets/Scripts/EdgegapServerStarter.cs" data-line-numbers><code class="lang-csharp">using System;
using System.Collections;
using System.Collections.Generic;
using Unity.BossRoom.ConnectionManagement;
using UnityEngine;

namespace Unity.Multiplayer.Samples.BossRoom
{
    public class EdgegapServerStarter : MonoBehaviour
    {
        // Start is called before the first frame update
        void Start()
        {
            if (Application.isBatchMode)
            {
                ConnectionManager connectionManager = GameObject.Find("ConnectionManager").GetComponent&#x3C;ConnectionManager>();
<strong>                string internalPortAsStr = Environment.GetEnvironmentVariable($"<a data-footnote-ref href="#user-content-fn-1">ARBITRIUM_PORT_GAMEPORT_INTERNAL</a>");
</strong>
                ushort port;
                if (internalPortAsStr == null)
                {
                    // Not on Edgegap (local run or docker run): use EdgegapSettings.ListenPort or -edgegapListenPort.
                    port = EdgegapBootstrap.Settings != null ? EdgegapBootstrap.Settings.ListenPort : (ushort)9998;
                    Debug.LogWarning($"No ARBITRIUM_PORT_GAMEPORT_INTERNAL, not on Edgegap: listening on port {port}.");
                }
                else if (!ushort.TryParse(internalPortAsStr, out port))
                {
                    throw new Exception($"Could not find port mapping, make sure your app version port name matches with \"{portMapName}\"");
                }

                Debug.Log($"Edgegap Server | Starting server on 0.0.0.0:{port}.");
<strong>                connectionManager.StartHostIp("<a data-footnote-ref href="#user-content-fn-2">SERVER</a>", "0.0.0.0", port);
</strong>            }
        }
    }
}
</code></pre>

{% endtab %}

{% tab title="Connection Management" %}
Modified files:

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionMethod.cs"><code class="lang-csharp"><strong>116        public override async Task SetupHostConnectionAsync()
</strong>117        {
<strong>118            <a data-footnote-ref href="#user-content-fn-3">//SetConnectionPayload(GetPlayerId(), m_PlayerName); // Need to set connection payload for host as well, as host is a client too</a>
</strong>119            var utp = (UnityTransport)m_ConnectionManager.NetworkManager.NetworkConfig.NetworkTransport;
120            utp.SetConnectionData(m_Ipaddress, m_Port);
121        }
</code></pre>

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionState/StartingHostState.cs"><code class="lang-csharp"><strong>66        async void StartHost()
</strong>67        {
              ...

72                // NGO's StartHost launches everything
<strong>--                <a data-footnote-ref href="#user-content-fn-3">//if (!m_ConnectionManager.NetworkManager.StartHost())</a>
</strong><strong>73                <a data-footnote-ref href="#user-content-fn-4">if (!m_ConnectionManager.NetworkManager.StartServer())</a>
</strong>74                {
75                    StartHostFailed();
76                }

              ...
</code></pre>

<pre class="language-csharp" data-title="Assets/Scripts/Gameplay/GameplayObjects/Character/ClientCharacter.cs"><code class="lang-csharp"><strong>112        public override void OnNetworkSpawn()
</strong>113        {
               ...

125            m_ServerCharacter.IsStealthy.OnValueChanged += OnStealthyChanged;
126            m_ServerCharacter.MovementStatus.OnValueChanged += OnMovementStatusChanged;
<strong>127            <a data-footnote-ref href="#user-content-fn-3">//OnMovementStatusChanged(MovementStatus.Normal, m_ServerCharacter.MovementStatus.Value);</a>
</strong> 
               ...
</code></pre>

{% endtab %}

{% tab title="Editor/Server Compatibility" %}
Modify files:

<pre class="language-csharp" data-title="Assets/Scripts/ConnectionManagement/ConnectionState/HostingState.cs"><code class="lang-csharp"><strong>148        ConnectStatus GetConnectStatus(ConnectionPayload connectionPayload)
</strong>149        {
               ...
                
<strong>155            <a data-footnote-ref href="#user-content-fn-5">//if (connectionPayload.isDebug != Debug.isDebugBuild)</a>
</strong><strong>156            //{
</strong><strong>157            //    return ConnectStatus.IncompatibleBuildType;
</strong><strong>158            //}
</strong>
               ...
</code></pre>

{% endtab %}
{% endtabs %}

[^1]: default port name on Edgegap is 'gameport'

[^2]: player name for server can be any value

[^3]: p2p only ⇒ comment this out

[^4]: start as dedicated server instead

[^5]: Editor compatibility ⇒ comment this out
