Prove released Aetheria world lowering

This commit is contained in:
Meta Vens 2026-07-14 00:49:36 +01:00
commit b05e7daf05
5 changed files with 279 additions and 37 deletions

View file

@ -2,6 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Eve.Surface;
@ -175,12 +176,16 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Path.Combine(Application.temporaryCachePath, $"eve-unity-{Guid.NewGuid():N}.cc");
var capturePath = Environment.GetEnvironmentVariable("EVEUNITY_AETHERIA_CAPTURE_PATH") ??
Path.Combine(Application.temporaryCachePath, "aetheria-daemon-world.png");
var mapCapturePath = Environment.GetEnvironmentVariable("EVEUNITY_AETHERIA_MAP_CAPTURE_PATH") ??
Path.Combine(Application.temporaryCachePath, "aetheria-daemon-map.png");
var root = new GameObject("Generic Eve World Projection");
var cameraObject = new GameObject("Generic Eve Capture Camera");
var mapCameraObject = new GameObject("Generic Eve Map Camera");
var lightObject = new GameObject("Generic Eve World Light");
RenderTexture target = null;
Texture2D pixels = null;
EveUnityCultMeshPlayableWorldProvider provider = null;
EveUnityPlayableWorldClientHost host = null;
EveUnityPlayableWorldRuntime runtime = null;
WitnessReceipt movementReceipt = null;
WitnessReceipt focusReceipt = null;
@ -217,18 +222,38 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(provider.Selection.VerseId, Is.Not.Empty);
Assert.That(provider.Selection.ProviderId, Is.Not.Empty);
Assert.That(provider.Selection.SurfaceId, Is.Not.Empty);
runtime = EveUnityPlayableWorldRuntime.CreateForGameObjectScene(
host = root.AddComponent<EveUnityPlayableWorldClientHost>();
host.Configure(
root.transform,
provider,
provider,
null,
provider,
provider,
provider);
var initial = runtime.Connect();
Assert.That(initial.ActiveEntities, Is.GreaterThan(0));
host.Connect();
runtime = host.Runtime;
Assert.That(runtime, Is.Not.Null);
Assert.That(runtime.ActiveWorld.PlayerEntityId, Is.Not.Empty);
var entityDeadline = Time.realtimeSinceStartup + 12f;
while (Time.realtimeSinceStartup < entityDeadline &&
(host.PresentedEntities?.CurrentGeneration == null ||
host.PresentedEntities.CurrentGeneration.Entities.Count == 0))
{
yield return new WaitForSecondsRealtime(0.1f);
host.Refresh();
}
Assert.That(host.PresentedEntities?.CurrentGeneration, Is.Not.Null);
Assert.That(host.PresentedEntities.CurrentGeneration.Entities.Count, Is.GreaterThan(0));
var playerId = runtime.ActiveWorld.PlayerEntityId;
var playerFact = host.PresentedEntities.CurrentGeneration.Entities
.Single(entity => entity.EntityId == playerId);
var playerPrefab = provider.ResolvePrefab(new EveUnityPlayableWorldAssetBinding(
playerFact.AssetRef,
playerFact.EntityKind,
"provider-asset-ref"));
Assert.That(playerPrefab, Is.Not.Null,
$"Provider asset catalog did not resolve player AssetRef '{playerFact.AssetRef}'.");
var marker = FindMarker(root, playerId);
Assert.That(
marker.GetComponentsInChildren<Transform>(includeInactive: true).Length,
@ -302,6 +327,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
var camera = cameraObject.AddComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = new Color(0.035f, 0.055f, 0.08f, 1f);
camera.cullingMask = -1;
foreach (var entityMarker in root.GetComponentsInChildren<EveUnityPlayableWorldEntityMarker>())
{
foreach (var renderer in entityMarker.GetComponentsInChildren<Renderer>())
@ -316,15 +342,25 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
var playerMarker = FindMarker(root, playerId);
var playerRenderers = playerMarker.GetComponentsInChildren<Renderer>();
Assert.That(playerRenderers, Is.Not.Empty, "The provider-authored player prefab has no renderable visual.");
var playerBounds = playerRenderers[0].bounds;
foreach (var renderer in playerRenderers)
playerBounds.Encapsulate(renderer.bounds);
var halfFovRadians = camera.fieldOfView * 0.5f * Mathf.Deg2Rad;
var cameraDistance = Mathf.Max(8f, playerBounds.extents.magnitude / Mathf.Tan(halfFovRadians) * 1.35f);
camera.nearClipPlane = Mathf.Max(0.01f, cameraDistance * 0.001f);
camera.farClipPlane = Mathf.Max(1000f, cameraDistance + playerBounds.size.magnitude * 4f);
camera.transform.position = playerBounds.center + new Vector3(1f, 0.65f, -1.2f).normalized * cameraDistance;
camera.transform.LookAt(playerBounds.center);
Assert.That(provider.TryGetRenderChannelLayer("map", out var mapLayer), Is.True);
var mapRenderers = new List<Renderer>();
foreach (var renderer in root.GetComponentsInChildren<Renderer>(includeInactive: true))
{
if (renderer.gameObject.layer == mapLayer)
mapRenderers.Add(renderer);
}
Assert.That(mapRenderers, Is.Not.Empty, "The live provider prefab has no renderer assigned to its advertised map channel.");
var cameraRig = cameraObject.AddComponent<EveUnityPlayableWorldCameraRig>();
cameraRig.Host = host;
cameraRig.CameraTransform = camera.transform;
cameraRig.RenderPolicySource = provider;
Assert.That(cameraRig.ApplyRig(0f), Is.True);
Assert.That(camera.cullingMask & (1 << mapLayer), Is.Zero);
var mapCamera = mapCameraObject.AddComponent<Camera>();
mapCamera.cullingMask = 1 << mapLayer;
Assert.That(mapCamera.cullingMask & (1 << mapLayer), Is.Not.Zero);
target = new RenderTexture(640, 360, 24, RenderTextureFormat.ARGB32);
camera.targetTexture = target;
yield return null;
@ -336,10 +372,36 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(capturePath)));
File.WriteAllBytes(capturePath, pixels.EncodeToPNG());
Assert.That(new FileInfo(capturePath).Length, Is.GreaterThan(1024));
var mapBounds = mapRenderers[0].bounds;
foreach (var renderer in mapRenderers)
mapBounds.Encapsulate(renderer.bounds);
mapCamera.clearFlags = CameraClearFlags.SolidColor;
mapCamera.backgroundColor = camera.backgroundColor;
mapCamera.orthographic = true;
mapCamera.orthographicSize = Mathf.Max(0.1f, mapBounds.extents.x, mapBounds.extents.z) * 1.25f;
mapCamera.nearClipPlane = 0.01f;
mapCamera.farClipPlane = Mathf.Max(10f, mapBounds.size.magnitude * 4f);
mapCamera.transform.position = mapBounds.center + Vector3.up * (mapCamera.farClipPlane * 0.25f);
mapCamera.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
mapCamera.targetTexture = target;
mapCamera.Render();
RenderTexture.active = target;
pixels.ReadPixels(new Rect(0, 0, 640, 360), 0, 0);
pixels.Apply();
var mapChangedPixels = CountChangedPixels(pixels, mapCamera.backgroundColor);
Assert.That(mapChangedPixels, Is.GreaterThan(25), "The map-only camera rendered no provider-authored map glyph pixels.");
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(mapCapturePath)));
File.WriteAllBytes(mapCapturePath, pixels.EncodeToPNG());
Assert.That(new FileInfo(mapCapturePath).Length, Is.GreaterThan(1024));
WriteWitnessFacts(
initialVersion,
runtime.ActiveVersion,
movementDistance,
mapRenderers.Count,
mapChangedPixels,
(camera.cullingMask & (1 << mapLayer)) == 0,
(mapCamera.cullingMask & (1 << mapLayer)) != 0,
movementReceipt,
focusReceipt,
actionReceipt);
@ -349,10 +411,13 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
RenderTexture.active = null;
var camera = cameraObject.GetComponent<Camera>();
if (camera != null) camera.targetTexture = null;
var mapCamera = mapCameraObject.GetComponent<Camera>();
if (mapCamera != null) mapCamera.targetTexture = null;
if (target != null) UnityEngine.Object.DestroyImmediate(target);
if (pixels != null) UnityEngine.Object.DestroyImmediate(pixels);
runtime?.Dispose();
host?.Disconnect();
UnityEngine.Object.DestroyImmediate(lightObject);
UnityEngine.Object.DestroyImmediate(mapCameraObject);
UnityEngine.Object.DestroyImmediate(cameraObject);
UnityEngine.Object.DestroyImmediate(root);
}
@ -362,6 +427,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
long initialVersion,
long finalVersion,
float movementDistance,
int mapChannelRendererCount,
int mapChangedPixels,
bool pilotCameraExcludesMapChannel,
bool mapCameraIncludesMapChannel,
WitnessReceipt movement,
WitnessReceipt targeting,
WitnessReceipt action)
@ -378,6 +447,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
initialVersion = initialVersion,
finalVersion = finalVersion,
movementDistance = movementDistance,
mapChannelRendererCount = mapChannelRendererCount,
mapChangedPixels = mapChangedPixels,
pilotCameraExcludesMapChannel = pilotCameraExcludesMapChannel,
mapCameraIncludesMapChannel = mapCameraIncludesMapChannel,
receipts = new[] { movement, targeting, action }
};
Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(path)));
@ -395,6 +468,10 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
public long initialVersion;
public long finalVersion;
public float movementDistance;
public int mapChannelRendererCount;
public int mapChangedPixels;
public bool pilotCameraExcludesMapChannel;
public bool mapCameraIncludesMapChannel;
public WitnessReceipt[] receipts;
}
@ -428,6 +505,19 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
throw new InvalidOperationException($"Projected world has no entity marker '{entityId}'.");
}
private static int CountChangedPixels(Texture2D texture, Color background)
{
var changed = 0;
foreach (var pixel in texture.GetPixels())
{
if (Mathf.Abs(pixel.r - background.r) +
Mathf.Abs(pixel.g - background.g) +
Mathf.Abs(pixel.b - background.b) > 0.08f)
changed++;
}
return changed;
}
private static EveUnitySceneProviderSurfaceDocument WorldDocument()
{
var entities = new[]

View file

@ -5,10 +5,11 @@
"com.unity.modules.imageconversion": "1.0.0",
"com.unity.modules.physics": "1.0.0",
"com.unity.modules.uielements": "1.0.0",
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.8",
"com.unity.render-pipelines.universal": "17.4.0",
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.10",
"org.gamecult.eve.plugin-fields": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.1.1",
"org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.0",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.2.0",
"org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.1",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.15",
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0"
},
"testables": [

View file

@ -1,5 +1,27 @@
{
"dependencies": {
"com.unity.burst": {
"version": "1.8.29",
"depth": 2,
"source": "registry",
"dependencies": {
"com.unity.mathematics": "1.2.1",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.collections": {
"version": "6.4.0",
"depth": 2,
"source": "builtin",
"dependencies": {
"com.unity.burst": "1.8.23",
"com.unity.mathematics": "1.3.2",
"com.unity.nuget.mono-cecil": "1.11.5",
"com.unity.test-framework": "1.4.6",
"com.unity.test-framework.performance": "3.0.3"
}
},
"com.unity.ext.nunit": {
"version": "2.0.5",
"depth": 1,
@ -13,6 +35,60 @@
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.nuget.mono-cecil": {
"version": "1.11.6",
"depth": 3,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.render-pipelines.core": {
"version": "17.4.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.burst": "1.8.14",
"com.unity.mathematics": "1.3.2",
"com.unity.ugui": "2.0.0",
"com.unity.collections": "2.4.3",
"com.unity.modules.terrain": "1.0.0",
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.render-pipelines.universal": {
"version": "17.4.0",
"depth": 0,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.4.0",
"com.unity.shadergraph": "17.4.0",
"com.unity.render-pipelines.universal-config": "17.4.0"
}
},
"com.unity.render-pipelines.universal-config": {
"version": "17.4.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.4.0"
}
},
"com.unity.searcher": {
"version": "4.9.4",
"depth": 2,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.shadergraph": {
"version": "17.4.0",
"depth": 1,
"source": "builtin",
"dependencies": {
"com.unity.render-pipelines.core": "17.4.0",
"com.unity.searcher": "4.9.3"
}
},
"com.unity.test-framework": {
"version": "1.6.0",
"depth": 0,
@ -23,12 +99,31 @@
"com.unity.modules.jsonserialize": "1.0.0"
}
},
"com.unity.test-framework.performance": {
"version": "3.4.0",
"depth": 3,
"source": "registry",
"dependencies": {
"com.unity.test-framework": "1.1.33",
"com.unity.modules.jsonserialize": "1.0.0"
},
"url": "https://packages.unity.com"
},
"com.unity.ugui": {
"version": "2.0.0",
"depth": 2,
"source": "builtin",
"dependencies": {
"com.unity.modules.ui": "1.0.0",
"com.unity.modules.imgui": "1.0.0"
}
},
"org.gamecult.cultlib": {
"version": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.7",
"version": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.10",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "1f8f832a788e2bdf59df90fb91da583574d3a42d"
"hash": "085aa2c0756328770623474c4e9fbacd7218b390"
},
"org.gamecult.eve.plugin-fields": {
"version": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.1.1",
@ -38,25 +133,25 @@
"hash": "0b099274a4e2b4c301eced4322166837792f585a"
},
"org.gamecult.eve.surface": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#b6f001b300a88aff0bc13372b5c1dbc2002954f8",
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.1",
"depth": 0,
"source": "git",
"dependencies": {
"org.gamecult.cultlib": "1.0.6"
"org.gamecult.cultlib": "1.0.8"
},
"hash": "b6f001b300a88aff0bc13372b5c1dbc2002954f8"
"hash": "d0aa50d31f3b6da96bffc194fdc3aa358f37e332"
},
"org.gamecult.eve.unity-scene": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#b6f001b300a88aff0bc13372b5c1dbc2002954f8",
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.15",
"depth": 0,
"source": "git",
"dependencies": {
"org.gamecult.cultlib": "1.0.6",
"org.gamecult.cultlib": "1.0.10",
"org.gamecult.eve.plugin-fields": "0.1.1",
"org.gamecult.eve.surface": "0.1.0",
"org.gamecult.eve.surface": "0.2.1",
"com.unity.mathematics": "1.3.2"
},
"hash": "b6f001b300a88aff0bc13372b5c1dbc2002954f8"
"hash": "474cadac1c173e24b2c9b9ddde7b781059dbae4c"
},
"org.gamecult.eve.unity-uitoolkit": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0",
@ -103,6 +198,12 @@
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.terrain": {
"version": "1.0.0",
"depth": 2,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.ui": {
"version": "1.0.0",
"depth": 1,

