param( [string]$ProjectRoot = 'C:\HyperTwist', [string]$UnrealEditorCmdPath = 'C:\Program Files\Epic Games\UE_5.7\Engine\Binaries\Win64\UnrealEditor-Cmd.exe', [string]$PythonScriptPath ) $ErrorActionPreference = 'Stop' if ([string]::IsNullOrWhiteSpace($PythonScriptPath)) { $PythonScriptPath = Join-Path $ProjectRoot 'scripts\hypertwist_author_virtual_4d_training_maps.py' } $UProjectPath = Join-Path $ProjectRoot 'UnrealHyperTwist\UnrealHyperTwist.uproject' $ReceiptDirectory = Join-Path $ProjectRoot 'UnrealHyperTwist\Saved\HyperTwistMapAuthoring' $ManifestPath = Join-Path $ProjectRoot 'docs\generated\virtual_4d_training_maps\virtual_4d_dedicated_map_manifest.json' $Orders = @(2, 3, 4, 5, 6) if (-not (Test-Path -LiteralPath $UnrealEditorCmdPath -PathType Leaf)) { throw "UnrealEditor-Cmd.exe was not found at '$UnrealEditorCmdPath'." } if (-not (Test-Path -LiteralPath $UProjectPath -PathType Leaf)) { throw "UnrealHyperTwist project file was not found at '$UProjectPath'." } if (-not (Test-Path -LiteralPath $PythonScriptPath -PathType Leaf)) { throw "Virtual 4D map authoring script was not found at '$PythonScriptPath'." } $NormalizedPythonScriptPath = $PythonScriptPath -replace '\\', '/' foreach ($Order in $Orders) { $MapName = "L_HyperTwist_MagicCube4D_${Order}x${Order}x${Order}x${Order}Training" $MapPath = Join-Path $ProjectRoot "UnrealHyperTwist\Content\HyperTwistTraining\Maps\$MapName.umap" $ReceiptPath = Join-Path $ReceiptDirectory "virtual_4d_${Order}x_complete.json" $ExpectedGameMode = if ($Order -eq 2) { '/Script/UnrealHyperTwist.HyperTwistMelindaProjectionGameMode' } else { "/Script/UnrealHyperTwist.HyperTwistVirtual${Order}${Order}${Order}${Order}ProjectionGameMode" } Remove-Item -LiteralPath $ReceiptPath -Force -ErrorAction SilentlyContinue $AuthoringStartedAtUtc = [DateTime]::UtcNow $env:HYPERTWIST_VIRTUAL_4D_ORDER = [string]$Order Write-Host "Authoring exact ${Order}x${Order}x${Order}x${Order} HyperTwist training map..." & $UnrealEditorCmdPath ` $UProjectPath ` "-ExecutePythonScript=$NormalizedPythonScriptPath" ` -unattended ` -nop4 ` -nullrhi ` -nosound ` -nosplash ` -stdout ` -FullStdOutLogOutput ` -log $AuthoringExitCode = $LASTEXITCODE $Receipt = $null if (Test-Path -LiteralPath $ReceiptPath -PathType Leaf) { $Receipt = Get-Content -LiteralPath $ReceiptPath -Raw | ConvertFrom-Json } $MapHash = if (Test-Path -LiteralPath $MapPath -PathType Leaf) { (Get-FileHash -LiteralPath $MapPath -Algorithm MD5).Hash.ToLowerInvariant() } else { '' } $ReceiptIsFresh = $Receipt -and ` (Get-Item -LiteralPath $ReceiptPath).LastWriteTimeUtc -ge $AuthoringStartedAtUtc.AddSeconds(-2) $ReceiptIsValid = $ReceiptIsFresh -and ` $Receipt.authoringComplete -eq $true -and ` $Receipt.order -eq $Order -and ` $Receipt.gameModeClassPath -eq $ExpectedGameMode -and ` $Receipt.mapHashMd5 -eq $MapHash if (-not $ReceiptIsValid) { Remove-Item Env:\HYPERTWIST_VIRTUAL_4D_ORDER -ErrorAction SilentlyContinue if ($AuthoringExitCode -ne 0) { throw "Virtual 4D map authoring failed with exit code $AuthoringExitCode before a valid receipt was written for order $Order." } throw "Virtual 4D map authoring exited without a fresh hash-bound receipt for order $Order." } if ($AuthoringExitCode -ne 0) { Write-Warning "UnrealEditor-Cmd exited with code $AuthoringExitCode after order $Order completed and wrote a valid receipt. Accepting the known post-completion headless shutdown failure." } } Remove-Item Env:\HYPERTWIST_VIRTUAL_4D_ORDER -ErrorAction SilentlyContinue if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) { throw "Virtual 4D map manifest was not generated at '$ManifestPath'." } $Manifest = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json $ManifestEntries = @($Manifest.entries) if ($Manifest.schemaVersion -ne 'hypertwist/virtual-4d-dedicated-map-manifest/v1' ` -or $ManifestEntries.Count -ne $Orders.Count) { throw "Virtual 4D map manifest does not contain the complete 2x through 6x route set." } foreach ($Order in $Orders) { $Entry = @($ManifestEntries | Where-Object { $_.order -eq $Order }) if ($Entry.Count -ne 1) { throw "Virtual 4D map manifest does not contain exactly one entry for order $Order." } $MapPath = Join-Path $ProjectRoot $Entry[0].mapFileRelativePath if (-not (Test-Path -LiteralPath $MapPath -PathType Leaf)) { throw "Authored virtual 4D map is missing at '$MapPath'." } $MapHash = (Get-FileHash -LiteralPath $MapPath -Algorithm MD5).Hash.ToLowerInvariant() if ($Entry[0].mapHashMd5 -ne $MapHash) { throw "Virtual 4D map manifest hash does not match order $Order." } } Write-Host 'Exact 2x through 6x virtual 4D maps and manifest verified.'