Prove the released generic Aetheria cockpit

This commit is contained in:
Meta Vens 2026-07-15 09:49:31 +01:00
commit 3b9e2a922f
12 changed files with 186 additions and 12 deletions

View file

@ -0,0 +1,20 @@
using System.IO;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;
public static class GenericEveUnityBuild
{
public static void BuildWindows()
{
const string scenePath = "Assets/GenericEveUnity.unity";
var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
new GameObject("Generic EveUnity Client").AddComponent<GenericEveUnityLauncher>();
EditorSceneManager.SaveScene(scene, scenePath);
Directory.CreateDirectory("Build/Windows");
var report = BuildPipeline.BuildPlayer(new[] { scenePath }, "Build/Windows/EveUnity.exe", BuildTarget.StandaloneWindows64, BuildOptions.Development);
if (report.summary.result != UnityEditor.Build.Reporting.BuildResult.Succeeded)
throw new System.InvalidOperationException("Generic EveUnity Windows build failed: " + report.summary.result);
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8fcd5fd63f21489eb5a70306d22ed4a4

View file

@ -0,0 +1,96 @@
using System;
using System.Collections;
using System.IO;
using GameCult.Eve.UnityUIToolkit;
using GameCult.Eve.UnityScene;
using UnityEngine;
using UnityEngine.UIElements;
public sealed class GenericEveUnityLauncher : MonoBehaviour
{
private EveUnityCultMeshPlayableWorldProvider _provider;
private EveUnityPlayableWorldClientBootstrap _bootstrap;
private EveUiToolkitSurfaceLowerer _surfaceLowerer;
private UIDocument _surfaceDocument;
private PanelSettings _panelSettings;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static void StartClient()
{
if (string.Equals(Environment.GetEnvironmentVariable("EVEUNITY_DISABLE_AUTO_LAUNCHER"), "1", StringComparison.Ordinal))
return;
if (FindObjectOfType<GenericEveUnityLauncher>() != null) return;
new GameObject("Generic EveUnity Client").AddComponent<GenericEveUnityLauncher>();
}
private void Awake()
{
Application.runInBackground = true;
var endpoint = Environment.GetEnvironmentVariable("EVEUNITY_RENDEZVOUS_ENDPOINT") ?? "rudp://127.0.0.1:3076";
var surface = Environment.GetEnvironmentVariable("EVEUNITY_SURFACE_ID") ?? "aetheria.pilot";
var replica = Path.Combine(Application.persistentDataPath, "eve-unity-client.cc");
var cameraObject = new GameObject("Main Camera") { tag = "MainCamera" };
var camera = cameraObject.AddComponent<Camera>();
camera.clearFlags = CameraClearFlags.SolidColor;
camera.backgroundColor = new Color(0.025f, 0.035f, 0.055f);
camera.nearClipPlane = 0.05f;
camera.farClipPlane = 10000f;
_provider = gameObject.AddComponent<EveUnityCultMeshPlayableWorldProvider>();
_provider.Configure(endpoint, replica, surfaceId: surface, requiredSurfaceKind: "interactive-world", clientRuntimeId: "eve-unity-interactive");
_provider.DocumentAvailable += OnSurfaceAvailable;
_surfaceLowerer = new EveUiToolkitSurfaceLowerer();
_panelSettings = ScriptableObject.CreateInstance<PanelSettings>();
_panelSettings.scaleMode = PanelScaleMode.ScaleWithScreenSize;
_panelSettings.referenceResolution = new Vector2Int(1920, 1080);
_panelSettings.sortingOrder = 20;
_surfaceDocument = gameObject.AddComponent<UIDocument>();
_surfaceDocument.panelSettings = _panelSettings;
_bootstrap = gameObject.AddComponent<EveUnityPlayableWorldClientBootstrap>();
_bootstrap.ConfigureProvider(_provider);
StartCoroutine(Connect());
}
private IEnumerator Connect()
{
while (true)
{
var connected = false;
try
{
var presentation = _bootstrap.Mount();
LowerSurface(_provider.CurrentDocument);
Debug.Log($"Connected generic Eve client: {_provider.Selection?.ProviderId} / {_provider.Selection?.SurfaceId} / {presentation.ActiveEntities} entities");
connected = true;
}
catch (Exception ex)
{
Debug.Log("Waiting for Eve provider: " + ex.Message);
}
if (connected) yield break;
yield return new WaitForSecondsRealtime(0.5f);
}
}
private void OnSurfaceAvailable(EveUnitySceneProviderSurfaceDocument document)
{
LowerSurface(document);
}
private void LowerSurface(EveUnitySceneProviderSurfaceDocument document)
{
if (_surfaceDocument == null || _surfaceLowerer == null || document == null) return;
var root = _surfaceDocument.rootVisualElement;
root.Clear();
var lowered = _surfaceLowerer.Lower(document.SurfaceDocument, _provider.Submit);
lowered.style.flexGrow = 1;
root.Add(lowered);
}
private void OnDestroy()
{
if (_provider != null) _provider.DocumentAvailable -= OnSurfaceAvailable;
if (_panelSettings != null) Destroy(_panelSettings);
}
}

View file

@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 5c2c8060cff846cda741810156b0558a

View file

@ -4,6 +4,7 @@
"references": [
"GameCult.CultLib",
"GameCult.Eve.Surface",
"GameCult.Eve.UnityUIToolkit",
"GameCult.Eve.UnityScene",
"GameCult.Eve.UnityScene.CultMesh"
],

View file

@ -6,10 +6,12 @@ using System.Linq;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Eve.Surface;
using GameCult.Eve.UnityUIToolkit;
using GameCult.Eve.UnityScene;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UIElements;
namespace GameCult.EveUnity.GenericClient.PlayModeTests
{
@ -197,6 +199,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
long initialVersion = 0;
float movementDistance = 0f;
float aimDotDistance = 0f;
int cockpitProgressCount = 0;
try
{
@ -236,6 +239,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
provider,
provider);
host.Connect();
var loweredSurface = new EveUiToolkitSurfaceLowerer().Lower(
provider.CurrentDocument.SurfaceDocument,
provider.Submit);
var cockpit = loweredSurface.Q<VisualElement>("aetheria.daemon.game.cockpit");
Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay.");
cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count;
Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(6));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI.");
host.ShotAvailable += shot => observedShot = shot;
runtime = host.Runtime;
Assert.That(runtime, Is.Not.Null);
@ -575,6 +587,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
directionalLightIntensity,
pilotAverageLuminance,
pilotBrightPixelCount,
cockpitProgressCount,
playerRendererFacts);
}
finally
@ -621,6 +634,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
float directionalLightIntensity,
float pilotAverageLuminance,
int pilotBrightPixelCount,
int cockpitProgressCount,
RendererFact[] playerRendererFacts)
{
var path = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_FACTS_PATH");
@ -657,6 +671,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
pilotChangedPixels = pilotChangedPixels,
pilotAverageLuminance = pilotAverageLuminance,
pilotBrightPixelCount = pilotBrightPixelCount,
cockpitProgressCount = cockpitProgressCount,
playerRenderers = playerRendererFacts,
mapChannelRendererCount = mapChannelRendererCount,
mapChangedPixels = mapChangedPixels,
@ -701,6 +716,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
public int pilotChangedPixels;
public float pilotAverageLuminance;
public int pilotBrightPixelCount;
public int cockpitProgressCount;
public RendererFact[] playerRenderers;
public int mapChannelRendererCount;
public int mapChangedPixels;

View file

@ -10,7 +10,7 @@
"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.1",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.27",
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0"
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1"
},
"testables": [
"org.gamecult.eve.unity-scene",

View file

@ -154,13 +154,13 @@
"hash": "63a41712c5a68ce835bc1820980fc4786c34eb7b"
},
"org.gamecult.eve.unity-uitoolkit": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.0",
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.1",
"depth": 0,
"source": "git",
"dependencies": {
"org.gamecult.eve.surface": "0.1.0"
},
"hash": "e10821f1cc81f619479c6507438f6a097d1f0ce2"
"hash": "4d0cbe0185bdc4fc65eb63503a7c5cb578539669"
},
"com.unity.modules.assetbundle": {
"version": "1.0.0",

View file

@ -4,6 +4,7 @@
"references": [
"GameCult.CultLib",
"GameCult.Eve.Surface",
"GameCult.Eve.UnityUIToolkit",
"GameCult.Eve.UnityScene",
"GameCult.Eve.UnityScene.CultMesh"
],

View file

@ -6,10 +6,12 @@ using System.Linq;
using GameCult.Caching;
using GameCult.Caching.MessagePack;
using GameCult.Eve.Surface;
using GameCult.Eve.UnityUIToolkit;
using GameCult.Eve.UnityScene;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
using UnityEngine.UIElements;
namespace GameCult.EveUnity.GenericClient.PlayModeTests
{
@ -197,6 +199,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
long initialVersion = 0;
float movementDistance = 0f;
float aimDotDistance = 0f;
int cockpitProgressCount = 0;
try
{
@ -236,6 +239,15 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
provider,
provider);
host.Connect();
var loweredSurface = new EveUiToolkitSurfaceLowerer().Lower(
provider.CurrentDocument.SurfaceDocument,
provider.Submit);
var cockpit = loweredSurface.Q<VisualElement>("aetheria.daemon.game.cockpit");
Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay.");
cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count;
Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(6));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI.");
host.ShotAvailable += shot => observedShot = shot;
runtime = host.Runtime;
Assert.That(runtime, Is.Not.Null);
@ -575,6 +587,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
directionalLightIntensity,
pilotAverageLuminance,
pilotBrightPixelCount,
cockpitProgressCount,
playerRendererFacts);
}
finally
@ -621,6 +634,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
float directionalLightIntensity,
float pilotAverageLuminance,
int pilotBrightPixelCount,
int cockpitProgressCount,
RendererFact[] playerRendererFacts)
{
var path = Environment.GetEnvironmentVariable("EVEUNITY_WITNESS_FACTS_PATH");
@ -657,6 +671,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
pilotChangedPixels = pilotChangedPixels,
pilotAverageLuminance = pilotAverageLuminance,
pilotBrightPixelCount = pilotBrightPixelCount,
cockpitProgressCount = cockpitProgressCount,
playerRenderers = playerRendererFacts,
mapChannelRendererCount = mapChannelRendererCount,
mapChangedPixels = mapChangedPixels,
@ -701,6 +716,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
public int pilotChangedPixels;
public float pilotAverageLuminance;
public int pilotBrightPixelCount;
public int cockpitProgressCount;
public RendererFact[] playerRenderers;
public int mapChannelRendererCount;
public int mapChangedPixels;

View file

@ -6,6 +6,8 @@ The warm-cache released-package witness passes with:
- `org.gamecult.eve.unity-scene` `0.3.27`, commit
`63a41712c5a68ce835bc1820980fc4786c34eb7b`;
- `org.gamecult.eve.unity-uitoolkit` `0.1.1`, commit
`4d0cbe0185bdc4fc65eb63503a7c5cb578539669`;
- `org.gamecult.cultlib` `1.0.13`, commit
`feb5c71513e71d681699f462fe3682b3168c6f73`;
- the generic `ReleaseConsumerProject` client connected directly to the
@ -25,15 +27,28 @@ Aetheria also advertises the original `ARPG.unity` follow-camera composition:
`70` unit distance, `60` degree vertical field of view, target screen position
`0.66,0.55`, and position damping `2`. The generic camera rig lowers those
values without Aetheria-specific camera code.
Aetheria's pilot surface also publishes a transparent cockpit overlay. The
released generic UI Toolkit lowerer produced six native `ProgressBar` elements
for hull, shield, capacitor, weapon cooldown, and target state. Daemon frame,
player, and command diagnostics remain available in the document but lower
with `display: none` in the pilot presentation. The runnable generic-client
launcher mounts the same advertised document and forwards its typed commands;
it contains no Aetheria gameplay or client-authored world light.
The two PNGs below are deliberate camera-channel captures, not a composited
screen capture, so they prove world/map isolation rather than HUD pixels. The
native cockpit proof is the asserted UI Toolkit visual tree recorded as
`cockpitProgressCount: 6` in the witness facts.
Camera-channel facts:
- provider-authored player renderers: `12`;
- pilot changed pixels: `230,169`;
- pilot average luminance: `0.003425`;
- pilot bright pixels: `1,242`;
- pilot changed pixels: `230,229`;
- pilot average luminance: `0.003307`;
- pilot bright pixels: `1,289`;
- map-channel renderers: `11`;
- map changed pixels: `5,153`;
- map changed pixels: `5,396`;
- native cockpit progress bars: `6`;
- the pilot camera excludes the advertised map layer;
- the player prefab's embedded layer-14 map icon contributes exactly `0` pilot
pixels;

View file

@ -14,6 +14,7 @@ param(
$ErrorActionPreference = "Stop"
$expectedEveUnityCommit = "63a41712c5a68ce835bc1820980fc4786c34eb7b"
$expectedEveUnityUiToolkitCommit = "4d0cbe0185bdc4fc65eb63503a7c5cb578539669"
$expectedCultLibCommit = "feb5c71513e71d681699f462fe3682b3168c6f73"
$repoRoot = Split-Path -Parent $PSScriptRoot
$projectRoot = $ClientProject
@ -188,14 +189,17 @@ try {
$releaseLock = Get-Content -LiteralPath (Join-Path $repoRoot "ReleaseConsumerProject\Packages\packages-lock.json") -Raw | ConvertFrom-Json
$releasedPackageClient = $projectRoot -eq "ReleaseConsumerProject"
$sceneLock = $releaseLock.dependencies.'org.gamecult.eve.unity-scene'
$uiToolkitLock = $releaseLock.dependencies.'org.gamecult.eve.unity-uitoolkit'
$cultLibLock = $releaseLock.dependencies.'org.gamecult.cultlib'
if (-not $releasedPackageClient) { throw "Released witness must run ReleaseConsumerProject, got '$projectRoot'." }
if ($sceneLock.source -ne "git" -or $cultLibLock.source -ne "git" -or
$sceneLock.version -like "file:*" -or $cultLibLock.version -like "file:*") {
if ($sceneLock.source -ne "git" -or $uiToolkitLock.source -ne "git" -or $cultLibLock.source -ne "git" -or
$sceneLock.version -like "file:*" -or $uiToolkitLock.version -like "file:*" -or $cultLibLock.version -like "file:*") {
throw "Released witness resolved a non-git/local package dependency."
}
if ($sceneLock.hash -ne $expectedEveUnityCommit -or $cultLibLock.hash -ne $expectedCultLibCommit) {
throw "Released package commits do not match the witnessed releases. scene=$($sceneLock.hash) cultlib=$($cultLibLock.hash)"
if ($sceneLock.hash -ne $expectedEveUnityCommit -or
$uiToolkitLock.hash -ne $expectedEveUnityUiToolkitCommit -or
$cultLibLock.hash -ne $expectedCultLibCommit) {
throw "Released package commits do not match the witnessed releases. scene=$($sceneLock.hash) uitoolkit=$($uiToolkitLock.hash) cultlib=$($cultLibLock.hash)"
}
$finalBodies = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.body -File -Recurse -ErrorAction SilentlyContinue)
$finalPartials = @(Get-ChildItem -LiteralPath $assetCachePath -Filter *.partial -File -Recurse -ErrorAction SilentlyContinue)
@ -215,6 +219,7 @@ try {
$facts | Add-Member -NotePropertyName releasedPackageClient -NotePropertyValue $releasedPackageClient
$facts | Add-Member -NotePropertyName clientProject -NotePropertyValue $projectRoot
$facts | Add-Member -NotePropertyName eveUnityPackageCommit -NotePropertyValue $sceneLock.hash
$facts | Add-Member -NotePropertyName eveUnityUiToolkitPackageCommit -NotePropertyValue $uiToolkitLock.hash
$facts | Add-Member -NotePropertyName cultLibPackageCommit -NotePropertyValue $cultLibLock.hash
$facts | Add-Member -NotePropertyName contentDelivery -NotePropertyValue ([ordered]@{
initialBodyCount = $initialBodyCount
@ -222,7 +227,7 @@ try {
finalBodyCount = $finalBodies.Count
finalPartialCount = $finalPartials.Count
contentHash = $bodyHash
sizeBytes = $finalBodies[0].Length
sizeBytes = $promotedBundleBodies[0].Length
})
$capture = Get-Item -LiteralPath $capturePath
$mapCapture = Get-Item -LiteralPath $mapCapturePath