fix(rendering): isolate world selection transactions
Keep shared private entity viewports outside the normal-world picking product and make the injected spatial-pin retry test independent of the production frame clock. Co-Authored-By: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
0eb6648589
commit
056bbd4efd
3 changed files with 54 additions and 1 deletions
|
|
@ -31,6 +31,7 @@ internal sealed class RetailSelectionScene :
|
|||
private readonly HashSet<PartKey> _buildingKeys = new();
|
||||
private FrustumPlanes? _viewFrustum;
|
||||
private ICurrentRenderSelectionObserver? _currentRenderSceneObserver;
|
||||
private bool _frameOpen;
|
||||
|
||||
private readonly record struct PartKey(uint LocalEntityId, int PartIndex, uint GfxObjId);
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ internal sealed class RetailSelectionScene :
|
|||
public void Reset()
|
||||
{
|
||||
_currentRenderSceneObserver?.AbortSelectionFrame();
|
||||
_frameOpen = false;
|
||||
_building.Clear();
|
||||
_published.Clear();
|
||||
_buildingKeys.Clear();
|
||||
|
|
@ -75,6 +77,13 @@ internal sealed class RetailSelectionScene :
|
|||
|
||||
public void BeginFrame()
|
||||
{
|
||||
if (_frameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The retail selection scene cannot begin a second frame before completing or aborting the first.");
|
||||
}
|
||||
|
||||
_frameOpen = true;
|
||||
_building.Clear();
|
||||
_buildingKeys.Clear();
|
||||
_viewFrustum = null;
|
||||
|
|
@ -95,6 +104,12 @@ internal sealed class RetailSelectionScene :
|
|||
uint gfxObjId,
|
||||
Matrix4x4 partWorld)
|
||||
{
|
||||
// WbDrawDispatcher is also shared by sealed CreatureMode-style UI
|
||||
// viewports (paperdoll and appraisal). Those draws occur after the
|
||||
// normal-world selection transaction has published and must never
|
||||
// enter the world picking product.
|
||||
if (!_frameOpen)
|
||||
return;
|
||||
if (entity.ServerGuid == 0u)
|
||||
return;
|
||||
|
||||
|
|
@ -124,6 +139,13 @@ internal sealed class RetailSelectionScene :
|
|||
|
||||
public void CompleteFrame()
|
||||
{
|
||||
if (!_frameOpen)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"The retail selection scene cannot complete without an open frame.");
|
||||
}
|
||||
|
||||
_frameOpen = false;
|
||||
(_published, _building) = (_building, _published);
|
||||
_currentRenderSceneObserver?.CompleteSelectionFrame();
|
||||
}
|
||||
|
|
@ -132,6 +154,10 @@ internal sealed class RetailSelectionScene :
|
|||
/// completely published selection product.</summary>
|
||||
public void AbortFrame()
|
||||
{
|
||||
if (!_frameOpen)
|
||||
return;
|
||||
|
||||
_frameOpen = false;
|
||||
_currentRenderSceneObserver?.AbortSelectionFrame();
|
||||
_building.Clear();
|
||||
_buildingKeys.Clear();
|
||||
|
|
|
|||
|
|
@ -94,6 +94,32 @@ public sealed class RetailSelectionSceneTests
|
|||
Assert.Empty(oracle.SelectionParts);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PrivateViewportPartsOutsideWorldFrame_DoNotEnterPickingOrReferee()
|
||||
{
|
||||
var oracle = new CurrentRenderSceneOracle();
|
||||
var scene = CreateScene();
|
||||
scene.SetCurrentRenderSceneObserver(oracle);
|
||||
var world = Entity(localId: 49u, serverGuid: 0x5000_0049u);
|
||||
var privateViewport =
|
||||
Entity(localId: 50u, serverGuid: 0x5000_0050u);
|
||||
Publish(scene, world);
|
||||
|
||||
scene.AddVisiblePart(
|
||||
privateViewport,
|
||||
partIndex: 0,
|
||||
gfxObjId: 0x0100_0001u,
|
||||
Matrix4x4.CreateTranslation(0f, 0f, -5f));
|
||||
|
||||
RetailSelectionHit? hit = PickCenter(scene);
|
||||
Assert.NotNull(hit);
|
||||
Assert.Equal(world.Id, hit.Value.LocalEntityId);
|
||||
CurrentRenderSelectionFingerprint fingerprint =
|
||||
Assert.Single(oracle.SelectionParts);
|
||||
Assert.Equal(world.Id, fingerprint.LocalEntityId);
|
||||
Assert.Equal(1, oracle.Snapshot.SelectionPartCount);
|
||||
}
|
||||
|
||||
private static RetailSelectionScene CreateScene(
|
||||
RetailSelectionLightingPulse? pulse = null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -900,7 +900,8 @@ public sealed class LandblockPresentationPipelineTests
|
|||
nearRadius: 0,
|
||||
farRadius: 0,
|
||||
onLandblockLoaded: _ => liveRecoveryCalls++,
|
||||
ensureEnvCellMeshes: _ => replayCalls++);
|
||||
ensureEnvCellMeshes: _ => replayCalls++,
|
||||
workBudgetOptions: GenerousWorkBudget());
|
||||
|
||||
Assert.Throws<InvalidOperationException>(() => controller.Tick(0x60, 0x60));
|
||||
Assert.True(state.IsNearTier(landblockId));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue