XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEWarframe

【WinUI】基于Warframe游戏本体的公开服务器以及安装器

公开
关注 0 Fork 0 Star 0
UTF-8
#requires -Version 7.0
param([string]$Destination, [switch]$SkipLaunchers, [switch]$VerifyDialogs, [string]$GameValidationRoot, [switch]$VerifyAbilityBanks, [switch]$VerifyExaltedEquipment)
$ErrorActionPreference = 'Stop'
if ($VerifyExaltedEquipment -and -not $VerifyAbilityBanks) { throw 'Exalted verification requires a fresh ability-bank validation fixture.' }
$taskRoot = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..'))
if (-not $Destination) { $Destination = Join-Path $taskRoot ('artifacts/package-' + [DateTime]::Now.ToString('yyyyMMdd-HHmmss')) }
$Destination = [IO.Path]::GetFullPath($Destination)
if (Test-Path -LiteralPath $Destination) { throw 'Choose a new output directory; existing packages are not overwritten.' }
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
$buildRoot = Join-Path $taskRoot ('artifacts/publish-build/' + [Guid]::NewGuid().ToString('N'))
Push-Location $taskRoot
try {
    & (Join-Path $PSScriptRoot 'Check-Git.ps1')
    $applications = @(
        @{ Name='Server'; Project='Server/XFEWarframe.Server'; Output='Server' },
        @{ Name='Admin'; Project='Admin/XFEWarframe.Admin'; Output='Admin' },
        @{ Name='Player'; Project='Client/XFEWarframe.Player'; Output='Client' },
        @{ Name='Installer'; Project='Client/XFEWarframe.Installer'; Output='Client/Installer' }
    )
    foreach ($app in $applications) {
        $project = Join-Path $app.Project ("XFEWarframe." + $app.Name + ".csproj")
        dotnet publish $project -c Release -r win-x64 --self-contained true ("-p:XFEIntermediateRoot=" + (Join-Path $buildRoot "obj/$($app.Name)")) ("-p:OutputPath=" + (Join-Path $buildRoot $app.Name) + [IO.Path]::DirectorySeparatorChar) -o (Join-Path $Destination $app.Output) --nologo
        if ($LASTEXITCODE -ne 0) { throw "$($app.Name) publish failed" }
    }
    & (Join-Path $PSScriptRoot 'Build-Bridge.ps1') -Destination (Join-Path $Destination 'Client/Bridge') -IfNeeded
    Copy-Item -LiteralPath LICENSE,README.md,THIRD-PARTY-NOTICES.md -Destination $Destination
    Copy-Item -LiteralPath docs -Destination $Destination -Recurse
    & (Join-Path $PSScriptRoot 'Export-Licenses.ps1') -Destination (Join-Path $Destination 'licenses')
    $validation = Join-Path $Destination 'validation'
    New-Item -ItemType Directory -Path $validation -Force | Out-Null
    foreach ($name in @('core-tests.trx','client-tests.trx')) {
        $report = Join-Path $taskRoot "artifacts/test-results/$name"
        if (Test-Path -LiteralPath $report) { Copy-Item -LiteralPath $report -Destination $validation }
    }
    foreach ($name in @('client-validation.json','client-compatibility-initial.json')) {
        $report = Join-Path $taskRoot "artifacts/$name"
        if (Test-Path -LiteralPath $report) { Copy-Item -LiteralPath $report -Destination $validation }
    }
    $sourceRoot = Join-Path $Destination 'source'
    New-Item -ItemType Directory -Path $sourceRoot -Force | Out-Null
    # Include corresponding source, never machine-specific profiles, secrets, live state, or build caches.
    $sourceRelativeFiles = @(git -c core.quotepath=false ls-files --cached --others --exclude-standard -- Server Client Admin scripts docs)
    if ($LASTEXITCODE -ne 0) { throw 'Cannot enumerate source files with the repository ignore rules.' }
    $sourceFiles = $sourceRelativeFiles | ForEach-Object { $path = Join-Path $taskRoot $_; if (Test-Path -LiteralPath $path -PathType Leaf) { Get-Item -LiteralPath $path } }
    foreach ($file in $sourceFiles) {
        $target = Join-Path $sourceRoot ([IO.Path]::GetRelativePath($taskRoot, $file.FullName))
        New-Item -ItemType Directory -Path (Split-Path $target) -Force | Out-Null
        Copy-Item -LiteralPath $file.FullName -Destination $target
    }
    Copy-Item -LiteralPath XFEWarframe.slnx,XFEWarframe.slnLaunch,Directory.Build.props,global.json,LICENSE,README.md,THIRD-PARTY-NOTICES.md,.gitignore,.gitattributes,.editorconfig -Destination $sourceRoot

    $launchers = @{
        'Server/Start.cmd' = '"%~dp0XFEWarframe.Server.exe" --root="%~dp0runtime" %*'
        'Admin/Start.cmd' = 'start "" "%~dp0XFEWarframe.Admin.exe" %*'
        'Client/Start.cmd' = 'start "" "%~dp0XFEWarframe.Player.exe" %*'
        'Client/Installer.cmd' = '"%~dp0Installer\XFEWarframe.Installer.exe" %*'
    }
    foreach ($entry in $launchers.GetEnumerator()) {
        [IO.File]::WriteAllText((Join-Path $Destination $entry.Key), "@echo off" + [Environment]::NewLine + $entry.Value + [Environment]::NewLine, [Text.Encoding]::ASCII)
    }

    # Exercise the exact published executables and resource files before archiving.
    & (Join-Path $PSScriptRoot 'Smoke-Test.ps1') -ServerPath (Join-Path $Destination 'Server/XFEWarframe.Server.exe') | Tee-Object -Variable taskHttpOutput
    & (Join-Path $PSScriptRoot 'Smoke-Desktop.ps1') -Package $Destination | Tee-Object -Variable taskLayoutOutput
    if ($VerifyDialogs) {
        & (Join-Path $PSScriptRoot 'Smoke-Desktop.ps1') -Package $Destination -VerifyDialogs | Tee-Object -Variable taskDialogOutput
    }
    & (Join-Path $Destination 'Client/Installer/XFEWarframe.Installer.exe') selftest
    if ($LASTEXITCODE -ne 0) { throw 'Published NativeAOT bridge self-test failed' }
    @{ passed=$true; scope='NativeAOT, x64 relocation, callbacks and rollback; automatic Client/Bridge discovery' } |
        ConvertTo-Json | Set-Content -LiteralPath (Join-Path $validation 'native-bridge-selftest.json') -Encoding utf8
    $taskReportSets = @(
        @{ Output=$taskHttpOutput; Marker='HTTP smoke results: '; Names=@('results.json'); Prefix='http-' },
        @{ Output=$taskLayoutOutput; Marker='Desktop reports: '; Names=@('Admin.json','Player.json','PlayerReadOnly.json'); Prefix='desktop-layout-' }
    )
    if ($VerifyDialogs) {
        $taskReportSets += @{ Output=$taskDialogOutput; Marker='Desktop reports: '; Names=@('Admin.json','Player.json','PlayerReadOnly.json'); Prefix='desktop-' }
    } else {
        $taskReportSets += @{ Output=$taskLayoutOutput; Marker='Desktop reports: '; Names=@('Admin.json','Player.json','PlayerReadOnly.json'); Prefix='desktop-' }
    }
    # Copy this run's reports, never another parallel validation's most recent directory.
    foreach ($spec in $taskReportSets) {
        $taskReportLine = @($spec.Output | Where-Object { "$_".StartsWith($spec.Marker, [StringComparison]::Ordinal) })
        if ($taskReportLine.Count -ne 1) { throw "Missing or ambiguous validation report: $($spec.Prefix)" }
        $taskReportDirectory = "$($taskReportLine[0])".Substring($spec.Marker.Length)
        foreach ($name in $spec.Names) {
            Copy-Item -LiteralPath (Join-Path $taskReportDirectory $name) -Destination (Join-Path $validation ($spec.Prefix + $name))
        }
    }
    if ($VerifyAbilityBanks) {
        & (Join-Path $PSScriptRoot 'Start-MultiAbilityTest.ps1') -Package $Destination -AbilityBanks -NoWait
        $GameValidationRoot = Join-Path $taskRoot 'artifacts/ability-bank-game-test'
    }
    if ($VerifyExaltedEquipment) {
        & (Join-Path $PSScriptRoot 'Start-ExaltedEquipmentTest.ps1') -Package $Destination -SessionFile (Join-Path $GameValidationRoot 'session.json') -ReportDirectory (Join-Path $GameValidationRoot 'exalted-validation')
    }
    if ($GameValidationRoot) {
        $taskBridgeHash = (Get-FileHash -LiteralPath (Join-Path $Destination 'Client/Bridge/XFEWarframe.Bridge.dll') -Algorithm SHA256).Hash
        foreach ($name in @('login','engine')) {
            $report = Join-Path $GameValidationRoot ($name + '-validation/result.json')
            $taskGameResult = Get-Content -LiteralPath $report -Raw | ConvertFrom-Json
            if ($taskGameResult.passed -ne $true -or $taskGameResult.bridgeSha256 -ne $taskBridgeHash) {
                throw "The $name game validation did not pass with this exact bridge binary."
            }
            Copy-Item -LiteralPath $report -Destination (Join-Path $validation ('game-' + $name + '.json'))
        }
        $taskWheelReport = Join-Path $GameValidationRoot 'wheel-validation/result.json'
        if (Test-Path -LiteralPath $taskWheelReport) {
            $taskWheelResult = Get-Content -LiteralPath $taskWheelReport -Raw | ConvertFrom-Json
            if ($taskWheelResult.passed -ne $true -or $taskWheelResult.bridgeSha256 -ne $taskBridgeHash) {
                throw 'The wheel validation did not pass with this exact bridge binary.'
            }
            Copy-Item -LiteralPath $taskWheelReport -Destination (Join-Path $validation 'game-wheels.json')
        }
        foreach ($name in @('king','audio','branding-login','branding-game') + $(if ($VerifyExaltedEquipment) { @('exalted') } else { @() })) {
            $report = Join-Path $GameValidationRoot ($name + '-validation/result.json')
            if (-not (Test-Path -LiteralPath $report)) {
                if ($VerifyAbilityBanks) { throw "Missing $name game validation." }
                continue
            }
            $taskGameResult = Get-Content -LiteralPath $report -Raw | ConvertFrom-Json
            if ($taskGameResult.passed -ne $true -or $taskGameResult.bridgeSha256 -ne $taskBridgeHash) {
                throw "The $name game validation did not pass with this exact bridge binary."
            }
            Copy-Item -LiteralPath $report -Destination (Join-Path $validation ('game-' + $name + '.json'))
        }
    }

    $common = @('source','docs','licenses','LICENSE','README.md','THIRD-PARTY-NOTICES.md','validation') | ForEach-Object { Join-Path $Destination $_ }
    foreach ($role in @('Server','Client','Admin')) {
        Compress-Archive -LiteralPath (@((Join-Path $Destination $role)) + $common) -DestinationPath (Join-Path $Destination "XFEWarframe-$role-win-x64.zip") -CompressionLevel Fastest
    }
    Get-ChildItem -LiteralPath $Destination -Filter '*.zip' | Get-FileHash -Algorithm SHA256 |
        Select-Object @{n='File';e={Split-Path $_.Path -Leaf}},Hash | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $Destination 'SHA256.json') -Encoding utf8
} finally { Pop-Location }
Write-Output "Published Server, Client and Admin to $Destination"
if ($SkipLaunchers) { return }
$taskPackageRelative = [IO.Path]::GetRelativePath((Join-Path $taskRoot 'artifacts'), $Destination)
foreach ($entry in @(
    @{Name='Server';Command=('"%~dp0' + $taskPackageRelative + '\Server\XFEWarframe.Server.exe" --root="%~dp0..\Server\runtime" %*')},
    @{Name='Desktop';Command=('start "" "%~dp0' + $taskPackageRelative + '\Admin\XFEWarframe.Admin.exe" --root="%~dp0..\Server\runtime" %*')},
    @{Name='Player';Command=('start "" "%~dp0' + $taskPackageRelative + '\Client\XFEWarframe.Player.exe" %*')}
)) {
    [IO.File]::WriteAllText((Join-Path $taskRoot ('artifacts/Start-Updated-' + $entry.Name + '.cmd')), "@echo off`r`nchcp 65001 >nul`r`n" + $entry.Command + "`r`n", [Text.UTF8Encoding]::new($false))
}