refactor(streaming): compose landblock presentation transaction
Own render, physics, DAT-static presentation, and retained static-resource rebinds behind retryable receipts so a failed publication resumes its exact unfinished suffix without replaying retail create-time defaults. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
e1110f7e54
commit
ea0da8c8ae
15 changed files with 1744 additions and 89 deletions
|
|
@ -64,6 +64,42 @@ public sealed class RetailStaticAnimatingObjectSchedulerTests
|
|||
Assert.Equal(2, posePublishes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rebind_RetainsSequencerAndPublishesIntoReplacementEntity()
|
||||
{
|
||||
var loader = new Loader();
|
||||
loader.Add(AnimationId, TwoFrameAnimation());
|
||||
var scheduler = new RetailStaticAnimatingObjectScheduler(
|
||||
loader,
|
||||
(_, _) => { },
|
||||
(_, _, _) => { });
|
||||
Setup setup = MakeSetup();
|
||||
WorldEntity first = MakeEntity();
|
||||
ScriptActivationInfo info = new(
|
||||
ScriptId: 0,
|
||||
PartTransforms: first.IndexedPartTransforms,
|
||||
PartAvailability: first.IndexedPartAvailable,
|
||||
Setup: setup,
|
||||
DefaultAnimationId: AnimationId,
|
||||
UsesStaticAnimationWorkset: true);
|
||||
scheduler.Register(first, info);
|
||||
scheduler.Tick(1f / 60f);
|
||||
float phaseBeforeRebind = first.MeshRefs[0].PartTransform.Translation.X;
|
||||
|
||||
WorldEntity replacement = MakeEntity();
|
||||
replacement.Position = new Vector3(4f, 5f, 6f);
|
||||
scheduler.Rebind(replacement, info with
|
||||
{
|
||||
PartTransforms = replacement.IndexedPartTransforms,
|
||||
PartAvailability = replacement.IndexedPartAvailable,
|
||||
});
|
||||
scheduler.Tick(1f / 60f);
|
||||
|
||||
Assert.Equal(1, scheduler.Count);
|
||||
Assert.True(
|
||||
replacement.MeshRefs[0].PartTransform.Translation.X > phaseBeforeRebind);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ShortStaticAnimFrame_RetainsCompleteTrailingPartState()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ public sealed class EntityScriptActivatorTests
|
|||
registerCount++;
|
||||
registeredEntity = entity;
|
||||
Assert.Same(info, actual);
|
||||
return true;
|
||||
},
|
||||
unregisterDatStaticAnimationOwner: ownerId =>
|
||||
{
|
||||
|
|
@ -313,7 +314,11 @@ public sealed class EntityScriptActivatorTests
|
|||
ScriptId: 0,
|
||||
PartTransforms: Array.Empty<Matrix4x4>(),
|
||||
DefaultAnimationId: 0x03000001u),
|
||||
registerDatStaticAnimationOwner: (_, _) => registerCount++);
|
||||
registerDatStaticAnimationOwner: (_, _) =>
|
||||
{
|
||||
registerCount++;
|
||||
return true;
|
||||
});
|
||||
|
||||
activator.OnCreate(MakeEntity(0x70000420u, Vector3.Zero));
|
||||
|
||||
|
|
@ -335,7 +340,11 @@ public sealed class EntityScriptActivatorTests
|
|||
PartTransforms: Array.Empty<Matrix4x4>(),
|
||||
DefaultAnimationId: 0x03000001u,
|
||||
UsesStaticAnimationWorkset: true),
|
||||
registerDatStaticAnimationOwner: (_, _) => registerCount++,
|
||||
registerDatStaticAnimationOwner: (_, _) =>
|
||||
{
|
||||
registerCount++;
|
||||
return true;
|
||||
},
|
||||
unregisterDatStaticAnimationOwner: _ => unregisterCount++);
|
||||
WorldEntity entity = MakeEntity(0x70000421u, Vector3.Zero);
|
||||
|
||||
|
|
@ -378,6 +387,7 @@ public sealed class EntityScriptActivatorTests
|
|||
animationAttempts++;
|
||||
if (animationAttempts == 1)
|
||||
throw new InvalidOperationException("animation acquire fault");
|
||||
return true;
|
||||
});
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => activator.OnCreate(entity));
|
||||
|
|
@ -424,7 +434,7 @@ public sealed class EntityScriptActivatorTests
|
|||
if (effectReleaseAttempts == 1)
|
||||
throw new InvalidOperationException("effect release fault");
|
||||
},
|
||||
registerDatStaticAnimationOwner: (_, _) => { },
|
||||
registerDatStaticAnimationOwner: (_, _) => true,
|
||||
unregisterDatStaticAnimationOwner: _ =>
|
||||
{
|
||||
animationReleaseAttempts++;
|
||||
|
|
@ -465,6 +475,97 @@ public sealed class EntityScriptActivatorTests
|
|||
Assert.Equal(first.Id, GameWindow.ParticleEntityKey(first));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatStaticRebind_RetriesOnlyUnfinishedOwnersAndDoesNotReplayDefault()
|
||||
{
|
||||
var p = BuildPipeline(
|
||||
(0xAAu, BuildScript((1.0, new CreateParticleHook { EmitterInfoId = 100 }))));
|
||||
var setup = new DatReaderWriter.DBObjs.Setup();
|
||||
ScriptActivationInfo info = new(
|
||||
ScriptId: 0xAAu,
|
||||
PartTransforms: Array.Empty<Matrix4x4>(),
|
||||
EffectProfile: EntityEffectProfile.CreateDatStatic(setup),
|
||||
Setup: setup,
|
||||
DefaultAnimationId: 0x03000001u,
|
||||
UsesStaticAnimationWorkset: true);
|
||||
int effectUpdates = 0;
|
||||
int animationRegistrations = 0;
|
||||
int animationRebindAttempts = 0;
|
||||
var activator = new EntityScriptActivator(
|
||||
p.Runner,
|
||||
p.Sink,
|
||||
p.Poses,
|
||||
_ => info,
|
||||
registerDatStaticEffectOwner: (_, _, _) => effectUpdates++,
|
||||
registerDatStaticAnimationOwner: (_, _) =>
|
||||
{
|
||||
animationRegistrations++;
|
||||
return true;
|
||||
},
|
||||
rebindDatStaticAnimationOwner: (_, _) =>
|
||||
{
|
||||
animationRebindAttempts++;
|
||||
if (animationRebindAttempts == 1)
|
||||
throw new InvalidOperationException("animation rebind fault");
|
||||
});
|
||||
WorldEntity first = MakeDatStatic(0x80A9B400u, Vector3.One);
|
||||
WorldEntity replacement = MakeDatStatic(
|
||||
first.Id,
|
||||
new Vector3(7f, 8f, 9f));
|
||||
activator.OnCreate(first);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() =>
|
||||
activator.OnRebindStatic(first, replacement));
|
||||
activator.OnRebindStatic(first, replacement);
|
||||
activator.OnCreate(replacement);
|
||||
|
||||
Assert.Equal(1, p.Runner.ActiveOwnerCount);
|
||||
Assert.Equal(2, effectUpdates);
|
||||
Assert.Equal(1, animationRegistrations);
|
||||
Assert.Equal(2, animationRebindAttempts);
|
||||
Assert.True(p.Poses.TryGetRootPose(first.Id, out Matrix4x4 root));
|
||||
Assert.Equal(replacement.Position, root.Translation);
|
||||
p.Runner.Tick(1.1);
|
||||
Assert.Single(p.Recording.Calls);
|
||||
Assert.Equal(replacement.Position, p.Recording.Calls[0].Pos);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DatStaticRebind_MissingDefaultAnimationNeverClaimsWorksetOwner()
|
||||
{
|
||||
var p = BuildPipeline();
|
||||
var setup = new DatReaderWriter.DBObjs.Setup();
|
||||
ScriptActivationInfo info = new(
|
||||
ScriptId: 0,
|
||||
PartTransforms: Array.Empty<Matrix4x4>(),
|
||||
Setup: setup,
|
||||
DefaultAnimationId: 0x03000001u,
|
||||
UsesStaticAnimationWorkset: true);
|
||||
int registerAttempts = 0;
|
||||
int rebindAttempts = 0;
|
||||
var activator = new EntityScriptActivator(
|
||||
p.Runner,
|
||||
p.Sink,
|
||||
p.Poses,
|
||||
_ => info,
|
||||
registerDatStaticAnimationOwner: (_, _) =>
|
||||
{
|
||||
registerAttempts++;
|
||||
return false;
|
||||
},
|
||||
rebindDatStaticAnimationOwner: (_, _) => rebindAttempts++);
|
||||
WorldEntity first = MakeDatStatic(0x80A9B400u, Vector3.Zero);
|
||||
WorldEntity replacement = MakeDatStatic(first.Id, Vector3.One);
|
||||
|
||||
activator.OnCreate(first);
|
||||
activator.OnRebindStatic(first, replacement);
|
||||
activator.OnCreate(replacement);
|
||||
|
||||
Assert.Equal(2, registerAttempts);
|
||||
Assert.Equal(0, rebindAttempts);
|
||||
Assert.True(p.Poses.TryGetRootPose(first.Id, out _));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LiveParticleFilterUsesCanonicalLocalEffectOwnerNotServerGuid()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue