Compare commits

...
This repository has been archived on 2026-10-29. You can view files and clone it, but you cannot make any changes to its state, such as pushing and creating new issues, pull requests or comments.
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,
provider); provider);
host.Connect(); host.Connect();
var loweredSurface = new EveUiToolkitSurfaceLowerer().Lower( var mountedOverlay = root.GetComponent<EveUnityUiToolkitOverlay>();
provider.CurrentDocument.SurfaceDocument, Assert.That(mountedOverlay, Is.Not.Null);
provider.Submit); var cockpit = mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.cockpit");
var cockpit = loweredSurface.Q<VisualElement>("aetheria.daemon.game.cockpit");
Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay."); Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay.");
cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count; cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count;
Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(7)); Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(7));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value, Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI."); 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>(); trajectories = root.GetComponent<EveUnityShotTrajectoryRenderer>();
Assert.That(trajectories, Is.Not.Null); Assert.That(trajectories, Is.Not.Null);
host.ShotAvailable += shot => host.ShotAvailable += shot =>
@ -956,6 +960,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount, Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount,
Is.GreaterThanOrEqualTo(temporalSettlingFrames), Is.GreaterThanOrEqualTo(temporalSettlingFrames),
"The pilot capture ran before the advertised temporal reconstruction had settled."); "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."); Debug.Log($"EveUnity witness phase temporal-settling-128-frames took {temporalSettlingElapsed.Elapsed.TotalMilliseconds:0.###}ms.");
var pilotCaptureElapsed = Stopwatch.StartNew(); var pilotCaptureElapsed = Stopwatch.StartNew();
yield return null; 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.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.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.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" "org.gamecult.eve.unity-uitoolkit": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.2"
}, },
"testables": [ "testables": [

View file

@ -144,7 +144,7 @@
"hash": "e08fa08335f99e9edddeb706912eecfad07cb281" "hash": "e08fa08335f99e9edddeb706912eecfad07cb281"
}, },
"org.gamecult.eve.unity-scene": { "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, "depth": 0,
"source": "git", "source": "git",
"dependencies": { "dependencies": {
@ -156,7 +156,7 @@
"com.unity.render-pipelines.universal": "17.4.0", "com.unity.render-pipelines.universal": "17.4.0",
"com.unity.modules.particlesystem": "1.0.0" "com.unity.modules.particlesystem": "1.0.0"
}, },
"hash": "ddd2c5871f935978a0640d43cb58e18579523125" "hash": "050b2019bd6faa4275013516870d1ded245ee391"
}, },
"org.gamecult.eve.unity-uitoolkit": { "org.gamecult.eve.unity-uitoolkit": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-uitoolkit#eveunity-uitoolkit-v0.1.2", "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,
provider); provider);
host.Connect(); host.Connect();
var loweredSurface = new EveUiToolkitSurfaceLowerer().Lower( var mountedOverlay = root.GetComponent<EveUnityUiToolkitOverlay>();
provider.CurrentDocument.SurfaceDocument, Assert.That(mountedOverlay, Is.Not.Null);
provider.Submit); var cockpit = mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.cockpit");
var cockpit = loweredSurface.Q<VisualElement>("aetheria.daemon.game.cockpit");
Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay."); Assert.That(cockpit, Is.Not.Null, "The provider did not publish a generic cockpit overlay.");
cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count; cockpitProgressCount = cockpit.Query<ProgressBar>().ToList().Count;
Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(7)); Assert.That(cockpitProgressCount, Is.GreaterThanOrEqualTo(7));
Assert.That(loweredSurface.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value, Assert.That(mountedOverlay.Root.Q<VisualElement>("aetheria.daemon.game.frame").style.display.value,
Is.EqualTo(DisplayStyle.None), "Daemon diagnostics leaked into the pilot UI."); 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>(); trajectories = root.GetComponent<EveUnityShotTrajectoryRenderer>();
Assert.That(trajectories, Is.Not.Null); Assert.That(trajectories, Is.Not.Null);
host.ShotAvailable += shot => host.ShotAvailable += shot =>
@ -956,6 +960,9 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount, Assert.That(captureFieldVolume.CompositeCount - initialCompositeCount,
Is.GreaterThanOrEqualTo(temporalSettlingFrames), Is.GreaterThanOrEqualTo(temporalSettlingFrames),
"The pilot capture ran before the advertised temporal reconstruction had settled."); "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."); Debug.Log($"EveUnity witness phase temporal-settling-128-frames took {temporalSettlingElapsed.Elapsed.TotalMilliseconds:0.###}ms.");
var pilotCaptureElapsed = Stopwatch.StartNew(); var pilotCaptureElapsed = Stopwatch.StartNew();
yield return null; yield return null;

