using AcDream.App.World; using AcDream.Core.Net.Messages; using AcDream.Core.Physics; using DatReaderWriter.DBObjs; namespace AcDream.App.Rendering; /// /// Completes a fresher same-incarnation CreateObject after it interrupted an /// active projection transaction. Logical entity/resource ownership is /// retained; the exact current appearance is republished, the plugin/current /// snapshot is refreshed, and the retained animation owner is synchronized in /// that order. Every potentially reentrant stage is guarded by the monotonic /// Create-integration authority. /// internal static class LiveEntityCreateSupersessionRecovery { public static bool TryApply( LiveEntityRuntime runtime, LiveEntityRecord expectedRecord, ulong expectedCreateIntegrationVersion, Func captureAppearance, Func publishAppearance, Action publishCurrentSnapshot, Action synchronizeAnimation) { ArgumentNullException.ThrowIfNull(runtime); ArgumentNullException.ThrowIfNull(expectedRecord); ArgumentNullException.ThrowIfNull(captureAppearance); ArgumentNullException.ThrowIfNull(publishAppearance); ArgumentNullException.ThrowIfNull(publishCurrentSnapshot); ArgumentNullException.ThrowIfNull(synchronizeAnimation); if (!runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion) || captureAppearance() is not { } visualUpdate || !runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion) || !publishAppearance(visualUpdate) || !runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion)) { return false; } publishCurrentSnapshot(visualUpdate); if (!runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion)) { return false; } synchronizeAnimation(visualUpdate); return runtime.IsCurrentCreateIntegration( expectedRecord, expectedCreateIntegrationVersion); } } /// /// Reconciles the exceptional case where a fresher same-incarnation Create /// interrupts initial PartArray construction. Completed owners already route /// MovementData through SetObjectMovement and therefore retain their exact /// sequencer, dispatch sink, and paired manager/interpreter queues. /// internal static class LiveEntityCreateAnimationSynchronization { public static bool TrySynchronizeInterruptedInitialOwner( LiveEntityRecord record, LiveEntityAnimationState animation, Animation? canonicalAnimation, int canonicalLowFrame, int canonicalHighFrame, float canonicalFramerate, MotionTable? motionTable, CreateObject.ServerMotionState? wireState) { ArgumentNullException.ThrowIfNull(record); ArgumentNullException.ThrowIfNull(animation); if (record.InitialHydrationCompleted) return false; if (canonicalAnimation is not null) { animation.Animation = canonicalAnimation; animation.LowFrame = canonicalLowFrame; animation.HighFrame = canonicalHighFrame; animation.Framerate = canonicalFramerate; animation.CurrFrame = canonicalLowFrame; } if (animation.Sequencer is { } sequencer && motionTable is not null) { SpawnMotionInitializer.Reinitialize( sequencer, motionTable, wireState); } return true; } }