Agrandissement multi modif repertoires
This commit is contained in:
parent
39ffe368c9
commit
89079e554a
192
Patchs/Agrandissement_Multi/README_AGRANDISSEMENT_MULTI.md
Normal file
192
Patchs/Agrandissement_Multi/README_AGRANDISSEMENT_MULTI.md
Normal file
@ -0,0 +1,192 @@
|
||||
# Agrandissement de salles — Synchronisation multijoueur
|
||||
|
||||
## Ce que fait ce pack
|
||||
|
||||
Rend l'agrandissement de salle (clic [E] sur un panneau mural) **synchrone entre tous les joueurs** en multijoueur, tout en restant fonctionnel en solo.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Client joueur clique [E]
|
||||
│
|
||||
▼
|
||||
PlayerInteraction.Ramasser()
|
||||
│
|
||||
▼
|
||||
agr.DemanderAgrandir() ←─── [nouveau]
|
||||
│
|
||||
▼
|
||||
SynchronisationAgrandissement.DemanderAgrandissement(salleId, direction)
|
||||
│
|
||||
├── Mode solo : execution directe locale
|
||||
│
|
||||
└── Mode multi :
|
||||
│
|
||||
▼
|
||||
PlayerAgrandissementBridge.CmdDemanderAgrandissement (sur le joueur local)
|
||||
│
|
||||
▼
|
||||
Serveur : SynchronisationAgrandissement.TraiterDemandeServeur
|
||||
│
|
||||
├── Valide (pas deja agrandi dans cette direction)
|
||||
├── Ajoute a la SyncList des agrandissements effectues
|
||||
│
|
||||
▼
|
||||
RpcLancerAgrandissement(salleId, direction)
|
||||
│
|
||||
▼
|
||||
Chaque client + serveur : AgrandirLocal(false)
|
||||
│
|
||||
▼
|
||||
Animation identique chez tous (meme parametres → meme geometrie)
|
||||
```
|
||||
|
||||
## Script 1 — `SynchronisationAgrandissement.cs` (NOUVEAU)
|
||||
|
||||
Contient deux classes :
|
||||
|
||||
1. **`SynchronisationAgrandissement`** — singleton NetworkBehaviour, orchestre tout
|
||||
2. **`PlayerAgrandissementBridge`** — NetworkBehaviour a ajouter au prefab du joueur pour porter la `[Command]`
|
||||
|
||||
## Script 2 — `AgrandissementSalle.cs` (MODIFIÉ)
|
||||
|
||||
Changements :
|
||||
- Nouvelle méthode `DemanderAgrandir()` : point d'entrée multi
|
||||
- Nouvelle méthode `AgrandirLocal(bool instantane)` : execute localement (avec ou sans anim)
|
||||
- `Agrandir()` conservée pour compat : équivaut à `AgrandirLocal(false)`
|
||||
- Toutes les coroutines d'animation (`ConstruireSol`, `ConstruireMurs`, `ConstruirePlafond`, `ConstruireEclairage`) acceptent un paramètre `skipVisuel` pour construire instantanément (rejeu client qui rejoint)
|
||||
- Gardes `DedicatedServerMode.IsDedicatedServer` sur la création de `Material`/`TextMesh`/debris
|
||||
- Layer `Emplacement` correctement appliqué aux emplacements de l'extension (il manquait dans l'original)
|
||||
|
||||
## Script 3 — `PlayerInteraction.cs` (MODIFIÉ)
|
||||
|
||||
Un seul changement : ligne ~933, `agr.Agrandir()` remplacé par `agr.DemanderAgrandir()`.
|
||||
|
||||
## Étapes dans Unity
|
||||
|
||||
### 1. Remplacer les 3 scripts
|
||||
|
||||
Copier dans `Assets/Scripts/` :
|
||||
- `SynchronisationAgrandissement.cs` (nouveau fichier)
|
||||
- `AgrandissementSalle.cs` (remplace l'existant)
|
||||
- `PlayerInteraction.cs` (remplace l'existant)
|
||||
|
||||
### 2. Ajouter `SynchronisationAgrandissement` à la scène
|
||||
|
||||
Dans `Datacenter_01` :
|
||||
1. Sélectionner le GameObject qui porte le `NetworkManager`
|
||||
2. `Add Component` → `SynchronisationAgrandissement`
|
||||
3. S'assurer que ce GameObject a un `NetworkIdentity` ; la plupart du temps le `NetworkManager` n'en a pas lui-même. Créer un enfant `SyncAgrandissement_Holder` avec un `NetworkIdentity` + le script si besoin.
|
||||
|
||||
**Important** : pour que Mirror synchronise le `SyncList`, ce GameObject doit être spawné sur le réseau. Deux options :
|
||||
- **Option A (simple)** : le mettre dans `NetworkManager.spawnPrefabs` et le spawner au `OnStartServer` via un petit bootstrap
|
||||
- **Option B (scène)** : garder le GameObject dans la scène. Mirror va automatiquement le traiter comme un objet de scène avec `NetworkIdentity`. Dans ce cas, s'assurer que l'objet porte **un `NetworkIdentity`** et qu'il est présent **dès l'ouverture de la scène**.
|
||||
|
||||
L'option B est ce que je recommande pour ton cas : Datacenter_01 est une scène fixe, tu ajoutes `SynchronisationAgrandissement` + `NetworkIdentity` sur un GameObject dédié, et Mirror le gère tout seul.
|
||||
|
||||
### 3. Ajouter `PlayerAgrandissementBridge` au prefab du joueur
|
||||
|
||||
1. Ouvrir le prefab du joueur (celui qui est dans `NetworkManager.playerPrefab`)
|
||||
2. `Add Component` → `PlayerAgrandissementBridge`
|
||||
3. Sauver le prefab
|
||||
|
||||
Sans ce composant sur le joueur, les clients ne pourront pas envoyer leur `CmdDemanderAgrandissement` vers le serveur.
|
||||
|
||||
### 4. Rebuild
|
||||
|
||||
- Client Windows (pour tester avec deux joueurs)
|
||||
- Dedicated Server Linux (pour déployer sur la Debian)
|
||||
|
||||
## Test à faire
|
||||
|
||||
### Test 1 — Mode solo
|
||||
1. Lancer en éditeur (play mode normal, pas host)
|
||||
2. Cliquer [E] sur un panneau d'agrandissement
|
||||
3. **Attendu** : l'animation se lance comme avant, salle agrandie à la fin
|
||||
|
||||
### Test 2 — Mode host + client
|
||||
1. Lancer l'éditeur en Host
|
||||
2. Lancer un build client et le connecter
|
||||
3. **Host** clique [E] sur un panneau
|
||||
4. **Attendu** : les deux voient la même animation en même temps, les nouveaux emplacements apparaissent des deux côtés
|
||||
|
||||
### Test 3 — Client déclencheur
|
||||
1. Host + client
|
||||
2. **Client** clique [E] sur un panneau
|
||||
3. **Attendu** : les deux voient l'animation, la SyncList côté serveur contient l'entrée
|
||||
|
||||
### Test 4 — Dedicated + 2 clients
|
||||
1. Lancer le dedicated sur la Debian
|
||||
2. Connecter 2 clients Windows
|
||||
3. Un des clients clique [E]
|
||||
4. **Attendu** : les 2 clients voient l'animation synchrone, le serveur logue la création de l'extension
|
||||
|
||||
### Test 5 — Rejoin
|
||||
1. Dedicated + 1 client
|
||||
2. Le client déclenche l'agrandissement
|
||||
3. Un **2e client** se connecte APRÈS
|
||||
4. **Attendu** : le 2e client voit l'extension instantanément (pas d'animation), l'état est cohérent
|
||||
|
||||
## Points d'attention
|
||||
|
||||
### GameEconomy
|
||||
Ton `GameEconomy` est en mode sandbox, donc les `TenterAchat` passent toujours. La validation économique serveur n'est pas implémentée dans ce pack, elle le sera quand tu sortiras du sandbox.
|
||||
|
||||
### Emplacements de baies
|
||||
Les `EmplacementBaie` de l'extension sont créés **localement sur chaque instance** (pas via `NetworkServer.Spawn`). C'est cohérent avec le pattern actuel de `SalleDatacenter.GenererEmplacementsBaies()` qui fonctionne déjà en multi.
|
||||
|
||||
Les baies qui seront placées dessus continueront d'utiliser le flux `CmdPlacerBaie` qui spawne bien via `NetworkServer.Spawn` dans `BoutiqueReseau.TraiterPlacementBaieServeur`.
|
||||
|
||||
### Colliders sol + murs
|
||||
Ils sont créés **partout (serveur et clients)** car ils sont nécessaires pour :
|
||||
- Le CharacterController du joueur (marcher dans la nouvelle zone)
|
||||
- La physique des objets posés
|
||||
- Les raycasts d'interaction
|
||||
|
||||
Pas de risque de "tomber dans le vide" côté serveur.
|
||||
|
||||
### Néons
|
||||
Sur le dedicated server, les `Light` ne sont pas créées (gain CPU + logs plus propres). Les clients les créent normalement. C'est sans impact gameplay.
|
||||
|
||||
## Workflow de debug
|
||||
|
||||
Côté serveur dedicated, les logs à surveiller :
|
||||
|
||||
```
|
||||
[SyncAgrandissement] Agrandissement valide : 1_Nord (total : 1)
|
||||
```
|
||||
|
||||
Côté client :
|
||||
```
|
||||
[SyncAgrandissement] Rpc recu, execution agrandissement : salle 1 direction Nord
|
||||
AgrandissementSalle: PHASE 3 - Début construction sol
|
||||
AgrandissementSalle: PHASE 7 - Construction complète !
|
||||
```
|
||||
|
||||
Si tu vois :
|
||||
```
|
||||
[SyncAgrandissement] Mur introuvable : salle 1 direction Nord
|
||||
```
|
||||
|
||||
Ça veut dire que le `salleId` ou la direction ne correspondent à aucun mur dans la scène. Vérifier que `SalleDatacenter.salleId` est bien = 1 (ou que le fallback "n'importe quel mur dans la bonne direction" trouve un match).
|
||||
|
||||
Si tu vois :
|
||||
```
|
||||
[PlayerAgrandissementBridge] Instance SynchronisationAgrandissement introuvable cote serveur !
|
||||
```
|
||||
|
||||
Ça veut dire que le GameObject qui porte `SynchronisationAgrandissement` n'a pas bien été spawné côté serveur. Vérifier qu'il est présent dans la scène **au démarrage** (option B) ou dans `NetworkManager.spawnPrefabs` + spawné au `OnStartServer` (option A).
|
||||
|
||||
## Bugs connus limités
|
||||
|
||||
1. **Chat spam si plusieurs clients déclenchent en même temps** : si 2 clients cliquent [E] sur le même panneau dans la même frame, le serveur validera le premier et rejettera le second (log warning "deja agrandi"). Pas grave pour les joueurs.
|
||||
|
||||
2. **Anim qui démarre décalée** : la latence réseau entre Cmd et Rpc peut faire que les clients voient l'animation avec ~50-200ms de retard entre eux. Acceptable en coop standard.
|
||||
|
||||
3. **Animation coupée si un client disconnect pendant l'anim** : comme c'est local sur chaque client, ça ne casse rien pour les autres. Le joueur qui reconnect verra l'extension déjà construite via le rejeu.
|
||||
|
||||
## À implémenter plus tard (pas dans ce pack)
|
||||
|
||||
- Validation économique serveur-side (quand tu sortiras du sandbox)
|
||||
- Persistence des agrandissements dans le SaveManager
|
||||
- Support multi-salles pour l'agrandissement (actuellement `SynchronisationAgrandissement` trouve `FindObjectOfType<SalleDatacenter>()`, donc ça ciblera toujours la salle 1 ; à affiner quand tu auras plusieurs salles agrandissables)
|
||||
1387
Patchs/Agrandissement_Multi/Scripts_Modifies/AgrandissementSalle.cs
Normal file
1387
Patchs/Agrandissement_Multi/Scripts_Modifies/AgrandissementSalle.cs
Normal file
File diff suppressed because it is too large
Load Diff
1424
Patchs/Agrandissement_Multi/Scripts_Modifies/PlayerInteraction.cs
Normal file
1424
Patchs/Agrandissement_Multi/Scripts_Modifies/PlayerInteraction.cs
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,281 @@
|
||||
// SynchronisationAgrandissement.cs
|
||||
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Singleton NetworkBehaviour qui synchronise les agrandissements de salle entre
|
||||
/// le serveur dedicated et tous les clients.
|
||||
///
|
||||
/// Pattern :
|
||||
/// 1. Un client clique [E] sur un panneau d'agrandissement
|
||||
/// -> AgrandissementSalle.DemanderAgrandir() trouve le singleton et appelle CmdAgrandir(id)
|
||||
/// 2. Cote serveur, CmdAgrandir valide (pas deja agrandi) et diffuse RpcLancerAgrandissement(id)
|
||||
/// 3. Chaque client (+ serveur) recoit le Rpc et execute l'animation locale d'Agrandir()
|
||||
/// en parallele. Tous voient la meme animation car les parametres sont identiques.
|
||||
/// 4. Un client qui rejoint en cours de partie recoit la SyncList des agrandissements deja
|
||||
/// faits et les rejoue instantanement (sans animation).
|
||||
///
|
||||
/// A placer : en composant sur le GameObject du NetworkManager (ou tout objet avec
|
||||
/// NetworkIdentity configure pour exister des le demarrage reseau).
|
||||
///
|
||||
/// L'identifiant d'un agrandissement est "salleId_direction", ex: "1_Nord".
|
||||
/// </summary>
|
||||
public class SynchronisationAgrandissement : NetworkBehaviour
|
||||
{
|
||||
public static SynchronisationAgrandissement Instance { get; private set; }
|
||||
|
||||
// Liste synchronisee des agrandissements deja effectues
|
||||
// Format des entrees : "salleId_Direction" (ex: "1_Nord", "2_Est")
|
||||
private readonly SyncList<string> _agrandissementsEffectues = new SyncList<string>();
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this)
|
||||
{
|
||||
Debug.LogWarning("[SyncAgrandissement] Instance deja existante, destruction de ce duplicata");
|
||||
Destroy(this);
|
||||
return;
|
||||
}
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CLIENT : appel par AgrandissementSalle.DemanderAgrandir()
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// Appele par un client qui veut agrandir. Envoie la demande au serveur.
|
||||
/// En mode solo (pas de Mirror actif), execute directement localement.
|
||||
/// </summary>
|
||||
public void DemanderAgrandissement(int salleId, string direction)
|
||||
{
|
||||
string id = ConstruireId(salleId, direction);
|
||||
|
||||
// Mode solo (pas de serveur Mirror demarre) : execution directe locale
|
||||
if (!NetworkServer.active && !NetworkClient.active)
|
||||
{
|
||||
Debug.Log($"[SyncAgrandissement] Mode solo, execution locale directe : {id}");
|
||||
ExecuterAgrandissementLocal(salleId, direction, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mode multi : Cmd vers serveur
|
||||
// Il faut que le client ait authority. On passe par le player local qui
|
||||
// porte ce role, ou directement si on est serveur nous-meme.
|
||||
if (NetworkServer.active)
|
||||
{
|
||||
// Host ou dedicated : on est serveur, on peut traiter directement
|
||||
TraiterDemandeServeur(salleId, direction);
|
||||
}
|
||||
else if (NetworkClient.active && NetworkClient.connection != null)
|
||||
{
|
||||
// Client pur : on doit passer par un PlayerBridge qui porte isLocalPlayer
|
||||
PlayerAgrandissementBridge bridge = TrouverBridgeLocal();
|
||||
if (bridge != null)
|
||||
{
|
||||
bridge.CmdDemanderAgrandissement(salleId, direction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogError("[SyncAgrandissement] Aucun PlayerAgrandissementBridge trouve sur le joueur local !");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// SERVEUR : validation + broadcast
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// Appele cote serveur (soit par un host qui joue, soit par le bridge d'un client).
|
||||
/// Valide et diffuse aux clients.
|
||||
/// </summary>
|
||||
[Server]
|
||||
public void TraiterDemandeServeur(int salleId, string direction)
|
||||
{
|
||||
string id = ConstruireId(salleId, direction);
|
||||
|
||||
// Validation
|
||||
if (_agrandissementsEffectues.Contains(id))
|
||||
{
|
||||
Debug.LogWarning($"[SyncAgrandissement] Demande refusee, deja agrandi : {id}");
|
||||
return;
|
||||
}
|
||||
|
||||
// Enregistrer dans la liste synchronisee (pour rejoin)
|
||||
_agrandissementsEffectues.Add(id);
|
||||
Debug.Log($"[SyncAgrandissement] Agrandissement valide : {id} (total : {_agrandissementsEffectues.Count})");
|
||||
|
||||
// Diffuser a tous les clients (le host le recoit aussi)
|
||||
RpcLancerAgrandissement(salleId, direction);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CLIENTS : reception du Rpc
|
||||
// ============================================================
|
||||
|
||||
[ClientRpc]
|
||||
private void RpcLancerAgrandissement(int salleId, string direction)
|
||||
{
|
||||
Debug.Log($"[SyncAgrandissement] Rpc recu, execution agrandissement : salle {salleId} direction {direction}");
|
||||
ExecuterAgrandissementLocal(salleId, direction, false);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// REJOIN : un nouveau client rejoue l'etat actuel
|
||||
// ============================================================
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
|
||||
// Ne pas rejouer sur le host (il est deja a jour, c'est lui le serveur)
|
||||
if (NetworkServer.active) return;
|
||||
|
||||
// Le SyncList n'est pas forcement deja rempli a ce moment-la.
|
||||
// On se branche sur ses callbacks et on rejoue les entrees existantes.
|
||||
_agrandissementsEffectues.Callback += OnAgrandissementsListChanged;
|
||||
|
||||
// Rejouer immediatement les entrees deja presentes
|
||||
if (_agrandissementsEffectues.Count > 0)
|
||||
{
|
||||
Debug.Log($"[SyncAgrandissement] Client rejoint : rejeu de {_agrandissementsEffectues.Count} agrandissement(s) passe(s)");
|
||||
foreach (string id in _agrandissementsEffectues)
|
||||
{
|
||||
RejouerInstantane(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStopClient()
|
||||
{
|
||||
base.OnStopClient();
|
||||
_agrandissementsEffectues.Callback -= OnAgrandissementsListChanged;
|
||||
}
|
||||
|
||||
private void OnAgrandissementsListChanged(SyncList<string>.Operation op, int index, string oldItem, string newItem)
|
||||
{
|
||||
// On ignore les changements qui arrivent par le flux normal (deja gere par Rpc)
|
||||
// Ce callback ne sert qu'au cas ou la SyncList est modifiee avant que le client recoive
|
||||
// le Rpc correspondant (race condition rare). Dans ce cas on rejoue aussi.
|
||||
// En pratique on s'en remet surtout au rejeu initial dans OnStartClient().
|
||||
}
|
||||
|
||||
private void RejouerInstantane(string id)
|
||||
{
|
||||
int salleId;
|
||||
string direction;
|
||||
if (!DecomposerId(id, out salleId, out direction))
|
||||
{
|
||||
Debug.LogWarning($"[SyncAgrandissement] Id mal forme : {id}");
|
||||
return;
|
||||
}
|
||||
ExecuterAgrandissementLocal(salleId, direction, true);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// EXECUTION LOCALE (cote serveur ET cote client)
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// Execute l'agrandissement sur l'instance locale : trouve le bon mur et lance Agrandir().
|
||||
/// Si instantane=true, skip les animations (pour rejoin).
|
||||
/// </summary>
|
||||
private void ExecuterAgrandissementLocal(int salleId, string direction, bool instantane)
|
||||
{
|
||||
AgrandissementSalle mur = TrouverMurAgrandissement(salleId, direction);
|
||||
if (mur == null)
|
||||
{
|
||||
Debug.LogWarning($"[SyncAgrandissement] Mur introuvable : salle {salleId} direction {direction}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mur.estAgrandi || mur.enAnimation)
|
||||
{
|
||||
Debug.Log($"[SyncAgrandissement] Agrandissement deja lance ou termine localement, skip");
|
||||
return;
|
||||
}
|
||||
|
||||
mur.AgrandirLocal(instantane);
|
||||
}
|
||||
|
||||
private AgrandissementSalle TrouverMurAgrandissement(int salleId, string direction)
|
||||
{
|
||||
AgrandissementSalle.DirectionAgrandissement dirEnum;
|
||||
if (!System.Enum.TryParse(direction, out dirEnum)) return null;
|
||||
|
||||
AgrandissementSalle[] tous = FindObjectsOfType<AgrandissementSalle>();
|
||||
foreach (var a in tous)
|
||||
{
|
||||
if (a.direction != dirEnum) continue;
|
||||
SalleDatacenter salle = FindObjectOfType<SalleDatacenter>();
|
||||
if (salle == null) continue;
|
||||
if (salle.salleId == salleId) return a;
|
||||
}
|
||||
|
||||
// Fallback : si aucun salleId ne matche (salle mono, pas encore enregistree
|
||||
// par le GestionnaireMultiSalles au moment de l'appel), on renvoie n'importe
|
||||
// quel mur dans la bonne direction.
|
||||
foreach (var a in tous)
|
||||
{
|
||||
if (a.direction == dirEnum) return a;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private PlayerAgrandissementBridge TrouverBridgeLocal()
|
||||
{
|
||||
PlayerAgrandissementBridge[] tous = FindObjectsOfType<PlayerAgrandissementBridge>();
|
||||
foreach (var b in tous)
|
||||
{
|
||||
if (b.isLocalPlayer) return b;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HELPERS ID
|
||||
// ============================================================
|
||||
|
||||
private static string ConstruireId(int salleId, string direction) => $"{salleId}_{direction}";
|
||||
|
||||
private static bool DecomposerId(string id, out int salleId, out string direction)
|
||||
{
|
||||
salleId = -1;
|
||||
direction = "";
|
||||
if (string.IsNullOrEmpty(id)) return false;
|
||||
int sep = id.IndexOf('_');
|
||||
if (sep <= 0 || sep >= id.Length - 1) return false;
|
||||
if (!int.TryParse(id.Substring(0, sep), out salleId)) return false;
|
||||
direction = id.Substring(sep + 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bridge Mirror : composant a ajouter au prefab du joueur pour qu'il puisse
|
||||
/// envoyer une Cmd d'agrandissement vers le serveur (car les [Command] ne peuvent
|
||||
/// etre appelees que depuis un NetworkBehaviour porte par le joueur local).
|
||||
/// </summary>
|
||||
public class PlayerAgrandissementBridge : NetworkBehaviour
|
||||
{
|
||||
[Command]
|
||||
public void CmdDemanderAgrandissement(int salleId, string direction)
|
||||
{
|
||||
if (SynchronisationAgrandissement.Instance == null)
|
||||
{
|
||||
Debug.LogError("[PlayerAgrandissementBridge] Instance SynchronisationAgrandissement introuvable cote serveur !");
|
||||
return;
|
||||
}
|
||||
|
||||
SynchronisationAgrandissement.Instance.TraiterDemandeServeur(salleId, direction);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user