> 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/fishnet-on-edgegap-webgl.md).

# Fishnet WebGL

Use FishNet's Bayou WebSocket transport to run a Unity WebGL multiplayer game on Edgegap game servers.

This guide will help you use [FishNet](https://github.com/FirstGearGames/FishNet)'s Websocket Transport, [Bayou](https://fish-networking.gitbook.io/docs/manual/components/transports/bayou), and create a headless server on Edgegap for a Unity project.

You can find the [final sample project](https://github.com/edgegap/netcode-game-sample-unity-fishnet-hashgrid) on GitHub, under the `Fishnet_v4.1.6R_WebGL_Matchmaker` folder. This guide should work with any version of Fishnet, so long as you use the appropriate version of Bayou for it.

The `Start Game` button in the sample has two modes, selected on the `MatchmakingSystem` gameObject in the `MainMenu` scene:

* **Direct connection (default)** - the client connects to a server address and port you set. Test against a local server first, then against a deployment you start from the Edgegap dashboard. No matchmaker is needed.
* **Edgegap Matchmaker (optional, disabled by default)** - the [Edgegap Matchmaker](/learn/matchmaking.md) groups players, Edgegap starts a dedicated server near them, and every client connects to it automatically.

{% hint style="info" %}
In both modes, **the game client never holds an Edgegap API token**. Matchmaker mode only uses the matchmaker's `Auth Token`, which is safe to ship in game clients. The server stops its own deployment once the match is empty.
{% endhint %}

### Unity requirements

This project is tested on Unity version `2021.3.45f2`, with FishNet `4.1.6R`, the Bayou transport and the Edgegap Unity SDK. You will need the following modules to be installed with it:

* WebGL Build Support;
* Linux Dedicated Server Build Support;
* Linux Build Support (Mono).

You will also need [Docker](https://docs.docker.com/get-docker/) to containerize the server.

{% hint style="warning" %}
The sample needs Edgegap Unity SDK `3.5.6` or later. Before Unity 2022.3, SDK `3.5.5` and earlier form-encode the JSON body of POST requests, and the matchmaker answers every ticket request with HTTP 400. The sample's `Packages/manifest.json` tracks the SDK's `main` branch. If you pin a tag instead, use `#3.5.6` or newer.
{% endhint %}

### Switch the transport

The sample already uses Bayou on port `7770`. To do the same in your own project, download the [Bayou Unity package](https://github.com/FirstGearGames/Bayou/releases) from its GitHub releases. Import the package in the game project with the toolbar under `Asset/Import Package/Custom Package...`.

Open the `MainMenu` scene located under `Assets/SpaceEdge/Scenes`. In the `NetworkManager` gameObject, remove the `Tugboat` transport component. Replace it with the `Bayou` transport, located under `Assets/FishNet/Plugins/Bayou`. Take note of the port value that's set, in this case `7770`.

You don't need FishNet's `Start On Headless` option, and the sample leaves it off. In server builds, the `EdgegapServerLifecycle` component (under `Assets/Edgegap`) starts the FishNet server on `0.0.0.0:7770`, so the same scene works for client and server builds. On Edgegap, it also stops the deployment when nobody joins within 120 seconds, or 30 seconds after the last player leaves.

### Build the game server & Containerizing

Build the server with the `Tools/Edgegap/Build Linux Dedicated Server` toolbar menu. The output goes to `Builds/EdgegapServer/`, which the sample's `Dockerfile` copies into the image.

To test it locally, run it in Docker:

{% code collapsedlinecount="10" %}

```bash
docker build --platform linux/amd64 -t fishnet-server:local .
docker run -p 7770:7770 fishnet-server:local
```

{% endcode %}

The log shows `Edgegap Server | Starting server on 0.0.0.0:7770.` and `Not running on Edgegap, self-stop disabled.`

### Testing the client

#### In the editor or locally

Make a WebGL client build (see [On Itch.io](#on-itch.io) for the build settings), or build it from the command line:

{% code collapsedlinecount="10" %}

```bash
Unity -batchmode -quit -projectPath . -executeMethod EdgegapServerBuild.BuildClient -edgegapClientTarget webgl
```

{% endcode %}

Serve `Builds/Client/webgl` (for example with `python -m http.server 8080`), open `http://localhost:8080` in two tabs, then click on `Start Game`. The `MatchmakingSystem` defaults (`Direct Address` set to `localhost`, `Direct Port` set to `7770`, `Direct Use Wss` off) connect to the local server. Desktop builds (`-edgegapClientTarget win64`) work too, since Bayou supports native clients.

#### On an Edgegap server

Push the image to your [Container Registry](/learn/advanced-features/edgegap-container-registry.md#getting-your-credentials). Your registry project, username and token are on the dashboard's `Container Registry` page.

{% tabs %}
{% tab title="Bash (Linux)" %}

```bash
project='<REGISTRY_PROJECT>';
username='robot$<REGISTRY_ORGANIZATION_ID>+client-push';
token='<REGISTRY_TOKEN>';

echo "$token" | docker login registry.edgegap.com -u "$username" --password-stdin 2>&1
```

{% endtab %}

{% tab title="PowerShell (Windows)" %}

```powershell
$project = '<REGISTRY_PROJECT>'
$username = 'robot$<REGISTRY_ORGANIZATION_ID>+client-push'
$token = '<REGISTRY_TOKEN>'

echo "$token" | docker login registry.edgegap.com -u "$username" --password-stdin 2>&1
```

{% endtab %}
{% endtabs %}

Then build and push the image:

{% code collapsedlinecount="10" %}

```bash
docker build --platform linux/amd64 -t registry.edgegap.com/<project>/fishnet-server:v1 .
docker push registry.edgegap.com/<project>/fishnet-server:v1
```

{% endcode %}

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

In the [Edgegap dashboard](https://app.edgegap.com/application-management/applications/list), create an application and a version for that image:

* [Port](/learn/orchestration/application-and-versions.md#port-mapping): name `gameport`, port `7770`, protocol `WS`, with `TLS Upgrade` enabled. Edgegap terminates TLS, so browsers connect with `wss://` while the server speaks plain `ws` (see [Websockets (WS) and Secure Websockets (WSS)](/learn/orchestration/deployments.md#websockets-ws-and-secure-websockets-wss)).
* Container registry username and token: set them, otherwise deployments fail to pull the image.

Deploy the version from the dashboard. On the `MatchmakingSystem` gameObject, set `Direct Address` to the deployment's host (`<id>.pr.edgegap.net`), `Direct Port` to its external port, and enable `Direct Use Wss`. Rebuild the client and click on `Start Game`.

#### With the Edgegap Matchmaker (optional)

Turn on the matchmaker once direct connection works:

1. In the sample's `matchmaker-config.json`, set `application.name` and `version` to your application and version, and list every origin that serves the WebGL build in [`allowed_cors_origins`](/learn/matchmaking/matchmaker-in-depth.md#cross-origin-resource-sharing-cors).
2. In the dashboard, open [Matchmaker](https://app.edgegap.com/matchmaker-management-v2/matchmakers/list), click `Create Matchmaker`, upload the file, then click `Create and Start`.
3. In the `MainMenu` scene, copy the matchmaker's `API URL` and `Auth Token` into the `Base Url` and `Auth Token` fields of the `Edgegap Matchmaking Client` component on the `Edgegap` gameObject. Then enable `Use Edgegap Matchmaker` on the `MatchmakingSystem` gameObject.
4. Rebuild the client, serve it from an origin listed in `allowed_cors_origins` (for example `http://localhost:8080`), open two tabs and click on `Start Game` in both.

The client creates a ticket, waits for a server to be assigned, then connects with `wss://`. The `casual` profile matches 2 to 4 players. It also lowers the minimum to 1 player after 30 seconds, but a lone player may only get a server when the ticket nears its 5-minute expiration, so test with two clients.

To ship a new server build: build the server, push a new tag, create a new app version, then in the matchmaker click `Stop`, edit `application.version` and click `Start` (see [Rolling Updates](/learn/matchmaking/matchmaker-in-depth.md#rolling-updates-and-ab-tests)).

{% hint style="info" %}
WebGL clients can't ping [latency beacons](/learn/orchestration/ping-beacons.md), so the sample's matchmaker profile has no `latencies` rule and Edgegap places the server based on the players' IP addresses. See [Matchmaking](/learn/matchmaking.md) to learn more.
{% endhint %}

#### On Itch.io

Once you've confirmed the game client works locally, it should also work in Itch.io. To upload your game there, you will need to make a client build in the `Build` screen of the Unity Editor, under `File -> Build Settings`. Select the following options:

* Set `Platform` to `WebGL`;
* Open up the `Player Settings`. Under `Player -> Publishing Settings`, set the `Compression Format` to `Gzip` and make sure the `Decompression Fallback` option is selected. Save these settings, and close the window.

<figure><img src="https://3027732442-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FsR0dHSFv9ymoC0DO5G8J%2Fuploads%2Fgit-blob-d31291cf36a863193c24a856718a791db0c61577%2Fbuild-settings.png?alt=media" alt=""><figcaption></figcaption></figure>

Then press build and select a new empty folder named `build` as the file destination. Compress the files of this `build` folder into a zip folder, making sure that they are at the root of it. Then upload the zip folder to your Itch project, and make sure to select the option for the file to play in the browser. The game should then work the same way as locally. In matchmaker mode, add your Itch.io page's origin to `allowed_cors_origins` first.
