refactor(world): extract live projection mechanics
Move appearance rebinding, collision construction, default-pose resolution, local shadow ownership, and exact leave-world presentation into focused owners. Preserve retail parent ordering with staged validation, committed recovery, recursive attached-subtree withdrawal, and retryable exact teardown across parent, pickup, position, unwield, and pose-loss edges. Co-Authored-By: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
c87bac31a0
commit
69a2ca0c6d
26 changed files with 4172 additions and 430 deletions
127
src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs
Normal file
127
src/AcDream.App/Rendering/LiveEntityAppearanceBinding.cs
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Physics;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Net;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.World;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
||||
namespace AcDream.App.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// Exact runtime identity retained while retail
|
||||
/// <c>SmartBox::UpdateVisualDesc</c> mutates only a live object's visual
|
||||
/// description.
|
||||
/// </summary>
|
||||
internal sealed record LiveEntityAppearanceUpdateState(
|
||||
WorldEntity Entity,
|
||||
LiveEntityAnimationState? Animation);
|
||||
|
||||
internal sealed record LiveEntityAppearanceCollisionUpdate(
|
||||
LiveEntityRecord Record,
|
||||
WorldEntity Entity,
|
||||
LiveEntityCollisionRegistration? Registration);
|
||||
|
||||
/// <summary>
|
||||
/// Focused appearance-lifetime operations shared by ObjDesc hydration and the
|
||||
/// final animation presenter. This type owns no identity index; every capture
|
||||
/// is resolved from <see cref="LiveEntityRuntime"/>.
|
||||
/// </summary>
|
||||
internal static class LiveEntityAppearanceBinding
|
||||
{
|
||||
public static LiveEntityAppearanceUpdateState? Capture(
|
||||
LiveEntityRuntime runtime,
|
||||
uint serverGuid)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
if (!runtime.TryGetRecord(serverGuid, out LiveEntityRecord record)
|
||||
|| record.WorldEntity is not { } entity)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LiveEntityAppearanceUpdateState(
|
||||
entity,
|
||||
record.AnimationRuntime as LiveEntityAnimationState);
|
||||
}
|
||||
|
||||
public static void RebindAnimation(
|
||||
LiveEntityAnimationState animation,
|
||||
WorldEntity entity,
|
||||
Setup setup,
|
||||
float scale,
|
||||
IReadOnlyList<LiveAnimationPartTemplate> partTemplate,
|
||||
IReadOnlyList<bool> partAvailability)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(animation);
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
ArgumentNullException.ThrowIfNull(setup);
|
||||
ArgumentNullException.ThrowIfNull(partTemplate);
|
||||
ArgumentNullException.ThrowIfNull(partAvailability);
|
||||
|
||||
animation.Entity = entity;
|
||||
animation.Setup = setup;
|
||||
animation.Scale = scale;
|
||||
animation.PartTemplate = partTemplate;
|
||||
animation.PartAvailability = partAvailability;
|
||||
animation.InvalidatePresentationPoses();
|
||||
}
|
||||
|
||||
public static LiveEntityAppearanceCollisionUpdate? PrepareCollision(
|
||||
LiveEntityRuntime runtime,
|
||||
LiveEntityCollisionBuilder builder,
|
||||
WorldEntity entity,
|
||||
Setup setup,
|
||||
IReadOnlyList<uint> effectivePartGfxObjIds,
|
||||
WorldSession.EntitySpawn spawn,
|
||||
Vector3 worldOrigin)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
ArgumentNullException.ThrowIfNull(entity);
|
||||
ArgumentNullException.ThrowIfNull(setup);
|
||||
ArgumentNullException.ThrowIfNull(effectivePartGfxObjIds);
|
||||
if (!runtime.TryGetRecord(spawn.Guid, out LiveEntityRecord record)
|
||||
|| !ReferenceEquals(record.WorldEntity, entity))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new LiveEntityAppearanceCollisionUpdate(
|
||||
record,
|
||||
entity,
|
||||
builder.Build(
|
||||
entity,
|
||||
setup,
|
||||
effectivePartGfxObjIds,
|
||||
spawn,
|
||||
record,
|
||||
worldOrigin,
|
||||
retainEmptyPayload: true));
|
||||
}
|
||||
|
||||
public static bool CommitCollision(
|
||||
LiveEntityRuntime runtime,
|
||||
ShadowObjectRegistry registry,
|
||||
LiveEntityAppearanceCollisionUpdate update)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(runtime);
|
||||
ArgumentNullException.ThrowIfNull(registry);
|
||||
ArgumentNullException.ThrowIfNull(update);
|
||||
if (!runtime.TryGetRecord(update.Record.ServerGuid, out LiveEntityRecord current)
|
||||
|| !ReferenceEquals(current, update.Record)
|
||||
|| !ReferenceEquals(current.WorldEntity, update.Entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LiveEntityCollisionBuilder.ReconcileAppearance(
|
||||
registry,
|
||||
update.Entity.Id,
|
||||
update.Registration,
|
||||
suspendIfNew: !current.IsSpatiallyVisible
|
||||
|| current.ProjectionKind is not LiveEntityProjectionKind.World
|
||||
|| (current.FinalPhysicsState & PhysicsStateFlags.Hidden) != 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue