diff --git a/Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs b/Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
index ae40802ded..1d7314471e 100644
--- a/Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
+++ b/Nitrox.Test/Server/Serialization/WorldPersistenceTest.cs
@@ -1,7 +1,6 @@
using Nitrox.Test;
using Nitrox.Test.Helper.Faker;
using NitroxModel.Core;
-using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.GameLogic.Entities.Bases;
@@ -199,8 +198,9 @@ private static void EntityTest(Entity entity, Entity entityAfter)
case BatteryMetadata metadata when entityAfter.Metadata is BatteryMetadata metadataAfter:
Assert.AreEqual(metadata.Charge, metadataAfter.Charge);
break;
- case RepairedComponentMetadata metadata when entityAfter.Metadata is RepairedComponentMetadata metadataAfter:
- Assert.AreEqual(metadata.TechType, metadataAfter.TechType);
+ case EscapePodMetadata metadata when entityAfter.Metadata is EscapePodMetadata metadataAfter:
+ Assert.AreEqual(metadata.PodRepaired, metadataAfter.PodRepaired);
+ Assert.AreEqual(metadata.RadioRepaired, metadataAfter.RadioRepaired);
break;
case CrafterMetadata metadata when entityAfter.Metadata is CrafterMetadata metadataAfter:
Assert.AreEqual(metadata.TechType, metadataAfter.TechType);
@@ -337,9 +337,9 @@ private static void EntityTest(Entity entity, Entity entityAfter)
case PlaceholderGroupWorldEntity placeholderGroupWorldEntity when worldEntityAfter is PlaceholderGroupWorldEntity placeholderGroupWorldEntityAfter:
Assert.AreEqual(placeholderGroupWorldEntity.ComponentIndex, placeholderGroupWorldEntityAfter.ComponentIndex);
break;
- case CellRootEntity _ when worldEntityAfter is CellRootEntity _:
+ case CellRootEntity when worldEntityAfter is CellRootEntity:
break;
- case PlacedWorldEntity _ when worldEntityAfter is PlacedWorldEntity _:
+ case PlacedWorldEntity when worldEntityAfter is PlacedWorldEntity:
break;
case OxygenPipeEntity oxygenPipeEntity when worldEntityAfter is OxygenPipeEntity oxygenPipeEntityAfter:
Assert.AreEqual(oxygenPipeEntity.ParentPipeId, oxygenPipeEntityAfter.ParentPipeId);
@@ -351,7 +351,7 @@ private static void EntityTest(Entity entity, Entity entityAfter)
break;
case SerializedWorldEntity serializedWorldEntity when entityAfter is SerializedWorldEntity serializedWorldEntityAfter:
Assert.AreEqual(serializedWorldEntity.AbsoluteEntityCell, serializedWorldEntityAfter.AbsoluteEntityCell);
- AssertHelper.IsListEqual(serializedWorldEntity.Components.OrderBy(c => c.GetHashCode()), serializedWorldEntityAfter.Components.OrderBy(c => c.GetHashCode()), (SerializedComponent c1, SerializedComponent c2) => c1.Equals(c2));
+ AssertHelper.IsListEqual(serializedWorldEntity.Components.OrderBy(c => c.GetHashCode()), serializedWorldEntityAfter.Components.OrderBy(c => c.GetHashCode()), (c1, c2) => c1.Equals(c2));
Assert.AreEqual(serializedWorldEntity.Layer, serializedWorldEntityAfter.Layer);
Assert.AreEqual(serializedWorldEntity.BatchId, serializedWorldEntityAfter.BatchId);
Assert.AreEqual(serializedWorldEntity.CellId, serializedWorldEntityAfter.CellId);
@@ -381,7 +381,6 @@ private static void EntityTest(Entity entity, Entity entityAfter)
Assert.AreEqual(buildEntity.BaseData, buildEntityAfter.BaseData);
break;
case EscapePodWorldEntity escapePodWorldEntity when globalRootEntityAfter is EscapePodWorldEntity escapePodWorldEntityAfter:
- Assert.AreEqual(escapePodWorldEntity.Damaged, escapePodWorldEntityAfter.Damaged);
Assert.IsTrue(escapePodWorldEntity.Players.SequenceEqual(escapePodWorldEntityAfter.Players));
break;
case InteriorPieceEntity interiorPieceEntity when globalRootEntityAfter is InteriorPieceEntity interiorPieceEntityAfter:
@@ -411,9 +410,9 @@ private static void EntityTest(Entity entity, Entity entityAfter)
case MoonpoolEntity moonpoolEntity when globalRootEntityAfter is MoonpoolEntity moonpoolEntityAfter:
Assert.AreEqual(moonpoolEntity.Cell, moonpoolEntityAfter.Cell);
break;
- case PlanterEntity _ when globalRootEntityAfter is PlanterEntity:
+ case PlanterEntity when globalRootEntityAfter is PlanterEntity:
break;
- case PlayerWorldEntity _ when globalRootEntityAfter is PlayerWorldEntity:
+ case PlayerWorldEntity when globalRootEntityAfter is PlayerWorldEntity:
break;
case VehicleWorldEntity vehicleWorldEntity when globalRootEntityAfter is VehicleWorldEntity vehicleWorldEntityAfter:
Assert.AreEqual(vehicleWorldEntity.SpawnerId, vehicleWorldEntityAfter.SpawnerId);
@@ -449,7 +448,7 @@ private static void EntityTest(Entity entity, Entity entityAfter)
case PathBasedChildEntity pathBasedChildEntity when entityAfter is PathBasedChildEntity pathBasedChildEntityAfter:
Assert.AreEqual(pathBasedChildEntity.Path, pathBasedChildEntityAfter.Path);
break;
- case InstalledBatteryEntity _ when entityAfter is InstalledBatteryEntity _:
+ case InstalledBatteryEntity when entityAfter is InstalledBatteryEntity:
break;
case InstalledModuleEntity installedModuleEntity when entityAfter is InstalledModuleEntity installedModuleEntityAfter:
Assert.AreEqual(installedModuleEntity.Slot, installedModuleEntityAfter.Slot);
diff --git a/Nitrox.sln.DotSettings b/Nitrox.sln.DotSettings
index 44bad62e91..50b2ca91c5 100644
--- a/Nitrox.sln.DotSettings
+++ b/Nitrox.sln.DotSettings
@@ -100,6 +100,7 @@
IP
LAN
PDA
+ UWE
False
<Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
<Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
@@ -319,6 +320,7 @@
True
True
True
+ True
True
True
True
diff --git a/NitroxClient/Debuggers/Drawer/DrawerManager.cs b/NitroxClient/Debuggers/Drawer/DrawerManager.cs
index 3c202ba262..3187b41fea 100644
--- a/NitroxClient/Debuggers/Drawer/DrawerManager.cs
+++ b/NitroxClient/Debuggers/Drawer/DrawerManager.cs
@@ -41,6 +41,8 @@ public DrawerManager(SceneDebugger sceneDebugger)
AddDrawer(nitroxEntityDrawer);
AddDrawer(nitroxEntityDrawer);
AddDrawer();
+ AddDrawer>();
+ AddDrawer>();
AddDrawer();
AddDrawer(new(selectableDrawer, unityEventDrawer));
AddDrawer(new(sceneDebugger));
diff --git a/NitroxClient/Debuggers/Drawer/Subnautica/UWEEventDrawer.cs b/NitroxClient/Debuggers/Drawer/Subnautica/UWEEventDrawer.cs
new file mode 100644
index 0000000000..127c02eb9e
--- /dev/null
+++ b/NitroxClient/Debuggers/Drawer/Subnautica/UWEEventDrawer.cs
@@ -0,0 +1,76 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UWE;
+
+namespace NitroxClient.Debuggers.Drawer.Subnautica;
+
+public class UWEEventDrawer : IDrawer>, IDrawer>
+{
+ private const float LABEL_WIDTH = 250;
+
+ public void Draw(Event uweEvent) => UWEEventDrawer.Draw(uweEvent);
+ public void Draw(Event uweEvent) => UWEEventDrawer.Draw(uweEvent);
+
+ private static void Draw(Event uweEvent)
+ {
+ using GUILayout.VerticalScope scope = new();
+
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label("Triggering", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
+ NitroxGUILayout.Separator();
+ uweEvent.triggering = NitroxGUILayout.BoolField(uweEvent.triggering);
+ }
+
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label("Handlers", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
+ NitroxGUILayout.Separator();
+ DrawUweEventHandlerList(uweEvent.handlers);
+ }
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label("ToRemove", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
+ NitroxGUILayout.Separator();
+ DrawUweEventHandlerList(uweEvent.toRemove);
+ }
+ using (new GUILayout.HorizontalScope())
+ {
+ GUILayout.Label("HandlersToTrigger", NitroxGUILayout.DrawerLabel, GUILayout.Width(LABEL_WIDTH));
+ NitroxGUILayout.Separator();
+ DrawUweEventHandlerList(uweEvent.handlersToTrigger);
+ }
+ }
+
+ private static void DrawUweEventHandlerList(ICollection.Handler> uweEventHandlerList)
+ {
+ if (uweEventHandlerList == null)
+ {
+ GUILayout.Label("null", NitroxGUILayout.DrawerLabel);
+ return;
+ }
+
+ if (uweEventHandlerList.Count == 0)
+ {
+ GUILayout.Label("empty", NitroxGUILayout.DrawerLabel);
+ return;
+ }
+
+ foreach (Event.Handler uweEventHandler in uweEventHandlerList)
+ {
+ using (new GUILayout.HorizontalScope())
+ {
+ NitroxGUILayout.Separator();
+ if (uweEventHandler == null)
+ {
+ GUILayout.Label("Handler was null", NitroxGUILayout.DrawerLabel);
+ continue;
+ }
+
+ string labelText = uweEventHandler.obj ? $"{uweEventHandler.obj.GetType().Name}." : string.Empty;
+ labelText += uweEventHandler.function;
+ GUILayout.Label(labelText, NitroxGUILayout.DrawerLabel);
+ }
+ }
+ }
+}
diff --git a/NitroxClient/GameLogic/Entities.cs b/NitroxClient/GameLogic/Entities.cs
index 9c7e41296b..a16ec16a52 100644
--- a/NitroxClient/GameLogic/Entities.cs
+++ b/NitroxClient/GameLogic/Entities.cs
@@ -42,7 +42,7 @@ public class Entities
private readonly HashSet deletedEntitiesIds = new();
private readonly List pendingSimulatedEntities = new();
- public Entities(IPacketSender packetSender, ThrottledPacketSender throttledPacketSender, EntityMetadataManager entityMetadataManager, PlayerManager playerManager, ILocalNitroxPlayer localPlayer, LiveMixinManager liveMixinManager, TimeManager timeManager, SimulationOwnership simulationOwnership)
+ public Entities(IPacketSender packetSender, ThrottledPacketSender throttledPacketSender, EntityMetadataManager entityMetadataManager, PlayerManager playerManager, LocalPlayer localPlayer, LiveMixinManager liveMixinManager, TimeManager timeManager, SimulationOwnership simulationOwnership)
{
this.packetSender = packetSender;
this.throttledPacketSender = throttledPacketSender;
@@ -218,7 +218,7 @@ public IEnumerator SpawnBatchAsync(List batch, bool forceRespawn = false
MarkAsSpawned(entity);
// Finding out about all children (can be hidden in the object's hierarchy or in a pending list)
-
+
if (!entitySpawner.SpawnsOwnChildren(entity))
{
batch.AddRange(entity.ChildEntities);
@@ -231,7 +231,7 @@ public IEnumerator SpawnBatchAsync(List batch, bool forceRespawn = false
pendingParentEntitiesByParentId.Remove(entity.Id);
}
}
-
+
// Skip a frame to maintain FPS
if (Time.realtimeSinceStartup >= timeLimit && skipFrames)
{
diff --git a/NitroxClient/GameLogic/InitialSync/PlayerPositionInitialSyncProcessor.cs b/NitroxClient/GameLogic/InitialSync/PlayerPositionInitialSyncProcessor.cs
index 30a093f0d0..e8b2b43cbe 100644
--- a/NitroxClient/GameLogic/InitialSync/PlayerPositionInitialSyncProcessor.cs
+++ b/NitroxClient/GameLogic/InitialSync/PlayerPositionInitialSyncProcessor.cs
@@ -25,6 +25,7 @@ public PlayerPositionInitialSyncProcessor()
public override IEnumerator Process(InitialPlayerSync packet, WaitScreen.ManualWaitItem waitScreenItem)
{
// We freeze the player so that he doesn't fall before the cells around him have loaded
+ // Is disabled manually or in Terrain.WaitForWorldLoad()
Player.main.cinematicModeActive = true;
AttachPlayerToEscapePod(packet.AssignedEscapePodId);
@@ -37,13 +38,12 @@ public override IEnumerator Process(InitialPlayerSync packet, WaitScreen.ManualW
}
Player.main.SetPosition(position, rotation);
- // Player.Update is setting SubRootID to null after Player position is set
+ // Player.ValidateEscapePod is setting currentEscapePod to null if player is not inside EscapePod
using (PacketSuppressor.Suppress())
{
Player.main.ValidateEscapePod();
}
- // Player position is relative to a subroot if in a subroot
Optional subRootId = packet.PlayerSubRootId;
if (!subRootId.HasValue)
{
@@ -59,16 +59,19 @@ public override IEnumerator Process(InitialPlayerSync packet, WaitScreen.ManualW
yield break;
}
- if (!sub.Value.TryGetComponent(out SubRoot subRoot))
+ if (sub.Value.TryGetComponent(out SubRoot subRoot))
{
- Log.Debug("SubRootId-GameObject has no SubRoot component, so it's assumed to be the EscapePod");
- yield return Terrain.WaitForWorldLoad();
- yield break;
+ Player.main.SetCurrentSub(subRoot, true);
+ Player.main.UpdateIsUnderwater();
+ }
+ else if (sub.Value.GetComponent())
+ {
+ Player.main.escapePod.Update(true);
+ }
+ else
+ {
+ Log.Error("SubRootId-GameObject has no SubRoot or EscapePod component");
}
-
- Player.main.SetCurrentSub(subRoot, true);
- // If the player's in a base/cyclops we don't need to wait for the world to load
- Player.main.UpdateIsUnderwater();
Player.main.cinematicModeActive = false;
}
diff --git a/NitroxClient/GameLogic/Spawning/Metadata/EntityMetadataManager.cs b/NitroxClient/GameLogic/Spawning/Metadata/EntityMetadataManager.cs
index f73840dc07..ec4ef38dd5 100644
--- a/NitroxClient/GameLogic/Spawning/Metadata/EntityMetadataManager.cs
+++ b/NitroxClient/GameLogic/Spawning/Metadata/EntityMetadataManager.cs
@@ -54,6 +54,13 @@ public Optional Extract(GameObject o)
return Optional.Empty;
}
+ public bool TryExtract(object o, out EntityMetadata metadata)
+ {
+ Optional opMetadata = Extract(o);
+ metadata = opMetadata.Value;
+ return opMetadata.HasValue;
+ }
+
public Optional FromMetaData(EntityMetadata metadata)
{
if (metadata != null && processors.TryGetValue(metadata.GetType(), out IEntityMetadataProcessor processor))
diff --git a/NitroxClient/GameLogic/Spawning/Metadata/Extractor/EscapePodMetadataExtractor.cs b/NitroxClient/GameLogic/Spawning/Metadata/Extractor/EscapePodMetadataExtractor.cs
new file mode 100644
index 0000000000..0bc092d6f8
--- /dev/null
+++ b/NitroxClient/GameLogic/Spawning/Metadata/Extractor/EscapePodMetadataExtractor.cs
@@ -0,0 +1,14 @@
+using NitroxClient.GameLogic.Spawning.Metadata.Extractor.Abstract;
+using NitroxClient.Unity.Helper;
+using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
+
+namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;
+
+public class EscapePodMetadataExtractor : EntityMetadataExtractor
+{
+ public override EscapePodMetadata Extract(EscapePod entity)
+ {
+ Radio radio = entity.radioSpawner.spawnedObj.RequireComponent();
+ return new EscapePodMetadata(entity.liveMixin.IsFullHealth(), radio.liveMixin.IsFullHealth());
+ }
+}
diff --git a/NitroxClient/GameLogic/Spawning/Metadata/Processor/EscapePodMetadataProcessor.cs b/NitroxClient/GameLogic/Spawning/Metadata/Processor/EscapePodMetadataProcessor.cs
new file mode 100644
index 0000000000..2d4b9f353e
--- /dev/null
+++ b/NitroxClient/GameLogic/Spawning/Metadata/Processor/EscapePodMetadataProcessor.cs
@@ -0,0 +1,75 @@
+using NitroxClient.GameLogic.Spawning.Metadata.Processor.Abstract;
+using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
+using UnityEngine;
+
+namespace NitroxClient.GameLogic.Spawning.Metadata.Processor;
+
+public class EscapePodMetadataProcessor : EntityMetadataProcessor
+{
+ // For metadata changes outside initial sync we only care about broken -> repaired
+ public override void ProcessMetadata(GameObject gameObject, EscapePodMetadata metadata)
+ {
+ if (!gameObject.TryGetComponent(out EscapePod pod))
+ {
+ Log.Error($"[{nameof(EscapePodMetadataProcessor)}] Could not get the EscapePod component from the provided gameobject.");
+ return;
+ }
+
+ if (!pod.liveMixin.IsFullHealth() && metadata.PodRepaired)
+ {
+ pod.liveMixin.health = pod.liveMixin.maxHealth;
+ pod.healthScalar = 1;
+ pod.damageEffectsShowing = true;
+ pod.UpdateDamagedEffects();
+ pod.OnRepair();
+ }
+
+ if (!pod.radioSpawner.spawnedObj.TryGetComponent(out Radio radio))
+ {
+ Log.Error($"[{nameof(EscapePodMetadataProcessor)}] Could not get Radio from EscapePod.");
+ return;
+ }
+
+ if (!radio.liveMixin.IsFullHealth() && metadata.RadioRepaired)
+ {
+ radio.liveMixin.AddHealth(radio.liveMixin.maxHealth);
+ }
+ }
+
+ ///
+ /// Applies repaired state without animations and minimal audio playback
+ ///
+ public static void ProcessInitialSyncMetadata(EscapePod pod, Radio radio, EscapePodMetadata metadata)
+ {
+ if (metadata.PodRepaired)
+ {
+ pod.liveMixin.health = pod.liveMixin.maxHealth;
+ pod.healthScalar = 1;
+ pod.damageEffectsShowing = true; // Needs to be set to true for UpdateDamagedEffects() to function
+ pod.UpdateDamagedEffects();
+ pod.vfxSpawner.SpawnManual(); // Spawn vfx to instantly disable it so no smoke is fading after player has joined
+ pod.vfxSpawner.spawnedObj.SetActive(false);
+ pod.lightingController.SnapToState(0);
+ }
+ else
+ {
+ IntroLifepodDirector introLifepodDirector = pod.GetComponent();
+ introLifepodDirector.OnProtoDeserializeObjectTree(null);
+ introLifepodDirector.ToggleActiveObjects(false);
+ pod.lightingController.SnapToState(2);
+ }
+
+ if (metadata.RadioRepaired)
+ {
+ radio.liveMixin.health = radio.liveMixin.maxHealth;
+ if (radio.liveMixin.loopingDamageEffectObj)
+ {
+ Object.Destroy(radio.liveMixin.loopingDamageEffectObj);
+ }
+ }
+ else
+ {
+ pod.DamageRadio();
+ }
+ }
+}
diff --git a/NitroxClient/GameLogic/Spawning/Metadata/Processor/RepairedComponentMetadataProcessor.cs b/NitroxClient/GameLogic/Spawning/Metadata/Processor/RepairedComponentMetadataProcessor.cs
deleted file mode 100644
index a7be29fa8f..0000000000
--- a/NitroxClient/GameLogic/Spawning/Metadata/Processor/RepairedComponentMetadataProcessor.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using NitroxClient.GameLogic.Spawning.Metadata.Processor.Abstract;
-using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
-using UnityEngine;
-
-namespace NitroxClient.GameLogic.Spawning.Metadata.Processor;
-
-public class RepairedComponentMetadataProcessor : EntityMetadataProcessor
-{
- public override void ProcessMetadata(GameObject gameObject, RepairedComponentMetadata metadata)
- {
- Radio radio = gameObject.GetComponent();
-
- if (radio)
- {
- radio.liveMixin.health = radio.liveMixin.maxHealth;
- radio.repairNotification.Play();
- }
-
- EscapePod pod = gameObject.GetComponent();
-
- if (pod)
- {
- pod.liveMixin.health = pod.liveMixin.maxHealth;
- pod.animator.SetFloat("lifepod_damage", 1.0f);
- pod.fixPanelGoal.Trigger();
- pod.fixPanelPowerUp.Play();
- }
- }
-}
diff --git a/NitroxClient/GameLogic/Spawning/WorldEntities/EscapePodWorldEntitySpawner.cs b/NitroxClient/GameLogic/Spawning/WorldEntities/EscapePodWorldEntitySpawner.cs
index dd4f711a95..fd71f9f2ef 100644
--- a/NitroxClient/GameLogic/Spawning/WorldEntities/EscapePodWorldEntitySpawner.cs
+++ b/NitroxClient/GameLogic/Spawning/WorldEntities/EscapePodWorldEntitySpawner.cs
@@ -1,103 +1,114 @@
using System.Collections;
-using NitroxClient.GameLogic.Spawning.Metadata;
+using NitroxClient.Communication;
+using NitroxClient.GameLogic.FMOD;
+using NitroxClient.GameLogic.Spawning.Metadata.Processor;
using NitroxClient.MonoBehaviours;
using NitroxClient.MonoBehaviours.CinematicController;
using NitroxModel.DataStructures.GameLogic.Entities;
+using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Util;
using NitroxModel_Subnautica.DataStructures;
+using NitroxModel.DataStructures.GameLogic;
+using NitroxModel.Packets;
using UnityEngine;
-namespace NitroxClient.GameLogic.Spawning.WorldEntities
+namespace NitroxClient.GameLogic.Spawning.WorldEntities;
+
+public class EscapePodWorldEntitySpawner : IWorldEntitySpawner
{
- public class EscapePodWorldEntitySpawner : IWorldEntitySpawner
+ /*
+ * When creating additional escape pods (multiple users with multiple pods)
+ * we want to suppress the escape pod's awake method so it doesn't override
+ * EscapePod.main to the new escape pod.
+ */
+ public static bool SuppressEscapePodAwakeMethod;
+
+ private readonly LocalPlayer localPlayer;
+
+ public EscapePodWorldEntitySpawner(LocalPlayer localPlayer)
{
- private EntityMetadataManager entityMetadataManager;
+ this.localPlayer = localPlayer;
+ }
- public EscapePodWorldEntitySpawner(EntityMetadataManager entityMetadataManager)
+ public IEnumerator SpawnAsync(WorldEntity entity, Optional parent, EntityCell cellRoot, TaskResult> result)
+ {
+ if (entity is not EscapePodWorldEntity escapePodEntity)
{
- this.entityMetadataManager = entityMetadataManager;
+ result.Set(Optional.Empty);
+ Log.Error($"Received incorrect entity type: {entity.GetType()}");
+ yield break;
}
- /*
- * When creating additional escape pods (multiple users with multiple pods)
- * we want to supress the escape pod's awake method so it doesn't override
- * EscapePod.main to the new escape pod.
- */
- public static bool SURPRESS_ESCAPE_POD_AWAKE_METHOD;
+ SuppressEscapePodAwakeMethod = true;
- public IEnumerator SpawnAsync(WorldEntity entity, Optional parent, EntityCell cellRoot, TaskResult> result)
- {
- if (entity is not EscapePodWorldEntity escapePodEntity)
- {
- result.Set(Optional.Empty);
- Log.Error($"Received incorrect entity type: {entity.GetType()}");
- yield break;
- }
+ GameObject escapePod = CreateNewEscapePod(escapePodEntity);
- SURPRESS_ESCAPE_POD_AWAKE_METHOD = true;
+ SuppressEscapePodAwakeMethod = false;
- GameObject escapePod = CreateNewEscapePod(escapePodEntity);
+ result.Set(Optional.Of(escapePod));
+ }
- SURPRESS_ESCAPE_POD_AWAKE_METHOD = false;
+ private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity)
+ {
+ // TODO: When we want to implement multiple escape pods, instantiate the prefab. Backlog task: #1945
+ // This will require some work as instantiating the prefab as-is will not make it visible.
+ //GameObject escapePod = Object.Instantiate(EscapePod.main.gameObject);
+ GameObject escapePod = EscapePod.main.gameObject;
+ EscapePod pod = escapePod.GetComponent();
- result.Set(Optional.Of(escapePod));
- }
+ Object.DestroyImmediate(escapePod.GetComponent()); // if template has a pre-existing NitroxEntity, remove.
+ NitroxEntity.SetNewId(escapePod, escapePodEntity.Id);
- private GameObject CreateNewEscapePod(EscapePodWorldEntity escapePodEntity)
+ if (escapePod.TryGetComponent(out Rigidbody rigidbody))
{
- // TODO: When we want to implement multiple escape pods, instantiate the prefab. Backlog task: #1945
- // This will require some work as instantiating the prefab as-is will not make it visible.
- //GameObject escapePod = Object.Instantiate(EscapePod.main.gameObject);
+ rigidbody.constraints = RigidbodyConstraints.FreezeAll;
+ }
+ else
+ {
+ Log.Error("Escape pod did not have a rigid body!");
+ }
- GameObject escapePod = EscapePod.main.gameObject;
- UnityEngine.Component.DestroyImmediate(escapePod.GetComponent()); // if template has a pre-existing NitroxEntity, remove.
- NitroxEntity.SetNewId(escapePod, escapePodEntity.Id);
+ pod.anchorPosition = escapePod.transform.position = escapePodEntity.Transform.Position.ToUnity();
- entityMetadataManager.ApplyMetadata(escapePod, escapePodEntity.Metadata);
+ pod.ForceSkyApplier();
+ pod.escapePodCinematicControl.StopAll();
- Rigidbody rigidbody = escapePod.GetComponent();
- if (rigidbody != null)
- {
- rigidbody.constraints = RigidbodyConstraints.FreezeAll;
- }
- else
- {
- Log.Error("Escape pod did not have a rigid body!");
- }
+ // Player is not new and has completed the intro cinematic. If not EscapePod repair status is handled by the intro cinematic.
+ if (escapePodEntity.Metadata is EscapePodMetadata metadata && localPlayer.IntroCinematicMode == IntroCinematicMode.COMPLETED)
+ {
+ using FMODSoundSuppressor soundSuppressor = FMODSystem.SuppressSubnauticaSounds();
+ using PacketSuppressor packetSuppressor = PacketSuppressor.Suppress();
- escapePod.transform.position = escapePodEntity.Transform.Position.ToUnity();
+ Radio radio = pod.radioSpawner.spawnedObj.GetComponent();
+ EscapePodMetadataProcessor.ProcessInitialSyncMetadata(pod, radio, metadata);
+ }
- FixStartMethods(escapePod);
+ FixStartMethods(escapePod);
- return escapePod;
+ return escapePod;
+ }
+
+ ///
+ /// Start() isn't executed for the EscapePod and children (Why? Idk, maybe because it's a scene...) so we call the components here where we have patches in Start.
+ ///
+ private static void FixStartMethods(GameObject escapePod)
+ {
+ foreach (FMOD_CustomEmitter customEmitter in escapePod.GetComponentsInChildren(true))
+ {
+ customEmitter.Start();
}
- ///
- /// Start() isn't executed for the EscapePod and children (Why? Idk, maybe because it's a scene...) so we call the components here where we have patches in Start.
- ///
- private static void FixStartMethods(GameObject escapePod)
+ foreach (FMOD_StudioEventEmitter studioEventEmitter in escapePod.GetComponentsInChildren(true))
{
- foreach (FMOD_CustomEmitter customEmitter in escapePod.GetComponentsInChildren(true))
- {
- customEmitter.Start();
- }
-
- foreach (FMOD_StudioEventEmitter studioEventEmitter in escapePod.GetComponentsInChildren(true))
- {
- studioEventEmitter.Start();
- }
-
- MultiplayerCinematicReference reference = escapePod.AddComponent();
- foreach (PlayerCinematicController controller in escapePod.GetComponentsInChildren(true))
- {
- reference.AddController(controller);
- }
+ studioEventEmitter.Start();
}
- public bool SpawnsOwnChildren()
+ MultiplayerCinematicReference reference = escapePod.EnsureComponent();
+ foreach (PlayerCinematicController controller in escapePod.GetComponentsInChildren(true))
{
- return false;
+ reference.AddController(controller);
}
}
+ public bool SpawnsOwnChildren() => false;
}
diff --git a/NitroxClient/GameLogic/Spawning/WorldEntities/WorldEntitySpawnerResolver.cs b/NitroxClient/GameLogic/Spawning/WorldEntities/WorldEntitySpawnerResolver.cs
index fa251e7f84..0bd4d55890 100644
--- a/NitroxClient/GameLogic/Spawning/WorldEntities/WorldEntitySpawnerResolver.cs
+++ b/NitroxClient/GameLogic/Spawning/WorldEntities/WorldEntitySpawnerResolver.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using NitroxClient.GameLogic.PlayerLogic.PlayerModel.Abstract;
using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel_Subnautica.DataStructures;
@@ -22,20 +21,20 @@ public class WorldEntitySpawnerResolver
private readonly Dictionary customSpawnersByTechType = new();
- public WorldEntitySpawnerResolver(EntityMetadataManager entityMetadataManager, PlayerManager playerManager, ILocalNitroxPlayer localPlayer, Entities entities, SimulationOwnership simulationOwnership)
+ public WorldEntitySpawnerResolver(EntityMetadataManager entityMetadataManager, PlayerManager playerManager, LocalPlayer localPlayer, Entities entities, SimulationOwnership simulationOwnership)
{
customSpawnersByTechType[TechType.Crash] = new CrashEntitySpawner();
- customSpawnersByTechType[TechType.EscapePod] = new EscapePodWorldEntitySpawner(entityMetadataManager);
+ customSpawnersByTechType[TechType.EscapePod] = new EscapePodWorldEntitySpawner(localPlayer);
- vehicleWorldEntitySpawner = new(entities);
- prefabPlaceholderEntitySpawner = new(defaultEntitySpawner);
+ vehicleWorldEntitySpawner = new VehicleWorldEntitySpawner(entities);
+ prefabPlaceholderEntitySpawner = new PrefabPlaceholderEntitySpawner(defaultEntitySpawner);
placeholderGroupWorldEntitySpawner = new PlaceholderGroupWorldEntitySpawner(entities, this, defaultEntitySpawner, entityMetadataManager, prefabPlaceholderEntitySpawner);
playerWorldEntitySpawner = new PlayerWorldEntitySpawner(playerManager, localPlayer);
serializedWorldEntitySpawner = new SerializedWorldEntitySpawner();
- geyserWorldEntitySpawner = new(entities);
+ geyserWorldEntitySpawner = new GeyserWorldEntitySpawner(entities);
reefbackChildEntitySpawner = new ReefbackChildEntitySpawner();
reefbackEntitySpawner = new ReefbackEntitySpawner(reefbackChildEntitySpawner);
- creatureRespawnEntitySpawner = new(simulationOwnership);
+ creatureRespawnEntitySpawner = new CreatureRespawnEntitySpawner(simulationOwnership);
}
public IWorldEntitySpawner ResolveEntitySpawner(WorldEntity entity)
diff --git a/NitroxClient/GameLogic/Spawning/WorldEntitySpawner.cs b/NitroxClient/GameLogic/Spawning/WorldEntitySpawner.cs
index 26e073d9ba..baef65683f 100644
--- a/NitroxClient/GameLogic/Spawning/WorldEntitySpawner.cs
+++ b/NitroxClient/GameLogic/Spawning/WorldEntitySpawner.cs
@@ -19,7 +19,7 @@ public class WorldEntitySpawner : SyncEntitySpawner
private readonly WorldEntitySpawnerResolver worldEntitySpawnResolver;
private readonly Dictionary batchCellsById;
- public WorldEntitySpawner(EntityMetadataManager entityMetadataManager, PlayerManager playerManager, ILocalNitroxPlayer localPlayer, Entities entities, SimulationOwnership simulationOwnership)
+ public WorldEntitySpawner(EntityMetadataManager entityMetadataManager, PlayerManager playerManager, LocalPlayer localPlayer, Entities entities, SimulationOwnership simulationOwnership)
{
worldEntitySpawnResolver = new WorldEntitySpawnerResolver(entityMetadataManager, playerManager, localPlayer, entities, simulationOwnership);
diff --git a/NitroxModel/DataStructures/GameLogic/Entities/EscapePodEntity.cs b/NitroxModel/DataStructures/GameLogic/Entities/EscapePodWorldEntity.cs
similarity index 67%
rename from NitroxModel/DataStructures/GameLogic/Entities/EscapePodEntity.cs
rename to NitroxModel/DataStructures/GameLogic/Entities/EscapePodWorldEntity.cs
index d1c98a92c3..cf28e3b4e7 100644
--- a/NitroxModel/DataStructures/GameLogic/Entities/EscapePodEntity.cs
+++ b/NitroxModel/DataStructures/GameLogic/Entities/EscapePodWorldEntity.cs
@@ -12,9 +12,6 @@ namespace NitroxModel.DataStructures.GameLogic.Entities;
public class EscapePodWorldEntity : GlobalRootEntity
{
[DataMember(Order = 1)]
- public bool Damaged { get; set; }
-
- [DataMember(Order = 2)]
public List Players { get; set; }
[IgnoreConstructor]
@@ -25,28 +22,25 @@ protected EscapePodWorldEntity()
public EscapePodWorldEntity(NitroxVector3 position, NitroxId id, EntityMetadata metadata)
{
+ Transform = new NitroxTransform(position, NitroxQuaternion.Identity, NitroxVector3.One);
Id = id;
Metadata = metadata;
- Transform = new NitroxTransform(position, NitroxQuaternion.Identity, NitroxVector3.Zero);
- Players = new List();
+ Players = [];
Level = 0;
TechType = new NitroxTechType("EscapePod");
- Damaged = true;
SpawnedByServer = true;
-
- ChildEntities = new List();
+ ChildEntities = [];
}
/// Used for deserialization
- public EscapePodWorldEntity(bool damaged, List players, NitroxTransform transform, int level, string classId, bool spawnedByServer, NitroxId id, NitroxTechType techType, EntityMetadata metadata, NitroxId parentId, List childEntities) :
+ public EscapePodWorldEntity(List players, NitroxTransform transform, int level, string classId, bool spawnedByServer, NitroxId id, NitroxTechType techType, EntityMetadata metadata, NitroxId parentId, List childEntities) :
base(transform, level, classId, spawnedByServer, id, techType, metadata, parentId, childEntities)
{
- Damaged = damaged;
Players = players;
}
public override string ToString()
{
- return $"[EscapePodWorldEntity Damaged: {Damaged} {base.ToString()}]";
+ return $"[EscapePodWorldEntity Players: [{string.Join(", ", Players)}] {base.ToString()}]";
}
}
diff --git a/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EntityMetadata.cs b/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EntityMetadata.cs
index 9f2233ea93..bdb0c4b860 100644
--- a/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EntityMetadata.cs
+++ b/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EntityMetadata.cs
@@ -20,7 +20,7 @@ namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata
[ProtoInclude(60, typeof(ConstructorMetadata))]
[ProtoInclude(61, typeof(FlashlightMetadata))]
[ProtoInclude(62, typeof(BatteryMetadata))]
- [ProtoInclude(63, typeof(RepairedComponentMetadata))]
+ [ProtoInclude(63, typeof(EscapePodMetadata))]
[ProtoInclude(64, typeof(CrafterMetadata))]
[ProtoInclude(65, typeof(PlantableMetadata))]
[ProtoInclude(66, typeof(CyclopsMetadata))]
diff --git a/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EscapePodMetadata.cs b/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EscapePodMetadata.cs
new file mode 100644
index 0000000000..b95bb825d3
--- /dev/null
+++ b/NitroxModel/DataStructures/GameLogic/Entities/Metadata/EscapePodMetadata.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.Serialization;
+using BinaryPack.Attributes;
+
+namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata;
+
+[Serializable]
+[DataContract]
+public class EscapePodMetadata : EntityMetadata
+{
+ [DataMember(Order = 1)]
+ public bool PodRepaired { get; }
+
+ [DataMember(Order = 2)]
+ public bool RadioRepaired { get; }
+
+ [IgnoreConstructor]
+ protected EscapePodMetadata()
+ {
+ //Constructor for serialization. Has to be "protected" for json serialization.
+ }
+
+ public EscapePodMetadata(bool podRepaired, bool radioRepaired)
+ {
+ PodRepaired = podRepaired;
+ RadioRepaired = radioRepaired;
+ }
+
+ public override string ToString()
+ {
+ return $"[{nameof(EscapePodMetadata)} - PodRepaired: {PodRepaired}, RadioRepaired: {RadioRepaired}]";
+ }
+}
diff --git a/NitroxModel/DataStructures/GameLogic/Entities/Metadata/RepairedComponentMetadata.cs b/NitroxModel/DataStructures/GameLogic/Entities/Metadata/RepairedComponentMetadata.cs
deleted file mode 100644
index 3223e96523..0000000000
--- a/NitroxModel/DataStructures/GameLogic/Entities/Metadata/RepairedComponentMetadata.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-using System;
-using System.Runtime.Serialization;
-using BinaryPack.Attributes;
-
-namespace NitroxModel.DataStructures.GameLogic.Entities.Metadata;
-
-[Serializable]
-[DataContract]
-public class RepairedComponentMetadata : EntityMetadata
-{
- [DataMember(Order = 1)]
- public NitroxTechType TechType { get; }
-
- [IgnoreConstructor]
- protected RepairedComponentMetadata()
- {
- //Constructor for serialization. Has to be "protected" for json serialization.
- }
-
- public RepairedComponentMetadata(NitroxTechType techType)
- {
- TechType = techType;
- }
-
- public override string ToString()
- {
- return $"[RepairedComponentMetadata - TechType: {TechType}]";
- }
-}
diff --git a/NitroxPatcher/Patches/Dynamic/EscapePod_Awake_Patch.cs b/NitroxPatcher/Patches/Dynamic/EscapePod_Awake_Patch.cs
index 40247b8c48..f52c1d4559 100644
--- a/NitroxPatcher/Patches/Dynamic/EscapePod_Awake_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/EscapePod_Awake_Patch.cs
@@ -10,6 +10,6 @@ public sealed partial class EscapePod_Awake_Patch : NitroxPatch, IDynamicPatch
public static bool Prefix(EscapePod __instance)
{
- return !EscapePodWorldEntitySpawner.SURPRESS_ESCAPE_POD_AWAKE_METHOD;
+ return !EscapePodWorldEntitySpawner.SuppressEscapePodAwakeMethod;
}
}
diff --git a/NitroxPatcher/Patches/Dynamic/EscapePod_OnRepair_Patch.cs b/NitroxPatcher/Patches/Dynamic/EscapePod_OnRepair_Patch.cs
index 465a18ef78..09ccbf72ae 100644
--- a/NitroxPatcher/Patches/Dynamic/EscapePod_OnRepair_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/EscapePod_OnRepair_Patch.cs
@@ -1,21 +1,22 @@
using System.Reflection;
using NitroxClient.GameLogic;
+using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures;
using NitroxModel.Helper;
-using NitroxModel_Subnautica.DataStructures;
namespace NitroxPatcher.Patches.Dynamic;
public sealed partial class EscapePod_OnRepair_Patch : NitroxPatch, IDynamicPatch
{
- public static readonly MethodInfo TARGET_METHOD = Reflect.Method((EscapePod t) => t.OnRepair());
+ private static readonly MethodInfo TARGET_METHOD = Reflect.Method((EscapePod t) => t.OnRepair());
public static void Prefix(EscapePod __instance)
{
- if (__instance.TryGetIdOrWarn(out NitroxId id))
+ if (__instance.TryGetIdOrWarn(out NitroxId id) &&
+ Resolve().TryExtract(__instance, out EntityMetadata metadata))
{
- Resolve().BroadcastMetadataUpdate(id, new RepairedComponentMetadata(TechType.EscapePod.ToDto()));
+ Resolve().BroadcastMetadataUpdate(id, metadata);
}
}
}
diff --git a/NitroxPatcher/Patches/Dynamic/Radio_OnRepair_Patch.cs b/NitroxPatcher/Patches/Dynamic/Radio_OnRepair_Patch.cs
index 478f541f8c..eb0a0fbc8b 100644
--- a/NitroxPatcher/Patches/Dynamic/Radio_OnRepair_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/Radio_OnRepair_Patch.cs
@@ -1,9 +1,10 @@
using System.Reflection;
using NitroxClient.GameLogic;
+using NitroxClient.GameLogic.Spawning.Metadata;
+using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.Helper;
-using NitroxModel_Subnautica.DataStructures;
namespace NitroxPatcher.Patches.Dynamic;
@@ -13,9 +14,11 @@ public sealed partial class Radio_OnRepair_Patch : NitroxPatch, IDynamicPatch
public static void Prefix(Radio __instance)
{
- if (__instance.TryGetIdOrWarn(out NitroxId id))
+ if (__instance.TryGetComponentInParent(out EscapePod pod) &&
+ pod.TryGetIdOrWarn(out NitroxId id) &&
+ Resolve().TryExtract(pod, out EntityMetadata metadata))
{
- Resolve().BroadcastMetadataUpdate(id, new RepairedComponentMetadata(TechType.Radio.ToDto()));
+ Resolve().BroadcastMetadataUpdate(id, metadata);
}
}
}
diff --git a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_HandleInput_Patch.cs b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_HandleInput_Patch.cs
index 569891c1ad..5096d149a2 100644
--- a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_HandleInput_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_HandleInput_Patch.cs
@@ -52,6 +52,7 @@ private static bool HandleButtonHeld(uGUI_SceneIntro instance)
if (!uGUI_SceneIntro_IntroSequence_Patch.IsWaitingForPartner &&
Resolve().IntroCinematicMode == IntroCinematicMode.SINGLEPLAYER) // Skipping intro alone
{
+ EscapePod.main.DamageRadio(); // This is a special edge case where neither our EscapePodMetadataProcessor nor the intro is damaging the radio. Therefore we do it manually.
uGUI_SceneIntro_IntroSequence_Patch.SkipLocalCinematic(instance);
return false;
}
diff --git a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs
index 9f558fc3c5..79eba60da1 100644
--- a/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs
+++ b/NitroxPatcher/Patches/Dynamic/uGUI_SceneIntro_IntroSequence_Patch.cs
@@ -112,9 +112,11 @@ private static bool IsRemoteCinematicReady(uGUI_SceneIntro uGuiSceneIntro)
return true;
}
- if (GameModeUtils.currentGameMode.HasFlag(GameModeOption.Creative))
+ // Skipping intro if creative like in normal SN or in debug configuration
+ if (!NitroxEnvironment.IsReleaseMode ||
+ GameModeUtils.currentGameMode.HasFlag(GameModeOption.Creative))
{
- SkipLocalCinematic(uGuiSceneIntro); // Skipping intro if Creative like in normal SN
+ SkipLocalCinematic(uGuiSceneIntro);
return false;
}
@@ -199,8 +201,18 @@ private static void EndRemoteCinematic()
public static void SkipLocalCinematic(uGUI_SceneIntro uGuiSceneIntro)
{
+ // EscapePod.DamageRadio() is called by GuiSceneIntro.Stop(true) but is undesired. We revert it here
+ LiveMixin radioLiveMixin = EscapePod.main.radioSpawner.spawnedObj.GetComponent().liveMixin;
+ float radioHealthBefore = radioLiveMixin.health;
+
uGuiSceneIntro.Stop(true);
+ radioLiveMixin.health = radioHealthBefore;
+ if (radioLiveMixin.IsFullHealth())
+ {
+ Object.Destroy(radioLiveMixin.loopingDamageEffectObj);
+ }
+
Transform introFireHolder = EscapePod.main.transform.Find("Intro");
if (introFireHolder) // Can be null if called very early
{
diff --git a/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs b/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs
deleted file mode 100644
index 99a125e18a..0000000000
--- a/NitroxPatcher/Patches/Persistent/MainGameController_ShouldPlayIntro_Patch.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-#if DEBUG
-using System.Reflection;
-using NitroxModel.Helper;
-using NitroxPatcher.Patches.Dynamic;
-
-namespace NitroxPatcher.Patches.Persistent;
-
-public sealed partial class MainGameController_ShouldPlayIntro_Patch : NitroxPatch, IPersistentPatch
-{
- private static readonly MethodInfo TARGET_METHOD = Reflect.Method(() => MainGameController.ShouldPlayIntro());
-
- public static void Postfix(ref bool __result)
- {
- __result = false;
- uGUI_SceneIntro_IntroSequence_Patch.SkipLocalCinematic(uGUI.main.intro);
- }
-}
-#endif
diff --git a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
index 6d9fa4b4fb..d7844308aa 100644
--- a/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
+++ b/NitroxServer/Communication/Packets/Processors/PlayerJoiningMultiplayerSessionProcessor.cs
@@ -44,6 +44,11 @@ public override void Process(PlayerJoiningMultiplayerSession packet, INitroxConn
Player player = playerManager.PlayerConnected(connection, packet.ReservationKey, out bool wasBrandNewPlayer);
NitroxId assignedEscapePodId = world.EscapePodManager.AssignPlayerToEscapePod(player.Id, out Optional newlyCreatedEscapePod);
+ if (wasBrandNewPlayer)
+ {
+ player.SubRootId = assignedEscapePodId;
+ }
+
if (newlyCreatedEscapePod.HasValue)
{
SpawnEntities spawnNewEscapePod = new(newlyCreatedEscapePod.Value);
diff --git a/NitroxServer/GameLogic/EscapePodManager.cs b/NitroxServer/GameLogic/EscapePodManager.cs
index a7ba742f72..46f8e58bb3 100644
--- a/NitroxServer/GameLogic/EscapePodManager.cs
+++ b/NitroxServer/GameLogic/EscapePodManager.cs
@@ -3,154 +3,153 @@
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
+using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.DataStructures.Unity;
using NitroxModel.DataStructures.Util;
using NitroxServer.GameLogic.Entities;
-namespace NitroxServer.GameLogic
+namespace NitroxServer.GameLogic;
+
+public class EscapePodManager
{
- public class EscapePodManager
- {
- public const int PLAYERS_PER_ESCAPEPOD = 50;
- public const int ESCAPE_POD_X_OFFSET = 40;
+ private const int PLAYERS_PER_ESCAPEPOD = 50;
- private readonly EntityRegistry entityRegistry;
- private readonly ThreadSafeDictionary escapePodsByPlayerId = new ThreadSafeDictionary();
- private EscapePodWorldEntity podForNextPlayer;
- private readonly string seed;
+ private readonly EntityRegistry entityRegistry;
+ private readonly ThreadSafeDictionary escapePodsByPlayerId = new();
+ private EscapePodWorldEntity podForNextPlayer;
+ private readonly string seed;
- private readonly RandomStartGenerator randomStart;
+ private readonly RandomStartGenerator randomStart;
- public EscapePodManager(EntityRegistry entityRegistry, RandomStartGenerator randomStart, string seed)
- {
- this.seed = seed;
- this.randomStart = randomStart;
- this.entityRegistry = entityRegistry;
+ public EscapePodManager(EntityRegistry entityRegistry, RandomStartGenerator randomStart, string seed)
+ {
+ this.seed = seed;
+ this.randomStart = randomStart;
+ this.entityRegistry = entityRegistry;
+
+ List escapePods = entityRegistry.GetEntities();
+
+ InitializePodForNextPlayer(escapePods);
+ InitializeEscapePodsByPlayerId(escapePods);
+ }
- List escapePods = entityRegistry.GetEntities();
+ public NitroxId AssignPlayerToEscapePod(ushort playerId, out Optional newlyCreatedPod)
+ {
+ newlyCreatedPod = Optional.Empty;
- InitializePodForNextPlayer(escapePods);
- InitializeEscapePodsByPlayerId(escapePods);
+ if (escapePodsByPlayerId.TryGetValue(playerId, out EscapePodWorldEntity podEntity))
+ {
+ return podEntity.Id;
}
- public NitroxId AssignPlayerToEscapePod(ushort playerId, out Optional newlyCreatedPod)
+ if (IsPodFull(podForNextPlayer))
{
- newlyCreatedPod = Optional.Empty;
+ newlyCreatedPod = Optional.Of(CreateNewEscapePod());
+ podForNextPlayer = newlyCreatedPod.Value;
+ }
- if (escapePodsByPlayerId.ContainsKey(playerId))
- {
- return escapePodsByPlayerId[playerId].Id;
- }
+ podForNextPlayer.Players.Add(playerId);
+ escapePodsByPlayerId[playerId] = podForNextPlayer;
- if (IsPodFull(podForNextPlayer))
- {
- newlyCreatedPod = Optional.Of(CreateNewEscapePod());
- podForNextPlayer = newlyCreatedPod.Value;
- }
+ return podForNextPlayer.Id;
+ }
- podForNextPlayer.Players.Add(playerId);
- escapePodsByPlayerId[playerId] = podForNextPlayer;
+ private EscapePodWorldEntity CreateNewEscapePod()
+ {
+ EscapePodWorldEntity escapePod = new(GetStartPosition(), new NitroxId(), new EscapePodMetadata(false, false));
- return podForNextPlayer.Id;
- }
+ escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "5c06baec-0539-4f26-817d-78443548cc52", new NitroxTechType("Radio"), 0, null, escapePod.Id));
+ escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "c0175cf7-0b6a-4a1d-938f-dad0dbb6fa06", new NitroxTechType("MedicalCabinet"), 0, null, escapePod.Id));
+ escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "9f16d82b-11f4-4eeb-aedf-f2fa2bfca8e3", new NitroxTechType("Fabricator"), 0, null, escapePod.Id));
+ escapePod.ChildEntities.Add(new InventoryEntity(0, new NitroxId(), new NitroxTechType("SmallStorage"), null, escapePod.Id, []));
- private EscapePodWorldEntity CreateNewEscapePod()
- {
- EscapePodWorldEntity escapePod = new EscapePodWorldEntity(GetStartPosition(), new NitroxId(), null);
+ entityRegistry.AddOrUpdate(escapePod);
- escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "5c06baec-0539-4f26-817d-78443548cc52", new NitroxTechType("Radio"), 0, null, escapePod.Id));
- escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "c0175cf7-0b6a-4a1d-938f-dad0dbb6fa06", new NitroxTechType("MedicalCabinet"), 0, null, escapePod.Id));
- escapePod.ChildEntities.Add(new PrefabChildEntity(new NitroxId(), "9f16d82b-11f4-4eeb-aedf-f2fa2bfca8e3", new NitroxTechType("Fabricator"), 0, null, escapePod.Id));
- escapePod.ChildEntities.Add(new InventoryEntity(0, new NitroxId(), new NitroxTechType("SmallStorage"), null, escapePod.Id, new List()));
+ return escapePod;
+ }
- entityRegistry.AddOrUpdate(escapePod);
+ private NitroxVector3 GetStartPosition()
+ {
+ List escapePods = entityRegistry.GetEntities();
- return escapePod;
- }
+ Random rnd = new(seed.GetHashCode());
+ NitroxVector3 position = randomStart.GenerateRandomStartPosition(rnd);
- private NitroxVector3 GetStartPosition()
+ if (escapePods.Count == 0)
{
- List escapePods = entityRegistry.GetEntities();
-
- Random rnd = new Random(seed.GetHashCode());
- NitroxVector3 position = randomStart.GenerateRandomStartPosition(rnd);
+ return position;
+ }
- if (escapePods.Count == 0)
+ foreach (EscapePodWorldEntity escapePodModel in escapePods)
+ {
+ if (position == NitroxVector3.Zero)
{
- return position;
+ break;
}
- foreach (EscapePodWorldEntity escapePodModel in escapePods)
+ if (escapePodModel.Transform.Position != position)
{
- if (position == NitroxVector3.Zero)
- {
- break;
- }
-
- if (escapePodModel.Transform.Position != position)
- {
- return position;
- }
+ return position;
}
+ }
- float xNormed = (float)rnd.NextDouble();
- float zNormed = (float)rnd.NextDouble();
+ float xNormed = (float)rnd.NextDouble();
+ float zNormed = (float)rnd.NextDouble();
- if (xNormed < 0.3f)
- {
- xNormed = 0.3f;
- }
- else if (xNormed > 0.7f)
- {
- xNormed = 0.7f;
- }
+ if (xNormed < 0.3f)
+ {
+ xNormed = 0.3f;
+ }
+ else if (xNormed > 0.7f)
+ {
+ xNormed = 0.7f;
+ }
- if (zNormed < 0.3f)
- {
- zNormed = 0.3f;
- }
- else if (zNormed > 0.7f)
- {
- zNormed = 0.7f;
- }
+ if (zNormed < 0.3f)
+ {
+ zNormed = 0.3f;
+ }
+ else if (zNormed > 0.7f)
+ {
+ zNormed = 0.7f;
+ }
- NitroxVector3 lastEscapePodPosition = escapePods[escapePods.Count - 1].Transform.Position;
+ NitroxVector3 lastEscapePodPosition = escapePods[escapePods.Count - 1].Transform.Position;
- float x = xNormed * 100 - 50;
- float z = zNormed * 100 - 50;
+ float x = xNormed * 100 - 50;
+ float z = zNormed * 100 - 50;
- return new NitroxVector3(lastEscapePodPosition.X + x, 0, lastEscapePodPosition.Z + z);
- }
+ return new NitroxVector3(lastEscapePodPosition.X + x, 0, lastEscapePodPosition.Z + z);
+ }
- private void InitializePodForNextPlayer(List escapePods)
+ private void InitializePodForNextPlayer(List escapePods)
+ {
+ foreach (EscapePodWorldEntity pod in escapePods)
{
- foreach (EscapePodWorldEntity pod in escapePods)
+ if (!IsPodFull(pod))
{
- if (!IsPodFull(pod))
- {
- podForNextPlayer = pod;
- return;
- }
+ podForNextPlayer = pod;
+ return;
}
-
- podForNextPlayer = CreateNewEscapePod();
}
- private void InitializeEscapePodsByPlayerId(List escapePods)
+ podForNextPlayer = CreateNewEscapePod();
+ }
+
+ private void InitializeEscapePodsByPlayerId(List escapePods)
+ {
+ escapePodsByPlayerId.Clear();
+ foreach (EscapePodWorldEntity pod in escapePods)
{
- escapePodsByPlayerId.Clear();
- foreach (EscapePodWorldEntity pod in escapePods)
+ foreach (ushort playerId in pod.Players)
{
- foreach (ushort playerId in pod.Players)
- {
- escapePodsByPlayerId[playerId] = pod;
- }
+ escapePodsByPlayerId[playerId] = pod;
}
}
+ }
- private static bool IsPodFull(EscapePodWorldEntity pod)
- {
- return pod.Players.Count >= PLAYERS_PER_ESCAPEPOD;
- }
+ private static bool IsPodFull(EscapePodWorldEntity pod)
+ {
+ return pod.Players.Count >= PLAYERS_PER_ESCAPEPOD;
}
}