From 101d23712947a7c28ecd8462b19d793c664ccfff Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 16 Jun 2026 05:51:07 +0100 Subject: [PATCH] Lower dropped pickups from typed zone state --- .voidbot/state/aetheria.cc | Bin 145736 -> 145748 bytes Aetheria.State.Unity.Smoke/Program.cs | 1 + Aetheria.State.Verify/Program.cs | 10 +++- Assets/Scripts/Gameplay/ActionGameManager.cs | 44 ++++++++++++++++++ .../Runtime/AetheriaRuntimeCatalogSnapshot.cs | 3 ++ .../Runtime/AetheriaRuntimeCatalogStore.cs | 5 +- docs/aetheria-perfect-machine-map.md | 18 ++++--- 7 files changed, 70 insertions(+), 11 deletions(-) diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index 7b237ca2846077624fe35f750f19881f798e3b67..c13e868338a70dc8f3a47cc2eb3201ea5de672df 100644 GIT binary patch delta 1470 zcma)6&5ImG6t`w$m?(>y7#2a~As!Og>HSE^j=AWfMArBbCabVZNT}_8GhI%1Ra;fP zlSxyS=jcB|AL+vvH-eyn4LtEo$acZ8@>%EzNzv=58F_cly84 zh4o>E5ky*L^xMir*v~V#q$Gk?0|QbGAW;L%fvq9nMIPEJ z5<{gU=;JW>HcYrk|7I_*#|R%tVTX`WBHqK&6*Te)QWarB88T(iK*xul#^F5h@$2WF z9|avWR%v{*M0v)8G^eymJ^D>Td)9JGkSUn~OQlk7!&t~3^b)njlan0Dss#^pE-x78 z99*WywP+`B%?6j<_nM6G;!d!=RAMudXC_v51iWjB0avGky>OoKQEQW#7{bKcNc4&0_r&GDjH9i9G{sUq$Pa?{e z8}#ELX;k=H9#p9@M`5N#Dy&l&mk~*V7>Ynx-(7igK7V#{jIo%px5vBp;E3O<$JFi{-XrnJ>PMRD}VBY#I6m;Q`YyHc}VTua!w)z!19 z>XQHrVIWQKIT+L9;OVOFR=ovv{z47e;i8kWBL}PNNVo8seITvct2>U%DlYn&Beb#4sCOZp_KMqu0$Clcg4*~Yu7Nh_G delta 1387 zcmZWp&5ImG6t{ZwG0BpPF4-&!`#=Z@PWQ6zgxx423&Bey#7#ulNVKN=Wx6umRh_Ep znaqML-qc(YI?@+G5R*VeQFEBw77wC-fEUGoz>8?Wo1m|2YL;wr>#nNz@%wo1mq)Fi zAGN-{8ysBjywX{VR;s^dj#WPfH(LJxbp3u9%vWk=>(Jz+^Kz%=VQ{o+?E^OZ;N-ru ze{P>Wqg7F00z;*<1PT%N%fbNH2vQBfj+(nIB{4LT7baB}jO7+VNYJH(IK{YMC?Tzh z?!*}v`ETs_#-0lo4%2Ys^2S{25uDK*uK0Ohbtr7=TcG?2UT;I8jIct zl&K+V-}!`S&}wL)OO**PHJOP4f{9TgsP0vaiBxDB-aukQQ?Rij%RmjeB#mO*JRb;C z@@z_*4}+xCLiT_+BFW~yrJB-7BPDRX?9;wMOIWY8EB!Q`Jkerr3fgdCu>P0t=~7?R|K&2Yk*k({73 zbVmg@P7_b%t_2@_^3VKi{n4lF$f=2MJ;@WpHm=c_bK&UNqe;V0j~CU=`zPzmbL_eL z{AX-&e2%e{1?FPK)xta83j~=LLX+z3&;=J!Vq5qP^X-c;tCX79$y<5AO@`0e{ z`($glLiJD=B#<5d7qG8bu(?q`{~}v>z44yeqj z+bqWwe|4uOB%10 zf(UD734G;nT6uT=U7q^$jJ;dEcl6*YX@r{Q?}<)bg;3IXxiNlx=$bKz>Rat#u6`?L zC!Y0dY`L@frR6>I`!gSmZ{+OX1LND4eeuxmw3X#f{aq9+_@&obt^S^wuZLl DroppedPickups" }, [Path.Combine(root, "Assets", "Scripts", "Gameplay", "ActionGameManager.cs")] = new[] { "ProjectDroppedPickups", - "DroppedPickups = ProjectDroppedPickups(zone)" + "DroppedPickups = ProjectDroppedPickups(zone)", + "RestoreDroppedPickupsFromTypedZoneState", + "AetheriaRuntimeCatalogStore.ReadZoneStates(RuntimeStateFilePath)", + "ZoneRenderer.DropItem(" }, [Path.Combine(root, "Assets", "Scripts", "Zone Display", "ZoneRenderer.cs")] = new[] { @@ -1266,6 +1271,7 @@ static void RequireDroppedPickupCheckpointState(string root) [Path.Combine(root, "Aetheria.State.Unity.Smoke", "Program.cs")] = new[] { "new AetheriaRuntimeDroppedPickupCommit", + "packageZones[0].RecordKey", "packageZones[0].DroppedPickups" } }; diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index 613bea880..67b8ee0de 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -2316,6 +2316,7 @@ public class ActionGameManager : MonoBehaviour } ZoneRenderer.LoadZone(Zone); + RestoreDroppedPickupsFromTypedZoneState(galaxyZone); if (CurrentEntity != null) { @@ -2324,6 +2325,49 @@ public class ActionGameManager : MonoBehaviour } } + private void RestoreDroppedPickupsFromTypedZoneState(GalaxyZone galaxyZone) + { + if (RuntimeCatalog == null || + ZoneRenderer == null || + galaxyZone == null) + { + return; + } + + var zoneIndex = ZoneIndex(galaxyZone); + if (zoneIndex < 0) + return; + + var runId = IsTutorial ? "tutorial" : "local"; + var zoneKey = $"global:aetheria.run_state.{runId}.zone.{zoneIndex}.v1"; + AetheriaRuntimeZoneStateSnapshot zoneState; + try + { + zoneState = AetheriaRuntimeCatalogStore.ReadZoneStates(RuntimeStateFilePath) + .FirstOrDefault(zone => string.Equals(zone.RecordKey, zoneKey, StringComparison.Ordinal)); + } + catch (Exception ex) + { + Debug.LogError($"Failed to read typed dropped pickup state for zone {zoneKey}: {ex}"); + return; + } + + if (zoneState?.DroppedPickups == null || zoneState.DroppedPickups.Count == 0) + return; + + foreach (var pickup in zoneState.DroppedPickups.OrderBy(pickup => pickup.PickupIndex)) + { + var item = CreateLoadoutItem(pickup.Item); + if (item == null) + continue; + + ZoneRenderer.DropItem( + new Vector3((float)pickup.PositionX, (float)pickup.PositionY, (float)pickup.PositionZ), + new Vector3((float)pickup.VelocityX, (float)pickup.VelocityY, (float)pickup.VelocityZ), + item); + } + } + private void ToggleFullscreenMenu(GameObject menu) { if (EventSystem.current != null && EventSystem.current.currentSelectedGameObject != null && EventSystem.current.currentSelectedGameObject.GetComponent() != null) return; diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs index 482f59459..10020dfae 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs @@ -774,6 +774,7 @@ namespace GameCult.Aetheria.State.Unity public sealed class AetheriaRuntimeZoneStateSnapshot { public AetheriaRuntimeZoneStateSnapshot( + string recordKey, string name, double positionX, double positionY, @@ -785,6 +786,7 @@ namespace GameCult.Aetheria.State.Unity IReadOnlyList bodies, IReadOnlyList droppedPickups) { + RecordKey = recordKey; Name = name; PositionX = positionX; PositionY = positionY; @@ -797,6 +799,7 @@ namespace GameCult.Aetheria.State.Unity DroppedPickups = droppedPickups; } + public string RecordKey { get; } public string Name { get; } public double PositionX { get; } public double PositionY { get; } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index eee6ec794..f40842c1f 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -143,7 +143,7 @@ namespace GameCult.Aetheria.State.Unity schemaName != ZoneStateSchema) continue; - zones.Add(ReadZoneStatePayload(record.Payload)); + zones.Add(ReadZoneStatePayload(record.Key, record.Payload)); } return zones.OrderBy(zone => zone.Name, StringComparer.Ordinal).ToArray(); @@ -479,7 +479,7 @@ namespace GameCult.Aetheria.State.Unity generationSeed); } - private static AetheriaRuntimeZoneStateSnapshot ReadZoneStatePayload(byte[] payload) + private static AetheriaRuntimeZoneStateSnapshot ReadZoneStatePayload(string recordKey, byte[] payload) { var reader = new MessagePackReader(payload); var fields = reader.ReadArrayHeader(); @@ -494,6 +494,7 @@ namespace GameCult.Aetheria.State.Unity var droppedPickups = ReadFieldDroppedPickups(ref reader, fields, 8); SkipRemaining(ref reader, fields, 9); return new AetheriaRuntimeZoneStateSnapshot( + recordKey, name, position.X, position.Y, diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index 2868c12fb..adf13584a 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -958,11 +958,15 @@ First Aetheria surfaces to publish: checkpoints now carry pickup index, position, velocity, item key, quality, durability, quantity, and package readback through `AetheriaRuntimeZoneStateSnapshot.DroppedPickups`. + - Done: lower typed dropped-pickup zone rows back into live scene pickups on + zone load. `AetheriaRuntimeZoneStateSnapshot.RecordKey` preserves the exact + typed zone identity, and `ActionGameManager` restores only the matching + `RunId + ZoneIndex` pickup rows through `ZoneRenderer.DropItem`. - Remaining: add typed documents/mappers for runtime object graphs, - restore/lowering of dropped pickups back into live scene objects, typed - behavior factory construction, remaining behavior-private state not covered - by progress, weapon, sensor, radiator, reactor, or capacitor rows, and any - catalog fields not covered by the stable scalar/fingerprint/payload pass. + complete Continue/run restore from typed state, typed behavior factory + construction, remaining behavior-private state not covered by progress, + weapon, sensor, radiator, reactor, or capacitor rows, and any catalog + fields not covered by the stable scalar/fingerprint/payload pass. 4. Runtime cutover - Done: add a Unity-facing typed catalog read facade and smoke proving it can @@ -1423,8 +1427,8 @@ First Aetheria surfaces to publish: death and may spawn the local destruction effect, but gameplay owns equipment/cargo drop decisions, zone entity removal, and the typed run checkpoint. Dropped world pickups are now projected into typed zone - snapshot state; restoring/lowering those typed pickup rows back into live - scene objects remains future runtime object graph work. + snapshot state and lowered back into live scene pickups by exact typed zone + record key during zone load. - Replace the next concrete uGUI screen with an Eve-owned surface. - Move the staged packages into the Eve repo once its worktree is clean, then import them back into Aetheria from Eve instead of carrying a local copy. @@ -1764,7 +1768,7 @@ follows the same rule: collision code reports the pickup opportunity, gameplay owns cargo storage and checkpoint. Entity destruction follows the same rule: instance code observes death, gameplay owns drop decisions, zone removal, and checkpoint. Dropped world pickups are typed zone-snapshot state, not only -renderer-local objects; restore/lowering work must consume that typed state +renderer-local objects; live lowering consumes the exact typed zone record key instead of rehydrating a parallel presentation list. Any predicate that still needs legacy DTO objects must earn that dependency by using behavior objects or simulation-only methods that typed facets do not yet