wip(assembler): round L13 -- one density field for every route (multiplayer determinism fix)

Region pages fell back to the legacy GetDensityAt evaluator while the base shape used the Core
manifest field, so a tile's density (and sometimes mesh) depended on streaming history. Pages
are now bound to their world's base FStandaloneShapeWorld evaluator before publication; density,
lattice, detail, classification and solid queries all delegate to it. WorldAssemblyEvaluatorVersion
5 -> 6. Route-parity audit: fails on L12 (5/5); after the fix 0 mismatches over 87 tile pairs
(base/page, base/native-only, edited, two histories), 56 exact-query pairs, 2.3M page samples,
4 seeds/recipes. Page-tile proof audit: 1,274 blocks, 0 violations. The spawn and island worlds
are unchanged; crossing output changed as intended. Reviewer interleaved spawn: +1.1% (noise).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CSFYgsdW8ejZSyiMf4qRSX
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-20 01:54:54 +02:00
1 parent 32b8670833
commit 976d012a86
10 files changed
+767 -11

No files matched your search

@@ -235,7 +235,8 @@ namespace VoxelForgeCore
// L'assembleur v1 encode coordonnees et scalaires en 1/256 de voxel, independamment des tuiles.
inline constexpr std::int64_t WorldAssemblySubvoxelsPerVoxel = 256;
inline constexpr std::uint32_t WorldAssemblySchemaVersion = 1;
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 5;
// Version 6 binds every compiled tile page to the world's base density evaluator.
inline constexpr std::uint32_t WorldAssemblyEvaluatorVersion = 6;
enum class FieldChannel : std::uint32_t
{
@@ -1050,6 +1051,9 @@ namespace VoxelForgeCore
std::int32_t bandMaxZVoxel = 2147483647;
bool hashDensity = false;
bool captureDensitySamples = false;
// Diagnostic only; materialize the full density lattice even when block proofs
// establish that the tile has no surface.
bool forceFullDensityCapture = false;
bool skipUvAndColors = false;
bool skipUv = false;
bool skipColors = false;
+31 -4
View File
@@ -478,9 +478,9 @@ public:
using Clock=std::chrono::steady_clock;
Owner=&World;WorldAssembly=InWorldAssembly;
WorkerCount=1;DetailVariant=Variant;Scheduler=nullptr;
// Region pages use the immutable manifest for conservative block proofs, while keeping
// the legacy SurfaceWorld density evaluator so existing tile hashes remain exact.
bUseNativeManifestFields=false;
// Analytic pages retain their bounded owner bins for route bookkeeping. Once published,
// the Core world binds each page to its base shape as the single density evaluator.
bUseNativeManifestFields=true;
RoundTunnelsEnabled=bInRoundTunnels;bAnalyticOnly=true;bProfileDetailBand=false;
for(int32 A=0;A<3;++A)
{
@@ -991,6 +991,9 @@ public:
}
}
void SetSharedDensitySource(const FStandaloneShapeWorld* Source)
{ SharedDensitySource=Source==this?nullptr:Source; }
bool HasEditTransaction() const { return !EditBrushes.empty(); }
FIntVector GetEditBoundsMin() const { return EditBoundsMin; }
FIntVector GetEditBoundsMax() const { return EditBoundsMax; }
@@ -1017,6 +1020,7 @@ public:
// edge interpolation and normals to the existing mesher.
float DensityAt(float X,float Y,float Z) const
{
if(SharedDensitySource)return SharedDensitySource->DensityAt(X,Y,Z);
if(bNativeOnly)
{
if(!NativeOnlyContains(X,Y,Z))
@@ -1050,6 +1054,7 @@ public:
float DensityAtLattice(int32 X,int32 Y,int32 Z) const
{
if(SharedDensitySource)return SharedDensitySource->DensityAtLattice(X,Y,Z);
if(bNativeOnly)
{
if(!NativeOnlyContains(static_cast<float>(X),static_cast<float>(Y),
@@ -1089,6 +1094,7 @@ public:
float DensityAtUnclamped(float X,float Y,float Z) const
{
if(SharedDensitySource)return SharedDensitySource->DensityAtUnclamped(X,Y,Z);
if(bNativeOnly)
{
if(!NativeOnlyContains(X,Y,Z))
@@ -1117,6 +1123,7 @@ public:
VoxelForgeCore::DetailFieldSample DetailSampleAt(float X,float Y,float Z) const
{
if(SharedDensitySource)return SharedDensitySource->DetailSampleAt(X,Y,Z);
VoxelForgeCore::DetailFieldSample Sample;
if(bNativeOnly)
{RecordNativeOnlyViolation();return Sample;}
@@ -1135,6 +1142,11 @@ public:
void DensityAtLatticeX8(int32 XStart,int32 XStep,int32 Y,int32 Z,
int32 Count,float OutDensity[8]) const
{
if(SharedDensitySource)
{
SharedDensitySource->DensityAtLatticeX8(XStart,XStep,Y,Z,Count,OutDensity);
return;
}
Count=std::clamp(Count,0,8);
if(Count==0)return;
if(bNativeOnly)
@@ -1241,6 +1253,8 @@ private:
float* OutFloorInfluence=nullptr,
bool bApplyEdits=true) const
{
if(SharedDensitySource)return SharedDensitySource->AnalyticDensityAt(X,Y,Z,
bClampNearZero,OutDetailDisplacement,OutCaveInfluence,OutFloorInfluence,bApplyEdits);
if(OutDetailDisplacement)*OutDetailDisplacement=0.0f;
if(OutCaveInfluence)*OutCaveInfluence=0.0f;
if(OutFloorInfluence)*OutFloorInfluence=0.0f;
@@ -1303,6 +1317,8 @@ public:
bool GetNativeSurfaceHeightsAt(float X,float Y,int32 ChunkZ,
float& OutGroundZ,float& OutCeilingZ) const
{
if(SharedDensitySource)
return SharedDensitySource->GetNativeSurfaceHeightsAt(X,Y,ChunkZ,OutGroundZ,OutCeilingZ);
if(!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y))return false;
const std::int64_t chunkMinimum=static_cast<std::int64_t>(ChunkZ)*CHUNK_SIZE;
@@ -1339,6 +1355,7 @@ public:
bool IsNativeSurfaceBox(const FIntVector& Origin,int32 Step,int32 Cells) const
{
if(SharedDensitySource)return SharedDensitySource->IsNativeSurfaceBox(Origin,Step,Cells);
if(!WorldAssembly||!Owner||!Owner->Manager||Step<=0||Cells<=0)return false;
const int32 Pad=std::max(Step,1);
const int32 BMin=Origin.Z-Pad;
@@ -1377,6 +1394,8 @@ public:
FBoxClassification ClassifyBoxDetailed(const FIntVector& Origin,int32 Step,int32 Cells,
bool bUseNativeLatticeProof=false) const
{
if(SharedDensitySource)
return SharedDensitySource->ClassifyBoxDetailed(Origin,Step,Cells,bUseNativeLatticeProof);
using Clock=std::chrono::steady_clock;
const auto Start=Clock::now();
const int32 Pad=std::max(Step,1);
@@ -1557,6 +1576,7 @@ public:
bool IsRock(int32 X,int32 Y,int32 Z) const
{
if(SharedDensitySource)return SharedDensitySource->IsRock(X,Y,Z);
if(!Contains(X,Y,Z))return EvaluateFast(X,Y,Z);
const size_t Line=static_cast<size_t>(Y-Min[1])*Size[0]
+static_cast<size_t>(X-Min[0]);
@@ -2083,7 +2103,8 @@ private:
bool NativeFloatingIslandDensityAt(float X,float Y,float Z,float& OutDensity) const
{
if(!bUseNativeManifestFields||!WorldAssembly||!Owner||!Owner->Manager||!Owner->Generator
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))return false;
||!std::isfinite(X)||!std::isfinite(Y)||!std::isfinite(Z))
return false;
// Convert Core query Z back to the owner's legacy chunk coordinate.
// Convertir Z du Core vers la coordonnee de chunk legacy du proprietaire.
const double CoreVoxelsPerLegacyChunk=GetCoreVoxelsPerOwnerChunk();
@@ -4823,6 +4844,11 @@ public:
void DensityAtLatticeZRun(int32 X,int32 Y,int32 ZStart,int32 ZStep,
int32 Count,float* OutDensity) const
{
if(SharedDensitySource)
{
SharedDensitySource->DensityAtLatticeZRun(X,Y,ZStart,ZStep,Count,OutDensity);
return;
}
if(Count<=0||OutDensity==nullptr)return;
if(bNativeOnly)
{
@@ -5353,6 +5379,7 @@ private:
FStandaloneOwnerWorld* Owner=nullptr;
const VoxelForgeCore::WorldAssemblyManifest* WorldAssembly=nullptr;
const FStandaloneShapeWorld* SharedDensitySource=nullptr;
int32 WorkerCount=6;
const VoxelForgeCore::JobScheduler* Scheduler=nullptr;
bool RoundTunnelsEnabled=false;
+4 -1
View File
@@ -1386,6 +1386,8 @@ namespace VoxelForgeCore
compiledRegions[i]=std::move(region);
}
}
for(const auto& region:compiledRegions)
if(region)region->SetSharedDensitySource(impl_->shape.get());
{
std::unordered_map<TileKey,std::shared_ptr<FStandaloneShapeWorld>,TileKeyHash> publish;
publish.reserve(stats.tileKeysCovered);
@@ -2021,7 +2023,8 @@ namespace VoxelForgeCore
MeshBandMinZVoxel,MeshBandMaxZVoxel,&sampleCount,nullptr,nullptr,
(options.hashDensity||options.captureDensitySamples)?&densityValues:nullptr,
options.profileMesherStages?&McStageTimings:nullptr,
options.auditUniformBlockProofs?&BlockProofAudit:nullptr);
options.auditUniformBlockProofs?&BlockProofAudit:nullptr,
options.forceFullDensityCapture);
if(options.auditUniformBlockProofs)
{
out.diagnostics.uniformBlockProofsChecked=BlockProofAudit.CheckedBlocks;