mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
Make recursive delete detach parents before children (#3008)
This commit is contained in:
@@ -4,6 +4,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Prometheus;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Profiling;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -273,7 +274,7 @@ namespace Robust.Shared.GameObjects
|
||||
/// <param name="e">Entity to remove</param>
|
||||
public virtual void DeleteEntity(EntityUid e)
|
||||
{
|
||||
// Some UIs get dispose after entity-manager has shut down and already deleted all entities.
|
||||
// Some UIs get disposed after entity-manager has shut down and already deleted all entities.
|
||||
if (!Started)
|
||||
return;
|
||||
|
||||
@@ -289,31 +290,61 @@ namespace Robust.Shared.GameObjects
|
||||
return;
|
||||
|
||||
if (meta.EntityLifeStage == EntityLifeStage.Terminating)
|
||||
{
|
||||
var msg = $"Called Delete on an entity already being deleted. Entity: {ToPrettyString(meta.Owner)}";
|
||||
#if !EXCEPTION_TOLERANCE
|
||||
throw new InvalidOperationException("Called Delete on an entity already being deleted.");
|
||||
throw new InvalidOperationException(msg);
|
||||
#else
|
||||
return;
|
||||
Logger.Error(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Notify all entities they are being terminated prior to detaching & deleting
|
||||
RecursiveFlagEntityTermination(meta, metaQuery, xformQuery, xformSys);
|
||||
|
||||
// Then actually delete them
|
||||
RecursiveDeleteEntity(meta, metaQuery, xformQuery, xformSys);
|
||||
}
|
||||
|
||||
private void RecursiveFlagEntityTermination(MetaDataComponent metadata, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> xformQuery, SharedTransformSystem xformSys)
|
||||
{
|
||||
var transform = xformQuery.GetComponent(metadata.Owner);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Terminating;
|
||||
var ev = new EntityTerminatingEvent(metadata.Owner);
|
||||
|
||||
// TODO: consider making this a meta-data flag?
|
||||
// veeeery few entities make use of this event.
|
||||
EventBus.RaiseLocalEvent(metadata.Owner, ref ev, false);
|
||||
|
||||
foreach (var child in transform._children)
|
||||
{
|
||||
if (!metaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
|
||||
{
|
||||
Logger.Error($"A deleted entity was still the transform child of another entity. Parent: {ToPrettyString(metadata.Owner)}.");
|
||||
transform._children.Remove(child);
|
||||
continue;
|
||||
}
|
||||
|
||||
RecursiveFlagEntityTermination(childMeta, metaQuery, xformQuery, xformSys);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecursiveDeleteEntity(MetaDataComponent metadata, EntityQuery<MetaDataComponent> metaQuery, EntityQuery<TransformComponent> xformQuery, SharedTransformSystem xformSys)
|
||||
{
|
||||
var transform = xformQuery.GetComponent(metadata.Owner);
|
||||
metadata.EntityLifeStage = EntityLifeStage.Terminating;
|
||||
var ev = new EntityTerminatingEvent(metadata.Owner);
|
||||
EventBus.RaiseLocalEvent(metadata.Owner, ref ev, false);
|
||||
|
||||
// DeleteEntity modifies our _children collection, we must cache the collection to iterate properly
|
||||
foreach (var child in transform._children.ToArray())
|
||||
// Detach the base entity to null before iterating over children
|
||||
// This also ensures that the entity-lookup updates don't have to be re-run for every child (which recurses up the transform hierarchy).
|
||||
if (transform.ParentUid != EntityUid.Invalid)
|
||||
xformSys.DetachParentToNull(transform, xformQuery, metaQuery);
|
||||
|
||||
foreach (var child in transform._children)
|
||||
{
|
||||
if (!metaQuery.TryGetComponent(child, out var childMeta) || childMeta.EntityDeleted)
|
||||
continue; //TODO: Why was this still a child if it was already deleted?
|
||||
|
||||
// Recursion Alert
|
||||
RecursiveDeleteEntity(childMeta, metaQuery, xformQuery, xformSys);
|
||||
RecursiveDeleteEntity(metaQuery.GetComponent(child), metaQuery, xformQuery, xformSys);
|
||||
}
|
||||
|
||||
DebugTools.Assert(transform._children.Count == 0, $"Failed to delete all children of entity: {ToPrettyString(metadata.Owner)}");
|
||||
|
||||
// Shut down all components.
|
||||
foreach (var component in InSafeOrder(_entCompIndex[metadata.Owner]))
|
||||
{
|
||||
@@ -321,13 +352,6 @@ namespace Robust.Shared.GameObjects
|
||||
component.LifeShutdown(this);
|
||||
}
|
||||
|
||||
// map does not have a parent node, everything else needs to be detached
|
||||
if (transform.ParentUid != EntityUid.Invalid)
|
||||
{
|
||||
// Detach from my parent, if any
|
||||
xformSys.DetachParentToNull(transform, xformQuery, metaQuery);
|
||||
}
|
||||
|
||||
// Dispose all my components, in a safe order so transform is available
|
||||
DisposeComponents(metadata.Owner);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user