diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 5358a05c4..75b6d9ebf 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -1863,7 +1863,8 @@ static void RequireDaemonRenderQueryAuthority(string root) "public double GravityTerrainDepth", "public double GravityTerrainDepthExponent", "public double GravityTerrainBoundaryFog", - "public double GravityTerrainWaveFrequency" + "public double GravityTerrainWaveFrequency", + "public double SimulationTimeSeconds" }; var missingPackageSnapshotSymbols = requiredSnapshotSymbols @@ -1915,6 +1916,13 @@ static void RequireDaemonRenderQueryAuthority(string root) "ZoneRenderer must position bodies from daemon render body poses instead of the mirrored Unity Zone orbit evaluator."); } + if (zoneRenderer.Contains("Zone.Time", StringComparison.Ordinal) || + zoneRenderer.Contains("Zone?.Time", StringComparison.Ordinal)) + { + throw new InvalidOperationException( + "ZoneRenderer must sample animation and terrain from daemon simulation time instead of mirrored Unity Zone time."); + } + if (zoneRenderer.Contains("Zone.PlanetInstances[planet.Key]", StringComparison.Ordinal)) { throw new InvalidOperationException( @@ -1995,6 +2003,7 @@ static void RequireDaemonRenderQueryAuthority(string root) "AetheriaRuntimeDaemonRenderQueries.QueryCompassMarkers(", "AetheriaRuntimeDaemonRenderQueries.QueryVisibleEntityIndices(", "AetheriaRuntimeDaemonRenderQueries.QueryWormholeExits(", + "private double DaemonSimulationTimeSeconds => _daemonZoneSnapshot?.SimulationTimeSeconds ?? 0;", "_daemonCompassMarkersByEntityIndex.TryGetValue(entityInstance.Entity.DaemonEntityIndex, out var marker)", "_daemonBodyPosesByBodyKey.TryGetValue(planet.Key, out var pose)", "pose.GravityWaveSpeed" diff --git a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs index 54a984bfa..adcf345ce 100644 --- a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs +++ b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs @@ -284,6 +284,9 @@ public sealed class AetheriaZoneState [Key(13)] public double GravityTerrainWaveFrequency { get; set; } = 1.0; + + [Key(14)] + public double SimulationTimeSeconds { get; set; } } [MessagePackObject] diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 2b41c5e6c..185a04190 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -2266,7 +2266,8 @@ public class ActionGameManager : MonoBehaviour var blueprint = new ZoneConstructionBlueprint { Radius = 2000, - Mass = 10000 + Mass = 10000, + Time = (float)zone.SimulationTimeSeconds }; foreach (var orbit in zone.Orbits ?? Array.Empty()) @@ -3462,6 +3463,7 @@ public class ActionGameManager : MonoBehaviour return; } + StampDaemonZoneSimulationTime(observed.Run, observed.Frame.SimulationTimeSeconds); if (TryRestoreEntityGraphFromDaemonRun(observed.Run)) { _lastAppliedAuthoritativeDaemonFrameId = observed.Frame.FrameId; @@ -3469,6 +3471,17 @@ public class ActionGameManager : MonoBehaviour } } + private static void StampDaemonZoneSimulationTime( + AetheriaRuntimeRunCheckpointCommit run, + double simulationTimeSeconds) + { + foreach (var zone in run?.Zones ?? Array.Empty()) + { + if (zone != null) + zone.SimulationTimeSeconds = simulationTimeSeconds; + } + } + private bool TryRequestDaemonMoveVector(Vector2 movement) { var observer = ResolveDaemonObserver(); diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index 15372fa2d..4682110c2 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -182,6 +182,7 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(1, run.Zones[0].Entities[0].TargetEntityIndex); Assert.AreEqual("zone.0.entity.0", result.Intents.Movement.ActorEntityKey); Assert.AreEqual(1.0, result.Intents.Movement.DirectionX, 0.0001); + Assert.AreEqual(12.5, result.Run.Zones[0].SimulationTimeSeconds, 0.0001); Assert.IsTrue(AetheriaRuntimeDaemonFrameStore.TryReadFrame(statePath, out var frame)); Assert.AreEqual(result.FramePath, AetheriaRuntimeDaemonFrameStore.GetFramePath(statePath)); Assert.AreEqual("test-daemon", frame.DaemonId); @@ -199,6 +200,7 @@ public class DaemonRuntimeDocumentTests CollectionAssert.Contains(frame.AccountedCommandIds, targetCommand.CommandId); CollectionAssert.Contains(frame.AccountedCommandIds, movementCommand.CommandId); Assert.AreEqual(1, frame.Run.Zones[0].Entities[0].TargetEntityIndex); + Assert.AreEqual(12.5, frame.Run.Zones[0].SimulationTimeSeconds, 0.0001); Assert.AreEqual( AetheriaRuntimeDaemonPublicationStore.GetProviderAdvertisementPath(statePath), result.ProviderAdvertisementPath); diff --git a/Assets/Scripts/Zone Display/ZoneRenderer.cs b/Assets/Scripts/Zone Display/ZoneRenderer.cs index 18928dc77..8aabff43d 100644 --- a/Assets/Scripts/Zone Display/ZoneRenderer.cs +++ b/Assets/Scripts/Zone Display/ZoneRenderer.cs @@ -495,7 +495,7 @@ public class ZoneRenderer : MonoBehaviour AetheriaRuntimeDaemonRenderQueries.QueryAsteroidInstancePoses( _daemonZoneSnapshot, key, - Zone?.Time ?? 0, + DaemonSimulationTimeSeconds, _visibleAsteroidInstancePoses); } @@ -544,7 +544,7 @@ public class ZoneRenderer : MonoBehaviour if (planet.Value is GasGiantObject gasGiantObject) { gasGiantObject.GravityWaves.material.SetFloat("_Phase", - Zone.Time * (float)pose.GravityWaveSpeed); + (float)(DaemonSimulationTimeSeconds * pose.GravityWaveSpeed)); if (!string.Equals(pose.Kind, "sun", StringComparison.OrdinalIgnoreCase)) { var parent = new float2((float)pose.ParentCenterX, (float)pose.ParentCenterZ); @@ -661,9 +661,11 @@ public class ZoneRenderer : MonoBehaviour _daemonZoneSnapshot, position.x, position.y, - Zone?.Time ?? 0); + DaemonSimulationTimeSeconds); } + private double DaemonSimulationTimeSeconds => _daemonZoneSnapshot?.SimulationTimeSeconds ?? 0; + public void DestroyLoot(ItemPickup loot) { _loot.Remove(loot); diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs index a6bc1652e..d75ac6f12 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs @@ -817,7 +817,8 @@ namespace GameCult.Aetheria.State.Verse double gravityTerrainDepth, double gravityTerrainDepthExponent, double gravityTerrainBoundaryFog, - double gravityTerrainWaveFrequency) + double gravityTerrainWaveFrequency, + double simulationTimeSeconds) { RecordKey = recordKey; Name = name; @@ -835,6 +836,7 @@ namespace GameCult.Aetheria.State.Verse GravityTerrainDepthExponent = gravityTerrainDepthExponent; GravityTerrainBoundaryFog = gravityTerrainBoundaryFog; GravityTerrainWaveFrequency = gravityTerrainWaveFrequency; + SimulationTimeSeconds = simulationTimeSeconds; } public string RecordKey { get; } @@ -853,6 +855,7 @@ namespace GameCult.Aetheria.State.Verse public double GravityTerrainDepthExponent { get; } public double GravityTerrainBoundaryFog { get; } public double GravityTerrainWaveFrequency { get; } + public double SimulationTimeSeconds { get; } } public sealed class AetheriaRuntimeDroppedPickupSnapshot diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index 9c1e7c0a7..2e59d53fe 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -1153,7 +1153,8 @@ namespace GameCult.Aetheria.State.Verse var gravityTerrainDepthExponent = ReadFieldDouble(ref reader, fields, 11, 1.0); var gravityTerrainBoundaryFog = ReadFieldDouble(ref reader, fields, 12); var gravityTerrainWaveFrequency = ReadFieldDouble(ref reader, fields, 13, 1.0); - SkipRemaining(ref reader, fields, 14); + var simulationTimeSeconds = ReadFieldDouble(ref reader, fields, 14); + SkipRemaining(ref reader, fields, 15); return new AetheriaRuntimeZoneStateSnapshot( recordKey, name, @@ -1170,7 +1171,8 @@ namespace GameCult.Aetheria.State.Verse gravityTerrainDepth, gravityTerrainDepthExponent, gravityTerrainBoundaryFog, - gravityTerrainWaveFrequency); + gravityTerrainWaveFrequency, + simulationTimeSeconds); } private static IReadOnlyList ReadFieldDroppedPickups(ref MessagePackReader reader, int fields, int index) diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs index c47885456..229b1f9f1 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonTickRunner.cs @@ -96,6 +96,7 @@ namespace GameCult.Aetheria.State.Verse run, commands, options.OperationContext); + StampZoneSimulationTime(operationResult.Run, options.SimulationTimeSeconds); var frame = AetheriaRuntimeDaemonFrameDocument.Create( operationResult.Run, @@ -194,5 +195,16 @@ namespace GameCult.Aetheria.State.Verse editorTuiSurfacePath); } + private static void StampZoneSimulationTime( + AetheriaRuntimeRunCheckpointCommit run, + double simulationTimeSeconds) + { + foreach (var zone in run?.Zones ?? Array.Empty()) + { + if (zone != null) + zone.SimulationTimeSeconds = simulationTimeSeconds; + } + } + } } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs index b1ed55c66..a94d069ba 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs @@ -279,6 +279,9 @@ namespace GameCult.Aetheria.State.Verse [Key(15)] public double GravityTerrainWaveFrequency { get; set; } = 1.0; + + [Key(16)] + public double SimulationTimeSeconds { get; set; } } [MessagePackObject]