Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement more entities #2087

Merged
merged 6 commits into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Synced oxygen pipes, integrated beacons to entity system, allow conta…
…iners in water
  • Loading branch information
tornac1234 committed Nov 7, 2023
commit 74a1d15ca661cac55a33ca334d705321fd16fe8c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using NitroxClient.Communication.Abstract;
Expand Down Expand Up @@ -38,8 +38,8 @@ public override void Process(InitialPlayerSync packet)

private IEnumerator ProcessInitialSyncPacket(object sender, EventArgs eventArgs)
{
// Some packets should not fire during game session join but only afterwards so that initialized/spawned game objects don't trigger packet sending again.
using (PacketSuppressor<PingRenamed>.Suppress())
// Some packets should not fire during game session join but only afterwards so that initialized/spawned game objects don't trigger packet sending again.
using (PacketSuppressor<EntityMetadataUpdate>.Suppress())
Jannify marked this conversation as resolved.
Show resolved Hide resolved
{
bool moreProcessorsToRun;
do
Expand Down

This file was deleted.

4 changes: 3 additions & 1 deletion NitroxClient/GameLogic/Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NitroxClient.GameLogic.Spawning.Bases;
using NitroxClient.GameLogic.Spawning.Metadata;
using NitroxClient.GameLogic.Spawning.Metadata.Extractor;
using NitroxClient.GameLogic.Spawning.WorldEntities;
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures;
Expand Down Expand Up @@ -54,11 +55,12 @@ public Entities(IPacketSender packetSender, ThrottledPacketSender throttledPacke
entitySpawnersByType[typeof(EscapePodWorldEntity)] = entitySpawnersByType[typeof(WorldEntity)];
entitySpawnersByType[typeof(PlayerWorldEntity)] = entitySpawnersByType[typeof(WorldEntity)];
entitySpawnersByType[typeof(VehicleWorldEntity)] = entitySpawnersByType[typeof(WorldEntity)];
entitySpawnersByType[typeof(GlobalRootEntity)] = entitySpawnersByType[typeof(WorldEntity)];
entitySpawnersByType[typeof(GlobalRootEntity)] = new GlobalRootEntitySpawner();
entitySpawnersByType[typeof(BuildEntity)] = new BuildEntitySpawner(this);
entitySpawnersByType[typeof(ModuleEntity)] = new ModuleEntitySpawner(this);
entitySpawnersByType[typeof(GhostEntity)] = new GhostEntitySpawner();
entitySpawnersByType[typeof(InteriorPieceEntity)] = new InteriorPieceEntitySpawner(this);
entitySpawnersByType[typeof(OxygenPipeEntity)] = new OxygenPipeEntitySpawner(this, entitySpawnersByType[typeof(WorldEntity)]);
}

public void EntityMetadataChanged(object o, NitroxId id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,5 @@ public override IEnumerator Process(InitialPlayerSync packet, WaitScreen.ManualW
yield return entities.SpawnBatchAsync(packet.GlobalRootEntities);
}
}
}
}
38 changes: 38 additions & 0 deletions NitroxClient/GameLogic/Items.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,44 @@ public void Dropped(GameObject gameObject, TechType? techType = null)
droppedItem.ParentId = parentId;
}
}

if (droppedItem is not GlobalRootEntity && gameObject.TryGetComponent(out LargeWorldEntity largeWorldEntity) &&
largeWorldEntity.initialCellLevel == LargeWorldEntity.CellLevel.Global)
{
droppedItem = GlobalRootEntity.From(droppedItem);
}

if (gameObject.TryGetComponent(out OxygenPipe oxygenPipe))
{
// We can't spawn an OxygenPipe without its parent and root
// Dropped patch is called in OxygenPipe.PlaceInWorld which is why OxygenPipe.ghostModel is valid
IPipeConnection parentConnection = OxygenPipe.ghostModel.GetParent();
if (parentConnection == null || !parentConnection.GetGameObject() ||
!parentConnection.GetGameObject().TryGetNitroxId(out NitroxId parentPipeId))
{
Log.Error($"Couldn't find a valid reference to the OxygenPipe's parent pipe");
return;
}
IPipeConnection rootConnection = parentConnection.GetRoot();
if (rootConnection == null || !rootConnection.GetGameObject() ||
!rootConnection.GetGameObject().TryGetNitroxId(out NitroxId rootPipeId))
{
Log.Error($"Couldn't find a valid reference to the OxygenPipe's root pipe");
return;
}

OxygenPipeEntity pipeEntity = OxygenPipeEntity.From(droppedItem);

// Updating the local pipe's references to replace the UniqueIdentifier's id by their NitroxEntity's id
oxygenPipe.rootPipeUID = rootPipeId.ToString();
oxygenPipe.parentPipeUID = parentPipeId.ToString();

pipeEntity.RootPipeId = rootPipeId;
pipeEntity.ParentPipeId = parentPipeId;
pipeEntity.ParentPosition = parentConnection.GetAttachPoint().ToDto();
droppedItem = pipeEntity;
}

Log.Debug($"Dropping item: {droppedItem}");

packetSender.Send(new EntitySpawnedByClient(droppedItem));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using NitroxClient.Communication;
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;
using NitroxModel.Packets;
using UnityEngine;

namespace NitroxClient.GameLogic.Spawning.Metadata;

public class BeaconMetadataProcessor : GenericEntityMetadataProcessor<BeaconMetadata>
{
public override void ProcessMetadata(GameObject gameObject, BeaconMetadata metadata)
{
if (gameObject.TryGetComponent(out Beacon beacon))
{
using (PacketSuppressor<EntityMetadataUpdate>.Suppress())
{
beacon.beaconLabel.SetLabel(metadata.Label);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using NitroxModel.DataStructures.GameLogic.Entities.Metadata;

namespace NitroxClient.GameLogic.Spawning.Metadata.Extractor;

public class BeaconMetadataExtractor : GenericEntityMetadataExtractor<BeaconLabel, BeaconMetadata>
{
public override BeaconMetadata Extract(BeaconLabel beaconLabel)
{
return new(beaconLabel.GetLabel());
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
using System.Collections;
using NitroxClient.MonoBehaviours;
using NitroxClient.Unity.Helper;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.Util;
using UnityEngine;

namespace NitroxClient.GameLogic.Spawning.WorldEntities
{
// TODO: Fix all of this
Jannify marked this conversation as resolved.
Show resolved Hide resolved
public class CrashEntitySpawner : IWorldEntitySpawner
{
private static readonly Quaternion spawnRotation = Quaternion.Euler(-90f, 0f, 0f);

/**
* Crash fish are spawned by the CrashHome in the Monobehaviours Start method
*/
Expand All @@ -17,7 +21,7 @@ public IEnumerator SpawnAsync(WorldEntity entity, Optional<GameObject> parent, E
{
CrashHome crashHome = parent.Value.GetComponent<CrashHome>();

GameObject gameObject = Object.Instantiate(crashHome.crashPrefab, Vector3.zero, Quaternion.Euler(-90f, 0f, 0f));
GameObject gameObject = GameObjectHelper.InstantiateWithId(crashHome.crashPrefab, entity.Id, rotation: spawnRotation);
gameObject.transform.SetParent(crashHome.transform, false);
crashHome.crash = gameObject.GetComponent<Crash>();
crashHome.spawnTime = -1;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections;
using NitroxClient.MonoBehaviours;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.Util;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;

namespace NitroxClient.GameLogic.Spawning.WorldEntities;

public class GlobalRootEntitySpawner : EntitySpawner<GlobalRootEntity>
{
public override IEnumerator SpawnAsync(GlobalRootEntity entity, TaskResult<Optional<GameObject>> result)
{
TaskResult<GameObject> gameObjectResult = new();
yield return DefaultWorldEntitySpawner.CreateGameObject(entity.TechType.ToUnity(), entity.ClassId, gameObjectResult);
GameObject gameObject = gameObjectResult.Get();
NitroxEntity.SetNewId(gameObject, entity.Id);

LargeWorldEntity largeWorldEntity = gameObject.EnsureComponent<LargeWorldEntity>();
largeWorldEntity.cellLevel = LargeWorldEntity.CellLevel.Global;
LargeWorld.main.streamer.cellManager.RegisterEntity(largeWorldEntity);
gameObject.transform.localPosition = entity.Transform.LocalPosition.ToUnity();
gameObject.transform.localRotation = entity.Transform.LocalRotation.ToUnity();
gameObject.transform.localScale = entity.Transform.LocalScale.ToUnity();

result.Set(Optional.Of(gameObject));
}

public override bool SpawnsOwnChildren(GlobalRootEntity entity)
{
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System.Collections;
using System.Collections.Generic;
using NitroxClient.Communication.Packets.Processors;
using NitroxClient.MonoBehaviours;
using NitroxModel.DataStructures;
using NitroxModel.DataStructures.GameLogic.Entities;
using NitroxModel.DataStructures.Util;
using NitroxModel_Subnautica.DataStructures;
using UnityEngine;

namespace NitroxClient.GameLogic.Spawning.WorldEntities;

public class OxygenPipeEntitySpawner : EntitySpawner<OxygenPipeEntity>
{
private readonly Entities entities;
private readonly WorldEntitySpawner worldEntitySpawner;
private readonly InitialPlayerSyncProcessor initialPlayerSyncProcessor;

private readonly Dictionary<NitroxId, List<OxygenPipe>> childrenPipeEntitiesByParentId;

public OxygenPipeEntitySpawner(Entities entities, IEntitySpawner worldEntitySpawner)
{
this.entities = entities;
this.worldEntitySpawner = (WorldEntitySpawner)worldEntitySpawner;
Jannify marked this conversation as resolved.
Show resolved Hide resolved

childrenPipeEntitiesByParentId = new();
}

public override IEnumerator SpawnAsync(OxygenPipeEntity entity, TaskResult<Optional<GameObject>> result)
{
if (!DefaultWorldEntitySpawner.TryGetCachedPrefab(out GameObject prefab, classId: entity.ClassId))
{
TaskResult<GameObject> prefabResult = new();
yield return DefaultWorldEntitySpawner.RequestPrefab(entity.ClassId, prefabResult);
if (!prefabResult.Get())
{
Log.Error($"Couldn't find a prefab for {nameof(OxygenPipeEntity)} of ClassId {entity.ClassId}");
yield break;
}
prefab = prefabResult.Get();
}

GameObject gameObject = Object.Instantiate(prefab);
if (!IsSpawnedPrefabValid(entity, gameObject, out OxygenPipe oxygenPipe, out string errorLog))
{
Log.Error(errorLog);
tornac1234 marked this conversation as resolved.
Show resolved Hide resolved
result.Set(Optional.Empty);
yield break;
}
NitroxEntity.TryGetComponentFrom(entity.ParentPipeId, out IPipeConnection parentConnection);
NitroxEntity.TryGetComponentFrom(entity.RootPipeId, out IPipeConnection rootConnection);
tornac1234 marked this conversation as resolved.
Show resolved Hide resolved
SetupObject(entity, gameObject, oxygenPipe, parentConnection, rootConnection);

result.Set(Optional.Of(gameObject));
}

public override bool SpawnSync(OxygenPipeEntity entity, TaskResult<Optional<GameObject>> result)
{
if (!DefaultWorldEntitySpawner.TryGetCachedPrefab(out GameObject prefab, classId: entity.ClassId))
{
return false;
}

GameObject gameObject = Object.Instantiate(prefab);
if (!IsSpawnedPrefabValid(entity, gameObject, out OxygenPipe oxygenPipe, out string errorLog))
{
Log.Error(errorLog);
return true;
}
NitroxEntity.TryGetComponentFrom(entity.ParentPipeId, out IPipeConnection parentConnection);
NitroxEntity.TryGetComponentFrom(entity.RootPipeId, out IPipeConnection rootConnection);
SetupObject(entity, gameObject, oxygenPipe, parentConnection, rootConnection);

result.Set(gameObject);
return true;
}

private bool IsSpawnedPrefabValid(OxygenPipeEntity entity, GameObject prefabObject, out OxygenPipe oxygenPipe, out string errorLog)
{
if (prefabObject.TryGetComponent(out oxygenPipe))
{
errorLog = string.Empty;
return true;
}
errorLog = $"Couldn't find component {nameof(OxygenPipe)} on prefab with ClassId: {entity.ClassId}";
return false;
}

private void SetupObject(OxygenPipeEntity entity, GameObject gameObject, OxygenPipe oxygenPipe, IPipeConnection parentConnection, IPipeConnection rootConnection)
{
EntityCell cellRoot = worldEntitySpawner.EnsureCell(entity);

NitroxEntity.SetNewId(gameObject, entity.Id);
gameObject.transform.SetParent(cellRoot.liveRoot.transform, false);
gameObject.transform.position = entity.Transform.Position.ToUnity();
gameObject.transform.rotation = entity.Transform.Rotation.ToUnity();
gameObject.transform.localScale = entity.Transform.LocalScale.ToUnity();

oxygenPipe.parentPipeUID = entity.ParentPipeId.ToString();
oxygenPipe.rootPipeUID = entity.RootPipeId.ToString();
oxygenPipe.parentPosition = entity.ParentPosition.ToUnity();

// It can happen that the parent connection hasn't loaded yet (normal behaviour)
if (parentConnection != null)
{
oxygenPipe.parentPosition = parentConnection.GetAttachPoint();
parentConnection.AddChild(oxygenPipe);
}
else
{
// We add this pipe to a pending list so that its parent pipe will know which children are already spawned when being spanwed
if (!childrenPipeEntitiesByParentId.TryGetValue(entity.ParentPipeId, out List<OxygenPipe> pendingChildren))
{
childrenPipeEntitiesByParentId[entity.ParentPipeId] = pendingChildren = new();
}
pendingChildren.Add(oxygenPipe);
}

if (childrenPipeEntitiesByParentId.TryGetValue(entity.Id, out List<OxygenPipe> children))
{
foreach (OxygenPipe childPipe in children)
{
oxygenPipe.AddChild(childPipe);
}
childrenPipeEntitiesByParentId.Remove(entity.Id);
}

UWE.Utils.SetIsKinematicAndUpdateInterpolation(oxygenPipe.rigidBody, true, false);
oxygenPipe.UpdatePipe();
}

public override bool SpawnsOwnChildren(OxygenPipeEntity entity)
{
return true;
}
}
2 changes: 1 addition & 1 deletion NitroxClient/GameLogic/Spawning/WorldEntitySpawner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ protected override bool SpawnsOwnChildren(WorldEntity entity)
return entitySpawner.SpawnsOwnChildren();
}

private EntityCell EnsureCell(WorldEntity entity)
public EntityCell EnsureCell(WorldEntity entity)
{
EntityCell entityCell;

Expand Down
Loading