Planar far horizon samples the LOD8 interior at the MC cell spacing, keeping 128-voxel boundary strips; far recipe v3. LOD8 p95 59.8 -> 13.9 ms, spawn worker-seconds ~41 -> 26.9, full set 7.8 -> 7.1 s, ring 0.49 s, LOD0 1.43 s, 40 m/s ring 75/75. S3/S4 2 cm agreement, 100% cap coverage, W1/W8/W12 and W1/W8/W16 determinism pass. Report: Standalone/Research/WorldAssemblerM3/Round14. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MmSoF64BbRFmk9C3p1VsBJ
708 lines
37 KiB
PowerShell
708 lines
37 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[ValidateRange(60, 900)][int]$TimeoutSeconds = 900,
|
|
[ValidateRange(12.5, 25.0)][double]$CoreVoxelSizeCm = 25.0,
|
|
[ValidateSet('CoarseFirst', 'Legacy')][string]$StreamingOrder = 'Legacy',
|
|
[ValidateSet('Auto', 'Legacy', 'Dynamic')][string]$NearReserveMode = 'Auto',
|
|
[switch]$EnforceCollisionGate,
|
|
[switch]$StartupTrace,
|
|
[ValidateRange(0, 1)][int]$StartupApplyBoost = 0,
|
|
[switch]$StreamingTraceOnly,
|
|
[ValidateRange(1, 6000)][int]$TestMoveSpeedCmPerSecond = 600,
|
|
[ValidateRange(0, 1)][int]$StreamingCollisionLookahead = 1,
|
|
[string]$TestMoveDirection = '0,1,0',
|
|
[ValidateRange(0, 1)][int]$CoreRegionConcurrentAdmission = 1,
|
|
[ValidateRange(0, 4096)][int]$StreamingResultBacklogCap = 256,
|
|
[ValidateRange(0, 900)][int]$Step1MaxMeshApplies = 0,
|
|
[ValidateRange(0, 900)][int]$TestTeleportAfterSeconds = 0,
|
|
[ValidateRange(0, 1000000)][int]$TestTeleportDistanceCm = 0,
|
|
[string]$WorldAssemblyManifestPath = '',
|
|
[ValidateSet('MarchingCubesV1', 'WorldAssemblerHybridV1', 'WorldAssemblerHybridV2')][string]$CoreMesherRecipe = 'MarchingCubesV1',
|
|
[switch]$EnableHybridFixturePlacement,
|
|
[switch]$HybridCliffOnly,
|
|
[switch]$HybridFixturesOnly,
|
|
[switch]$FloatingIslandsOnly,
|
|
[switch]$CoreFallbackTest,
|
|
[switch]$MismatchedManifestFallback,
|
|
[switch]$CoreOffTest,
|
|
[switch]$CorePlanOnly,
|
|
[switch]$SharedFeatureAuditOnly,
|
|
[int]$CoreTestSeed = [int]::MinValue
|
|
)
|
|
|
|
# Exercise AVoxelWorld Core streaming, collision, anchor movement, and carving in BuildHost only.
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$PluginRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
|
|
$HostRoot = Join-Path $PluginRoot 'Saved\BuildHost\HostProject'
|
|
$StagePlugin = Join-Path $HostRoot 'Plugins\VoxelForge'
|
|
$HostProject = Join-Path $HostRoot 'HostProject.uproject'
|
|
$OutputRoot = Join-Path $StagePlugin 'Saved\CoreWorldValidation'
|
|
$OutputDirectory = Join-Path $OutputRoot 'Output'
|
|
$InputDirectory = Join-Path $OutputRoot 'Input'
|
|
$StagedWorldAssembly = Join-Path $InputDirectory 'world_assembly.json'
|
|
$LogDirectory = Join-Path $OutputRoot 'Logs'
|
|
$LocalAppData = Join-Path $OutputRoot 'LocalAppData'
|
|
$UserDirectory = Join-Path $OutputRoot 'User'
|
|
$DerivedDataCache = Join-Path $PluginRoot 'Saved\BuildHost\VFCoreWorldDDC'
|
|
$ZenData = Join-Path $OutputRoot 'ZenData'
|
|
$EditorCmd = 'E:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
|
|
$MemoryLimitBytes = 8GB
|
|
$CrashRoots = @((Join-Path $HostRoot 'Saved\Crashes'), (Join-Path $StagePlugin 'Saved\Crashes'),
|
|
(Join-Path $UserDirectory 'Saved\Crashes'))
|
|
$CoreSizeSuffix = ''
|
|
if ([Math]::Abs($CoreVoxelSizeCm - 25.0) -gt 0.000001) {
|
|
$CoreSizeText = $CoreVoxelSizeCm.ToString('0.####', [Globalization.CultureInfo]::InvariantCulture)
|
|
$CoreSizeSuffix = "_voxel_$CoreSizeText"
|
|
}
|
|
$RunName = if ($FloatingIslandsOnly) { "floating_islands$CoreSizeSuffix" }
|
|
elseif ($CoreFallbackTest -or $MismatchedManifestFallback) { "mismatched_fallback$CoreSizeSuffix" }
|
|
elseif ($CoreOffTest) { "core_off$CoreSizeSuffix" }
|
|
elseif ($CorePlanOnly) { if ($CoreTestSeed -ne [int]::MinValue) { "plan_seed_$CoreTestSeed$CoreSizeSuffix" } else { "plan_only$CoreSizeSuffix" } }
|
|
else { "owner_surface$CoreSizeSuffix" }
|
|
$RunName += $(if ($StreamingTraceOnly) { '_stream_trace_only' } else { '' })
|
|
$RunName += "_applyboost_$StartupApplyBoost"
|
|
|
|
if ($MismatchedManifestFallback) { $CoreFallbackTest = $true }
|
|
$SelectedModeFlags = @(
|
|
[bool]$FloatingIslandsOnly, [bool]$CoreFallbackTest,
|
|
[bool]$CoreOffTest, [bool]$CorePlanOnly, [bool]$StreamingTraceOnly
|
|
)
|
|
$SelectedModeCount = @($SelectedModeFlags | Where-Object { $_ }).Count
|
|
if ($SelectedModeCount -gt 1) { throw 'Select one Core test mode per BuildHost launch.' }
|
|
if ($CorePlanOnly -and $CoreTestSeed -eq [int]::MinValue) {
|
|
throw '-CorePlanOnly requires a specific CoreTestSeed.'
|
|
}
|
|
if ($CoreTestSeed -ne [int]::MinValue -and -not $CorePlanOnly) {
|
|
throw '-CoreTestSeed is valid only with -CorePlanOnly.'
|
|
}
|
|
if ($StreamingTraceOnly -and ($SharedFeatureAuditOnly -or $EnableHybridFixturePlacement -or
|
|
$HybridCliffOnly -or $HybridFixturesOnly -or $WorldAssemblyManifestPath)) {
|
|
throw '-StreamingTraceOnly uses the staged owner plan without additional fixture or manifest overrides.'
|
|
}
|
|
if ($TestMoveDirection -notmatch '^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+),[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+),[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)$') {
|
|
throw '-TestMoveDirection must be three comma-separated invariant-culture numbers without spaces.'
|
|
}
|
|
$MoveDirectionComponents = @($TestMoveDirection.Split(',') | ForEach-Object {
|
|
[double]::Parse($_, [Globalization.CultureInfo]::InvariantCulture)
|
|
})
|
|
if ($MoveDirectionComponents[0] -eq 0 -and $MoveDirectionComponents[1] -eq 0 -and
|
|
$MoveDirectionComponents[2] -eq 0) {
|
|
throw '-TestMoveDirection must not be the zero vector.'
|
|
}
|
|
if (-not $StreamingTraceOnly -and ($TestMoveSpeedCmPerSecond -ne 600 -or
|
|
$StreamingCollisionLookahead -ne 1 -or $TestMoveDirection -ne '0,1,0' -or
|
|
$CoreRegionConcurrentAdmission -ne 1 -or $StreamingResultBacklogCap -ne 256 -or
|
|
$Step1MaxMeshApplies -ne 0 -or $TestTeleportAfterSeconds -ne 0 -or
|
|
$TestTeleportDistanceCm -ne 0)) {
|
|
throw 'Streaming, backlog, apply and teleport controls require -StreamingTraceOnly.'
|
|
}
|
|
if (($TestTeleportAfterSeconds -eq 0) -ne ($TestTeleportDistanceCm -eq 0)) {
|
|
throw 'Set both -TestTeleportAfterSeconds and -TestTeleportDistanceCm for the teleport probe.'
|
|
}
|
|
|
|
function Assert-UnderPath([string]$Candidate, [string]$Root) {
|
|
$candidateFull = [IO.Path]::GetFullPath($Candidate).TrimEnd('\', '/')
|
|
$rootFull = [IO.Path]::GetFullPath($Root).TrimEnd('\', '/')
|
|
if (-not ($candidateFull.Equals($rootFull, [StringComparison]::OrdinalIgnoreCase) -or
|
|
$candidateFull.StartsWith($rootFull + '\', [StringComparison]::OrdinalIgnoreCase))) {
|
|
throw "Refusing a BuildHost path outside its root: $candidateFull"
|
|
}
|
|
}
|
|
|
|
if (-not ('VoxelForgeRound4MemoryProbe' -as [type])) {
|
|
Add-Type -TypeDefinition @'
|
|
using System;
|
|
using System.Runtime.InteropServices;
|
|
public static class VoxelForgeRound4MemoryProbe {
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
private struct MEMORYSTATUSEX {
|
|
public uint dwLength;
|
|
public uint dwMemoryLoad;
|
|
public ulong ullTotalPhys;
|
|
public ulong ullAvailPhys;
|
|
public ulong ullTotalPageFile;
|
|
public ulong ullAvailPageFile;
|
|
public ulong ullTotalVirtual;
|
|
public ulong ullAvailVirtual;
|
|
public ulong ullAvailExtendedVirtual;
|
|
}
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
|
|
public static ulong AvailablePhysicalBytes() {
|
|
var status = new MEMORYSTATUSEX();
|
|
status.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
|
|
if (!GlobalMemoryStatusEx(ref status)) throw new InvalidOperationException("GlobalMemoryStatusEx failed.");
|
|
return status.ullAvailPhys;
|
|
}
|
|
}
|
|
'@
|
|
}
|
|
|
|
function Wait-ForLaunchMemory {
|
|
while ([VoxelForgeRound4MemoryProbe]::AvailablePhysicalBytes() -lt 4GB) {
|
|
Write-Warning 'BuildHost launch is waiting for at least 4 GiB of free physical memory.'
|
|
Start-Sleep -Seconds 20
|
|
}
|
|
}
|
|
|
|
Assert-UnderPath $HostRoot (Join-Path $PluginRoot 'Saved\BuildHost')
|
|
Assert-UnderPath $OutputRoot (Join-Path $StagePlugin 'Saved')
|
|
if ([string]::IsNullOrWhiteSpace($WorldAssemblyManifestPath) -and
|
|
($CoreFallbackTest -or $MismatchedManifestFallback)) {
|
|
$WorldAssemblyManifestPath = Join-Path $PluginRoot `
|
|
'Standalone\Research\WorldAssemblerM3\Round3\results\world_assembly_floating_islands.json'
|
|
}
|
|
$HasManifestOverride = -not [string]::IsNullOrWhiteSpace($WorldAssemblyManifestPath)
|
|
if ($HasManifestOverride) {
|
|
$WorldAssemblyManifestPath = [IO.Path]::GetFullPath($WorldAssemblyManifestPath)
|
|
Assert-UnderPath $WorldAssemblyManifestPath $PluginRoot
|
|
}
|
|
foreach ($required in @($HostProject, $EditorCmd)) {
|
|
if (-not (Test-Path -LiteralPath $required -PathType Leaf)) {
|
|
throw "Required BuildHost commandlet input is missing: $required"
|
|
}
|
|
}
|
|
if ($HasManifestOverride -and -not (Test-Path -LiteralPath $WorldAssemblyManifestPath -PathType Leaf)) {
|
|
throw "Reviewed world assembly manifest is missing: $WorldAssemblyManifestPath"
|
|
}
|
|
|
|
if ($MismatchedManifestFallback) { $RunName = 'mismatched_fallback' }
|
|
if ($SharedFeatureAuditOnly) {
|
|
if ($SelectedModeCount -ne 0) { throw '-SharedFeatureAuditOnly uses the SurfaceWorld owner route.' }
|
|
$RunName = 'shared_feature_audit'
|
|
}
|
|
$RunName += if ($StreamingOrder -eq 'Legacy') { '_legacy_order' } else { '_coarse_first' }
|
|
$RunName += switch ($NearReserveMode) {
|
|
'Legacy' { '_nearreserve_legacy' }
|
|
'Dynamic' { '_nearreserve_dynamic' }
|
|
default { '_nearreserve_auto' }
|
|
}
|
|
if ($HybridFixturesOnly) { $RunName += "_$($CoreMesherRecipe.ToLowerInvariant())" }
|
|
if ($EnforceCollisionGate) { $RunName += '_collision_gate' }
|
|
if ($StartupTrace -or $StreamingTraceOnly) { $RunName += '_trace' }
|
|
if ($StreamingTraceOnly) {
|
|
$DirectionTag = $TestMoveDirection.Replace(',', '_').Replace('-', 'm').Replace('+', 'p').Replace('.', 'p')
|
|
$RunName += "_move_${TestMoveSpeedCmPerSecond}_lookahead_${StreamingCollisionLookahead}_dir_${DirectionTag}_region_${CoreRegionConcurrentAdmission}_backlog_${StreamingResultBacklogCap}"
|
|
if ($Step1MaxMeshApplies -gt 0) { $RunName += "_applymax_$Step1MaxMeshApplies" }
|
|
if ($TestTeleportAfterSeconds -gt 0) {
|
|
$RunName += "_teleport_${TestTeleportAfterSeconds}s_${TestTeleportDistanceCm}cm"
|
|
}
|
|
}
|
|
|
|
$foreignEditors = @(Get-Process -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.ProcessName -like 'UnrealEditor*' })
|
|
if ($foreignEditors.Count -gt 0) {
|
|
$ids = ($foreignEditors | ForEach-Object { "$($_.ProcessName):$($_.Id)" }) -join ', '
|
|
throw "An UnrealEditor process is already running; leaving it untouched: $ids"
|
|
}
|
|
|
|
$ExpectedAssetHashes = @{
|
|
'DA_Settings' = '8ABE8243104980260FECA0A6F4CF4927768E8E568221A6F6A2C2694F6D15D454'
|
|
'DA_Strate1' = '76063D691D5CE26D2014B587E1972A6724787BE1204E4652CA863BB8D5001BD0'
|
|
'DA_Strate2' = '02F56A18CED991BAF93DFB2F69438152275E8353B5E279C1CB7DD6E079E352E2'
|
|
'DA_Strate3' = 'F47437F8D156C608AA06E40C5B76716ED4A19FA0F057688E7FECC04DC976428C'
|
|
'DA_Strate4' = '7C0DE7D06876035AC7BC4F117B9166AFD77402021843BA4900B470F4B0D0185B'
|
|
}
|
|
foreach ($assetName in $ExpectedAssetHashes.Keys) {
|
|
$stagedAsset = Join-Path $HostRoot "Content\VoxelForge\$assetName.uasset"
|
|
if (-not (Test-Path -LiteralPath $stagedAsset -PathType Leaf)) {
|
|
throw "Verified BuildHost asset copy is missing: $assetName"
|
|
}
|
|
if ((Get-FileHash -LiteralPath $stagedAsset -Algorithm SHA256).Hash -ne $ExpectedAssetHashes[$assetName]) {
|
|
throw "BuildHost owner asset copy differs from the recorded round-4 hash: $assetName"
|
|
}
|
|
}
|
|
|
|
foreach ($directory in @($OutputDirectory, $InputDirectory, $LogDirectory, $LocalAppData,
|
|
$UserDirectory, $DerivedDataCache, $ZenData)) {
|
|
[IO.Directory]::CreateDirectory($directory) | Out-Null
|
|
}
|
|
if ($HasManifestOverride) {
|
|
Copy-Item -LiteralPath $WorldAssemblyManifestPath -Destination $StagedWorldAssembly -Force
|
|
$ManifestData = Get-Content -LiteralPath $StagedWorldAssembly -Raw | ConvertFrom-Json
|
|
} else {
|
|
$ManifestData = $null
|
|
}
|
|
if ($MismatchedManifestFallback -and ($null -eq $ManifestData -or $ManifestData.content_hash -ne '94b563f1ce622def' -or @($ManifestData.floating_islands).Count -ne 1 -or @($ManifestData.plan.strates).Count -ne 0)) {
|
|
throw "Fallback regression input is not the old round-3 island manifest: $($ManifestData.content_hash)"
|
|
}
|
|
if ($EnableHybridFixturePlacement -and $CoreMesherRecipe -eq 'MarchingCubesV1') {
|
|
throw 'Hybrid fixture placement requires a WorldAssemblerHybrid recipe.'
|
|
}
|
|
if ($HybridCliffOnly -and $CoreMesherRecipe -eq 'MarchingCubesV1') {
|
|
throw '-HybridCliffOnly requires a WorldAssemblerHybrid recipe.'
|
|
}
|
|
if ($HybridCliffOnly -and $EnableHybridFixturePlacement) {
|
|
throw '-HybridCliffOnly uses the reviewed native cliff manifest and cannot add the surface fixtures.'
|
|
}
|
|
if ($HybridFixturesOnly -and $CoreMesherRecipe -eq 'MarchingCubesV1') {
|
|
throw '-HybridFixturesOnly requires a WorldAssemblerHybrid recipe.'
|
|
}
|
|
if ($HybridFixturesOnly -and ($HybridCliffOnly -or $EnableHybridFixturePlacement)) {
|
|
throw '-HybridFixturesOnly cannot be combined with the other focused hybrid modes.'
|
|
}
|
|
if (($FloatingIslandsOnly -or $CoreFallbackTest -or $CoreOffTest -or $CorePlanOnly) -and ($CoreMesherRecipe -ne 'MarchingCubesV1' -or $EnableHybridFixturePlacement -or $HybridCliffOnly -or $HybridFixturesOnly)) {
|
|
throw 'Core world integration modes use Marching Cubes and cannot be combined with hybrid modes.'
|
|
}
|
|
|
|
$CrashBefore = @{}
|
|
foreach ($CrashRoot in $CrashRoots) {
|
|
if (Test-Path -LiteralPath $CrashRoot -PathType Container) {
|
|
foreach ($Crash in @(Get-ChildItem -LiteralPath $CrashRoot -Directory -Filter 'UECC-*' -Recurse -Force)) {
|
|
$CrashBefore[$Crash.FullName.ToLowerInvariant()] = $Crash.LastWriteTimeUtc.Ticks
|
|
}
|
|
}
|
|
}
|
|
|
|
function Get-NewCrashFolders {
|
|
$Found = [Collections.Generic.List[string]]::new()
|
|
foreach ($CrashRoot in $CrashRoots) {
|
|
if (Test-Path -LiteralPath $CrashRoot -PathType Container) {
|
|
foreach ($Crash in @(Get-ChildItem -LiteralPath $CrashRoot -Directory -Filter 'UECC-*' -Recurse -Force)) {
|
|
if (-not $CrashBefore.ContainsKey($Crash.FullName.ToLowerInvariant())) {
|
|
[void]$Found.Add($Crash.FullName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return @($Found.ToArray())
|
|
}
|
|
|
|
$HybridMesherFlag = switch ($CoreMesherRecipe) {
|
|
'WorldAssemblerHybridV1' { 1; break }
|
|
'WorldAssemblerHybridV2' { 2; break }
|
|
default { 0 }
|
|
}
|
|
$HybridFixturePlacementFlag = if ($EnableHybridFixturePlacement) { 1 } else { 0 }
|
|
$HybridCliffOnlyFlag = if ($HybridCliffOnly) { 1 } else { 0 }
|
|
$HybridFixturesOnlyFlag = if ($HybridFixturesOnly) { 1 } else { 0 }
|
|
$FloatingIslandsOnlyFlag = if ($FloatingIslandsOnly) { 1 } else { 0 }
|
|
$CoreFallbackTestFlag = if ($CoreFallbackTest) { 1 } else { 0 }
|
|
$CoreOffTestFlag = if ($CoreOffTest) { 1 } else { 0 }
|
|
$CorePlanOnlyFlag = if ($CorePlanOnly) { 1 } else { 0 }
|
|
$BaseArguments = @(
|
|
('-project="{0}"' -f $HostProject),
|
|
'-nullrhi', '-unattended', '-nop4', '-nosplash', '-NoSound', '-corelimit=12',
|
|
'-stdout', '-FullStdOutLogOutput', '-forcelogflush', '-NoZenAutoLaunch',
|
|
'-notraceserver', '-nocrashreports', '-ddc=NoZenLocalFallback',
|
|
('-userdir="{0}"' -f $UserDirectory),
|
|
('-ZenDataPath="{0}"' -f $ZenData),
|
|
('-LocalDataCachePath="{0}"' -f $DerivedDataCache),
|
|
'-run=VoxelForgeExplore', '-seed=0', '-archetype=TunnelNetwork', '-slot=0',
|
|
'-modes=export', '-gameconfig=1', '-coreworldtest=1',
|
|
('-corevoxelsizecm={0}' -f $CoreVoxelSizeCm.ToString('0.####', [Globalization.CultureInfo]::InvariantCulture)),
|
|
("-coreislandtest=$FloatingIslandsOnlyFlag"),
|
|
("-corefallbacktest=$CoreFallbackTestFlag"),
|
|
("-coreofftest=$CoreOffTestFlag"),
|
|
("-coreplantest=$CorePlanOnlyFlag"),
|
|
'-settingsref=/Game/VoxelForge/DA_Settings.DA_Settings',
|
|
("-corehybridmesher=$HybridMesherFlag"),
|
|
("-corehybridfixtureplacement=$HybridFixturePlacementFlag"),
|
|
("-corehybridcliffonly=$HybridCliffOnlyFlag"),
|
|
("-corehybridfixturesonly=$HybridFixturesOnlyFlag"),
|
|
('-out="{0}"' -f $OutputDirectory)
|
|
)
|
|
$CoarseFirstValue = if ($StreamingOrder -eq 'CoarseFirst') { 1 } else { 0 }
|
|
$DynamicReserveValue = switch ($NearReserveMode) {
|
|
'Legacy' { 0 }
|
|
'Dynamic' { 1 }
|
|
default { 1 }
|
|
}
|
|
$LegacyPendingResultCapValue = if ($NearReserveMode -eq 'Legacy') { 1 } else { 0 }
|
|
$BaseArguments += "-voxel.StreamingCoarseFirst=$CoarseFirstValue"
|
|
$BaseArguments += "-voxel.StreamingDynamicNearReserve=$DynamicReserveValue"
|
|
$BaseArguments += "-voxel.StreamingLegacyPendingResultCap=$LegacyPendingResultCapValue"
|
|
$BaseArguments += "-voxel.StreamingStartupApplyBoost=$StartupApplyBoost"
|
|
$BaseArguments += "-voxel.CoreRegionConcurrentAdmission=$CoreRegionConcurrentAdmission"
|
|
$BaseArguments += "-voxel.StreamingResultBacklogCap=$StreamingResultBacklogCap"
|
|
if ($Step1MaxMeshApplies -gt 0) {
|
|
$BaseArguments += "-voxel.Step1MaxMeshApplies=$Step1MaxMeshApplies"
|
|
}
|
|
if ($StreamingTraceOnly) {
|
|
$BaseArguments += '-coreworldstreamtrace=1'
|
|
$BaseArguments += '-voxel.TestMove=1'
|
|
$BaseArguments += "-voxel.TestMoveSpeedCmPerSecond=$TestMoveSpeedCmPerSecond"
|
|
$BaseArguments += '-voxel.TestMoveStartSeconds=30'
|
|
$BaseArguments += "-voxel.TestMoveDirection=$TestMoveDirection"
|
|
$BaseArguments += "-voxel.StreamingCollisionLookahead=$StreamingCollisionLookahead"
|
|
if ($TestTeleportAfterSeconds -gt 0) {
|
|
$BaseArguments += "-voxel.TestTeleportAfterSeconds=$TestTeleportAfterSeconds"
|
|
$BaseArguments += "-voxel.TestTeleportDistanceCm=$TestTeleportDistanceCm"
|
|
}
|
|
}
|
|
if ($StartupTrace -or $StreamingTraceOnly) {
|
|
$TracePath = Join-Path $LogDirectory "CoreWorldValidation-$RunName.trace.jsonl"
|
|
$BaseArguments += ('-voxel.StartupTraceFile="{0}"' -f $TracePath)
|
|
$BaseArguments += '-voxel.StartupTraceUntilEndPlay=1'
|
|
}
|
|
if ($SharedFeatureAuditOnly) { $BaseArguments += '-corefeatureauditonly' }
|
|
if ($HasManifestOverride) {
|
|
$BaseArguments += ('-coreworldassembly="{0}"' -f $StagedWorldAssembly)
|
|
}
|
|
if ($CoreTestSeed -ne [int]::MinValue) {
|
|
$BaseArguments += "-coretestseed=$CoreTestSeed"
|
|
}
|
|
if (-not $EnforceCollisionGate -and
|
|
($FloatingIslandsOnly -or (-not $CoreFallbackTest -and -not $CoreOffTest -and -not $CorePlanOnly))) {
|
|
# This commandlet keeps the travel pawn stationary; surface's initial collision cooks are still audited,
|
|
# and FloatingIslandsOnly separately probes cooked island/span capsule contacts.
|
|
# Le pawn de voyage du commandlet reste immobile ; les collisions de surface initiales sont auditees,
|
|
# et FloatingIslandsOnly sonde separement les contacts capsules ile/pont cuisines.
|
|
$BaseArguments += '-voxel.CollisionGate=0'
|
|
}
|
|
|
|
$OldLocalAppData = $env:LOCALAPPDATA
|
|
$env:LOCALAPPDATA = $LocalAppData
|
|
try {
|
|
$Succeeded = $false
|
|
for ($Attempt = 1; $Attempt -le 2 -and -not $Succeeded; ++$Attempt) {
|
|
if ($Attempt -eq 1) {
|
|
$LogFileName = "CoreWorldValidation-$RunName.log"
|
|
$StdoutFileName = "CoreWorldValidation-$RunName.stdout.log"
|
|
$StderrFileName = "CoreWorldValidation-$RunName.stderr.log"
|
|
} else {
|
|
$LogFileName = "CoreWorldValidation-$RunName.retry.log"
|
|
$StdoutFileName = "CoreWorldValidation-$RunName.retry.stdout.log"
|
|
$StderrFileName = "CoreWorldValidation-$RunName.retry.stderr.log"
|
|
}
|
|
$LogPath = Join-Path $LogDirectory $LogFileName
|
|
$StdoutPath = Join-Path $LogDirectory $StdoutFileName
|
|
$StderrPath = Join-Path $LogDirectory $StderrFileName
|
|
$Arguments = @($BaseArguments) + ('-abslog="{0}"' -f $LogPath)
|
|
Wait-ForLaunchMemory
|
|
$foreignEditors = @(Get-Process -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.ProcessName -like 'UnrealEditor*' })
|
|
if ($foreignEditors.Count -gt 0) {
|
|
$ids = ($foreignEditors | ForEach-Object { "$($_.ProcessName):$($_.Id)" }) -join ', '
|
|
throw "UnrealEditor is already running; leaving it untouched: $ids"
|
|
}
|
|
Write-Host "BuildHost Core actor commandlet attempt $Attempt of 2; Normal priority; run=$RunName."
|
|
$Process = Start-Process -FilePath $EditorCmd -ArgumentList $Arguments -WorkingDirectory $HostRoot `
|
|
-RedirectStandardOutput $StdoutPath -RedirectStandardError $StderrPath `
|
|
-WindowStyle Hidden -PassThru
|
|
$Process.PriorityClass = [Diagnostics.ProcessPriorityClass]::Normal
|
|
# W16 is a logical determinism partition; at most 12 logical CPUs execute host work.
|
|
# W16 reste un partitionnement logique; limiter l'execution hote a 12 processeurs logiques.
|
|
$Process.ProcessorAffinity = [IntPtr]0xFFF
|
|
$StartedUtc = [datetime]::UtcNow
|
|
$PeakPrivateBytes = [int64]0
|
|
while (-not $Process.HasExited) {
|
|
Start-Sleep -Seconds 2
|
|
$Process.Refresh()
|
|
if (-not $Process.HasExited) {
|
|
$PeakPrivateBytes = [Math]::Max($PeakPrivateBytes, [int64]$Process.PrivateMemorySize64)
|
|
}
|
|
if (([datetime]::UtcNow - $StartedUtc).TotalSeconds -gt $TimeoutSeconds) {
|
|
Stop-Process -Id $Process.Id -Force -ErrorAction SilentlyContinue
|
|
throw "BuildHost Core actor commandlet exceeded the $TimeoutSeconds-second time box."
|
|
}
|
|
if (-not $Process.HasExited -and $Process.PrivateMemorySize64 -gt $MemoryLimitBytes) {
|
|
Stop-Process -Id $Process.Id -Force -ErrorAction SilentlyContinue
|
|
throw 'BuildHost Core actor commandlet exceeded the 8 GiB private-memory limit.'
|
|
}
|
|
}
|
|
$Process.WaitForExit()
|
|
$Process.Refresh()
|
|
$ExitCode = $Process.ExitCode
|
|
if ([string]::IsNullOrEmpty([string]$ExitCode) -and (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
|
|
$CompletedLog = Get-Content -LiteralPath $LogPath -Raw
|
|
if (($CompletedLog -match '\[VoxelForgeCoreWorldTest\] pass complete=1' -or
|
|
$CompletedLog -match '\[VoxelForgeCoreIslandTest\] pass complete=1') -and
|
|
$CompletedLog -match 'Commandlet VoxelForgeExploreCommandlet_\d+ finished execution \(result 0\)') {
|
|
$ExitCode = 0
|
|
} elseif ($CompletedLog -match 'Commandlet VoxelForgeExploreCommandlet_\d+ finished execution \(result (-?\d+)\)') {
|
|
$ExitCode = [int]$Matches[1]
|
|
}
|
|
}
|
|
if ($CoreFallbackTest -and $ExitCode -eq 1 -and (Test-Path -LiteralPath $LogPath -PathType Leaf)) {
|
|
$CompletedLog = Get-Content -LiteralPath $LogPath -Raw
|
|
$ExpectedFallbackErrors = $CompletedLog -match '\[VoxelForgeCoreFallbackTest\] fallback=PASS core_disabled=1 legacy_generator=1' -and
|
|
$CompletedLog -match '\[VoxelForgeCoreRuntime\] fallback_notice=on_screen seconds=20' -and
|
|
$CompletedLog -match '\[VoxelForgeCoreWorldTest\] pass complete=1' -and
|
|
$CompletedLog -match 'Commandlet VoxelForgeExploreCommandlet_\d+ finished execution \(result 0\)'
|
|
if ($ExpectedFallbackErrors -and @(Get-NewCrashFolders).Count -eq 0) {
|
|
Write-Warning 'Accepting commandlet status 1 because the expected Core Error logs accompanied explicit fallback-pass/result-0 markers and no crash.'
|
|
$ExitCode = 0
|
|
}
|
|
}
|
|
if ($ExitCode -eq -1073741502 -and $Attempt -eq 1) {
|
|
Write-Warning 'BuildHost Core actor commandlet exited with 0xC0000142; retrying once.'
|
|
continue
|
|
}
|
|
if ($ExitCode -ne 0) {
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
$CrashDetail = if ($NewCrashes.Count -gt 0) { " New UECC folders: $($NewCrashes -join '; ')." } else { '' }
|
|
throw "BuildHost Core actor commandlet attempt $Attempt failed with exit code '$ExitCode'.$CrashDetail See $LogPath."
|
|
}
|
|
$Succeeded = $true
|
|
}
|
|
if (-not $Succeeded) { throw 'BuildHost Core actor commandlet did not complete successfully.' }
|
|
} finally {
|
|
if ($null -eq $OldLocalAppData) {
|
|
Remove-Item Env:LOCALAPPDATA -ErrorAction SilentlyContinue
|
|
} else {
|
|
$env:LOCALAPPDATA = $OldLocalAppData
|
|
}
|
|
}
|
|
|
|
$FinalLog = Join-Path $LogDirectory "CoreWorldValidation-$RunName.log"
|
|
if (-not (Test-Path -LiteralPath $FinalLog -PathType Leaf)) {
|
|
$FinalLog = Join-Path $LogDirectory "CoreWorldValidation-$RunName.retry.log"
|
|
}
|
|
$Text = Get-Content -LiteralPath $FinalLog -Raw
|
|
if ($SharedFeatureAuditOnly) {
|
|
if ($Text -notmatch '\[VoxelForgeSharedFeatureCost\] pass complete=1 warmed_routes=1') {
|
|
throw "Shared feature audit did not pass: $FinalLog"
|
|
}
|
|
if (@(Get-NewCrashFolders).Count -gt 0) { throw 'Shared feature audit produced a new crash folder.' }
|
|
Write-Host 'VOXELFORGE_SHARED_FEATURE_AUDIT_PASS warmed_routes=1'
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($StreamingTraceOnly) {
|
|
$ExpectedWalkDirection = "walk_direction=$TestMoveDirection"
|
|
if ($Text -notmatch '\[VoxelForgeCoreWorldStreamTrace\] initial_ready=1 .*walk_tick_ms_p95=.*walk_tick_ms_max=' -or
|
|
$Text -notmatch ("\[VoxelForgeCoreWorldStreamTrace\] pass complete=1 .*walk_speed_cm_s=$TestMoveSpeedCmPerSecond") -or
|
|
-not $Text.Contains($ExpectedWalkDirection) -or
|
|
$Text -notmatch '\[VoxelForgeCoreWorldTest\] pass complete=1') {
|
|
throw "BuildHost actor-only stream trace did not prove initial readiness and a completed walk sample. See $FinalLog."
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the stream trace: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_STREAM_TRACE_PASS boost=$StartupApplyBoost order=$StreamingOrder"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Startup trace: $TracePath"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($CoreFallbackTest) {
|
|
foreach ($Marker in @(
|
|
'[VoxelForgeCoreFallbackTest] fallback=PASS core_disabled=1 legacy_generator=1',
|
|
'[VoxelForgeCoreRuntime] fallback_notice=on_screen seconds=20',
|
|
'Core initialization failed; falling back to the legacy generator.'
|
|
)) {
|
|
if ($Text.IndexOf($Marker, [StringComparison]::Ordinal) -lt 0) {
|
|
throw "BuildHost fallback regression log is missing '$Marker'. See $FinalLog."
|
|
}
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the fallback regression: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host 'VOXELFORGE_CORE_MISMATCHED_MANIFEST_FALLBACK_PASS on_screen=1 legacy=1'
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($CoreOffTest) {
|
|
if ($Text -notmatch '\[VoxelForgeCoreOffTest\] legacy_unchanged=PASS core_disabled=1 voxel_cm=12\.5 density_samples=3' -or
|
|
$Text -notmatch '\[VoxelForgeCoreWorldTest\] pass complete=1') {
|
|
throw "BuildHost Core-off run did not prove unchanged owner legacy density and scale. See $FinalLog."
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the Core-off test: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host 'VOXELFORGE_CORE_OFF_LEGACY_PASS voxel_cm=12.5 density_samples=3'
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($CorePlanOnly) {
|
|
if ($Text -notmatch ('\[VoxelForgeCorePlanTest\] pass source=locked_owner_layout_seed_override seed=' +
|
|
[regex]::Escape([string]$CoreTestSeed) + ' .* deterministic_json=1')) {
|
|
throw "BuildHost did not generate a deterministic owner-layout plan for seed $CoreTestSeed. See $FinalLog."
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the nonzero-seed plan test: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_NONZERO_SEED_PLAN_PASS seed=$CoreTestSeed"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($FloatingIslandsOnly) {
|
|
foreach ($Marker in @(
|
|
'[VoxelForgeCoreIslandTest] plan_generated=PASS',
|
|
'[VoxelForgeCoreIslandTest] standalone_reference=PASS',
|
|
'[VoxelForgeCoreIslandTest] worker_hash_matrix=1/8/16 stable=1',
|
|
'[VoxelForgeCoreIslandTest] pass phase=initial',
|
|
'[VoxelForgeCoreIslandTest] height_anchor_agreement=PASS',
|
|
'[VoxelForgeCoreIslandTest] content_anchor=PASS',
|
|
'[VoxelForgeCoreIslandTest] blueprint_edit=PASS',
|
|
'[VoxelForgeCoreIslandTest] switch_off_legacy=PASS',
|
|
'[VoxelForgeCoreIslandTest] pass complete=1',
|
|
'[VoxelForgePawnSpeedFixture] source=staged_blueprints walk_cm_s=600.000 sprint_cm_s=6000.000'
|
|
)) {
|
|
if ($Text.IndexOf($Marker, [StringComparison]::Ordinal) -lt 0) {
|
|
throw "BuildHost floating-island log is missing '$Marker'. See $FinalLog."
|
|
}
|
|
}
|
|
foreach ($Surface in @('island_top', 'span')) {
|
|
if ($Text -notmatch ('\[VoxelForgeCoreIslandTest\] capsule_contact=PASS surface=' +
|
|
[regex]::Escape($Surface) + ' .*collision_ready=1')) {
|
|
throw "BuildHost island log is missing cooked capsule contact for '$Surface'. See $FinalLog."
|
|
}
|
|
}
|
|
foreach ($Lod in @(0, 1)) {
|
|
if ($Text -notmatch ('\[VoxelForgeIslandStreaming\] timing lod=' + $Lod + ' kind=.*air .*core_p95_ms=') -or
|
|
$Text -notmatch ('\[VoxelForgeIslandStreaming\] timing lod=' + $Lod + ' kind=.*(island|mixed|span) .*legacy_p95_ms=')) {
|
|
throw "BuildHost island log is missing matched air and surface timing at LOD$Lod. See $FinalLog."
|
|
}
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the island actor test: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_FLOATING_ISLANDS_PASS hashes=standalone water_fixed=1 lod=0,1 workers=1,8,16"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($HybridFixturesOnly) {
|
|
foreach ($Fixture in @('corner90', 'doorway', 'riser1m', 'cliff_ledge', 'organic_cave_chamber')) {
|
|
$FixtureMarker = '\[WorldAssemblerHybridPluginFixture\] name=' +
|
|
[regex]::Escape($Fixture) + ' lod=0 .*exact_repeat=1'
|
|
if ($Text -notmatch $FixtureMarker) {
|
|
throw "BuildHost fixture log is missing '$Fixture'. See $FinalLog."
|
|
}
|
|
}
|
|
if ($Text -notmatch '\[WorldAssemblerHybridPluginBorder\] same_lod=PASS independent=1' -or
|
|
$Text -notmatch '\[WorldAssemblerHybridPluginBorder\] lod0_lod1_doorway=1 cases=125 over_50cm_cases=0') {
|
|
throw "BuildHost fixture log is missing passing border probes. See $FinalLog."
|
|
}
|
|
if ($CoreMesherRecipe -eq 'WorldAssemblerHybridV2') {
|
|
foreach ($Fixture in @('corner90', 'doorway', 'riser1m', 'cliff_ledge', 'organic_cave_chamber')) {
|
|
$FixtureMarker = '\[WorldAssemblerHybridPluginFixtureV2\] name=' +
|
|
[regex]::Escape($Fixture) + ' lod=0 .*exact_repeat=1'
|
|
if ($Text -notmatch $FixtureMarker) {
|
|
throw "BuildHost V2 fixture log is missing '$Fixture'. See $FinalLog."
|
|
}
|
|
}
|
|
if ($Text -notmatch '\[WorldAssemblerHybridPluginBorderV2\] same_lod=PASS independent=1 mc_identity=1') {
|
|
throw "BuildHost fixture log is missing the V2 border identity proof. See $FinalLog."
|
|
}
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during plugin fixture checks: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_HYBRID_FIXTURES_PASS recipe=$CoreMesherRecipe analytic=5 borders=1"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
if ($HybridCliffOnly) {
|
|
if ($Text -notmatch '\[WorldAssemblerHybridNativeCliff\] key=\(-22,13,-58\) lod=0 .*bit_identical=1 type4_only=1') {
|
|
throw "BuildHost log is missing the sampled native SurfaceWorld cliff identity proof. See $FinalLog."
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during the native cliff check: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_HYBRID_CLIFF_PASS recipe=$CoreMesherRecipe bit_identical=1"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|
|
return
|
|
}
|
|
foreach ($Phase in @('initial', 'anchor', 'edit', 'surface_travel', 'surface_strate_travel')) {
|
|
if ($Text -notmatch "\[VoxelForgeCoreWorldTest\] pass phase=$Phase") {
|
|
throw "BuildHost Core actor log is missing the '$Phase' validation marker. See $FinalLog."
|
|
}
|
|
}
|
|
if ($Text -notmatch '\[VoxelForgeCoreWorldTest\] worker_hash_matrix=1/8/16 stable=1') {
|
|
throw "BuildHost Core actor log is missing the W1/W8/W16 determinism marker. See $FinalLog."
|
|
}
|
|
$ExpectedRuntimeAssemblyHash = ''
|
|
if ($EnableHybridFixturePlacement) {
|
|
$FixtureHashMatch = [regex]::Match($Text,
|
|
'\[VoxelForgeCoreWorldTest\] hybrid_fixture_placement=1 boxes=9 contacts=5 manifest_hash=([0-9a-f]{16})')
|
|
if (-not $FixtureHashMatch.Success) {
|
|
throw "BuildHost actor log is missing the generated hybrid fixture manifest hash. See $FinalLog."
|
|
}
|
|
$ExpectedRuntimeAssemblyHash = $FixtureHashMatch.Groups[1].Value
|
|
}
|
|
if ($EnableHybridFixturePlacement) {
|
|
$AssemblyMarker = '\[VoxelForgeCoreWorldTest\] world_assembly_source=settings_override world_assembly_hash=' +
|
|
[regex]::Escape($ExpectedRuntimeAssemblyHash) + ' surface_strates=1 island_strates=0 native=1'
|
|
} else {
|
|
$AssemblyMarker = '\[VoxelForgeCoreWorldTest\] world_assembly_source=generated_at_startup world_assembly_hash=[0-9a-f]{16} surface_strates=1 island_strates=0 native=1'
|
|
}
|
|
if ($Text -notmatch $AssemblyMarker) {
|
|
throw "BuildHost actor log does not prove a plan for the unchanged owner SurfaceWorld settings. See $FinalLog."
|
|
}
|
|
$RecipeMarker = '\[VoxelForgeCoreWorldTest\] world_initialized=1 recipe=' +
|
|
[regex]::Escape($CoreMesherRecipe)
|
|
if ($Text -notmatch $RecipeMarker) {
|
|
throw "BuildHost actor log does not confirm the requested $CoreMesherRecipe runtime selection. See $FinalLog."
|
|
}
|
|
if ($CoreMesherRecipe -in @('WorldAssemblerHybridV1', 'WorldAssemblerHybridV2')) {
|
|
foreach ($Fixture in @('corner90', 'doorway', 'riser1m', 'cliff_ledge', 'organic_cave_chamber')) {
|
|
$FixtureMarker = '\[WorldAssemblerHybridPluginFixture\] name=' +
|
|
[regex]::Escape($Fixture) + ' lod=0 .*exact_repeat=1'
|
|
if ($Text -notmatch $FixtureMarker) {
|
|
throw "BuildHost log is missing the production mesher fixture '$Fixture'. See $FinalLog."
|
|
}
|
|
if ($CoreMesherRecipe -eq 'WorldAssemblerHybridV2') {
|
|
$V2FixtureMarker = '\[WorldAssemblerHybridPluginFixtureV2\] name=' +
|
|
[regex]::Escape($Fixture) + ' lod=0 .*exact_repeat=1'
|
|
if ($Text -notmatch $V2FixtureMarker) {
|
|
throw "BuildHost actor log is missing the V2 production mesher fixture '$Fixture'. See $FinalLog."
|
|
}
|
|
}
|
|
}
|
|
if ($Text -notmatch '\[WorldAssemblerHybridPluginBorder\] same_lod=PASS independent=1') {
|
|
throw "BuildHost log is missing the independent same-LOD border proof. See $FinalLog."
|
|
}
|
|
if ($Text -notmatch '\[WorldAssemblerHybridPluginBorder\] lod0_lod1_doorway=1 cases=125 over_50cm_cases=0') {
|
|
throw "BuildHost log is missing a passing 125-phase MC doorway contour result. See $FinalLog."
|
|
}
|
|
}
|
|
if ($EnableHybridFixturePlacement -and $Text -notmatch '\[VoxelForgeHybridCollisionContact\] pass=1 probes=5') {
|
|
throw "BuildHost log is missing the cooked hybrid fixture trace contacts. See $FinalLog."
|
|
}
|
|
if ($CoreMesherRecipe -eq 'WorldAssemblerHybridV2' -and $EnableHybridFixturePlacement -and
|
|
$Text -notmatch '\[VoxelForgeHybridCollisionContact\] fixture=corner_top .*expected_point_error_cm=[0-9.]+') {
|
|
throw "BuildHost log is missing the V2 flat-face contact precision proof. See $FinalLog."
|
|
}
|
|
if ($Text -notmatch '\[VoxelForgeCoreWorldTest\] native_biome_field=1 .*sampled_biomes=[2-9]') {
|
|
throw "BuildHost Core actor log does not prove native SurfaceWorld biome assignment. See $FinalLog."
|
|
}
|
|
if ($Text -notmatch '\[VoxelForgeCoreWorldTest\] matched_surface_tile_timing=1 .*same_keys=1 tiles=[1-9]\d* hashes=on') {
|
|
throw "BuildHost Core actor log is missing the matched native/legacy-bridge surface timing. See $FinalLog."
|
|
}
|
|
foreach ($Marker in @(
|
|
'[VoxelForgeCoreWorldTest] geometry_scale_agreement=PASS',
|
|
'[VoxelForgeCoreWorldTest] height_agreement=PASS',
|
|
'[VoxelForgeCoreWorldTest] switch_off_content_gate=PASS',
|
|
'[VoxelForgeCoreWorldTest] blueprint_carve=PASS',
|
|
'[VoxelForgeCoreWorldTest] blueprint_fill=PASS',
|
|
'[VoxelForgePawnSpeedFixture] source=staged_blueprints walk_cm_s=600.000 sprint_cm_s=6000.000'
|
|
)) {
|
|
if ($Text -notmatch [regex]::Escape($Marker)) {
|
|
throw "BuildHost actor log is missing the round-6 proof '$Marker'. See $FinalLog."
|
|
}
|
|
}
|
|
foreach ($Backend in @('core_native', 'legacy_surface_bridge')) {
|
|
foreach ($LOD in @(0, 1)) {
|
|
foreach ($Kind in @('flat_column', 'relief', 'mixed', 'unknown')) {
|
|
if ($Text -notmatch "\[WorldAssemblerPawnLag\] sample=unhashed_runtime_fastpath backend=$Backend lod=$LOD kind=$Kind ") {
|
|
throw "BuildHost actor log is missing the unhashed $Backend LOD$LOD $Kind streaming-lag row. See $FinalLog."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
$NewCrashes = @(Get-NewCrashFolders)
|
|
if ($NewCrashes.Count -gt 0) {
|
|
throw "New UECC crash folders appeared during Core actor validation: $($NewCrashes -join '; ')"
|
|
}
|
|
Write-Host "VOXELFORGE_CORE_WORLD_PASS recipe=$CoreMesherRecipe fixtures=$HybridFixturePlacementFlag streaming=1 collision=1 anchor_move=1 carve=1"
|
|
Write-Host "Core actor log: $FinalLog"
|
|
Write-Host "Peak commandlet private bytes sampled: $PeakPrivateBytes"
|