diff --git a/Aetheria.State.Smoke/Program.cs b/Aetheria.State.Smoke/Program.cs index 2908cdacd..cf8897af2 100644 --- a/Aetheria.State.Smoke/Program.cs +++ b/Aetheria.State.Smoke/Program.cs @@ -1,6 +1,7 @@ using Aetheria.State; using Aetheria.State.Documents; using Aetheria.State.Migration; +using System.Buffers.Binary; using GameCult.Caching; using GameCult.Aetheria.State.Verse; using GameCult.Mesh; @@ -1277,7 +1278,6 @@ static async Task ProveDirectSoaPublicationPipeline() } }; - using var liveBodies = new CultMeshNetworkBodyStore(); using var publisher = new AetheriaRuntimeDaemonSoaFramePublisher(producerEpoch: 9); var built = publisher.BuildCurrentZoneEntities(frame); var payloadIdentity = built.View.Identities.SingleOrDefault(identity => @@ -1301,7 +1301,7 @@ static async Task ProveDirectSoaPublicationPipeline() if (cache.Get(AetheriaRuntimeVerseRecordKeys.EveEntitySoaViewLatest) != null) throw new InvalidOperationException("SoA view became visible before its body publication."); - var published = await publisher.PublishAsync(built); + var published = await publisher.PublishAsync(built, includeRealtimePayload: true); if (cache.Get(AetheriaRuntimeVerseRecordKeys.EveEntitySoaViewLatest) != null) throw new InvalidOperationException("Building a CultMesh body publication made the Eve view visible before commit."); if (cache.GetAll().Any() || cache.GetAll().Any()) @@ -1353,11 +1353,10 @@ static async Task ProveDirectSoaPublicationPipeline() ?? throw new InvalidOperationException("Typed Eve SoA view did not become visible after body publication."); var localRepresentation = body.Representations.SingleOrDefault(candidate => candidate.TransportKind == CultMeshBodyTransportKind.SharedMemory); - var networkRepresentation = body.Representations.SingleOrDefault(candidate => - candidate.TransportKind == CultMeshBodyTransportKind.Network); - if (localRepresentation == null || networkRepresentation == null || + if (localRepresentation == null || published.RealtimePayload.IsEmpty || + body.Representations.Any(candidate => candidate.TransportKind == CultMeshBodyTransportKind.Network) || body.Representations.Any(candidate => candidate.TransportKind == CultMeshBodyTransportKind.SharedFileMapping)) - throw new InvalidOperationException("Direct SoA publication did not expose shared memory plus direct network representations."); + throw new InvalidOperationException("Direct SoA publication did not expose shared memory plus an explicit realtime payload."); var request = new CultMeshBodyValidationRequest { @@ -1373,34 +1372,25 @@ static async Task ProveDirectSoaPublicationPipeline() using var localLease = new CultMeshBodyPublicationResolver(new CultMeshBodyTransportService( new ICultMeshBodyTransportAdapter[] { - new CultMeshSharedMemoryBodyAdapter(), - new CultMeshNetworkBodyAdapter(descriptor => ReadLiveBody(liveBodies, descriptor)) - }, - (producerId, _) => producerId == AetheriaRuntimeDaemonSoaFramePublisher.ProducerId)) - .ResolveReadOnly(body, request); - using var networkLease = new CultMeshBodyPublicationResolver(new CultMeshBodyTransportService( - new ICultMeshBodyTransportAdapter[] - { - new CultMeshNetworkBodyAdapter(descriptor => ReadLiveBody(liveBodies, descriptor)) + new CultMeshSharedMemoryBodyAdapter() }, (producerId, _) => producerId == AetheriaRuntimeDaemonSoaFramePublisher.ProducerId)) .ResolveReadOnly(body, request); var localEntityIndex = ReadInt32(view, localLease, "entity.index", 0); - var networkEntityIndex = ReadInt32(view, networkLease, "entity.index", 0); + var networkEntityIndex = ReadInt32Bytes(view, published.RealtimePayload.Span, "entity.index", 0); var localPosition = ReadFloat3(view, localLease, "transform.position", 0); - var networkPosition = ReadFloat3(view, networkLease, "transform.position", 0); + var networkPosition = ReadFloat3Bytes(view, published.RealtimePayload.Span, "transform.position", 0); var localPayloadEntityIndex = ReadInt32(view, localLease, "entity.index", 2); - var networkPayloadEntityIndex = ReadInt32(view, networkLease, "entity.index", 2); + var networkPayloadEntityIndex = ReadInt32Bytes(view, published.RealtimePayload.Span, "entity.index", 2); var localPayloadPosition = ReadFloat3(view, localLease, "transform.position", 2); - var networkPayloadPosition = ReadFloat3(view, networkLease, "transform.position", 2); + var networkPayloadPosition = ReadFloat3Bytes(view, published.RealtimePayload.Span, "transform.position", 2); var localSunPosition = ReadFloat3(view, localLease, "transform.position", 3); - var networkSunPosition = ReadFloat3(view, networkLease, "transform.position", 3); + var networkSunPosition = ReadFloat3Bytes(view, published.RealtimePayload.Span, "transform.position", 3); var localAsteroidPosition = ReadFloat3(view, localLease, "transform.position", 4); - var networkAsteroidPosition = ReadFloat3(view, networkLease, "transform.position", 4); + var networkAsteroidPosition = ReadFloat3Bytes(view, published.RealtimePayload.Span, "transform.position", 4); var identity = view.Identities.Single(candidate => candidate.Index == localEntityIndex); if (localLease.TransportKind != CultMeshBodyTransportKind.SharedMemory || - networkLease.TransportKind != CultMeshBodyTransportKind.Network || localEntityIndex != 12 || networkEntityIndex != localEntityIndex || localPosition != (1.25f, -2.5f, 7.75f) || networkPosition != localPosition || localPayloadEntityIndex != payloadIdentity.EntityIndex || @@ -1410,7 +1400,7 @@ static async Task ProveDirectSoaPublicationPipeline() localAsteroidPosition.X != 45f || localAsteroidPosition.Z != 50f || networkAsteroidPosition != localAsteroidPosition || identity.EntityId != "ship:soa-witness" || identity.Label != "SoA Witness") - throw new InvalidOperationException("Local and network SoA views were not logically equivalent."); + throw new InvalidOperationException("Shared-memory and realtime SoA payloads were not logically equivalent."); frame.Run.Zones[0].PhysicalPayloads[0].Active = false; var withoutPayload = publisher.BuildCurrentZoneEntities(frame); @@ -1419,25 +1409,6 @@ static async Task ProveDirectSoaPublicationPipeline() throw new InvalidOperationException("Inactive physical payload remained in SoA or disturbed a surviving synthetic identity."); } -static byte[] ReadLiveBody(CultMeshNetworkBodyStore bodies, CultMeshBodyDescriptor descriptor) -{ - var request = new CultMeshBodyReadRequestMessage - { - MessageId = Guid.NewGuid().ToString("N"), - CapabilityToken = descriptor.CapabilityToken, - BodyId = descriptor.BodyId, - BodySchemaId = descriptor.SchemaId, - LayoutVersion = descriptor.LayoutVersion, - ProducerEpoch = descriptor.ProducerEpoch, - Sequence = descriptor.Sequence, - ExpectedSizeBytes = descriptor.ByteSize, - SemanticHash = descriptor.SemanticHash - }; - if (!bodies.TryRead(request, DateTimeOffset.UtcNow, out _, out var body)) - throw new InvalidOperationException("Ephemeral SoA network generation could not be resolved."); - return body; -} - static IEnumerable Flatten(AetheriaRuntimeSurfaceComponent component) { yield return component; @@ -1453,6 +1424,14 @@ static int ReadInt32(EveEntitySoaViewDocument view, ICultMeshBodyReadLease lease return lease.ReadInt32(buffer.ByteOffset + column.ByteOffset + (long)row * column.ElementStride); } +static int ReadInt32Bytes(EveEntitySoaViewDocument view, ReadOnlySpan body, string semantic, int row) +{ + var column = view.Columns.Single(candidate => candidate.Semantic == semantic); + var buffer = view.Buffers.Single(candidate => candidate.BufferId == column.BufferId); + var offset = checked((int)(buffer.ByteOffset + column.ByteOffset + (long)row * column.ElementStride)); + return BinaryPrimitives.ReadInt32LittleEndian(body.Slice(offset, sizeof(int))); +} + static (float X, float Y, float Z) ReadFloat3( EveEntitySoaViewDocument view, ICultMeshBodyReadLease lease, @@ -1464,3 +1443,18 @@ static (float X, float Y, float Z) ReadFloat3( var offset = buffer.ByteOffset + column.ByteOffset + (long)row * column.ElementStride; return (lease.ReadSingle(offset), lease.ReadSingle(offset + 4), lease.ReadSingle(offset + 8)); } + +static (float X, float Y, float Z) ReadFloat3Bytes( + EveEntitySoaViewDocument view, + ReadOnlySpan body, + string semantic, + int row) +{ + var column = view.Columns.Single(candidate => candidate.Semantic == semantic); + var buffer = view.Buffers.Single(candidate => candidate.BufferId == column.BufferId); + var offset = checked((int)(buffer.ByteOffset + column.ByteOffset + (long)row * column.ElementStride)); + return ( + BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(offset, sizeof(float)))), + BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(offset + 4, sizeof(float)))), + BitConverter.Int32BitsToSingle(BinaryPrimitives.ReadInt32LittleEndian(body.Slice(offset + 8, sizeof(float))))); +} diff --git a/Aetheria.Unity/Packages/manifest.json b/Aetheria.Unity/Packages/manifest.json index abd61c3fc..6dd1e7741 100644 --- a/Aetheria.Unity/Packages/manifest.json +++ b/Aetheria.Unity/Packages/manifest.json @@ -1,6 +1,5 @@ { "dependencies": { - "com.gamecult.brokkr": "file:E:/Projects/Brokkr/surfaces/unity/Packages/com.gamecult.brokkr", "com.unity.ide.rider": "3.0.39", "com.unity.test-framework": "1.6.0", "com.unity.modules.assetbundle": "1.0.0", diff --git a/Aetheria.Unity/Packages/packages-lock.json b/Aetheria.Unity/Packages/packages-lock.json index e66349948..8f42c31f8 100644 --- a/Aetheria.Unity/Packages/packages-lock.json +++ b/Aetheria.Unity/Packages/packages-lock.json @@ -1,13 +1,5 @@ { "dependencies": { - "com.gamecult.brokkr": { - "version": "file:E:/Projects/Brokkr/surfaces/unity/Packages/com.gamecult.brokkr", - "depth": 0, - "source": "local", - "dependencies": { - "org.gamecult.cultlib": "1.0.16" - } - }, "com.unity.burst": { "version": "1.8.29", "depth": 2, diff --git a/Assets/Editor/EveAssetBundleBuilder.cs b/Assets/Editor/EveAssetBundleBuilder.cs index 7e53b8f8c..7647b13bd 100644 --- a/Assets/Editor/EveAssetBundleBuilder.cs +++ b/Assets/Editor/EveAssetBundleBuilder.cs @@ -36,8 +36,11 @@ namespace Aetheria.Editor foreach (var stale in Directory.GetFiles(output, "*", SearchOption.TopDirectoryOnly)) File.Delete(stale); - var catalog = AetheriaRuntimeCatalogStore.OpenReadOnly( - Path.GetFullPath(Path.Combine("GameData", "aetheria-world.cc"))); + var configuredCatalog = Environment.GetEnvironmentVariable("AETHERIA_ASSET_CATALOG_STATE"); + var catalogPath = string.IsNullOrWhiteSpace(configuredCatalog) + ? Path.GetFullPath(Path.Combine("GameData", "aetheria-world.cc")) + : Path.GetFullPath(configuredCatalog); + var catalog = AetheriaRuntimeCatalogStore.OpenReadOnly(catalogPath); var assets = AetheriaRuntimeAssets.ProjectManifest(catalog).Assets; foreach (var entry in assets.Where(entry => string.Equals(entry.Ref.Kind, AetheriaRuntimeAssetKinds.Prefab, StringComparison.Ordinal))) diff --git a/Packages/manifest.json b/Packages/manifest.json index 511a9b41d..ea4ebcfcf 100644 --- a/Packages/manifest.json +++ b/Packages/manifest.json @@ -1,6 +1,5 @@ { "dependencies": { - "com.gamecult.brokkr": "file:../../Brokkr/surfaces/unity/Packages/com.gamecult.brokkr/", "com.unity.2d.sprite": "1.0.0", "com.unity.2d.tilemap": "1.0.0", "com.unity.ai.navigation": "2.0.12", @@ -20,7 +19,7 @@ "com.unity.xr.legacyinputhelpers": "3.0.1", "org.gamecult.aetheria.eve-runtime": "file:org.gamecult.aetheria.eve-runtime", "org.gamecult.aetheria.state": "file:org.gamecult.aetheria.state", - "org.gamecult.cultmath": "https://github.com/GameCult/CultMath.git?path=/unity/org.gamecult.cultmath#cultmath-unity-v0.1.1", + "org.gamecult.cultmath": "https://github.com/GameCult/CultMath.git?path=/unity/org.gamecult.cultmath#cultmath-unity-v0.1.2", "org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43", "org.gamecult.eve.surface": "file:../../EveUnity/packages/org.gamecult.eve.surface", "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", diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorSimulation.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorSimulation.cs.meta new file mode 100644 index 000000000..beb8a8055 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorSimulation.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5f127e58b7fe83d41aae3d2c07e92b93 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorStateProjector.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorStateProjector.cs.meta new file mode 100644 index 000000000..4aeaabccf --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeBehaviorStateProjector.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c014edb10e374064983c53c270e56b25 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoCapacityQueries.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoCapacityQueries.cs.meta new file mode 100644 index 000000000..998b64521 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoCapacityQueries.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 65b37579114393648b8c881ae488399b \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoTransactions.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoTransactions.cs.meta new file mode 100644 index 000000000..108ea1ef9 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCargoTransactions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: aeffd152980489d42a76727ed45cadcb \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs.meta new file mode 100644 index 000000000..09b299cba --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 479d0ef759b333b4dae8052bad70eca7 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulationSettings.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulationSettings.cs.meta new file mode 100644 index 000000000..efce654bf --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulationSettings.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7a04293019e69804188e0fd89165928e \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs.meta new file mode 100644 index 000000000..969575684 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEveSurfaceCatalog.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 56b9ad6cb6412fb43a3bca09816f3c1e \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeGameEvents.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeGameEvents.cs.meta new file mode 100644 index 000000000..a63f66566 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeGameEvents.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d6d7dacec60ca5541b27d4edc3e71b67 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeInputCapabilityDocuments.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeInputCapabilityDocuments.cs.meta new file mode 100644 index 000000000..f3d159475 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeInputCapabilityDocuments.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 59cfb14aacc657e42840ff07db266e6d \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeMiningSimulation.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeMiningSimulation.cs.meta new file mode 100644 index 000000000..539d258da --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeMiningSimulation.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: efde765b290564a448c25d1d21635889 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimePickupTransactions.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimePickupTransactions.cs.meta new file mode 100644 index 000000000..71bd6cd43 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimePickupTransactions.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e4f402f9be2534446ae0532a197acd5c \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeProjectilePhysics.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeProjectilePhysics.cs.meta deleted file mode 100644 index f756a4e89..000000000 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeProjectilePhysics.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 5437add74c5d2fb45b81299b41aa999d diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeShotReceipts.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeShotReceipts.cs.meta new file mode 100644 index 000000000..a6fbb95bf --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeShotReceipts.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: aa7945f476afeed42887ab43de504d89 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSurveySimulation.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSurveySimulation.cs.meta new file mode 100644 index 000000000..2049550dc --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSurveySimulation.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7250563f73fb3f0489c35f3bac88a8ce \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs index 0aef76752..6e831e9e4 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeVerseClient.cs @@ -256,6 +256,9 @@ namespace GameCult.Aetheria.State.Verse var fullPath = Path.GetFullPath(statePath); var effectiveRuntimeId = string.IsNullOrWhiteSpace(runtimeId) ? DefaultRuntimeId : runtimeId; var registry = AetheriaRuntimeVerseContractRegistry.CreateCultCacheRegistry(); + var runtimeSchemaIds = new HashSet( + registry.AllDescriptors.Select(descriptor => descriptor.SchemaId), + StringComparer.Ordinal); var node = await CultMesh.CreateNodeAsync( fullPath, new CultMeshNodeOptions @@ -267,7 +270,12 @@ namespace GameCult.Aetheria.State.Verse Registry = registry, PullOnOpen = pullOnOpen, StoreFlushOnDispose = true, - UseDirectoryStore = true + UseDirectoryStore = true, + // A thin runtime shares the daemon's physical CultCache, but it does not + // own or understand catalog, migration, Hangar, or other daemon schemas. + // Hydrate only the contract it can deserialize; untouched records remain + // owned by the backing store and are not projected into this client cache. + DirectoryStoreHydrationFilter = metadata => runtimeSchemaIds.Contains(metadata.SchemaId) }, DatabaseOptions = new CultNetDatabaseOptions { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeWorldPhysics.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeWorldPhysics.cs.meta new file mode 100644 index 000000000..9f8162cc6 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeWorldPhysics.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 13dee70f7b1b8ef4eabd7f3d572b02f1 \ No newline at end of file diff --git a/Packages/org.gamecult.aetheria.state/Runtime/GameCult.Aetheria.State.Unity.asmdef b/Packages/org.gamecult.aetheria.state/Runtime/GameCult.Aetheria.State.Unity.asmdef index 5b9752053..f428629a5 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/GameCult.Aetheria.State.Unity.asmdef +++ b/Packages/org.gamecult.aetheria.state/Runtime/GameCult.Aetheria.State.Unity.asmdef @@ -14,6 +14,7 @@ "overrideReferences": true, "precompiledReferences": [ "ConcurrentCollections.dll", + "CultMath.dll", "GameCult.Caching.MessagePack.dll", "GameCult.Caching.dll", "GameCult.Logging.dll", @@ -28,6 +29,7 @@ "Microsoft.NET.StringTools.dll", "MessagePack.Annotations.dll", "MessagePack.CultMesh.dll", + "MessagePack.dll", "R3.dll", "System.Collections.Immutable.dll", "System.ComponentModel.Annotations.dll", diff --git a/Packages/org.gamecult.aetheria.state/package.json b/Packages/org.gamecult.aetheria.state/package.json index 087f5ba61..ac372031a 100644 --- a/Packages/org.gamecult.aetheria.state/package.json +++ b/Packages/org.gamecult.aetheria.state/package.json @@ -5,7 +5,6 @@ "unity": "2021.3", "description": "Unity runtime boundary for Aetheria typed CultCache/CultMesh state.", "dependencies": { - "com.gamecult.brokkr": "0.1.0", "org.gamecult.eve.surface": "0.1.0" }, "keywords": [ diff --git a/Packages/packages-lock.json b/Packages/packages-lock.json index 6b7bb83d5..b10003255 100644 --- a/Packages/packages-lock.json +++ b/Packages/packages-lock.json @@ -1,13 +1,5 @@ { "dependencies": { - "com.gamecult.brokkr": { - "version": "file:../../Brokkr/surfaces/unity/Packages/com.gamecult.brokkr/", - "depth": 0, - "source": "local", - "dependencies": { - "org.gamecult.cultlib": "1.0.16" - } - }, "com.unity.2d.sprite": { "version": "1.0.0", "depth": 0, @@ -308,7 +300,6 @@ "depth": 0, "source": "embedded", "dependencies": { - "com.gamecult.brokkr": "0.1.0", "org.gamecult.eve.surface": "0.1.0" } }, @@ -320,11 +311,11 @@ "hash": "f67f5122ed1bd11da016e7b820ed60145ccd0299" }, "org.gamecult.cultmath": { - "version": "https://github.com/GameCult/CultMath.git?path=/unity/org.gamecult.cultmath#cultmath-unity-v0.1.1", + "version": "https://github.com/GameCult/CultMath.git?path=/unity/org.gamecult.cultmath#cultmath-unity-v0.1.2", "depth": 0, "source": "git", "dependencies": {}, - "hash": "c4543b9443e96d848260ef00254d2d3c0fa6a9d2" + "hash": "fcdddda72f6aa33ae6eeff3bce6a2d3e26eb16f3" }, "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.2.3", diff --git a/docs/hangar-launcher.md b/docs/hangar-launcher.md index e678bdec5..1182777ab 100644 --- a/docs/hangar-launcher.md +++ b/docs/hangar-launcher.md @@ -3,7 +3,7 @@ Status: the shared Hangar, deployment admission, and portable Hangar surface contracts exist. Mode session bootstrap still has to consume accepted deployment receipts; the old generic New Game path remains live until that cut is complete. -The state assembly and viewport queries compile against CultMath `0.1.1`, whose +The state assembly and viewport queries compile against CultMath `0.1.2`, whose canonical `rect` stores normalized `min`/`max` bounds. The Hangar is the launcher and durable preparation space for Terminus, diff --git a/scripts/run-aetheria-unity.ps1 b/scripts/run-aetheria-unity.ps1 index f4be04d09..5732d27ea 100644 --- a/scripts/run-aetheria-unity.ps1 +++ b/scripts/run-aetheria-unity.ps1 @@ -15,37 +15,41 @@ $state = Join-Path $project "Build\aetheria-unity.cc" $stateRecords = "$state.records" New-Item -ItemType Directory -Force $artifacts | Out-Null -if (-not $SkipBuild) { - $assetLog = Join-Path $artifacts "asset-bundles.log" - $assets = Start-Process $UnityExe -ArgumentList @( - "-batchmode", "-quit", "-projectPath", $root, - "-executeMethod", "Aetheria.Editor.EveAssetBundleBuilder.BuildWindows", - "-logFile", $assetLog - ) -PassThru -WindowStyle Hidden - Write-Host "Asset build PID: $($assets.Id)" - Write-Host "Asset build log: $assetLog" - if (-not $assets.WaitForExit(240000)) { Stop-Process $assets.Id -Force; throw "Asset build timed out." } - if ($assets.ExitCode -ne 0) { Get-Content $assetLog -Tail 120; throw "Asset build failed." } - - $clientLog = Join-Path $artifacts "client-build.log" - $build = Start-Process $UnityExe -ArgumentList @( - "-batchmode", "-quit", "-projectPath", $project, - "-executeMethod", "AetheriaUnityBuild.BuildWindows", - "-logFile", $clientLog - ) -PassThru -WindowStyle Hidden - Write-Host "Client build PID: $($build.Id)" - Write-Host "Client build log: $clientLog" - if (-not $build.WaitForExit(300000)) { Stop-Process $build.Id -Force; throw "Client build timed out." } - if ($build.ExitCode -ne 0 -or -not (Test-Path $clientExe)) { Get-Content $clientLog -Tail 120; throw "Client build failed." } -} - -if (-not (Test-Path $clientExe)) { throw "Client executable not found. Run without -SkipBuild first." } if (-not (Test-Path $stateRecords)) { dotnet run --project $importProject -- $root $state if ($LASTEXITCODE -ne 0 -or -not (Test-Path $stateRecords)) { throw "Aetheria typed state import failed with exit code $LASTEXITCODE" } } + +if (-not $SkipBuild) { + $env:AETHERIA_ASSET_CATALOG_STATE = $state + $assetLog = Join-Path $artifacts "asset-bundles.log" + $assets = Start-Process $UnityExe -ArgumentList @( + "-batchmode", "-quit", "-projectPath", $root, + "-cacheServerEnableDownload", "false", "-cacheServerEnableUpload", "false", + "-executeMethod", "Aetheria.Editor.EveAssetBundleBuilder.BuildWindows", + "-logFile", $assetLog + ) -PassThru -WindowStyle Hidden + Write-Host "Asset build PID: $($assets.Id)" + Write-Host "Asset build log: $assetLog" + if (-not $assets.WaitForExit(600000)) { Stop-Process $assets.Id -Force; throw "Asset build timed out." } + if ($assets.ExitCode -ne 0) { Get-Content $assetLog -Tail 120; throw "Asset build failed." } + + $clientLog = Join-Path $artifacts "client-build.log" + $build = Start-Process $UnityExe -ArgumentList @( + "-batchmode", "-quit", "-projectPath", $project, + "-cacheServerEnableDownload", "false", "-cacheServerEnableUpload", "false", + "-executeMethod", "AetheriaUnityBuild.BuildWindows", + "-logFile", $clientLog + ) -PassThru -WindowStyle Hidden + Write-Host "Client build PID: $($build.Id)" + Write-Host "Client build log: $clientLog" + if (-not $build.WaitForExit(600000)) { Stop-Process $build.Id -Force; throw "Client build timed out." } + if ($build.ExitCode -ne 0 -or -not (Test-Path $clientExe)) { Get-Content $clientLog -Tail 120; throw "Client build failed." } +} + +if (-not (Test-Path $clientExe)) { throw "Client executable not found. Run without -SkipBuild first." } $daemonLog = Join-Path $artifacts "daemon.log" $daemon = Start-Process dotnet -ArgumentList @( "run", "--project", $daemonProject, "--", @@ -56,6 +60,7 @@ $daemon = Start-Process dotnet -ArgumentList @( "--client-cultmesh-port", $Port, "--tick-interval-ms", 20, "--fixed-delta-ms", 20, + "--terminus-scenario", "standard", "--no-odin-announcements" ) -PassThru -WindowStyle Hidden -RedirectStandardOutput $daemonLog -RedirectStandardError "$daemonLog.error" Write-Host "Daemon PID: $($daemon.Id)" @@ -63,7 +68,11 @@ Write-Host "Daemon log: $daemonLog" try { $ready = $false - for ($i = 0; $i -lt 60; $i++) { + # A cold daemon build can take several minutes because the state schema + # generator and the Ymir/CultMesh dependency graph compile before Program + # publishes the endpoint. Readiness belongs to the endpoint, not an + # arbitrary 30-second process lifetime. + for ($i = 0; $i -lt 600; $i++) { if ($daemon.HasExited) { throw "Aetheria daemon exited. See $daemonLog" } if ((Test-Path $daemonLog) -and (Select-String $daemonLog -Pattern "Aetheria client CultMesh endpoint: cultnet\+tcp://127.0.0.1:$Port" -Quiet)) { $ready = $true; break } Start-Sleep -Milliseconds 500 diff --git a/scripts/verify-aetheria-daemon.ps1 b/scripts/verify-aetheria-daemon.ps1 index f4e9ff9c6..4fb2073a5 100644 --- a/scripts/verify-aetheria-daemon.ps1 +++ b/scripts/verify-aetheria-daemon.ps1 @@ -1,11 +1,15 @@ param( - [string] $CultLibRoot = "E:\Projects\CultLib", - [string] $EveUnityRoot = "E:\Projects\EveUnity", - [string] $YmirRoot = "E:\Projects\Ymir" + [string] $CultLibRoot = "", + [string] $EveUnityRoot = "", + [string] $YmirRoot = "" ) $ErrorActionPreference = "Stop" $repoRoot = Split-Path -Parent $PSScriptRoot +$projectsRoot = Split-Path -Parent $repoRoot +if ([string]::IsNullOrWhiteSpace($CultLibRoot)) { $CultLibRoot = Join-Path $projectsRoot "CultLib" } +if ([string]::IsNullOrWhiteSpace($EveUnityRoot)) { $EveUnityRoot = Join-Path $projectsRoot "EveUnity" } +if ([string]::IsNullOrWhiteSpace($YmirRoot)) { $YmirRoot = Join-Path $projectsRoot "Ymir" } $project = Join-Path $repoRoot "Aetheria.State.Daemon.Smoke\Aetheria.State.Daemon.Smoke.csproj" foreach ($dependency in @( @@ -18,6 +22,17 @@ foreach ($dependency in @( } } +$ymirSession = Join-Path $YmirRoot "src\Ymir.Core\YmirSession.cs" +$box2DProject = Join-Path $YmirRoot "src\Ymir.Box3D\Ymir.Box3D.csproj" +$box2DSource = Join-Path $YmirRoot "extern\box3d\CMakeLists.txt" +if (-not (Test-Path -LiteralPath $ymirSession -PathType Leaf) -or + -not (Test-Path -LiteralPath $box2DProject -PathType Leaf)) { + throw "Ymir does not expose the retained Box2D session API required by Aetheria. Use the current Aetheria integration revision." +} +if (-not (Test-Path -LiteralPath $box2DSource -PathType Leaf)) { + throw "Ymir's pinned Box2D submodule is missing. Run 'git -C $YmirRoot submodule update --init --recursive'." +} + $properties = @( "-p:CultLibRoot=$CultLibRoot", "-p:EveUnityRoot=$EveUnityRoot", diff --git a/scripts/verify-aetheria-unity-client.ps1 b/scripts/verify-aetheria-unity-client.ps1 index 7b094bc0f..48413d70c 100644 --- a/scripts/verify-aetheria-unity-client.ps1 +++ b/scripts/verify-aetheria-unity-client.ps1 @@ -14,14 +14,14 @@ $manifest = Get-Content (Join-Path $ProjectPath "Packages/manifest.json") -Raw | $lock = Get-Content (Join-Path $ProjectPath "Packages/packages-lock.json") -Raw | ConvertFrom-Json $expected = @{ "org.gamecult.cultlib" = @( - "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.30", - "0ef59b50c27ac019929f5e68bcd602983a190062") + "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.43", + "f67f5122ed1bd11da016e7b820ed60145ccd0299") "org.gamecult.eve.surface" = @( "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.4", "e08fa08335f99e9edddeb706912eecfad07cb281") "org.gamecult.eve.unity-scene" = @( - "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.88", - "3ce813e29dcc5342b51985187de26c62eb8c90b4") + "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.103", + "050b2019bd6faa4275013516870d1ded245ee391") } foreach ($packageName in $expected.Keys) { @@ -34,9 +34,9 @@ foreach ($packageName in $expected.Keys) { $cultLibPackage = Get-ChildItem (Join-Path $ProjectPath "Library/PackageCache") -Directory | Where-Object Name -Like "org.gamecult.cultlib@*" | - Where-Object { (Get-Content (Join-Path $_.FullName "package.json") -Raw | ConvertFrom-Json).version -eq "1.0.30" } | + Where-Object { (Get-Content (Join-Path $_.FullName "package.json") -Raw | ConvertFrom-Json).version -eq "1.0.43" } | Select-Object -First 1 -if (-not $cultLibPackage) { throw "Resolved CultLib package is missing from Library/PackageCache." } +if (-not $cultLibPackage) { throw "Resolved CultLib 1.0.43 package is missing from Library/PackageCache." } $meshAssembly = Join-Path $cultLibPackage.FullName "Runtime/Plugins/GameCult.Mesh.dll" if (-not (Test-Path $meshAssembly)) { throw "Resolved GameCult.Mesh.dll is missing." } @@ -46,8 +46,8 @@ $requiredMeshApiNames = @( "CultMeshBodyPublicationDocument", "CultMeshBodyPublicationHandle", "CultMeshFrameBodyPublisher", - "CultMeshContentServer", - "CultMeshSessionContentProviderOptions") + "CultMeshTcpContentServer", + "CultMeshSessionContentProvider") foreach ($apiName in $requiredMeshApiNames) { if (-not ($meshApi | Where-Object { $_ -eq $apiName })) { throw "Resolved GameCult.Mesh.dll does not expose $apiName." @@ -68,9 +68,9 @@ foreach ($requiredInputContract in @("PayloadKeys", "CurrentValue", "ActionBar", $scenePackage = Get-ChildItem (Join-Path $ProjectPath "Library/PackageCache") -Directory | Where-Object Name -Like "org.gamecult.eve.unity-scene@*" | - Where-Object { (Get-Content (Join-Path $_.FullName "package.json") -Raw | ConvertFrom-Json).version -eq "0.3.88" } | + Where-Object { (Get-Content (Join-Path $_.FullName "package.json") -Raw | ConvertFrom-Json).version -eq "0.3.103" } | Select-Object -First 1 -if (-not $scenePackage) { throw "Resolved Eve Unity scene 0.3.88 package is missing from Library/PackageCache." } +if (-not $scenePackage) { throw "Resolved Eve Unity scene 0.3.103 package is missing from Library/PackageCache." } $advertisedInputSource = Get-Content (Join-Path $scenePackage.FullName "Runtime/EveUnityAdvertisedInputAction.cs") -Raw $inputDriverSource = Get-Content (Join-Path $scenePackage.FullName "Runtime/EveUnityPlayableWorldInputDriver.cs") -Raw $actionBarSource = Get-Content (Join-Path $scenePackage.FullName "Runtime/EveUnityInputActionBar.cs") -Raw