Project active zone snapshots into typed state
This commit is contained in:
parent
25f27231c1
commit
30e73a7b79
6 changed files with 405 additions and 28 deletions
Binary file not shown.
|
|
@ -144,8 +144,59 @@ try
|
|||
TutorialPassed = true,
|
||||
ActionBarInputs = new[] { "<Keyboard>/1" }
|
||||
});
|
||||
AetheriaRuntimeStateCommitLog.QueueRunCheckpoint(
|
||||
commitSmokeStatePath,
|
||||
new AetheriaRuntimeRunCheckpointCommit
|
||||
{
|
||||
RunId = "smoke-run",
|
||||
CurrentZoneIndex = 0,
|
||||
CurrentZoneEntityIndex = 0,
|
||||
DiscoveredZoneIndices = new[] { 0 },
|
||||
Zones = new[]
|
||||
{
|
||||
new AetheriaRuntimeZoneSnapshotCommit
|
||||
{
|
||||
ZoneIndex = 0,
|
||||
Name = "Unity Smoke Zone",
|
||||
PositionX = 12.0,
|
||||
PositionY = -4.0,
|
||||
AdjacentZoneIndices = new[] { 1 },
|
||||
OwnerFactionIndex = 0,
|
||||
Entities = new[]
|
||||
{
|
||||
new AetheriaRuntimeEntitySnapshotCommit
|
||||
{
|
||||
EntityIndex = 0,
|
||||
Name = "Unity Smoke Ship",
|
||||
Kind = "ship",
|
||||
PositionX = 1.0,
|
||||
PositionY = 2.0,
|
||||
PositionZ = 3.0,
|
||||
DirectionX = 0.0,
|
||||
DirectionY = 1.0,
|
||||
HullItemLegacyId = "smoke:hull",
|
||||
Equipment = new[]
|
||||
{
|
||||
new AetheriaRuntimeLoadoutItemSlotCommit
|
||||
{
|
||||
X = 0,
|
||||
Y = 0,
|
||||
Item = new AetheriaRuntimeLoadoutItemCommit
|
||||
{
|
||||
ItemLegacyId = "smoke:weapon",
|
||||
Quality = 0.9,
|
||||
Durability = 0.8
|
||||
}
|
||||
}
|
||||
},
|
||||
WeaponGroups = new[] { new[] { 0 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
var pending = AetheriaRuntimeStateCommitLog.ReadPending(commitSmokeStatePath);
|
||||
if (pending.Count != 1 ||
|
||||
if (pending.Count != 2 ||
|
||||
pending[0].Kind != AetheriaRuntimeCommitKind.PlayerSettings ||
|
||||
pending[0].Schema != AetheriaRuntimeStateCommitLog.CommitSchema ||
|
||||
pending[0].Path != commit.Path)
|
||||
|
|
@ -156,12 +207,20 @@ try
|
|||
await using var commitNode = await AetheriaStateNode.OpenAsync(commitSmokeStatePath, "aetheria-unity-runtime-commit-smoke");
|
||||
var report = await AetheriaRuntimeCommitLogApplier.ApplyPendingAsync(commitNode);
|
||||
var settings = await commitNode.GetPlayerSettingsAsync();
|
||||
var run = await commitNode.GetRunStateAsync(new("global:aetheria.run_state.smoke-run.v1"));
|
||||
var zone = await commitNode.GetZoneStateAsync(new("global:aetheria.run_state.smoke-run.zone.0.v1"));
|
||||
var entity = await commitNode.GetEntitySnapshotAsync(new("global:aetheria.run_state.smoke-run.zone.0.entity.0.v1"));
|
||||
if (report.AppliedPlayerSettings != 1 ||
|
||||
report.AppliedRunCheckpoints != 1 ||
|
||||
settings?.PlayerName != "Unity smoke" ||
|
||||
settings.Input.ActionBarInputs.Length != 1 ||
|
||||
run?.ZoneKeys.Length != 1 ||
|
||||
zone?.EntityKeys.Length != 1 ||
|
||||
entity?.Equipment.Length != 1 ||
|
||||
entity.WeaponGroups.Length != 1 ||
|
||||
AetheriaRuntimeStateCommitLog.ReadPending(commitSmokeStatePath).Count != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Runtime state commit log did not apply queued settings through the typed state node.");
|
||||
throw new InvalidOperationException("Runtime state commit log did not apply queued settings/run snapshots through the typed state node.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
|
@ -176,4 +235,4 @@ Console.WriteLine($"Interior/hardpoint sample: {interior.Name} {interior.Interio
|
|||
Console.WriteLine($"Behavior payload sample: {behaviorHost.Name} {behaviorPayload.Kind}/{behaviorPayload.Fields.Count}");
|
||||
Console.WriteLine($"Behavior sample: {behaviorKind}");
|
||||
Console.WriteLine($"Eve surface: {surface.Surface.Id}");
|
||||
Console.WriteLine("Runtime state commit log smoke: player settings command queued, applied, and cleared");
|
||||
Console.WriteLine("Runtime state commit log smoke: settings and run zone/entity snapshots queued, applied, and cleared");
|
||||
|
|
|
|||
|
|
@ -50,6 +50,31 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
break;
|
||||
case "run_checkpoint":
|
||||
var run = command.RunState ?? throw MissingPayload(command);
|
||||
var zoneKeys = new List<string>();
|
||||
foreach (var zone in command.ZoneSnapshots)
|
||||
{
|
||||
var zoneKey = ZoneKey(run.RunId, zone.ZoneIndex);
|
||||
var entityKeys = new List<string>();
|
||||
foreach (var entity in zone.Entities)
|
||||
{
|
||||
var entityKey = EntityKey(run.RunId, zone.ZoneIndex, entity.EntityIndex);
|
||||
entity.Snapshot.ChildEntityKeys = entity.Snapshot.ChildEntityKeys
|
||||
.Select(key => key.StartsWith("pending:entity:", StringComparison.Ordinal)
|
||||
&& int.TryParse(key["pending:entity:".Length..], out var childIndex)
|
||||
? EntityKey(run.RunId, zone.ZoneIndex, childIndex).ToString()
|
||||
: key)
|
||||
.ToArray();
|
||||
await node.PutEntitySnapshotAsync(entityKey, entity.Snapshot).ConfigureAwait(false);
|
||||
entityKeys.Add(entityKey.ToString());
|
||||
}
|
||||
|
||||
zone.State.EntityKeys = entityKeys.ToArray();
|
||||
await node.PutZoneStateAsync(zoneKey, zone.State).ConfigureAwait(false);
|
||||
zoneKeys.Add(zoneKey.ToString());
|
||||
}
|
||||
|
||||
if (zoneKeys.Count > 0)
|
||||
run.ZoneKeys = zoneKeys.ToArray();
|
||||
await node.PutRunStateAsync(RunKey(run.RunId), run).ConfigureAwait(false);
|
||||
report.AppliedRunCheckpoints++;
|
||||
break;
|
||||
|
|
@ -98,7 +123,8 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
kind,
|
||||
commandId,
|
||||
createdAtUtc,
|
||||
ReadRunState(ref reader, createdAtUtc),
|
||||
ReadRunState(ref reader, createdAtUtc, out var zoneSnapshots),
|
||||
zoneSnapshots,
|
||||
path),
|
||||
_ => throw new InvalidDataException($"Unknown Aetheria runtime commit kind '{kind}' in {path}.")
|
||||
};
|
||||
|
|
@ -184,10 +210,13 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
};
|
||||
}
|
||||
|
||||
private static AetheriaRunState ReadRunState(ref MessagePackReader reader, string updatedAtUtc)
|
||||
private static AetheriaRunState ReadRunState(
|
||||
ref MessagePackReader reader,
|
||||
string updatedAtUtc,
|
||||
out AetheriaPendingZoneSnapshot[] zoneSnapshots)
|
||||
{
|
||||
var fields = reader.ReadArrayHeader();
|
||||
return new AetheriaRunState
|
||||
var run = new AetheriaRunState
|
||||
{
|
||||
RunId = ReadFieldString(ref reader, fields, 0),
|
||||
IsTutorial = ReadFieldBool(ref reader, fields, 1),
|
||||
|
|
@ -198,6 +227,97 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
DiscoveredZoneIndices = ReadIntArray(ref reader, fields, 6),
|
||||
UpdatedAtUtc = updatedAtUtc
|
||||
};
|
||||
zoneSnapshots = ReadZoneSnapshots(ref reader, fields, 7);
|
||||
return run;
|
||||
}
|
||||
|
||||
private static AetheriaPendingZoneSnapshot[] ReadZoneSnapshots(ref MessagePackReader reader, int fields, int index)
|
||||
{
|
||||
if (index >= fields) return [];
|
||||
var count = reader.ReadArrayHeader();
|
||||
var zones = new AetheriaPendingZoneSnapshot[count];
|
||||
for (var item = 0; item < count; item++)
|
||||
{
|
||||
var itemFields = reader.ReadArrayHeader();
|
||||
var zoneIndex = ReadFieldInt32(ref reader, itemFields, 0);
|
||||
var state = new AetheriaZoneState
|
||||
{
|
||||
Name = ReadFieldString(ref reader, itemFields, 1),
|
||||
Position = new AetheriaVector2
|
||||
{
|
||||
X = ReadFieldDouble(ref reader, itemFields, 2),
|
||||
Y = ReadFieldDouble(ref reader, itemFields, 3)
|
||||
},
|
||||
AdjacentZoneIndices = ReadIntArray(ref reader, itemFields, 4),
|
||||
FactionIndices = ReadIntArray(ref reader, itemFields, 5),
|
||||
OwnerFactionIndex = ReadFieldInt32(ref reader, itemFields, 6)
|
||||
};
|
||||
zones[item] = new AetheriaPendingZoneSnapshot(
|
||||
zoneIndex,
|
||||
state,
|
||||
ReadEntitySnapshots(ref reader, itemFields, 7));
|
||||
}
|
||||
|
||||
return zones;
|
||||
}
|
||||
|
||||
private static AetheriaPendingEntitySnapshot[] ReadEntitySnapshots(ref MessagePackReader reader, int fields, int index)
|
||||
{
|
||||
if (index >= fields) return [];
|
||||
var count = reader.ReadArrayHeader();
|
||||
var entities = new AetheriaPendingEntitySnapshot[count];
|
||||
for (var item = 0; item < count; item++)
|
||||
{
|
||||
var itemFields = reader.ReadArrayHeader();
|
||||
var entityIndex = ReadFieldInt32(ref reader, itemFields, 0);
|
||||
var snapshot = new AetheriaEntitySnapshot
|
||||
{
|
||||
Name = ReadFieldString(ref reader, itemFields, 1),
|
||||
Kind = ReadFieldString(ref reader, itemFields, 2),
|
||||
Position = new AetheriaVector3
|
||||
{
|
||||
X = ReadFieldDouble(ref reader, itemFields, 3),
|
||||
Y = ReadFieldDouble(ref reader, itemFields, 4),
|
||||
Z = ReadFieldDouble(ref reader, itemFields, 5)
|
||||
},
|
||||
Direction = new AetheriaVector2
|
||||
{
|
||||
X = ReadFieldDouble(ref reader, itemFields, 6),
|
||||
Y = ReadFieldDouble(ref reader, itemFields, 7)
|
||||
},
|
||||
FactionKey = ReferenceKey("aetheria.corporation", ReadFieldString(ref reader, itemFields, 8)),
|
||||
HullItemKey = ReferenceKey("aetheria.item_definition", ReadFieldString(ref reader, itemFields, 9)),
|
||||
Equipment = ReadEntityItemSlots(ref reader, itemFields, 10),
|
||||
CargoBays = ReadEntityItemSlots(ref reader, itemFields, 11),
|
||||
DockingBays = ReadEntityItemSlots(ref reader, itemFields, 12),
|
||||
ChildEntityKeys = ReadIntArray(ref reader, itemFields, 13)
|
||||
.Select(childIndex => $"pending:entity:{childIndex}")
|
||||
.ToArray(),
|
||||
WeaponGroups = ReadWeaponGroups(ref reader, itemFields, 14)
|
||||
};
|
||||
entities[item] = new AetheriaPendingEntitySnapshot(entityIndex, snapshot);
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
private static AetheriaEntityItemSlot[] ReadEntityItemSlots(ref MessagePackReader reader, int fields, int index)
|
||||
{
|
||||
var slots = ReadItemSlots(ref reader, fields, index);
|
||||
return slots
|
||||
.Select(slot => new AetheriaEntityItemSlot
|
||||
{
|
||||
Position = slot.Position,
|
||||
ItemKey = slot.Item.ItemKey
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static AetheriaWeaponGroupSnapshot[] ReadWeaponGroups(ref MessagePackReader reader, int fields, int index)
|
||||
{
|
||||
return ReadIntArrayArray(ref reader, fields, index)
|
||||
.Select(group => new AetheriaWeaponGroupSnapshot { EquipmentIndices = group })
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static AetheriaStoryFileHash[] ReadStoryFileHashes(ref MessagePackReader reader, int fields, int index)
|
||||
|
|
@ -350,6 +470,16 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
return new CultRecordKey($"global:aetheria.run_state.{StableToken(runId)}.v1");
|
||||
}
|
||||
|
||||
private static CultRecordKey ZoneKey(string runId, int zoneIndex)
|
||||
{
|
||||
return new CultRecordKey($"global:aetheria.run_state.{StableToken(runId)}.zone.{zoneIndex}.v1");
|
||||
}
|
||||
|
||||
private static CultRecordKey EntityKey(string runId, int zoneIndex, int entityIndex)
|
||||
{
|
||||
return new CultRecordKey($"global:aetheria.run_state.{StableToken(runId)}.zone.{zoneIndex}.entity.{entityIndex}.v1");
|
||||
}
|
||||
|
||||
private static string ReferenceKey(string documentName, string legacyId)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(legacyId) ? "" : $"{documentName}:legacy:{legacyId}";
|
||||
|
|
@ -380,7 +510,8 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
string path,
|
||||
AetheriaPlayerSettings? playerSettings,
|
||||
AetheriaLoadoutTemplate? loadoutTemplate,
|
||||
AetheriaRunState? runState)
|
||||
AetheriaRunState? runState,
|
||||
AetheriaPendingZoneSnapshot[] zoneSnapshots)
|
||||
{
|
||||
Kind = kind;
|
||||
CommandId = commandId;
|
||||
|
|
@ -389,11 +520,12 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
PlayerSettings = playerSettings;
|
||||
LoadoutTemplate = loadoutTemplate;
|
||||
RunState = runState;
|
||||
ZoneSnapshots = zoneSnapshots;
|
||||
}
|
||||
|
||||
public static PendingCommand Empty(string kind, string commandId, string createdAtUtc, string path)
|
||||
{
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, null, null);
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, null, null, []);
|
||||
}
|
||||
|
||||
public static PendingCommand ForPlayerSettings(
|
||||
|
|
@ -403,7 +535,7 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
AetheriaPlayerSettings settings,
|
||||
string path)
|
||||
{
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, settings, null, null);
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, settings, null, null, []);
|
||||
}
|
||||
|
||||
public static PendingCommand ForLoadoutTemplate(
|
||||
|
|
@ -413,7 +545,7 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
AetheriaLoadoutTemplate loadout,
|
||||
string path)
|
||||
{
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, loadout, null);
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, loadout, null, []);
|
||||
}
|
||||
|
||||
public static PendingCommand ForRunState(
|
||||
|
|
@ -421,9 +553,10 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
string commandId,
|
||||
string createdAtUtc,
|
||||
AetheriaRunState run,
|
||||
AetheriaPendingZoneSnapshot[] zoneSnapshots,
|
||||
string path)
|
||||
{
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, null, run);
|
||||
return new PendingCommand(kind, commandId, createdAtUtc, path, null, null, run, zoneSnapshots);
|
||||
}
|
||||
|
||||
public string Kind { get; }
|
||||
|
|
@ -433,5 +566,35 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
public AetheriaPlayerSettings? PlayerSettings { get; }
|
||||
public AetheriaLoadoutTemplate? LoadoutTemplate { get; }
|
||||
public AetheriaRunState? RunState { get; }
|
||||
public AetheriaPendingZoneSnapshot[] ZoneSnapshots { get; }
|
||||
}
|
||||
|
||||
private readonly struct AetheriaPendingZoneSnapshot
|
||||
{
|
||||
public AetheriaPendingZoneSnapshot(
|
||||
int zoneIndex,
|
||||
AetheriaZoneState state,
|
||||
AetheriaPendingEntitySnapshot[] entities)
|
||||
{
|
||||
ZoneIndex = zoneIndex;
|
||||
State = state;
|
||||
Entities = entities;
|
||||
}
|
||||
|
||||
public int ZoneIndex { get; }
|
||||
public AetheriaZoneState State { get; }
|
||||
public AetheriaPendingEntitySnapshot[] Entities { get; }
|
||||
}
|
||||
|
||||
private readonly struct AetheriaPendingEntitySnapshot
|
||||
{
|
||||
public AetheriaPendingEntitySnapshot(int entityIndex, AetheriaEntitySnapshot snapshot)
|
||||
{
|
||||
EntityIndex = entityIndex;
|
||||
Snapshot = snapshot;
|
||||
}
|
||||
|
||||
public int EntityIndex { get; }
|
||||
public AetheriaEntitySnapshot Snapshot { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -336,15 +336,86 @@ public class ActionGameManager : MonoBehaviour
|
|||
.Select(ZoneIndex)
|
||||
.Where(index => index >= 0)
|
||||
.OrderBy(index => index)
|
||||
.ToArray() ?? Array.Empty<int>()
|
||||
.ToArray() ?? Array.Empty<int>(),
|
||||
Zones = Zone == null ? Array.Empty<AetheriaRuntimeZoneSnapshotCommit>() : new[] { ProjectZoneSnapshot(Zone) }
|
||||
};
|
||||
}
|
||||
|
||||
private AetheriaRuntimeZoneSnapshotCommit ProjectZoneSnapshot(Zone zone)
|
||||
{
|
||||
var galaxyZone = zone.GalaxyZone;
|
||||
return new AetheriaRuntimeZoneSnapshotCommit
|
||||
{
|
||||
ZoneIndex = ZoneIndex(galaxyZone),
|
||||
Name = galaxyZone?.Name ?? "",
|
||||
PositionX = galaxyZone?.Position.x ?? 0,
|
||||
PositionY = galaxyZone?.Position.y ?? 0,
|
||||
AdjacentZoneIndices = galaxyZone?.AdjacentZones
|
||||
.Select(ZoneIndex)
|
||||
.Where(index => index >= 0)
|
||||
.OrderBy(index => index)
|
||||
.ToArray() ?? Array.Empty<int>(),
|
||||
FactionIndices = galaxyZone?.Factions?
|
||||
.Select(FactionIndex)
|
||||
.Where(index => index >= 0)
|
||||
.OrderBy(index => index)
|
||||
.ToArray() ?? Array.Empty<int>(),
|
||||
OwnerFactionIndex = FactionIndex(galaxyZone?.Owner),
|
||||
Entities = zone.Entities
|
||||
.Select((entity, index) => ProjectEntitySnapshot(zone, entity, index))
|
||||
.ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
private AetheriaRuntimeEntitySnapshotCommit ProjectEntitySnapshot(Zone zone, Entity entity, int entityIndex)
|
||||
{
|
||||
return new AetheriaRuntimeEntitySnapshotCommit
|
||||
{
|
||||
EntityIndex = entityIndex,
|
||||
Name = entity.Name ?? "",
|
||||
Kind = entity is Ship ? "ship" : entity is OrbitalEntity ? "orbital" : "entity",
|
||||
PositionX = entity.Position.x,
|
||||
PositionY = entity.Position.y,
|
||||
PositionZ = entity.Position.z,
|
||||
DirectionX = entity.Direction.x,
|
||||
DirectionY = entity.Direction.y,
|
||||
FactionLegacyId = entity.Faction?.ID.ToString("D") ?? "",
|
||||
HullItemLegacyId = entity.Hull?.Data?.LinkID.ToString("D") ?? "",
|
||||
Equipment = ProjectEquippedSlots(entity.Equipment),
|
||||
CargoBays = ProjectEquippedSlots(entity.CargoBays),
|
||||
DockingBays = ProjectEquippedSlots(entity.DockingBays),
|
||||
ChildEntityIndices = entity.Children?
|
||||
.Select(child => zone.Entities.IndexOf(child))
|
||||
.Where(index => index >= 0)
|
||||
.ToArray() ?? Array.Empty<int>(),
|
||||
WeaponGroups = entity.WeaponGroups?
|
||||
.Select(group => (IReadOnlyList<int>)group.items.Select(item => entity.Equipment.IndexOf(item)).Where(index => index >= 0).ToArray())
|
||||
.ToArray() ?? Array.Empty<IReadOnlyList<int>>()
|
||||
};
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeLoadoutItemSlotCommit[] ProjectEquippedSlots(IEnumerable<EquippedItem> slots)
|
||||
{
|
||||
return slots?
|
||||
.Select(slot => new AetheriaRuntimeLoadoutItemSlotCommit
|
||||
{
|
||||
X = slot.Position.x,
|
||||
Y = slot.Position.y,
|
||||
Item = ProjectLoadoutItem(slot.EquippableItem)
|
||||
})
|
||||
.ToArray() ?? Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>();
|
||||
}
|
||||
|
||||
private static int ZoneIndex(GalaxyZone zone)
|
||||
{
|
||||
return CurrentGalaxy?.Zones == null || zone == null ? -1 : Array.IndexOf(CurrentGalaxy.Zones, zone);
|
||||
}
|
||||
|
||||
private static int FactionIndex(Faction faction)
|
||||
{
|
||||
return CurrentGalaxy?.Factions == null || faction == null ? -1 : Array.IndexOf(CurrentGalaxy.Factions, faction);
|
||||
}
|
||||
|
||||
public void SaveLoadout(EntityPack pack)
|
||||
{
|
||||
Loadouts.RemoveAll(loadout => loadout.Name == pack.Name);
|
||||
|
|
|
|||
|
|
@ -117,6 +117,38 @@ namespace GameCult.Aetheria.State.Unity
|
|||
public int CurrentZoneIndex { get; set; } = -1;
|
||||
public int CurrentZoneEntityIndex { get; set; } = -1;
|
||||
public IReadOnlyList<int> DiscoveredZoneIndices { get; set; } = Array.Empty<int>();
|
||||
public IReadOnlyList<AetheriaRuntimeZoneSnapshotCommit> Zones { get; set; } = Array.Empty<AetheriaRuntimeZoneSnapshotCommit>();
|
||||
}
|
||||
|
||||
public sealed class AetheriaRuntimeZoneSnapshotCommit
|
||||
{
|
||||
public int ZoneIndex { get; set; } = -1;
|
||||
public string Name { get; set; } = "";
|
||||
public double PositionX { get; set; }
|
||||
public double PositionY { get; set; }
|
||||
public IReadOnlyList<int> AdjacentZoneIndices { get; set; } = Array.Empty<int>();
|
||||
public IReadOnlyList<int> FactionIndices { get; set; } = Array.Empty<int>();
|
||||
public int OwnerFactionIndex { get; set; } = -1;
|
||||
public IReadOnlyList<AetheriaRuntimeEntitySnapshotCommit> Entities { get; set; } = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>();
|
||||
}
|
||||
|
||||
public sealed class AetheriaRuntimeEntitySnapshotCommit
|
||||
{
|
||||
public int EntityIndex { get; set; } = -1;
|
||||
public string Name { get; set; } = "";
|
||||
public string Kind { get; set; } = "";
|
||||
public double PositionX { get; set; }
|
||||
public double PositionY { get; set; }
|
||||
public double PositionZ { get; set; }
|
||||
public double DirectionX { get; set; }
|
||||
public double DirectionY { get; set; }
|
||||
public string FactionLegacyId { get; set; } = "";
|
||||
public string HullItemLegacyId { get; set; } = "";
|
||||
public IReadOnlyList<AetheriaRuntimeLoadoutItemSlotCommit> Equipment { get; set; } = Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>();
|
||||
public IReadOnlyList<AetheriaRuntimeLoadoutItemSlotCommit> CargoBays { get; set; } = Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>();
|
||||
public IReadOnlyList<AetheriaRuntimeLoadoutItemSlotCommit> DockingBays { get; set; } = Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>();
|
||||
public IReadOnlyList<int> ChildEntityIndices { get; set; } = Array.Empty<int>();
|
||||
public IReadOnlyList<IReadOnlyList<int>> WeaponGroups { get; set; } = Array.Empty<IReadOnlyList<int>>();
|
||||
}
|
||||
|
||||
public static class AetheriaRuntimeStateCommitLog
|
||||
|
|
@ -321,7 +353,7 @@ namespace GameCult.Aetheria.State.Unity
|
|||
|
||||
private static void WriteRunCheckpoint(ref MessagePackWriter writer, AetheriaRuntimeRunCheckpointCommit run)
|
||||
{
|
||||
writer.WriteArrayHeader(7);
|
||||
writer.WriteArrayHeader(8);
|
||||
writer.Write(run.RunId ?? "");
|
||||
writer.Write(run.IsTutorial);
|
||||
writer.Write(run.EntranceZoneIndex);
|
||||
|
|
@ -329,6 +361,50 @@ namespace GameCult.Aetheria.State.Unity
|
|||
writer.Write(run.CurrentZoneIndex);
|
||||
writer.Write(run.CurrentZoneEntityIndex);
|
||||
WriteInts(ref writer, run.DiscoveredZoneIndices);
|
||||
WriteZoneSnapshots(ref writer, run.Zones);
|
||||
}
|
||||
|
||||
private static void WriteZoneSnapshots(ref MessagePackWriter writer, IReadOnlyList<AetheriaRuntimeZoneSnapshotCommit>? zones)
|
||||
{
|
||||
zones ??= Array.Empty<AetheriaRuntimeZoneSnapshotCommit>();
|
||||
writer.WriteArrayHeader(zones.Count);
|
||||
foreach (var zone in zones)
|
||||
{
|
||||
writer.WriteArrayHeader(8);
|
||||
writer.Write(zone.ZoneIndex);
|
||||
writer.Write(zone.Name ?? "");
|
||||
writer.Write(zone.PositionX);
|
||||
writer.Write(zone.PositionY);
|
||||
WriteInts(ref writer, zone.AdjacentZoneIndices);
|
||||
WriteInts(ref writer, zone.FactionIndices);
|
||||
writer.Write(zone.OwnerFactionIndex);
|
||||
WriteEntitySnapshots(ref writer, zone.Entities);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteEntitySnapshots(ref MessagePackWriter writer, IReadOnlyList<AetheriaRuntimeEntitySnapshotCommit>? entities)
|
||||
{
|
||||
entities ??= Array.Empty<AetheriaRuntimeEntitySnapshotCommit>();
|
||||
writer.WriteArrayHeader(entities.Count);
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
writer.WriteArrayHeader(15);
|
||||
writer.Write(entity.EntityIndex);
|
||||
writer.Write(entity.Name ?? "");
|
||||
writer.Write(entity.Kind ?? "");
|
||||
writer.Write(entity.PositionX);
|
||||
writer.Write(entity.PositionY);
|
||||
writer.Write(entity.PositionZ);
|
||||
writer.Write(entity.DirectionX);
|
||||
writer.Write(entity.DirectionY);
|
||||
writer.Write(entity.FactionLegacyId ?? "");
|
||||
writer.Write(entity.HullItemLegacyId ?? "");
|
||||
WriteItemSlots(ref writer, entity.Equipment);
|
||||
WriteItemSlots(ref writer, entity.CargoBays);
|
||||
WriteItemSlots(ref writer, entity.DockingBays);
|
||||
WriteInts(ref writer, entity.ChildEntityIndices);
|
||||
WriteIntLists(ref writer, entity.WeaponGroups);
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteStrings(ref MessagePackWriter writer, IReadOnlyList<string>? values)
|
||||
|
|
|
|||
|
|
@ -32,9 +32,11 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
from the `.cc` directory store before constructing the temporary legacy
|
||||
`ItemManager`. Player settings saves, loadout saves, shutdown checkpoints,
|
||||
and wormhole transition checkpoints now queue typed `.cc.pending` Verse
|
||||
commit commands through the embedded runtime state package. `Aetheria.State`
|
||||
owns applying those command envelopes into canonical `AetheriaPlayerSettings`,
|
||||
`AetheriaLoadoutTemplate`, and `AetheriaRunState` documents through
|
||||
commit commands through the embedded runtime state package. Run checkpoint
|
||||
commands include the current zone plus its runtime entity snapshots.
|
||||
`Aetheria.State` owns applying those command envelopes into canonical
|
||||
`AetheriaPlayerSettings`, `AetheriaLoadoutTemplate`, `AetheriaRunState`,
|
||||
`AetheriaZoneState`, and `AetheriaEntitySnapshot` documents through
|
||||
`AetheriaStateNode`. Local run saves, loadouts, player settings files, zone
|
||||
files, and generated keyboard layout caches no longer write bespoke durable files. The
|
||||
legacy
|
||||
|
|
@ -67,9 +69,10 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
state, and entity snapshots. The state smoke writes a run referencing a zone,
|
||||
a zone referencing an entity snapshot, and an entity snapshot carrying
|
||||
position, direction, faction, hull, equipment slots, weapon groups, and a stat
|
||||
grid. This proves the `.zone` replacement graph is durable typed state; Unity
|
||||
`ZonePack` and `EntityPack` are still runtime construction projections until
|
||||
the runtime package is wired.
|
||||
grid. This proves the `.zone` replacement graph is durable typed state. Unity
|
||||
now queues current-zone/current-entity-collection snapshots through the
|
||||
runtime commit log during run checkpoints; `ZonePack` and `EntityPack` remain
|
||||
runtime construction/loadout projections rather than durable file formats.
|
||||
- `Aetheria.State` now defines `AetheriaLoadoutTemplate` as the typed Verse
|
||||
replacement for bespoke `.loadout` files. It stores structured hull,
|
||||
equipment, cargo bay, docking bay, child-entity, assignment, and weapon-group
|
||||
|
|
@ -157,7 +160,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
loadout-template, and run-checkpoint command envelopes under
|
||||
`aetheria-world.cc.pending`. This log is command-only: it cannot decide
|
||||
canonical state, and the `Aetheria.State` node applicator deletes applied
|
||||
commands after writing typed documents.
|
||||
commands after writing typed documents. Run checkpoint envelopes now carry
|
||||
current-zone and entity snapshots so the old `.zone` file path has a live
|
||||
typed runtime projection path.
|
||||
- `AetheriaCatalogSurfaceProjector` now emits the first provider-owned Eve
|
||||
surface from typed catalog state. The importer materializes a
|
||||
`gamecult.eve.surface.v1` catalog operator document at
|
||||
|
|
@ -298,11 +303,11 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
local save and editor catalog write paths have been cut. Migrated catalog documents are
|
||||
addressed through `AetheriaCatalogKeys` and `AetheriaStateNode` legacy-ID
|
||||
methods, not importer-local string concatenation. Player settings, loadout
|
||||
templates, and lightweight run checkpoints have a shared Unity commit-log
|
||||
primitive plus a typed state node applicator. Run, zone, and entity snapshot
|
||||
state also have typed node put/get paths; full zone/entity runtime snapshot
|
||||
projection still needs to move through the same command/apply spine. The old
|
||||
`SaveState`/`SaveZone` command surface is gone.
|
||||
templates, and run checkpoints have a shared Unity commit-log primitive plus
|
||||
a typed state node applicator. Run checkpoints now carry the current zone and
|
||||
its entity snapshots through that same command/apply spine. Whole-galaxy
|
||||
unloaded-zone projection still needs to be added once lazy generation has a
|
||||
typed state owner. The old `SaveState`/`SaveZone` command surface is gone.
|
||||
The catalog operator UI is emitted as a typed Eve surface document derived
|
||||
from the catalog snapshot.
|
||||
- Derived state: UI panels, generated keyboard layouts, editor rows, typed
|
||||
|
|
@ -513,6 +518,9 @@ First Aetheria surfaces to publish:
|
|||
- Done: replace warning-only player settings, loadout, shutdown, and
|
||||
wormhole save paths with typed `.cc.pending` Verse commit commands and a
|
||||
state-node applicator that writes canonical typed documents.
|
||||
- Done: extend run checkpoint commands to carry current-zone and entity
|
||||
snapshots into canonical `AetheriaZoneState` and `AetheriaEntitySnapshot`
|
||||
documents.
|
||||
- Done: stop legacy catalog pull/read paths from writing entries back to
|
||||
their source backing store.
|
||||
- Done: delete legacy catalog backing-store write/realtime APIs.
|
||||
|
|
@ -551,9 +559,9 @@ First Aetheria surfaces to publish:
|
|||
package-owned runtime catalog read models without a MessagePack catalog cache,
|
||||
including typed item masks, interior masks, hardpoint definitions,
|
||||
corporation/name-file links, and typed behavior payloads. It also proves the
|
||||
Unity runtime state commit log can queue a player-settings command, the
|
||||
`Aetheria.State` node can apply it into canonical typed state, and the
|
||||
command is cleared after application.
|
||||
Unity runtime state commit log can queue player-settings and run snapshot
|
||||
commands, the `Aetheria.State` node can apply them into canonical typed
|
||||
settings/run/zone/entity state, and commands are cleared after application.
|
||||
- Unity play smoke proves runtime UI reads from Eve surfaces and sends commands
|
||||
through the shared state service.
|
||||
- UI Toolkit lowering parity compares Aetheria surfaces against the Eve browser
|
||||
|
|
|
|||
Loading…
Reference in a new issue