Compare commits

...
Author SHA1 Message Date
Meta Vens
e5556c3194 Keep live entity transforms body-owned 2026-07-22 01:02:30 +01:00
Meta Vens
8d0789ba0d Witness the mounted provider cockpit 2026-07-20 05:22:52 +01:00
Meta Vens
c0670fd07c Pin visibly themed Eve cockpit release 2026-07-20 05:06:23 +01:00
9 changed files with 95 additions and 22 deletions

View file

@ -331,15 +331,19 @@ 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");
var mountedOverlay = root.GetComponent<EveUnityUiToolkitOverlay>();
Assert.That(mountedOverlay, Is.Not.Null);
var cockpit = mountedOverlay.Root.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(7));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI.");
Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the mounted pilot UI.");
Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.main_menu").style.display.value,
Is.EqualTo(DisplayStyle.None), "The closed main menu leaked into the mounted pilot UI.");
Assert.That(provider.CurrentDocument.SurfaceDocument.Surface.Root.Children
.Single(component => component.Id == "aetheria.daemon.game.frame").Layout["display"],
Is.EqualTo("none"), "Daemon diagnostics leaked into the pilot UI.");
trajectories = root.GetComponent<EveUnityShotTrajectoryRenderer>();
Assert.That(trajectories, Is.Not.Null);
host.ShotAvailable += shot =>
@ -956,6 +960,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount,
Is.GreaterThanOrEqualTo(temporalSettlingFrames),
"The pilot capture ran before the advertised temporal reconstruction had settled.");
Assert.That(cockpit.resolvedStyle.display, Is.Not.EqualTo(DisplayStyle.None));
Assert.That(cockpit.worldBound.width, Is.GreaterThan(0),
"The mounted provider cockpit has no visible screen layout.");
Debug.Log($"EveUnity witness phase temporal-settling-128-frames took {temporalSettlingElapsed.Elapsed.TotalMilliseconds:0.###}ms.");
var pilotCaptureElapsed = Stopwatch.StartNew();
yield return null;

View file

@ -9,7 +9,7 @@
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43",
"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.2.3",
"org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.4",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.102",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.103",
"org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.2"
},
"testables": [

View file

@ -144,7 +144,7 @@
"hash": "e08fa08335f99e9edddeb706912eecfad07cb281"
},
"org.gamecult.eve.unity-scene": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.102",
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.103",
"depth": 0,
"source": "git",
"dependencies": {
@ -156,7 +156,7 @@
"com.unity.render-pipelines.universal": "17.4.0",
"com.unity.modules.particlesystem": "1.0.0"
},
"hash": "ddd2c5871f935978a0640d43cb58e18579523125"
"hash": "050b2019bd6faa4275013516870d1ded245ee391"
},
"org.gamecult.eve.unity-uitoolkit": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.2",

View file

@ -331,15 +331,19 @@ 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");
var mountedOverlay = root.GetComponent<EveUnityUiToolkitOverlay>();
Assert.That(mountedOverlay, Is.Not.Null);
var cockpit = mountedOverlay.Root.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(7));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI.");
Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the mounted pilot UI.");
Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.main_menu").style.display.value,
Is.EqualTo(DisplayStyle.None), "The closed main menu leaked into the mounted pilot UI.");
Assert.That(provider.CurrentDocument.SurfaceDocument.Surface.Root.Children
.Single(component => component.Id == "aetheria.daemon.game.frame").Layout["display"],
Is.EqualTo("none"), "Daemon diagnostics leaked into the pilot UI.");
trajectories = root.GetComponent<EveUnityShotTrajectoryRenderer>();
Assert.That(trajectories, Is.Not.Null);
host.ShotAvailable += shot =>
@ -956,6 +960,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount,
Is.GreaterThanOrEqualTo(temporalSettlingFrames),
"The pilot capture ran before the advertised temporal reconstruction had settled.");
Assert.That(cockpit.resolvedStyle.display, Is.Not.EqualTo(DisplayStyle.None));
Assert.That(cockpit.worldBound.width, Is.GreaterThan(0),
"The mounted provider cockpit has no visible screen layout.");
Debug.Log($"EveUnity witness phase temporal-settling-128-frames took {temporalSettlingElapsed.Elapsed.TotalMilliseconds:0.###}ms.");
var pilotCaptureElapsed = Stopwatch.StartNew();
yield return null;

View file

