> 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/unity/server-browser.md).

# Server Browser

This SDK is an optional starter kit for Unity users, which can be extended and customized later.

## 💡 Features

Gain access to prebuilt automated features by installing our SDK:

{% columns %}
{% column %}

* Complete Examples
* Lifecycle Management
* Capacity Management
  {% endcolumn %}

{% column width="33.33333333333333%" %}

* Filter Query Compiler
* Type Definitions (C#)
* Local Dev Testing
  {% endcolumn %}

{% column width="33.33333333333333%" %}

* Cross-Platform
* Easy to Customize
* Automated Retry
  {% endcolumn %}
  {% endcolumns %}

## ✔️ Preparation

Unity SDK contains optional integration utilities for Deployments, Matchmaking, and Server Browser. This plugin officially supports Unity versions 2021.3.0f1 and later.

{% hint style="success" %}
This plugin is provided 100% free of charge, under Terms and Conditions of Free Tier.
{% endhint %}

#### Requirements

<details>

<summary>Install a Git Client (for example <a href="https://git-scm.com/">git-scm</a>)</summary>

A git client is needed for Unity to download and install our Unity package automatically. You will not need to use git directly once it's installed.

</details>

#### Installation

1. Open your Unity Project,
2. Select `Window > Package Management > Package Manager` ,
3. Click the :heavy\_plus\_sign: icon and select `Add package from git URL...` ,
4. Input the URL of our SDK when prompted:

{% code title="" %}

```
https://github.com/edgegap/edgegap-unity-sdk.git
```

{% endcode %}

5. Click `Add`  and wait until installation is completed.

#### Import Samples

This package includes multiple samples, intended to be used individually (do not combine samples).

#### Verified Sources

This is the only official distribution channel for this SDK, do not trust unverified sources!

#### Update Package

Navigate to Edgegap SDK in Unity Package Manager and click `Update` .

{% hint style="warning" %}
**Imported Samples are not updated automatically!** Back up any custom property values, delete the sample scripts used in your scene currently and re-import samples.
{% endhint %}

{% hint style="info" %}
Some releases may contain breaking changes. This will be indicated by a new MAJOR version.
{% endhint %}

#### Updating to v3

This update includes many new [Server Browser](/unity/server-browser.md) utilities and examples, improves matchmaking error handling, and more. See [Release Notes](/docs/release-notes.md) for a complete list.

{% hint style="warning" %}
Unity SDK update v3 includes a few breaking changes. Please re-test your integration carefully.
{% endhint %}

## 🍀 Getting Started

This guide assumes basic knowledge of [Server Browser](/learn/server-browser.md) concepts and a running Server Browser.

{% hint style="success" %}
**We strongly recommend importing our Auto-Assign Example** to follow along code as you read this document. You can do so in `Unity Package Manager > Edgegap SDK > Samples` .
{% endhint %}

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

### Overview

Our SDK utilizes heavily [Dependency Injection](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection/overview#the-concept) and [Observer](https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern) programming patterns.

{% hint style="info" %}
This package integrates both [Server Browser](/learn/server-browser.md) and [Matchmaking](/learn/matchmaking.md), which can be used together or separately. You may freely reuse any scripts for your own customized forks and integrations.
{% endhint %}

This package includes:

* Runtime Files - will be compiled and bundled with your client and server builds:
  * Service-Specific Utilities:
    * [#server-agent](#server-agent "mention") - a complete server integration to reuse/extend.
    * [#client-agent](#client-agent "mention") - a complete client integration to reuse/extend.
    * API functions - endpoint definitions, error handling, and logging automations.
    * Filter Compiler - strongly typed utilities for building filter queries.
  * Service-Specific DTOs[^1] - typed data containers for Server Browser API.
  * Shared Utilities - logging, HTTP, ping, observables, etc...
  * Shared DTOs[^1] - used by multiple Edgegap services to pass data around.
* Sample Files - bundled and compiled ONLY if imported in your project:
  * [#auto-assign](#auto-assign "mention") - example handlers with auto-assigned reservations,
  * [#custom-search](#custom-search "mention") - example handlers with manual instance choice.

### Server Agent

**Server Lifecycle and Capacity management** are performed by Server Agent.

Once instantiated, agent's **parent Monobehaviour (handler) must initialize the agent** and provide:

* `onMonitorUpdate`  callback - observe service health changes,
* `onInstanceUpdate`  callback - observe and react to instance and slot changes,
* `onConfirmationsUpdate`  callback - observe and react to federated authentication.

Once initialized, this agent will automatically provide validations and hook up logging observers, finishing with a single call to the monitoring API endpoint to indicate service health.

Agent's handler is expected to take control and call agent functions from this point:

* `DiscoverInstance`  to create initial Server Instance and Slots and initiate heartbeat,
* `DeleteInstance`  once the match concludes / to prevent new players from joining,
* `ConfirmReservation`  when players join, to verify their identity and slot assignment,
* `UpdateSlot`  to update slot capacity (on player joined/abandoned) or modify metadata,
* `UpdateInstance`  to modify instance metadata,
* `Status`  to verify Server Browser service health.

{% hint style="success" %}
Confirmations and Slot/Instance Updates are **queued and performed in batches by default** (Heartbeat Mode) to maximize scalability. To iterate quicker during dev testing, use Greedy Mode.
{% endhint %}

{% hint style="warning" %}
**When updating metadata, all indices must be defined.** To unset non-indexed keys, simply omit them.
{% endhint %}

The agent automatically maintains a heartbeat to keep the server discoverable while running. If the agent can't reach your server browser for several consecutive heartbeats (configurable):

* less than maximum - instance will be automatically re-discovered,
* more than maximum - instance will be automatically deleted.

When a new player connection is established, player is expected to send their reservation ID (third party player ID) to the game server using your netcode, to perform reservation confirmation.

Once `onConfirmationsUpdate`  triggers, handler must perform additional actions:

* call `UpdateSlot`  to reduce available seats for any slots with confirmed reservations,
* accept or deny connection using netcode-specific methods.

When a player abandons game, handler is expected to increase available seats for this slot.

{% hint style="info" %}
Allow a short time period for players to reconnect before abandoning in case of unexpected crashes.
{% endhint %}

### Client Agent

**Searching for instances, pagination, filtering, and reservations** are performed by Client Agent.

Once instantiated, agent's **parent Monobehaviour (handler) must initialize the agent** and provide:

* `onMonitorUpdate`  callback - observe service health changes,
* `onInstancesUpdate`  callback - observe and react to instance list changes.

Once initialized, this agent will automatically provide validations and hook up logging observers, finishing with a single call to the monitoring API endpoint to indicate service health.

Agent's handler is expected to take control and call agent functions from this point:

* `ReserveSeats`  to create a capacity reservation for a particular instance/slot or auto-assign,
* `ListInstances`  to list instances with a specific filter, ordering, cursor, and page size,
* `GetNextPage`  to fetch more instances with the current parameters (filters, etc),
* `RefreshList`  to clear cache and reload first page, or refresh with a specific cursor,
* `GetInstanceDetails`  to fetch instance metadata and slots information for a specific instance,
* `Status`  to verify Server Browser service health.

When a new player connection is established, player is expected to send their reservation ID (third party player ID) to the game server using your netcode, to perform reservation confirmation.

{% hint style="success" %}
Save connection details in client or game backend to reconnect in case of an unexpected crash.
{% endhint %}

## 🧪 Samples

Get started with samples including a complete functioning integration for both server and client.

### Auto-Assign

Utilizes auto-assigned reservations, with client specifying only policy name. Server browser chooses an instance matching policy filter and slot with sufficient seats automatically.

### Custom Search

Includes a full implementation demonstrating how to search instances and slots, hook up UI elements, and let player choose where they wish to reserve capacity manually.

## ⚙️ Customization

This SDK is meant to be extended and modified, though some modifications may be risky:

✅ Handler - safely hook up UI observers and perform minor additions or modifications,

⚠️ Agent - modify lifecycle and capacity management at your own risk,

⚠️ API - write your own integration from scratch, using cherry-picked utilities.

Handlers may observe any events emitted by Server and Client agents as described below.

{% hint style="warning" %}
Make sure to get familiar with [Server Browser In-Depth](/learn/server-browser.md) concepts before making customizations.
{% endhint %}

{% hint style="info" %}
If you need help, [please reach out to us over Discord](https://discord.gg/MmJf8fWjnt). For live games support see our [ticketing system](https://edgegap.atlassian.net/servicedesk/customer/portal/3).
{% endhint %}

### Server Events

Server Agent emits events (actions) for parent handler to observe and consume.

{% hint style="success" %}
Read event payloads by accessing  `.Current` state of any observable. 🔴 `Error` events contain full error message delimited by a newline character after the main event message.
{% endhint %}

Preview events emitted by observable `Monitor` :

<table data-full-width="true"><thead><tr><th width="125">Action Type</th><th width="450">Event Message</th><th>Description</th></tr></thead><tbody><tr><td>🟢 <code>Update</code> </td><td><code>healthy</code></td><td>All systems OK.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>unhealthy</code></td><td>Unexpected issue.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>get monitor failed</code></td><td>Misconfiguration or unexpected issue.</td></tr><tr><td>🟡 <code>Warn</code></td><td><code>request timeout clamped to heartbeat [{timeout}]</code></td><td>Prevents race conditions.</td></tr></tbody></table>

Preview events (actions) emitted by observable `Instance`:

<table data-full-width="true"><thead><tr><th width="125">Action Type</th><th width="450">Event Message</th><th>Description</th></tr></thead><tbody><tr><td>🟢 <code>Update</code> </td><td><code>discovered</code></td><td><a href="/pages/UkbJIQ8RFaAL5RDNZsvm#discover-instance">Instance Discovery</a> completed OK. May be triggered if instance lost connection due to temporary condition and re-discovered.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>discovery duplicate</code></td><td>Instance with this Request ID is already discovered.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>discovery failed</code></td><td>Unexpected issue with discovery.</td></tr><tr><td>🔵 <code>Notify</code></td><td><code>heartbeat ok</code></td><td>Heartbeat completed OK.</td></tr><tr><td>🟡 <code>Warn</code></td><td><code>heartbeat failed [{consecutive}/{maximum}]</code></td><td>Heartbeat failed, server couldn't reach Server Browser.</td></tr><tr><td>🔵 <code>Notify</code></td><td><code>instance update enqueued</code></td><td>Enqueued instance update for next batch (heartbeat/greedy).</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance updated</code></td><td>Instance metadata updated OK.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>instance update failed, enqueuing for retry</code></td><td>Instance update failed, possibly due to rate limiting or error.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance deleted</code></td><td>Instance is no longer discoverable by players.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance delete failed (not found)</code></td><td>Instance may have expired due to too many missed heartbeats.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>instance delete failed</code></td><td>Failed to delete Instance, possibly due to rate limiting or error.</td></tr><tr><td>🔵 <code>Notify</code></td><td><code>slot update enqueued [{slot}]</code></td><td>Enqueued slot update for next batch (heartbeat/greedy).</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>slot updated [{slot}]</code></td><td>Slot seats capacity and/or metadata updated OK.</td></tr><tr><td>🟡 <code>Warn</code></td><td><code>agent throttled concurrent slot update</code></td><td>Prevented attempt at concurrent update (race condition).</td></tr><tr><td>🔴 <code>Error</code></td><td><code>slot update failed (not found) [{slot}]</code></td><td>Slot with this name is not defined for this Instance yet.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>slot update failed (not enough seats) [{slot}]</code></td><td>Slot update tried to reduce available seats below zero.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>slot update failed, enqueuing for retry [{slot}]</code></td><td>Slot update failed, possibly due to rate limiting or error.</td></tr></tbody></table>

Preview events (actions) emitted by observable `Confirmations`:

<table data-full-width="true"><thead><tr><th width="125">Action Type</th><th width="450">Event Message</th><th>Description</th></tr></thead><tbody><tr><td>🔵 <code>Notify</code></td><td><code>enqueued [{player}]</code></td><td>Enqueued confirmation for next batch (heartbeat/greedy).</td></tr><tr><td>🟡 <code>Warn</code></td><td><code>duplicate [{player}]</code></td><td>Prevented attempt at duplicated confirmation (already queued).</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>confirmed</code></td><td>Confirmed reservations for individual slots, includes also expired and unknown player IDs for handler to resolve (accept/kick).</td></tr><tr><td>🔴 <code>Error</code></td><td><code>failed</code></td><td>Unexpected issue with confirmations. Check service status.</td></tr></tbody></table>

### Client Events

Client Agent emits events (actions) for parent handler to observe and consume.

{% hint style="success" %}
Read event payloads by accessing  `.Current` state of any observable. 🔴 `Error` events contain full error message delimited by a newline character after the main event message.
{% endhint %}

Preview events emitted by observable `Monitor` :

<table data-full-width="true"><thead><tr><th width="125">Action Type</th><th width="450">Event Message</th><th>Description</th></tr></thead><tbody><tr><td>🟢 <code>Update</code> </td><td><code>healthy</code></td><td>All systems OK.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>unhealthy</code></td><td>Unexpected issue.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>get monitor failed</code></td><td>Misconfiguration or unexpected issue.</td></tr></tbody></table>

Preview events emitted by observable `Instances`:

<table data-full-width="true"><thead><tr><th width="125">Action Type</th><th width="450">Event Message</th><th>Description</th></tr></thead><tbody><tr><td>🔵 <code>Notify</code></td><td><code>seats reserved</code></td><td>Seat reservation OK.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>seats reservation failed (not found)</code></td><td><a data-mention href="#auto-assign">#auto-assign</a> - policy name not found (deleted or inactive).<br><a data-mention href="#custom-search">#custom-search</a> - instance or slot not found.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>seats reservation failed (reached capacity)</code></td><td><a data-mention href="#auto-assign">#auto-assign</a> - policy reached maximum capacity.<br><a data-mention href="#custom-search">#custom-search</a> - slot reached maximum capacity.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>seats reservation failed</code></td><td>Seat reservation failed, possibly invalid policy, request ID, or slot ID.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance list retrieved</code></td><td>Fetched list of instances OK.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance list next page retrieved</code></td><td>Fetched next page of instances OK.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>instance list last page reached</code></td><td>Failed to fetch next page, try refreshing or changing filters.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>instance list next page retrieval failed</code></td><td>Failed to fetch next page, possibly due to an invalid cursor.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance details retrieved</code></td><td>Fetched details of a listed instance OK.</td></tr><tr><td>🟢 <code>Update</code> </td><td><code>instance not cached, prepending</code></td><td>Fetched details of instance outside current list.</td></tr><tr><td>🔴 <code>Error</code></td><td><code>instance details retrieval failed</code></td><td>Failed to fetch details, possibly due to invalid request ID.</td></tr></tbody></table>

[^1]: Data Transfer Object
