Keep orbit task targets on typed keys

This commit is contained in:
Meta Vens 2026-06-16 15:54:18 +01:00
commit cfc1ddac6d
8 changed files with 137 additions and 10 deletions

Binary file not shown.

View file

@ -14,6 +14,7 @@ RequireTypedRuntimeFactionKeys(root);
RequireTypedGalaxyFactionRelationships(root);
RequireRuntimeCatalogKeyOnlyLookups(root);
RequireTypedBehaviorBodyKeys(root);
RequireTypedOrbitTaskKeys(root);
RequireTypedFactionShellLinks(root);
RequireFactionKeyIdentity(root);
RequireEveRuntimeBootstrap(root);
@ -856,6 +857,95 @@ static void RequireTypedBehaviorBodyKeys(string root)
}
}
static void RequireTypedOrbitTaskKeys(string root)
{
var checkedFiles = new[]
{
Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs"),
Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "States", "MoveTo.cs"),
Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "PatrolOrbits.cs"),
Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "StationTowing.cs")
};
var forbiddenSymbols = new[]
{
"public Guid Orbit { get; set; }",
"Guid[] Circuit",
"public Guid OrbitParent",
"Select(x => x.Key)"
};
var hits = checkedFiles
.Where(File.Exists)
.SelectMany(path => File.ReadLines(path)
.Select((line, index) => new { Path = path, LineNumber = index + 1, Line = line }))
.Where(line => forbiddenSymbols.Any(symbol => line.Line.Contains(symbol, StringComparison.Ordinal)))
.Select(line => $"{Path.GetRelativePath(root, line.Path)}:{line.LineNumber}: {line.Line.Trim()}")
.ToArray();
if (hits.Length > 0)
{
throw new InvalidOperationException(
"Orbit-targeting agent tasks and move states must stay on typed OrbitKey surfaces, with Zone owning orbit-key resolution: " +
string.Join("; ", hits));
}
var zoneSourcePath = Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs");
var zoneSource = File.Exists(zoneSourcePath)
? File.ReadAllText(zoneSourcePath)
: throw new InvalidOperationException("Cannot verify orbit-key task ownership; Zone source is missing.");
var requiredZoneSymbols = new[]
{
"public static Guid ParseOrbitGuid(string orbitKey)",
"public bool TryGetOrbit(string orbitKey, out Orbit orbit)",
"public float2 GetOrbitPosition(string orbitKey)"
};
var missingZoneSymbols = requiredZoneSymbols
.Where(symbol => !zoneSource.Contains(symbol, StringComparison.Ordinal))
.ToArray();
if (missingZoneSymbols.Length > 0)
{
throw new InvalidOperationException(
"Zone must own typed orbit-key parsing and resolution for agent/runtime orbit movement: " +
string.Join(", ", missingZoneSymbols));
}
var requiredTaskSymbols = new Dictionary<string, string[]>
{
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "States", "MoveTo.cs")] = new[]
{
"public string OrbitKey { get; set; } = \"\";",
"_agent.Ship.Zone.GetOrbitPosition(OrbitKey)"
},
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "PatrolOrbits.cs")] = new[]
{
"public string[] Circuit = Array.Empty<string>();",
"public string CurrentTarget",
"patrolMoveState.OrbitKey = CurrentTarget"
},
[Path.Combine(root, "Assets", "Scripts", "ServerShared", "Agents", "Tasks", "StationTowing.cs")] = new[]
{
"public string OrbitParentKey = \"\";"
}
};
var missingTaskSymbols = requiredTaskSymbols
.Where(pair => !File.Exists(pair.Key) || pair.Value.Any(symbol => !File.ReadAllText(pair.Key).Contains(symbol, StringComparison.Ordinal)))
.SelectMany(pair =>
{
var text = File.Exists(pair.Key) ? File.ReadAllText(pair.Key) : "";
return pair.Value
.Where(symbol => !text.Contains(symbol, StringComparison.Ordinal))
.Select(symbol => $"{Path.GetRelativePath(root, pair.Key)}: missing {symbol}");
})
.ToArray();
if (missingTaskSymbols.Length > 0)
{
throw new InvalidOperationException(
"Orbit-targeting agent tasks must retain typed orbit keys rather than raw GUIDs: " +
string.Join("; ", missingTaskSymbols));
}
}
static void RequireTypedFactionShellLinks(string root)
{
var checkedFiles = new[]

View file

@ -129,6 +129,9 @@ and `MiningTool` now retain typed body-key runtime state directly, while
`Zone`, `Planet`, `AsteroidBelt`, and `Orbit` now own the runtime body/orbit
key surfaces used for projection, and `ActionGameManager` no longer formats or
resolves body/orbit keys locally while checkpointing or restoring state.
Orbit-targeting agent runtime also now retains typed orbit keys directly:
`PatrolOrbitsTask`, `MoveToOrbitState`, and `StationTowing` no longer own raw
orbit GUID targets, and `Zone` resolves orbit keys for agent orbit movement.
The package runtime catalog snapshot indexes items by canonical item key; Unity
gameplay, HUD, inventory, trade, ship, and zone helper lookups use `ItemKey`
directly instead of deriving `ItemId` and searching by legacy GUID.

View file

@ -37,8 +37,8 @@ public class MoveToEntityState : MoveToState
public class MoveToOrbitState : MoveToState
{
public Guid Orbit { get; set; }
public string OrbitKey { get; set; } = "";
public MoveToOrbitState(Agent agent) : base(agent) { }
protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(Orbit);
protected override float2 TargetPosition => _agent.Ship.Zone.GetOrbitPosition(OrbitKey);
}

View file

@ -3,13 +3,13 @@ using System;
public class PatrolOrbitsTask : AgentTask
{
public override TaskType Type => TaskType.Defend;
public Guid[] Circuit;
public string[] Circuit = Array.Empty<string>();
}
public class PatrolOrbitsState : BaseState
{
public PatrolOrbitsTask Task;
public Guid CurrentTarget
public string CurrentTarget
{
get => Task.Circuit[_currentTargetIndex];
}
@ -25,7 +25,7 @@ public class PatrolOrbitsState : BaseState
var patrolMoveState = new MoveToOrbitState(agent);
Transitions.Add(new StateTransition(patrolMoveState,
() => true,
() => patrolMoveState.Orbit = CurrentTarget));
() => patrolMoveState.OrbitKey = CurrentTarget));
patrolMoveState.Transitions.Add(new StateTransition(this, () => patrolMoveState.Distance < 10, NextTarget));
}
}
}

