chore(unreal): U2 -- owner's four-generator plan staged; owner_world.json re-exported
Owner re-saved strates 2026-10-26: DA_Strate1 SurfaceWorld, 2 FloatingIslands, 3 TunnelNetwork, 4 VerticalShafts (default params). BuildHost copies and the enforced SHA-256 tables updated (old U1 set kept in the report); the export commandlet handles the four-strate plan; Standalone/data/owner_world.json regenerated (SHA-256 F42F96AD...). One normal BuildHost gate passed (64/64 tiles) but its top-strate window produced 0 triangles, and a missing NewDataAsset reference warns -- both to check. No measurements this round. Report: Standalone/Research/WorldAssemblerM3/Unreal2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fc9227f9e0
commit
9136f5f0e7
7 files changed
+314
-95
No files matched your search
@@ -878,6 +878,8 @@ struct FExploreArguments
|
||||
bool bPassageFloorWritesOverride = false;
|
||||
int32 PassageFloorWrites = 1;
|
||||
bool bStandaloneWorldExport = false;
|
||||
bool bOwnerFourStratePlan = false;
|
||||
bool bSkipStandaloneCoreAdapterParity = false;
|
||||
bool bCoreWorldTest = false;
|
||||
bool bCoreWorldStreamTraceOnly = false;
|
||||
bool bRenderCameraOverride = false;
|
||||
@@ -922,6 +924,12 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
int32 StandaloneWorldExport = 0;
|
||||
FParse::Value(*Params, TEXT("standaloneworldexport="), StandaloneWorldExport);
|
||||
OutArguments.bStandaloneWorldExport = StandaloneWorldExport != 0;
|
||||
int32 OwnerFourStratePlan = 0;
|
||||
FParse::Value(*Params, TEXT("ownerfourstrateplan="), OwnerFourStratePlan);
|
||||
OutArguments.bOwnerFourStratePlan = OwnerFourStratePlan != 0;
|
||||
int32 SkipStandaloneCoreAdapterParity = 0;
|
||||
FParse::Value(*Params, TEXT("skipstandalonecoreadapterparity="), SkipStandaloneCoreAdapterParity);
|
||||
OutArguments.bSkipStandaloneCoreAdapterParity = SkipStandaloneCoreAdapterParity != 0;
|
||||
int32 CoreWorldTest = 0;
|
||||
FParse::Value(*Params, TEXT("coreworldtest="), CoreWorldTest);
|
||||
OutArguments.bCoreWorldTest = CoreWorldTest != 0;
|
||||
@@ -1266,12 +1274,11 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
return false;
|
||||
}
|
||||
|
||||
// The historical explorer contract needs two adjacent mouths around an interior slot. A
|
||||
// parity export can instead target the game's authored single-strate layout, which has no
|
||||
// such mouths and is valid for export-only mode. Build() still validates the slot against
|
||||
// the loaded settings layout.
|
||||
const int32 MinimumSlot = OutArguments.bGameRuntimeConfig ? 0 : 1;
|
||||
const int32 MaximumSlot = OutArguments.bGameRuntimeConfig
|
||||
// Passage walks need both mouths; a render/export-only generation can inspect an end slot.
|
||||
// Parity exports also accept the game's authored single-strate layout.
|
||||
const bool bRequiresInteriorSlot = OutArguments.bWalk;
|
||||
const int32 MinimumSlot = OutArguments.bGameRuntimeConfig || !bRequiresInteriorSlot ? 0 : 1;
|
||||
const int32 MaximumSlot = OutArguments.bGameRuntimeConfig || !bRequiresInteriorSlot
|
||||
? MaxSyntheticStrates - 1 : MaxSyntheticStrates - 2;
|
||||
if (OutArguments.Slot < MinimumSlot || OutArguments.Slot > MaximumSlot)
|
||||
{
|
||||
@@ -1284,8 +1291,8 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
else
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("slot must be an interior slot in [1,%d] so arrival and departure mouths exist."),
|
||||
MaxSyntheticStrates - 2);
|
||||
TEXT("slot must be in [0,%d]; walk mode requires an interior slot in [1,%d] for both passage mouths."),
|
||||
MaxSyntheticStrates - 1, MaxSyntheticStrates - 2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -1498,6 +1505,17 @@ bool ParseArguments(const FString& Params, FExploreArguments& OutArguments, FStr
|
||||
OutError = TEXT("standaloneworldexport requires modes=export and settingsref, and must omit strateref.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bOwnerFourStratePlan
|
||||
&& (OutArguments.SettingsReference.IsEmpty() || !OutArguments.StrateReference.IsEmpty()))
|
||||
{
|
||||
OutError = TEXT("ownerfourstrateplan requires settingsref and must omit strateref.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bSkipStandaloneCoreAdapterParity && !OutArguments.bStandaloneWorldExport)
|
||||
{
|
||||
OutError = TEXT("skipstandalonecoreadapterparity requires standaloneworldexport.");
|
||||
return false;
|
||||
}
|
||||
if (OutArguments.bCoreWorldTest
|
||||
&& (OutArguments.SettingsReference.IsEmpty() || !OutArguments.StrateReference.IsEmpty()))
|
||||
{
|
||||
@@ -1702,6 +1720,66 @@ struct FExploreMeshBvhNode
|
||||
}
|
||||
};
|
||||
|
||||
bool ApplyOwnerFourStratePlan(UVoxelSettings* Settings,
|
||||
TArray<TStrongObjectPtr<UVoxelStrateDefinition>>& OutDefinitions,
|
||||
bool bDuplicateDefinitions, FString& OutError)
|
||||
{
|
||||
if (Settings == nullptr)
|
||||
{
|
||||
OutError = TEXT("The transient owner plan has no settings object.");
|
||||
return false;
|
||||
}
|
||||
if (!Settings->Season.IsNull())
|
||||
{
|
||||
OutError = TEXT("The transient four-strate owner plan cannot replace an assigned season manifest.");
|
||||
return false;
|
||||
}
|
||||
|
||||
static const TCHAR* AssetPaths[] = {
|
||||
TEXT("/Game/VoxelForge/DA_Strate1.DA_Strate1"),
|
||||
TEXT("/Game/VoxelForge/DA_Strate2.DA_Strate2"),
|
||||
TEXT("/Game/VoxelForge/DA_Strate3.DA_Strate3"),
|
||||
TEXT("/Game/VoxelForge/DA_Strate4.DA_Strate4")
|
||||
};
|
||||
Settings->TotalStrates = UE_ARRAY_COUNT(AssetPaths);
|
||||
Settings->StratePool.Reset();
|
||||
Settings->FixedStrates.Reset();
|
||||
OutDefinitions.Reset();
|
||||
OutDefinitions.Reserve(UE_ARRAY_COUNT(AssetPaths));
|
||||
|
||||
for (int32 Index = 0; Index < UE_ARRAY_COUNT(AssetPaths); ++Index)
|
||||
{
|
||||
UVoxelStrateDefinition* AuthoredDefinition = LoadObject<UVoxelStrateDefinition>(
|
||||
nullptr, AssetPaths[Index]);
|
||||
if (AuthoredDefinition == nullptr)
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not load staged owner strate asset '%s'."), AssetPaths[Index]);
|
||||
return false;
|
||||
}
|
||||
UVoxelStrateDefinition* Definition = AuthoredDefinition;
|
||||
if (bDuplicateDefinitions)
|
||||
{
|
||||
Definition = DuplicateObject<UVoxelStrateDefinition>(
|
||||
AuthoredDefinition, GetTransientPackage(),
|
||||
FName(*FString::Printf(TEXT("VoxelForgeOwnerPlan_Strate_%d"), Index)));
|
||||
if (Definition == nullptr)
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not make a transient copy of owner strate asset '%s'."), AssetPaths[Index]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Settings->FixedStrates.Add(Index,
|
||||
TSoftObjectPtr<UVoxelStrateDefinition>(Definition));
|
||||
OutDefinitions.Add(TStrongObjectPtr<UVoxelStrateDefinition>(Definition));
|
||||
}
|
||||
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeOwnerPlan] transient fixed slots=4 assets=DA_Strate1,DA_Strate2,DA_Strate3,DA_Strate4 settings_asset_untouched=1"));
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FExploreWorld
|
||||
{
|
||||
TStrongObjectPtr<UVoxelSettings> Settings;
|
||||
@@ -1834,7 +1912,7 @@ struct FExploreWorld
|
||||
Settings->InterStrateGapChunks, Settings->SkirtCells);
|
||||
}
|
||||
|
||||
const int32 NumStrates = bUseAuthoredSettings
|
||||
const int32 NumStrates = Arguments.bOwnerFourStratePlan ? 4 : bUseAuthoredSettings
|
||||
? (Arguments.bGameRuntimeConfig
|
||||
? FMath::Max(Settings->TotalStrates, 1)
|
||||
: FMath::Max(Settings->TotalStrates, Arguments.Slot + 2))
|
||||
@@ -1864,64 +1942,73 @@ struct FExploreWorld
|
||||
// loaded package remains untouched.
|
||||
Settings->TotalStrates = NumStrates;
|
||||
|
||||
if (bUseAuthoredStrate)
|
||||
if (Arguments.bOwnerFourStratePlan)
|
||||
{
|
||||
// The owner’s snapshot states that every slot uses DA_Strate3. Clone it per slot so
|
||||
// commandlet overrides never mutate a loaded package or leak between slots.
|
||||
Settings->StratePool.Reset();
|
||||
Settings->FixedStrates.Reset();
|
||||
}
|
||||
Definitions.Reserve(NumStrates);
|
||||
for (int32 Index = 0; Index < NumStrates; ++Index)
|
||||
{
|
||||
UVoxelStrateDefinition* Definition = bUseAuthoredStrate
|
||||
? DuplicateObject<UVoxelStrateDefinition>(
|
||||
AuthoredDefinition, GetTransientPackage())
|
||||
: NewObject<UVoxelStrateDefinition>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
if (Definition == nullptr)
|
||||
if (!ApplyOwnerFourStratePlan(Settings.Get(), Definitions, true, OutError))
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not create transient strate definition for slot %d."), Index);
|
||||
return false;
|
||||
}
|
||||
if (!bUseAuthoredStrate)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bUseAuthoredStrate)
|
||||
{
|
||||
Definition->StrateName = FText::FromString(FString::Printf(
|
||||
TEXT("VoxelForgeExplore_%s_slot%d"),
|
||||
ArchetypeName(Index == Arguments.Slot
|
||||
// Keep the historical single-asset interior probe isolated from loaded packages.
|
||||
Settings->StratePool.Reset();
|
||||
Settings->FixedStrates.Reset();
|
||||
}
|
||||
Definitions.Reserve(NumStrates);
|
||||
for (int32 Index = 0; Index < NumStrates; ++Index)
|
||||
{
|
||||
UVoxelStrateDefinition* Definition = bUseAuthoredStrate
|
||||
? DuplicateObject<UVoxelStrateDefinition>(
|
||||
AuthoredDefinition, GetTransientPackage())
|
||||
: NewObject<UVoxelStrateDefinition>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
if (Definition == nullptr)
|
||||
{
|
||||
OutError = FString::Printf(
|
||||
TEXT("Could not create transient strate definition for slot %d."), Index);
|
||||
return false;
|
||||
}
|
||||
if (!bUseAuthoredStrate)
|
||||
{
|
||||
Definition->StrateName = FText::FromString(FString::Printf(
|
||||
TEXT("VoxelForgeExplore_%s_slot%d"),
|
||||
ArchetypeName(Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork),
|
||||
Index));
|
||||
Definition->StrateDescription = FText::FromString(
|
||||
TEXT("Transient, deterministic commandlet definition."));
|
||||
// The commandlet's acceptance sweep uses the owner-facing base spacing:
|
||||
// 8 chunks * 32 voxels * 0.125 m = 32 m per strate. Production assets may be taller;
|
||||
// this keeps the measured tunnel arithmetic tied to the stated ~32 m descent.
|
||||
Definition->StrateHeightInChunks = 8;
|
||||
Definition->TransitionType = EVoxelStrateTransition::Hard;
|
||||
Definition->GeneratorType = Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork),
|
||||
Index));
|
||||
Definition->StrateDescription = FText::FromString(
|
||||
TEXT("Transient, deterministic commandlet definition."));
|
||||
// The commandlet's acceptance sweep uses the owner-facing base spacing:
|
||||
// 8 chunks * 32 voxels * 0.125 m = 32 m per strate. Production assets may be taller;
|
||||
// this keeps the measured tunnel arithmetic tied to the stated ~32 m descent.
|
||||
Definition->StrateHeightInChunks = 8;
|
||||
Definition->TransitionType = EVoxelStrateTransition::Hard;
|
||||
Definition->GeneratorType = Index == Arguments.Slot
|
||||
? Arguments.Archetype
|
||||
: ECaveGeneratorType::TunnelNetwork;
|
||||
if (Arguments.bWormsEnabledOverride)
|
||||
{
|
||||
Definition->bEnableWorms = Arguments.bWormsEnabled;
|
||||
: ECaveGeneratorType::TunnelNetwork;
|
||||
if (Arguments.bWormsEnabledOverride)
|
||||
{
|
||||
Definition->bEnableWorms = Arguments.bWormsEnabled;
|
||||
}
|
||||
if (Arguments.bWormStrengthOverride
|
||||
&& (Definition->GeneratorType == ECaveGeneratorType::TunnelNetwork
|
||||
|| Definition->GeneratorType == ECaveGeneratorType::Underwater))
|
||||
{
|
||||
Definition->GenerationParams.WormStrength = Arguments.WormStrength;
|
||||
}
|
||||
}
|
||||
if (Arguments.bWormStrengthOverride
|
||||
&& (Definition->GeneratorType == ECaveGeneratorType::TunnelNetwork
|
||||
|| Definition->GeneratorType == ECaveGeneratorType::Underwater))
|
||||
{
|
||||
Definition->GenerationParams.WormStrength = Arguments.WormStrength;
|
||||
}
|
||||
}
|
||||
|
||||
const TSoftObjectPtr<UVoxelStrateDefinition> SoftDefinition(Definition);
|
||||
Settings->FixedStrates.Add(Index, SoftDefinition);
|
||||
if (!bUseAuthoredStrate)
|
||||
{
|
||||
Settings->StratePool.Add(SoftDefinition);
|
||||
const TSoftObjectPtr<UVoxelStrateDefinition> SoftDefinition(Definition);
|
||||
Settings->FixedStrates.Add(Index, SoftDefinition);
|
||||
if (!bUseAuthoredStrate)
|
||||
{
|
||||
Settings->StratePool.Add(SoftDefinition);
|
||||
}
|
||||
Definitions.Add(TStrongObjectPtr<UVoxelStrateDefinition>(Definition));
|
||||
}
|
||||
Definitions.Add(TStrongObjectPtr<UVoxelStrateDefinition>(Definition));
|
||||
}
|
||||
|
||||
auto ApplyTargetOverrides = [&Arguments](UVoxelStrateDefinition* Definition) -> bool
|
||||
@@ -13515,6 +13602,24 @@ bool RunStandaloneWorldExport(const FExploreArguments& Arguments, FString& OutEr
|
||||
*Arguments.SettingsReference);
|
||||
return false;
|
||||
}
|
||||
TStrongObjectPtr<UVoxelSettings> OwnerPlanSettings;
|
||||
TArray<TStrongObjectPtr<UVoxelStrateDefinition>> OwnerPlanDefinitions;
|
||||
UVoxelSettings* ExportSettings = AuthoredSettings;
|
||||
if (Arguments.bOwnerFourStratePlan)
|
||||
{
|
||||
OwnerPlanSettings = TStrongObjectPtr<UVoxelSettings>(
|
||||
DuplicateObject<UVoxelSettings>(AuthoredSettings, GetTransientPackage()));
|
||||
if (!OwnerPlanSettings.IsValid())
|
||||
{
|
||||
OutError = TEXT("Could not make a transient copy of owner settings for the four-strate plan.");
|
||||
return false;
|
||||
}
|
||||
ExportSettings = OwnerPlanSettings.Get();
|
||||
if (!ApplyOwnerFourStratePlan(ExportSettings, OwnerPlanDefinitions, false, OutError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
UVoxelStrateManager* Manager = NewObject<UVoxelStrateManager>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
int32 Seed = AuthoredSettings->Seed;
|
||||
@@ -13526,7 +13631,7 @@ bool RunStandaloneWorldExport(const FExploreArguments& Arguments, FString& OutEr
|
||||
OriginSpineRadius = Season->GetOriginSpineRadius();
|
||||
WorldRadiusVoxels = Season->GetWorldRadiusVoxels();
|
||||
}
|
||||
if (!Manager->Initialize(AuthoredSettings, Seed))
|
||||
if (!Manager->Initialize(ExportSettings, Seed))
|
||||
{
|
||||
OutError = TEXT("Owner settings did not produce a valid resolved strate layout.");
|
||||
return false;
|
||||
@@ -13534,7 +13639,7 @@ bool RunStandaloneWorldExport(const FExploreArguments& Arguments, FString& OutEr
|
||||
Seed = Manager->GetWorldSeed();
|
||||
UVoxelGenerator* Generator = NewObject<UVoxelGenerator>(
|
||||
GetTransientPackage(), NAME_None, RF_Transient);
|
||||
Generator->InitializeSettings(AuthoredSettings);
|
||||
Generator->InitializeSettings(ExportSettings);
|
||||
Generator->SetStrateManager(Manager);
|
||||
|
||||
TArray<FStandaloneExportSample> Samples;
|
||||
@@ -13668,25 +13773,25 @@ bool RunStandaloneWorldExport(const FExploreArguments& Arguments, FString& OutEr
|
||||
Writer->WriteValue(TEXT("source"), TEXT("VoxelForgeExplore standalone base-world export"));
|
||||
Writer->WriteValue(TEXT("voxel_size_cm"), StandaloneCoreVoxelSizeCm);
|
||||
Writer->WriteValue(TEXT("seed"), Seed);
|
||||
Writer->WriteValue(TEXT("open_surface_entry"), AuthoredSettings->bOpenSurfaceEntry);
|
||||
Writer->WriteValue(TEXT("open_surface_entry"), ExportSettings->bOpenSurfaceEntry);
|
||||
Writer->WriteValue(TEXT("origin_spine_radius"), OriginSpineRadius);
|
||||
Writer->WriteValue(TEXT("world_radius_voxels"), WorldRadiusVoxels);
|
||||
Writer->WriteValue(TEXT("edge_seal_thickness"), AuthoredSettings->EdgeSealThickness);
|
||||
Writer->WriteValue(TEXT("edge_seal_thickness"), ExportSettings->EdgeSealThickness);
|
||||
Writer->WriteValue(TEXT("total_strates"), Manager->GetNumStrates());
|
||||
Writer->WriteValue(TEXT("inter_strate_gap_chunks"), AuthoredSettings->InterStrateGapChunks);
|
||||
Writer->WriteValue(TEXT("clip_radius"), AuthoredSettings->ClipRadius);
|
||||
Writer->WriteValue(TEXT("max_clip_level"), AuthoredSettings->MaxClipLevel);
|
||||
Writer->WriteValue(TEXT("full_res_clip_levels"), AuthoredSettings->FullResClipLevels);
|
||||
Writer->WriteValue(TEXT("coarse_tile_cells"), AuthoredSettings->CoarseTileCells);
|
||||
Writer->WriteValue(TEXT("render_distance_chunks"), AuthoredSettings->RenderDistanceChunks);
|
||||
Writer->WriteValue(TEXT("generate_skirts"), AuthoredSettings->bGenerateSkirts);
|
||||
Writer->WriteValue(TEXT("skirt_cells"), AuthoredSettings->SkirtCells);
|
||||
Writer->WriteValue(TEXT("lod_octave_drop"), AuthoredSettings->LODOctaveDrop);
|
||||
Writer->WriteValue(TEXT("strate_content_cut_min_level"), AuthoredSettings->GetEffectiveStrateContentCutMinLevel());
|
||||
Writer->WriteValue(TEXT("inter_strate_gap_chunks"), ExportSettings->InterStrateGapChunks);
|
||||
Writer->WriteValue(TEXT("clip_radius"), ExportSettings->ClipRadius);
|
||||
Writer->WriteValue(TEXT("max_clip_level"), ExportSettings->MaxClipLevel);
|
||||
Writer->WriteValue(TEXT("full_res_clip_levels"), ExportSettings->FullResClipLevels);
|
||||
Writer->WriteValue(TEXT("coarse_tile_cells"), ExportSettings->CoarseTileCells);
|
||||
Writer->WriteValue(TEXT("render_distance_chunks"), ExportSettings->RenderDistanceChunks);
|
||||
Writer->WriteValue(TEXT("generate_skirts"), ExportSettings->bGenerateSkirts);
|
||||
Writer->WriteValue(TEXT("skirt_cells"), ExportSettings->SkirtCells);
|
||||
Writer->WriteValue(TEXT("lod_octave_drop"), ExportSettings->LODOctaveDrop);
|
||||
Writer->WriteValue(TEXT("strate_content_cut_min_level"), ExportSettings->GetEffectiveStrateContentCutMinLevel());
|
||||
TSet<const UObject*> ActiveObjects;
|
||||
StandaloneWriteJsonValue(*Writer, TEXT("settings_asset"),
|
||||
StandaloneSerializeStruct(AuthoredSettings->GetClass(), AuthoredSettings,
|
||||
ActiveObjects, AuthoredSettings));
|
||||
StandaloneSerializeStruct(ExportSettings->GetClass(), ExportSettings,
|
||||
ActiveObjects, ExportSettings));
|
||||
|
||||
Writer->WriteArrayStart(TEXT("strates"));
|
||||
for (const FStrateSlot& Slot : Manager->GetLayout())
|
||||
@@ -13764,11 +13869,16 @@ bool RunStandaloneWorldExport(const FExploreArguments& Arguments, FString& OutEr
|
||||
return false;
|
||||
}
|
||||
if (OutJson) *OutJson = Json;
|
||||
if (!VerifyStandaloneCoreAdapter(Json, OutError))
|
||||
if (!Arguments.bSkipStandaloneCoreAdapterParity && !VerifyStandaloneCoreAdapter(Json, OutError))
|
||||
{
|
||||
UE_LOG(LogTemp, Error, TEXT("[VoxelForgeCoreAdapter] %s"), *OutError);
|
||||
return false;
|
||||
}
|
||||
if (Arguments.bSkipStandaloneCoreAdapterParity)
|
||||
{
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeCoreAdapter] skipped legacy W1/W8/W16 fixture parity for this export-only update."));
|
||||
}
|
||||
UE_LOG(LogTemp, Display,
|
||||
TEXT("[VoxelForgeStandaloneExport] seed=%d voxel_size_cm=%.3f strates=%d passages=%d samples=%d camera_found=%d file=%s"),
|
||||
Seed, StandaloneCoreVoxelSizeCm, Manager->GetNumStrates(), Manager->GetPassages().Num(),
|
||||
|
||||
Reference in new issue
Block a user