View file

@ -0,0 +1,18 @@
# Aetheria released-package witness
The warm-cache witness passes against `org.gamecult.eve.unity-scene` `0.3.15`
at commit `474cadac1c173e24b2c9b9ddde7b781059dbae4c`.
Evidence is in `artifacts/render-channel-witness-41`:
- `results.xml`: one passing PlayMode witness.
- `aetheria-daemon-world.png`: provider-authored 3D pilot view.
- `aetheria-daemon-map.png`: map-channel-only view.
- `witness-facts.json`: movement, targeting, action, receipts, and camera-mask facts.
- `runtime-witness.warm.json`: released package and screenshot hashes.
The cold CDN path is not proven. `artifacts/render-channel-witness-21/results.xml`
records a timeout while fetching the 13,006,384-byte bundle through batches of
32 `gamecult.mesh.cdn_artifact_chunk.v1` snapshot records. The bundle must move
through the intended mapped/network body transport; increasing the snapshot
timeout is not an accepted fix.

View file

@ -1,6 +1,8 @@
param(
[string] $UnityExe = "C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Unity.exe",
[string] $AetheriaRoot = "E:\Projects\Aetheria",
[string] $CultLibRoot = "E:\Projects\CultLib",
[string] $EveUnityRoot = "E:\Projects\EveUnity",
[string] $ClientProject = "ReleaseConsumerProject",
[int] $Port = 3076,
[string] $OutputDirectory = "artifacts\aetheria-daemon",
@ -18,6 +20,7 @@ $unityLogPath = Join-Path $outputRoot "unity.log"
$bundleBuildLogPath = Join-Path $outputRoot "asset-bundle-build.log"
$daemonLogPath = Join-Path $outputRoot "aetheria-daemon.log"
$capturePath = Join-Path $outputRoot "aetheria-daemon-world.png"
$mapCapturePath = Join-Path $outputRoot "aetheria-daemon-map.png"
$factsPath = Join-Path $outputRoot "witness-facts.json"
$witnessPath = Join-Path $outputRoot "runtime-witness.json"
$replicaPath = Join-Path $outputRoot "eve-unity-replica.cc"
@ -55,13 +58,24 @@ foreach ($ephemeralPath in @(
if (Test-Path -LiteralPath $resolved) { Remove-Item -LiteralPath $resolved -Recurse -Force }
}
$import = Start-Process -FilePath "dotnet" -ArgumentList @(
"run", "--project", $importProject, "--", $AetheriaRoot, $statePath
) -PassThru -WindowStyle Hidden -Wait `
-RedirectStandardOutput (Join-Path $outputRoot "aetheria-import.log") `
-RedirectStandardError (Join-Path $outputRoot "aetheria-import.error.log")
if ($import.ExitCode -ne 0) {
throw "Aetheria state import failed with exit code $($import.ExitCode). See $outputRoot\aetheria-import.error.log"
$importArguments = @(
"run", "--project", $importProject,
"-p:CultLibRoot=$CultLibRoot", "-p:EveUnityRoot=$EveUnityRoot",
"--", $AetheriaRoot, $statePath
)
$importLogPath = Join-Path $outputRoot "aetheria-import.log"
$importErrorLogPath = Join-Path $outputRoot "aetheria-import.error.log"
$previousErrorActionPreference = $ErrorActionPreference
try {
$ErrorActionPreference = "Continue"
& dotnet @importArguments 1> $importLogPath 2> $importErrorLogPath
$importExitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $previousErrorActionPreference
}
if ($importExitCode -ne 0) {
throw "Aetheria state import failed with exit code $importExitCode. See $importErrorLogPath"
}
if (-not $SkipAssetBundleBuild) {
$bundleBuilder = Start-Process -FilePath $UnityExe -ArgumentList @(
@ -83,7 +97,9 @@ if (-not $SkipAssetBundleBuild) {
}
$daemonArguments = @(
"run", "--project", $daemonProject, "--",
"run", "--project", $daemonProject,
"-p:CultLibRoot=$CultLibRoot", "-p:EveUnityRoot=$EveUnityRoot",
"--",
"--root", $AetheriaRoot,
"--state", $statePath,
"--client-cultmesh-host", "127.0.0.1",
@ -112,10 +128,12 @@ try {
if (-not $ready) { throw "Aetheria daemon did not open CultMesh port $Port. See $daemonLogPath" }
$env:EVEUNITY_RENDEZVOUS_ENDPOINT = "rudp://127.0.0.1:$Port"
Remove-Item Env:EVEUNITY_PROVIDER_ID -ErrorAction SilentlyContinue
$env:EVEUNITY_PROVIDER_ID = "aetheria"
Remove-Item Env:EVEUNITY_SURFACE_ID -ErrorAction SilentlyContinue
$env:EVEUNITY_REPLICA_PATH = $replicaPath
$env:EVEUNITY_AETHERIA_CAPTURE_PATH = $capturePath
$env:EVEUNITY_AETHERIA_MAP_CAPTURE_PATH = $mapCapturePath
$env:EVEUNITY_DISABLE_AUTO_LAUNCHER = "1"
$env:EVEUNITY_ASSET_CACHE_PATH = $assetCachePath
$env:EVEUNITY_WITNESS_FACTS_PATH = $factsPath
$arguments = @(
@ -135,6 +153,7 @@ try {
$run = $results.SelectSingleNode("//test-run")
if ($null -eq $run -or [int]$run.passed -ne 1 -or [int]$run.failed -ne 0) { throw "Live world witness did not pass: $resultsPath" }
if (-not (Test-Path $capturePath) -or (Get-Item $capturePath).Length -lt 1024) { throw "Live world capture is missing: $capturePath" }
if (-not (Test-Path $mapCapturePath) -or (Get-Item $mapCapturePath).Length -lt 1024) { throw "Live map-channel capture is missing: $mapCapturePath" }
if (-not (Test-Path $factsPath)) { throw "Live world witness facts are missing: $factsPath" }
$facts = Get-Content -LiteralPath $factsPath -Raw | ConvertFrom-Json
$releaseLock = Get-Content -LiteralPath (Join-Path $repoRoot "ReleaseConsumerProject\Packages\packages-lock.json") -Raw | ConvertFrom-Json
@ -144,6 +163,7 @@ try {
$facts | Add-Member -NotePropertyName eveUnityPackageCommit -NotePropertyValue $releaseLock.dependencies.'org.gamecult.eve.unity-scene'.hash
$facts | Add-Member -NotePropertyName cultLibPackageCommit -NotePropertyValue $releaseLock.dependencies.'org.gamecult.cultlib'.hash
$capture = Get-Item -LiteralPath $capturePath
$mapCapture = Get-Item -LiteralPath $mapCapturePath
$resultsArtifact = Get-Item -LiteralPath $resultsPath
$durationMs = [Math]::Round(([DateTimeOffset]::UtcNow - $witnessStartedAt).TotalMilliseconds, 3)
$testDurationMs = [Math]::Round(([double]$run.duration) * 1000, 3)
@ -152,7 +172,7 @@ try {
witnessId = "eveunity.aetheria.game.$observedCacheState"
runtimeId = "unity-scene"
runtimeOwnerRepo = "EveUnity"
providerId = "aetheria.daemon"
providerId = "aetheria"
surfaceId = "aetheria.game"
projectionKind = "provider-authored-world-surface"
status = "pass"
@ -169,6 +189,9 @@ try {
height = 360
encodedSizeBytes = $capture.Length
sha256 = (Get-FileHash -LiteralPath $capture.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
mapEncodedSizeBytes = $mapCapture.Length
mapSha256 = (Get-FileHash -LiteralPath $mapCapture.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
mapChangedPixels = $facts.mapChangedPixels
}
artifacts = @(
[ordered]@{
@ -179,6 +202,14 @@ try {
width = 640
height = 360
},
[ordered]@{
kind = "unity-map-channel-png"
path = $mapCapture.Name
sha256 = (Get-FileHash -LiteralPath $mapCapture.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
sizeBytes = $mapCapture.Length
width = 640
height = 360
},
[ordered]@{
kind = "unity-test-results"
path = $resultsArtifact.Name
@ -193,11 +224,12 @@ try {
$witness | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $stateWitnessPath -Encoding UTF8
Write-Host "Aetheria daemon world witness: passed"
Write-Host "Capture: $capturePath"
Write-Host "Map capture: $mapCapturePath"
Write-Host "Runtime witness: $witnessPath"
Write-Host "Cache-state witness: $stateWitnessPath"
}
finally {
if ($null -ne $daemon -and -not $daemon.HasExited) { Stop-Process -Id $daemon.Id -Force }
Remove-Item Env:EVEUNITY_RENDEZVOUS_ENDPOINT, Env:EVEUNITY_PROVIDER_ENDPOINT, Env:EVEUNITY_PROVIDER_ID, Env:EVEUNITY_SURFACE_ID, Env:EVEUNITY_REPLICA_PATH, Env:EVEUNITY_AETHERIA_CAPTURE_PATH, Env:EVEUNITY_ASSET_CACHE_PATH, Env:EVEUNITY_WITNESS_FACTS_PATH -ErrorAction SilentlyContinue
Remove-Item Env:EVEUNITY_RENDEZVOUS_ENDPOINT, Env:EVEUNITY_PROVIDER_ENDPOINT, Env:EVEUNITY_PROVIDER_ID, Env:EVEUNITY_SURFACE_ID, Env:EVEUNITY_REPLICA_PATH, Env:EVEUNITY_AETHERIA_CAPTURE_PATH, Env:EVEUNITY_AETHERIA_MAP_CAPTURE_PATH, Env:EVEUNITY_DISABLE_AUTO_LAUNCHER, Env:EVEUNITY_ASSET_CACHE_PATH, Env:EVEUNITY_WITNESS_FACTS_PATH -ErrorAction SilentlyContinue
Remove-Item Env:AETHERIA_TRACE_EVE_SNAPSHOTS -ErrorAction SilentlyContinue
}