first commit

This commit is contained in:
2025-07-06 00:23:46 +02:00
commit 38f50c8819
1788 changed files with 112878 additions and 0 deletions

View File

@@ -0,0 +1,179 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NitroxClient.Communication.Packets.Processors.Abstract;
using NitroxClient.GameLogic;
using NitroxClient.GameLogic.Bases;
using NitroxClient.GameLogic.Spawning.Bases;
using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.GameLogic.Entities.Bases;
using NitroxModel.Packets;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
namespace NitroxClient.Communication.Packets.Processors;
public class BuildingResyncProcessor : ClientPacketProcessor<BuildingResync>
{
private readonly Entities entities;
private readonly EntityMetadataManager entityMetadataManager;
public BuildingResyncProcessor(Entities entities, EntityMetadataManager entityMetadataManager)
{
this.entities = entities;
this.entityMetadataManager = entityMetadataManager;
}
public override void Process(BuildingResync packet)
{
if (!BuildingHandler.Main)
{
return;
}
BuildingHandler.Main.StartCoroutine(ResyncBuildingEntities(packet.BuildEntities, packet.ModuleEntities));
}
public IEnumerator ResyncBuildingEntities(Dictionary<BuildEntity, int> buildEntities, Dictionary<ModuleEntity, int> moduleEntities)
{
Stopwatch stopwatch = Stopwatch.StartNew();
BuildingHandler.Main.StartResync(buildEntities);
yield return UpdateEntities<Base, BuildEntity>(buildEntities.Keys.ToList(), OverwriteBase, IsInCloseProximity).OnYieldError(exception => Log.Error(exception, $"Encountered an exception while resyncing BuildEntities"));
BuildingHandler.Main.StartResync(moduleEntities);
yield return UpdateEntities<Constructable, ModuleEntity>(moduleEntities.Keys.ToList(), OverwriteModule, IsInCloseProximity).OnYieldError(exception => Log.Error(exception, $"Encountered an exception while resyncing ModuleEntities"));
BuildingHandler.Main.StopResync();
stopwatch.Stop();
int totalEntities = buildEntities.Count + moduleEntities.Count;
Log.InGame(Language.main.Get("Nitrox_FinishedResyncRequest").Replace("{TIME}", stopwatch.ElapsedMilliseconds.ToString()).Replace("{COUNT}", totalEntities.ToString()));
}
private bool IsInCloseProximity<C>(WorldEntity entity, C componentInWorld) where C : Component
{
return Vector3.Distance(entity.Transform.Position.ToUnity(), componentInWorld.transform.position) < 0.001f;
}
/// <summary>
/// Tries to overwrite components of the provided type found in GlobalRoot's hierarchy by the provided list of entities to update.
/// If no component is found to be corresponding to a provided entity, the entity will be spawned independently.
/// Other components of the provided type which weren't updated shall be destroyed.
/// </summary>
/// <remarks>
/// The provided list is modified by the function. Make sure it's not used somewhere else.
/// </remarks>
/// <typeparam name="C">The Unity component to be looked for</typeparam>
/// <typeparam name="E">The GlobalRootEntity type which will be updated</typeparam>
/// <param name="overwrite">A function to overwrite a given component by a given entity</param>
/// <param name="correspondingPredicate">
/// Predicate to determine if an entity can overwrite the GameObject of the provided component.
/// </param>
public IEnumerator UpdateEntities<C,E>(List<E> entitiesToUpdate, Func<C, E, IEnumerator> overwrite, Func<E, C, bool> correspondingPredicate) where C : Component where E : GlobalRootEntity
{
List<C> unmarkedComponents = new();
Dictionary<NitroxId, E> entitiesToUpdateById = entitiesToUpdate.ToDictionary(e => e.Id);
foreach (Transform childTransform in LargeWorldStreamer.main.globalRoot.transform)
{
if (childTransform.TryGetComponent(out C component))
{
if (component.TryGetNitroxId(out NitroxId id) && entitiesToUpdateById.TryGetValue(id, out E correspondingEntity))
{
yield return overwrite(component, correspondingEntity).OnYieldError(Log.Error);
entitiesToUpdate.Remove(correspondingEntity);
continue;
}
unmarkedComponents.Add(component);
}
}
for (int i = entitiesToUpdate.Count - 1; i >= 0; i--)
{
E entity = entitiesToUpdate[i];
C associatedComponent = unmarkedComponents.Find(c =>
correspondingPredicate(entity, c));
yield return overwrite(associatedComponent, entity).OnYieldError(Log.Error);
unmarkedComponents.Remove(associatedComponent);
entitiesToUpdate.RemoveAt(i);
}
for (int i = unmarkedComponents.Count - 1; i >= 0; i--)
{
Log.Info($"[{typeof(E)} RESYNC] Destroyed GameObject {unmarkedComponents[i].gameObject}");
GameObject.Destroy(unmarkedComponents[i].gameObject);
}
foreach (E entity in entitiesToUpdate)
{
Log.Info($"[{typeof(E)} RESYNC] spawning entity {entity.Id}");
yield return entities.SpawnEntityAsync(entity).OnYieldError(Log.Error);
}
}
public IEnumerator OverwriteBase(Base @base, BuildEntity buildEntity)
{
Log.Info($"[Base RESYNC] Overwriting base with id {buildEntity.Id}");
ClearBaseChildren(@base);
// Frame to let all children be deleted properly
yield return Yielders.WaitForEndOfFrame;
yield return BuildEntitySpawner.SetupBase(buildEntity, @base, entities);
yield return MoonpoolManager.RestoreMoonpools(buildEntity.ChildEntities.OfType<MoonpoolEntity>(), @base);
yield return entities.SpawnBatchAsync(buildEntity.ChildEntities.OfType<PlayerWorldEntity>().ToList<Entity>(), false, false);
foreach (Entity childEntity in buildEntity.ChildEntities)
{
switch (childEntity)
{
case MapRoomEntity mapRoomEntity:
yield return InteriorPieceEntitySpawner.RestoreMapRoom(@base, mapRoomEntity);
break;
case BaseLeakEntity baseLeakEntity:
yield return entities.SpawnEntityAsync(baseLeakEntity, true);
break;
}
}
}
public IEnumerator OverwriteModule(Constructable constructable, ModuleEntity moduleEntity)
{
Log.Info($"[Module RESYNC] Overwriting module with id {moduleEntity.Id}");
ModuleEntitySpawner.ApplyModuleData(moduleEntity, constructable.gameObject);
entityMetadataManager.ApplyMetadata(constructable.gameObject, moduleEntity.Metadata);
yield break;
}
/// <summary>
/// Destroys manually ghosts, modules, interior pieces and vehicles of a base
/// </summary>
/// <remarks>
/// This is the destructive way of clearing the base, if the base isn't modified consequently, IBaseModuleGeometry under the base cells may start spamming errors.
/// </remarks>
public static void ClearBaseChildren(Base @base)
{
for (int i = @base.transform.childCount - 1; i >= 0; i--)
{
Transform child = @base.transform.GetChild(i);
if (child.GetComponent<IBaseModule>().AliveOrNull() || child.GetComponent<Constructable>())
{
UnityEngine.Object.Destroy(child.gameObject);
}
}
foreach (VehicleDockingBay vehicleDockingBay in @base.GetComponentsInChildren<VehicleDockingBay>(true))
{
if (vehicleDockingBay.dockedVehicle)
{
UnityEngine.Object.Destroy(vehicleDockingBay.dockedVehicle.gameObject);
vehicleDockingBay.SetVehicleUndocked();
}
}
}
}