# Photon Fusion 1

### Photon Fusion sur Arbitrium

Ce guide vous aidera à créer un serveur sans tête sur Edgegap pour un projet Unity utilisant [Photon Fusion](https://doc.photonengine.com/en-us/fusion/current/getting-started/fusion-intro) comme solution réseau.

Nous utiliserons le projet exemple [Tanknarok de Photon Fusion](https://doc.photonengine.com/en-us/fusion/current/samples/fusion-tanknarok) comme exemple.

Parce qu'Edgegap fonctionne mieux lorsqu'il sait où sont tous les joueurs (afin de pouvoir choisir un serveur à l'emplacement optimal), le jeu Tanknarok démarrera d'abord en mode hébergé par un client puis basculera vers le serveur Edgegap une fois que tous les joueurs auront rejoint. Cela s'explique par le fait que Tanknarok dispose d'un lobby jouable qui s'exécute avant le début du match où les joueurs peuvent rejoindre et partir à volonté. Donc le flux logique est le suivant :

1. Les joueurs se connectent à la même salle en utilisant `AutoHostOrClient` mode. Ce qui sélectionne automatiquement le premier joueur dans la salle comme hôte.
2. Tous les joueurs envoient leur adresse IP à l'hôte lorsqu'ils rejoignent.
3. Une fois que les joueurs se déclarent prêts, l'hôte utilise ces adresses IP pour démarrer une version serveur Edgegap du jeu avec un code de salle aléatoire (en utilisant un guid).
4. Lorsque l'hôte voit que l'instance Edgegap a fini de se déployer, il informe les autres joueurs du nouveau code de salle.
5. Tous les joueurs, y compris l'hôte, se déconnectent de leur salle actuelle et se reconnectent en mode Client à la nouvelle salle hébergée par le serveur Edgegap.
6. Lorsque le serveur Edgegap voit que tous les joueurs attendus ont rejoint, il démarre le match.
7. Lorsque le serveur Edgegap constate que tout le monde a quitté le match, il s'arrête.

### Configuration du compte

Pour exécuter cette application, vous devrez disposer à la fois d'un compte Photon et d'un compte Edgegap. Tout d'abord, vous devrez créer une application Fusion sur le tableau de bord Photon et définir l'identifiant de l'application dans le fichier `PhotonAppSettings` objet scriptable. Ensuite, vous devrez ajouter une application et créer un jeton API sur le tableau de bord. Vous utiliserez ceux-ci pour définir le nom de l'application, la version et le jeton Api dans le fichier `EdgegapConfig` objet scriptable.

{% hint style="info" %}
Lors de la création de l'application Edgegap, ce programme nécessite 512 unités CPU et 512 Mo de mémoire.
{% endhint %}

Il existe également deux plugins pour aider au développement : Newtonsoft Json pour une meilleure sérialisation Json et [ParrelSync](https://github.com/VeriorPies/ParrelSync) pour faciliter les tests avec plusieurs éditeurs Unity.

### Étape 1 : Configuration du mode serveur

Vous pouvez récupérer le projet Tanknarok modifié sur [GitHub](https://github.com/edgegap). Vous devrez également importer le [Photon Fusion SDK](https://doc.photonengine.com/en-us/fusion/current/getting-started/sdk-download) dans le projet.

Le projet Photon Fusion Tanknarok a dû être légèrement modifié pour fonctionner en tant que serveur. Cela a principalement impliqué de démarrer automatiquement le match et de définir le mode.

1. Modifié `GameLauncher.cs` pour lancer au démarrage avec `GameMode.Server`.
2. Modifié `FusionLauncher.cs` pour appeler `_spawnWorldCallback` une fois le jeu en cours d'exécution au lieu de s'appuyer sur `InstantiatePlayer` (puisqu'aucun joueur n'est instancié pour le serveur).
3. Passer la scène du lobby si c'est un serveur et aller directement au match lorsque tous les joueurs ont rejoint.

Cela a déjà été réalisé dans le projet Tanknarok modifié disponible sur notre [GitHub](https://github.com/edgegap).

#### Dockerfile

Le fichier docker est assez basique. Nous utilisons simplement une base ubuntu, copions la build serveur Linux compilée, définissons les permissions sur le fichier, et l'exécutons.

### Étape 2 : Le premier client

Le client initial prend le rôle d'hôte et de gestionnaire du serveur Edgegap. Il le fait via le fichier `EdgegapManager.cs` qui, à son tour, utilise le fichier `EdgegapAPIInterface.cs` qui encapsule la plupart des fonctionnalités liées à l'interaction avec Edgegap.

Dans `GameManager.cs`, le `OnAllPlayersReady` fonction pour l'hôte déploiera un serveur Edgegap au lieu de démarrer le match. Il le fait en se basant sur les adresses IP de tous les joueurs. Les coroutines Unity permettent à ces actions de se produire de manière asynchrone. La méthode Deploy prend une fonction de rappel, à laquelle est fournie le nom de la nouvelle salle que le serveur utilisera. Une fonction RPC Photon est envoyée pour signaler à tous les clients de basculer.

```cs
    if (EdgegapManager.EdgegapPreServerMode)
    {
        //si hôte, lancer un nouveau serveur edgegap
        //une fois le serveur prêt, s'y connecter

        FindObjectOfType<GameLauncher>().ShowMatchStartingUI();

        if (Object.HasStateAuthority)
        {
            string[] ips = PlayerManager.allPlayers.Select(p => p.ipAddress).ToArray();
            StartCoroutine(EdgegapManager.Instance.Deploy(ips, RpcOnEdgegapServerReady));
        }
    }
    else
    {
        LoadLevel(_levelManager.GetRandomLevelIndex(), -1);
    }
```

La bascule se produit dans une scène vide nommée `TransferringScene`.

Dans EdgegapManager, un code de salle aléatoire est créé et transmis, ainsi que le nombre de joueurs en tant que variables d'environnement à la nouvelle instance créée via l'API de déploiement. Cela est combiné avec les `statut` appels api qui commencent à vérifier une fois par seconde quand l'instance a fini de se déployer. Vous pouvez également utiliser le [webhook](https://docs.edgegap.com/docs.edgegap.com-fr/docs/deployment/arbitrium-deploy-webhook) .

```cs
    /// <summary>
    /// Appelle l'API Edgegap pour déployer un serveur
    /// </summary>
    /// <param name="clientIpList">Une liste d'adresses IP client</param>
    /// <param name="OnDeployed">Une méthode de rappel avec le nom de session en paramètre</param>
    public IEnumerator Deploy(string[] clientIpList, Action<string> OnDeployed = null)
    {
        var roomCode = Guid.NewGuid().ToString();
        yield return EdgegapAPIInterface.RequestDeployment(clientIpList,
             new Dictionary<string, string> {
                { "room_code", roomCode },
                { "player_count", clientIpList.Length.ToString() } });

        yield return EdgegapAPIInterface.WaitForDeployment();

        OnDeployed(roomCode);
    }
```

### Étape 3 : Les joueurs qui rejoignent

En plus de basculer vers le nouveau serveur Edgegap lorsqu'on leur demande, les joueurs qui rejoignent doivent aussi dire au joueur hôte quelle est leur adresse IP. Puisque Photon ne fournit pas cette information, un appel RPC dans `GameManager` appelée `ShareIpAddress` est appelée depuis la classe `Player.cs` dans la `Spawned` méthode.

L'hôte stocke ensuite ces adresses IP dans la classe joueur pour tous les joueurs en fonction de qui était la source du message RPC :

```cs
    [Rpc(sources:RpcSources.All, RpcTargets.StateAuthority)]
    public void RpcShareIpAddress(string ipAddress, RpcInfo info = default)
    {
        if (info.Source == PlayerRef.None)
        {
            // la source sera None pour le joueur local
            Player.local.ipAddress = ipAddress;
            return;
        }

        foreach (var player in PlayerManager.allPlayers)
        {
            if (player.playerID == info.Source.PlayerId)
            {
                player.ipAddress = ipAddress;
                break;
            }
        }
    }
```

### Étape 4 : Nettoyage

De retour sur le serveur, le fichier modifié `GameManager.cs` piste le nombre de joueurs dans le match dans la fonction Update principale. Il vérifie à la fois quand le nombre cible de joueurs a rejoint, pour pouvoir démarrer le match, mais détermine aussi quand il est temps de s'arrêter. Si le jeu a été en cours pendant plus d'une minute et qu'il n'y a plus aucun joueur, il appelle l'API Edgegap pour arrêter son propre déploiement.

```cs
    if (EdgegapManager.IsServer())
    {
        if (GameManager.playState == GameManager.PlayState.LOBBY)
        {
            // si nous sommes dans le lobby et que tous les joueurs ont rejoint, démarrer le match
            if (PlayerManager.allPlayers.Count >= EdgegapManager.ExpectedPlayerCount)
            {
                OnAllPlayersReady();
            }
        }

        if(Time.time > 60 && PlayerManager.allPlayers.Count == 0
            && GameManager.playState != GameManager.PlayState.TRANSITION)
        {
            // si cela fait plus d'une minute et qu'il n'y a personne ici, arrêter le serveur
            Debug.LogWarning("Arrêt en cours");
            StartCoroutine(EdgegapAPIInterface.StopDeploymentFromServer());
            GameManager.playState = PlayState.TRANSITION;
        }
    }
```

Vous avez maintenant un projet Photon Fusion disponible à déployer à la demande !
