From 6deea479dd9c492e67cdbee727ea72caba5e15cd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 14 Jun 2026 16:40:08 +0100 Subject: [PATCH] Persist turret controller state in typed snapshots --- .voidbot/state/aetheria.cc | Bin 86086 -> 86426 bytes Aetheria.State.Smoke/Program.cs | 16 ++++++++++++- Aetheria.State.Unity.Smoke/Program.cs | 21 ++++++++++++++++-- .../AetheriaRuntimeCommitLogApplier.cs | 5 ++++- .../AetheriaRuntimeStateDocuments.cs | 9 ++++++++ Assets/Scripts/Gameplay/ActionGameManager.cs | 9 ++++++++ .../Behaviors/TurretController.cs | 4 ++++ .../Runtime/AetheriaRuntimeCatalogSnapshot.cs | 11 ++++++++- .../Runtime/AetheriaRuntimeCatalogStore.cs | 10 +++++++-- .../AetheriaRuntimeStateCommitDocument.cs | 9 ++++++++ docs/aetheria-perfect-machine-map.md | 5 ++++- 11 files changed, 91 insertions(+), 8 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index c6e4ca17a19f6fde2f9da3acedc9ca96e8e35678..1923686dee4c6f44ce3cbcf5bfa29b075703d6ba 100644 GIT binary patch delta 1104 zcmai!zfTlF6vy-Up`wOZC>RrUF!2ZB?g069iUN1sQ6Un!MuWw$JI4*~?F=)sz-d7u zozcQ9$rxjSp|I6NL#rn82Uuy0g}oM9TljYGg8WD{xn|#cGw=KT%zL-F*7$V2@#TxU z1D7uMTuh~Vdu?|fTEY)?5BJw1wkr=O!=OH>1^U9D_5F33+biu28|@8{9hZhGC6t%#uR-XU`;=j(#1yAQGNrjT zn*8(NBBN6H#1VmS5=1kPIR@|3C1Hq`jJXLyeV0hZT<#dYz>?UC6g@g?=dZxh^2b6& zV!cD92^8y8jJh+_xkI$!o=054H1YAOGNV2z1{jJAa6e!~F>Qomglp_xmL4-~kSLVF z%Z$vh5?$azVHM)gMg`a$bw*mKlpc3z{5>U#TeVeMM2SUBVh!9kf_SJIQ6Wj+K{1Mz zDSi=(UL{qC7rSq3-~=X|e{*#CYXPm8z3imo3(=vN0(B5C#h(}Xt`HQ6@QI0$XrIa$ zqok&k`s;h9La+%knB79KLjjsDa=U&Nx>AXl9Uh>r8`a~ZqGX&5k05Ov6{0D+leFRq z>e{nIFx}dH1Boej_p{s8A!rLwlN1|wXN^(i?`nL p9K5Y8?1mac>OXH#JrND$+f-{s5kyiBbRn delta 938 zcma)5-%C_M6z2G&+>-vMlKs1shg7Mz7yHoexxy{T?mxw@L zgNO?aR8S$*OHdFY1VIG#>YLD04?(ZtKM>8md(DMjdYo_0cfRj@Gw1Pk{nzL9Z(r4< zG8cwMdx!hatv-a-@Jr481Q_U#0bYCOXr*Ex+^-$3$)>it>vp>9VB~yD7e!SU^RA~7Nra~$F!+J6UeQTqHQ5=n1kpTnt5nCZ(r%UHFjXYOMqW}`5z>{_ID zcNj{3TZcHV?-=^AQ~)JyTfXO7>P9E=CUZgCWSky#788 zC-l=PNL9OgHw9<)avr|wXL;z=Kc=B@{Ixv%_E%?MHrbrPc$M_W8TfR7j3&EuM8B8? dKUt`ZS*SMMHwT+ZStcg#?9RdG)B!ZI`UmX5OQiq+ diff --git a/Aetheria.State.Smoke/Program.cs b/Aetheria.State.Smoke/Program.cs index f9296294e..1ae940fa3 100644 --- a/Aetheria.State.Smoke/Program.cs +++ b/Aetheria.State.Smoke/Program.cs @@ -355,6 +355,16 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s StatModifierApplied = true, StatModifierExecuted = true, StatModifierTargetStatCount = 2 + }, + new AetheriaBehaviorStateSnapshot + { + OwnerKind = "equipment", + OwnerIndex = 0, + BehaviorIndex = 10, + BehaviorKind = "TurretController", + TurretControllerWeaponCount = 2, + TurretControllerShotSpeed = 80.0, + TurretControllerPredictShots = true } ], StatGrids = @@ -618,7 +628,7 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether entitySnapshot.WeaponStates[0].BehaviorKind != "LockWeapon" || entitySnapshot.WeaponStates[0].LockProgress != 0.65 || entitySnapshot.WeaponStates[0].LockTargetEntityKey != entityKey.ToString() || - entitySnapshot.BehaviorStates.Length != 10 || + entitySnapshot.BehaviorStates.Length != 11 || entitySnapshot.BehaviorStates[0].BehaviorKind != "AetherDrive" || entitySnapshot.BehaviorStates[0].AetherDriveAxisX != 0.5 || entitySnapshot.BehaviorStates[0].AetherDriveRpmY != 900.0 || @@ -651,6 +661,10 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether !entitySnapshot.BehaviorStates[9].StatModifierApplied || !entitySnapshot.BehaviorStates[9].StatModifierExecuted || entitySnapshot.BehaviorStates[9].StatModifierTargetStatCount != 2 || + entitySnapshot.BehaviorStates[10].BehaviorKind != "TurretController" || + entitySnapshot.BehaviorStates[10].TurretControllerWeaponCount != 2 || + entitySnapshot.BehaviorStates[10].TurretControllerShotSpeed != 80.0 || + !entitySnapshot.BehaviorStates[10].TurretControllerPredictShots || entitySnapshot.StatGrids.Length != 1 || entitySnapshot.StatGrids[0].Values.Length != 4) { diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 2027a9215..a965e34df 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -584,6 +584,16 @@ try StatModifierApplied = true, StatModifierExecuted = true, StatModifierTargetStatCount = 2 + }, + new AetheriaRuntimeBehaviorStateCommit + { + OwnerKind = "equipment", + OwnerIndex = 14, + BehaviorIndex = 0, + BehaviorKind = "TurretController", + TurretControllerWeaponCount = 2, + TurretControllerShotSpeed = 80.0, + TurretControllerPredictShots = true } }, StatGrids = new[] @@ -704,7 +714,7 @@ try entity.WeaponStates[3].BehaviorKind == "LockWeapon" && entity.WeaponStates[3].LockProgress == 0.65 && entity.WeaponStates[3].LockTargetEntityKey == "global:aetheria.run_state.smoke-run.zone.0.entity.0.v1" && - entity.BehaviorStates.Length == 14 && + entity.BehaviorStates.Length == 15 && entity.BehaviorStates[0].BehaviorKind == "Sensor" && entity.BehaviorStates[0].Pinging && entity.BehaviorStates[0].PingRadius == 1200.0 && @@ -749,6 +759,10 @@ try entity.BehaviorStates[13].StatModifierApplied && entity.BehaviorStates[13].StatModifierExecuted && entity.BehaviorStates[13].StatModifierTargetStatCount == 2 && + entity.BehaviorStates[14].BehaviorKind == "TurretController" && + entity.BehaviorStates[14].TurretControllerWeaponCount == 2 && + entity.BehaviorStates[14].TurretControllerShotSpeed == 80.0 && + entity.BehaviorStates[14].TurretControllerPredictShots && entity.WeaponGroups.Length == 1 && entity.StatGrids.Length == 2 && entity.StatGrids.Any(grid => grid.Name == "temperature" && grid.Values.Length == 2 && grid.Values[1] == 281.0) && @@ -804,7 +818,7 @@ try packageEntities[0].WeaponStates[2].AmmoIntervalProgress != 0.6 || packageEntities[0].WeaponStates[3].LockProgress != 0.65 || packageEntities[0].WeaponStates[3].LockTargetEntityKey != "global:aetheria.run_state.smoke-run.zone.0.entity.0.v1" || - packageEntities[0].BehaviorStates.Count != 14 || + packageEntities[0].BehaviorStates.Count != 15 || packageEntities[0].BehaviorStates[3].CapacitorCharge != 7.5 || packageEntities[0].BehaviorStates[4].AetherDriveRpmY != 900.0 || packageEntities[0].BehaviorStates[4].AetherDriveMaximumRpm != 2400.0 || @@ -821,6 +835,9 @@ try !packageEntities[0].BehaviorStates[12].TriggerPulled || !packageEntities[0].BehaviorStates[13].StatModifierApplied || packageEntities[0].BehaviorStates[13].StatModifierTargetStatCount != 2 || + packageEntities[0].BehaviorStates[14].TurretControllerWeaponCount != 2 || + packageEntities[0].BehaviorStates[14].TurretControllerShotSpeed != 80.0 || + !packageEntities[0].BehaviorStates[14].TurretControllerPredictShots || packageEntities[0].StatGrids.Count != 2 || packageEntities[0].StatGrids.All(grid => grid.Name != "temperature" || grid.Values.Count != 2 || grid.Values[1] != 281.0)) { diff --git a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs index ccbc231c6..869041a94 100644 --- a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs +++ b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs @@ -519,7 +519,10 @@ public static class AetheriaRuntimeCommitLogApplier TriggerPulled = state.TriggerPulled, StatModifierApplied = state.StatModifierApplied, StatModifierExecuted = state.StatModifierExecuted, - StatModifierTargetStatCount = state.StatModifierTargetStatCount + StatModifierTargetStatCount = state.StatModifierTargetStatCount, + TurretControllerWeaponCount = state.TurretControllerWeaponCount, + TurretControllerShotSpeed = state.TurretControllerShotSpeed, + TurretControllerPredictShots = state.TurretControllerPredictShots }) .ToArray(); } diff --git a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs index 449070b8d..ce594a17d 100644 --- a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs +++ b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs @@ -802,6 +802,15 @@ public sealed class AetheriaBehaviorStateSnapshot [Key(51)] public int StatModifierTargetStatCount { get; set; } + + [Key(52)] + public int TurretControllerWeaponCount { get; set; } + + [Key(53)] + public double TurretControllerShotSpeed { get; set; } + + [Key(54)] + public bool TurretControllerPredictShots { get; set; } } [MessagePackObject] diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index f80427f0d..8bb345c85 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -981,6 +981,15 @@ public class ActionGameManager : MonoBehaviour StatModifierTargetStatCount = statModifier.TargetStatCount }; } + else if (behavior is TurretController turretController) + { + state = new AetheriaRuntimeBehaviorStateCommit + { + TurretControllerWeaponCount = turretController.WeaponCount, + TurretControllerShotSpeed = turretController.ShotSpeed, + TurretControllerPredictShots = turretController.PredictShots + }; + } if (state == null) continue; diff --git a/Assets/Scripts/ServerShared/Behaviors/TurretController.cs b/Assets/Scripts/ServerShared/Behaviors/TurretController.cs index f1458d412..2a610344a 100644 --- a/Assets/Scripts/ServerShared/Behaviors/TurretController.cs +++ b/Assets/Scripts/ServerShared/Behaviors/TurretController.cs @@ -27,6 +27,10 @@ public class TurretController : Behavior, IInitializableBehavior private float _shotSpeed; private bool _predictShots; + public int WeaponCount => _weapons.Count; + public float ShotSpeed => _shotSpeed; + public bool PredictShots => _predictShots; + public TurretController(TurretControllerData data, EquippedItem item) : base(data, item) { _data = data; diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs index 614bb842c..f20145783 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs @@ -1119,7 +1119,10 @@ namespace GameCult.Aetheria.State.Unity bool triggerPulled, bool statModifierApplied, bool statModifierExecuted, - int statModifierTargetStatCount) + int statModifierTargetStatCount, + int turretControllerWeaponCount, + double turretControllerShotSpeed, + bool turretControllerPredictShots) { OwnerKind = ownerKind; OwnerIndex = ownerIndex; @@ -1173,6 +1176,9 @@ namespace GameCult.Aetheria.State.Unity StatModifierApplied = statModifierApplied; StatModifierExecuted = statModifierExecuted; StatModifierTargetStatCount = statModifierTargetStatCount; + TurretControllerWeaponCount = turretControllerWeaponCount; + TurretControllerShotSpeed = turretControllerShotSpeed; + TurretControllerPredictShots = turretControllerPredictShots; } public string OwnerKind { get; } @@ -1227,5 +1233,8 @@ namespace GameCult.Aetheria.State.Unity public bool StatModifierApplied { get; } public bool StatModifierExecuted { get; } public int StatModifierTargetStatCount { get; } + public int TurretControllerWeaponCount { get; } + public double TurretControllerShotSpeed { get; } + public bool TurretControllerPredictShots { get; } } } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index 549847110..96631cb02 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -909,7 +909,10 @@ namespace GameCult.Aetheria.State.Unity var statModifierApplied = ReadFieldBool(ref reader, behaviorFields, 49); var statModifierExecuted = ReadFieldBool(ref reader, behaviorFields, 50); var statModifierTargetStatCount = ReadFieldInt32(ref reader, behaviorFields, 51); - SkipRemaining(ref reader, behaviorFields, 52); + var turretControllerWeaponCount = ReadFieldInt32(ref reader, behaviorFields, 52); + var turretControllerShotSpeed = ReadFieldDouble(ref reader, behaviorFields, 53); + var turretControllerPredictShots = ReadFieldBool(ref reader, behaviorFields, 54); + SkipRemaining(ref reader, behaviorFields, 55); behaviorStates[behavior] = new AetheriaRuntimeBehaviorStateSnapshot( ownerKind, ownerIndex, @@ -962,7 +965,10 @@ namespace GameCult.Aetheria.State.Unity triggerPulled, statModifierApplied, statModifierExecuted, - statModifierTargetStatCount); + statModifierTargetStatCount, + turretControllerWeaponCount, + turretControllerShotSpeed, + turretControllerPredictShots); } return behaviorStates; diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs index b0c7ecbaf..39a638a52 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs @@ -851,5 +851,14 @@ namespace GameCult.Aetheria.State.Unity [Key(51)] public int StatModifierTargetStatCount { get; set; } + + [Key(52)] + public int TurretControllerWeaponCount { get; set; } + + [Key(53)] + public double TurretControllerShotSpeed { get; set; } + + [Key(54)] + public bool TurretControllerPredictShots { get; set; } } } diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 08bd75ebd..6c5ee6933 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -179,7 +179,8 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. energy usage, velocity limiters publish evaluated limit, and thermotoggles publish their active target temperature. Switches publish activated state, triggers publish pending pulled state, and stat modifiers publish - applied/executed flags plus target-stat count. + applied/executed flags plus target-stat count. Turret controllers publish + initialized weapon count, shot speed, and predictive-aim flag. `RuntimeEntityBlueprint` still exists for construction/loadout projection, but those live hull grids and session scalars are not blueprint fields and cannot be restored by the old blueprint projector path. @@ -828,6 +829,8 @@ First Aetheria surfaces to publish: pending pull state. - Done: extend behavior state rows for StatModifier applied/executed flags and target-stat count. + - Done: extend behavior state rows for TurretController initialized weapon + count, shot speed, and predictive-aim flag. - Done: cut live simulation grids out of `RuntimeEntityBlueprint`; loadout and construction templates no longer capture or restore temperature, armor, max-armor, or hull-conductivity state.