View file

@ -23,14 +23,17 @@ namespace GameCult.Eve.UnityScene
{ {
private readonly IEveUnityPlayableWorldSceneSink _sceneSink; private readonly IEveUnityPlayableWorldSceneSink _sceneSink;
private readonly IEveUnityPlayableWorldAssetResolver _assetResolver; private readonly IEveUnityPlayableWorldAssetResolver _assetResolver;
private readonly bool _liveEntityBodyOwner;
private readonly HashSet<string> _knownEntityIds = new HashSet<string>(StringComparer.Ordinal); private readonly HashSet<string> _knownEntityIds = new HashSet<string>(StringComparer.Ordinal);
public EveUnityPlayableWorldPresenter( public EveUnityPlayableWorldPresenter(
IEveUnityPlayableWorldSceneSink sceneSink, IEveUnityPlayableWorldSceneSink sceneSink,
IEveUnityPlayableWorldAssetResolver assetResolver) IEveUnityPlayableWorldAssetResolver assetResolver,
bool liveEntityBodyOwner = false)
{ {
_sceneSink = sceneSink ?? throw new ArgumentNullException(nameof(sceneSink)); _sceneSink = sceneSink ?? throw new ArgumentNullException(nameof(sceneSink));
_assetResolver = assetResolver ?? throw new ArgumentNullException(nameof(assetResolver)); _assetResolver = assetResolver ?? throw new ArgumentNullException(nameof(assetResolver));
_liveEntityBodyOwner = liveEntityBodyOwner;
} }
public EveUnityPlayableWorldPresentation Apply(EveUnitySceneProjection projection) public EveUnityPlayableWorldPresentation Apply(EveUnitySceneProjection projection)
@ -46,9 +49,26 @@ namespace GameCult.Eve.UnityScene
{ {
if (world == null) throw new ArgumentNullException(nameof(world)); if (world == null) throw new ArgumentNullException(nameof(world));
var nextEntityIds = new HashSet<string>(StringComparer.Ordinal);
_sceneSink.ConfigureWorld(world); _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; var upserted = 0;
foreach (var entity in world.Entities) foreach (var entity in world.Entities)
{ {

View file

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

View file

@ -1856,7 +1856,7 @@ namespace GameCult.Eve.UnityScene.Tests
var presenter = new EveUnityPlayableWorldPresenter(sink, new EveUnityAssetRefResolver()); var presenter = new EveUnityPlayableWorldPresenter(sink, new EveUnityAssetRefResolver());
var firstProjection = lowerer.Lower(PlayableArpgDocument(), Advertisement("aetheria.daemon.game")); 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.WorldRootId, Is.EqualTo("aetheria.daemon.game.playable"));
Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard")); Assert.That(firstPresentation.PlayerEntityId, Is.EqualTo("player-vanguard"));
@ -1875,7 +1875,7 @@ namespace GameCult.Eve.UnityScene.Tests
var secondProjection = lowerer.Lower( var secondProjection = lowerer.Lower(
PlayableArpgDocument(includeRaider: false, playerPosition: "5,0,2"), PlayableArpgDocument(includeRaider: false, playerPosition: "5,0,2"),
Advertisement("aetheria.daemon.game")); 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.UpsertedEntities, Is.EqualTo(2));
Assert.That(secondPresentation.RemovedEntities, Is.EqualTo(1)); 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)); 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] [Test]
public void LivePlayableWorldClientPresentsProviderSnapshotsAndKeepsCommandsProviderOwned() 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( private static EveUnityPlayableWorldEntity PlayableEntityModel(
string entityId, string entityId,
string assetRef, string assetRef,

View file

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

View file

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