View file

@ -10,7 +10,7 @@ public class StationTowing : AgentTask
public OrbitalEntity Station;
public Guid OrbitParent;
public string OrbitParentKey = "";
public float OrbitDistance;
}

View file

@ -100,7 +100,11 @@ public class Zone
{
var agent = new Minion(ship);
var task = new PatrolOrbitsTask();
task.Circuit = Orbits.OrderBy(_ => _itemManager.Random.NextFloat()).Take(4).Select(x => x.Key).ToArray();
task.Circuit = Orbits.Values
.OrderBy(_ => _itemManager.Random.NextFloat())
.Take(4)
.Select(orbit => orbit.OrbitKey)
.ToArray();
agent.Task = task;
return agent;
}
@ -115,6 +119,17 @@ public class Zone
return id == Guid.Empty ? "" : $"{BodyKeyPrefix}{id:D}";
}
public static Guid ParseOrbitGuid(string orbitKey)
{
if (string.IsNullOrWhiteSpace(orbitKey))
return Guid.Empty;
var runtimeOrbitId = orbitKey.StartsWith(OrbitKeyPrefix, StringComparison.OrdinalIgnoreCase)
? orbitKey.Substring(OrbitKeyPrefix.Length)
: orbitKey;
return Guid.TryParse(runtimeOrbitId, out var id) ? id : Guid.Empty;
}
public string GetBodyKey(Guid bodyId)
{
if (bodyId == Guid.Empty)
@ -147,6 +162,11 @@ public class Zone
return AsteroidBelts.TryGetValue(ParseBodyGuid(bodyKey), out belt);
}
public bool TryGetOrbit(string orbitKey, out Orbit orbit)
{
return Orbits.TryGetValue(ParseOrbitGuid(orbitKey), out orbit);
}
public void Update(float deltaTime)
{
_time += deltaTime;
@ -212,6 +232,11 @@ public class Zone
return Orbits[orbitID].Position;
}
public float2 GetOrbitPosition(string orbitKey)
{
return GetOrbitPosition(ParseOrbitGuid(orbitKey));
}
public float2 GetOrbitVelocity(Guid orbit)
{
if (Orbits.ContainsKey(orbit))

View file

@ -1201,6 +1201,9 @@ First Aetheria surfaces to publish:
- Done: rename `OrbitalEntity.OrbitData` to `OrbitId` and delete the unused
`Zone.CreateOrbit` construction-row writer; runtime entities no longer
expose an orbit id through DTO-shaped naming.
- Done: move patrol/tow orbit targets onto typed orbit keys: `PatrolOrbitsTask`,
`MoveToOrbitState`, and `StationTowing` now retain keyed orbit references,
and `Zone` owns orbit-key parsing/resolution for agent orbit movement.
- Done: move live asteroid-belt simulation, scanning, and mesh setup reads
off `AsteroidBeltConstructionData`; `Zone`, `ResourceScanner`, and `AsteroidBeltUI`
consume `AsteroidBelt` runtime asteroid/resource/orbit properties, and
@ -1363,8 +1366,10 @@ First Aetheria surfaces to publish:
mining-tool body references are named and serialized as typed body keys
through commit/readback state, with the behaviors retaining keyed runtime
state directly and `Zone` owning GUID parsing/resolution at the current
Unity body runtime boundary. Remaining broader simulation paths still
derive legacy GUIDs until those structures move to typed keys.
Unity body runtime boundary. Patrol/tow orbit task targets now also retain
typed orbit keys with `Zone` owning orbit-key resolution. Remaining broader
simulation paths still derive legacy GUIDs until those structures move to
typed keys.
- Done: delete the `ItemInstance.ItemId` legacy-GUID projection; diagnostics
now report `ItemKey` so item instances expose only typed item-key identity.
- Done: delete `Entity`/`EquippedCargoBay` cargo and consumable GUID lookup
@ -1779,6 +1784,10 @@ First Aetheria surfaces to publish:
return. `Zone`, `Planet`, `AsteroidBelt`, and `Orbit` now own the runtime
body/orbit key surfaces consumed by `ActionGameManager` projection, so local
body/orbit key formatters may not return there either.
- `Aetheria.State.Verify` guards orbit-targeting agent runtime naming:
patrol/tow task orbit references and move-state orbit targets use `OrbitKey`
fields, and `Zone` owns orbit-key parsing/resolution instead of agent-local
raw GUID targets.
- `Aetheria.State.Verify` guards temporary `Faction` shell links: geoname and
boss-hull relationships use typed keys, not legacy GUID projection fields.
- `Aetheria.State.Verify` guards temporary `Faction` identity: equality,