@ -23,14 +23,17 @@ namespace GameCult.Eve.UnityScene
{
private readonly IEveUnityPlayableWorldSceneSink _sceneSink;
private readonly IEveUnityPlayableWorldAssetResolver _assetResolver;
private readonly bool _liveEntityBodyOwner;
private readonly HashSet<string> _knownEntityIds = new HashSet<string>(StringComparer.Ordinal);
public EveUnityPlayableWorldPresenter(
IEveUnityPlayableWorldSceneSink sceneSink,
IEveUnityPlayableWorldAssetResolver assetResolver)
IEveUnityPlayableWorldAssetResolver assetResolver,
bool liveEntityBodyOwner = false)
{
_sceneSink = sceneSink ?? throw new ArgumentNullException(nameof(sceneSink));
_assetResolver = assetResolver ?? throw new ArgumentNullException(nameof(assetResolver));
_liveEntityBodyOwner = liveEntityBodyOwner;
}
public EveUnityPlayableWorldPresentation Apply(EveUnitySceneProjection projection)
@ -46,9 +49,26 @@ namespace GameCult.Eve.UnityScene
{
if (world == null) throw new ArgumentNullException(nameof(world));
var nextEntityIds = new HashSet<string>(StringComparer.Ordinal);
_sceneSink.ConfigureWorld(world);
// A world that advertises a live entity body has one transform and
// membership owner: the SoA generation presenter. The durable Eve
// surface remains configuration/topology and must not periodically
// overwrite realtime body state.
if (_liveEntityBodyOwner &&
(!string.IsNullOrWhiteSpace(world.EntityViewPointerId) ||
!string.IsNullOrWhiteSpace(world.EntityBodyId)))
return new EveUnityPlayableWorldPresentation(
world.WorldRootId,
world.PlayerEntityId,
world.InputProfile,
world.CameraRig,
0,
0,
_knownEntityIds.Count);
var nextEntityIds = new HashSet<string>(StringComparer.Ordinal);
var upserted = 0;
foreach (var entity in world.Entities)
{

View file

@ -41,7 +41,10 @@ namespace GameCult.Eve.UnityScene
_connection = new EveUnitySceneProviderConnection(_surfaceSource, commandSink);
_client = new EveUnityPlayableWorldLiveClient(
_connection,
new EveUnityPlayableWorldPresenter(sceneSink, assetResolver ?? new EveUnityAssetRefResolver()),
new EveUnityPlayableWorldPresenter(
sceneSink,
assetResolver ?? new EveUnityAssetRefResolver(),
liveEntityBodyOwner: _entityViews != null),
receiptSource);
if (assetManifestDocuments != null)

View file

@ -1856,7 +1856,7 @@ namespace GameCult.Eve.UnityScene.Tests
var presenter = new EveUnityPlayableWorldPresenter(sink, new EveUnityAssetRefResolver());
var firstProjection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
var firstPresentation = presenter.Apply(firstProjection);
var firstPresentation = presenter.Apply(SnapshotWorld(firstProjection.PlayableWorld!));
Assert.That(firstPresentation.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
@ -1875,7 +1875,7 @@ namespace GameCult.Eve.UnityScene.Tests
var secondProjection = lowerer.Lower(
PlayableArpgDocument(includeRaider: false, playerPosition: "5,0,2"),
Advertisement("aetheria.daemon.game"));
var secondPresentation = presenter.Apply(secondProjection);
var secondPresentation = presenter.Apply(SnapshotWorld(secondProjection.PlayableWorld!));
Assert.That(secondPresentation.UpsertedEntities, Is.EqualTo(2));
Assert.That(secondPresentation.RemovedEntities, Is.EqualTo(1));
@ -1888,6 +1888,27 @@ namespace GameCult.Eve.UnityScene.Tests
Assert.That(sink.Upserts[3].entity.PositionZ, Is.EqualTo(2f));
}
[Test]
public void PlayableWorldSurfaceCannotOverwriteLiveBodyEntities()
{
var projection = new EveUnitySceneSurfaceLowerer()
.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game"));
var sink = new FakePlayableWorldSceneSink();
var presenter = new EveUnityPlayableWorldPresenter(
sink,
new EveUnityAssetRefResolver(),
liveEntityBodyOwner: true);
var presentation = presenter.Apply(projection);
Assert.That(projection.PlayableWorld!.EntityBodyId, Is.Not.Empty);
Assert.That(sink.ConfiguredWorlds.Count, Is.EqualTo(1));
Assert.That(sink.Upserts, Is.Empty);
Assert.That(sink.RemovedEntityIds, Is.Empty);
Assert.That(presentation.UpsertedEntities, Is.Zero);
Assert.That(presentation.RemovedEntities, Is.Zero);
}
[Test]
public void LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned()
{
@ -2950,6 +2971,21 @@ namespace GameCult.Eve.UnityScene.Tests
}
}
private static EveUnityPlayableWorldProjection SnapshotWorld(EveUnityPlayableWorldProjection source) =>
new EveUnityPlayableWorldProjection(
source.WorldRootId,
source.StatePointerId,
source.AssetManifest,
source.InputProfile,
source.CameraRig,
source.ViewId,
source.PlayerEntityId,
source.MovementCommand,
source.FocusCommand,
source.TargetCommand,
source.ActionCommand,
source.Entities);
private static EveUnityPlayableWorldEntity PlayableEntityModel(
string entityId,
string assetRef,

View file

@ -1,7 +1,7 @@
{
"name": "org.gamecult.eve.unity-scene",
"displayName": "Eve Unity Scene Lowering",
"version": "0.3.103",
"version": "0.3.104",
"unity": "6000.0",
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
"dependencies": {

View file

@ -17,7 +17,7 @@ param(
)
$ErrorActionPreference = "Stop"
$expectedEveUnityCommit = "ddd2c5871f935978a0640d43cb58e18579523125"
$expectedEveUnityCommit = "050b2019bd6faa4275013516870d1ded245ee391"
$expectedEveFieldsCommit = "c5a4a75c1b727499b16c2dae1895f29e2a9f72f0"
$expectedEveUnityUiToolkitCommit = "44390549775ec535ee31fc60a5bc594f372f4147"
$expectedCultLibCommit = "f67f5122ed1bd11da016e7b820ed60145ccd0299"