Demote current entity slot from canonical run state
This commit is contained in:
parent
4ee0628a5a
commit
9bc8ec125d
11 changed files with 60 additions and 26 deletions
Binary file not shown.
|
|
@ -462,7 +462,6 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s
|
|||
EntranceZoneIndex = 0,
|
||||
ExitZoneIndex = 1,
|
||||
CurrentZoneIndex = 0,
|
||||
CurrentZoneEntityIndex = 0,
|
||||
CurrentEntityKey = entityKey.ToString(),
|
||||
GenerationSeed = 424242,
|
||||
DiscoveredZoneIndices = [0],
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ try
|
|||
RunId = "smoke-run",
|
||||
GenerationSeed = 424242,
|
||||
CurrentZoneIndex = 0,
|
||||
CurrentZoneEntityIndex = 0,
|
||||
CurrentEntityKey = "global:aetheria.run_state.smoke-run.zone.0.entity.0.v1",
|
||||
DiscoveredZoneIndices = new[] { 0 },
|
||||
ActionBarBindings = new[]
|
||||
|
|
|
|||
|
|
@ -1905,7 +1905,7 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
"ReadEntitySnapshots(RuntimeStateFilePath)",
|
||||
"entity.RecordKey",
|
||||
"run.CurrentEntityKey",
|
||||
"ResolveCurrentEntityRecordKey(run, zoneEntityKeyPrefix)",
|
||||
"ResolveCurrentEntityRecordKey(run)",
|
||||
"ReplaceZoneEntitiesFromTypedSnapshots",
|
||||
"Zone.Agents.Clear()",
|
||||
"FlattenEntityGraph(Zone)",
|
||||
|
|
@ -1956,6 +1956,23 @@ static void RequireMainMenuContinueRunState(string root)
|
|||
string.Join(", ", forbiddenGameplayHits));
|
||||
}
|
||||
|
||||
var canonicalRunStatePath = Path.Combine(root, "Aetheria.State", "Documents", "AetheriaRuntimeStateDocuments.cs");
|
||||
var canonicalRunState = File.Exists(canonicalRunStatePath)
|
||||
? File.ReadAllText(canonicalRunStatePath)
|
||||
: throw new InvalidOperationException("Cannot verify canonical run-state authority; AetheriaRuntimeStateDocuments.cs is missing.");
|
||||
|
||||
if (canonicalRunState.Contains("CurrentZoneEntityIndex", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Canonical typed run state still exposes integer current-entity slot authority.");
|
||||
}
|
||||
|
||||
if (packageSnapshot.Contains("CurrentZoneEntityIndex", StringComparison.Ordinal))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Unity package runtime run snapshot still exposes integer current-entity slot authority.");
|
||||
}
|
||||
|
||||
var requiredPackageSymbols = new[]
|
||||
{
|
||||
"public string RecordKey",
|
||||
|
|
|
|||
|
|
@ -237,7 +237,6 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
EntranceZoneIndex = checkpoint.EntranceZoneIndex,
|
||||
ExitZoneIndex = checkpoint.ExitZoneIndex,
|
||||
CurrentZoneIndex = checkpoint.CurrentZoneIndex,
|
||||
CurrentZoneEntityIndex = checkpoint.CurrentZoneEntityIndex,
|
||||
DiscoveredZoneIndices = ToIntArray(checkpoint.DiscoveredZoneIndices),
|
||||
ActionBarBindings = ToActionBarBindings(checkpoint.ActionBarBindings),
|
||||
FactionRelationships = ToFactionRelationships(checkpoint.FactionRelationships),
|
||||
|
|
|
|||
|
|
@ -213,9 +213,6 @@ public sealed class AetheriaRunState
|
|||
[Key(4)]
|
||||
public int CurrentZoneIndex { get; set; }
|
||||
|
||||
[Key(5)]
|
||||
public int CurrentZoneEntityIndex { get; set; }
|
||||
|
||||
[Key(6)]
|
||||
public int[] DiscoveredZoneIndices { get; set; } = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -327,7 +327,9 @@ preserve equipment slots, weapon groups, and simulation stat grids without
|
|||
reviving `PlayerSettings.msgpack`, `.loadout`, or `.zone` serialization.
|
||||
The run document also carries `CurrentEntityKey`, so Continue restore binds the
|
||||
saved player entity by exact typed record identity instead of reconstructing it
|
||||
from a transient entity-slot index.
|
||||
from a transient entity-slot index. Canonical typed run state no longer stores
|
||||
`CurrentZoneEntityIndex`; older stored runs are translated through the package
|
||||
reader into `CurrentEntityKey` when that compatibility seam is needed.
|
||||
|
||||
`Aetheria.State.Verify` opens a materialized state file and checks that the
|
||||
typed migration ledger matches the actual item, corporation, and name-file
|
||||
|
|
|
|||
|
|
@ -610,7 +610,6 @@ public class ActionGameManager : MonoBehaviour
|
|||
EntranceZoneIndex = ZoneIndex(CurrentGalaxy?.Entrance),
|
||||
ExitZoneIndex = ZoneIndex(CurrentGalaxy?.Exit),
|
||||
CurrentZoneIndex = currentZoneIndex,
|
||||
CurrentZoneEntityIndex = IndexOfEntity(entityGraph, CurrentEntity),
|
||||
CurrentEntityKey = CurrentEntityRecordKey(runId, currentZoneIndex, entityGraph, CurrentEntity),
|
||||
GenerationSeed = CurrentGalaxy?.GenerationSeed ?? 0,
|
||||
DiscoveredZoneIndices = CurrentGalaxy?.DiscoveredZones
|
||||
|
|
@ -2503,7 +2502,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
}
|
||||
|
||||
var zoneEntityKeyPrefix = $"global:aetheria.run_state.{run.RunId}.zone.{run.CurrentZoneIndex}.entity.";
|
||||
var currentEntityKey = ResolveCurrentEntityRecordKey(run, zoneEntityKeyPrefix);
|
||||
var currentEntityKey = ResolveCurrentEntityRecordKey(run);
|
||||
if (string.IsNullOrWhiteSpace(currentEntityKey))
|
||||
{
|
||||
Debug.LogWarning($"Typed run {run.RunId} does not identify a current entity for restored zone {zoneEntityKeyPrefix}.");
|
||||
|
|
@ -2550,18 +2549,11 @@ public class ActionGameManager : MonoBehaviour
|
|||
: $"global:aetheria.run_state.{runId}.zone.{currentZoneIndex}.entity.{currentZoneEntityIndex}.v1";
|
||||
}
|
||||
|
||||
private static string ResolveCurrentEntityRecordKey(
|
||||
AetheriaRuntimeRunStateSnapshot run,
|
||||
string zoneEntityKeyPrefix)
|
||||
private static string ResolveCurrentEntityRecordKey(AetheriaRuntimeRunStateSnapshot run)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(run.CurrentEntityKey))
|
||||
{
|
||||
return run.CurrentEntityKey;
|
||||
}
|
||||
|
||||
return run.CurrentZoneEntityIndex < 0
|
||||
return string.IsNullOrWhiteSpace(run.CurrentEntityKey)
|
||||
? ""
|
||||
: $"{zoneEntityKeyPrefix}{run.CurrentZoneEntityIndex}.v1";
|
||||
: run.CurrentEntityKey;
|
||||
}
|
||||
|
||||
private void ReplaceZoneEntitiesFromTypedSnapshots(
|
||||
|
|
|
|||
|
|
@ -690,7 +690,6 @@ namespace GameCult.Aetheria.State.Unity
|
|||
int entranceZoneIndex,
|
||||
int exitZoneIndex,
|
||||
int currentZoneIndex,
|
||||
int currentZoneEntityIndex,
|
||||
string currentEntityKey,
|
||||
IReadOnlyList<int> discoveredZoneIndices,
|
||||
IReadOnlyList<string> zoneKeys,
|
||||
|
|
@ -704,7 +703,6 @@ namespace GameCult.Aetheria.State.Unity
|
|||
EntranceZoneIndex = entranceZoneIndex;
|
||||
ExitZoneIndex = exitZoneIndex;
|
||||
CurrentZoneIndex = currentZoneIndex;
|
||||
CurrentZoneEntityIndex = currentZoneEntityIndex;
|
||||
CurrentEntityKey = currentEntityKey;
|
||||
DiscoveredZoneIndices = discoveredZoneIndices;
|
||||
ZoneKeys = zoneKeys;
|
||||
|
|
@ -719,7 +717,6 @@ namespace GameCult.Aetheria.State.Unity
|
|||
public int EntranceZoneIndex { get; }
|
||||
public int ExitZoneIndex { get; }
|
||||
public int CurrentZoneIndex { get; }
|
||||
public int CurrentZoneEntityIndex { get; }
|
||||
public string CurrentEntityKey { get; }
|
||||
public IReadOnlyList<int> DiscoveredZoneIndices { get; }
|
||||
public IReadOnlyList<string> ZoneKeys { get; }
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ namespace GameCult.Aetheria.State.Unity
|
|||
var entranceZoneIndex = ReadFieldInt32(ref reader, fields, 2);
|
||||
var exitZoneIndex = ReadFieldInt32(ref reader, fields, 3);
|
||||
var currentZoneIndex = ReadFieldInt32(ref reader, fields, 4);
|
||||
var currentZoneEntityIndex = ReadFieldInt32(ref reader, fields, 5);
|
||||
var legacyCurrentZoneEntityIndex = ReadFieldInt32(ref reader, fields, 5, -1);
|
||||
var discoveredZoneIndices = ReadFieldInt32Array(ref reader, fields, 6);
|
||||
var zoneKeys = ReadFieldStringArray(ref reader, fields, 7);
|
||||
var actionBarBindings = ReadFieldActionBarBindings(ref reader, fields, 8);
|
||||
|
|
@ -464,6 +464,11 @@ namespace GameCult.Aetheria.State.Unity
|
|||
var updatedAtUtc = ReadFieldString(ref reader, fields, 10);
|
||||
var generationSeed = ReadFieldUInt32(ref reader, fields, 11);
|
||||
var currentEntityKey = ReadFieldString(ref reader, fields, 12);
|
||||
if (string.IsNullOrWhiteSpace(currentEntityKey))
|
||||
{
|
||||
currentEntityKey = LegacyCurrentEntityKey(runId, currentZoneIndex, legacyCurrentZoneEntityIndex);
|
||||
}
|
||||
|
||||
SkipRemaining(ref reader, fields, 13);
|
||||
return new AetheriaRuntimeRunStateSnapshot(
|
||||
runId,
|
||||
|
|
@ -471,7 +476,6 @@ namespace GameCult.Aetheria.State.Unity
|
|||
entranceZoneIndex,
|
||||
exitZoneIndex,
|
||||
currentZoneIndex,
|
||||
currentZoneEntityIndex,
|
||||
currentEntityKey,
|
||||
discoveredZoneIndices,
|
||||
zoneKeys,
|
||||
|
|
@ -1085,6 +1089,22 @@ namespace GameCult.Aetheria.State.Unity
|
|||
return index >= fields ? 0 : reader.ReadInt32();
|
||||
}
|
||||
|
||||
private static int ReadFieldInt32(ref MessagePackReader reader, int fields, int index, int fallback)
|
||||
{
|
||||
if (index >= fields)
|
||||
{
|
||||
return fallback;
|
||||
}
|
||||
|
||||
if (reader.NextMessagePackType == MessagePackType.Nil)
|
||||
{
|
||||
reader.ReadNil();
|
||||
return fallback;
|
||||
}
|
||||
|
||||
return reader.ReadInt32();
|
||||
}
|
||||
|
||||
private static uint ReadFieldUInt32(ref MessagePackReader reader, int fields, int index)
|
||||
{
|
||||
return index >= fields ? 0 : reader.ReadUInt32();
|
||||
|
|
@ -1689,6 +1709,16 @@ namespace GameCult.Aetheria.State.Unity
|
|||
: $"aetheria.item_definition:legacy:{legacyItemId.Trim()}";
|
||||
}
|
||||
|
||||
private static string LegacyCurrentEntityKey(string runId, int zoneIndex, int entityIndex)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(runId) || zoneIndex < 0 || entityIndex < 0)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return $"global:aetheria.run_state.{runId}.zone.{zoneIndex}.entity.{entityIndex}.v1";
|
||||
}
|
||||
|
||||
private static string CorporationKey(string legacyCorporationId)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(legacyCorporationId)
|
||||
|
|
|
|||
|
|
@ -995,7 +995,9 @@ First Aetheria surfaces to publish:
|
|||
into live behavior instances, and binds the saved current entity by
|
||||
`AetheriaRunState.CurrentEntityKey` so Continue restore follows exact typed
|
||||
entity identity instead of reconstructing the player ship from an integer
|
||||
slot.
|
||||
slot. Canonical run state and package runtime snapshots no longer keep the
|
||||
old current-entity slot field; the package reader only synthesizes the key
|
||||
from older stored runs when that compatibility seam is encountered.
|
||||
- Done: project run checkpoint entity snapshots from the flattened live
|
||||
entity graph, not only `Zone.Entities`, so docked child ships can receive
|
||||
typed entity records and stable child/docking references instead of
|
||||
|
|
|
|||
Loading…
Reference in a new issue