From da92b62fc1e29fc506f1371f90256b71be47f304 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 28 Jun 2026 11:31:48 +0100 Subject: [PATCH] Move daemon entity snapshot lowering into state runtime --- Aetheria.State.Verify/Program.cs | 34 ++- ...heriaUnityDaemonEntitySnapshotProjector.cs | 255 ------------------ ...UnityDaemonEntitySnapshotProjector.cs.meta | 2 - .../AetheriaUnityObservedFrameApplier.cs | 2 +- GameCult.Aetheria.State.Unity.csproj | 1 + .../AetheriaRuntimeEntitySnapshotProjector.cs | 255 ++++++++++++++++++ ...eriaRuntimeEntitySnapshotProjector.cs.meta | 2 + 7 files changed, 283 insertions(+), 268 deletions(-) delete mode 100644 Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs delete mode 100644 Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs.meta create mode 100644 Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs create mode 100644 Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs.meta diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs index 20db81b93..de94bdd96 100644 --- a/Aetheria.State.Verify/Program.cs +++ b/Aetheria.State.Verify/Program.cs @@ -11722,10 +11722,17 @@ static void RequireMainMenuContinueRunState(string root) var pilotFrameAdapter = File.Exists(pilotFrameAdapterPath) ? File.ReadAllText(pilotFrameAdapterPath) : throw new InvalidOperationException("Cannot verify Continue pilot frame adapter; AetheriaUnityPilotFrameAdapter.cs is missing."); - var daemonEntitySnapshotProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityDaemonEntitySnapshotProjector.cs"); + var legacyUnityDaemonEntitySnapshotProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityDaemonEntitySnapshotProjector.cs"); + if (File.Exists(legacyUnityDaemonEntitySnapshotProjectorPath)) + { + throw new InvalidOperationException( + "Unity still owns daemon entity snapshot projection; package runtime should lower typed daemon entity snapshots."); + } + + var daemonEntitySnapshotProjectorPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeEntitySnapshotProjector.cs"); var daemonEntitySnapshotProjector = File.Exists(daemonEntitySnapshotProjectorPath) ? File.ReadAllText(daemonEntitySnapshotProjectorPath) - : throw new InvalidOperationException("Cannot verify Continue entity projection; AetheriaUnityDaemonEntitySnapshotProjector.cs is missing."); + : throw new InvalidOperationException("Cannot verify Continue entity projection; AetheriaRuntimeEntitySnapshotProjector.cs is missing."); var observedEntityProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedEntityProjector.cs"); var observedEntityProjector = File.Exists(observedEntityProjectorPath) ? File.ReadAllText(observedEntityProjectorPath) @@ -11827,7 +11834,7 @@ static void RequireMainMenuContinueRunState(string root) var requiredEntitySnapshotProjectorSymbols = new[] { - "public static class AetheriaUnityDaemonEntitySnapshotProjector", + "public static class AetheriaRuntimeEntitySnapshotProjector", "public static AetheriaRuntimeEntitySnapshot[] CreateSnapshots(", "public static string DaemonEntityRecordKey(string runId, int zoneIndex, int entityIndex)", "new AetheriaRuntimeEntitySnapshot(", @@ -11844,7 +11851,7 @@ static void RequireMainMenuContinueRunState(string root) if (missingEntitySnapshotProjectorSymbols.Length > 0) { throw new InvalidOperationException( - "Daemon entity snapshot projection must live outside ActionGameManager: " + + "Daemon entity snapshot projection must live in the runtime package outside ActionGameManager and Unity Assets: " + string.Join(", ", missingEntitySnapshotProjectorSymbols)); } @@ -12022,7 +12029,7 @@ static void RequireMainMenuContinueRunState(string root) "private bool TryRestoreEntityGraphFromZoneRender(", "if (string.IsNullOrWhiteSpace(render.RunId))", "Aetheria zone-render feed does not identify a run id.", - "AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots)", + "AetheriaRuntimeEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots)", ".OrderBy(entity => entity.EntityIndex)", "_entityProjector.TryApplyInPlace(", "zoneRenderer?.ApplyZoneRender(render)", @@ -12052,7 +12059,7 @@ static void RequireMainMenuContinueRunState(string root) "_lastAppliedAuthoritativeDaemonZoneIndex", "private bool TryRestoreEntityGraphFromZoneRender(", "AetheriaRuntimeRtsProjection.ProjectZoneRender(observed.Frame)", - "AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, daemonZone)", + "AetheriaRuntimeEntitySnapshotProjector.CreateSnapshots(runId, daemonZone)", "ObservedEntityProjector.TryApplyInPlace(", "ObservedEntityProjector.Replace(entitySnapshots, currentEntityKey, Zone)", "ZoneRenderer?.LoadDaemonZoneView(_observedEntityIndex.EntitiesByDaemonIndex, render)", @@ -12616,7 +12623,8 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) var pilotOperationAdapterPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityPilotOperationAdapter.cs"); var observedTargetQueryPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedTargetQuery.cs"); var observedFrameApplierPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedFrameApplier.cs"); - var daemonEntitySnapshotProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityDaemonEntitySnapshotProjector.cs"); + var legacyUnityDaemonEntitySnapshotProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityDaemonEntitySnapshotProjector.cs"); + var daemonEntitySnapshotProjectorPath = Path.Combine(root, "Packages", "org.gamecult.aetheria.state", "Runtime", "AetheriaRuntimeEntitySnapshotProjector.cs"); var observedEntityProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedEntityProjector.cs"); var entityConstructionBlueprintProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityEntityConstructionBlueprintProjector.cs"); var observedZoneContextProjectorPath = Path.Combine(root, "Assets", "Scripts", "Gameplay", "AetheriaUnityObservedZoneContextProjector.cs"); @@ -12663,9 +12671,15 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) var observedFrameApplier = File.Exists(observedFrameApplierPath) ? File.ReadAllText(observedFrameApplierPath) : throw new InvalidOperationException("Cannot verify Unity observer authority; AetheriaUnityObservedFrameApplier.cs is missing."); + if (File.Exists(legacyUnityDaemonEntitySnapshotProjectorPath)) + { + throw new InvalidOperationException( + "Unity still owns daemon entity snapshot projection; package runtime should lower typed daemon entity snapshots."); + } + var daemonEntitySnapshotProjector = File.Exists(daemonEntitySnapshotProjectorPath) ? File.ReadAllText(daemonEntitySnapshotProjectorPath) - : throw new InvalidOperationException("Cannot verify Unity observer authority; AetheriaUnityDaemonEntitySnapshotProjector.cs is missing."); + : throw new InvalidOperationException("Cannot verify Unity observer authority; AetheriaRuntimeEntitySnapshotProjector.cs is missing."); var observedEntityProjector = File.Exists(observedEntityProjectorPath) ? File.ReadAllText(observedEntityProjectorPath) : throw new InvalidOperationException("Cannot verify Unity observer authority; AetheriaUnityObservedEntityProjector.cs is missing."); @@ -12887,7 +12901,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "public bool ApplyLatestZoneRender()", "observer.LastObservedState?.ZoneRender", "private bool TryRestoreEntityGraphFromZoneRender(", - "AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots)", + "AetheriaRuntimeEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots)", ".OrderBy(entity => entity.EntityIndex)", "_entityProjector.Replace(entitySnapshots, currentEntityKey, _getZone())", "_entityProjector.TryApplyInPlace(", @@ -12917,7 +12931,7 @@ static void RequireUnityObserverDoesNotTickLocalSimulation(string root) "_lastAppliedAuthoritativeDaemonZoneIndex", "private bool TryRestoreEntityGraphFromZoneRender(", "AetheriaRuntimeRtsProjection.ProjectZoneRender(observed.Frame)", - "AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, daemonZone)", + "AetheriaRuntimeEntitySnapshotProjector.CreateSnapshots(runId, daemonZone)", "ObservedEntityProjector.Replace(entitySnapshots, currentEntityKey, Zone)", "ObservedEntityProjector.TryApplyInPlace(", "ZoneRenderer?.LoadDaemonZoneView(_observedEntityIndex.EntitiesByDaemonIndex, render)", diff --git a/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs b/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs deleted file mode 100644 index 4b5c4bb8e..000000000 --- a/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs +++ /dev/null @@ -1,255 +0,0 @@ -/* 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 static class AetheriaUnityDaemonEntitySnapshotProjector -{ - public static AetheriaRuntimeEntitySnapshot[] CreateSnapshots( - string runId, - AetheriaRuntimeZoneSnapshotCommit zone) - { - if (zone == null) - return Array.Empty(); - - return CreateSnapshots(runId, zone.ZoneIndex, zone.Entities); - } - - public static AetheriaRuntimeEntitySnapshot[] CreateSnapshots( - string runId, - int zoneIndex, - IReadOnlyList entities) - { - return (entities ?? Array.Empty()) - .Where(entity => entity != null) - .Select(entity => CreateSnapshot(runId, zoneIndex, entity)) - .ToArray(); - } - - public static string DaemonEntityRecordKey(string runId, int zoneIndex, int entityIndex) - { - return string.IsNullOrWhiteSpace(runId) - ? "" - : $"global:aetheria.run_state.{runId}.zone.{zoneIndex}.entity.{entityIndex}.v1"; - } - - private static AetheriaRuntimeEntitySnapshot CreateSnapshot( - string runId, - int zoneIndex, - AetheriaRuntimeEntitySnapshotCommit entity) - { - return new AetheriaRuntimeEntitySnapshot( - DaemonEntityRecordKey(runId, zoneIndex, entity.EntityIndex), - entity.EntityIndex, - entity.Name ?? "", - entity.Kind ?? "", - entity.PositionX, - entity.PositionY, - entity.PositionZ, - entity.DirectionX, - entity.DirectionY, - entity.FactionKey ?? "", - entity.HullItemKey ?? "", - CreateItemSlots(entity.Equipment), - CreateItemSlots(entity.CargoBays), - CreateItemSlots(entity.DockingBays), - (entity.ChildEntityIndices ?? Array.Empty()) - .Where(index => index >= 0) - .Select(index => DaemonEntityRecordKey(runId, zoneIndex, index)) - .ToArray(), - (entity.WeaponGroups ?? Array.Empty>()) - .Select(group => (IReadOnlyList)(group ?? Array.Empty()).ToArray()) - .ToArray(), - (entity.StatGrids ?? Array.Empty()) - .Select(grid => new AetheriaRuntimeEntityStatGridSnapshot( - grid.Name ?? "", - grid.Width, - grid.Height, - (grid.Values ?? Array.Empty()).ToArray())) - .ToArray(), - entity.VelocityX, - entity.VelocityY, - entity.TargetEntityIndex < 0 ? "" : DaemonEntityRecordKey(runId, zoneIndex, entity.TargetEntityIndex), - entity.IsActive, - entity.HeatsinksEnabled, - entity.OverrideShutdown, - entity.TractorPower, - entity.Heatstroke, - entity.Hypothermia, - (entity.ActiveConsumables ?? Array.Empty()) - .Select(consumable => new AetheriaRuntimeActiveConsumableSnapshot( - consumable.ItemKey ?? "", - consumable.Quality, - consumable.RemainingDuration, - consumable.Duration)) - .ToArray(), - (entity.BehaviorProgress ?? Array.Empty()) - .Select(progress => new AetheriaRuntimeBehaviorProgressSnapshot( - progress.OwnerKind ?? "", - progress.OwnerIndex, - progress.BehaviorIndex, - progress.BehaviorKind ?? "", - progress.Progress)) - .ToArray(), - CreateWeaponStates(runId, zoneIndex, entity.WeaponStates), - CreateBehaviorStates(entity.BehaviorStates), - CreateCargoBays(entity.CargoContents), - CreateCargoBays(entity.DockingBayContents), - (entity.DockingBayAssignments ?? Array.Empty()).ToArray(), - entity.Visibility, - entity.VisibilitySourceCount, - (entity.Contacts ?? Array.Empty()) - .Where(contact => contact != null && contact.TargetEntityIndex >= 0) - .Select(contact => new AetheriaRuntimeEntityContactSnapshot( - DaemonEntityRecordKey(runId, zoneIndex, contact.TargetEntityIndex), - contact.InfoGathered, - contact.Hostile, - contact.Visible)) - .ToArray(), - entity.ShutdownPerformance); - } - - private static AetheriaRuntimeEntityItemSlotSnapshot[] CreateItemSlots( - IReadOnlyList slots) - { - return (slots ?? Array.Empty()) - .Where(slot => slot?.Item != null) - .Select(slot => new AetheriaRuntimeEntityItemSlotSnapshot( - slot.X, - slot.Y, - slot.Item.ItemKey ?? "", - slot.Item.Quality, - slot.Item.Durability, - slot.Item.Quantity, - slot.Item.Enabled, - slot.Item.OverrideShutdown, - slot.Item.Temperature)) - .ToArray(); - } - - private static AetheriaRuntimeCargoBayLoadoutSnapshot[] CreateCargoBays( - IReadOnlyList cargoBays) - { - return (cargoBays ?? Array.Empty()) - .Select(bay => new AetheriaRuntimeCargoBayLoadoutSnapshot( - CreateLoadoutSlots(bay?.Items))) - .ToArray(); - } - - private static AetheriaRuntimeLoadoutItemSlotSnapshot[] CreateLoadoutSlots( - IReadOnlyList slots) - { - return (slots ?? Array.Empty()) - .Where(slot => slot?.Item != null) - .Select(slot => new AetheriaRuntimeLoadoutItemSlotSnapshot( - slot.X, - slot.Y, - new AetheriaRuntimeLoadoutItemSnapshot( - slot.Item.ItemKey ?? "", - slot.Item.Quality, - slot.Item.Durability, - slot.Item.Quantity, - slot.Item.Enabled, - slot.Item.OverrideShutdown, - slot.Item.Temperature))) - .ToArray(); - } - - private static AetheriaRuntimeWeaponStateSnapshot[] CreateWeaponStates( - string runId, - int zoneIndex, - IReadOnlyList weaponStates) - { - return (weaponStates ?? Array.Empty()) - .Where(state => state != null) - .Select(state => new AetheriaRuntimeWeaponStateSnapshot( - state.OwnerKind ?? "", - state.OwnerIndex, - state.BehaviorIndex, - state.BehaviorKind ?? "", - state.Firing, - state.Ammo, - state.BurstRemaining, - state.BurstTimer, - state.BurstInterval, - state.CooldownProgress, - state.CoolingDown, - state.Charging, - state.Charged, - state.Charge, - state.Reloading, - state.ReloadProgress, - state.AmmoIntervalProgress, - state.LockProgress, - state.LockTargetEntityIndex < 0 ? "" : DaemonEntityRecordKey(runId, zoneIndex, state.LockTargetEntityIndex))) - .ToArray(); - } - - private static AetheriaRuntimeBehaviorStateSnapshot[] CreateBehaviorStates( - IReadOnlyList behaviorStates) - { - return (behaviorStates ?? Array.Empty()) - .Where(state => state != null) - .Select(state => new AetheriaRuntimeBehaviorStateSnapshot( - state.OwnerKind ?? "", - state.OwnerIndex, - state.BehaviorIndex, - state.BehaviorKind ?? "", - state.Pinging, - state.PingCooldown, - state.PingLerp, - state.PingRadius, - state.PingedEntityCount, - state.RadiatorTemperature, - state.Emissivity, - state.PumpedHeat, - state.WasteHeat, - state.EnergyUsage, - state.ReactorDraw, - state.ReactorLoadRatio, - state.CapacitorCharge, - state.CapacitorCapacity, - state.CapacitorEfficiency, - state.AetherDriveAxisX, - state.AetherDriveAxisY, - state.AetherDriveAxisZ, - state.AetherDriveThrustX, - state.AetherDriveThrustY, - state.AetherDriveThrustZ, - state.AetherDriveRpmX, - state.AetherDriveRpmY, - state.AetherDriveRpmZ, - state.AetherDriveMaximumRpm, - state.AetherDriveThrustDirectionX, - state.AetherDriveThrustDirectionY, - state.ResourceScannerTargetBodyKey ?? "", - state.ResourceScannerAsteroidIndex, - state.ResourceScannerScanTime, - state.ResourceScannerRange, - state.ResourceScannerMinimumDensity, - state.ResourceScannerScanDuration, - state.MiningToolAsteroidBeltKey ?? "", - state.MiningToolAsteroidIndex, - state.MiningToolRange, - state.ThrusterAxis, - state.ThrusterThrust, - state.ThrusterTorque, - state.ShieldEfficiency, - state.ShieldEnergyUsage, - state.VelocityLimit, - state.ThermotoggleTargetTemperature, - state.SwitchActivated, - state.TriggerPulled, - state.StatModifierApplied, - state.StatModifierExecuted, - state.StatModifierTargetStatCount, - state.TurretControllerWeaponCount, - state.TurretControllerShotSpeed, - state.TurretControllerPredictShots)) - .ToArray(); - } -} diff --git a/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs.meta b/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs.meta deleted file mode 100644 index 159a65267..000000000 --- a/Assets/Scripts/Gameplay/AetheriaUnityDaemonEntitySnapshotProjector.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: e81930c7560adfe48b4f959791ab3572 \ No newline at end of file diff --git a/Assets/Scripts/Gameplay/AetheriaUnityObservedFrameApplier.cs b/Assets/Scripts/Gameplay/AetheriaUnityObservedFrameApplier.cs index a9ece783d..a20c4427b 100644 --- a/Assets/Scripts/Gameplay/AetheriaUnityObservedFrameApplier.cs +++ b/Assets/Scripts/Gameplay/AetheriaUnityObservedFrameApplier.cs @@ -90,7 +90,7 @@ public sealed class AetheriaUnityObservedFrameApplier return false; } - var entitySnapshots = AetheriaUnityDaemonEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots) + var entitySnapshots = AetheriaRuntimeEntitySnapshotProjector.CreateSnapshots(runId, render.ZoneIndex, render.EntitySnapshots) .OrderBy(entity => entity.EntityIndex) .ToArray(); diff --git a/GameCult.Aetheria.State.Unity.csproj b/GameCult.Aetheria.State.Unity.csproj index c4bf9528e..51354eb22 100644 --- a/GameCult.Aetheria.State.Unity.csproj +++ b/GameCult.Aetheria.State.Unity.csproj @@ -123,6 +123,7 @@ + diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs new file mode 100644 index 000000000..116bf7469 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +#nullable enable + +namespace GameCult.Aetheria.State.Verse +{ + public static class AetheriaRuntimeEntitySnapshotProjector + { + public static AetheriaRuntimeEntitySnapshot[] CreateSnapshots( + string runId, + AetheriaRuntimeZoneSnapshotCommit? zone) + { + if (zone == null) + return Array.Empty(); + + return CreateSnapshots(runId, zone.ZoneIndex, zone.Entities); + } + + public static AetheriaRuntimeEntitySnapshot[] CreateSnapshots( + string runId, + int zoneIndex, + IReadOnlyList? entities) + { + return (entities ?? Array.Empty()) + .Where(entity => entity != null) + .Select(entity => CreateSnapshot(runId, zoneIndex, entity)) + .ToArray(); + } + + public static string DaemonEntityRecordKey(string runId, int zoneIndex, int entityIndex) + { + return string.IsNullOrWhiteSpace(runId) + ? "" + : $"global:aetheria.run_state.{runId}.zone.{zoneIndex}.entity.{entityIndex}.v1"; + } + + private static AetheriaRuntimeEntitySnapshot CreateSnapshot( + string runId, + int zoneIndex, + AetheriaRuntimeEntitySnapshotCommit entity) + { + return new AetheriaRuntimeEntitySnapshot( + DaemonEntityRecordKey(runId, zoneIndex, entity.EntityIndex), + entity.EntityIndex, + entity.Name ?? "", + entity.Kind ?? "", + entity.PositionX, + entity.PositionY, + entity.PositionZ, + entity.DirectionX, + entity.DirectionY, + entity.FactionKey ?? "", + entity.HullItemKey ?? "", + CreateItemSlots(entity.Equipment), + CreateItemSlots(entity.CargoBays), + CreateItemSlots(entity.DockingBays), + (entity.ChildEntityIndices ?? Array.Empty()) + .Where(index => index >= 0) + .Select(index => DaemonEntityRecordKey(runId, zoneIndex, index)) + .ToArray(), + (entity.WeaponGroups ?? Array.Empty>()) + .Select(group => (IReadOnlyList)(group ?? Array.Empty()).ToArray()) + .ToArray(), + (entity.StatGrids ?? Array.Empty()) + .Select(grid => new AetheriaRuntimeEntityStatGridSnapshot( + grid.Name ?? "", + grid.Width, + grid.Height, + (grid.Values ?? Array.Empty()).ToArray())) + .ToArray(), + entity.VelocityX, + entity.VelocityY, + entity.TargetEntityIndex < 0 ? "" : DaemonEntityRecordKey(runId, zoneIndex, entity.TargetEntityIndex), + entity.IsActive, + entity.HeatsinksEnabled, + entity.OverrideShutdown, + entity.TractorPower, + entity.Heatstroke, + entity.Hypothermia, + (entity.ActiveConsumables ?? Array.Empty()) + .Select(consumable => new AetheriaRuntimeActiveConsumableSnapshot( + consumable.ItemKey ?? "", + consumable.Quality, + consumable.RemainingDuration, + consumable.Duration)) + .ToArray(), + (entity.BehaviorProgress ?? Array.Empty()) + .Select(progress => new AetheriaRuntimeBehaviorProgressSnapshot( + progress.OwnerKind ?? "", + progress.OwnerIndex, + progress.BehaviorIndex, + progress.BehaviorKind ?? "", + progress.Progress)) + .ToArray(), + CreateWeaponStates(runId, zoneIndex, entity.WeaponStates), + CreateBehaviorStates(entity.BehaviorStates), + CreateCargoBays(entity.CargoContents), + CreateCargoBays(entity.DockingBayContents), + (entity.DockingBayAssignments ?? Array.Empty()).ToArray(), + entity.Visibility, + entity.VisibilitySourceCount, + (entity.Contacts ?? Array.Empty()) + .Where(contact => contact != null && contact.TargetEntityIndex >= 0) + .Select(contact => new AetheriaRuntimeEntityContactSnapshot( + DaemonEntityRecordKey(runId, zoneIndex, contact.TargetEntityIndex), + contact.InfoGathered, + contact.Hostile, + contact.Visible)) + .ToArray(), + entity.ShutdownPerformance); + } + + private static AetheriaRuntimeEntityItemSlotSnapshot[] CreateItemSlots( + IReadOnlyList? slots) + { + return (slots ?? Array.Empty()) + .Where(slot => slot?.Item != null) + .Select(slot => new AetheriaRuntimeEntityItemSlotSnapshot( + slot.X, + slot.Y, + slot.Item!.ItemKey ?? "", + slot.Item.Quality, + slot.Item.Durability, + slot.Item.Quantity, + slot.Item.Enabled, + slot.Item.OverrideShutdown, + slot.Item.Temperature)) + .ToArray(); + } + + private static AetheriaRuntimeCargoBayLoadoutSnapshot[] CreateCargoBays( + IReadOnlyList? cargoBays) + { + return (cargoBays ?? Array.Empty()) + .Select(bay => new AetheriaRuntimeCargoBayLoadoutSnapshot( + CreateLoadoutSlots(bay?.Items))) + .ToArray(); + } + + private static AetheriaRuntimeLoadoutItemSlotSnapshot[] CreateLoadoutSlots( + IReadOnlyList? slots) + { + return (slots ?? Array.Empty()) + .Where(slot => slot?.Item != null) + .Select(slot => new AetheriaRuntimeLoadoutItemSlotSnapshot( + slot.X, + slot.Y, + new AetheriaRuntimeLoadoutItemSnapshot( + slot.Item!.ItemKey ?? "", + slot.Item.Quality, + slot.Item.Durability, + slot.Item.Quantity, + slot.Item.Enabled, + slot.Item.OverrideShutdown, + slot.Item.Temperature))) + .ToArray(); + } + + private static AetheriaRuntimeWeaponStateSnapshot[] CreateWeaponStates( + string runId, + int zoneIndex, + IReadOnlyList? weaponStates) + { + return (weaponStates ?? Array.Empty()) + .Where(state => state != null) + .Select(state => new AetheriaRuntimeWeaponStateSnapshot( + state.OwnerKind ?? "", + state.OwnerIndex, + state.BehaviorIndex, + state.BehaviorKind ?? "", + state.Firing, + state.Ammo, + state.BurstRemaining, + state.BurstTimer, + state.BurstInterval, + state.CooldownProgress, + state.CoolingDown, + state.Charging, + state.Charged, + state.Charge, + state.Reloading, + state.ReloadProgress, + state.AmmoIntervalProgress, + state.LockProgress, + state.LockTargetEntityIndex < 0 ? "" : DaemonEntityRecordKey(runId, zoneIndex, state.LockTargetEntityIndex))) + .ToArray(); + } + + private static AetheriaRuntimeBehaviorStateSnapshot[] CreateBehaviorStates( + IReadOnlyList? behaviorStates) + { + return (behaviorStates ?? Array.Empty()) + .Where(state => state != null) + .Select(state => new AetheriaRuntimeBehaviorStateSnapshot( + state.OwnerKind ?? "", + state.OwnerIndex, + state.BehaviorIndex, + state.BehaviorKind ?? "", + state.Pinging, + state.PingCooldown, + state.PingLerp, + state.PingRadius, + state.PingedEntityCount, + state.RadiatorTemperature, + state.Emissivity, + state.PumpedHeat, + state.WasteHeat, + state.EnergyUsage, + state.ReactorDraw, + state.ReactorLoadRatio, + state.CapacitorCharge, + state.CapacitorCapacity, + state.CapacitorEfficiency, + state.AetherDriveAxisX, + state.AetherDriveAxisY, + state.AetherDriveAxisZ, + state.AetherDriveThrustX, + state.AetherDriveThrustY, + state.AetherDriveThrustZ, + state.AetherDriveRpmX, + state.AetherDriveRpmY, + state.AetherDriveRpmZ, + state.AetherDriveMaximumRpm, + state.AetherDriveThrustDirectionX, + state.AetherDriveThrustDirectionY, + state.ResourceScannerTargetBodyKey ?? "", + state.ResourceScannerAsteroidIndex, + state.ResourceScannerScanTime, + state.ResourceScannerRange, + state.ResourceScannerMinimumDensity, + state.ResourceScannerScanDuration, + state.MiningToolAsteroidBeltKey ?? "", + state.MiningToolAsteroidIndex, + state.MiningToolRange, + state.ThrusterAxis, + state.ThrusterThrust, + state.ThrusterTorque, + state.ShieldEfficiency, + state.ShieldEnergyUsage, + state.VelocityLimit, + state.ThermotoggleTargetTemperature, + state.SwitchActivated, + state.TriggerPulled, + state.StatModifierApplied, + state.StatModifierExecuted, + state.StatModifierTargetStatCount, + state.TurretControllerWeaponCount, + state.TurretControllerShotSpeed, + state.TurretControllerPredictShots)) + .ToArray(); + } + } +} diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs.meta b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs.meta new file mode 100644 index 000000000..d01e8c8a1 --- /dev/null +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeEntitySnapshotProjector.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1625cd76fccf6284bb9d97aa5b755a59 \ No newline at end of file