harden(runtime): close canonical entity object lifetime
Retain exact teardown receipts before terminal callbacks, preserve ordered re-entrant entity/object publication, publish committed facts across projection failures, and make direct disposal converge every canonical owner. Add a complete ownership ledger plus adversarial direct/graphical parity, callback, GUID reuse, reset, and resource-churn gates. Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
parent
84954c8b77
commit
119b7c1151
9 changed files with 992 additions and 95 deletions
|
|
@ -9,6 +9,7 @@ using AcDream.Core.Net.Messages;
|
|||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Vfx;
|
||||
using AcDream.Core.World;
|
||||
using AcDream.Runtime.Entities;
|
||||
using DatReaderWriter.DBObjs;
|
||||
using DatReaderWriter.Enums;
|
||||
using DatReaderWriter.Types;
|
||||
|
|
@ -244,6 +245,7 @@ public sealed class LiveEntityLifecycleStressTests
|
|||
fixture.Effects.ClearNetworkState();
|
||||
Assert.Equal(0, fixture.Runtime.Count);
|
||||
Assert.Equal(0, fixture.Effects.PendingPacketCount);
|
||||
AssertOwnershipLedgerConverged(fixture);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -299,6 +301,98 @@ public sealed class LiveEntityLifecycleStressTests
|
|||
Assert.Equal(0, fixture.Spatial.PendingBucketCount);
|
||||
}
|
||||
|
||||
private static void AssertOwnershipLedgerConverged(Fixture fixture)
|
||||
{
|
||||
RuntimeEntityObjectOwnershipSnapshot canonical =
|
||||
fixture.EntityObjects.CaptureOwnership();
|
||||
var ledger = new J3OwnershipLedger(
|
||||
canonical,
|
||||
fixture.Runtime.MaterializedCount,
|
||||
fixture.Runtime.VisibleRecords.Count,
|
||||
fixture.Runtime.AnimationRuntimeCount,
|
||||
fixture.Runtime.SpatialAnimationRuntimeCount,
|
||||
fixture.Runtime.SpatialRemoteMotionRuntimeCount,
|
||||
fixture.Runtime.SpatialProjectileRuntimeCount,
|
||||
fixture.Runtime.SpatialRootObjectCount,
|
||||
fixture.RenderOwners.Count,
|
||||
fixture.RenderRegisterCount - fixture.RenderUnregisterCount,
|
||||
fixture.Projectiles.Count,
|
||||
fixture.Effects.ReadyOwnerCount,
|
||||
fixture.Effects.PendingPacketCount,
|
||||
fixture.Runner.ActiveOwnerCount,
|
||||
fixture.Runner.ActiveScriptCount,
|
||||
fixture.Runner.ScheduledCallPesCount,
|
||||
fixture.Runner.OwnerAnchorCount,
|
||||
fixture.Particles.ActiveEmitterCount,
|
||||
fixture.Particles.ActiveParticleCount,
|
||||
fixture.ParticleSink.ActiveBindingCount,
|
||||
fixture.ParticleSink.LogicalEmitterCount,
|
||||
fixture.ParticleSink.TrackedOwnerCount,
|
||||
fixture.ParticleSink.RenderPassOwnerCount,
|
||||
fixture.ParticleSink.HiddenPresentationOwnerCount,
|
||||
fixture.Poses.Count,
|
||||
fixture.Lights.RegisteredCount,
|
||||
fixture.LiveLights.TrackedOwnerCount,
|
||||
fixture.LiveLights.PresentedOwnerCount,
|
||||
fixture.Lighting.OwnedLightOwnerCount,
|
||||
fixture.Lighting.PoseTrackedOwnerCount,
|
||||
fixture.Lighting.RetainedOwnerStateCount,
|
||||
fixture.Engine.ShadowObjects.TotalRegistered,
|
||||
fixture.Engine.ShadowObjects.RetainedRegistrationCount,
|
||||
fixture.Engine.ShadowObjects.SuspendedRegistrationCount,
|
||||
fixture.Spatial.PendingLiveEntityCount,
|
||||
fixture.Spatial.PendingBucketCount,
|
||||
fixture.Spatial.PendingRescueCount,
|
||||
fixture.Spatial.PersistentGuidCount,
|
||||
fixture.Spatial.PendingVisibilityTransitionCount,
|
||||
fixture.Spatial.Entities.Count(entity =>
|
||||
entity.ServerGuid != 0u));
|
||||
|
||||
Assert.Equal(default(J3OwnershipLedger), ledger);
|
||||
}
|
||||
|
||||
private readonly record struct J3OwnershipLedger(
|
||||
RuntimeEntityObjectOwnershipSnapshot Canonical,
|
||||
int AppMaterializedCount,
|
||||
int AppVisibleCount,
|
||||
int AnimationRuntimeCount,
|
||||
int SpatialAnimationRuntimeCount,
|
||||
int SpatialRemoteMotionRuntimeCount,
|
||||
int SpatialProjectileRuntimeCount,
|
||||
int SpatialRootObjectCount,
|
||||
int RenderOwnerCount,
|
||||
int RenderRegistrationDelta,
|
||||
int ProjectileCount,
|
||||
int EffectReadyOwnerCount,
|
||||
int EffectPendingPacketCount,
|
||||
int ScriptOwnerCount,
|
||||
int ActiveScriptCount,
|
||||
int ScheduledCallPesCount,
|
||||
int ScriptAnchorCount,
|
||||
int EmitterCount,
|
||||
int ParticleCount,
|
||||
int ParticleBindingCount,
|
||||
int LogicalEmitterCount,
|
||||
int ParticleTrackedOwnerCount,
|
||||
int ParticleRenderPassOwnerCount,
|
||||
int HiddenParticlePresentationCount,
|
||||
int PoseCount,
|
||||
int RegisteredLightCount,
|
||||
int TrackedLiveLightCount,
|
||||
int PresentedLiveLightCount,
|
||||
int OwnedLightCount,
|
||||
int LightPoseCount,
|
||||
int RetainedLightStateCount,
|
||||
int ShadowCount,
|
||||
int RetainedShadowCount,
|
||||
int SuspendedShadowCount,
|
||||
int PendingSpatialEntityCount,
|
||||
int PendingBucketCount,
|
||||
int PendingRescueCount,
|
||||
int PersistentGuidCount,
|
||||
int PendingVisibilityCount,
|
||||
int MaterializedWorldEntityCount);
|
||||
|
||||
private sealed class Fixture : IDisposable
|
||||
{
|
||||
private readonly Dictionary<uint, DatPhysicsScript> _scripts = new();
|
||||
|
|
@ -328,7 +422,8 @@ public sealed class LiveEntityLifecycleStressTests
|
|||
randomUnit: () => 0.5,
|
||||
canAdvanceOwner: ownerId => _effects?.CanAdvanceOwner(ownerId) ?? true);
|
||||
|
||||
Runtime = LiveEntityRuntimeFixture.Create(
|
||||
EntityObjects = new RuntimeEntityObjectLifetime();
|
||||
Runtime = new LiveEntityRuntime(
|
||||
Spatial,
|
||||
new DelegateLiveEntityResourceLifecycle(
|
||||
entity =>
|
||||
|
|
@ -358,7 +453,8 @@ public sealed class LiveEntityLifecycleStressTests
|
|||
cleanups.Add(() => _liveLights?.Forget(record));
|
||||
}
|
||||
LiveEntityTeardown.Run(cleanups);
|
||||
});
|
||||
},
|
||||
EntityObjects);
|
||||
|
||||
Effects = new EntityEffectController(
|
||||
Runtime,
|
||||
|
|
@ -386,6 +482,7 @@ public sealed class LiveEntityLifecycleStressTests
|
|||
internal LightManager Lights { get; } = new();
|
||||
internal LightingHookSink Lighting { get; }
|
||||
internal PhysicsScriptRunner Runner { get; }
|
||||
internal RuntimeEntityObjectLifetime EntityObjects { get; }
|
||||
internal LiveEntityRuntime Runtime { get; }
|
||||
internal EntityEffectController Effects { get; }
|
||||
internal ProjectileController Projectiles { get; }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue