# Shared render-pack selection seam for connected graphical gates. $script:ConnectedGateEnvironmentNames = @( 'ACDREAM_AUTOMATION_ARTIFACT_DIR', 'ACDREAM_CACHE_DIR', 'ACDREAM_COLLISION_SHADOW_DIR', 'ACDREAM_COLLISION_SHADOW_EVERY', 'ACDREAM_CONFIG_DIR', 'ACDREAM_DATA_DIR', 'ACDREAM_DAT_DIR', 'ACDREAM_DEVTOOLS', 'ACDREAM_DUMP_MOVE_TRUTH', 'ACDREAM_FRAME_HISTORY', 'ACDREAM_FRAME_PROF', 'ACDREAM_LIVE', 'ACDREAM_NET_DROP_DIR', 'ACDREAM_NET_DROP_PCT', 'ACDREAM_NET_DROP_SEED', 'ACDREAM_NO_AUDIO', 'ACDREAM_PAK_PATH', 'ACDREAM_RENDER_BACKEND', 'ACDREAM_RETAIL_UI', 'ACDREAM_AUTOMATION_EXACT_FRAMEBUFFER', 'ACDREAM_DAY_GROUP', 'ACDREAM_WORLD_TIME', 'ACDREAM_SKY_PHASE_SECONDS', 'ACDREAM_ORBIT_DISTANCE_METERS', 'ACDREAM_ORBIT_YAW_DEGREES', 'ACDREAM_ORBIT_PITCH_DEGREES', 'ACDREAM_VULKAN_DEVICE', 'ACDREAM_VULKAN_FORCE_UNSUPPORTED', 'ACDREAM_VULKAN_PROBE', 'ACDREAM_VULKAN_PROBE_FRAMES', 'ACDREAM_TEST_HOST', 'ACDREAM_TEST_PASS', 'ACDREAM_TEST_PORT', 'ACDREAM_TEST_USER', 'ACDREAM_UI_PROBE_DUMP', 'ACDREAM_UI_PROBE_SCRIPT', 'ACDREAM_UNCAPPED_RENDER', 'ACDREAM_WB_DIAG' ) function Assert-ConnectedGateSafeLeafName { param([Parameter(Mandatory = $true)][string]$Name) if ($Name -notmatch '^[A-Za-z0-9][A-Za-z0-9_-]{0,79}$') { throw "Unsafe screenshot leaf name '$Name'." } } function Assert-ConnectedGateContainedPath { param([Parameter(Mandatory = $true)][string]$Root, [Parameter(Mandatory = $true)][string]$Path) $rootFull = [IO.Path]::GetFullPath($Root).TrimEnd( [IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar) $pathFull = [IO.Path]::GetFullPath($Path) $prefix = $rootFull + [IO.Path]::DirectorySeparatorChar if (-not $pathFull.StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase)) { throw "Path '$pathFull' is not contained by gate root '$rootFull'." } return $pathFull } function Assert-ConnectedGateNoReparsePoint { param([Parameter(Mandatory = $true)][string]$Path) if ((Get-Item -LiteralPath $Path).Attributes -band [IO.FileAttributes]::ReparsePoint) { throw "Gate path must not be a reparse point: '$Path'." } } function Get-ConnectedRenderPackExpectation { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateSet('retail', 'low', 'medium', 'high', 'auto')] [string]$Preset ) return [pscustomobject][ordered]@{ RequestedPreset = $Preset PackId = if ($Preset -eq 'retail') { 'retail' } else { 'acdream.atmospheric' } PackVersion = if ($Preset -eq 'retail') { $null } else { '1.0.0' } PresetId = if ($Preset -eq 'retail') { 'off' } else { $Preset } ExpectedState = if ($Preset -eq 'retail') { 0 } else { 2 } ExpectedSchemaVersion = 1 } } function New-ConnectedRenderPackGateState { [CmdletBinding()] param( [Parameter(Mandatory = $true)][string]$Root, [Parameter(Mandatory = $true)] [ValidateSet('retail', 'low', 'medium', 'high', 'auto')] [string]$Preset, [hashtable]$SettingOverrides = @{} ) if ($Preset -eq 'retail' -and $SettingOverrides.Count -ne 0) { throw 'Render-pack setting overrides require an enhanced render-pack preset.' } $rootFull = [IO.Path]::GetFullPath($Root) if (-not (Test-Path -LiteralPath $rootFull -PathType Container)) { throw "Connected gate root does not exist: '$rootFull'." } Assert-ConnectedGateNoReparsePoint $rootFull $stateDirectory = Assert-ConnectedGateContainedPath $rootFull (Join-Path $rootFull 'isolated-state') if (Test-Path -LiteralPath $stateDirectory) { throw "Isolated gate state already exists: '$stateDirectory'." } # A connected closeout row must not inherit a diagnostic, content-path, # budget, camera, weather, or device override from a prior shell run. Keep # the explicitly-owned launch names for absent-variable restoration and # also capture every currently-defined ACDREAM_* name so newly-added knobs # fail isolated without requiring this seam to know their semantics first. $transactionNames = @( $script:ConnectedGateEnvironmentNames Get-ChildItem Env: | Where-Object { $_.Name -like 'ACDREAM_*' } | Select-Object -ExpandProperty Name ) | Sort-Object -Unique $previous = [ordered]@{} foreach ($name in $transactionNames) { $previous[$name] = [Environment]::GetEnvironmentVariable( $name, [EnvironmentVariableTarget]::Process) Remove-Item -LiteralPath "Env:$name" -ErrorAction SilentlyContinue } $configDirectory = Join-Path $stateDirectory 'config' $dataDirectory = Join-Path $stateDirectory 'data' $cacheDirectory = Join-Path $stateDirectory 'cache' $null = New-Item -ItemType Directory -Path $configDirectory, $dataDirectory, $cacheDirectory foreach ($path in @($stateDirectory, $configDirectory, $dataDirectory, $cacheDirectory)) { Assert-ConnectedGateNoReparsePoint $path } $packId = if ($Preset -eq 'retail') { 'retail' } else { 'acdream.atmospheric' } $packVersion = if ($Preset -eq 'retail') { $null } else { '1.0.0' } $presetId = if ($Preset -eq 'retail') { 'off' } else { $Preset } $expectedState = if ($Preset -eq 'retail') { 0 } else { 2 } $orderedOverrides = [ordered]@{} foreach ($key in @($SettingOverrides.Keys | Sort-Object)) { if ([string]::IsNullOrWhiteSpace([string]$key)) { throw 'Render-pack setting override IDs cannot be empty.' } $orderedOverrides[[string]$key] = [string]$SettingOverrides[$key] } [ordered]@{ display = [ordered]@{ renderPack = [ordered]@{ packId = $packId; packVersion = $packVersion; presetId = $presetId settingOverrides = $orderedOverrides } } version = 3 } | ConvertTo-Json -Depth 8 | Set-Content -Encoding utf8 -LiteralPath (Join-Path $configDirectory 'settings.json') [Environment]::SetEnvironmentVariable('ACDREAM_CONFIG_DIR', $configDirectory, 'Process') [Environment]::SetEnvironmentVariable('ACDREAM_DATA_DIR', $dataDirectory, 'Process') [Environment]::SetEnvironmentVariable('ACDREAM_CACHE_DIR', $cacheDirectory, 'Process') return [pscustomobject][ordered]@{ RequestedPreset = $Preset; PackId = $packId; PackVersion = $packVersion PresetId = $presetId; ExpectedState = $expectedState; ExpectedSchemaVersion = 1 SettingOverrides = $orderedOverrides; StateDirectory = $stateDirectory ConfigDirectory = $configDirectory; DataDirectory = $dataDirectory CacheDirectory = $cacheDirectory; PreviousEnvironment = $previous } } function Restore-ConnectedRenderPackGateEnvironment { [CmdletBinding()] param([Parameter(Mandatory = $true)][object]$State) foreach ($entry in $State.PreviousEnvironment.GetEnumerator()) { if ($null -eq $entry.Value) { Remove-Item -LiteralPath "Env:$($entry.Key)" -ErrorAction SilentlyContinue } else { [Environment]::SetEnvironmentVariable( [string]$entry.Key, $entry.Value, [EnvironmentVariableTarget]::Process) } } } function New-ConnectedGraphicalSessionConfig { [CmdletBinding()] param( [Parameter(Mandatory = $true)][object]$State, [Parameter(Mandatory = $true)][string]$Account, [string]$HostName = '127.0.0.1', [ValidateRange(1, 65535)][int]$Port = 9000, [ValidateRange(0, [int]::MaxValue)][int]$CharacterIndex = 0, [string]$CredentialEnvironmentVariable = 'ACDREAM_TEST_PASS' ) if ([string]::IsNullOrWhiteSpace($Account)) { throw 'Connected graphical session account cannot be empty.' } if ([string]::IsNullOrWhiteSpace($HostName)) { throw 'Connected graphical session host cannot be empty.' } if ($CredentialEnvironmentVariable -notmatch '^[A-Za-z_][A-Za-z0-9_]*$') { throw "Unsafe credential environment-variable name '$CredentialEnvironmentVariable'." } $path = Assert-ConnectedGateContainedPath $State.StateDirectory ( Join-Path $State.StateDirectory 'graphical-session.json') if (Test-Path -LiteralPath $path) { throw "Connected graphical session config already exists: '$path'." } [ordered]@{ version = 1 sessions = @([ordered]@{ id = 'connected-gate' endpoint = [ordered]@{ host = $HostName; port = $Port } account = $Account character = [ordered]@{ index = $CharacterIndex } credential = [ordered]@{ provider = 'Environment' reference = $CredentialEnvironmentVariable } }) } | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $path -Encoding utf8 return $path } function Remove-ConnectedGraphicalSessionConfig { [CmdletBinding()] param( [Parameter(Mandatory = $true)][object]$State, [Parameter(Mandatory = $true)][string]$Path ) $containedPath = Assert-ConnectedGateContainedPath $State.StateDirectory $Path if (Test-Path -LiteralPath $containedPath -PathType Leaf) { Remove-Item -LiteralPath $containedPath -Force } } function Get-ConnectedGateBinaryIdentity { [CmdletBinding()] param([Parameter(Mandatory = $true)][string]$Repository, [Parameter(Mandatory = $true)][string]$Executable, [switch]$SkipBuild) $sourceCommit = (& git -C $Repository rev-parse HEAD).Trim().ToLowerInvariant() $trackedStatus = @(& git -C $Repository status --short --untracked-files=all) $productVersion = [Diagnostics.FileVersionInfo]::GetVersionInfo($Executable).ProductVersion $match = [regex]::Match([string]$productVersion, '(?i)(?