pack multi v7.1
This commit is contained in:
parent
ae8b5d3d2d
commit
95300d4815
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
Binary file not shown.
Binary file not shown.
@ -1 +0,0 @@
|
||||
{"root":[{"assemblyName":"Assembly-CSharp","nameSpace":"Mirror","className":"GeneratedNetworkCode","methodName":"InitReadWriters","loadTypes":1,"isUnityClass":false},{"assemblyName":"Cinemachine","nameSpace":"Cinemachine","className":"CinemachineStoryboard","methodName":"InitializeModule","loadTypes":0,"isUnityClass":false},{"assemblyName":"Cinemachine","nameSpace":"Cinemachine","className":"CinemachineCore","methodName":"InitializeModule","loadTypes":0,"isUnityClass":false},{"assemblyName":"Cinemachine","nameSpace":"Cinemachine","className":"UpdateTracker","methodName":"InitializeModule","loadTypes":0,"isUnityClass":false},{"assemblyName":"Cinemachine","nameSpace":"Cinemachine","className":"CinemachineImpulseManager","methodName":"InitializeModule","loadTypes":0,"isUnityClass":false},{"assemblyName":"Cinemachine","nameSpace":"Cinemachine.PostFX","className":"CinemachineVolumeSettings","methodName":"InitializeModule","loadTypes":0,"isUnityClass":false},{"assemblyName":"glTFast","nameSpace":"","className":"$BurstDirectCallInitializer","methodName":"Initialize","loadTypes":2,"isUnityClass":false},{"assemblyName":"Mirror.Authenticators","nameSpace":"Mirror.Authenticators","className":"UniqueNameAuthenticator","methodName":"ResetStatics","loadTypes":0,"isUnityClass":false},{"assemblyName":"Mirror.Authenticators","nameSpace":"Mirror","className":"GeneratedNetworkCode","methodName":"InitReadWriters","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror.Components","nameSpace":"Mirror","className":"GeneratedNetworkCode","methodName":"InitReadWriters","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkClient","methodName":"Shutdown","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkDiagnostics","methodName":"ResetStatics","loadTypes":0,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkIdentity","methodName":"ResetStatics","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkLoop","methodName":"ResetStatics","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkLoop","methodName":"RuntimeInitializeOnLoad","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkManager","methodName":"ResetStatics","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkServer","methodName":"Shutdown","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"NetworkTime","methodName":"ResetStatics","loadTypes":0,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"ThreadLog","methodName":"Initialize","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror","nameSpace":"Mirror","className":"GeneratedNetworkCode","methodName":"InitReadWriters","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror.Examples","nameSpace":"Mirror.Examples.MultipleMatch","className":"CanvasController","methodName":"ResetStatics","loadTypes":1,"isUnityClass":false},{"assemblyName":"Mirror.Examples","nameSpace":"Mirror","className":"GeneratedNetworkCode","methodName":"InitReadWriters","loadTypes":1,"isUnityClass":false},{"assemblyName":"Unity.Burst","nameSpace":"","className":"$BurstDirectCallInitializer","methodName":"Initialize","loadTypes":2,"isUnityClass":true},{"assemblyName":"Unity.Collections","nameSpace":"","className":"$BurstDirectCallInitializer","methodName":"Initialize","loadTypes":2,"isUnityClass":true},{"assemblyName":"Unity.InputSystem","nameSpace":"UnityEngine.InputSystem","className":"InputSystem","methodName":"RunInitializeInPlayer","loadTypes":4,"isUnityClass":true},{"assemblyName":"Unity.InputSystem","nameSpace":"UnityEngine.InputSystem","className":"InputSystem","methodName":"RunInitialUpdate","loadTypes":1,"isUnityClass":true},{"assemblyName":"Unity.InputSystem","nameSpace":"UnityEngine.InputSystem.UI","className":"InputSystemUIInputModule","methodName":"ResetDefaultActions","loadTypes":4,"isUnityClass":true},{"assemblyName":"Unity.RenderPipelines.Core.Runtime","nameSpace":"UnityEngine.Experimental.Rendering","className":"XRSystem","methodName":"XRSystemInit","loadTypes":3,"isUnityClass":true},{"assemblyName":"Unity.RenderPipelines.Core.Runtime","nameSpace":"UnityEngine.Rendering","className":"DebugUpdater","methodName":"RuntimeInit","loadTypes":0,"isUnityClass":true},{"assemblyName":"Unity.VisualScripting.Core","nameSpace":"Unity.VisualScripting","className":"RuntimeVSUsageUtility","methodName":"RuntimeInitializeOnLoadBeforeSceneLoad","loadTypes":1,"isUnityClass":true}]}
|
||||
@ -1 +0,0 @@
|
||||
{"names":["UnityEngine.dll","UnityEngine.AIModule.dll","UnityEngine.AccessibilityModule.dll","UnityEngine.AndroidJNIModule.dll","UnityEngine.AnimationModule.dll","UnityEngine.AssetBundleModule.dll","UnityEngine.AudioModule.dll","UnityEngine.ClothModule.dll","UnityEngine.ClusterInputModule.dll","UnityEngine.ClusterRendererModule.dll","UnityEngine.ContentLoadModule.dll","UnityEngine.CoreModule.dll","UnityEngine.CrashReportingModule.dll","UnityEngine.DSPGraphModule.dll","UnityEngine.DirectorModule.dll","UnityEngine.GIModule.dll","UnityEngine.GameCenterModule.dll","UnityEngine.GridModule.dll","UnityEngine.HotReloadModule.dll","UnityEngine.IMGUIModule.dll","UnityEngine.ImageConversionModule.dll","UnityEngine.InputModule.dll","UnityEngine.InputLegacyModule.dll","UnityEngine.JSONSerializeModule.dll","UnityEngine.LocalizationModule.dll","UnityEngine.ParticleSystemModule.dll","UnityEngine.PerformanceReportingModule.dll","UnityEngine.PhysicsModule.dll","UnityEngine.Physics2DModule.dll","UnityEngine.ProfilerModule.dll","UnityEngine.PropertiesModule.dll","UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll","UnityEngine.ScreenCaptureModule.dll","UnityEngine.SharedInternalsModule.dll","UnityEngine.SpriteMaskModule.dll","UnityEngine.SpriteShapeModule.dll","UnityEngine.StreamingModule.dll","UnityEngine.SubstanceModule.dll","UnityEngine.SubsystemsModule.dll","UnityEngine.TLSModule.dll","UnityEngine.TerrainModule.dll","UnityEngine.TerrainPhysicsModule.dll","UnityEngine.TextCoreFontEngineModule.dll","UnityEngine.TextCoreTextEngineModule.dll","UnityEngine.TextRenderingModule.dll","UnityEngine.TilemapModule.dll","UnityEngine.UIModule.dll","UnityEngine.UIElementsModule.dll","UnityEngine.UmbraModule.dll","UnityEngine.UnityAnalyticsModule.dll","UnityEngine.UnityAnalyticsCommonModule.dll","UnityEngine.UnityConnectModule.dll","UnityEngine.UnityCurlModule.dll","UnityEngine.UnityTestProtocolModule.dll","UnityEngine.UnityWebRequestModule.dll","UnityEngine.UnityWebRequestAssetBundleModule.dll","UnityEngine.UnityWebRequestAudioModule.dll","UnityEngine.UnityWebRequestTextureModule.dll","UnityEngine.UnityWebRequestWWWModule.dll","UnityEngine.VFXModule.dll","UnityEngine.VRModule.dll","UnityEngine.VehiclesModule.dll","UnityEngine.VideoModule.dll","UnityEngine.VirtualTexturingModule.dll","UnityEngine.WindModule.dll","UnityEngine.XRModule.dll","Assembly-CSharp.dll","glTFast.dots.dll","Unity.RenderPipelines.Core.Runtime.dll","Unity.ProBuilder.Stl.dll","Unity.ProBuilder.Csg.dll","Unity.RenderPipelines.Universal.Config.Runtime.dll","Unity.VisualScripting.Flow.dll","Telepathy.dll","glTFast.Newtonsoft.dll","Unity.RenderPipelines.Core.ShaderLibrary.dll","Mirror.Examples.dll","Unity.Collections.dll","Unity.ProBuilder.Poly2Tri.dll","glTFast.Documentation.Examples.dll","Unity.TextMeshPro.dll","kcp2k.dll","Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary.dll","Unity.RenderPipelines.Universal.Runtime.dll","Unity.Burst.dll","Unity.ProBuilder.dll","Mirror.dll","Unity.VisualScripting.Core.dll","Mirror.Transports.dll","UnityEngine.UI.dll","Mirror.Components.dll","glTFast.Export.dll","Unity.RenderPipelines.Universal.2D.Internal.dll","Unity.RenderPipeline.Universal.ShaderLibrary.dll","Unity.Timeline.dll","Unity.InputSystem.dll","SimpleWebTransport.dll","Unity.InputSystem.ForUI.dll","Mirror.Authenticators.dll","glTFast.dll","Unity.ProBuilder.KdTree.dll","Unity.Mathematics.dll","Unity.RenderPipelines.Universal.Shaders.dll","Unity.VisualScripting.State.dll","Cinemachine.dll","Unity.Collections.LowLevel.ILSupport.dll","Mirror.BouncyCastle.Cryptography.dll","Unity.VisualScripting.Antlr3.Runtime.dll","Unity.Burst.Unsafe.dll","Newtonsoft.Json.dll"],"types":[2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16]}
|
||||
@ -1,2 +0,0 @@
|
||||
MAURO Stéphane
|
||||
DatacenterSim_Alpha
|
||||
@ -1,6 +0,0 @@
|
||||
gfx-enable-gfx-jobs=1
|
||||
gfx-enable-native-gfx-jobs=1
|
||||
wait-for-native-debugger=0
|
||||
hdr-display-enabled=0
|
||||
gc-max-time-slice=3
|
||||
build-guid=e778e91ac99847908610029fa6e532a7
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
179
Patchs/pack_multi/README_PACK_v7.1.md
Normal file
179
Patchs/pack_multi/README_PACK_v7.1.md
Normal file
@ -0,0 +1,179 @@
|
||||
# DatacenterSim v7.1 — Pack multi complet
|
||||
|
||||
Pack de 3 patches qui corrigent les 3 bugs identifiés en multijoueur après reco :
|
||||
1. **Portage d'équipements** invisible pour les autres joueurs
|
||||
2. **Câbles alim + RJ45** pendants après reco
|
||||
3. **Serveurs éteints** après reco
|
||||
|
||||
---
|
||||
|
||||
## Ce qu'il y a dans le ZIP
|
||||
|
||||
### Scripts_Modifies/ (remplacer dans Assets/Scripts/)
|
||||
|
||||
- **`PlayerInteraction.cs`** — 3 changements
|
||||
- Revert de mon patch authority (plus de AssignClientAuthority/RemoveClientAuthority sur équipements)
|
||||
- `CmdUpdateTransportPosition` bouge maintenant l'objet côté serveur
|
||||
- Helper `SetNetworkTransformActif()` + désactivation NT local pendant portage + réactivation à la pose
|
||||
- `CmdAppuyerBoutonPower` appelle `EtatEquipementNetworkSync.ServerSyncEtat` pour le rejoin
|
||||
|
||||
- **`CablageReseau.cs`** — 4 ajouts
|
||||
- `CmdCreerCableRJ45` / `CmdCreerCableAlim` enregistrent dans le gestionnaire
|
||||
- `CmdSupprimerCableRJ45` / `CmdSupprimerCableAlim` désenregistrent
|
||||
|
||||
- **`BaieNetworkSetup.cs`** — 1 ajout
|
||||
- `OnStartClient` notifie le gestionnaire que la baie est prête
|
||||
|
||||
- **`EtatEquipement.cs`** — 1 ajout
|
||||
- `VerifierAlimentation` appelle `EtatEquipementNetworkSync.ServerSyncEtat` quand l'état change
|
||||
|
||||
### Scripts_Nouveaux/ (AJOUTER dans Assets/Scripts/)
|
||||
|
||||
- **`GestionnaireCablesReseau.cs`** — NetworkBehaviour singleton qui tient 2 SyncLists (câbles alim + RJ45) et rejoue les câbles au rejoin des clients
|
||||
|
||||
- **`EtatEquipementNetworkSync.cs`** — Composant à ajouter sur les prefabs d'équipements rackmount pour synchroniser l'état bouton/sous-tension au rejoin
|
||||
|
||||
---
|
||||
|
||||
## Configuration Unity requise
|
||||
|
||||
### 1. Prefabs d'équipements : NetworkTransform en `Server To Client`
|
||||
|
||||
**IMPORTANT** : tu avais configuré `Client To Server` pour mon dernier patch raté. **Remets-les en `Server To Client`** :
|
||||
|
||||
Pour chaque prefab dans `NetworkManager.spawnPrefabs` (sauf Chariot et prefab joueur) :
|
||||
- Ouvrir le prefab
|
||||
- Le `NetworkTransformReliable` / `NetworkTransformUnreliable` :
|
||||
- **Sync Direction : `Server To Client`** ⚠️
|
||||
- Sync Position : ✅
|
||||
- Sync Rotation : ✅
|
||||
- Sync Scale : ❌
|
||||
- Compress Rotation : ✅
|
||||
- Only Sync On Change : ✅
|
||||
- Interpolate Position : ✅
|
||||
- Interpolate Rotation : ✅
|
||||
- Send Interval Multiplier : 3 ou 5
|
||||
|
||||
Prefabs concernés : `Serveur_1U`, `Switch_24P`, `Parefeu`, `PDU_APC_*`, `Baie_Procedurale`, `Toron_Base`.
|
||||
|
||||
**Ne PAS toucher** : le chariot (Client To Server) et le prefab joueur (Client To Server).
|
||||
|
||||
### 2. Ajouter `GestionnaireCablesReseau` à la scène `Datacenter_01`
|
||||
|
||||
1. Dans la scène, `Hierarchy` → clic droit → `Create Empty`
|
||||
2. Renommer : `GestionnaireCables_Holder`
|
||||
3. `Add Component` → `NetworkIdentity` (laisser toutes les checkboxes décochées)
|
||||
4. `Add Component` → `GestionnaireCablesReseau`
|
||||
5. **Sauver la scène** (Ctrl+S)
|
||||
|
||||
### 3. Ajouter `EtatEquipementNetworkSync` sur les prefabs d'équipements rackmount
|
||||
|
||||
Pour chaque prefab qui a un `BoutonPower` + `EtatEquipement` :
|
||||
- Ouvrir le prefab
|
||||
- `Add Component` → `EtatEquipementNetworkSync`
|
||||
- Sauver
|
||||
|
||||
Prefabs concernés : `Serveur_1U`, `Switch_24P`, `Parefeu`.
|
||||
(Pas besoin sur PDU, Toron, Baie — ces prefabs n'ont pas de bouton power.)
|
||||
|
||||
---
|
||||
|
||||
## Tests à faire
|
||||
|
||||
### Test 1 — Non-régression solo
|
||||
Lancer en éditeur Play. Tout doit fonctionner comme avant (pas de multi).
|
||||
|
||||
### Test 2 — Portage visible en multi
|
||||
1. Host + 1 client connecté
|
||||
2. Client prend un serveur 1U depuis un carton → **tu dois voir le serveur suivre le client**
|
||||
3. Client le pose dans une baie → **tu dois voir le snap**
|
||||
4. Tu prends le même serveur pour le déracker → **le client doit te voir le porter**
|
||||
|
||||
### Test 3 — Câbles alim au rejoin
|
||||
1. Client pose baie + 2 serveurs + 2 PDUs + 2 câbles alim
|
||||
2. Client allume les serveurs (bouton Power)
|
||||
3. Client déco + reco
|
||||
4. **Attendu** : câbles alim toujours connectés (non pendants), serveurs toujours allumés (ventilos tournent, LED verte)
|
||||
|
||||
### Test 4 — Câbles RJ45 au rejoin
|
||||
1. Client pose baie + 2 serveurs + 1 switch + torons + câbles RJ45
|
||||
2. Client déco + reco
|
||||
3. **Attendu** : câbles RJ45 toujours branchés correctement
|
||||
|
||||
### Test 5 — 2e joueur qui rejoint en cours
|
||||
1. Client 1 a tout câblé et allumé
|
||||
2. Client 2 se connecte
|
||||
3. **Attendu** : client 2 voit TOUT l'état (câbles + serveurs allumés)
|
||||
|
||||
---
|
||||
|
||||
## Logs à surveiller
|
||||
|
||||
### Côté serveur, quand un câble est créé :
|
||||
```
|
||||
[Serveur] Câble alim créé : PDU 10 prise 11 → equip 7 port 0
|
||||
[GestionnaireCablesReseau] Cable alim enregistre (total : 1)
|
||||
```
|
||||
|
||||
### Côté client qui rejoint :
|
||||
```
|
||||
[GestionnaireCablesReseau] Client rejoint : 2 cables alim, 3 cables RJ45 a recreer
|
||||
[BaieNetworkSetup] Baie regeneree cote client : Baie_Procedurale(Clone)
|
||||
[GestionnaireCablesReseau] Baie prete : Baie_Procedurale(Clone), retry cables en attente
|
||||
[GestionnaireCablesReseau] Cable alim recree : PDU 10 prise #11 -> PSU0
|
||||
[GestionnaireCablesReseau] Tous les cables ont ete recrees
|
||||
```
|
||||
|
||||
### Si timeout (normalement jamais) :
|
||||
```
|
||||
[GestionnaireCablesReseau] Timeout : 1 cables alim + 0 cables RJ45 jamais recrees
|
||||
```
|
||||
→ ça veut dire qu'une baie ou un équipement ne s'est pas spawné correctement côté client.
|
||||
|
||||
---
|
||||
|
||||
## Architecture du mécanisme "baie prête"
|
||||
|
||||
Le problème classique au rejoin : on tente de recréer un câble entre PDU X et serveur Y, mais X ou Y n'existent pas encore localement parce que leur parent (la baie) n'a pas fini de se régénérer.
|
||||
|
||||
**Solution** :
|
||||
1. Au `OnStartClient` du gestionnaire, on met tous les câbles à recréer en "liste d'attente"
|
||||
2. On tente une première passe après 0.5s (laisse le temps aux premiers spawn)
|
||||
3. Chaque `BaieNetworkSetup.OnStartClient` se termine par `NotifierBaiePrete()` qui déclenche un **retry** sur tous les câbles en attente
|
||||
4. En fallback, un retry périodique toutes les 0.5s pendant 15s max
|
||||
5. Timeout à 15s avec un warning dans les logs
|
||||
|
||||
C'est robuste aux ordres de spawn aléatoires et évite tout callback hell.
|
||||
|
||||
---
|
||||
|
||||
## Remarques techniques
|
||||
|
||||
### Pourquoi retirer l'authority pour le portage ?
|
||||
|
||||
`NetworkTransform Client-to-Server` exige l'authority. Le patch précédent transférait l'authority au ramassage, mais ça créait plein de race conditions (logs `EntityStateMessage without authority`, objets figés/dédoublés...).
|
||||
|
||||
La nouvelle approche : le **serveur est TOUJOURS autoritaire**. Le client porteur envoie des `CmdUpdateTransportPosition` qui bougent l'objet côté serveur, puis `NetworkTransform Server-to-Client` propage aux autres clients. Beaucoup plus simple, plus robuste.
|
||||
|
||||
### Pourquoi désactiver NT local pour le porteur ?
|
||||
|
||||
Sans ça, le client porteur reçoit sa propre position serveur avec ~50ms de latence → son objet tremble. En désactivant le NT localement, son objet suit parfaitement ses mouvements (il est pilote par le Rigidbody local).
|
||||
|
||||
Quand il pose, on réactive NT → il reçoit alors la position "figée" du serveur (qui a fait `rb.isKinematic=true` + position snap).
|
||||
|
||||
### Pourquoi un gestionnaire séparé pour les câbles ?
|
||||
|
||||
`CablageReseau` est sur le prefab joueur : chaque joueur a sa propre instance. Impossible d'y mettre une SyncList globale. On crée donc un singleton à part qui porte l'état global.
|
||||
|
||||
### Pourquoi un composant séparé pour l'état power ?
|
||||
|
||||
Pour éviter de transformer `EtatEquipement` en `NetworkBehaviour` (ce qui casserait les équipements instanciés localement, par ex. pour les tests en éditeur). Composant séparé = découplage propre.
|
||||
|
||||
---
|
||||
|
||||
## Si ça ne marche pas
|
||||
|
||||
1. **Serveur crash au lancement** → probablement `GestionnaireCablesReseau` sans `NetworkIdentity` dans la scène. Vérifier.
|
||||
2. **Câbles toujours pendants au reco** → regarder les logs `[GestionnaireCablesReseau]`. Si "Timeout", c'est que les refs PDU/port sont toujours null. Ouvrir un ticket.
|
||||
3. **Portage local trembant** → `SetNetworkTransformActif` pas appelé au ramassage. Vérifier logs console.
|
||||
4. **Erreur `EntityStateMessage without authority`** → il reste un NT en Client-to-Server quelque part. Vérifier tous les prefabs.
|
||||
61
Patchs/pack_multi/Scripts_Modifies/BaieNetworkSetup.cs
Normal file
61
Patchs/pack_multi/Scripts_Modifies/BaieNetworkSetup.cs
Normal file
@ -0,0 +1,61 @@
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
|
||||
/// <summary>
|
||||
/// BaieNetworkSetup.cs - v6.3 Multijoueur
|
||||
///
|
||||
/// Ajouté automatiquement par BoutiqueReseau lors du spawn d'une baie.
|
||||
/// Quand le client reçoit l'objet via NetworkServer.Spawn(), les materials
|
||||
/// créés en runtime par BaieProcedurale.Generer() ne sont PAS synchronisés
|
||||
/// (Mirror ne sync pas les materials). Ce script régénère la baie localement
|
||||
/// sur chaque client après le spawn.
|
||||
///
|
||||
/// SETUP : Ne pas ajouter manuellement — BoutiqueReseau l'ajoute automatiquement.
|
||||
/// </summary>
|
||||
public class BaieNetworkSetup : NetworkBehaviour
|
||||
{
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
|
||||
// Sur le serveur/host, la baie est déjà générée dans TraiterPlacementBaieServeur
|
||||
// Sur les clients distants, il faut la régénérer pour avoir les materials
|
||||
if (isServer) return;
|
||||
|
||||
BaieProcedurale bp = GetComponent<BaieProcedurale>();
|
||||
if (bp != null)
|
||||
{
|
||||
bp.Generer();
|
||||
bp.InstallerPDUAutomatiquement();
|
||||
Debug.Log($"[BaieNetworkSetup] Baie régénérée côté client : {gameObject.name}");
|
||||
}
|
||||
|
||||
BaieRack br = GetComponent<BaieRack>() ?? GetComponentInChildren<BaieRack>();
|
||||
if (br != null) br.GenererSlots();
|
||||
|
||||
// Appliquer les layers
|
||||
int layerBaie = LayerMask.NameToLayer("Baie");
|
||||
int layerEquip = LayerMask.NameToLayer("Equipement");
|
||||
|
||||
if (layerBaie >= 0) SetLayerRecursif(gameObject, layerBaie);
|
||||
|
||||
foreach (Transform t in GetComponentsInChildren<Transform>(true))
|
||||
{
|
||||
if (t.GetComponent<PDU>() != null && layerEquip >= 0)
|
||||
SetLayerRecursif(t.gameObject, layerEquip);
|
||||
}
|
||||
|
||||
// v7.1 : notifier le gestionnaire de cables que cette baie est prete.
|
||||
// Le gestionnaire va retenter de recreer les cables en attente qui
|
||||
// referencent cette baie / ses PDUs / ses equipements.
|
||||
if (GestionnaireCablesReseau.Instance != null)
|
||||
GestionnaireCablesReseau.Instance.NotifierBaiePrete(gameObject);
|
||||
}
|
||||
|
||||
private void SetLayerRecursif(GameObject obj, int layer)
|
||||
{
|
||||
obj.layer = layer;
|
||||
foreach (Transform c in obj.transform)
|
||||
SetLayerRecursif(c.gameObject, layer);
|
||||
}
|
||||
}
|
||||
427
Patchs/pack_multi/Scripts_Modifies/CablageReseau.cs
Normal file
427
Patchs/pack_multi/Scripts_Modifies/CablageReseau.cs
Normal file
@ -0,0 +1,427 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using Mirror;
|
||||
|
||||
/// <summary>
|
||||
/// CablageReseau.cs - v6.7 Multijoueur
|
||||
///
|
||||
/// v6.7 : sync longueur toron
|
||||
/// - CmdSyncLongueurToron : met à jour la SyncVar longueur sur le serveur
|
||||
/// après consommation ou restitution de câble. Propage automatiquement
|
||||
/// à tous les clients via le hook de ToronNetworkSync.
|
||||
///
|
||||
/// v6.4 : sync câbles RJ45 + alimentation + interrupteur prise
|
||||
///
|
||||
/// SETUP : Ajouter sur le prefab PlayerCapsule.
|
||||
/// </summary>
|
||||
public class CablageReseau : NetworkBehaviour
|
||||
{
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// CÂBLES RJ45
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
[Command]
|
||||
public void CmdCreerCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex,
|
||||
Color couleur, float longueur)
|
||||
{
|
||||
Debug.Log($"[Serveur] Câble RJ45 créé : equip {equipSourceNetId} port {portSourceIndex} → equip {equipDestNetId} port {portDestIndex}");
|
||||
|
||||
// v7.1 : enregistrer le cable dans le gestionnaire pour le rejoin des clients
|
||||
if (GestionnaireCablesReseau.Instance != null)
|
||||
{
|
||||
GestionnaireCablesReseau.Instance.EnregistrerCableRJ45(
|
||||
equipSourceNetId, portSourceIndex, equipDestNetId, portDestIndex, couleur, longueur);
|
||||
}
|
||||
|
||||
RpcCreerCableRJ45(equipSourceNetId, portSourceIndex, equipDestNetId, portDestIndex, couleur, longueur);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcCreerCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex,
|
||||
Color couleur, float longueur)
|
||||
{
|
||||
if (isLocalPlayer) return;
|
||||
|
||||
PortRJ45 portSource = TrouverPortRJ45(equipSourceNetId, portSourceIndex);
|
||||
PortRJ45 portDest = TrouverPortRJ45(equipDestNetId, portDestIndex);
|
||||
|
||||
if (portSource == null || portDest == null)
|
||||
{
|
||||
Debug.LogWarning($"[CablageReseau] Ports introuvables pour câble RJ45 distant");
|
||||
return;
|
||||
}
|
||||
|
||||
List<PointAccroche> chemin = ConstruireCheminAutoLocal(
|
||||
portSource.GetPointConnexion(), portDest.GetPointConnexion());
|
||||
|
||||
string nom = "Cable_" + portSource.nomPort + "_vers_" + portDest.nomPort + "_distant";
|
||||
GameObject cableObj = new GameObject(nom);
|
||||
CableRJ45 cable = cableObj.AddComponent<CableRJ45>();
|
||||
cable.Initialiser(portSource, portDest, chemin, couleur, longueur, null);
|
||||
|
||||
portSource.Connecter(portDest, cable);
|
||||
portDest.Connecter(portSource, cable);
|
||||
|
||||
Debug.Log($"[CablageReseau] Câble RJ45 distant créé : {portSource.nomPort} → {portDest.nomPort}");
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdSupprimerCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex)
|
||||
{
|
||||
Debug.Log($"[Serveur] Câble RJ45 supprimé");
|
||||
|
||||
// v7.1 : desinscrire du gestionnaire
|
||||
if (GestionnaireCablesReseau.Instance != null)
|
||||
{
|
||||
GestionnaireCablesReseau.Instance.OublierCableRJ45(
|
||||
equipSourceNetId, portSourceIndex, equipDestNetId, portDestIndex);
|
||||
}
|
||||
|
||||
RpcSupprimerCableRJ45(equipSourceNetId, portSourceIndex, equipDestNetId, portDestIndex);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcSupprimerCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex)
|
||||
{
|
||||
if (isLocalPlayer) return;
|
||||
|
||||
PortRJ45 portSource = TrouverPortRJ45(equipSourceNetId, portSourceIndex);
|
||||
PortRJ45 portDest = TrouverPortRJ45(equipDestNetId, portDestIndex);
|
||||
|
||||
if (portSource != null && portSource.cable != null)
|
||||
{
|
||||
portSource.cable.SupprimerComplet();
|
||||
}
|
||||
else if (portDest != null && portDest.cable != null)
|
||||
{
|
||||
portDest.cable.SupprimerComplet();
|
||||
}
|
||||
|
||||
Debug.Log("[CablageReseau] Câble RJ45 distant supprimé");
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// SYNC LONGUEUR TORON (v6.7)
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
/// <summary>
|
||||
/// v6.7 : Met à jour la SyncVar longueur du toron côté serveur.
|
||||
/// Appelé par CableManager après consommation ou restitution de câble.
|
||||
/// La SyncVar de ToronNetworkSync propage automatiquement la nouvelle
|
||||
/// longueur à TOUS les clients via le hook.
|
||||
/// </summary>
|
||||
[Command]
|
||||
public void CmdSyncLongueurToron(uint toronNetId, float longueurRestante, float longueurTotale)
|
||||
{
|
||||
if (!NetworkServer.spawned.ContainsKey(toronNetId)) return;
|
||||
var obj = NetworkServer.spawned[toronNetId];
|
||||
if (obj == null) return;
|
||||
|
||||
ToronNetworkSync sync = obj.GetComponent<ToronNetworkSync>();
|
||||
if (sync != null)
|
||||
{
|
||||
sync.ServerSetLongueur(longueurTotale, longueurRestante);
|
||||
Debug.Log($"[Serveur] Longueur toron sync : {obj.name} → {longueurRestante:F1}m / {longueurTotale:F1}m");
|
||||
}
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// CÂBLES ALIMENTATION
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
[Command]
|
||||
public void CmdCreerCableAlim(uint pduNetId, int indexPrise,
|
||||
uint equipNetId, int indexPortAlim,
|
||||
Color couleur)
|
||||
{
|
||||
Debug.Log($"[Serveur] Câble alim créé : PDU {pduNetId} prise {indexPrise} → equip {equipNetId} port {indexPortAlim}");
|
||||
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
PortAlimentation port = TrouverPortAlimentation(equipNetId, indexPortAlim);
|
||||
if (prise != null && port != null && !prise.estConnectee)
|
||||
{
|
||||
GameObject cableObj = new GameObject("CableAlim_serveur");
|
||||
CableAlimentation cable = cableObj.AddComponent<CableAlimentation>();
|
||||
cable.couleurCable = couleur;
|
||||
cable.Initialiser(prise, port);
|
||||
prise.Connecter(cable, port);
|
||||
port.Connecter(cable, prise);
|
||||
}
|
||||
|
||||
// v7.1 : enregistrer le cable dans le gestionnaire pour le rejoin des clients
|
||||
if (GestionnaireCablesReseau.Instance != null)
|
||||
{
|
||||
GestionnaireCablesReseau.Instance.EnregistrerCableAlim(
|
||||
pduNetId, indexPrise, equipNetId, indexPortAlim, couleur);
|
||||
}
|
||||
|
||||
RpcCreerCableAlim(pduNetId, indexPrise, equipNetId, indexPortAlim, couleur);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcCreerCableAlim(uint pduNetId, int indexPrise,
|
||||
uint equipNetId, int indexPortAlim,
|
||||
Color couleur)
|
||||
{
|
||||
if (isLocalPlayer) return;
|
||||
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
PortAlimentation port = TrouverPortAlimentation(equipNetId, indexPortAlim);
|
||||
|
||||
if (prise == null || port == null)
|
||||
{
|
||||
Debug.LogWarning("[CablageReseau] Prise/Port introuvables pour câble alim distant");
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject cableObj = new GameObject("CableAlim_distant");
|
||||
CableAlimentation cable = cableObj.AddComponent<CableAlimentation>();
|
||||
cable.couleurCable = couleur;
|
||||
cable.Initialiser(prise, port);
|
||||
|
||||
prise.Connecter(cable, port);
|
||||
port.Connecter(cable, prise);
|
||||
|
||||
Debug.Log($"[CablageReseau] Câble alim distant créé : PDU prise #{indexPrise} → {port.nomPort}");
|
||||
}
|
||||
|
||||
[Command]
|
||||
public void CmdSupprimerCableAlim(uint pduNetId, int indexPrise,
|
||||
uint equipNetId, int indexPortAlim)
|
||||
{
|
||||
Debug.Log($"[Serveur] Câble alim supprimé");
|
||||
|
||||
// v7.1 : desinscrire du gestionnaire
|
||||
if (GestionnaireCablesReseau.Instance != null)
|
||||
{
|
||||
GestionnaireCablesReseau.Instance.OublierCableAlim(
|
||||
pduNetId, indexPrise, equipNetId, indexPortAlim);
|
||||
}
|
||||
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
if (prise != null && prise.cableBranche != null)
|
||||
prise.cableBranche.Supprimer();
|
||||
|
||||
RpcSupprimerCableAlim(pduNetId, indexPrise, equipNetId, indexPortAlim);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcSupprimerCableAlim(uint pduNetId, int indexPrise,
|
||||
uint equipNetId, int indexPortAlim)
|
||||
{
|
||||
if (isLocalPlayer) return;
|
||||
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
|
||||
if (prise != null && prise.cableBranche != null)
|
||||
{
|
||||
prise.cableBranche.Supprimer();
|
||||
}
|
||||
else
|
||||
{
|
||||
PortAlimentation port = TrouverPortAlimentation(equipNetId, indexPortAlim);
|
||||
if (port != null && port.cableBranche != null)
|
||||
port.cableBranche.Supprimer();
|
||||
}
|
||||
|
||||
Debug.Log("[CablageReseau] Câble alim distant supprimé");
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// INTERRUPTEUR PRISE
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
[Command]
|
||||
public void CmdBasculerPrise(uint pduNetId, int indexPrise)
|
||||
{
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
if (prise != null)
|
||||
prise.BasculerInterrupteur();
|
||||
|
||||
RpcBasculerPrise(pduNetId, indexPrise);
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
void RpcBasculerPrise(uint pduNetId, int indexPrise)
|
||||
{
|
||||
if (isLocalPlayer) return;
|
||||
|
||||
PriseC13 prise = TrouverPriseC13(pduNetId, indexPrise);
|
||||
if (prise != null)
|
||||
prise.BasculerInterrupteur();
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// HELPERS — Recherche de composants par netId + index
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
private PortRJ45 TrouverPortRJ45(uint equipNetId, int portIndex)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(equipNetId)) return null;
|
||||
|
||||
GameObject equipGO = NetworkClient.spawned[equipNetId].gameObject;
|
||||
PortRJ45[] ports = equipGO.GetComponentsInChildren<PortRJ45>();
|
||||
|
||||
foreach (var port in ports)
|
||||
{
|
||||
if (port.numeroPort == portIndex)
|
||||
return port;
|
||||
}
|
||||
|
||||
if (portIndex >= 0 && portIndex < ports.Length)
|
||||
return ports[portIndex];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private PriseC13 TrouverPriseC13(uint pduNetId, int indexPrise)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(pduNetId)) return null;
|
||||
|
||||
GameObject pduGO = NetworkClient.spawned[pduNetId].gameObject;
|
||||
PDU pdu = pduGO.GetComponent<PDU>();
|
||||
|
||||
if (pdu != null && indexPrise >= 0 && indexPrise < pdu.prises.Count)
|
||||
return pdu.prises[indexPrise];
|
||||
|
||||
PriseC13[] prises = pduGO.GetComponentsInChildren<PriseC13>();
|
||||
foreach (var p in prises)
|
||||
if (p.indexPrise == indexPrise) return p;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private PortAlimentation TrouverPortAlimentation(uint equipNetId, int indexPort)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(equipNetId)) return null;
|
||||
|
||||
GameObject equipGO = NetworkClient.spawned[equipNetId].gameObject;
|
||||
PortAlimentation[] ports = equipGO.GetComponentsInChildren<PortAlimentation>();
|
||||
|
||||
if (indexPort >= 0 && indexPort < ports.Length)
|
||||
return ports[indexPort];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<PointAccroche> ConstruireCheminAutoLocal(Vector3 posSource, Vector3 posDest)
|
||||
{
|
||||
List<PointAccroche> chemin = new List<PointAccroche>();
|
||||
PointAccroche[] tousPoints = FindObjectsOfType<PointAccroche>();
|
||||
|
||||
if (tousPoints.Length == 0) return chemin;
|
||||
|
||||
PointAccroche ptDepart = null;
|
||||
float bestDistDepart = 3f;
|
||||
foreach (var p in tousPoints)
|
||||
{
|
||||
float d = Vector3.Distance(posSource, p.GetPosition());
|
||||
if (d < bestDistDepart) { bestDistDepart = d; ptDepart = p; }
|
||||
}
|
||||
|
||||
PointAccroche ptArrivee = null;
|
||||
float bestDistArrivee = 3f;
|
||||
foreach (var p in tousPoints)
|
||||
{
|
||||
float d = Vector3.Distance(posDest, p.GetPosition());
|
||||
if (d < bestDistArrivee) { bestDistArrivee = d; ptArrivee = p; }
|
||||
}
|
||||
|
||||
if (ptDepart == null || ptArrivee == null) return chemin;
|
||||
if (ptDepart == ptArrivee) { chemin.Add(ptDepart); return chemin; }
|
||||
|
||||
float distanceVoisinage = 1.5f;
|
||||
Dictionary<PointAccroche, float> gScore = new Dictionary<PointAccroche, float>();
|
||||
Dictionary<PointAccroche, PointAccroche> cameFrom = new Dictionary<PointAccroche, PointAccroche>();
|
||||
HashSet<PointAccroche> closedSet = new HashSet<PointAccroche>();
|
||||
List<PointAccroche> openSet = new List<PointAccroche>();
|
||||
|
||||
gScore[ptDepart] = 0;
|
||||
openSet.Add(ptDepart);
|
||||
|
||||
int maxIter = 500;
|
||||
int iter = 0;
|
||||
|
||||
while (openSet.Count > 0 && iter < maxIter)
|
||||
{
|
||||
iter++;
|
||||
PointAccroche current = null;
|
||||
float bestF = float.MaxValue;
|
||||
foreach (var n in openSet)
|
||||
{
|
||||
float g = gScore.ContainsKey(n) ? gScore[n] : float.MaxValue;
|
||||
float f = g + Vector3.Distance(n.GetPosition(), ptArrivee.GetPosition());
|
||||
if (f < bestF) { bestF = f; current = n; }
|
||||
}
|
||||
if (current == null) break;
|
||||
if (current == ptArrivee)
|
||||
{
|
||||
List<PointAccroche> result = new List<PointAccroche> { current };
|
||||
while (cameFrom.ContainsKey(current))
|
||||
{
|
||||
current = cameFrom[current];
|
||||
result.Insert(0, current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
openSet.Remove(current);
|
||||
closedSet.Add(current);
|
||||
|
||||
foreach (var autre in tousPoints)
|
||||
{
|
||||
if (autre == current || closedSet.Contains(autre)) continue;
|
||||
if (Vector3.Distance(current.GetPosition(), autre.GetPosition()) > distanceVoisinage) continue;
|
||||
|
||||
float tentG = gScore[current] + Vector3.Distance(current.GetPosition(), autre.GetPosition());
|
||||
if (!openSet.Contains(autre)) openSet.Add(autre);
|
||||
else if (gScore.ContainsKey(autre) && tentG >= gScore[autre]) continue;
|
||||
|
||||
cameFrom[autre] = current;
|
||||
gScore[autre] = tentG;
|
||||
}
|
||||
}
|
||||
|
||||
chemin.Add(ptDepart);
|
||||
chemin.Add(ptArrivee);
|
||||
return chemin;
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// HELPERS PUBLICS
|
||||
// ════════════════════════════════════════════════════════════
|
||||
|
||||
public static uint GetEquipNetId(PortRJ45 port)
|
||||
{
|
||||
if (port == null) return 0;
|
||||
NetworkIdentity netId = port.GetComponentInParent<NetworkIdentity>();
|
||||
return netId != null ? netId.netId : 0;
|
||||
}
|
||||
|
||||
public static uint GetPDUNetId(PriseC13 prise)
|
||||
{
|
||||
if (prise == null || prise.pduParent == null) return 0;
|
||||
NetworkIdentity netId = prise.pduParent.GetComponent<NetworkIdentity>();
|
||||
return netId != null ? netId.netId : 0;
|
||||
}
|
||||
|
||||
public static uint GetEquipNetIdFromPortAlim(PortAlimentation port)
|
||||
{
|
||||
if (port == null || port.equipement == null) return 0;
|
||||
NetworkIdentity netId = port.equipement.GetComponent<NetworkIdentity>();
|
||||
return netId != null ? netId.netId : 0;
|
||||
}
|
||||
|
||||
public static int GetPortAlimIndex(PortAlimentation port)
|
||||
{
|
||||
if (port == null || port.equipement == null) return 0;
|
||||
PortAlimentation[] ports = port.equipement.GetComponentsInChildren<PortAlimentation>();
|
||||
for (int i = 0; i < ports.Length; i++)
|
||||
if (ports[i] == port) return i;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
306
Patchs/pack_multi/Scripts_Modifies/EtatEquipement.cs
Normal file
306
Patchs/pack_multi/Scripts_Modifies/EtatEquipement.cs
Normal file
@ -0,0 +1,306 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Gère l'état sous tension d'un équipement rackmount.
|
||||
///
|
||||
/// NE GÉNÈRE PAS les ports alimentation → utiliser EquipementAlim pour ça.
|
||||
/// NE GÉNÈRE PAS de ventilateur → utiliser un child "Ventilateur" existant
|
||||
/// ou laisser vide (pas de ventilo visuel).
|
||||
///
|
||||
/// Quand alimenté :
|
||||
/// - LEDs d'activité s'allument (emission verte) sur la face avant
|
||||
/// - Ventilateur tourne (si un child "Ventilateur" est assigné)
|
||||
/// - Signale à AmbianceSonore qu'une machine est active
|
||||
///
|
||||
/// Setup Unity :
|
||||
/// 1. Ajouter EquipementAlim sur l'équipement → générer les ports alim
|
||||
/// 2. Ajouter EtatEquipement sur l'équipement
|
||||
/// 3. Les ports alim sont trouvés automatiquement (GetComponentsInChildren)
|
||||
/// 4. Optionnel : créer un child "Ventilateur" et l'assigner
|
||||
/// 5. Configurer le nombre de LEDs, consommation, etc.
|
||||
/// </summary>
|
||||
public class EtatEquipement : MonoBehaviour
|
||||
{
|
||||
[Header("Configuration")]
|
||||
[Tooltip("Mode alimentation redondante : l'équipement reste allumé si au moins 1 port est alimenté")]
|
||||
public bool alimentationRedondante = false;
|
||||
|
||||
[Tooltip("Consommation électrique en watts")]
|
||||
public float consommationWatts = 350f;
|
||||
|
||||
[Header("LEDs")]
|
||||
[Tooltip("Nombre de LEDs d'activité sur la face avant")]
|
||||
[Range(0, 8)]
|
||||
public int nombreLEDs = 3;
|
||||
|
||||
public Color couleurLEDAllumee = new Color(0f, 1f, 0f, 1f); // Vert
|
||||
public Color couleurLEDEteinte = new Color(0.05f, 0.05f, 0.05f, 1f); // Quasi noir
|
||||
public Color couleurLEDStandby = new Color(1f, 0.5f, 0f, 1f); // Orange
|
||||
|
||||
[Tooltip("Intensité de l'émission des LEDs")]
|
||||
public float intensiteLED = 3f;
|
||||
|
||||
[Tooltip("Décalage vertical des LEDs depuis le centre")]
|
||||
public float ledsDecalageY = 0f;
|
||||
[Tooltip("Décalage horizontal de départ des LEDs")]
|
||||
public float ledsDecalageX = 0f;
|
||||
[Tooltip("Espacement entre les LEDs")]
|
||||
public float ledsEspacement = 0.015f;
|
||||
[Tooltip("Taille d'une LED")]
|
||||
public Vector3 ledsTaille = new Vector3(0.006f, 0.006f, 0.003f);
|
||||
|
||||
[Header("Ventilateur")]
|
||||
[Tooltip("Assigner un child 'Ventilateur' existant. Si vide, pas d'animation ventilo.")]
|
||||
public Transform ventilateur;
|
||||
[Tooltip("Vitesse de rotation du ventilateur en degrés/seconde")]
|
||||
public float vitesseVentilateur = 720f;
|
||||
[Tooltip("Axe de rotation du ventilateur (local)")]
|
||||
public Vector3 axeRotation = Vector3.forward;
|
||||
|
||||
[Header("État (lecture seule)")]
|
||||
public bool estSousTension = false;
|
||||
public bool estEnStandby = false;
|
||||
|
||||
[Header("Références (auto-remplies au Start)")]
|
||||
public List<Renderer> ledsRenderers = new List<Renderer>();
|
||||
|
||||
// Privé
|
||||
private bool _ledsGenerees = false;
|
||||
|
||||
void Start()
|
||||
{
|
||||
// Génère les LEDs visuelles si configurées
|
||||
if (nombreLEDs > 0 && !_ledsGenerees)
|
||||
{
|
||||
GenererLEDs();
|
||||
}
|
||||
|
||||
// Cherche un ventilateur existant si pas assigné
|
||||
if (ventilateur == null)
|
||||
{
|
||||
Transform ventiloChild = transform.Find("Ventilateur");
|
||||
if (ventiloChild != null)
|
||||
ventilateur = ventiloChild;
|
||||
// PAS de création automatique — on ne pollue plus le mesh
|
||||
}
|
||||
|
||||
// État initial : éteint
|
||||
MettreAJourVisuel();
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Animation ventilateur
|
||||
if (estSousTension && ventilateur != null)
|
||||
{
|
||||
ventilateur.Rotate(axeRotation, vitesseVentilateur * Time.deltaTime, Space.Self);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Génère les LEDs d'activité sur la face avant de l'équipement.
|
||||
/// </summary>
|
||||
private void GenererLEDs()
|
||||
{
|
||||
// Supprime les anciennes LEDs
|
||||
for (int i = transform.childCount - 1; i >= 0; i--)
|
||||
{
|
||||
Transform enfant = transform.GetChild(i);
|
||||
if (enfant.name.StartsWith("LED_Activity_"))
|
||||
{
|
||||
if (Application.isPlaying) Destroy(enfant.gameObject);
|
||||
else DestroyImmediate(enfant.gameObject);
|
||||
}
|
||||
}
|
||||
ledsRenderers.Clear();
|
||||
|
||||
Bounds bounds = GetBounds();
|
||||
|
||||
float startX = ledsDecalageX - ((nombreLEDs - 1) * ledsEspacement) / 2f;
|
||||
|
||||
for (int i = 0; i < nombreLEDs; i++)
|
||||
{
|
||||
GameObject ledObj = GameObject.CreatePrimitive(PrimitiveType.Sphere);
|
||||
ledObj.name = $"LED_Activity_{i}";
|
||||
ledObj.transform.SetParent(transform);
|
||||
|
||||
ledObj.transform.localPosition = new Vector3(
|
||||
startX + i * ledsEspacement,
|
||||
ledsDecalageY,
|
||||
bounds.extents.z + 0.002f // Juste devant la face avant
|
||||
);
|
||||
ledObj.transform.localScale = ledsTaille;
|
||||
|
||||
// Désactive le collider (pas interactif)
|
||||
Collider ledCol = ledObj.GetComponent<Collider>();
|
||||
if (ledCol != null)
|
||||
{
|
||||
if (Application.isPlaying) Destroy(ledCol);
|
||||
else DestroyImmediate(ledCol);
|
||||
}
|
||||
|
||||
// Material noir par défaut
|
||||
Renderer rend = ledObj.GetComponent<Renderer>();
|
||||
if (rend != null)
|
||||
{
|
||||
rend.material = new Material(Shader.Find("Standard"));
|
||||
rend.material.color = couleurLEDEteinte;
|
||||
}
|
||||
|
||||
ledsRenderers.Add(rend);
|
||||
}
|
||||
|
||||
_ledsGenerees = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Vérifie l'état d'alimentation en fonction des ports connectés.
|
||||
/// Appelé par PortAlimentation quand l'état change.
|
||||
/// Cherche les ports dynamiquement via GetComponentsInChildren.
|
||||
/// </summary>
|
||||
public void VerifierAlimentation()
|
||||
{
|
||||
bool ancienEtat = estSousTension;
|
||||
|
||||
PortAlimentation[] ports = GetComponentsInChildren<PortAlimentation>();
|
||||
|
||||
if (ports.Length == 0)
|
||||
{
|
||||
estSousTension = false;
|
||||
}
|
||||
else if (alimentationRedondante)
|
||||
{
|
||||
// Au moins 1 port alimenté suffit
|
||||
estSousTension = false;
|
||||
foreach (var port in ports)
|
||||
{
|
||||
if (port.estAlimente)
|
||||
{
|
||||
estSousTension = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tous les ports doivent être alimentés
|
||||
estSousTension = true;
|
||||
foreach (var port in ports)
|
||||
{
|
||||
if (!port.estAlimente)
|
||||
{
|
||||
estSousTension = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ancienEtat != estSousTension)
|
||||
{
|
||||
MettreAJourVisuel();
|
||||
|
||||
string etat = estSousTension ? "SOUS TENSION" : "HORS TENSION";
|
||||
Debug.Log($"{gameObject.name}: {etat}");
|
||||
|
||||
// Notifie l'ambiance sonore
|
||||
AmbianceSonore ambiance = FindObjectOfType<AmbianceSonore>();
|
||||
if (ambiance != null)
|
||||
ambiance.RecalculerAmbiance();
|
||||
|
||||
// v7.1 : propager l'etat sous-tension au EtatEquipementNetworkSync (pour rejoin)
|
||||
if (Mirror.NetworkServer.active)
|
||||
{
|
||||
EtatEquipementNetworkSync sync = GetComponent<EtatEquipementNetworkSync>();
|
||||
if (sync == null) sync = GetComponentInParent<EtatEquipementNetworkSync>();
|
||||
if (sync != null) sync.ServerSyncEtat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Met à jour l'apparence visuelle selon l'état d'alimentation.
|
||||
/// </summary>
|
||||
public void MettreAJourVisuel()
|
||||
{
|
||||
// LEDs
|
||||
foreach (var ledRenderer in ledsRenderers)
|
||||
{
|
||||
if (ledRenderer == null) continue;
|
||||
|
||||
Color couleur;
|
||||
if (estSousTension)
|
||||
couleur = couleurLEDAllumee;
|
||||
else if (estEnStandby)
|
||||
couleur = couleurLEDStandby;
|
||||
else
|
||||
couleur = couleurLEDEteinte;
|
||||
|
||||
ledRenderer.material.color = couleur;
|
||||
ledRenderer.material.EnableKeyword("_EMISSION");
|
||||
|
||||
if (estSousTension || estEnStandby)
|
||||
ledRenderer.material.SetColor("_EmissionColor", couleur * intensiteLED);
|
||||
else
|
||||
ledRenderer.material.SetColor("_EmissionColor", Color.black);
|
||||
}
|
||||
|
||||
// Clignotement
|
||||
if (estSousTension)
|
||||
InvokeRepeating(nameof(ClignoterLEDs), 0.5f, 0.3f);
|
||||
else
|
||||
CancelInvoke(nameof(ClignoterLEDs));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fait clignoter aléatoirement les LEDs pour simuler l'activité.
|
||||
/// </summary>
|
||||
private void ClignoterLEDs()
|
||||
{
|
||||
if (!estSousTension) return;
|
||||
|
||||
foreach (var ledRenderer in ledsRenderers)
|
||||
{
|
||||
if (ledRenderer == null) continue;
|
||||
|
||||
if (Random.value < 0.3f)
|
||||
{
|
||||
bool allumee = Random.value > 0.5f;
|
||||
Color couleur = allumee ? couleurLEDAllumee : couleurLEDEteinte;
|
||||
ledRenderer.material.color = couleur;
|
||||
ledRenderer.material.SetColor("_EmissionColor",
|
||||
allumee ? couleurLEDAllumee * intensiteLED : Color.black);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retourne la consommation actuelle en watts (0 si éteint).
|
||||
/// </summary>
|
||||
public float GetConsommationActuelle()
|
||||
{
|
||||
return estSousTension ? consommationWatts : 0f;
|
||||
}
|
||||
|
||||
private Bounds GetBounds()
|
||||
{
|
||||
BoxCollider boxCol = GetComponent<BoxCollider>();
|
||||
if (boxCol != null)
|
||||
return new Bounds(boxCol.center, boxCol.size);
|
||||
|
||||
Renderer rend = GetComponent<Renderer>();
|
||||
if (rend != null)
|
||||
{
|
||||
Bounds worldBounds = rend.bounds;
|
||||
Vector3 localCenter = transform.InverseTransformPoint(worldBounds.center);
|
||||
Vector3 localSize = new Vector3(
|
||||
worldBounds.size.x / transform.lossyScale.x,
|
||||
worldBounds.size.y / transform.lossyScale.y,
|
||||
worldBounds.size.z / transform.lossyScale.z
|
||||
);
|
||||
return new Bounds(localCenter, localSize);
|
||||
}
|
||||
|
||||
return new Bounds(Vector3.zero, new Vector3(0.48f, 0.044f, 0.7f));
|
||||
}
|
||||
}
|
||||
1476
Patchs/pack_multi/Scripts_Modifies/PlayerInteraction.cs
Normal file
1476
Patchs/pack_multi/Scripts_Modifies/PlayerInteraction.cs
Normal file
File diff suppressed because it is too large
Load Diff
107
Patchs/pack_multi/Scripts_Nouveaux/EtatEquipementNetworkSync.cs
Normal file
107
Patchs/pack_multi/Scripts_Nouveaux/EtatEquipementNetworkSync.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// EtatEquipementNetworkSync.cs - v1.0
|
||||
//
|
||||
// Composant a ajouter sur chaque prefab d'equipement rackmount qui possede un
|
||||
// EtatEquipement + BoutonPower (Serveur_1U, Switch_24P, Parefeu, etc.).
|
||||
//
|
||||
// Synchronise 2 etats critiques au rejoin :
|
||||
// 1. L'etat "bouton Power allume" (BoutonPower.estAllume)
|
||||
// 2. L'etat "sous tension" de l'equipement (EtatEquipement.estSousTension)
|
||||
//
|
||||
// Le serveur est autoritaire : quand le serveur change l'etat (via
|
||||
// CmdAppuyerBoutonPower), il appelle ServerSetEstAllume() qui met a jour la
|
||||
// SyncVar. Les clients (actuellement connectes ET futurs rejoigneurs)
|
||||
// recoivent l'etat via le hook SyncVar.
|
||||
//
|
||||
// Note : les animations visuelles (ventilos, LEDs) sont gerees par l'Update()
|
||||
// de EtatEquipement et BoutonPower qui lisent simplement les bool locaux.
|
||||
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
|
||||
[RequireComponent(typeof(NetworkIdentity))]
|
||||
public class EtatEquipementNetworkSync : NetworkBehaviour
|
||||
{
|
||||
[SyncVar(hook = nameof(OnBoutonAllumeChanged))]
|
||||
private bool _syncBoutonAllume = false;
|
||||
|
||||
[SyncVar(hook = nameof(OnSousTensionChanged))]
|
||||
private bool _syncSousTension = false;
|
||||
|
||||
private BoutonPower _bouton;
|
||||
private EtatEquipement _etat;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_bouton = GetComponentInChildren<BoutonPower>();
|
||||
_etat = GetComponentInChildren<EtatEquipement>();
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
|
||||
// Sur le host, rien a faire (le serveur est deja a jour)
|
||||
if (NetworkServer.active) return;
|
||||
|
||||
// Au rejoin : appliquer les valeurs actuelles des SyncVars.
|
||||
// Les hooks ne sont PAS appeles automatiquement pour les valeurs initiales,
|
||||
// il faut forcer l'application ici.
|
||||
ApplyBoutonAllume(_syncBoutonAllume);
|
||||
ApplySousTension(_syncSousTension);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API SERVEUR
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// Appele cote serveur quand BoutonPower.Appuyer() a ete execute et qu'il
|
||||
/// faut synchroniser l'etat aux clients (actuels et futurs).
|
||||
/// </summary>
|
||||
[Server]
|
||||
public void ServerSyncEtat()
|
||||
{
|
||||
if (_bouton != null) _syncBoutonAllume = _bouton.estAllume;
|
||||
if (_etat != null) _syncSousTension = _etat.estSousTension;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HOOKS
|
||||
// ============================================================
|
||||
|
||||
void OnBoutonAllumeChanged(bool ancien, bool nouveau)
|
||||
{
|
||||
ApplyBoutonAllume(nouveau);
|
||||
}
|
||||
|
||||
void OnSousTensionChanged(bool ancien, bool nouveau)
|
||||
{
|
||||
ApplySousTension(nouveau);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// APPLICATION LOCALE
|
||||
// ============================================================
|
||||
|
||||
private void ApplyBoutonAllume(bool valeur)
|
||||
{
|
||||
if (_bouton == null) _bouton = GetComponentInChildren<BoutonPower>();
|
||||
if (_bouton == null) return;
|
||||
|
||||
if (_bouton.estAllume != valeur)
|
||||
{
|
||||
// On assigne directement le champ sans appeler Appuyer() (qui ferait
|
||||
// des effets de bord comme verifier le cablage, rejouer les sons, etc.)
|
||||
_bouton.estAllume = valeur;
|
||||
_bouton.MettreAJourVisuel();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplySousTension(bool valeur)
|
||||
{
|
||||
if (_etat == null) _etat = GetComponentInChildren<EtatEquipement>();
|
||||
if (_etat == null) return;
|
||||
|
||||
_etat.estSousTension = valeur;
|
||||
}
|
||||
}
|
||||
474
Patchs/pack_multi/Scripts_Nouveaux/GestionnaireCablesReseau.cs
Normal file
474
Patchs/pack_multi/Scripts_Nouveaux/GestionnaireCablesReseau.cs
Normal file
@ -0,0 +1,474 @@
|
||||
// GestionnaireCablesReseau.cs - v1.0
|
||||
//
|
||||
// Singleton NetworkBehaviour qui synchronise les cables (alimentation + RJ45) entre
|
||||
// le serveur et les clients, y compris au moment du rejoin d'un client.
|
||||
//
|
||||
// Architecture :
|
||||
// - Cote serveur : chaque creation de cable est enregistree dans une SyncList.
|
||||
// La suppression retire l'entree correspondante.
|
||||
// - Cote client au rejoin (OnStartClient) : parcours des SyncLists et recreation
|
||||
// locale de chaque cable. On attend d'abord que toutes les baies et PDUs soient
|
||||
// "pretes" (via un systeme d'event) pour eviter les refs null sur les ports.
|
||||
//
|
||||
// Setup Unity :
|
||||
// 1. Dans la scene Datacenter_01, creer un GameObject "GestionnaireCables_Holder"
|
||||
// 2. Add Component : NetworkIdentity (rien coche)
|
||||
// 3. Add Component : GestionnaireCablesReseau
|
||||
// 4. Sauver la scene
|
||||
//
|
||||
// Le gestionnaire est appele depuis CablageReseau.CmdCreerCable* / CmdSupprimerCable*
|
||||
// via GestionnaireCablesReseau.Instance.
|
||||
|
||||
using UnityEngine;
|
||||
using Mirror;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class GestionnaireCablesReseau : NetworkBehaviour
|
||||
{
|
||||
public static GestionnaireCablesReseau Instance { get; private set; }
|
||||
|
||||
// ============================================================
|
||||
// STRUCTURES DE DONNEES
|
||||
// ============================================================
|
||||
|
||||
[System.Serializable]
|
||||
public struct CableAlimData
|
||||
{
|
||||
public uint pduNetId;
|
||||
public int indexPrise;
|
||||
public uint equipNetId;
|
||||
public int indexPortAlim;
|
||||
public Color couleur;
|
||||
|
||||
public string Id => $"A_{pduNetId}_{indexPrise}_{equipNetId}_{indexPortAlim}";
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public struct CableRJ45Data
|
||||
{
|
||||
public uint equipSourceNetId;
|
||||
public int portSourceIndex;
|
||||
public uint equipDestNetId;
|
||||
public int portDestIndex;
|
||||
public Color couleur;
|
||||
public float longueur;
|
||||
|
||||
public string Id => $"R_{equipSourceNetId}_{portSourceIndex}_{equipDestNetId}_{portDestIndex}";
|
||||
}
|
||||
|
||||
// SyncLists — synchronisees automatiquement aux clients
|
||||
private readonly SyncList<CableAlimData> _cablesAlim = new SyncList<CableAlimData>();
|
||||
private readonly SyncList<CableRJ45Data> _cablesRJ45 = new SyncList<CableRJ45Data>();
|
||||
|
||||
// ============================================================
|
||||
// EVENT : "Baies et equipements pretes pour recevoir les cables"
|
||||
// ============================================================
|
||||
//
|
||||
// Probleme : au rejoin, les baies sont spawnees dans un ordre imprevisible.
|
||||
// Si on tente de recreer un cable alors que la baie+PDU cibles ne sont pas
|
||||
// encore pretes (BaieNetworkSetup.OnStartClient pas encore termine), on aura
|
||||
// des refs null.
|
||||
//
|
||||
// Solution : chaque BaieNetworkSetup notifie le gestionnaire quand sa baie
|
||||
// est prete. Quand on recoit une notification, on re-tente les cables en
|
||||
// attente (ceux qui referencaient cette baie/PDU).
|
||||
|
||||
// Structures de "cables en attente" cote client
|
||||
private readonly HashSet<string> _cablesAlimRecrees = new HashSet<string>();
|
||||
private readonly HashSet<string> _cablesRJ45Recrees = new HashSet<string>();
|
||||
|
||||
// Cables en attente : ceux qu'on a tente de recreer mais dont les refs etaient null
|
||||
private readonly List<CableAlimData> _attenteAlim = new List<CableAlimData>();
|
||||
private readonly List<CableRJ45Data> _attenteRJ45 = new List<CableRJ45Data>();
|
||||
|
||||
// ============================================================
|
||||
// LIFECYCLE
|
||||
// ============================================================
|
||||
|
||||
void Awake()
|
||||
{
|
||||
if (Instance != null && Instance != this) { Destroy(gameObject); return; }
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (Instance == this) Instance = null;
|
||||
}
|
||||
|
||||
public override void OnStartClient()
|
||||
{
|
||||
base.OnStartClient();
|
||||
|
||||
// Sur le host, rien a faire : le serveur a deja cree les cables en live
|
||||
if (NetworkServer.active) return;
|
||||
|
||||
Debug.Log($"[GestionnaireCablesReseau] Client rejoint : {_cablesAlim.Count} cables alim, {_cablesRJ45.Count} cables RJ45 a recreer");
|
||||
|
||||
// Lancer la coroutine qui tentera de recreer les cables regulierement
|
||||
// jusqu'a ce que tous soient crees (ou abandon apres timeout)
|
||||
StartCoroutine(RejouerCablesProgressivement());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API SERVEUR (appelee par CablageReseau)
|
||||
// ============================================================
|
||||
|
||||
[Server]
|
||||
public void EnregistrerCableAlim(uint pduNetId, int indexPrise, uint equipNetId, int indexPortAlim, Color couleur)
|
||||
{
|
||||
CableAlimData data = new CableAlimData
|
||||
{
|
||||
pduNetId = pduNetId,
|
||||
indexPrise = indexPrise,
|
||||
equipNetId = equipNetId,
|
||||
indexPortAlim = indexPortAlim,
|
||||
couleur = couleur
|
||||
};
|
||||
|
||||
// Eviter les doublons (meme prise + meme port = meme cable)
|
||||
for (int i = 0; i < _cablesAlim.Count; i++)
|
||||
{
|
||||
if (_cablesAlim[i].pduNetId == pduNetId && _cablesAlim[i].indexPrise == indexPrise &&
|
||||
_cablesAlim[i].equipNetId == equipNetId && _cablesAlim[i].indexPortAlim == indexPortAlim)
|
||||
return;
|
||||
}
|
||||
|
||||
_cablesAlim.Add(data);
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable alim enregistre (total : {_cablesAlim.Count})");
|
||||
}
|
||||
|
||||
[Server]
|
||||
public void OublierCableAlim(uint pduNetId, int indexPrise, uint equipNetId, int indexPortAlim)
|
||||
{
|
||||
for (int i = _cablesAlim.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (_cablesAlim[i].pduNetId == pduNetId && _cablesAlim[i].indexPrise == indexPrise &&
|
||||
_cablesAlim[i].equipNetId == equipNetId && _cablesAlim[i].indexPortAlim == indexPortAlim)
|
||||
{
|
||||
_cablesAlim.RemoveAt(i);
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable alim supprime (reste : {_cablesAlim.Count})");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Server]
|
||||
public void EnregistrerCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex,
|
||||
Color couleur, float longueur)
|
||||
{
|
||||
CableRJ45Data data = new CableRJ45Data
|
||||
{
|
||||
equipSourceNetId = equipSourceNetId,
|
||||
portSourceIndex = portSourceIndex,
|
||||
equipDestNetId = equipDestNetId,
|
||||
portDestIndex = portDestIndex,
|
||||
couleur = couleur,
|
||||
longueur = longueur
|
||||
};
|
||||
|
||||
for (int i = 0; i < _cablesRJ45.Count; i++)
|
||||
{
|
||||
if (_cablesRJ45[i].equipSourceNetId == equipSourceNetId &&
|
||||
_cablesRJ45[i].portSourceIndex == portSourceIndex &&
|
||||
_cablesRJ45[i].equipDestNetId == equipDestNetId &&
|
||||
_cablesRJ45[i].portDestIndex == portDestIndex)
|
||||
return;
|
||||
}
|
||||
|
||||
_cablesRJ45.Add(data);
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable RJ45 enregistre (total : {_cablesRJ45.Count})");
|
||||
}
|
||||
|
||||
[Server]
|
||||
public void OublierCableRJ45(uint equipSourceNetId, int portSourceIndex,
|
||||
uint equipDestNetId, int portDestIndex)
|
||||
{
|
||||
for (int i = _cablesRJ45.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var c = _cablesRJ45[i];
|
||||
// Match dans les deux sens car un cable RJ45 est bidirectionnel
|
||||
bool match1 = c.equipSourceNetId == equipSourceNetId && c.portSourceIndex == portSourceIndex &&
|
||||
c.equipDestNetId == equipDestNetId && c.portDestIndex == portDestIndex;
|
||||
bool match2 = c.equipSourceNetId == equipDestNetId && c.portSourceIndex == portDestIndex &&
|
||||
c.equipDestNetId == equipSourceNetId && c.portDestIndex == portSourceIndex;
|
||||
|
||||
if (match1 || match2)
|
||||
{
|
||||
_cablesRJ45.RemoveAt(i);
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable RJ45 supprime (reste : {_cablesRJ45.Count})");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// API CLIENT : notification de baie prete
|
||||
// ============================================================
|
||||
|
||||
/// <summary>
|
||||
/// Appele par BaieNetworkSetup.OnStartClient une fois que la baie + ses PDUs
|
||||
/// sont genere localement. Le gestionnaire tente de recreer les cables en
|
||||
/// attente qui referencaient cette baie ou ses enfants.
|
||||
/// </summary>
|
||||
public void NotifierBaiePrete(GameObject baie)
|
||||
{
|
||||
if (!NetworkClient.active || NetworkServer.active) return;
|
||||
|
||||
Debug.Log($"[GestionnaireCablesReseau] Baie prete : {baie.name}, retry cables en attente");
|
||||
RetenterCablesEnAttente();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// COROUTINE DE REJEU (client qui rejoint)
|
||||
// ============================================================
|
||||
|
||||
private IEnumerator RejouerCablesProgressivement()
|
||||
{
|
||||
// Attendre 0.5s pour laisser les premieres baies + PDUs se generer via BaieNetworkSetup
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
|
||||
// Initialiser la liste d'attente avec tous les cables connus
|
||||
_attenteAlim.Clear();
|
||||
foreach (var data in _cablesAlim) _attenteAlim.Add(data);
|
||||
|
||||
_attenteRJ45.Clear();
|
||||
foreach (var data in _cablesRJ45) _attenteRJ45.Add(data);
|
||||
|
||||
// Tenter une premiere passe
|
||||
RetenterCablesEnAttente();
|
||||
|
||||
// Si il reste des cables en attente, re-tenter periodiquement
|
||||
// (les notifications de baie prete vont aussi declencher des retries)
|
||||
float tempsEcoule = 0f;
|
||||
const float timeoutMax = 15f;
|
||||
const float intervalRetry = 0.5f;
|
||||
|
||||
while ((_attenteAlim.Count > 0 || _attenteRJ45.Count > 0) && tempsEcoule < timeoutMax)
|
||||
{
|
||||
yield return new WaitForSeconds(intervalRetry);
|
||||
tempsEcoule += intervalRetry;
|
||||
RetenterCablesEnAttente();
|
||||
}
|
||||
|
||||
if (_attenteAlim.Count > 0 || _attenteRJ45.Count > 0)
|
||||
{
|
||||
Debug.LogWarning($"[GestionnaireCablesReseau] Timeout : {_attenteAlim.Count} cables alim + {_attenteRJ45.Count} cables RJ45 jamais recrees");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log($"[GestionnaireCablesReseau] Tous les cables ont ete recrees");
|
||||
}
|
||||
}
|
||||
|
||||
private void RetenterCablesEnAttente()
|
||||
{
|
||||
// Cables alim
|
||||
for (int i = _attenteAlim.Count - 1; i >= 0; i--)
|
||||
{
|
||||
CableAlimData data = _attenteAlim[i];
|
||||
if (_cablesAlimRecrees.Contains(data.Id)) { _attenteAlim.RemoveAt(i); continue; }
|
||||
|
||||
if (TenterRecreerCableAlim(data))
|
||||
{
|
||||
_cablesAlimRecrees.Add(data.Id);
|
||||
_attenteAlim.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Cables RJ45
|
||||
for (int i = _attenteRJ45.Count - 1; i >= 0; i--)
|
||||
{
|
||||
CableRJ45Data data = _attenteRJ45[i];
|
||||
if (_cablesRJ45Recrees.Contains(data.Id)) { _attenteRJ45.RemoveAt(i); continue; }
|
||||
|
||||
if (TenterRecreerCableRJ45(data))
|
||||
{
|
||||
_cablesRJ45Recrees.Add(data.Id);
|
||||
_attenteRJ45.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// RECREATION LOCALE DES CABLES
|
||||
// ============================================================
|
||||
|
||||
private bool TenterRecreerCableAlim(CableAlimData data)
|
||||
{
|
||||
PriseC13 prise = TrouverPriseC13(data.pduNetId, data.indexPrise);
|
||||
PortAlimentation port = TrouverPortAlimentation(data.equipNetId, data.indexPortAlim);
|
||||
|
||||
if (prise == null || port == null) return false;
|
||||
|
||||
// Deja connecte ? (si BaieNetworkSetup l'a fait automatiquement via InstallerPDUAutomatiquement)
|
||||
if (prise.estConnectee || port.estConnecte) return true;
|
||||
|
||||
GameObject cableObj = new GameObject($"CableAlim_rejoin_{data.pduNetId}_{data.indexPrise}");
|
||||
CableAlimentation cable = cableObj.AddComponent<CableAlimentation>();
|
||||
cable.couleurCable = data.couleur;
|
||||
cable.Initialiser(prise, port);
|
||||
prise.Connecter(cable, port);
|
||||
port.Connecter(cable, prise);
|
||||
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable alim recree : PDU {data.pduNetId} prise #{data.indexPrise} -> {port.nomPort}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TenterRecreerCableRJ45(CableRJ45Data data)
|
||||
{
|
||||
PortRJ45 portSource = TrouverPortRJ45(data.equipSourceNetId, data.portSourceIndex);
|
||||
PortRJ45 portDest = TrouverPortRJ45(data.equipDestNetId, data.portDestIndex);
|
||||
|
||||
if (portSource == null || portDest == null) return false;
|
||||
|
||||
// Deja connecte ?
|
||||
if (portSource.cable != null || portDest.cable != null) return true;
|
||||
|
||||
List<PointAccroche> chemin = ConstruireCheminAuto(
|
||||
portSource.GetPointConnexion(), portDest.GetPointConnexion());
|
||||
|
||||
GameObject cableObj = new GameObject($"CableRJ45_rejoin_{data.equipSourceNetId}_{data.portSourceIndex}");
|
||||
CableRJ45 cable = cableObj.AddComponent<CableRJ45>();
|
||||
cable.Initialiser(portSource, portDest, chemin, data.couleur, data.longueur, null);
|
||||
|
||||
portSource.Connecter(portDest, cable);
|
||||
portDest.Connecter(portSource, cable);
|
||||
|
||||
Debug.Log($"[GestionnaireCablesReseau] Cable RJ45 recree : {portSource.nomPort} -> {portDest.nomPort}");
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// HELPERS DE RECHERCHE (copies depuis CablageReseau)
|
||||
// ============================================================
|
||||
|
||||
private PortRJ45 TrouverPortRJ45(uint equipNetId, int portIndex)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(equipNetId)) return null;
|
||||
GameObject equipGO = NetworkClient.spawned[equipNetId].gameObject;
|
||||
if (equipGO == null) return null;
|
||||
|
||||
PortRJ45[] ports = equipGO.GetComponentsInChildren<PortRJ45>();
|
||||
foreach (var port in ports)
|
||||
if (port.numeroPort == portIndex) return port;
|
||||
|
||||
if (portIndex >= 0 && portIndex < ports.Length)
|
||||
return ports[portIndex];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private PriseC13 TrouverPriseC13(uint pduNetId, int indexPrise)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(pduNetId)) return null;
|
||||
GameObject pduGO = NetworkClient.spawned[pduNetId].gameObject;
|
||||
if (pduGO == null) return null;
|
||||
|
||||
PDU pdu = pduGO.GetComponent<PDU>();
|
||||
if (pdu != null && indexPrise >= 0 && indexPrise < pdu.prises.Count)
|
||||
return pdu.prises[indexPrise];
|
||||
|
||||
PriseC13[] prises = pduGO.GetComponentsInChildren<PriseC13>();
|
||||
foreach (var p in prises)
|
||||
if (p.indexPrise == indexPrise) return p;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private PortAlimentation TrouverPortAlimentation(uint equipNetId, int indexPort)
|
||||
{
|
||||
if (!NetworkClient.spawned.ContainsKey(equipNetId)) return null;
|
||||
GameObject equipGO = NetworkClient.spawned[equipNetId].gameObject;
|
||||
if (equipGO == null) return null;
|
||||
|
||||
PortAlimentation[] ports = equipGO.GetComponentsInChildren<PortAlimentation>();
|
||||
if (indexPort >= 0 && indexPort < ports.Length) return ports[indexPort];
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<PointAccroche> ConstruireCheminAuto(Vector3 posSource, Vector3 posDest)
|
||||
{
|
||||
// Algorithme A* simplifie (copie depuis CablageReseau)
|
||||
List<PointAccroche> chemin = new List<PointAccroche>();
|
||||
PointAccroche[] tousPoints = FindObjectsOfType<PointAccroche>();
|
||||
if (tousPoints.Length == 0) return chemin;
|
||||
|
||||
PointAccroche ptDepart = null;
|
||||
float bestDistDepart = 3f;
|
||||
foreach (var p in tousPoints)
|
||||
{
|
||||
float d = Vector3.Distance(posSource, p.GetPosition());
|
||||
if (d < bestDistDepart) { bestDistDepart = d; ptDepart = p; }
|
||||
}
|
||||
|
||||
PointAccroche ptArrivee = null;
|
||||
float bestDistArrivee = 3f;
|
||||
foreach (var p in tousPoints)
|
||||
{
|
||||
float d = Vector3.Distance(posDest, p.GetPosition());
|
||||
if (d < bestDistArrivee) { bestDistArrivee = d; ptArrivee = p; }
|
||||
}
|
||||
|
||||
if (ptDepart == null || ptArrivee == null) return chemin;
|
||||
if (ptDepart == ptArrivee) { chemin.Add(ptDepart); return chemin; }
|
||||
|
||||
float distanceVoisinage = 1.5f;
|
||||
Dictionary<PointAccroche, float> gScore = new Dictionary<PointAccroche, float>();
|
||||
Dictionary<PointAccroche, PointAccroche> cameFrom = new Dictionary<PointAccroche, PointAccroche>();
|
||||
HashSet<PointAccroche> closedSet = new HashSet<PointAccroche>();
|
||||
List<PointAccroche> openSet = new List<PointAccroche>();
|
||||
|
||||
gScore[ptDepart] = 0;
|
||||
openSet.Add(ptDepart);
|
||||
|
||||
int maxIter = 500;
|
||||
int iter = 0;
|
||||
|
||||
while (openSet.Count > 0 && iter < maxIter)
|
||||
{
|
||||
iter++;
|
||||
PointAccroche current = null;
|
||||
float bestF = float.MaxValue;
|
||||
foreach (var n in openSet)
|
||||
{
|
||||
float g = gScore.ContainsKey(n) ? gScore[n] : float.MaxValue;
|
||||
float f = g + Vector3.Distance(n.GetPosition(), ptArrivee.GetPosition());
|
||||
if (f < bestF) { bestF = f; current = n; }
|
||||
}
|
||||
if (current == null) break;
|
||||
if (current == ptArrivee)
|
||||
{
|
||||
List<PointAccroche> result = new List<PointAccroche> { current };
|
||||
while (cameFrom.ContainsKey(current))
|
||||
{
|
||||
current = cameFrom[current];
|
||||
result.Insert(0, current);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
openSet.Remove(current);
|
||||
closedSet.Add(current);
|
||||
|
||||
foreach (var autre in tousPoints)
|
||||
{
|
||||
if (autre == current || closedSet.Contains(autre)) continue;
|
||||
if (Vector3.Distance(current.GetPosition(), autre.GetPosition()) > distanceVoisinage) continue;
|
||||
|
||||
float tentG = gScore[current] + Vector3.Distance(current.GetPosition(), autre.GetPosition());
|
||||
if (!openSet.Contains(autre)) openSet.Add(autre);
|
||||
else if (gScore.ContainsKey(autre) && tentG >= gScore[autre]) continue;
|
||||
|
||||
cameFrom[autre] = current;
|
||||
gScore[autre] = tentG;
|
||||
}
|
||||
}
|
||||
|
||||
chemin.Add(ptDepart);
|
||||
chemin.Add(ptArrivee);
|
||||
return chemin;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user