wip(assembler): M1 round 5 -- native SurfaceWorld manifest delivered into the Unreal Core route

Manifest imported into UVoxelSettings (hash 8933693d9560ba9f), folded into the season
content hash only when Core is on. BuildHost: W1/W8/W16 stable, 2553 surface tiles match
standalone, 432 LOD transitions, collision on all 36 expected tiles. Matched timing
96.8 s native vs 197.3 s bridge (2.04x). Collision support tiles wait for Core region admission.

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-09 15:31:48 +02:00
1 parent 2d0cec90e4
commit f67b519e75
11 files changed
+1002 -45

No files matched your search

@@ -2,9 +2,12 @@
#include "Dom/JsonObject.h"
#include "Policies/CondensedJsonPrintPolicy.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
#include "Serialization/JsonWriter.h"
#include "UObject/UnrealType.h"
#include "UObject/TextProperty.h"
#include <VoxelForgeCore/VoxelForgeCore.h>
#include "VoxelSeasonAsset.h"
#include "VoxelSettings.h"
#include "VoxelStrateDefinition.h"
@@ -271,6 +274,95 @@ void WriteCoreJsonValue(FCoreJsonWriter& Writer, const TCHAR* Identifier,
Writer.WriteNull();
}
}
bool AttachValidatedWorldAssembly(const UVoxelSettings* Settings,
const UVoxelStrateManager* StrateManager,
const TSharedRef<FJsonObject>& Root,
FString& OutError)
{
FString ManifestJson;
FString ManifestHash;
if (!Settings->GetWorldAssemblyManifestJson(ManifestJson, ManifestHash, OutError))
{
return false;
}
if (ManifestJson.IsEmpty())
{
return true;
}
FTCHARToUTF8 Utf8Json(*ManifestJson);
std::string CoreError;
const std::optional<VoxelForgeCore::WorldAssemblyManifest> Manifest =
VoxelForgeCore::WorldAssemblyManifest::FromJson(
std::string_view(Utf8Json.Get(), static_cast<std::size_t>(Utf8Json.Length())), CoreError);
if (!Manifest)
{
OutError = FString::Printf(TEXT("Core world assembly validation failed: %s"),
UTF8_TO_TCHAR(CoreError.c_str()));
return false;
}
if (Manifest->recipe.seed != static_cast<uint32>(StrateManager->GetWorldSeed()))
{
OutError = TEXT("Core world assembly recipe seed does not match the resolved strate layout.");
return false;
}
int32 SurfaceSlotCount = 0;
for (const FStrateSlot& Slot : StrateManager->GetLayout())
{
if (Slot.Definition != nullptr
&& Slot.Definition->GeneratorType == ECaveGeneratorType::SurfaceWorld)
{
++SurfaceSlotCount;
}
}
if (SurfaceSlotCount == 0
|| Manifest->plan.strates.size() != static_cast<std::size_t>(SurfaceSlotCount))
{
OutError = FString::Printf(
TEXT("Core world assembly has %d plans for %d resolved SurfaceWorld strates."),
static_cast<int32>(Manifest->plan.strates.size()), SurfaceSlotCount);
return false;
}
TSet<int32> MatchedStrateIndices;
for (const VoxelForgeCore::WorldAssemblyStratumPlanV1& Plan : Manifest->plan.strates)
{
const FStrateSlot* MatchingSlot = StrateManager->GetLayout().FindByPredicate(
[&Plan](const FStrateSlot& Slot) { return Slot.StrateIndex == Plan.strateIndex; });
if (MatchingSlot == nullptr || MatchingSlot->Definition == nullptr
|| MatchingSlot->Definition->GeneratorType != ECaveGeneratorType::SurfaceWorld
|| MatchingSlot->TopChunkZ != Plan.topChunkZ
|| MatchingSlot->BottomChunkZ != Plan.bottomChunkZ
|| MatchedStrateIndices.Contains(Plan.strateIndex))
{
OutError = FString::Printf(
TEXT("Core world assembly plan for strate %d does not exactly match a unique resolved SurfaceWorld slot."),
Plan.strateIndex);
return false;
}
MatchedStrateIndices.Add(Plan.strateIndex);
}
const std::string CanonicalJson = Manifest->ToJson();
if (CanonicalJson.empty())
{
OutError = TEXT("Core world assembly manifest could not be canonicalized for the snapshot.");
return false;
}
const FString CanonicalText = UTF8_TO_TCHAR(CanonicalJson.c_str());
TSharedPtr<FJsonObject> ManifestObject;
const TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(CanonicalText);
if (!FJsonSerializer::Deserialize(Reader, ManifestObject) || !ManifestObject.IsValid())
{
OutError = TEXT("Core world assembly canonical JSON could not be embedded in the snapshot.");
return false;
}
Root->SetObjectField(TEXT("world_assembly"), ManifestObject.ToSharedRef());
Root->SetStringField(TEXT("world_assembly_content_hash"), ManifestHash);
return true;
}
}
bool VoxelForgeCoreAdapter::SerializeResolvedWorld(
@@ -380,6 +472,10 @@ bool VoxelForgeCoreAdapter::SerializeResolvedWorld(
Passages.Add(MakeShared<FJsonValueObject>(CorePassageJson(Passage)));
}
Root->SetArrayField(TEXT("passages"), MoveTemp(Passages));
if (!AttachValidatedWorldAssembly(Settings, StrateManager, Root, OutError))
{
return false;
}
Root->SetArrayField(TEXT("density_samples"), {});
TSharedRef<FJsonObject> ViewerStart = MakeShared<FJsonObject>();
ViewerStart->SetBoolField(TEXT("found_open_space"), false);
+127
View File
@@ -2,6 +2,13 @@
#include "VoxelSeasonAsset.h"
#if WITH_EDITOR
#include "Misc/FileHelper.h"
#endif
#include <VoxelForgeCore/VoxelForgeCore.h>
#include <string>
int32 UVoxelSettings::GetEffectiveWorldSeed() const
{
if (!Season.IsNull())
@@ -49,3 +56,123 @@ int32 UVoxelSettings::GetEffectiveSeasonNumber() const
}
return CurrentSeason;
}
bool UVoxelSettings::SetWorldAssemblyManifestJson(const FString& InJson, FString& OutReport)
{
OutReport.Reset();
if (InJson.IsEmpty())
{
OutReport = TEXT("world assembly manifest JSON is empty");
return false;
}
FTCHARToUTF8 Utf8Json(*InJson);
std::string CoreError;
const std::optional<VoxelForgeCore::WorldAssemblyManifest> Manifest =
VoxelForgeCore::WorldAssemblyManifest::FromJson(
std::string_view(Utf8Json.Get(), static_cast<std::size_t>(Utf8Json.Length())), CoreError);
if (!Manifest)
{
OutReport = FString::Printf(TEXT("world assembly manifest is invalid: %s"),
UTF8_TO_TCHAR(CoreError.c_str()));
return false;
}
if (Manifest->recipe.seed != static_cast<uint32>(GetEffectiveWorldSeed()))
{
OutReport = FString::Printf(
TEXT("world assembly seed %u does not match the effective settings seed %d"),
Manifest->recipe.seed, GetEffectiveWorldSeed());
return false;
}
const std::string CanonicalJson = Manifest->ToJson();
if (CanonicalJson.empty())
{
OutReport = TEXT("world assembly manifest could not be canonicalized");
return false;
}
Modify();
WorldAssemblyManifestJson = UTF8_TO_TCHAR(CanonicalJson.c_str());
WorldAssemblyManifestHash = FString::Printf(TEXT("%016llx"),
static_cast<unsigned long long>(Manifest->contentHash));
MarkPackageDirty();
OutReport = FString::Printf(TEXT("imported native world assembly %s (%d stratum plans)"),
*WorldAssemblyManifestHash, static_cast<int32>(Manifest->plan.strates.size()));
return true;
}
bool UVoxelSettings::GetWorldAssemblyManifestJson(
FString& OutCanonicalJson, FString& OutContentHash, FString& OutError) const
{
OutCanonicalJson.Reset();
OutContentHash.Reset();
OutError.Reset();
if (WorldAssemblyManifestJson.IsEmpty())
{
if (!WorldAssemblyManifestHash.IsEmpty())
{
OutError = TEXT("world assembly hash is set but its manifest JSON is empty");
return false;
}
return true;
}
FTCHARToUTF8 Utf8Json(*WorldAssemblyManifestJson);
std::string CoreError;
const std::optional<VoxelForgeCore::WorldAssemblyManifest> Manifest =
VoxelForgeCore::WorldAssemblyManifest::FromJson(
std::string_view(Utf8Json.Get(), static_cast<std::size_t>(Utf8Json.Length())), CoreError);
if (!Manifest)
{
OutError = FString::Printf(TEXT("cooked world assembly manifest is invalid: %s"),
UTF8_TO_TCHAR(CoreError.c_str()));
return false;
}
if (Manifest->recipe.seed != static_cast<uint32>(GetEffectiveWorldSeed()))
{
OutError = TEXT("cooked world assembly seed no longer matches the effective settings seed");
return false;
}
const FString ComputedHash = FString::Printf(TEXT("%016llx"),
static_cast<unsigned long long>(Manifest->contentHash));
if (!ComputedHash.Equals(WorldAssemblyManifestHash, ESearchCase::IgnoreCase))
{
OutError = FString::Printf(
TEXT("cooked world assembly hash mismatch (stored %s, computed %s)"),
*WorldAssemblyManifestHash, *ComputedHash);
return false;
}
const std::string CanonicalJson = Manifest->ToJson();
if (CanonicalJson.empty())
{
OutError = TEXT("cooked world assembly manifest could not be canonicalized");
return false;
}
OutCanonicalJson = UTF8_TO_TCHAR(CanonicalJson.c_str());
OutContentHash = ComputedHash;
return true;
}
void UVoxelSettings::ImportWorldAssemblyManifestJson()
{
#if WITH_EDITOR
FString JsonText;
FString Report;
if (SourceWorldAssemblyManifestJson.FilePath.IsEmpty()
|| !FFileHelper::LoadFileToString(JsonText, *SourceWorldAssemblyManifestJson.FilePath))
{
UE_LOG(LogTemp, Error, TEXT("[VoxelSettings] Could not read world assembly manifest '%s'."),
*SourceWorldAssemblyManifestJson.FilePath);
return;
}
if (!SetWorldAssemblyManifestJson(JsonText, Report))
{
UE_LOG(LogTemp, Error, TEXT("[VoxelSettings] World assembly import failed: %s"), *Report);
return;
}
UE_LOG(LogTemp, Log, TEXT("[VoxelSettings] %s"), *Report);
#else
UE_LOG(LogTemp, Error, TEXT("[VoxelSettings] World assembly JSON import is editor-only."));
#endif
}
+63 -1
View File
@@ -40,6 +40,7 @@
#include "Engine/World.h"
#include "Misc/IQueuedWork.h"
#include "Misc/QueuedThreadPool.h"
#include "Misc/SecureHash.h"
#include "RealtimeMeshCollisionLibrary.h"
#include "Camera/PlayerCameraManager.h"
#include "Async/Async.h"
@@ -7259,6 +7260,15 @@ bool AVoxelWorld::InitializeVoxelForgeCoreWorld()
UTF8_TO_TCHAR(CoreError.c_str()));
return false;
}
if (const VoxelForgeCore::WorldAssemblyManifest* Assembly = Snapshot->worldAssembly())
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCoreRuntime] world_assembly_hash=%016llx recipe_hash=%016llx plan_hash=%016llx strates=%d"),
static_cast<unsigned long long>(Assembly->contentHash),
static_cast<unsigned long long>(Assembly->recipe.contentHash),
static_cast<unsigned long long>(Assembly->plan.contentHash),
static_cast<int32>(Assembly->plan.strates.size()));
}
TSharedPtr<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe> State =
MakeShared<FVoxelForgeCoreRuntimeState, ESPMode::ThreadSafe>();
@@ -7786,6 +7796,18 @@ void AVoxelWorld::RequestCollisionGateSupportTile(const FVoxelTileKey& SupportTi
}
bForceDesiredRebuild |= bNeedsDesiredRebuild;
// Core surface tiles must enter through the desired-set region compile before collision work.
// Les tuiles de surface Core doivent passer par la compilation de région avant la collision.
if (bUseVoxelForgeCore && !bDesiredAtRequest)
{
UE_LOG(LogTemp, Display,
TEXT("[VoxelForgeCollisionGate] support_request_deferred tile=(%d,%d,%d) "
"reason=core_region_admission desired=0"),
SupportTile.Coord.X, SupportTile.Coord.Y, SupportTile.Coord.Z);
bPawnGateSupportRequestReported = true;
return;
}
// Preserve the established critical floor ordering when it already belongs to this desired
// epoch. Before the first build there is no array to promote into; LoadTile below still uses
// the same CollisionCritical work class and priority queue.
@@ -13222,7 +13244,47 @@ int32 AVoxelWorld::GetCurrentSeason() const
FString AVoxelWorld::GetCurrentSeasonContentHash() const
{
return StrateManager ? StrateManager->GetSeasonContentHash() : FString();
const FString SeasonHash = StrateManager ? StrateManager->GetSeasonContentHash() : FString();
// Only active Core generation includes the optional native surface manifest in world identity.
// Seul le Core actif inclut le manifeste de surface natif dans l'identite du monde.
if (!Settings || !bUseVoxelForgeCore)
{
return SeasonHash;
}
FString AssemblyJson;
FString AssemblyHash;
FString AssemblyError;
if (!Settings->GetWorldAssemblyManifestJson(AssemblyJson, AssemblyHash, AssemblyError))
{
return FString();
}
if (AssemblyHash.IsEmpty())
{
return SeasonHash;
}
FString Payload = SeasonHash;
if (Payload.IsEmpty())
{
Payload = FString::Printf(TEXT("seed=%d"), StrateManager ? StrateManager->GetWorldSeed()
: Settings->GetEffectiveWorldSeed());
if (StrateManager)
{
for (const FStrateSlot& Slot : StrateManager->GetLayout())
{
Payload += FString::Printf(TEXT("|strate=%d,%d,%d,%s"), Slot.StrateIndex,
Slot.TopChunkZ, Slot.BottomChunkZ,
Slot.Definition ? *Slot.Definition->GetPathName() : TEXT("missing"));
}
}
}
Payload += TEXT("|world_assembly=");
Payload += AssemblyHash;
FTCHARToUTF8 Utf8Payload(*Payload);
uint8 Digest[FSHA1::DigestSize];
FSHA1::HashBuffer(Utf8Payload.Get(), Utf8Payload.Length(), Digest);
return BytesToHex(Digest, FSHA1::DigestSize);
}
//=============================================================================