🔍 Module Scanned
src/world/worldgen/ (automated audit scan)
📝 Summary
The ClassificationCache.recenter function does not enforce alignment of the center position to CELL_SIZE. When a misaligned center is provided, the contains() check can return true for world positions that map to out-of-bounds cell indices in getCellIndex(), causing cache corruption or missed lookups at chunk boundaries.
📍 Location
- File:
src/world/worldgen/gen_region.zig:418-447
- Function/Scope:
ClassificationCache.recenter and ClassificationCache.getCellIndex
🔴 Severity: High
- High: Memory corruption, cache misses, incorrect terrain at chunk boundaries
💥 Impact
When recenter(center_x, center_z) is called with coordinates that are not multiples of CELL_SIZE (8 blocks), the cache's contains() check returns true for positions near the grid boundary that actually map to invalid cell indices. This causes:
get() to return garbage data from out-of-bounds cells
put() to write to incorrect cells
has() to return incorrect results
In practice, this affects LOD terrain generation at chunk boundaries when the player moves to non-aligned positions.
🔎 Evidence
// gen_region.zig:418-427
pub fn recenter(self: *ClassificationCache, center_x: i32, center_z: i32) void {
const half_size: i32 = @intCast((GRID_SIZE * CELL_SIZE) / 2);
self.origin_x = center_x - half_size; // NOT aligned to CELL_SIZE!
self.origin_z = center_z - half_size;
...
}
// gen_region.zig:429-436
pub fn contains(self: *const ClassificationCache, world_x: i32, world_z: i32) bool {
const grid_extent: i32 = @intCast(GRID_SIZE * CELL_SIZE);
return world_x >= self.origin_x and
world_x < self.origin_x + grid_extent; // Half-open interval [origin, origin+extent)
}
// gen_region.zig:439-447
fn getCellIndex(self: *const ClassificationCache, world_x: i32, world_z: i32) ?usize {
if (!self.contains(world_x, world_z)) return null;
const local_x: u32 = @intCast(world_x - self.origin_x);
const cell_x = local_x / CELL_SIZE; // Can exceed GRID_SIZE when origin is misaligned!
...
}
Example of bug:
center_x = 1 (not aligned to 8)
origin_x = 1 - 1024 = -1023
contains(1024) → 1024 >= -1023 and 1024 < -1023 + 2048 = 1025 → TRUE
getCellIndex(1024) → local_x = 2047, cell_x = 2047 / 8 = 256 → OUT OF BOUNDS (valid: 0-255)
When center_x = 0 (aligned), origin_x = -1024, and contains(1023) returns TRUE with cell_x = 255 (correct, the last valid cell).
🛠️ Proposed Fix
Modify recenter to align center_x and center_z to CELL_SIZE before computing origin:
pub fn recenter(self: *ClassificationCache, center_x: i32, center_z: i32) void {
const half_size: i32 = @intCast((GRID_SIZE * CELL_SIZE) / 2);
// Align center to CELL_SIZE before computing origin
const aligned_center_x = @divFloor(center_x, @as(i32, @intCast(CELL_SIZE))) * @as(i32, @intCast(CELL_SIZE));
const aligned_center_z = @divFloor(center_z, @as(i32, @intCast(CELL_SIZE))) * @as(i32, @intCast(CELL_SIZE));
self.origin_x = aligned_center_x - half_size;
self.origin_z = aligned_center_z - half_size;
...
}
Alternatively, use @divTrunc which is more idiomatic in Zig for this alignment pattern.
✅ Acceptance Criteria
📚 References
- Related module:
ClassificationCache in gen_region.zig
CELL_SIZE = 8 defined in world_class.zig:22
- Issue affects LOD terrain generation via
OverworldGenerator.generateHeightmapOnly()
🔍 Module Scanned
src/world/worldgen/(automated audit scan)📝 Summary
The
ClassificationCache.recenterfunction does not enforce alignment of the center position toCELL_SIZE. When a misaligned center is provided, thecontains()check can returntruefor world positions that map to out-of-bounds cell indices ingetCellIndex(), causing cache corruption or missed lookups at chunk boundaries.📍 Location
src/world/worldgen/gen_region.zig:418-447ClassificationCache.recenterandClassificationCache.getCellIndex🔴 Severity: High
💥 Impact
When
recenter(center_x, center_z)is called with coordinates that are not multiples ofCELL_SIZE(8 blocks), the cache'scontains()check returnstruefor positions near the grid boundary that actually map to invalid cell indices. This causes:get()to return garbage data from out-of-bounds cellsput()to write to incorrect cellshas()to return incorrect resultsIn practice, this affects LOD terrain generation at chunk boundaries when the player moves to non-aligned positions.
🔎 Evidence
Example of bug:
center_x = 1(not aligned to 8)origin_x = 1 - 1024 = -1023contains(1024)→1024 >= -1023 and 1024 < -1023 + 2048 = 1025→ TRUEgetCellIndex(1024)→local_x = 2047,cell_x = 2047 / 8 = 256→ OUT OF BOUNDS (valid: 0-255)When
center_x = 0(aligned),origin_x = -1024, andcontains(1023)returns TRUE withcell_x = 255(correct, the last valid cell).🛠️ Proposed Fix
Modify
recenterto aligncenter_xandcenter_ztoCELL_SIZEbefore computingorigin:Alternatively, use
@divTruncwhich is more idiomatic in Zig for this alignment pattern.✅ Acceptance Criteria
recenter()properly aligns the cache origin toCELL_SIZEboundariescontains()return valid cell indices fromgetCellIndex()nix develop --command zig build test📚 References
ClassificationCacheingen_region.zigCELL_SIZE = 8defined inworld_class.zig:22OverworldGenerator.generateHeightmapOnly()