# Интеграция игры

### Зачем мне нужен подборщик матчей (Matchmaker) и/или Лобби?

Служба лобби ([#nakama-by-heroic-labs](https://docs.edgegap.com/ru/advanced-features/managed-clusters#nakama-by-heroic-labs "mention")) играет решающую роль в онлайн-многопользовательских играх по различным техническим причинам.

**Управление игроками**

Система лобби или подборщика матчей помогает управлять игроками, группируя их в подходящие матчи на основе различных критериев, таких как уровень навыков, географическое расположение или игровые предпочтения. Это обеспечивает сбалансированный и приятный игровой опыт для всех участвующих игроков.

**Бэкенд**

Вы не хотите, чтобы ваш игрок напрямую связывался с вашим бэкендом, и вы хотите предоставить централизованную точку доступа. Представьте, что у вас есть API-токен, который запускает виртуальную машину у определённого провайдера — вы не хотите, чтобы токен был захардкожен в клиенте игры и доступен любому игроку!

**Безопасность**

Использование централизованной системы лобби или подборщика матчей помогает поддерживать контроль над безопасностью игры. Фильтруя и проверяя подключения игроков, подборщик может предотвращать несанкционированный доступ, защищать данные пользователей и снижать вероятность мошенничества или взломов.

**Управление игровыми сессиями**

Система лобби или подборщика матчей управляет игровыми сессиями, создавая, обновляя и завершая их по мере необходимости. Это гарантирует, что игроки могут беспрепятственно присоединяться к играм или покидать их, а экземпляры игры могут эффективно управляться системой.

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

### Вам не нужно это прямо сейчас! (Режим разработки)

На этапе разработки вам может не понадобиться полнофункциональная система лобби или подборщика матчей по нескольким техническим причинам:

**Упрощённая тестовая среда**

На начальных этапах разработки вы можете сосредоточиться на реализации и тестировании основных игровых механик и сетевых функций. Избегая сложности лобби или подборщика матчей, вы можете проще тестировать и отлаживать игру с небольшой группой игроков, часто в составе вашей команды разработчиков.

**Быстрая итерация**

Без лобби или подборщика матчей вы можете быстро вносить изменения в код игры и тестировать новые функции или исправления, не беспокоясь о влиянии на процесс матчмейкинга. Это позволяет применять более гибкий подход к разработке, позволяя вашей команде быстрее итеративно улучшать игровые механики.

**Распределение ресурсов**

Разработка полнофункциональной системы лобби или подборщика матчей требует значительного времени и ресурсов. Отложив реализацию этих компонентов, вы сможете более эффективно распределить ресурсы, сначала сосредоточившись на основной игровой механике и сетевых функциях.

**Проблемы масштабируемости**

На ранних этапах разработки у вас, вероятно, не будет большого числа игроков для управления. В результате может быть достаточно базовой системы для подключения игроков или даже ручных подключений для целей тестирования. Однако по мере роста вашей игры и привлечения большего числа игроков система лобби или подборщик матчей станет всё более необходимой для управления подключениями игроков и обеспечения плавного игрового процесса.

Крайне важно спроектировать архитектуру игры с учётом возможности интеграции системы лобби или подборщика матчей в будущем. Такой дальновидный подход облегчит интеграцию этих компонентов, когда вы будете готовы тестировать игру с более крупной группой игроков, обеспечив более плавный переход и более отполированный конечный продукт.

### Взаимодействие с API Edgegap

Edgegap предлагает простой API для взаимодействия с сессиями реле, чтобы авторизовать ваших игроков подключаться к ближайшему реле для низкой задержки.

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

Для взаимодействия с API вам потребуется указать `токен в заголовке авторизации` в ваших запросах.

{% hint style="info" %}
Вы можете получить токен, зарегистрировав аккаунт и создав профиль реле. [**Зарегистрируйтесь здесь**](https://app.edgegap.com/auth/register)

Обратите внимание, что вам нужен токен, предназначенный исключительно для реле. Если у вас уже есть стандартный API-токен, он не подойдёт.
{% endhint %}

Этот параметр доступен в боковой панели на панели управления.

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

После получения токена включите его в заголовки запросов для всех вызовов API. Не забудьте добавить префикс к вашему API-ключу `token`.

{% hint style="success" %}
Если вы не знакомы с работой с API, вы можете обратиться к [этому разделу](#interacting-with-api-curl-or-postman) чтобы помочь вам начать.
{% endhint %}

### Создание сессии реле

Чтобы использовать распределённое реле, вам нужно создать сессию реле. Это делается путём отправки `POST` запроса на `/v1/relays/sessions` эндпоинт с JSON-пayload. Сессии могут создаваться вручную, с помощью подборщика матчей, через лобби или через вашу собственную пользовательскую службу.

Это динамически выберет лучшее доступное реле для ваших игроков в реальном времени и создаст авторизацию для доступа к реле. Однако мы рекомендуем предварительно фильтровать матчмейкинг, чтобы группировать игроков в рамках одного региона, чтобы избежать экстремальных расстояний между ними.

{% hint style="info" %}
Этот шаг не подключит ваших игроков непосредственно к реле; это нужно будет сделать на более позднем этапе процесса. Этот шаг только предоставляет вам реле, с которым следует установить соединение.
{% endhint %}

Пример запроса:

```bash
POST - /v1/relays/sessions
```

В теле должен содержаться массив объектов пользователей, где каждый объект содержит IP-адрес пользователя. Вы также можете включить `webhook_url`, который представляет собой URL, который будет получать уведомления, связанные с сессией.

Пример полезной нагрузки:

```json
{
  "users": [
    {
      "ip": "1.1.1.1"
    },
    {
      "ip": "2.2.2.2"
    }
  ],
  "webhook_url": "https://webhook.example.com/notify"
}
```

{% hint style="info" %}
Где `"users"` — массив объектов пользователей, содержащих `"ip"` каждого пользователя в сессии, и `"webhook_url"` — это необязательный URL для получения обновлений о сессии.

После отправки `POST` запроса вы получите JSON-ответ, содержащий `session_id`, который потребуется для последующего получения информации о сессии.
{% endhint %}

Пример ответа:

```json
{
  "session_id": "3960c873aafd-S",
  "authorization_token": null,
  "status": "Initializing",
  "ready": false,
  "linked": false,
  "error": null,
  "session_users": [],
  "relay": null,
  "webhook_url": "https://webhook.example.com/notify"
}
```

### Получение информации о сессии реле

Для получения информации о сессии отправьте `GET` запроса на `/v1/relays/sessions/{session_id}` эндпоинт с `session_id` полученным из `POST` запроса.

Пример запроса:

```bash
GET - /v1/relays/sessions/3960c873aafd-S
```

Ответ будет содержать информацию о сессии, включая статус сессии, информацию о пользователях и сведения о реле.

Ожидаемый ответ будет содержать информацию о сессии, примерно такой:

```json
{
  "session_id": "3960c873aafd-S",
  "authorization_token": 1031196689,
  "status": "Linked",
  "ready": true,
  "linked": true,
  "error": null,
  "session_users": [
    {
      "ip_address": "2.2.2.2",
      "latitude": 48.8602294921875,
      "longitude": 2.34106993675232,
      "authorization_token": 3499933322
    },
    {
      "ip_address": "1.1.1.1",
      "latitude": -37.7036018371582,
      "longitude": 145.180633544922,
      "authorization_token": 4261594560
    }
  ],
  "relay": {
    "ip": "178.79.131.238",
    "host": "cc84b011777b.pr.edgegap.net",
    "ports": {
      "server": {
        "port": 31527,
        "protocol": "UDP",
        "link": "cc84b011777b.pr.edgegap.net:31527"
      },
      "client": {
        "port": 32089,
        "protocol": "UDP",
        "link": "cc84b011777b.pr.edgegap.net:32089"
      }
    }
  },
  "webhook_url": "https://webhook.example.com/notify"
}
```

Получение авторизации к ближайшему реле может занять небольшое время, поэтому когда поле `ready` будет `true` вы можете извлечь JSON-данные для использования.

[Подробнее здесь](https://docs.edgegap.com/ru/docs/api)

{% hint style="success" %}
В качестве альтернативы вы можете использовать параметр \`webhook\_url\` при создании сессии реле, чтобы получать уведомления о том, что сессия либо успешно назначена реле, либо не смогла этого сделать.
{% endhint %}

### Завершение сессии реле

Когда вы решите прекратить подключение к реле, вы можете легко завершить сессию реле. Вам нужно отправить `DELETE` запрос на следующий эндпоинт:

```bash
DELETE - /v1/relays/sessions/{session_id}
```

{% hint style="info" %}
Замените `{session_id}` на фактический идентификатор сессии, которую вы хотите завершить.
{% endhint %}

Ответ со статусом 204 No Content означает, что сессия успешно удалена. Игроки потеряют доступ к реле, и вам больше не будут начисляться расходы за эту сессию.

{% hint style="warning" %}
Важно правильно завершать сессии, чтобы избежать оставления неиспользуемых выделенных ресурсов, что может повлиять на производительность и повлечь за собой лишние расходы.
{% endhint %}

***

### Пример на C\#

Это пример на C#, который ваше Лобби или Подборщик матчей мог бы выполнить для создания сессии реле и извлечения данных, которые вам нужно вернуть клиенту игры.

{% hint style="info" %}
Этот пример использует C# версии > 7.0
{% endhint %}

```csharp
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace LobbyNamespace
{
    public class EdgegapAPI
    {
        private readonly HttpClient _httpClient;

        public EdgegapAPI(string apiToken)
        {
            _httpClient = new HttpClient();
            _httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {apiToken}");
        }

        public async Task<RelaySessionAuthorization> CreateRelaySession(List<string> ips)
        {
            var jsonBody = new
            {
                users = ips.ConvertAll(ip => new { ip })
            };

            var postResponse = await _httpClient.PostAsJsonAsync("https://api.edgegap.com/v1/relays/sessions", jsonBody);
            var postJsonData = await postResponse.Content.ReadFromJsonAsync<Dictionary<string, object>>();

            if (!postJsonData.ContainsKey("session_id"))
            {
                throw new Exception("Failed to create relay session");
            }

            var sessionId = postJsonData["session_id"].ToString();

            var retries = 5;
            while (retries > 0)
            {
                var getResponse = await _httpClient.GetAsync($"https://api.edgegap.com/v1/relays/sessions/{sessionId}");
                var getJsonData = await getResponse.Content.ReadFromJsonAsync<Dictionary<string, object>>();
                var ready = (JsonElement)getJsonData["ready"];

                if (ready.ValueKind == JsonValueKind.True)
                {
                    var usersData = (JsonElement)getJsonData["session_users"];
                    var userAuthorizationTokens = new Dictionary<string, UInt32>();
                    foreach (var user in usersData.EnumerateArray())
                    {
                        var ipAddress = user.GetProperty("ip_address").GetString();
                        var userAuthorizationToken = user.GetProperty("authorization_token").GetUInt32();
                        userAuthorizationTokens[ipAddress] = userAuthorizationToken;
                    }

                    var relayData = (JsonElement)getJsonData["relay"];
                    var relayAddress = relayData.GetProperty("host").GetString();
                    var relayServerPort = relayData.GetProperty("ports").GetProperty("server").GetProperty("port").GetInt32();
                    var relayClientPort = relayData.GetProperty("ports").GetProperty("client").GetProperty("port").GetInt32();

                    return new RelaySessionAuthorization
                    {
                        SessionAuthorizationToken = sessionId,
                        UserAuthorizationTokens = userAuthorizationTokens,
                        RelayAddress = relayAddress,
                        RelayServerPort = relayServerPort,
                        RelayClientPort = relayClientPort
                    };
                }

                retries--;
                await Task.Delay(2000); // wait for 2 second before retrying
            }

            throw new Exception("Failed to get a ready relay session");
        }
    }

    public class RelaySessionAuthorization
    {
        public string? SessionAuthorizationToken { get; set; }
        public Dictionary<string, UInt32>? UserAuthorizationTokens { get; set; }
        public string? RelayAddress { get; set; }
        public int RelayServerPort { get; set; }
        public int RelayClientPort { get; set; }
    }
}
```

Вам нужно будет вернуть эту информацию соответствующему игроку.

Игрок, выступающий в роли сервера, будет нуждаться в `RelayServerPort` а все остальные будут нуждаться в `RelayClientPort`

```csharp
using System.Text.Json;
using LobbyNamespace;

class Program
{
    static async Task Main(string[] args)
    {
        // Замените YOUR_API_TOKEN на ваш реальный API-токен
        var apiToken = "YOUR_API_TOKEN";

        // 1.1.1.1 — ваш игрок, выступающий в роли сервера
        // 2.2.2.2 — ваш игрок, выступающий в роли клиента
        var ips = new List<string> { "1.1.1.1", "2.2.2.2" };

        var edgegapAPI = new EdgegapAPI(apiToken);
        var relaySessionAuthorization = await edgegapAPI.CreateRelaySession(ips);

        // Сформировать JSON-тело для сервера (1.1.1.1)
        var serverJsonBody = new
        {
            relay_address = relaySessionAuthorization.RelayAddress,
            port = relaySessionAuthorization.RelayServerPort,
            session_authorization_token = relaySessionAuthorization.SessionAuthorizationToken,
            user_authorization_token = relaySessionAuthorization.UserAuthorizationTokens["1.1.1.1"]
        };
        var serverJson = JsonSerializer.Serialize(serverJsonBody);
        Console.WriteLine($"JSON body for server (1.1.1.1):\n{serverJson}");

        // Сформировать JSON-тело для клиента (2.2.2.2)
        var clientJsonBody = new
        {
            relay_address = relaySessionAuthorization.RelayAddress,
            port = relaySessionAuthorization.RelayClientPort,
            session_authorization_token = relaySessionAuthorization.SessionAuthorizationToken,
            user_authorization_token = relaySessionAuthorization.UserAuthorizationTokens["2.2.2.2"]
        };
        var clientJson = JsonSerializer.Serialize(clientJsonBody);
        Console.WriteLine($"JSON body for client (2.2.2.2):\n{clientJson}");
    }
}
```

### Взаимодействие с API (cURL или POSTMAN)

Во время разработки вашей игры вам потребуется взаимодействовать с API для создания, управления и удаления сессий реле. Вы можете использовать инструменты, такие как cURL или POSTMAN, чтобы отправлять HTTP-запросы к API.

cURL — это инструмент командной строки, который позволяет выполнять HTTP-запросы и взаимодействовать с API прямо из терминала. POSTMAN — популярное GUI-приложение, которое упрощает тестирование API, предоставляя удобный интерфейс для создания, отправки и анализа HTTP-запросов.

Вот пример того, как создать сессию реле с помощью cURL:

```bash
curl -X POST -H "Content-Type: application/json" -H "Authorization: Token API_TOKEN" -d '{
  "users": [
    {
      "ip": "1.1.1.1"
    },
    {
      "ip": "2.2.2.2"
    }
  ],
  "webhook_url": "https://webhook.example.com/notify"
}' "https://api.edgegap.com/v1/relays/sessions"
```

{% hint style="info" %}
Не забудьте заменить `API_TOKEN` на ваш реальный relay API-токен.
{% endhint %}

API вернёт вам ответ, содержащий данные. Вам потребуется распарсить этот ответ и извлечь необходимые данные.
