Move game feel settings to daemon authority

This commit is contained in:
Meta Vens 2026-07-05 15:23:39 +01:00
commit dc8a7b069d
4 changed files with 129 additions and 21 deletions

View file

@ -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))

View file

@ -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()
{

View file

@ -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,

View file

@ -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<AetheriaRuntimeDroppedPickupCommit>())
@ -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,