wip(assembler): M3 round 4 -- plans built inside Unreal from the owner's strates; no import needed

SurfaceWorld and FloatingIslands plans generate at startup from the resolved seed,
strate types and heights (hashed, cached). Imported JSON is an opt-in override (off by
default); an invalid override shows an on-screen notice and falls back to legacy.
BuildHost on staged owner assets: SurfaceWorld cross-system checks, islands at owner
height (route, collision, anchors, BP edits), seed 137501, W1/W8/W16, Core-off legacy.
XY-column cache verified (hashes kept); island LOD0 p95 91 ms vs 133 ms sprint deadline.
Not exercised: season-seed path (no Season in staged settings), rendered notice pixels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
This commit is contained in:
Fr0zkaandClaude Opus 5.5 committed 2026-10-10 22:48:46 +02:00
1 parent 48fa028086
commit 111cf3d262
21 files changed
+1934 -391

No files matched your search

+122 -17
View File
@@ -152,6 +152,7 @@ public:
std::atomic<bool> bShuttingDown{false};
std::atomic<uint64> NextEditSequence{1};
int32 FullResolutionLodCount = 0;
FString InitialFailureReason;
};
#if WITH_EDITOR
@@ -2456,12 +2457,13 @@ void AVoxelWorld::BeginPlay()
DiffLayer->SetBudget(Settings->MaxModifications, Settings->MaxBrushRadius, Settings->MaxTotalVolume);
Generator->SetDiffLayer(DiffLayer);
if (bUseVoxelForgeCore && !InitializeVoxelForgeCoreWorld())
if (bUseVoxelForgeCore)
{
// A failed opt-in must stay visibly failed. Falling back to the legacy route here would
// make the switch appear to work while silently changing the generated world.
UE_LOG(LogTemp, Error,
TEXT("[VoxelForgeCoreRuntime] Core mode is enabled but initialization did not complete."));
FString CoreFailureReason;
if (!InitializeVoxelForgeCoreWorld(CoreFailureReason))
{
FallBackToLegacyAfterCoreInitializationFailure(CoreFailureReason);
}
}
if (bTestCeilingViewEnabled && IsParameterizedResearchFieldActive())
@@ -6596,6 +6598,12 @@ void AVoxelWorld::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
TRACE_CPUPROFILER_EVENT_SCOPE(VoxelForge_Tick); // game-thread streaming orchestration breakdown
if (bUseVoxelForgeCore && VoxelForgeCoreState.IsValid()
&& VoxelForgeCoreState->bInitialFailed.load(std::memory_order_acquire))
{
FallBackToLegacyAfterCoreInitializationFailure(
VoxelForgeCoreState->InitialFailureReason);
}
#if WITH_EDITOR
if (bTestResearchProfileSwitchConfigured
&& !bTestResearchProfileSwitchApplied
@@ -7217,6 +7225,14 @@ FVector AVoxelWorld::GetVoxelForgeCoreDefaultCenterWorld() const
return GetActorTransform().TransformPosition(CoreFallbackCenterLocalCm);
}
bool AVoxelWorld::GetVoxelForgeCoreWorldAssembly(
FString& OutCanonicalJson, FString& OutContentHash) const
{
OutCanonicalJson = VoxelForgeCoreWorldAssemblyJson;
OutContentHash = VoxelForgeCoreWorldAssemblyHash;
return bUseVoxelForgeCore && !OutCanonicalJson.IsEmpty() && !OutContentHash.IsEmpty();
}
bool AVoxelWorld::QueryVoxelForgeCoreDensityAt(FVector WorldLocation, float& OutDensity) const
{
OutDensity = 0.0f;
@@ -7230,13 +7246,15 @@ bool AVoxelWorld::QueryVoxelForgeCoreDensityAt(FVector WorldLocation, float& Out
return FMath::IsFinite(OutDensity);
}
bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
bool AVoxelWorld::InitializeVoxelForgeCoreWorld(FString& OutFailureReason)
{
OutFailureReason.Reset();
VoxelForgeCoreWorldAssemblyJson.Reset();
VoxelForgeCoreWorldAssemblyHash.Reset();
if (!bUseVoxelForgeCore) return true;
if (!Settings || !StrateManager)
{
UE_LOG(LogTemp, Error,
TEXT("[VoxelForgeCoreRuntime] initialization failed: Settings or resolved StrateManager is missing."));
OutFailureReason = TEXT("Settings or the resolved strate layout is missing.");
return false;
}
@@ -7247,7 +7265,8 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
if (!VoxelForgeCoreAdapter::SerializeResolvedWorld(
Settings, StrateManager, ActiveCoreClipRadius, ActiveCoreMaxClipLevel, Json, ExportError))
{
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] snapshot export failed: %s"), *ExportError);
OutFailureReason = FString::Printf(TEXT("snapshot export failed: %s"), *ExportError);
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] %s"), *OutFailureReason);
return false;
}
FTCHARToUTF8 Utf8Json(*Json);
@@ -7257,12 +7276,22 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
std::string_view(Utf8Json.Get(), static_cast<std::size_t>(Utf8Json.Length())), CoreError);
if (!Snapshot)
{
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] schema-v1 resolve failed: %s"),
OutFailureReason = FString::Printf(TEXT("schema-v1 snapshot resolve failed: %s"),
UTF8_TO_TCHAR(CoreError.c_str()));
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] %s"), *OutFailureReason);
return false;
}
if (const VoxelForgeCore::WorldAssemblyManifest* Assembly = Snapshot->worldAssembly())
{
const std::string CanonicalAssembly = Assembly->ToJson();
if (CanonicalAssembly.empty())
{
OutFailureReason = TEXT("the generated world assembly could not be serialized canonically.");
return false;
}
VoxelForgeCoreWorldAssemblyJson = UTF8_TO_TCHAR(CanonicalAssembly.c_str());
VoxelForgeCoreWorldAssemblyHash = FString::Printf(TEXT("%016llx"),
static_cast<unsigned long long>(Assembly->contentHash));
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCoreRuntime] world_assembly_hash=%016llx recipe_hash=%016llx plan_hash=%016llx surface_strates=%d island_strates=%d"),
static_cast<unsigned long long>(Assembly->contentHash),
@@ -7294,7 +7323,7 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
{CameraVoxels.X, CameraVoxels.Y, CameraVoxels.Z});
if (InitialDesired.empty())
{
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] Core returned an empty initial clipmap."));
OutFailureReason = TEXT("Core produced an empty initial clipmap.");
return false;
}
@@ -7327,7 +7356,7 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
}
if (FineMin.x == IntMax || ClipMin.x == IntMax)
{
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] Core initial clipmap bounds are invalid."));
OutFailureReason = TEXT("Core produced invalid initial clipmap bounds.");
return false;
}
@@ -7373,6 +7402,8 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
}
if (!bCompiled)
{
State->InitialFailureReason = FString::Printf(
TEXT("Core shape compilation failed: %s"), UTF8_TO_TCHAR(Error.c_str()));
State->bInitialFailed.store(true, std::memory_order_release);
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreRuntime] shape compile failed: %s"),
UTF8_TO_TCHAR(Error.c_str()));
@@ -7394,6 +7425,49 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
return true;
}
void AVoxelWorld::FallBackToLegacyAfterCoreInitializationFailure(const FString& FailureReason)
{
if (!bUseVoxelForgeCore) return;
const FString Reason = FailureReason.IsEmpty()
? TEXT("Core initialization failed without a diagnostic reason.") : FailureReason;
if (VoxelForgeCoreState.IsValid())
{
VoxelForgeCoreState->bShuttingDown.store(true, std::memory_order_release);
VoxelForgeCoreState.Reset();
}
VoxelForgeCoreWorldAssemblyJson.Reset();
VoxelForgeCoreWorldAssemblyHash.Reset();
bUseVoxelForgeCore = false;
CoreFallbackCenterLocalCm = FVector::ZeroVector;
if (Mesher != nullptr)
{
Mesher->SetGeometryVoxelSizeCm(VOXEL_SIZE);
}
if (ContentManager != nullptr && Settings != nullptr)
{
ContentManager->Initialize(this, StrateManager, Generator, Settings,
Settings->GetEffectiveWorldSeed());
ContentManager->ClearAll();
}
if (AtmosphereManager != nullptr)
{
AtmosphereManager->Reset();
}
RegenerateAllChunks();
UE_LOG(LogTemp, Error,
TEXT("[VoxelForgeCoreRuntime] Core initialization failed; falling back to the legacy generator. reason=%s"),
*Reason);
if (GEngine != nullptr)
{
const FString ScreenMessage = FString::Printf(
TEXT("VoxelForge Core could not start: %s Legacy voxel generation is active."), *Reason);
GEngine->AddOnScreenDebugMessage(INDEX_NONE, 20.0f, FColor::Red, ScreenMessage);
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCoreRuntime] fallback_notice=on_screen seconds=20 reason=%s"), *Reason);
}
}
bool AVoxelWorld::TryGetPlayerPosition(FVector& OutPosition, APawn** OutPawn) const
{
OutPosition = FVector::ZeroVector;
@@ -13293,12 +13367,43 @@ FString AVoxelWorld::GetCurrentSeasonContentHash() const
return SeasonHash;
}
FString AssemblyJson;
FString AssemblyHash;
FString AssemblyError;
if (!Settings->GetWorldAssemblyManifestJson(AssemblyJson, AssemblyHash, AssemblyError))
FString AssemblyHash = VoxelForgeCoreWorldAssemblyHash;
if (AssemblyHash.IsEmpty())
{
return FString();
FString AssemblyJson;
FString AssemblyError;
if (!Settings->GetWorldAssemblyManifestJson(AssemblyJson, AssemblyHash, AssemblyError))
{
return FString();
}
if (AssemblyHash.IsEmpty() && StrateManager != nullptr)
{
// Multiplayer/content-hash callers can ask before BeginPlay has cached the startup
// artifact. Resolve the same deterministic plan from the already-resolved settings
// and layout instead of temporarily returning an identity without that plan.
FString SnapshotJson;
if (!VoxelForgeCoreAdapter::SerializeResolvedWorld(Settings, StrateManager,
FMath::Clamp(CoreClipRadius, 1, 64),
FMath::Clamp(CoreMaxClipLevel, 0, 8), SnapshotJson, AssemblyError))
{
return FString();
}
FTCHARToUTF8 Utf8Snapshot(*SnapshotJson);
std::string CoreError;
const std::optional<VoxelForgeCore::ResolvedWorldSnapshot> Snapshot =
VoxelForgeCore::ResolvedWorldSnapshot::FromJson(
std::string_view(Utf8Snapshot.Get(),
static_cast<std::size_t>(Utf8Snapshot.Length())), CoreError);
if (!Snapshot)
{
return FString();
}
if (const VoxelForgeCore::WorldAssemblyManifest* Assembly = Snapshot->worldAssembly())
{
AssemblyHash = FString::Printf(TEXT("%016llx"),
static_cast<unsigned long long>(Assembly->contentHash));
}
}
}
const bool bHybridMesher = CoreMesherRecipe != EVoxelForgeCoreMesherRecipe::MarchingCubesV1;
if (AssemblyHash.IsEmpty() && !bHybridMesher)