From 9d1c2c45e51361246bc38679347f4f35a7ab4f72 Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 11 Apr 2026 22:11:35 +0200 Subject: [PATCH] =?UTF-8?q?feat(app):=20Phase=20A.1=20=E2=80=94=20job=20+?= =?UTF-8?q?=20result=20records=20for=20LandblockStreamer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LandblockStreamJob (Load/Unload) and LandblockStreamResult (Loaded/Failed/Unloaded) are the channel payload types the next task's LandblockStreamer will use. Separate file because they're shared between the worker thread and the render thread and deserve a focused home. Folds in two carryover nits from the Task 1 fix review: - Stale "radius + 1" comments in StreamingRegionTests updated to match the real radius+2 threshold (no numeric-assertion changes). - Single-step recenter test now asserts Visible.Count == 25 and Resident.Count == 30, locking in the Visible/Resident semantic split behaviorally. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Streaming/LandblockStreamJob.cs | 28 +++++++++++++++++++ .../Streaming/StreamingRegionTests.cs | 13 ++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 src/AcDream.App/Streaming/LandblockStreamJob.cs diff --git a/src/AcDream.App/Streaming/LandblockStreamJob.cs b/src/AcDream.App/Streaming/LandblockStreamJob.cs new file mode 100644 index 0000000..7542c68 --- /dev/null +++ b/src/AcDream.App/Streaming/LandblockStreamJob.cs @@ -0,0 +1,28 @@ +using AcDream.Core.World; + +namespace AcDream.App.Streaming; + +/// +/// A job posted to 's inbox. Either a load +/// (fetch this landblock from the dats and build its CPU-side mesh data) +/// or an unload (release any state tied to this landblock on the render +/// thread's next Tick drain). +/// +public abstract record LandblockStreamJob(uint LandblockId) +{ + public sealed record Load(uint LandblockId) : LandblockStreamJob(LandblockId); + public sealed record Unload(uint LandblockId) : LandblockStreamJob(LandblockId); +} + +/// +/// Outbox record the render thread drains. Either a successful load, a +/// failed load (logged and ignored until region recenters off/back), or +/// an unload notification (tells the render thread to release GPU state +/// for this landblock id). +/// +public abstract record LandblockStreamResult(uint LandblockId) +{ + public sealed record Loaded(uint LandblockId, LoadedLandblock Landblock) : LandblockStreamResult(LandblockId); + public sealed record Failed(uint LandblockId, string Error) : LandblockStreamResult(LandblockId); + public sealed record Unloaded(uint LandblockId) : LandblockStreamResult(LandblockId); +} diff --git a/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs b/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs index 767dd6d..d65b480 100644 --- a/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs +++ b/tests/AcDream.Core.Tests/Streaming/StreamingRegionTests.cs @@ -45,17 +45,22 @@ public class StreamingRegionTests [Fact] public void RecenterTo_SingleStepEast_LoadsColumn_NoUnloadsDueToHysteresis() { - // Radius 2 → unload threshold is radius+1 = 3. + // Radius 2 → unload threshold is radius+2 = 4. // Starting center (50,50) covers X in [48..52]. Step to (51,50): // new coverage X in [49..53]. New column is x=53 (5 entries). - // Departing column would be x=48, but |48-51| = 3 which equals the - // threshold, so it stays loaded (hysteresis keeps radius+1). + // Departing column x=48 is at distance |48-51| = 3, which is within + // the radius+2 threshold, so it stays loaded (hysteresis keeps radius+2). var region = new StreamingRegion(cx: 50, cy: 50, radius: 2); var diff = region.RecenterTo(51, 50); Assert.Equal(5, diff.ToLoad.Count); Assert.Empty(diff.ToUnload); + // Visible is strictly the 5×5 window around (51, 50). + Assert.Equal(25, region.Visible.Count); + // Resident includes the hysteresis-retained x=48 column (5 entries) + // plus the full new window, for 30 total. + Assert.Equal(30, region.Resident.Count); } [Fact] @@ -63,7 +68,7 @@ public class StreamingRegionTests { // Starting (50,50) covers X in [48..52]. Step to (53,50): // new coverage X in [51..55]. New columns: x=53,54,55 (15 entries). - // x=48 is now 5 away → unload. x=49,50 still within radius+1 → keep. + // x=48 is now 5 away, > radius+2 = 4 → unload. x=49 is 4 away, not > 4 → keep. x=50 is 3 away, not > 4 → keep. var region = new StreamingRegion(cx: 50, cy: 50, radius: 2); var diff = region.RecenterTo(53, 50);