refactor(app): key live projections by runtime identity

Move materialized live-object sidecars and presentation worksets to exact RuntimeEntityKey ownership. Runtime remains the only GUID/incarnation/local-ID authority while hydration, animation, effects, lights, equipped children, renderer resources, visibility, liveness, and teardown resolve exact projection identities. Preserve synchronous callbacks, local-ID allocation order, and current rendering behavior.
This commit is contained in:
Erik 2026-07-25 21:50:58 +02:00
parent e18df84437
commit 420e5eea70
73 changed files with 2939 additions and 1715 deletions

View file

@ -11,6 +11,7 @@ using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Core.Plugins;
using AcDream.Core.World;
using AcDream.Runtime.Entities;
using DatReaderWriter;
using DatReaderWriter.DBObjs;
using DatReaderWriter.Types;
@ -116,13 +117,13 @@ internal sealed class DatLiveEntityProjectionMaterializer
}
public bool TryMaterialize(
LiveEntityRecord expectedRecord,
RuntimeEntityRecord expectedCanonical,
WorldSession.EntitySpawn canonicalSpawn,
LiveProjectionPurpose purpose,
ulong expectedCreateIntegrationVersion,
LiveEntityAppearanceUpdateState? appearanceUpdate = null)
{
ArgumentNullException.ThrowIfNull(expectedRecord);
ArgumentNullException.ThrowIfNull(expectedCanonical);
if (purpose is LiveProjectionPurpose.AppearanceMutation
!= (appearanceUpdate is not null))
{
@ -131,13 +132,18 @@ internal sealed class DatLiveEntityProjectionMaterializer
nameof(appearanceUpdate));
}
if (!_runtime.IsCurrentCreateIntegration(
expectedRecord,
expectedCanonical,
expectedCreateIntegrationVersion)
|| canonicalSpawn.Guid != expectedRecord.ServerGuid
|| canonicalSpawn.InstanceSequence != expectedRecord.Generation)
|| canonicalSpawn.Guid != expectedCanonical.ServerGuid
|| canonicalSpawn.InstanceSequence != expectedCanonical.Incarnation)
{
return false;
}
_runtime.TryGetProjection(
expectedCanonical,
out LiveEntityRecord? expectedRecord);
if (appearanceUpdate is not null && expectedRecord is null)
return false;
_received++;
bool dumpLiveSpawns = _options.DumpLiveSpawns;
@ -186,7 +192,7 @@ internal sealed class DatLiveEntityProjectionMaterializer
return false;
}
expectedRecord.HasPartArray = true;
_runtime.SetHasPartArray(expectedCanonical, true);
ushort? stanceOverride = canonicalSpawn.MotionState?.Stance;
ushort? commandOverride = canonicalSpawn.MotionState?.ForwardCommand;
var idleCycle = MotionResolver.GetIdleCycle(
@ -327,7 +333,8 @@ internal sealed class DatLiveEntityProjectionMaterializer
bool supersessionRecovery =
purpose is LiveProjectionPurpose.CreateSupersessionRecovery;
if (supersessionRecovery && expectedRecord.WorldEntity is not null)
if (supersessionRecovery
&& expectedRecord?.WorldEntity is not null)
{
return LiveEntityCreateSupersessionRecovery.TryApply(
_runtime,
@ -380,7 +387,7 @@ internal sealed class DatLiveEntityProjectionMaterializer
if (appearanceUpdate is { } visualUpdate)
{
return ApplyAppearance(
expectedRecord,
expectedRecord!,
canonicalSpawn,
visualUpdate,
setup,
@ -398,6 +405,7 @@ internal sealed class DatLiveEntityProjectionMaterializer
}
return MaterializeProjection(
expectedCanonical,
expectedRecord,
canonicalSpawn,
setup,
@ -670,7 +678,8 @@ internal sealed class DatLiveEntityProjectionMaterializer
}
private bool MaterializeProjection(
LiveEntityRecord expectedRecord,
RuntimeEntityRecord expectedCanonical,
LiveEntityRecord? retainedRecord,
WorldSession.EntitySpawn spawn,
Setup setup,
MotionResolver.IdleCycle? idleCycle,
@ -690,17 +699,18 @@ internal sealed class DatLiveEntityProjectionMaterializer
ulong expectedCreateIntegrationVersion,
bool synchronizeAnimation)
{
if (!_runtime.TryGetEffectProfile(spawn.Guid, out _))
EntityEffectProfile profile = spawn.Physics is { } physics
? EntityEffectProfile.CreateLive(setup, physics)
: EntityEffectProfile.CreateDatStatic(setup);
if (retainedRecord is not null
&& !_runtime.TryGetEffectProfile(spawn.Guid, out _))
{
EntityEffectProfile profile = spawn.Physics is { } physics
? EntityEffectProfile.CreateLive(setup, physics)
: EntityEffectProfile.CreateDatStatic(setup);
_runtime.SetEffectProfile(spawn.Guid, profile);
}
bool createdProjection = false;
WorldEntity? entity = _runtime.MaterializeLiveEntity(
spawn.Guid,
expectedCanonical,
spawn.Position!.Value.LandblockId,
localId =>
{
@ -721,8 +731,12 @@ internal sealed class DatLiveEntityProjectionMaterializer
if (bounds.TryGet(out Vector3 minimum, out Vector3 maximum))
created.SetLocalBounds(minimum, maximum);
return created;
});
},
LiveEntityProjectionKind.World,
initializeProjection: record => record.EffectProfile = profile,
out LiveEntityRecord? expectedRecord);
if (entity is null
|| expectedRecord is null
|| !_runtime.IsCurrentCreateIntegration(
expectedRecord,
expectedCreateIntegrationVersion)
@ -828,7 +842,7 @@ internal sealed class DatLiveEntityProjectionMaterializer
if (dumpLiveSpawns && _received % 20 == 0)
{
Console.WriteLine(
$"live: animated={_runtime.AnimationRuntimes.Count} "
$"live: animated={_runtime.AnimationRuntimeCount} "
+ $"animReject: noCycle={_noCycle} fr0={_zeroFramerate} "
+ $"1frame={_singleFrame} partFrames={_missingPartFrames}");
Console.WriteLine(