From 1189773e70bba2735bea938bbd3f330aabecbcfc Mon Sep 17 00:00:00 2001 From: uGuardian Date: Tue, 13 Aug 2024 22:10:29 -0600 Subject: [PATCH 1/4] Fixed server starting with errored entities Streamlined sorting into a single enumeration pass --- .../GameLogic/Entities/EntityManager.cs | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/NitroxServer/GameLogic/Entities/EntityManager.cs b/NitroxServer/GameLogic/Entities/EntityManager.cs index a7863901d9..82c73aa370 100644 --- a/NitroxServer/GameLogic/Entities/EntityManager.cs +++ b/NitroxServer/GameLogic/Entities/EntityManager.cs @@ -28,14 +28,46 @@ public class EntityManager public EntityManager(List entities, BatchEntitySpawner batchEntitySpawner) { - entitiesById = entities.ToDictionary(entity => entity.Id); + entitiesById = new Dictionary(entities.Count); + globalRootEntitiesById = new Dictionary(); + phasingEntitiesByAbsoluteCell = new Dictionary>(); + bool toWarn = false; - globalRootEntitiesById = entities.FindAll(entity => entity.ExistsInGlobalRoot) - .ToDictionary(entity => entity.Id); - - phasingEntitiesByAbsoluteCell = entities.FindAll(entity => !entity.ExistsInGlobalRoot) - .GroupBy(entity => entity.AbsoluteEntityCell) - .ToDictionary(group => group.Key, group => group.ToList()); + for (int i = 0; i < entities.Count; i++) + { + Entity entity = entities[i]; + try + { + if (entity.ExistsInGlobalRoot) + { + globalRootEntitiesById.Add(entity.Id, entity); + } + else + { + AbsoluteEntityCell cell = entity.AbsoluteEntityCell; + if (phasingEntitiesByAbsoluteCell.TryGetValue(cell, out List list)) + { + list.Add(entity); + } + else + { + list = new List {entity}; + phasingEntitiesByAbsoluteCell.Add(cell, list); + } + } + // At the end since we don't want entities that cause an error + entitiesById.Add(entity.Id, entity); + } + catch (System.Exception ex) + { + toWarn = true; + Log.Error(ex); + } + } + if (toWarn) + { + Log.Warn("One or more entities have failed to load"); + } this.batchEntitySpawner = batchEntitySpawner; } From 3dfd522ed12ca8208254c9b52ad655d9cfcf3f08 Mon Sep 17 00:00:00 2001 From: uGuardian Date: Wed, 14 Aug 2024 05:18:24 -0600 Subject: [PATCH 2/4] Retailored warning log for less clutter and better readability. --- NitroxServer/GameLogic/Entities/EntityManager.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/NitroxServer/GameLogic/Entities/EntityManager.cs b/NitroxServer/GameLogic/Entities/EntityManager.cs index 82c73aa370..d3bf2d1bbd 100644 --- a/NitroxServer/GameLogic/Entities/EntityManager.cs +++ b/NitroxServer/GameLogic/Entities/EntityManager.cs @@ -31,7 +31,6 @@ public EntityManager(List entities, BatchEntitySpawner batchEntitySpawne entitiesById = new Dictionary(entities.Count); globalRootEntitiesById = new Dictionary(); phasingEntitiesByAbsoluteCell = new Dictionary>(); - bool toWarn = false; for (int i = 0; i < entities.Count; i++) { @@ -60,14 +59,12 @@ public EntityManager(List entities, BatchEntitySpawner batchEntitySpawne } catch (System.Exception ex) { - toWarn = true; + Log.Warn($"Entity of type {entity.TechType.Name ?? "Unknown"} with GUID {entity.Id?.ToString() ?? "Unknown"} failed to load"); +#if DEBUG Log.Error(ex); +#endif } } - if (toWarn) - { - Log.Warn("One or more entities have failed to load"); - } this.batchEntitySpawner = batchEntitySpawner; } From b34e3211c492cb65a944c56f5f2b55157bee6fd7 Mon Sep 17 00:00:00 2001 From: uGuardian Date: Wed, 14 Aug 2024 08:30:24 -0600 Subject: [PATCH 3/4] Added optional position fixing that approximates entity position based on other entities in parent, which is generally a cell. --- .../GameLogic/Entities/EntityManager.cs | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/NitroxServer/GameLogic/Entities/EntityManager.cs b/NitroxServer/GameLogic/Entities/EntityManager.cs index d3bf2d1bbd..97b3af8fa7 100644 --- a/NitroxServer/GameLogic/Entities/EntityManager.cs +++ b/NitroxServer/GameLogic/Entities/EntityManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using System.Runtime.InteropServices; using NitroxModel.Core; using NitroxModel.DataStructures; using NitroxModel.DataStructures.GameLogic; @@ -63,12 +64,87 @@ public EntityManager(List entities, BatchEntitySpawner batchEntitySpawne #if DEBUG Log.Error(ex); #endif + // TODO - Teleport entity back to where it belongs, rather than just towards the center. + Log.Info("Press D to delete the entity, or any other key to teleport it between other things in its parent"); + System.ConsoleKeyInfo key = System.Console.ReadKey(); + if (key.Key != System.ConsoleKey.D) + { + try + { + Entity parent = entities.Find(x => x.Id == entity.ParentId); // Parent isn't certain to be added to dictionary yet + if (FixNaNTransform(entity, parent)) + { + i--; // Back up one and retry adding + } + } + catch + { + Log.Error("Failed to fix entity, aborting"); + throw; + } + } } } this.batchEntitySpawner = batchEntitySpawner; } + private static bool FixNaNTransform(Entity entity, [Optional]Entity parent) + { + var transform = entity.Transform; + bool needsPos = IsNaN(transform.LocalPosition); + bool needsRot = IsNaN(transform.LocalRotation); + bool needsScale = IsNaN(transform.LocalScale); + if (needsPos) + { + if (parent != null) + { + List siblingPosList = parent.ChildEntities.Select(x => x.Transform.LocalPosition).Where(x => !IsNaN(x)).ToList(); + if (siblingPosList.Count <= 0) + { + Log.Warn("Object has no siblings, placing at center of parent"); + transform.LocalPosition = NitroxVector3.One; + } + else + { + transform.LocalPosition = siblingPosList.Aggregate((x, y) => x + y) / siblingPosList.Count; + } + } + else + { + Log.Error("Object has no parent, position fixing will result in unexpected behaviors."); + Log.Info("Press D to delete the entity, or any other key to continue anyways"); + System.ConsoleKeyInfo key = System.Console.ReadKey(); + if (key.Key != System.ConsoleKey.D) + { + transform.LocalPosition = NitroxVector3.One; + } + else + { + return false; + } + } + } + if (needsRot) + { + transform.LocalRotation = NitroxQuaternion.Identity; + } + if (needsScale) + { + transform.LocalScale = NitroxVector3.One; + } + return true; + } + + private static bool IsNaN(NitroxVector3 vector3) + { + return float.IsNaN(vector3.X) || float.IsNaN(vector3.Y) || float.IsNaN(vector3.Z); + } + private static bool IsNaN(NitroxQuaternion quaternion) + { + return float.IsNaN(quaternion.X) || float.IsNaN(quaternion.Y) || float.IsNaN(quaternion.Z) || float.IsNaN(quaternion.W); + } + public List GetVisibleEntities(AbsoluteEntityCell[] cells) { LoadUnspawnedEntities(cells); From 4dab186d9598924afbe11b5109fecf442ac80565 Mon Sep 17 00:00:00 2001 From: uGuardian Date: Wed, 14 Aug 2024 08:36:23 -0600 Subject: [PATCH 4/4] Updated TODO comment --- NitroxServer/GameLogic/Entities/EntityManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NitroxServer/GameLogic/Entities/EntityManager.cs b/NitroxServer/GameLogic/Entities/EntityManager.cs index 97b3af8fa7..1c8052964c 100644 --- a/NitroxServer/GameLogic/Entities/EntityManager.cs +++ b/NitroxServer/GameLogic/Entities/EntityManager.cs @@ -64,7 +64,7 @@ public EntityManager(List entities, BatchEntitySpawner batchEntitySpawne #if DEBUG Log.Error(ex); #endif - // TODO - Teleport entity back to where it belongs, rather than just towards the center. + // TODO - Add options for manual relocation and "natural respawning" Log.Info("Press D to delete the entity, or any other key to teleport it between other things in its parent"); System.ConsoleKeyInfo key = System.Console.ReadKey(); if (key.Key != System.ConsoleKey.D)