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,163 @@
using System.Collections;
using System.Collections.Generic;
using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.Util;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;
namespace NitroxClient.GameLogic.Spawning.WorldEntities;
/// <remarks>
/// This spawner can't hold a SpawnSync function because it is also responsible for spawning its children
/// so the <see cref="SpawnAsync"/> function will still use sync spawning when possible and fall back to async when required.
/// </remarks>
public class PlaceholderGroupWorldEntitySpawner : IWorldEntitySpawner
{
private readonly Entities entities;
private readonly WorldEntitySpawnerResolver spawnerResolver;
private readonly DefaultWorldEntitySpawner defaultSpawner;
private readonly EntityMetadataManager entityMetadataManager;
private readonly PrefabPlaceholderEntitySpawner prefabPlaceholderEntitySpawner;
public PlaceholderGroupWorldEntitySpawner(Entities entities, WorldEntitySpawnerResolver spawnerResolver, DefaultWorldEntitySpawner defaultSpawner, EntityMetadataManager entityMetadataManager, PrefabPlaceholderEntitySpawner prefabPlaceholderEntitySpawner)
{
this.entities = entities;
this.spawnerResolver = spawnerResolver;
this.defaultSpawner = defaultSpawner;
this.entityMetadataManager = entityMetadataManager;
this.prefabPlaceholderEntitySpawner = prefabPlaceholderEntitySpawner;
}
public IEnumerator SpawnAsync(WorldEntity entity, Optional<GameObject> parent, EntityCell cellRoot, TaskResult<Optional<GameObject>> result)
{
if (entity is not PlaceholderGroupWorldEntity placeholderGroupEntity)
{
Log.Error($"[{nameof(PlaceholderGroupWorldEntitySpawner)}] Can't spawn {entity.Id} of type {entity.GetType()} because it is not a {nameof(PlaceholderGroupWorldEntity)}");
yield break;
}
TaskResult<Optional<GameObject>> prefabPlaceholderGroupTaskResult = new();
if (!defaultSpawner.SpawnSync(entity, parent, cellRoot, prefabPlaceholderGroupTaskResult))
{
yield return defaultSpawner.SpawnAsync(entity, parent, cellRoot, prefabPlaceholderGroupTaskResult);
}
Optional<GameObject> prefabPlaceholderGroupGameObject = prefabPlaceholderGroupTaskResult.Get();
if (!prefabPlaceholderGroupGameObject.HasValue)
{
yield break;
}
GameObject groupObject = prefabPlaceholderGroupGameObject.Value;
// Spawning PrefabPlaceholders as siblings to the group
PrefabPlaceholdersGroup prefabPlaceholderGroup = groupObject.GetComponent<PrefabPlaceholdersGroup>();
// Spawning all children iteratively
Stack<Entity> stack = new(placeholderGroupEntity.ChildEntities);
TaskResult<Optional<GameObject>> childResult = new();
Dictionary<NitroxId, GameObject> parentById = new()
{
{ entity.Id, groupObject }
};
while (stack.Count > 0)
{
// It may happen that the chunk is unloaded, and the group along so we just cancel this spawn behaviour
if (!groupObject)
{
yield break;
}
childResult.Set(Optional.Empty);
Entity current = stack.Pop();
switch (current)
{
case PrefabPlaceholderEntity prefabEntity:
if (!prefabPlaceholderEntitySpawner.SpawnSync(prefabEntity, groupObject, cellRoot, childResult))
{
yield return prefabPlaceholderEntitySpawner.SpawnAsync(prefabEntity, groupObject, cellRoot, childResult);
}
break;
case PlaceholderGroupWorldEntity groupEntity:
PrefabPlaceholder placeholder = prefabPlaceholderGroup.prefabPlaceholders[groupEntity.ComponentIndex];
yield return SpawnAsync(groupEntity, placeholder.transform.parent.gameObject, cellRoot, childResult);
break;
case WorldEntity worldEntity:
if (!SpawnWorldEntityChildSync(worldEntity, cellRoot, parentById.GetOrDefault(current.ParentId, null), childResult, out IEnumerator asyncInstructions))
{
yield return asyncInstructions;
}
break;
default:
Log.Error($"[{nameof(PlaceholderGroupWorldEntitySpawner)}] Can't spawn a child entity which is not a WorldEntity: {current}");
continue;
}
if (!childResult.value.HasValue)
{
Log.Error($"[{nameof(PlaceholderGroupWorldEntitySpawner)}] Spawning of child failed {current}");
continue;
}
GameObject childObject = childResult.value.Value;
entities.MarkAsSpawned(current);
parentById[current.Id] = childObject;
entityMetadataManager.ApplyMetadata(childObject, current.Metadata);
// PlaceholderGroupWorldEntity's children spawning is already handled by this function which is called recursively
if (current is not PlaceholderGroupWorldEntity)
{
// Adding children to be spawned by this loop
foreach (Entity slotEntityChild in current.ChildEntities)
{
stack.Push(slotEntityChild);
}
}
}
result.Set(prefabPlaceholderGroupGameObject);
}
public bool SpawnsOwnChildren() => true;
private IEnumerator SpawnWorldEntityChildAsync(WorldEntity worldEntity, EntityCell cellRoot, GameObject parent, TaskResult<Optional<GameObject>> worldEntityResult)
{
IWorldEntitySpawner spawner = spawnerResolver.ResolveEntitySpawner(worldEntity);
yield return spawner.SpawnAsync(worldEntity, parent, cellRoot, worldEntityResult);
if (!worldEntityResult.value.HasValue)
{
yield break;
}
GameObject spawnedObject = worldEntityResult.value.Value;
spawnedObject.transform.localPosition = worldEntity.Transform.LocalPosition.ToUnity();
spawnedObject.transform.localRotation = worldEntity.Transform.LocalRotation.ToUnity();
spawnedObject.transform.localScale = worldEntity.Transform.LocalScale.ToUnity();
}
private bool SpawnWorldEntityChildSync(WorldEntity worldEntity, EntityCell cellRoot, GameObject parent, TaskResult<Optional<GameObject>> worldEntityResult, out IEnumerator asyncInstructions)
{
IWorldEntitySpawner spawner = spawnerResolver.ResolveEntitySpawner(worldEntity);
if (spawner is not IWorldEntitySyncSpawner syncSpawner ||
!syncSpawner.SpawnSync(worldEntity, parent, cellRoot, worldEntityResult) ||
!worldEntityResult.value.HasValue)
{
asyncInstructions = SpawnWorldEntityChildAsync(worldEntity, cellRoot, parent, worldEntityResult);
return false;
}
GameObject spawnedObject = worldEntityResult.value.Value;
spawnedObject.transform.localPosition = worldEntity.Transform.LocalPosition.ToUnity();
spawnedObject.transform.localRotation = worldEntity.Transform.LocalRotation.ToUnity();
spawnedObject.transform.localScale = worldEntity.Transform.LocalScale.ToUnity();
asyncInstructions = null;
return true;
}
}