Ship generic CultMesh playable world client
This commit is contained in:
parent
9700446b44
commit
7e65154d7f
9 changed files with 185 additions and 29 deletions
|
|
@ -13,6 +13,10 @@ discover a Verse from one CultMesh rendezvous endpoint, select an advertised
|
|||
Aetheria's daemon-published 3D ARPG surface is the product witness; no Aetheria
|
||||
assembly is part of the client.
|
||||
|
||||
`EveUnityCultMeshPlayableWorldProvider` is the shipped composition root. Its
|
||||
provider, surface, and Verse filters default to empty, so a client can select by
|
||||
the generic `interactive-world` contract alone.
|
||||
|
||||
```powershell
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\run-package-tests.ps1
|
||||
powershell -ExecutionPolicy Bypass -File .\scripts\pack.ps1
|
||||
|
|
|
|||
|
|
@ -180,8 +180,7 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
var lightObject = new GameObject("Generic Eve World Light");
|
||||
RenderTexture target = null;
|
||||
Texture2D pixels = null;
|
||||
EveUnityCultMeshLiveProviderTransport transport = null;
|
||||
EveUnitySceneLiveProviderBridge bridge = null;
|
||||
EveUnityCultMeshPlayableWorldProvider provider = null;
|
||||
EveUnityPlayableWorldRuntime runtime = null;
|
||||
WitnessReceipt movementReceipt = null;
|
||||
WitnessReceipt focusReceipt = null;
|
||||
|
|
@ -191,27 +190,21 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
|
||||
try
|
||||
{
|
||||
var selection = new EveUnityCultMeshProviderDiscovery().Discover(
|
||||
provider = root.AddComponent<EveUnityCultMeshPlayableWorldProvider>();
|
||||
provider.Configure(
|
||||
rendezvousEndpoint,
|
||||
replicaPath,
|
||||
providerId,
|
||||
surfaceId,
|
||||
"interactive-world");
|
||||
Assert.That(selection.VerseId, Is.Not.Empty);
|
||||
Assert.That(selection.ProviderId, Is.Not.Empty);
|
||||
Assert.That(selection.SurfaceId, Is.Not.Empty);
|
||||
transport = new EveUnityCultMeshLiveProviderTransport(
|
||||
replicaPath,
|
||||
selection.Endpoint,
|
||||
selection.ProviderId,
|
||||
selection.SurfaceId,
|
||||
$"eve-unity-test-{Guid.NewGuid():N}");
|
||||
requiredSurfaceKind: "interactive-world",
|
||||
clientRuntimeId: $"eve-unity-test-{Guid.NewGuid():N}");
|
||||
var publicationDeadline = Time.realtimeSinceStartup + 10f;
|
||||
while (true)
|
||||
{
|
||||
var published = false;
|
||||
try
|
||||
{
|
||||
transport.Refresh();
|
||||
provider.Refresh();
|
||||
published = true;
|
||||
}
|
||||
catch (InvalidOperationException) when (Time.realtimeSinceStartup < publicationDeadline)
|
||||
|
|
@ -220,15 +213,17 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
if (published) break;
|
||||
yield return new WaitForSecondsRealtime(0.1f);
|
||||
}
|
||||
bridge = new EveUnitySceneLiveProviderBridge(transport);
|
||||
bridge.Connect();
|
||||
Assert.That(provider.Selection, Is.Not.Null);
|
||||
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(
|
||||
root.transform,
|
||||
bridge,
|
||||
bridge,
|
||||
assetManifestDocuments: null,
|
||||
receiptSource: bridge,
|
||||
fallbackAssetProvider: transport);
|
||||
provider,
|
||||
provider,
|
||||
null,
|
||||
provider,
|
||||
provider);
|
||||
var initial = runtime.Connect();
|
||||
Assert.That(initial.ActiveEntities, Is.GreaterThan(0));
|
||||
Assert.That(runtime.ActiveWorld.PlayerEntityId, Is.Not.Empty);
|
||||
|
|
@ -357,8 +352,6 @@ namespace GameCult.EveUnity.GenericClient.PlayModeTests
|
|||
if (target != null) UnityEngine.Object.DestroyImmediate(target);
|
||||
if (pixels != null) UnityEngine.Object.DestroyImmediate(pixels);
|
||||
runtime?.Dispose();
|
||||
bridge?.Dispose();
|
||||
transport?.Dispose();
|
||||
UnityEngine.Object.DestroyImmediate(lightObject);
|
||||
UnityEngine.Object.DestroyImmediate(cameraObject);
|
||||
UnityEngine.Object.DestroyImmediate(root);
|
||||
|
|
|
|||
|
|
@ -7,5 +7,10 @@ Providers implement the surface-document, asset-manifest, command-sink, and
|
|||
receipt-source ports. `EveUnityPlayableWorldClientBootstrap` wires those ports
|
||||
to the generic scene host, input driver, camera rig, and `GameObject` sink.
|
||||
|
||||
The package never imports provider product assemblies.
|
||||
`EveUnityCultMeshPlayableWorldProvider` is the runtime composition root for a
|
||||
networked client. Give it one Verse rendezvous endpoint and it discovers an
|
||||
advertised `interactive-world`, constructs the generic CultMesh transport, and
|
||||
provides every bootstrap port. Provider, surface, and Verse IDs are optional
|
||||
selection filters; they are not product knowledge required by the client.
|
||||
|
||||
The package never imports provider product assemblies.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using GameCult.Eve.Surface;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace GameCult.Eve.UnityScene
|
||||
{
|
||||
public sealed class EveUnityCultMeshPlayableWorldProvider :
|
||||
MonoBehaviour,
|
||||
IEveUnitySceneProviderSurfaceDocumentSource,
|
||||
IEveUnitySceneProviderSurfaceDocumentConnection,
|
||||
IEveUnitySceneCommandSink,
|
||||
IEveUnitySceneCommandReceiptSource,
|
||||
IEveUnityProviderRefreshSource,
|
||||
IEveUnityGameObjectAssetProvider
|
||||
{
|
||||
[SerializeField] private string rendezvousEndpoint = "";
|
||||
[SerializeField] private string providerFilter = "";
|
||||
[SerializeField] private string surfaceFilter = "";
|
||||
[SerializeField] private string verseFilter = "";
|
||||
[SerializeField] private string surfaceKind = "interactive-world";
|
||||
[SerializeField] private string replicaPath = "";
|
||||
[SerializeField] private string runtimeId = "eve-unity";
|
||||
|
||||
private EveUnityCultMeshLiveProviderTransport? _transport;
|
||||
private EveUnitySceneLiveProviderBridge? _bridge;
|
||||
|
||||
public EveUnityCultMeshProviderSelection? Selection { get; private set; }
|
||||
|
||||
public string SinkKind => Bridge.SinkKind;
|
||||
|
||||
public EveUnitySceneProviderSurfaceDocument CurrentDocument => Bridge.CurrentSurfaceDocument;
|
||||
|
||||
public event Action<EveUnitySceneProviderSurfaceDocument>? DocumentAvailable
|
||||
{
|
||||
add { Bridge.DocumentAvailable += value; }
|
||||
remove { Bridge.DocumentAvailable -= value; }
|
||||
}
|
||||
|
||||
public event Action<EveUnitySceneCommandReceipt>? ReceiptAvailable
|
||||
{
|
||||
add { Bridge.ReceiptAvailable += value; }
|
||||
remove { Bridge.ReceiptAvailable -= value; }
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
string endpoint,
|
||||
string replicaStorePath = "",
|
||||
string providerId = "",
|
||||
string surfaceId = "",
|
||||
string verseId = "",
|
||||
string requiredSurfaceKind = "interactive-world",
|
||||
string clientRuntimeId = "eve-unity")
|
||||
{
|
||||
if (_bridge != null)
|
||||
throw new InvalidOperationException("Disconnect the active provider before changing discovery configuration.");
|
||||
|
||||
rendezvousEndpoint = endpoint ?? "";
|
||||
replicaPath = replicaStorePath ?? "";
|
||||
providerFilter = providerId ?? "";
|
||||
surfaceFilter = surfaceId ?? "";
|
||||
verseFilter = verseId ?? "";
|
||||
surfaceKind = string.IsNullOrWhiteSpace(requiredSurfaceKind) ? "interactive-world" : requiredSurfaceKind;
|
||||
runtimeId = string.IsNullOrWhiteSpace(clientRuntimeId) ? "eve-unity" : clientRuntimeId;
|
||||
}
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
Bridge.Connect();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
_bridge?.Disconnect();
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
Bridge.Connect();
|
||||
Bridge.Refresh();
|
||||
}
|
||||
|
||||
public void Submit(EveSurfaceCommandRequest request)
|
||||
{
|
||||
Bridge.Submit(request);
|
||||
}
|
||||
|
||||
public GameObject? ResolvePrefab(EveUnityPlayableWorldAssetBinding asset)
|
||||
{
|
||||
EnsureTransport();
|
||||
return _transport!.ResolvePrefab(asset);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_bridge?.Dispose();
|
||||
_transport?.Dispose();
|
||||
_bridge = null;
|
||||
_transport = null;
|
||||
Selection = null;
|
||||
}
|
||||
|
||||
private EveUnitySceneLiveProviderBridge Bridge
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureTransport();
|
||||
return _bridge!;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureTransport()
|
||||
{
|
||||
if (_transport != null)
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(rendezvousEndpoint))
|
||||
throw new InvalidOperationException("EveUnity requires a CultMesh rendezvous endpoint.");
|
||||
|
||||
Selection = new EveUnityCultMeshProviderDiscovery().Discover(
|
||||
rendezvousEndpoint,
|
||||
providerFilter,
|
||||
surfaceFilter,
|
||||
surfaceKind,
|
||||
verseFilter);
|
||||
var resolvedReplicaPath = string.IsNullOrWhiteSpace(replicaPath)
|
||||
? Path.Combine(Application.temporaryCachePath, $"eve-unity-{GetInstanceID()}.cc")
|
||||
: replicaPath;
|
||||
_transport = new EveUnityCultMeshLiveProviderTransport(
|
||||
resolvedReplicaPath,
|
||||
Selection.Endpoint,
|
||||
Selection.ProviderId,
|
||||
Selection.SurfaceId,
|
||||
runtimeId);
|
||||
_bridge = new EveUnitySceneLiveProviderBridge(_transport);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 984ba05ee3424bb2a2eb147465a3f17d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -58,7 +58,7 @@ namespace GameCult.Eve.UnityScene
|
|||
resolvedProvider);
|
||||
var resolvedFallbackAssetProvider = ResolveOptionalBehaviour<IEveUnityGameObjectAssetProvider>(
|
||||
fallbackAssetProvider,
|
||||
null);
|
||||
resolvedProvider);
|
||||
|
||||
sceneRoot = ResolveSceneRoot();
|
||||
resolvedHost.ConnectOnEnable = false;
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"supportedFeatures": [
|
||||
"providerAdvertisements",
|
||||
"verseDiscovery",
|
||||
"genericPlayableWorldDiscovery",
|
||||
"commandTransport",
|
||||
"providerSurfaceSession",
|
||||
"providerSurfaceSource",
|
||||
|
|
@ -86,6 +87,7 @@
|
|||
"packages/org.gamecult.eve.unity-scene/Runtime/EveUnitySceneProviderConnection.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/EveUnitySceneLiveProviderBridge.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshProviderDiscovery.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/CultMesh/EveUnityCultMeshPlayableWorldProvider.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldLiveClient.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldRuntime.cs",
|
||||
"packages/org.gamecult.eve.unity-scene/Runtime/EveUnityPlayableWorldClientHost.cs",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "org.gamecult.eve.unity-scene",
|
||||
"displayName": "Eve Unity Scene Lowering",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"unity": "2021.3",
|
||||
"description": "Provider-agnostic Unity scene shell for gamecult.eve.surface.v1 interactive world surfaces.",
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
param(
|
||||
[string] $UnityExe = "C:\Program Files\Unity\Hub\Editor\6000.4.2f1\Editor\Unity.exe",
|
||||
[string] $AetheriaRoot = "E:\Projects\Aetheria",
|
||||
[string] $ClientProject = "ReleaseConsumerProject",
|
||||
[int] $Port = 3076,
|
||||
[string] $OutputDirectory = "artifacts\aetheria-daemon",
|
||||
[ValidateSet("auto", "cold", "warm")]
|
||||
|
|
@ -10,7 +11,7 @@ param(
|
|||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$projectRoot = "ReleaseConsumerProject"
|
||||
$projectRoot = $ClientProject
|
||||
$outputRoot = if ([IO.Path]::IsPathRooted($OutputDirectory)) { $OutputDirectory } else { Join-Path $repoRoot $OutputDirectory }
|
||||
$resultsPath = Join-Path $outputRoot "results.xml"
|
||||
$unityLogPath = Join-Path $outputRoot "unity.log"
|
||||
|
|
@ -126,8 +127,9 @@ try {
|
|||
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
|
||||
$facts | Add-Member -NotePropertyName releasedPackageClient -NotePropertyValue $true
|
||||
$facts | Add-Member -NotePropertyName clientProject -NotePropertyValue "ReleaseConsumerProject"
|
||||
$releasedPackageClient = $projectRoot -eq "ReleaseConsumerProject"
|
||||
$facts | Add-Member -NotePropertyName releasedPackageClient -NotePropertyValue $releasedPackageClient
|
||||
$facts | Add-Member -NotePropertyName clientProject -NotePropertyValue $projectRoot
|
||||
$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
|
||||
|
|
|
|||
Reference in a new issue