From dc8a7b069ddd1c2eb0e4eea870ac51adc35bef16 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 5 Jul 2026 15:23:39 +0100 Subject: [PATCH] Move game feel settings to daemon authority --- .../AetheriaUnityGameplayBootShell.cs | 21 ++++- .../Tests/DaemonRuntimeDocumentTests.cs | 82 +++++++++++++++++++ .../AetheriaRuntimeDaemonRenderQueries.cs | 38 ++++++--- .../Runtime/AetheriaRuntimeRtsDocuments.cs | 9 +- 4 files changed, 129 insertions(+), 21 deletions(-) diff --git a/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs b/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs index 6c080b541..4cfeb6a95 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityGameplayBootShell.cs @@ -63,10 +63,7 @@ public sealed class AetheriaUnityGameplayBootShell var loadoutItemFactory = new AetheriaUnityLoadoutItemFactory(itemManager, runtimeCatalog); ZoneRenderer.SetDroppedPickupItemFactory(loadoutItemFactory.CreateLoadoutItem); ZoneRenderer.BodySettingsCollections = Settings.BodySettingsCollections; - ZoneRenderer.RenderSettings = AetheriaUnityRenderSettingsBridge.Build( - Settings, - TargetSpottedBlinkFrequency, - TargetSpottedBlinkOffset); + ZoneRenderer.RenderSettings = ResolveDaemonRenderSettings(runtimeState); CockpitHudShell.SetRenderSettings(ZoneRenderer.RenderSettings); if (!AetheriaUnityRuntimeClientProvider.PlayerSettings.GraphicsSettings.ShowAsteroidsInMinimap) @@ -79,6 +76,22 @@ public sealed class AetheriaUnityGameplayBootShell loadoutItemFactory); } + private AetheriaRuntimeDaemonRenderSettings ResolveDaemonRenderSettings(AetheriaClientState runtimeState) + { + var frame = runtimeState?.DaemonFrame?.Latest(); + if (frame?.RenderSettings != null) + { + Log?.Invoke($"Aetheria render/game-feel settings: daemon frame {frame.FrameId}"); + return frame.RenderSettings; + } + + Log?.Invoke("Aetheria render/game-feel settings: local Unity Settings fallback; daemon frame unavailable."); + return AetheriaUnityRenderSettingsBridge.Build( + Settings, + TargetSpottedBlinkFrequency, + TargetSpottedBlinkOffset); + } + private AetheriaRuntimeStateBootReport SyncRemoteReplicaBeforeBoot(AetheriaRuntimeStateBootReport stateBoot) { if (!ShouldSyncRemoteReplicaBeforeBoot(stateBoot)) diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index 6cc37b383..e1a4f01b6 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -1208,6 +1208,35 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(0, AetheriaRuntimeDaemonRenderQueries.ResolveZoneRenderRadius(null, -5)); } + [Test] + public void DaemonRenderSettingsDefaultOwnsAetheriaGameFeel() + { + var settings = AetheriaRuntimeDaemonRenderSettings.AetheriaDefault; + + Assert.AreEqual(0.75, settings.WormholeDistanceRatio); + Assert.AreEqual(4096, settings.DefaultViewDistance); + Assert.AreEqual(0.125, settings.MinimapIconScale); + Assert.AreEqual(3, settings.MinimapAsteroidSize); + Assert.AreEqual(0.45, settings.MinimapZoneGravityRange); + Assert.AreEqual(-10, settings.AsteroidVerticalOffset); + Assert.AreEqual(0.1, settings.PlanetRotationSpeed); + Assert.AreEqual(2, settings.ZoneBoundaryPower); + Assert.AreEqual(64, settings.ZoneBoundaryDepth); + Assert.AreEqual(4, settings.AsteroidMeshCount); + Assert.AreEqual(3, settings.DefaultMinimapZoom); + CollectionAssert.AreEqual(new[] { 250.0, 500.0, 1000.0, 2000.0, 4000.0 }, settings.ResolveMinimapZoomLevels().ToArray()); + Assert.AreEqual(2000, settings.ResolveDefaultMinimapDistance()); + Assert.AreEqual(0.15, settings.BodyIconSizeCurve.Exponent); + Assert.AreEqual(10, settings.BodyIconSizeCurve.Multiplier); + Assert.AreEqual(25, settings.BodyIconSizeCurve.Constant); + Assert.AreEqual(0.25, settings.BodyRadiusCurve.Exponent); + Assert.AreEqual(3, settings.BodyRadiusCurve.Multiplier); + Assert.AreEqual(0.25, settings.LightRadiusCurve.Exponent); + Assert.AreEqual(300, settings.LightRadiusCurve.Multiplier); + Assert.AreEqual(0.45, settings.GravityWaveFrequencyCurve.Exponent); + Assert.AreEqual(0.2, settings.GravityWaveFrequencyCurve.Multiplier); + } + [Test] public void DaemonRenderQueriesPublishGravityTerrainMaterialBand() { @@ -1801,6 +1830,59 @@ public class DaemonRuntimeDocumentTests Assert.AreEqual(40, exits[0].PositionZ, 0.0001); } + [Test] + public void RtsZoneRenderUsesDaemonFrameWormholeDistanceRatio() + { + var defaults = AetheriaRuntimeDaemonRenderSettings.AetheriaDefault; + var renderSettings = new AetheriaRuntimeDaemonRenderSettings( + defaults.TemperatureEmissionCurve, + defaults.LockIndicatorFrequency, + defaults.LockSpinSpeed, + defaults.ConvergenceMinimumDistance, + defaults.HypothermiaTemperature, + defaults.HeatstrokeTemperature, + defaults.SevereHeatstrokeRiskThreshold, + defaults.TargetDetectionInfoThreshold, + defaults.LockIndicatorNoiseAmplitude, + wormholeDistanceRatio: 0.25); + var currentZone = new AetheriaRuntimeZoneSnapshotCommit + { + ZoneIndex = 0, + PositionX = 0, + PositionY = 0, + GravityTerrainRadius = 100, + AdjacentZoneIndices = new[] { 1 } + }; + var frame = AetheriaRuntimeDaemonFrameDocument.Create( + new AetheriaRuntimeRunCheckpointCommit + { + RunId = "wormhole-ratio-run", + CurrentZoneIndex = 0, + Zones = new[] + { + currentZone, + new AetheriaRuntimeZoneSnapshotCommit + { + ZoneIndex = 1, + PositionX = 4, + PositionY = 3 + } + } + }, + "daemon", + "session", + 12, + 0, + 0.02, + renderSettings: renderSettings); + + var zoneRender = AetheriaRuntimeRtsDocuments.ZoneRender(frame); + + Assert.AreEqual(1, zoneRender.WormholeExits.Count); + Assert.AreEqual(0.8 * 25, zoneRender.WormholeExits[0].PositionX, 0.0001); + Assert.AreEqual(0.6 * 25, zoneRender.WormholeExits[0].PositionZ, 0.0001); + } + [Test] public void SoaViewIndexRejectsInvalidRenderGroups() { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs index 2d346a6ec..2bb715ea3 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs @@ -68,19 +68,33 @@ namespace GameCult.Aetheria.State.Verse { public static AetheriaRuntimeDaemonRenderSettings AetheriaDefault { get; } = new AetheriaRuntimeDaemonRenderSettings( - default, - default, - default, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, - 0.0, + new AetheriaRuntimeExponentialCurve(3.0, 0.000000001, 0.0), + new AetheriaRuntimeExponentialLerp(4.0, 0.01, 10.0), + new AetheriaRuntimeExponentialLerp(2.0, 5.0, 720.0), + 50.0, + 273.0, + 330.0, + 0.25, + 0.1, + 256.0, + heatstrokePhasingFloor: 0.0, + heatstrokePhasingFrequency: 5.0, + minimapZoomLevels: new[] { 250.0, 500.0, 1000.0, 2000.0, 4000.0 }, + defaultMinimapZoom: 3, + wormholeDistanceRatio: 0.75, + defaultViewDistance: 4096.0, + minimapIconScale: 0.125, + minimapAsteroidSize: 3.0, bodyIconSizeCurve: new AetheriaRuntimeExponentialCurve(0.15, 10.0, 25.0), - bodyRadiusCurve: default, - lightRadiusCurve: default, - gravityWaveFrequencyCurve: default); + minimapZoneGravityRange: 0.45, + asteroidVerticalOffset: -10.0, + planetRotationSpeed: 0.1, + zoneBoundaryPower: 2.0, + zoneBoundaryDepth: 64.0, + asteroidMeshCount: 4, + bodyRadiusCurve: new AetheriaRuntimeExponentialCurve(0.25, 3.0, 0.0), + lightRadiusCurve: new AetheriaRuntimeExponentialCurve(0.25, 300.0, 0.0), + gravityWaveFrequencyCurve: new AetheriaRuntimeExponentialCurve(0.45, 0.2, 0.0)); public AetheriaRuntimeDaemonRenderSettings( AetheriaRuntimeExponentialCurve temperatureEmissionCurve, diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRtsDocuments.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRtsDocuments.cs index 3798beae8..7ff555aac 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRtsDocuments.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeRtsDocuments.cs @@ -8,8 +8,6 @@ namespace GameCult.Aetheria.State.Verse { public static class AetheriaRuntimeRtsDocuments { - private const double ZoneRenderWormholeDistanceRatio = 1.0; - public static AetheriaRuntimeRtsViewportDocument Viewport( AetheriaRuntimeDaemonFrameDocument frame, AetheriaRuntimeRtsViewportBounds viewport) @@ -711,7 +709,7 @@ namespace GameCult.Aetheria.State.Verse ZoneRenderRadius = zoneRenderRadius, Credits = run.Credits, AdjacentZones = ZoneRenderAdjacentZones(run, zone), - WormholeExits = ZoneRenderWormholeExits(run, zone, zoneRenderRadius), + WormholeExits = ZoneRenderWormholeExits(run, zone, zoneRenderRadius, frame.RenderSettings), BodyPoses = ZoneRenderBodyPoses(zone), AsteroidBeltPoses = ZoneRenderAsteroidBeltPoses(zone, frame.SimulationTimeSeconds), DroppedPickups = (zone.DroppedPickups ?? Array.Empty()) @@ -958,10 +956,11 @@ namespace GameCult.Aetheria.State.Verse private static AetheriaRuntimeZoneRenderWormholeExit[] ZoneRenderWormholeExits( AetheriaRuntimeRunCheckpointCommit run, AetheriaRuntimeZoneSnapshotCommit zone, - double zoneRenderRadius) + double zoneRenderRadius, + AetheriaRuntimeDaemonRenderSettings renderSettings) { return AetheriaRuntimeDaemonRenderQueries - .QueryWormholeExits(run, zone, zoneRenderRadius, ZoneRenderWormholeDistanceRatio) + .QueryWormholeExits(run, zone, zoneRenderRadius, renderSettings.WormholeDistanceRatio) .Select(exit => new AetheriaRuntimeZoneRenderWormholeExit { TargetZoneIndex = exit.TargetZoneIndex,