fix(correctness): cold-audit fixes -- tile-cache determinism bug, cache identities, lifetimes
Correctness round on AUDIT-COLD-2026-09-14.md (Codex; key fixes reviewed by me). #7 (determinism, introduced by the tile-cache work): the refetch guard in GetDensityAt now rebuilds on `bTileCacheContext != CP_UsesTileCacheWindow` or when the worker-local stack/core were moved into a cache entry (`CP_ActiveTunnelDensityCache != nullptr`). A cache miss can no longer evaluate a moved-from, empty operator stack. Test (VoxelForgeCorrectnessRoundTest): the pre-fix binary CRASHED in the context-switch case (negative control); the fixed binary passes with 0/5 coarse and fine mismatches, and warm vs fresh origin-tile meshes are identical. Also fixed, each with a focused test that passes: - #3/#8: worker diff/surface/biome caches carry owner/layer identity (no cross-world reuse); #9: the shaft key gains LedgeSpacing/LedgeDepth and the island key BoundarySealThickness; #14: clearing passages bumps PassagesVersion; #21: HasAnyModInChunkRange is proportional to the queried range (the sealed-rock proof calls it per candidate). - #1: the density-volume fill thread is paused/drained with generation; #2: each content manager joins its own decoration tasks before destruction; #15: non-finite/invalid modification geometry is rejected; #20: zero-width transition blend is guarded at runtime. - Also #4-#6, #10-#13, #16-#18, #22. #19 is valid C++20 and unchanged. Verified: canonical export b3e5f4c3..9b377; capability 20,830 / 10,909; fresh trace 841/841 identical, 0 origin-column mismatches; static mesher calls 18,078,130. Performance: two pairs disagree in sign, and the means are final -2.5% static / -0.8% moving; no repeatable regression. Known and NOT caused by this round: ClassifyTileSoundness and PassageLandsInOpenSpace were already failing (15/17 in the grouped suite). Queued for investigation. Ledger: first determinism launch hit the DDC fatal (crash folder 57DB6ADC, 23:21); the retry exited 1 on the two known failures; one malformed multi-test launch was inconclusive; one malformed trace launch was stopped; one launch with an unquoted project path found no test and its retry passed. Everything else passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TaFdXhTdYMkW1xrV6ApT6Y
This commit is contained in:
1 parent
39c306b524
commit
8879045627
14 files changed
+1365
-139
No files matched your search
@@ -280,6 +280,8 @@ struct FSurfaceColumnBox
|
||||
static constexpr int32 Dim = 2 * Halo + 1;
|
||||
int32 BaseX = 0, BaseY = 0; // box origin (voxel coords)
|
||||
int32 StrateKey = MIN_int32, Seed = MIN_int32; // key: same strate ⇒ identical heightfield params
|
||||
uint64 OwnerId = 0; // generator/world identity
|
||||
uint64 ManagerLifetimeId = 0; // strate-manager lifetime identity
|
||||
// AUDIT C2, étendu : StrateKey vaut round(StrateBottomWorldZ), donc une édition à chaud qui
|
||||
// change les params de terrain SANS déplacer la strate (fréquence de bruit, hauteur de
|
||||
// montagne, un biome…) laisse la clé identique et sert des colonnes périmées. C'est la forme
|
||||
@@ -301,14 +303,17 @@ struct FSurfaceColumnCache
|
||||
FSurfaceColumnBox Boxes[NumBoxes];
|
||||
uint32 Clock = 0;
|
||||
|
||||
// Return the box covering (IX,IY) for this (StrateKey,Seed,LayoutVersion); allocate by
|
||||
// Return the box covering (IX,IY) for this (owner,manager,StrateKey,Seed,LayoutVersion); allocate by
|
||||
// evicting the LRU box on miss.
|
||||
FSurfaceColumnBox& Acquire(int32 IX, int32 IY, int32 InStrateKey, int32 InSeed, uint32 InLayoutVersion)
|
||||
FSurfaceColumnBox& Acquire(int32 IX, int32 IY, int32 InStrateKey, int32 InSeed,
|
||||
uint32 InLayoutVersion, uint64 InOwnerId,
|
||||
uint64 InManagerLifetimeId)
|
||||
{
|
||||
++Clock;
|
||||
for (FSurfaceColumnBox& B : Boxes)
|
||||
{
|
||||
if (B.bValid && B.StrateKey == InStrateKey && B.Seed == InSeed
|
||||
if (B.bValid && B.OwnerId == InOwnerId && B.ManagerLifetimeId == InManagerLifetimeId
|
||||
&& B.StrateKey == InStrateKey && B.Seed == InSeed
|
||||
&& B.LayoutVersion == InLayoutVersion
|
||||
&& IX >= B.BaseX && IX < B.BaseX + FSurfaceColumnBox::Dim
|
||||
&& IY >= B.BaseY && IY < B.BaseY + FSurfaceColumnBox::Dim)
|
||||
@@ -324,6 +329,8 @@ struct FSurfaceColumnCache
|
||||
Victim->BaseY = IY - FSurfaceColumnBox::Halo;
|
||||
Victim->StrateKey = InStrateKey;
|
||||
Victim->Seed = InSeed;
|
||||
Victim->OwnerId = InOwnerId;
|
||||
Victim->ManagerLifetimeId = InManagerLifetimeId;
|
||||
Victim->LayoutVersion = InLayoutVersion;
|
||||
Victim->bValid = true;
|
||||
Victim->LastUse = Clock;
|
||||
@@ -2798,6 +2805,10 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
// The key MUST include the layout version, not just the chunk coord. Symptom without it:
|
||||
// "I tweaked the strate asset, regenerated, and one patch kept the old shape."
|
||||
thread_local uint32 CP_Version = 0xFFFFFFFFu;
|
||||
// The tunnel cache key also includes whether the current tile requested the coarse
|
||||
// tile-window context. A cache hit moves the prepared stack/core out of these TLS values;
|
||||
// when the other context misses, this bit must force the rebuild guard below to run.
|
||||
thread_local bool CP_UsesTileCacheWindow = false;
|
||||
// OPSTACK Phase 1 — la pile d'opérateurs, construite dans le MÊME bloc de refetch que les
|
||||
// params (donc même clé owner+chunk+version, aucune logique d'invalidation en plus). Vide tant que
|
||||
// la strate n'a pas coché `bUseOperatorStack` ET que son archétype n'est pas porté.
|
||||
@@ -2916,6 +2927,7 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
CP_TunnelFP = CachedEntry.TunnelFingerprint;
|
||||
CP_Dist = CachedEntry.Disturbance;
|
||||
CP_UseOpStack = CachedEntry.bUseOpStack;
|
||||
CP_UsesTileCacheWindow = CachedEntry.bUsesTileCacheWindow;
|
||||
CP_UseCustomRecipe = false;
|
||||
#if WITH_EDITOR
|
||||
CP_UseComposerRegions = false;
|
||||
@@ -2935,9 +2947,17 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
|
||||
const bool bOwnerChanged = DensityCacheOwnerId != CP_OwnerId;
|
||||
const bool bManagerChanged = ManagerLifetimeId != CP_ManagerLifetimeId;
|
||||
// A cache hit may have moved CP_OpStack/GTunnelCoreCache into the shared entry. If the
|
||||
// next lookup misses (including an eviction/collision for the same shared Z key), the
|
||||
// fallback TLS objects are moved-from and must never be evaluated as if they were valid.
|
||||
// The active-entry pointer is the explicit moved-out marker; a miss therefore forces the
|
||||
// normal rebuild even when the shared key happens to equal ChunkCoord at the origin.
|
||||
const bool bMovedOutTunnelState = CP_ActiveTunnelDensityCache != nullptr;
|
||||
if (!bLoadedTunnelDensityCache
|
||||
&& (bOwnerChanged || bManagerChanged || ChunkCoord != CP_Chunk
|
||||
|| LayoutVersion != CP_Version))
|
||||
|| LayoutVersion != CP_Version
|
||||
|| bTileCacheContext != CP_UsesTileCacheWindow
|
||||
|| bMovedOutTunnelState))
|
||||
{
|
||||
CP_ActiveTunnelDensityCache = nullptr;
|
||||
|
||||
@@ -2955,6 +2975,7 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
CP_ManagerLifetimeId = ManagerLifetimeId;
|
||||
CP_Version = LayoutVersion;
|
||||
CP_Chunk = ChunkCoord;
|
||||
CP_UsesTileCacheWindow = bTileCacheContext;
|
||||
CP_GenType = StrateManager->GetGeneratorTypeForChunk(ChunkCoord);
|
||||
CP_UseCustomRecipe = false;
|
||||
bool bAllowCustomRecipe = true;
|
||||
@@ -3305,7 +3326,9 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
const int32 IX = (int32)WorldX, IY = (int32)WorldY;
|
||||
// XY-keyed LRU box (shared down the whole vertical strate stack). Acquire centres a box on
|
||||
// the first sample so the rest of the chunk's queries — incl. the ±Step margin ring — hit.
|
||||
FSurfaceColumnBox& Box = GSurfColCache.Acquire(IX, IY, CP_StrateKey, Seed, LayoutVersion);
|
||||
FSurfaceColumnBox& Box = GSurfColCache.Acquire(
|
||||
IX, IY, CP_StrateKey, Seed, LayoutVersion,
|
||||
DensityCacheOwnerId, ManagerLifetimeId);
|
||||
const int32 CI = (IY - Box.BaseY) * FSurfaceColumnBox::Dim + (IX - Box.BaseX);
|
||||
if (!Box.Computed[CI])
|
||||
{
|
||||
@@ -3695,15 +3718,18 @@ float UVoxelGenerator::GetDensityAt(float WorldX, float WorldY, float WorldZ) co
|
||||
struct FDiffSlot
|
||||
{
|
||||
FIntVector Chunk = FIntVector(INT32_MAX, INT32_MAX, INT32_MAX);
|
||||
uint64 OwnerId = 0;
|
||||
uint32 Version = 0;
|
||||
TArray<FVoxelModification> Mods;
|
||||
};
|
||||
static thread_local FDiffSlot DiffSlots[64];
|
||||
|
||||
const uint64 DiffLayerOwnerId = DiffLayer->GetCacheLifetimeId();
|
||||
const uint32 V = DiffLayer->GetModsVersion();
|
||||
FDiffSlot& Slot = DiffSlots[(ChunkCoord.X & 3) | ((ChunkCoord.Y & 3) << 2) | ((ChunkCoord.Z & 3) << 4)];
|
||||
if (Slot.Chunk != ChunkCoord || Slot.Version != V)
|
||||
if (Slot.OwnerId != DiffLayerOwnerId || Slot.Chunk != ChunkCoord || Slot.Version != V)
|
||||
{
|
||||
Slot.OwnerId = DiffLayerOwnerId;
|
||||
Slot.Chunk = ChunkCoord;
|
||||
Slot.Version = V;
|
||||
DiffLayer->GetChunkModsSnapshot(ChunkCoord, Slot.Mods); // ONE lock per chunk, not per voxel
|
||||
@@ -5768,12 +5794,24 @@ bool UVoxelGenerator::GetSurfaceHeightAt(float WorldX, float WorldY, int32 Chunk
|
||||
thread_local TArray<FSurfaceGenerationParams> OC_BiomeParams;
|
||||
thread_local FChunkBiomeCache OC_BiomeCache;
|
||||
thread_local uint32 OC_Version = 0xFFFFFFFFu; // AUDIT C2
|
||||
thread_local uint64 OC_OwnerId = 0;
|
||||
thread_local uint64 OC_ManagerLifetimeId = 0;
|
||||
|
||||
const uint32 OC_LayoutVersion = StrateManager->GetLayoutVersion();
|
||||
if (ChunkCoord != OC_Chunk || OC_LayoutVersion != OC_Version)
|
||||
const uint64 OC_CurrentOwnerId = DensityCacheOwnerId;
|
||||
const uint64 OC_CurrentManagerLifetimeId = StrateManager->GetCacheLifetimeId();
|
||||
if (ChunkCoord != OC_Chunk || OC_LayoutVersion != OC_Version
|
||||
|| OC_CurrentOwnerId != OC_OwnerId
|
||||
|| OC_CurrentManagerLifetimeId != OC_ManagerLifetimeId)
|
||||
{
|
||||
if (OC_LayoutVersion != OC_Version) { OC_BiomeCache.Invalidate(); }
|
||||
if (OC_LayoutVersion != OC_Version || OC_CurrentOwnerId != OC_OwnerId
|
||||
|| OC_CurrentManagerLifetimeId != OC_ManagerLifetimeId)
|
||||
{
|
||||
OC_BiomeCache.Invalidate();
|
||||
}
|
||||
OC_Version = OC_LayoutVersion;
|
||||
OC_OwnerId = OC_CurrentOwnerId;
|
||||
OC_ManagerLifetimeId = OC_CurrentManagerLifetimeId;
|
||||
OC_Chunk = ChunkCoord;
|
||||
ResolveSurfaceChunkParams(ChunkCoord, OC_Surface, OC_BiomeCtx, OC_BiomeParams);
|
||||
}
|
||||
@@ -6655,10 +6693,18 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
||||
// AUDIT C2 — même discipline que le chemin densité : la grille de biome du classifieur survit
|
||||
// d'un appel à l'autre et sa boîte de validité ne dit rien du contexte qui l'a produite.
|
||||
static thread_local uint32 TC_SeenVersion = 0xFFFFFFFFu;
|
||||
static thread_local uint64 TC_SeenOwnerId = 0;
|
||||
static thread_local uint64 TC_SeenManagerLifetimeId = 0;
|
||||
const uint32 TC_LayoutVersion = StrateManager->GetLayoutVersion();
|
||||
if (TC_LayoutVersion != TC_SeenVersion)
|
||||
const uint64 TC_OwnerId = DensityCacheOwnerId;
|
||||
const uint64 TC_ManagerLifetimeId = StrateManager->GetCacheLifetimeId();
|
||||
if (TC_LayoutVersion != TC_SeenVersion
|
||||
|| TC_OwnerId != TC_SeenOwnerId
|
||||
|| TC_ManagerLifetimeId != TC_SeenManagerLifetimeId)
|
||||
{
|
||||
TC_SeenVersion = TC_LayoutVersion;
|
||||
TC_SeenOwnerId = TC_OwnerId;
|
||||
TC_SeenManagerLifetimeId = TC_ManagerLifetimeId;
|
||||
TC_BiomeCache.Invalidate();
|
||||
}
|
||||
int32 NumSlots = 0;
|
||||
@@ -7208,7 +7254,9 @@ EVoxelTileClass UVoxelGenerator::ClassifyTileUncached(
|
||||
{
|
||||
FSurfSlot& S = Slots[s];
|
||||
if (S.InteriorZ.Num() == 0) continue;
|
||||
FSurfaceColumnBox& Box = GSurfColCache.Acquire(Xi, Yi, S.StrateKey, Seed, TC_LayoutVersion);
|
||||
FSurfaceColumnBox& Box = GSurfColCache.Acquire(
|
||||
Xi, Yi, S.StrateKey, Seed, TC_LayoutVersion,
|
||||
TC_OwnerId, TC_ManagerLifetimeId);
|
||||
const int32 CI = (Yi - Box.BaseY) * FSurfaceColumnBox::Dim + (Xi - Box.BaseX);
|
||||
if (!Box.Computed[CI])
|
||||
{
|
||||
@@ -7447,10 +7495,13 @@ FBiomeSample UVoxelGenerator::SampleBiomeAt(float WorldX, float WorldY, const FB
|
||||
}
|
||||
|
||||
void UVoxelGenerator::RebuildBiomeGrid(int32 ChunkX, int32 ChunkY, int32 ChunkZ,
|
||||
const FBiomeContext& Ctx, FChunkBiomeCache& Cache) const
|
||||
const FBiomeContext& Ctx, FChunkBiomeCache& Cache,
|
||||
uint64 OwnerId, uint64 ManagerLifetimeId) const
|
||||
{
|
||||
Cache.ChunkZ = ChunkZ;
|
||||
Cache.Seed = Seed;
|
||||
Cache.OwnerId = OwnerId;
|
||||
Cache.ManagerLifetimeId = ManagerLifetimeId;
|
||||
Cache.bActive = Ctx.IsValid();
|
||||
Cache.Ctx = Ctx;
|
||||
Cache.CellBiome.Reset();
|
||||
@@ -7506,12 +7557,16 @@ FBiomeSample UVoxelGenerator::ResolveBiomeSampleAt(float WorldX, float WorldY, i
|
||||
const FBiomeContext& Ctx, FChunkBiomeCache& Cache) const
|
||||
{
|
||||
FBiomeSample Out;
|
||||
const uint64 OwnerId = DensityCacheOwnerId;
|
||||
const uint64 ManagerLifetimeId = StrateManager
|
||||
? StrateManager->GetCacheLifetimeId() : 0;
|
||||
|
||||
// Box-validated rebuild (perf-only; result is the pure function of XY either way).
|
||||
if (!Cache.Contains(WorldX, WorldY, ChunkZ, Seed))
|
||||
if (!Cache.Contains(WorldX, WorldY, ChunkZ, Seed, OwnerId, ManagerLifetimeId))
|
||||
{
|
||||
RebuildBiomeGrid(FMath::FloorToInt(WorldX / CHUNK_SIZE),
|
||||
FMath::FloorToInt(WorldY / CHUNK_SIZE), ChunkZ, Ctx, Cache);
|
||||
FMath::FloorToInt(WorldY / CHUNK_SIZE), ChunkZ, Ctx, Cache,
|
||||
OwnerId, ManagerLifetimeId);
|
||||
}
|
||||
|
||||
if (!Cache.bActive) return Out;
|
||||
@@ -7659,12 +7714,24 @@ void UVoxelGenerator::GetBiomeMaterialAt(float WorldX, float WorldY, float World
|
||||
thread_local FBiomeContext BM_Ctx;
|
||||
thread_local FChunkBiomeCache BM_Cache;
|
||||
thread_local uint32 BM_Version = 0xFFFFFFFFu; // AUDIT C2
|
||||
thread_local uint64 BM_OwnerId = 0;
|
||||
thread_local uint64 BM_ManagerLifetimeId = 0;
|
||||
|
||||
const uint32 BM_LayoutVersion = StrateManager->GetLayoutVersion();
|
||||
if (ChunkCoord != BM_Chunk || BM_LayoutVersion != BM_Version)
|
||||
const uint64 BM_CurrentOwnerId = DensityCacheOwnerId;
|
||||
const uint64 BM_CurrentManagerLifetimeId = StrateManager->GetCacheLifetimeId();
|
||||
if (ChunkCoord != BM_Chunk || BM_LayoutVersion != BM_Version
|
||||
|| BM_CurrentOwnerId != BM_OwnerId
|
||||
|| BM_CurrentManagerLifetimeId != BM_ManagerLifetimeId)
|
||||
{
|
||||
if (BM_LayoutVersion != BM_Version) { BM_Cache.Invalidate(); }
|
||||
if (BM_LayoutVersion != BM_Version || BM_CurrentOwnerId != BM_OwnerId
|
||||
|| BM_CurrentManagerLifetimeId != BM_ManagerLifetimeId)
|
||||
{
|
||||
BM_Cache.Invalidate();
|
||||
}
|
||||
BM_Version = BM_LayoutVersion;
|
||||
BM_OwnerId = BM_CurrentOwnerId;
|
||||
BM_ManagerLifetimeId = BM_CurrentManagerLifetimeId;
|
||||
BM_Chunk = ChunkCoord;
|
||||
BM_Ctx = StrateManager->GetBiomeContextForChunk(ChunkCoord);
|
||||
}
|
||||
@@ -7727,14 +7794,15 @@ float UVoxelGenerator::GetVerticalShaftDensity(float WorldX, float WorldY, float
|
||||
thread_local float VS_Spacing = -1.0f, VS_Dens = -1.0f, VS_MinR = -1.0f, VS_MaxR = -1.0f,
|
||||
VS_Cross = -1.0f, VS_ConnR = -1.0f, VS_Rough = -1.0f,
|
||||
VS_SpineR = -1.0f, VS_BotZ = FLT_MAX, VS_TopZ = FLT_MAX,
|
||||
VS_Seal = -1.0f;
|
||||
VS_Seal = -1.0f, VS_LedgeSpacing = -1.0f, VS_LedgeDepth = -1.0f;
|
||||
|
||||
if (CX != VS_CX || CY != VS_CY || S != VS_Seed || Spacing != VS_Spacing ||
|
||||
Params.ShaftDensity != VS_Dens || Params.ShaftMinRadius != VS_MinR || Params.ShaftMaxRadius != VS_MaxR ||
|
||||
Params.CrossConnectChance != VS_Cross || Params.ConnectorRadius != VS_ConnR ||
|
||||
Params.SurfaceRoughness != VS_Rough || OriginSpineRadius != VS_SpineR ||
|
||||
Params.StrateBottomWorldZ != VS_BotZ || Params.StrateTopWorldZ != VS_TopZ ||
|
||||
Params.BoundarySealThickness != VS_Seal)
|
||||
Params.BoundarySealThickness != VS_Seal
|
||||
|| Params.LedgeSpacing != VS_LedgeSpacing || Params.LedgeDepth != VS_LedgeDepth)
|
||||
{
|
||||
VS_CX = CX; VS_CY = CY; VS_Seed = S; VS_Spacing = Spacing;
|
||||
VS_Dens = Params.ShaftDensity; VS_MinR = Params.ShaftMinRadius; VS_MaxR = Params.ShaftMaxRadius;
|
||||
@@ -7742,6 +7810,8 @@ float UVoxelGenerator::GetVerticalShaftDensity(float WorldX, float WorldY, float
|
||||
VS_Rough = Params.SurfaceRoughness; VS_SpineR = OriginSpineRadius;
|
||||
VS_BotZ = Params.StrateBottomWorldZ; VS_TopZ = Params.StrateTopWorldZ;
|
||||
VS_Seal = Params.BoundarySealThickness;
|
||||
VS_LedgeSpacing = Params.LedgeSpacing;
|
||||
VS_LedgeDepth = Params.LedgeDepth;
|
||||
Shafts.Reset();
|
||||
Conns.Reset();
|
||||
TreeEmitShafts.Reset();
|
||||
@@ -8154,17 +8224,20 @@ float UVoxelGenerator::GetFloatingIslandDensity(float WorldX, float WorldY, floa
|
||||
thread_local int32 FI_CX = INT32_MAX, FI_CY = INT32_MAX;
|
||||
thread_local uint32 FI_Seed = 0xFFFFFFFFu;
|
||||
thread_local float FI_Spacing = -1.0f, FI_Dens = -1.0f, FI_MinR = -1.0f, FI_MaxR = -1.0f,
|
||||
FI_Thick = -1.0f, FI_VJit = -1.0f, FI_BotZ = FLT_MAX, FI_TopZ = FLT_MAX;
|
||||
FI_Thick = -1.0f, FI_VJit = -1.0f, FI_BotZ = FLT_MAX, FI_TopZ = FLT_MAX,
|
||||
FI_Seal = -1.0f;
|
||||
|
||||
if (CX != FI_CX || CY != FI_CY || S != FI_Seed || Spacing != FI_Spacing ||
|
||||
Params.IslandDensity != FI_Dens || Params.IslandMinRadius != FI_MinR || Params.IslandMaxRadius != FI_MaxR ||
|
||||
Params.ThicknessRatio != FI_Thick || Params.VerticalJitter != FI_VJit ||
|
||||
Params.StrateBottomWorldZ != FI_BotZ || Params.StrateTopWorldZ != FI_TopZ)
|
||||
Params.StrateBottomWorldZ != FI_BotZ || Params.StrateTopWorldZ != FI_TopZ
|
||||
|| Params.BoundarySealThickness != FI_Seal)
|
||||
{
|
||||
FI_CX = CX; FI_CY = CY; FI_Seed = S; FI_Spacing = Spacing;
|
||||
FI_Dens = Params.IslandDensity; FI_MinR = Params.IslandMinRadius; FI_MaxR = Params.IslandMaxRadius;
|
||||
FI_Thick = Params.ThicknessRatio; FI_VJit = Params.VerticalJitter;
|
||||
FI_BotZ = Params.StrateBottomWorldZ; FI_TopZ = Params.StrateTopWorldZ;
|
||||
FI_Seal = Params.BoundarySealThickness;
|
||||
Islands.Reset();
|
||||
|
||||
for (int32 dy = -1; dy <= 1; dy++)
|
||||
|
||||
Reference in new issue
Block a user