Restore Continue entity graph from typed state
This commit is contained in:
parent
75d3f05fd9
commit
794e65f4ad
6 changed files with 125 additions and 44 deletions
Binary file not shown.
|
|
@ -841,11 +841,15 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
"AetheriaRuntimeRunStateSnapshot ContinueRunState",
|
||||
"ResolveStartZone(continuingRun)",
|
||||
"if (continuingRun != null)",
|
||||
"RestoreCurrentEntityFromTypedRun(continuingRun)",
|
||||
"RestoreEntityGraphFromTypedRun(continuingRun)",
|
||||
"ReadEntitySnapshots(RuntimeStateFilePath)",
|
||||
"entity.RecordKey",
|
||||
"CreateEntityConstructionBlueprint(entitySnapshot, true)",
|
||||
"BindToEntity(entity)",
|
||||
"ReplaceZoneEntitiesFromTypedSnapshots",
|
||||
"Zone.Agents.Clear()",
|
||||
"RestoreEntityContactsFromTypedSnapshot",
|
||||
"entity.Target.Value = target",
|
||||
"string.Equals(entitySnapshot.RecordKey, currentEntityKey",
|
||||
"BindToEntity(currentEntity)",
|
||||
"RestoreActiveConsumablesFromTypedEntitySnapshot(entity, entitySnapshot)",
|
||||
"entity.RestoreStatGrids(entitySnapshot.StatGrids)",
|
||||
"RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia)",
|
||||
|
|
|
|||
|
|
@ -177,11 +177,11 @@ orbit/body rows into `AetheriaZoneState`, so `ZoneConstructionBlueprint` is a
|
|||
one-shot construction input rather than the retained surface carrying celestial graph facts. Entity checkpoints
|
||||
persist hull simulation grids into `AetheriaEntitySnapshot`, so
|
||||
`EntityConstructionBlueprint` is no longer the only surface carrying those live
|
||||
values. Continue restore lowers the current player entity from the exact typed
|
||||
entity snapshot record, including position, velocity, shutdown/heatsink state,
|
||||
thermal exposure, active consumable timers, and typed stat grids for
|
||||
temperature, thermal mass, armor, max armor, and hull conductivity. Broader runtime object graphs
|
||||
and behavior-private simulation state remain legacy until dedicated typed runtime documents exist. Live behavior
|
||||
values. Continue restore lowers the current zone entity graph from exact typed
|
||||
entity snapshot records, including position, velocity, target/contact state,
|
||||
shutdown/heatsink state, thermal exposure, active consumable timers, and typed
|
||||
stat grids for temperature, thermal mass, armor, max armor, and hull conductivity. Broader runtime object graphs
|
||||
such as child/docked activation and behavior-private simulation state remain legacy until dedicated typed runtime documents exist. Live behavior
|
||||
instances expose typed behavior kind and group through `Behavior.Kind` and
|
||||
`Behavior.Group`; behavior construction now reads typed payload fields through
|
||||
`RuntimeBehaviorDefinition` instead of projecting through config DTOs. Stat
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ using static Unity.Mathematics.math;
|
|||
using float2 = Unity.Mathematics.float2;
|
||||
using float3 = Unity.Mathematics.float3;
|
||||
using quaternion = Unity.Mathematics.quaternion;
|
||||
using Regex = System.Text.RegularExpressions.Regex;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class ActionGameManager : MonoBehaviour
|
||||
|
|
@ -2482,7 +2483,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
|
||||
if (continuingRun != null)
|
||||
{
|
||||
RestoreCurrentEntityFromTypedRun(continuingRun);
|
||||
RestoreEntityGraphFromTypedRun(continuingRun);
|
||||
ContinueRunState = null;
|
||||
return;
|
||||
}
|
||||
|
|
@ -2502,7 +2503,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
private void RestoreCurrentEntityFromTypedRun(AetheriaRuntimeRunStateSnapshot run)
|
||||
private void RestoreEntityGraphFromTypedRun(AetheriaRuntimeRunStateSnapshot run)
|
||||
{
|
||||
if (run == null ||
|
||||
run.CurrentZoneIndex < 0 ||
|
||||
|
|
@ -2511,43 +2512,116 @@ public class ActionGameManager : MonoBehaviour
|
|||
return;
|
||||
}
|
||||
|
||||
var entityKey = $"global:aetheria.run_state.{run.RunId}.zone.{run.CurrentZoneIndex}.entity.{run.CurrentZoneEntityIndex}.v1";
|
||||
AetheriaRuntimeEntitySnapshot entitySnapshot;
|
||||
var zoneEntityKeyPrefix = $"global:aetheria.run_state.{run.RunId}.zone.{run.CurrentZoneIndex}.entity.";
|
||||
var currentEntityKey = $"{zoneEntityKeyPrefix}{run.CurrentZoneEntityIndex}.v1";
|
||||
AetheriaRuntimeEntitySnapshot[] entitySnapshots;
|
||||
try
|
||||
{
|
||||
entitySnapshot = AetheriaRuntimeCatalogStore.ReadEntitySnapshots(RuntimeStateFilePath)
|
||||
.FirstOrDefault(entity => string.Equals(entity.RecordKey, entityKey, StringComparison.Ordinal));
|
||||
entitySnapshots = AetheriaRuntimeCatalogStore.ReadEntitySnapshots(RuntimeStateFilePath)
|
||||
.Where(entity => entity.RecordKey.StartsWith(zoneEntityKeyPrefix, StringComparison.Ordinal))
|
||||
.OrderBy(entity => EntityIndexFromRecordKey(entity.RecordKey))
|
||||
.ToArray();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Failed to read typed current entity snapshot {entityKey}: {ex}");
|
||||
Debug.LogError($"Failed to read typed entity snapshots for zone {zoneEntityKeyPrefix}: {ex}");
|
||||
return;
|
||||
}
|
||||
|
||||
var blueprint = CreateEntityConstructionBlueprint(entitySnapshot, true);
|
||||
if (blueprint == null)
|
||||
if (entitySnapshots.Length == 0)
|
||||
{
|
||||
Debug.LogWarning($"Typed current entity snapshot {entityKey} could not be lowered into a runtime entity.");
|
||||
Debug.LogWarning($"No typed entity snapshots found for restored zone {zoneEntityKeyPrefix}.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entity = EntityConstructionBlueprintProjector.InstantiateFromBlueprint(ItemManager, Zone, blueprint, true);
|
||||
if (entity == null)
|
||||
return;
|
||||
ReplaceZoneEntitiesFromTypedSnapshots(entitySnapshots, currentEntityKey);
|
||||
}
|
||||
|
||||
entity.Position = new float3((float)entitySnapshot.PositionX, (float)entitySnapshot.PositionY, (float)entitySnapshot.PositionZ);
|
||||
entity.Direction = new float2((float)entitySnapshot.DirectionX, (float)entitySnapshot.DirectionY);
|
||||
entity.Velocity = new float2((float)entitySnapshot.VelocityX, (float)entitySnapshot.VelocityY);
|
||||
entity.OverrideShutdown = entitySnapshot.OverrideShutdown;
|
||||
entity.TractorPower = (float)entitySnapshot.TractorPower;
|
||||
entity.Zone = Zone;
|
||||
Zone.Entities.Add(entity);
|
||||
entity.Activate();
|
||||
entity.HeatsinksEnabled = entitySnapshot.HeatsinksEnabled;
|
||||
entity.RestoreStatGrids(entitySnapshot.StatGrids);
|
||||
entity.RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia);
|
||||
RestoreActiveConsumablesFromTypedEntitySnapshot(entity, entitySnapshot);
|
||||
BindToEntity(entity);
|
||||
private void ReplaceZoneEntitiesFromTypedSnapshots(
|
||||
IReadOnlyList<AetheriaRuntimeEntitySnapshot> entitySnapshots,
|
||||
string currentEntityKey)
|
||||
{
|
||||
foreach (var existingEntity in Zone.Entities.ToArray())
|
||||
{
|
||||
existingEntity.Deactivate();
|
||||
Zone.Entities.Remove(existingEntity);
|
||||
}
|
||||
Zone.Agents.Clear();
|
||||
|
||||
var restoredEntities = new Dictionary<string, Entity>();
|
||||
foreach (var entitySnapshot in entitySnapshots)
|
||||
{
|
||||
var blueprint = CreateEntityConstructionBlueprint(
|
||||
entitySnapshot,
|
||||
string.Equals(entitySnapshot.RecordKey, currentEntityKey, StringComparison.Ordinal));
|
||||
if (blueprint == null)
|
||||
{
|
||||
Debug.LogWarning($"Typed entity snapshot {entitySnapshot.RecordKey} could not be lowered into a runtime entity.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = EntityConstructionBlueprintProjector.InstantiateFromBlueprint(ItemManager, Zone, blueprint, true);
|
||||
if (entity == null)
|
||||
continue;
|
||||
|
||||
entity.Position = new float3((float)entitySnapshot.PositionX, (float)entitySnapshot.PositionY, (float)entitySnapshot.PositionZ);
|
||||
entity.Direction = new float2((float)entitySnapshot.DirectionX, (float)entitySnapshot.DirectionY);
|
||||
entity.Velocity = new float2((float)entitySnapshot.VelocityX, (float)entitySnapshot.VelocityY);
|
||||
entity.OverrideShutdown = entitySnapshot.OverrideShutdown;
|
||||
entity.TractorPower = (float)entitySnapshot.TractorPower;
|
||||
entity.Zone = Zone;
|
||||
Zone.Entities.Add(entity);
|
||||
restoredEntities[entitySnapshot.RecordKey] = entity;
|
||||
}
|
||||
|
||||
foreach (var entity in restoredEntities.Values)
|
||||
entity.Activate();
|
||||
|
||||
foreach (var entitySnapshot in entitySnapshots)
|
||||
{
|
||||
if (!restoredEntities.TryGetValue(entitySnapshot.RecordKey, out var entity))
|
||||
continue;
|
||||
|
||||
entity.HeatsinksEnabled = entitySnapshot.HeatsinksEnabled;
|
||||
entity.RestoreStatGrids(entitySnapshot.StatGrids);
|
||||
entity.RestoreThermalExposure((float)entitySnapshot.Heatstroke, (float)entitySnapshot.Hypothermia);
|
||||
RestoreActiveConsumablesFromTypedEntitySnapshot(entity, entitySnapshot);
|
||||
RestoreEntityContactsFromTypedSnapshot(entity, entitySnapshot, restoredEntities);
|
||||
if (restoredEntities.TryGetValue(entitySnapshot.TargetEntityKey, out var target))
|
||||
entity.Target.Value = target;
|
||||
}
|
||||
|
||||
if (restoredEntities.TryGetValue(currentEntityKey, out var currentEntity))
|
||||
BindToEntity(currentEntity);
|
||||
}
|
||||
|
||||
private void RestoreEntityContactsFromTypedSnapshot(
|
||||
Entity entity,
|
||||
AetheriaRuntimeEntitySnapshot snapshot,
|
||||
IReadOnlyDictionary<string, Entity> restoredEntities)
|
||||
{
|
||||
foreach (var contact in snapshot.Contacts)
|
||||
{
|
||||
if (!restoredEntities.TryGetValue(contact.TargetEntityKey, out var target))
|
||||
continue;
|
||||
|
||||
entity.EntityInfoGathered[target] = (float)contact.InfoGathered;
|
||||
entity.EntityHostility[target] = contact.Hostile;
|
||||
if (contact.Visible && !entity.VisibleEntities.Contains(target))
|
||||
entity.VisibleEntities.Add(target);
|
||||
if (contact.Visible && contact.Hostile && !entity.VisibleEnemies.Contains(target))
|
||||
entity.VisibleEnemies.Add(target);
|
||||
if (contact.Visible && !contact.Hostile && !entity.VisibleFriendlies.Contains(target))
|
||||
entity.VisibleFriendlies.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
private static int EntityIndexFromRecordKey(string recordKey)
|
||||
{
|
||||
var match = Regex.Match(recordKey ?? "", @"\.entity\.(\d+)\.v1$");
|
||||
return match.Success && int.TryParse(match.Groups[1].Value, out var index)
|
||||
? index
|
||||
: int.MaxValue;
|
||||
}
|
||||
|
||||
private void RestoreActiveConsumablesFromTypedEntitySnapshot(Entity entity, AetheriaRuntimeEntitySnapshot snapshot)
|
||||
|
|
|
|||
|
|
@ -338,7 +338,10 @@ public class ZoneRenderer : MonoBehaviour
|
|||
}
|
||||
}
|
||||
|
||||
Destroy(EntityInstances[entity].gameObject);
|
||||
if (!EntityInstances.TryGetValue(entity, out var instance))
|
||||
return;
|
||||
|
||||
Destroy(instance.gameObject);
|
||||
EntityInstances.Remove(entity);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -968,21 +968,21 @@ First Aetheria surfaces to publish:
|
|||
galaxy from the saved generation seed, and passes the selected run to
|
||||
`ActionGameManager` so boot enters the saved current zone and lowers typed
|
||||
pickup rows.
|
||||
- Done: restore the current player entity from typed state during Continue
|
||||
boot. Package entity readback now preserves `AetheriaEntitySnapshot` record
|
||||
identity, and `ActionGameManager` resolves the exact
|
||||
`RunId + ZoneIndex + CurrentZoneEntityIndex` entity record, lowers it
|
||||
- Done: restore the current zone's entity graph from typed state during
|
||||
Continue boot. Package entity readback preserves `AetheriaEntitySnapshot`
|
||||
record identity, and `ActionGameManager` resolves the exact
|
||||
`RunId + ZoneIndex` entity-record prefix, removes generated zone entities
|
||||
and their agents as Continue authority, lowers all typed entity snapshots
|
||||
through the existing construction blueprint path, restores hull,
|
||||
equipment, cargo, docking contents, weapon groups, position, direction,
|
||||
velocity, shutdown override, heatsink toggle, tractor power,
|
||||
heatstroke/hypothermia exposure, active consumable item/timer rows,
|
||||
and typed stat grids for temperature, thermal mass, armor, max armor,
|
||||
and hull-conductivity axes, then binds the lowered entity as the current
|
||||
player ship.
|
||||
typed stat grids for temperature, thermal mass, armor, max armor, and
|
||||
hull-conductivity axes, then reconnects target/contact rows among restored
|
||||
entities and binds the saved current entity as the player ship.
|
||||
- Remaining: add typed documents/mappers for runtime object graphs,
|
||||
complete full Continue/run entity graph reconstruction from typed state
|
||||
for non-current entities, child/docked entity activation, targets,
|
||||
contacts, restored active-consumable behavior runtime internals,
|
||||
for child/docked entity activation, restored active-consumable behavior runtime internals,
|
||||
behavior-private state not covered by progress, weapon, sensor, radiator,
|
||||
reactor, or capacitor rows, typed behavior factory construction, and any
|
||||
catalog fields not covered by the stable scalar/fingerprint/payload pass.
|
||||
|
|
|
|||
Loading…
Reference in a new issue