Persist run generation seed in typed state
This commit is contained in:
parent
39ebe5b931
commit
9ca3de2ed6
12 changed files with 53 additions and 13 deletions
Binary file not shown.
|
|
@ -286,6 +286,7 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s
|
|||
ExitZoneIndex = 1,
|
||||
CurrentZoneIndex = 0,
|
||||
CurrentZoneEntityIndex = 0,
|
||||
GenerationSeed = 424242,
|
||||
DiscoveredZoneIndices = [0],
|
||||
ZoneKeys = [zoneKey.ToString()],
|
||||
ActionBarBindings =
|
||||
|
|
@ -441,6 +442,7 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether
|
|||
}
|
||||
|
||||
if (runState?.RunId != "smoke" ||
|
||||
runState.GenerationSeed != 424242 ||
|
||||
runState.ZoneKeys.Length != 1 ||
|
||||
runState.ActionBarBindings.Length != 1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -216,6 +216,7 @@ try
|
|||
new AetheriaRuntimeRunCheckpointCommit
|
||||
{
|
||||
RunId = "smoke-run",
|
||||
GenerationSeed = 424242,
|
||||
CurrentZoneIndex = 0,
|
||||
CurrentZoneEntityIndex = 0,
|
||||
DiscoveredZoneIndices = new[] { 0 },
|
||||
|
|
@ -504,6 +505,7 @@ try
|
|||
loadout.RootEntity.CargoContents.Length == 1 &&
|
||||
loadout.RootEntity.CargoContents[0].Items[0].Item.Quantity == 5 &&
|
||||
run?.ZoneKeys.Length == 1 &&
|
||||
run.GenerationSeed == 424242 &&
|
||||
run.ActionBarBindings.Length == 2 &&
|
||||
run.ActionBarBindings[0].ControlPath == "<Keyboard>/1" &&
|
||||
run.ActionBarBindings[0].Kind == "weapon_group" &&
|
||||
|
|
@ -593,6 +595,7 @@ try
|
|||
packageLoadouts[0].RootEntity.CargoContents[0].Items[0].Item.Quantity != 5 ||
|
||||
packageRuns.Count != 1 ||
|
||||
packageRuns[0].RunId != "smoke-run" ||
|
||||
packageRuns[0].GenerationSeed != 424242 ||
|
||||
packageRuns[0].ZoneKeys.Count != 1 ||
|
||||
packageRuns[0].ActionBarBindings.Count != 2 ||
|
||||
packageRuns[0].ActionBarBindings[1].TargetKey != "aetheria.item_definition:legacy:smoke:weapon" ||
|
||||
|
|
|
|||
|
|
@ -227,7 +227,8 @@ public static class AetheriaRuntimeCommitLogApplier
|
|||
DiscoveredZoneIndices = ToIntArray(checkpoint.DiscoveredZoneIndices),
|
||||
ActionBarBindings = ToActionBarBindings(checkpoint.ActionBarBindings),
|
||||
FactionRelationships = ToFactionRelationships(checkpoint.FactionRelationships),
|
||||
UpdatedAtUtc = updatedAtUtc
|
||||
UpdatedAtUtc = updatedAtUtc,
|
||||
GenerationSeed = checkpoint.GenerationSeed
|
||||
};
|
||||
|
||||
var zoneKeys = new List<string>();
|
||||
|
|
|
|||
|
|
@ -225,6 +225,9 @@ public sealed class AetheriaRunState
|
|||
|
||||
[Key(10)]
|
||||
public string UpdatedAtUtc { get; set; } = "";
|
||||
|
||||
[Key(11)]
|
||||
public uint GenerationSeed { get; set; }
|
||||
}
|
||||
|
||||
[CultDocument("aetheria.zone_state", "aetheria.zone_state.v1")]
|
||||
|
|
|
|||
|
|
@ -538,6 +538,7 @@ public class ActionGameManager : MonoBehaviour
|
|||
ExitZoneIndex = ZoneIndex(CurrentGalaxy?.Exit),
|
||||
CurrentZoneIndex = ZoneIndex(Zone?.GalaxyZone),
|
||||
CurrentZoneEntityIndex = Zone?.Entities?.IndexOf(CurrentEntity) ?? -1,
|
||||
GenerationSeed = CurrentGalaxy?.GenerationSeed ?? 0,
|
||||
DiscoveredZoneIndices = CurrentGalaxy?.DiscoveredZones
|
||||
.Select(ZoneIndex)
|
||||
.Where(index => index >= 0)
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public class Galaxy
|
|||
public GalaxyZone[] Zones { get; }
|
||||
public GalaxyZone Entrance { get; }
|
||||
public GalaxyZone Exit { get; }
|
||||
public uint GenerationSeed { get; }
|
||||
public Dictionary<Faction, FactionRelationship> FactionRelationships { get; } = new Dictionary<Faction, FactionRelationship>();
|
||||
private Action<string> Log { get; }
|
||||
public bool IsPrelude { get; }
|
||||
|
|
@ -60,7 +61,8 @@ public class Galaxy
|
|||
IsPrelude = false;
|
||||
Background = background;
|
||||
Log = log;
|
||||
var random = new Random(seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed);
|
||||
GenerationSeed = seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed;
|
||||
var random = new Random(GenerationSeed);
|
||||
Factions = _allFactions.OrderBy(x => random.NextFloat()).Take(settings.MegaCount).ToArray();
|
||||
foreach (var f in Factions) FactionRelationships[f] = FactionRelationship.Neutral;
|
||||
|
||||
|
|
@ -117,7 +119,8 @@ public class Galaxy
|
|||
|
||||
Background = background;
|
||||
Log = log;
|
||||
var random = new Random(seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed);
|
||||
GenerationSeed = seed == 0 ? (uint) (DateTime.Now.Ticks % uint.MaxValue) : seed;
|
||||
var random = new Random(GenerationSeed);
|
||||
|
||||
var factions = new List<Faction>();
|
||||
|
||||
|
|
|
|||
|
|
@ -107,6 +107,7 @@ public class MainMenu : MonoBehaviour
|
|||
{
|
||||
Settings.SectorBackgroundSettings.NoisePosition = Random.value * 1000;
|
||||
ActionGameManager.IsTutorial = false;
|
||||
var generationSeed = NextGenerationSeed();
|
||||
Task.Run(() =>
|
||||
{
|
||||
var sector = new Galaxy(
|
||||
|
|
@ -115,7 +116,8 @@ public class MainMenu : MonoBehaviour
|
|||
Settings.NameGeneratorSettings,
|
||||
ActionGameManager.RuntimeCatalog,
|
||||
Debug.Log,
|
||||
setState);
|
||||
setState,
|
||||
generationSeed);
|
||||
Observable.NextFrame().Subscribe(_ =>
|
||||
{
|
||||
ActionGameManager.CurrentGalaxy = sector;
|
||||
|
|
@ -133,6 +135,7 @@ public class MainMenu : MonoBehaviour
|
|||
} while (Settings.TutorialBackgroundSettings.CloudDensity(float2(0.5f)) < .5f);
|
||||
|
||||
ActionGameManager.IsTutorial = true;
|
||||
var generationSeed = NextGenerationSeed();
|
||||
Task.Run(() =>
|
||||
{
|
||||
var sector = new Galaxy(
|
||||
|
|
@ -143,7 +146,8 @@ public class MainMenu : MonoBehaviour
|
|||
ActionGameManager.RuntimePlayerSettings,
|
||||
ActionGameManager.GameDataDirectory.CreateSubdirectory("Narrative"),
|
||||
Debug.Log,
|
||||
setState);
|
||||
setState,
|
||||
generationSeed);
|
||||
Observable.NextFrame().Subscribe(_ =>
|
||||
{
|
||||
ActionGameManager.CurrentGalaxy = sector;
|
||||
|
|
@ -161,6 +165,12 @@ public class MainMenu : MonoBehaviour
|
|||
_nextMenu.panel.AddButton("Quit", Application.Quit);
|
||||
}
|
||||
|
||||
private static uint NextGenerationSeed()
|
||||
{
|
||||
var seed = (uint)Random.Range(1, int.MaxValue);
|
||||
return seed == 0 ? 1u : seed;
|
||||
}
|
||||
|
||||
private void ShowSettings()
|
||||
{
|
||||
_nextMenu.panel.Clear();
|
||||
|
|
|
|||
|
|
@ -687,7 +687,8 @@ namespace GameCult.Aetheria.State.Unity
|
|||
IReadOnlyList<string> zoneKeys,
|
||||
IReadOnlyList<AetheriaRuntimeActionBarBindingSnapshot> actionBarBindings,
|
||||
IReadOnlyList<AetheriaRuntimeFactionRelationshipSnapshot> factionRelationships,
|
||||
string updatedAtUtc)
|
||||
string updatedAtUtc,
|
||||
uint generationSeed)
|
||||
{
|
||||
RunId = runId;
|
||||
IsTutorial = isTutorial;
|
||||
|
|
@ -700,6 +701,7 @@ namespace GameCult.Aetheria.State.Unity
|
|||
ActionBarBindings = actionBarBindings;
|
||||
FactionRelationships = factionRelationships;
|
||||
UpdatedAtUtc = updatedAtUtc;
|
||||
GenerationSeed = generationSeed;
|
||||
}
|
||||
|
||||
public string RunId { get; }
|
||||
|
|
@ -713,6 +715,7 @@ namespace GameCult.Aetheria.State.Unity
|
|||
public IReadOnlyList<AetheriaRuntimeActionBarBindingSnapshot> ActionBarBindings { get; }
|
||||
public IReadOnlyList<AetheriaRuntimeFactionRelationshipSnapshot> FactionRelationships { get; }
|
||||
public string UpdatedAtUtc { get; }
|
||||
public uint GenerationSeed { get; }
|
||||
}
|
||||
|
||||
public sealed class AetheriaRuntimeZoneStateSnapshot
|
||||
|
|
|
|||
|
|
@ -462,7 +462,8 @@ namespace GameCult.Aetheria.State.Unity
|
|||
var actionBarBindings = ReadFieldActionBarBindings(ref reader, fields, 8);
|
||||
var factionRelationships = ReadFieldFactionRelationships(ref reader, fields, 9);
|
||||
var updatedAtUtc = ReadFieldString(ref reader, fields, 10);
|
||||
SkipRemaining(ref reader, fields, 11);
|
||||
var generationSeed = ReadFieldUInt32(ref reader, fields, 11);
|
||||
SkipRemaining(ref reader, fields, 12);
|
||||
return new AetheriaRuntimeRunStateSnapshot(
|
||||
runId,
|
||||
isTutorial,
|
||||
|
|
@ -474,7 +475,8 @@ namespace GameCult.Aetheria.State.Unity
|
|||
zoneKeys,
|
||||
actionBarBindings,
|
||||
factionRelationships,
|
||||
updatedAtUtc);
|
||||
updatedAtUtc,
|
||||
generationSeed);
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeZoneStateSnapshot ReadZoneStatePayload(byte[] payload)
|
||||
|
|
|
|||
|
|
@ -238,6 +238,9 @@ namespace GameCult.Aetheria.State.Unity
|
|||
|
||||
[Key(9)]
|
||||
public IReadOnlyList<AetheriaRuntimeFactionRelationshipCommit> FactionRelationships { get; set; } = Array.Empty<AetheriaRuntimeFactionRelationshipCommit>();
|
||||
|
||||
[Key(10)]
|
||||
public uint GenerationSeed { get; set; }
|
||||
}
|
||||
|
||||
[MessagePackObject]
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
`AetheriaStateNode`. The embedded Unity state package can also read typed
|
||||
run, zone, and entity snapshot documents back from the `.cc` store as
|
||||
read-only runtime DTOs, including action-bar bindings, faction
|
||||
relationships, zone entity/orbit/body references, entity session scalars,
|
||||
active consumables, behavior progress, weapon state, behavior state, and
|
||||
stat-grid rows. Local run saves, loadouts, player settings files, zone
|
||||
relationships, generation seed, zone entity/orbit/body references, entity
|
||||
session scalars, active consumables, behavior progress, weapon state,
|
||||
behavior state, and stat-grid rows. Local run saves, loadouts, player settings files, zone
|
||||
files, and generated keyboard layout caches no longer write bespoke durable files. The
|
||||
legacy
|
||||
`RuntimePlayerSettings` runtime object is no longer named or decorated as a
|
||||
|
|
@ -122,8 +122,8 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
- `Aetheria.State` now exposes typed node put/get ports for run state, zone
|
||||
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
|
||||
generation seed, position, direction, faction, hull, equipment slots, weapon
|
||||
groups, and a stat grid. This proves the `.zone` replacement graph is durable typed state. Unity
|
||||
now queues current-zone/current-entity-collection snapshots, current
|
||||
action-bar bindings, and current faction relationship rows through the
|
||||
runtime commit log during run checkpoints; `RuntimeZoneBlueprint` and
|
||||
|
|
@ -136,6 +136,12 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
|
|||
Runtime blueprints no longer capture or restore behavior-private
|
||||
`PersistentBehaviorData` blobs; that dead save-shape hook and its
|
||||
`IPersistentBehavior` interface are deleted.
|
||||
- Galaxy generation now exposes the actual nonzero seed used to create topology.
|
||||
The main menu passes explicit seeds into normal and tutorial galaxy
|
||||
construction, and run checkpoints persist that seed into `AetheriaRunState`.
|
||||
Current-zone snapshot restore must attach to a regenerated galaxy with the
|
||||
same seed; restoring zone indices against a fresh random galaxy is forbidden
|
||||
split-brain state, not a Continue feature.
|
||||
- Run checkpoint zone snapshots include typed orbit and body rows. Orbit IDs,
|
||||
parent IDs, distance, phase, fixed positions, body kind/name/orbit,
|
||||
resources, gravity/body multipliers, asteroid belt entries, and gas/sun
|
||||
|
|
@ -791,6 +797,9 @@ First Aetheria surfaces to publish:
|
|||
entity snapshot documents, so the `.zone` replacement graph is not only
|
||||
writable through the state node but also inspectable by runtime package
|
||||
consumers.
|
||||
- Done: persist the actual galaxy generation seed in typed run state, so
|
||||
future Continue/restore work has a reproducible topology body before
|
||||
applying current-zone/entity snapshots.
|
||||
- Remaining: add typed documents/mappers for runtime object graphs,
|
||||
typed behavior factory construction, remaining behavior-private state not
|
||||
covered by progress, weapon, sensor, radiator, reactor, or capacitor rows,
|
||||
|
|
|
|||
Loading…
Reference in a new issue