# Intégration du jeu

### Pourquoi ai-je besoin d'un entremetteur (Matchmaker) et/ou d'un Lobby ?

Un service de lobby ([#nakama-by-heroic-labs](https://docs.edgegap.com/docs.edgegap.com-fr/advanced-features/managed-clusters#nakama-by-heroic-labs "mention")) joue un rôle crucial dans les jeux multijoueur en ligne pour diverses raisons techniques.

**Gestion des joueurs**

Un système de lobby ou d'entremetteur aide à gérer les joueurs en les regroupant dans des parties appropriées en fonction de différents critères tels que le niveau de compétence, la localisation géographique ou les préférences de jeu. Cela garantit une expérience de jeu équilibrée et agréable pour tous les joueurs impliqués.

**Backend**

Vous ne voulez pas que votre joueur communique directement avec votre backend et vous souhaitez offrir un point centralisé auquel il puisse accéder. Imaginez que vous avez un jeton API qui démarre une VM chez un certain fournisseur : vous ne voulez pas que le jeton soit intégré en dur dans le client du jeu, accessible à tous les joueurs !

**Sécurité**

L'utilisation d'un système centralisé de lobby ou d'entremetteur aide à maintenir le contrôle sur la sécurité du jeu. En filtrant et en validant les connexions des joueurs, un entremetteur peut empêcher les accès non autorisés, protéger les données des utilisateurs et atténuer les tentatives de triche ou de piratage.

**Gestion des sessions de jeu**

Un système de lobby ou d'entremetteur gère les sessions de jeu en les créant, en les mettant à jour et en les terminant selon les besoins. Cela garantit que les joueurs peuvent rejoindre ou quitter les parties sans heurts et que les instances de jeu peuvent être efficacement gérées par le système.

<figure><img src="https://3008966946-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>

### Vous n'en avez pas besoin tout de suite ! (Mode développement)

Pendant la phase de développement, vous n'avez peut-être pas besoin d'un système complet de lobby ou d'entremetteur pour plusieurs raisons techniques :

**Environnement de test simplifié**

Aux premières étapes du développement, vous pouvez vous concentrer sur la mise en œuvre et le test des mécaniques de jeu principales et des fonctionnalités réseau. En évitant la complexité d'un système de lobby ou d'entremetteur, vous pouvez tester et déboguer votre jeu plus facilement avec un groupe plus restreint de joueurs, souvent au sein de votre équipe de développement.

**Itération plus rapide**

Sans lobby ni entremetteur, vous pouvez apporter rapidement des modifications à votre code de jeu et tester de nouvelles fonctionnalités ou corrections sans vous soucier de l'impact sur le processus de matchmaking. Cela permet une approche de développement plus agile, permettant à votre équipe d'itérer et d'affiner plus rapidement les mécaniques de jeu.

**Allocation des ressources**

Développer un système de lobby ou d'entremetteur à grande échelle nécessite beaucoup de temps et de ressources. En retardant la mise en œuvre de ces composants, vous pouvez allouer vos ressources plus efficacement, en vous concentrant d'abord sur le gameplay et les fonctionnalités réseau de base.

**Préoccupations de scalabilité**

Aux premiers stades du développement, vous n'aurez probablement pas un grand nombre de joueurs à gérer. Par conséquent, un système basique de connexion des joueurs ou même des connexions manuelles pour les tests peut être suffisant. Cependant, à mesure que votre jeu grandit et attire plus de joueurs, un système de lobby ou d'entremetteur deviendra de plus en plus nécessaire pour gérer les connexions et assurer une expérience de jeu fluide.

Il est crucial de concevoir l'architecture de votre jeu pour pouvoir accueillir un système de lobby ou d'entremetteur à l'avenir. Cette approche prospective facilitera l'intégration de ces composants lorsque vous serez prêt à tester votre jeu avec un plus grand nombre de joueurs, garantissant une transition plus douce et un produit final plus abouti.

### Interagir avec l'API d'Edgegap

Edgegap propose une API simple pour interagir avec les sessions de relais afin d'autoriser vos joueurs à se connecter au relais le plus proche pour une faible latence.

<figure><img src="https://3008966946-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>

Pour interagir avec l'API, vous devrez fournir un `jeton d'en-tête d'autorisation` dans vos requêtes.

{% hint style="info" %}
Vous pouvez obtenir un jeton en vous inscrivant pour un compte et en générant un profil de relais. [**Inscrivez-vous ici**](https://app.edgegap.com/auth/register)

Veuillez noter que vous avez besoin d'un jeton exclusivement destiné aux relais. Si vous disposez déjà d'un jeton API standard, il ne fonctionnera pas.
{% endhint %}

Ceci est accessible depuis la barre latérale du tableau de bord.

<figure><img src="https://3008966946-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>

Une fois que vous avez le jeton, incluez-le dans les en-têtes de requête pour tous les appels API. N'oubliez pas de préfixer votre clé API par `jeton`.

{% hint style="success" %}
Si vous n'êtes pas familier avec le travail avec une API, vous pouvez vous référer à [cette section](#interacting-with-api-curl-or-postman) pour vous aider à démarrer.
{% endhint %}

### Créer une session de relais

Pour utiliser le relais distribué, vous devez créer une session de relais. Cela se fait en envoyant une `requête POST` au `/v1/relays/sessions` endpoint avec une charge utile JSON. Les sessions peuvent être créées manuellement, avec un entremetteur, avec un lobby ou avec votre propre service personnalisé.

Cela sélectionnera dynamiquement le meilleur relais disponible pour vos joueurs en temps réel et créera une autorisation pour accéder au relais. Cependant, nous recommandons de pré-filtrer votre matchmaking pour regrouper les joueurs dans la même région afin d'éviter des distances extrêmes entre eux.

{% hint style="info" %}
Cette étape ne connectera pas directement vos joueurs au relais ; vous devrez prendre en charge cela à une étape ultérieure du processus. Cette étape vous fournit uniquement un relais avec lequel établir une connexion.
{% endhint %}

Exemple de requête :

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

La charge utile doit contenir un tableau d'objets utilisateur, où chaque objet contient l'adresse IP d'un utilisateur. Vous pouvez également inclure un `webhook_url`, qui est l'URL qui recevra les notifications liées à la session.

Exemple de payload :

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

{% hint style="info" %}
Où `"users"` est un tableau d'objets utilisateur contenant la `"ip"` de chaque utilisateur dans la session, et `"webhook_url"` est une URL optionnelle pour recevoir des mises à jour de la session.

Après avoir envoyé la `requête POST` requête, vous recevrez une réponse JSON contenant le `session_id`, qui est nécessaire pour récupérer les informations de la session ultérieurement.
{% endhint %}

Exemple de réponse :

```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"
}
```

### Obtenir des informations sur la session de relais

Pour récupérer les informations de session, envoyez une `requête GET` au `/v1/relays/sessions/{session_id}` à l'endpoint avec le `session_id` obtenu depuis la `requête POST` requête.

Exemple de requête :

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

La réponse contiendra des informations sur la session, y compris le statut de la session, les informations des utilisateurs et les informations du relais.

La réponse attendue contiendra des informations de session comme ceci :

```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"
}
```

L'obtention de l'autorisation pour le relais le plus proche peut prendre un peu de temps, donc lorsque le champ `ready` est `true` vous pouvez extraire les données JSON pour les utiliser.

[Plus de détails ici](https://docs.edgegap.com/docs.edgegap.com-fr/docs/api)

{% hint style="success" %}
Alternativement, vous pouvez utiliser le paramètre \`webhook\_url\` lors de la création d'une session de relais pour être notifié lorsque la session a été soit attribuée à un relais avec succès, soit a échoué.
{% endhint %}

### Terminer une session de relais

Lorsque vous décidez de mettre fin à votre connexion au relais, vous pouvez facilement terminer une session de relais. Vous devez envoyer une `requête DELETE` au endpoint suivant :

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

{% hint style="info" %}
Remplacez `{session_id}` par l'identifiant de session réel que vous souhaitez terminer.
{% endhint %}

Une réponse avec le statut 204 no content signifie que la session a été supprimée avec succès. Les joueurs perdront l'accès au relais et vous ne serez plus facturé pour cette session.

{% hint style="warning" %}
Il est important de terminer correctement les sessions afin d'éviter de laisser des ressources inutilisées allouées, ce qui peut affecter les performances et entraîner des coûts inutiles.
{% endhint %}

***

### Exemple en C\#

Ceci est un exemple en C# que votre Lobby ou Matchmaker pourrait exécuter pour créer une session de relais et extraire les données que vous devrez renvoyer à votre client de jeu.

{% hint style="info" %}
Cet exemple utilise C# version > 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("Échec de la création de la session de relais");
            }

            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); // attendre 2 secondes avant de réessayer
            }

            throw new Exception("Échec pour obtenir une session de relais prête");
        }
    }

    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; }
    }
}
```

Vous devrez renvoyer ces informations au joueur concerné.

Le joueur agissant en tant que serveur aura besoin du `RelayServerPort` et tous les autres auront besoin du `RelayClientPort`

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

class Program
{
    static async Task Main(string[] args)
    {
        // Remplacez YOUR_API_TOKEN par votre jeton API réel
        var apiToken = "YOUR_API_TOKEN";

        // 1.1.1.1 est votre joueur agissant comme serveur
        // 2.2.2.2 est votre joueur agissant comme client
        var ips = new List<string> { "1.1.1.1", "2.2.2.2" };

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

        // Générer le corps JSON pour le serveur (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($"Corps JSON pour le serveur (1.1.1.1):\n{serverJson}");

        // Générer le corps JSON pour le client (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($"Corps JSON pour le client (2.2.2.2):\n{clientJson}");
    }
}
```

### Interagir avec l'API (cURL ou POSTMAN)

Pendant le développement de votre jeu, vous devrez interagir avec l'API pour créer, gérer et supprimer des sessions de relais. Vous pouvez utiliser des outils comme cURL ou POSTMAN pour envoyer des requêtes HTTP à l'API.

cURL est un outil en ligne de commande qui vous permet d'effectuer des requêtes HTTP et d'interagir avec des API directement depuis le terminal. POSTMAN est une application GUI populaire qui simplifie les tests d'API en fournissant une interface conviviale pour créer, envoyer et analyser des requêtes HTTP.

Voici un exemple de création d'une session de relais en utilisant 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" %}
N'oubliez pas de remplacer `API_TOKEN` par votre jeton API de relais réel.
{% endhint %}

L'API vous renverra également une réponse contenant des données. Vous devrez analyser cette réponse et extraire les données dont vous avez besoin.
