Aetheria/Assets/Scripts/Gameplay/AetheriaUnityObservedFrameApplier.cs

159 lines
6.3 KiB
C#
Raw Normal View History

2026-06-25 18:09:33 +01:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
using System;
using System.Collections.Generic;
using System.Linq;
using GameCult.Aetheria.State.Verse;
public sealed class AetheriaUnityObservedFrameApplier
{
private readonly Func<AetheriaDaemonObserver> _resolveObserver;
private readonly Func<int, GalaxyZone> _resolveObservedZone;
private readonly Func<Zone> _getZone;
private readonly Action<Zone> _setZone;
2026-06-30 19:14:49 +01:00
private readonly AetheriaUnityPresentationEntityIndex _entityIndex;
2026-06-28 14:37:28 +01:00
private readonly AetheriaUnityObservedEntityRestorer _entityRestorer;
private readonly AetheriaUnityObservedZoneContextFactory _zoneContextFactory;
2026-06-25 18:09:33 +01:00
private readonly Func<ZoneRenderer> _resolveZoneRenderer;
private readonly Func<Entity> _getCurrentEntity;
private readonly Action<Entity> _restoreCurrentEntityBinding;
private readonly Action<string> _logWarning;
private long _lastAppliedZoneRenderFrameId = -1;
private string _lastAppliedZoneRenderRunId = "";
private int _lastAppliedZoneRenderZoneIndex = -1;
private AetheriaRuntimeZoneRenderDocument _lastZoneRender;
public AetheriaUnityObservedFrameApplier(
Func<AetheriaDaemonObserver> resolveObserver,
Func<int, GalaxyZone> resolveObservedZone,
Func<Zone> getZone,
Action<Zone> setZone,
2026-06-30 19:14:49 +01:00
AetheriaUnityPresentationEntityIndex entityIndex,
2026-06-28 14:37:28 +01:00
AetheriaUnityObservedEntityRestorer entityRestorer,
AetheriaUnityObservedZoneContextFactory zoneContextFactory,
2026-06-25 18:09:33 +01:00
Func<ZoneRenderer> resolveZoneRenderer,
Func<Entity> getCurrentEntity,
Action<Entity> restoreCurrentEntityBinding,
Action<string> logWarning)
{
_resolveObserver = resolveObserver ?? (() => null);
_resolveObservedZone = resolveObservedZone ?? (_ => null);
_getZone = getZone ?? (() => null);
_setZone = setZone ?? (_ => { });
_entityIndex = entityIndex ?? throw new ArgumentNullException(nameof(entityIndex));
2026-06-28 14:37:28 +01:00
_entityRestorer = entityRestorer ?? throw new ArgumentNullException(nameof(entityRestorer));
_zoneContextFactory = zoneContextFactory ?? throw new ArgumentNullException(nameof(zoneContextFactory));
2026-06-25 18:09:33 +01:00
_resolveZoneRenderer = resolveZoneRenderer ?? (() => null);
_getCurrentEntity = getCurrentEntity ?? (() => null);
_restoreCurrentEntityBinding = restoreCurrentEntityBinding ?? (_ => { });
_logWarning = logWarning ?? (_ => { });
}
public bool ApplyLatestZoneRender()
{
var observer = _resolveObserver();
if (observer == null || !observer.HasAuthoritativeState)
{
return false;
}
var render = observer.LastRenderView?.ZoneRender;
2026-06-25 18:09:33 +01:00
if (render == null)
{
return false;
}
return TryRestoreEntityGraphFromZoneRender(render);
}
private bool TryRestoreEntityGraphFromZoneRender(AetheriaRuntimeZoneRenderDocument render)
{
if (render == null || render.ZoneIndex < 0)
{
return false;
}
if (string.IsNullOrWhiteSpace(render.RunId))
{
_logWarning("Aetheria zone-render feed does not identify a run id.");
return false;
}
var runId = render.RunId;
var currentEntityKey = string.IsNullOrWhiteSpace(render.CurrentEntityKey) ? "" : render.CurrentEntityKey;
if (string.IsNullOrWhiteSpace(currentEntityKey))
{
_logWarning($"Aetheria zone-render feed for run {runId} does not identify a current entity.");
return false;
}
var entitySnapshots = render.CreateEntitySnapshots()
.OrderBy(entity => entity.EntityIndex)
2026-06-25 18:09:33 +01:00
.ToArray();
if (entitySnapshots.Length == 0)
{
_logWarning($"Aetheria zone-render feed has no entity snapshots for zone {render.ZoneIndex}.");
2026-06-25 18:09:33 +01:00
return false;
}
var targetZone = _resolveObservedZone(render.ZoneIndex);
if (targetZone == null)
{
_logWarning($"Aetheria zone-render feed references missing zone index {render.ZoneIndex}.");
return false;
}
if (render.FrameId == _lastAppliedZoneRenderFrameId &&
string.Equals(render.RunId, _lastAppliedZoneRenderRunId, StringComparison.Ordinal) &&
render.ZoneIndex == _lastAppliedZoneRenderZoneIndex)
{
return false;
}
var zoneRenderer = _resolveZoneRenderer();
2026-06-28 14:37:28 +01:00
if (_entityRestorer.TryApplyInPlace(
2026-06-25 18:09:33 +01:00
_lastAppliedZoneRenderRunId,
_lastAppliedZoneRenderZoneIndex,
runId,
render.ZoneIndex,
entitySnapshots,
currentEntityKey,
_getCurrentEntity(),
out var reboundCurrentEntity))
{
if (reboundCurrentEntity != null)
_restoreCurrentEntityBinding(reboundCurrentEntity);
zoneRenderer?.ApplyZoneRender(render);
zoneRenderer?.RestoreDroppedPickupsFromZoneRender(render);
_lastAppliedZoneRenderFrameId = render.FrameId;
_lastAppliedZoneRenderRunId = runId;
_lastAppliedZoneRenderZoneIndex = render.ZoneIndex;
2026-06-25 18:09:33 +01:00
_lastZoneRender = render;
return true;
}
if (_getZone()?.GalaxyZone != targetZone)
{
2026-06-28 14:37:28 +01:00
var resolvedZone = _zoneContextFactory.ResolveContext(targetZone, render);
2026-06-25 18:09:33 +01:00
if (resolvedZone == null)
return false;
_setZone(resolvedZone);
}
2026-06-28 14:37:28 +01:00
_entityRestorer.Replace(entitySnapshots, currentEntityKey, _getZone());
2026-06-30 19:14:49 +01:00
zoneRenderer?.LoadDaemonZoneView(_entityIndex.PresentationEntitiesByDaemonIndex, render);
if (_entityIndex.TryGetPresentationEntityByRecordKey(currentEntityKey, out var currentEntity))
2026-06-25 18:09:33 +01:00
_restoreCurrentEntityBinding(currentEntity);
zoneRenderer?.RestoreDroppedPickupsFromZoneRender(render);
_lastAppliedZoneRenderFrameId = render.FrameId;
_lastAppliedZoneRenderRunId = runId;
_lastAppliedZoneRenderZoneIndex = render.ZoneIndex;
_lastZoneRender = render;
return true;
}
}