first commit
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user