fix(streaming): commit EnvCell landblocks atomically (#214)

Carry one complete cell payload with each streaming completion and publish visibility, physics, and render state on the render thread. Remove the obsolete post-readiness login reload that triggered a duplicate dungeon build.

Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
Erik 2026-07-13 18:45:24 +02:00
parent 85980fe13f
commit b0c175afc0
26 changed files with 973 additions and 617 deletions

View file

@ -1,5 +1,6 @@
using System.Threading.Tasks;
using AcDream.App.Streaming;
using AcDream.App.Rendering.Wb;
using AcDream.Core.World;
using DatReaderWriter.DBObjs;
using Xunit;
@ -109,6 +110,35 @@ public class LandblockStreamerTests
Assert.Equal(1, meshBuildCalls);
}
[Fact]
public async Task Load_CarriesTheExactCompletedCellTransactionToTheConsumer()
{
var landblock = new LoadedLandblock(
0x8C04FFFFu,
new LandBlock(),
System.Array.Empty<WorldEntity>());
var cellBuild = new EnvCellLandblockBuild(
landblock.LandblockId,
System.Array.Empty<AcDream.App.Rendering.LoadedCell>(),
System.Array.Empty<EnvCellShellPlacement>());
var build = new LandblockBuild(landblock, cellBuild);
var mesh = new AcDream.Core.Terrain.LandblockMeshData(
System.Array.Empty<AcDream.Core.Terrain.TerrainVertex>(),
System.Array.Empty<uint>());
using var streamer = new LandblockStreamer(
loadLandblock: (_, _) => build,
buildMeshOrNull: (_, _) => mesh);
streamer.Start();
streamer.EnqueueLoad(landblock.LandblockId, LandblockStreamJobKind.LoadNear);
var loaded = Assert.IsType<LandblockStreamResult.Loaded>(
await DrainFirstAsync(streamer));
Assert.Same(build, loaded.Build);
Assert.Same(cellBuild, loaded.Build.EnvCells);
}
[Fact]
public async Task PromoteToNear_OvertakesAndSupersedesQueuedFarLoadForSameLandblock()
{

View file

@ -163,6 +163,33 @@ public class StreamingControllerDungeonGateTests
Assert.DoesNotContain(h.Loads, l => l.Kind == LandblockStreamJobKind.LoadFar);
}
[Fact]
public void InitializeKnownLoginCenter_DungeonPerformsOneCollapseWithoutReloadChurn()
{
var h = Make();
h.Ctrl.InitializeKnownLoginCenter(0, 7, isSealedDungeon: true);
h.Ctrl.InitializeKnownLoginCenter(0, 7, isSealedDungeon: true);
Assert.Single(h.Loads);
Assert.Equal(Encode(0, 7), h.Loads[0].Id);
Assert.Equal(1, h.ClearCalls());
}
[Fact]
public void InitializeKnownLoginCenter_OutdoorWaitsForFirstCorrectlyCenteredTick()
{
var h = Make();
h.Ctrl.InitializeKnownLoginCenter(140, 4, isSealedDungeon: false);
Assert.Empty(h.Loads);
Assert.Equal(0, h.ClearCalls());
h.Ctrl.Tick(140, 4, insideDungeon: false);
Assert.Contains(h.Loads, load => load.Id == Encode(140, 4));
}
[Fact]
public void PreCollapse_AfterBootstrapTick_CancelsWindow_UnloadsResidentNeighbors_KeepsDungeon()
{

View file

@ -73,7 +73,7 @@ public class StreamingControllerTests
var applied = new List<LoadedLandblock>();
var controller = new StreamingController(
fake.EnqueueLoad, fake.EnqueueUnload, fake.DrainCompletions,
(lb, _) => applied.Add(lb), state, nearRadius: 2, farRadius: 2);
(build, _) => applied.Add(build.Landblock), state, nearRadius: 2, farRadius: 2);
// Note: LoadedLandblock's actual fields are LandblockId, Heightmap,
// Entities (positional record). Adjust if the first positional arg

View file

@ -131,7 +131,7 @@ public class StreamingControllerTwoTierTests
applyTerrain: (appliedLb, appliedMesh) =>
{
applyPromotedCount++;
Assert.Same(promotedLb, appliedLb);
Assert.Same(promotedLb, appliedLb.Landblock);
Assert.Same(promotedMesh, appliedMesh);
},
state: state,