Resolve zone body and orbit keys natively

This commit is contained in:
Meta Vens 2026-06-16 16:27:33 +01:00
commit 452184ea3a
5 changed files with 96 additions and 38 deletions

Binary file not shown.

View file

@ -18,6 +18,7 @@ RequireTypedOrbitTaskKeys(root);
RequireTypedOrbitalEntityOrbitKeys(root);
RequireTypedOrbitConsumerKeys(root);
RequireKeyedOrbitRuntimeWrappers(root);
RequireNativeZoneKeyResolution(root);
RequireTypedFactionShellLinks(root);
RequireFactionKeyIdentity(root);
RequireEveRuntimeBootstrap(root);
@ -807,7 +808,6 @@ static void RequireTypedBehaviorBodyKeys(string root)
"public string OrbitKey { get; }",
"public string ParentOrbitKey { get; }",
"public string GetBodyKey(Guid bodyId)",
"public static Guid ParseBodyGuid(string bodyKey)",
"public bool TryGetPlanet(string bodyKey, out Planet planet)",
"public bool TryGetAsteroidBelt(string bodyKey, out AsteroidBelt belt)"
};
@ -899,7 +899,6 @@ static void RequireTypedOrbitTaskKeys(string root)
: 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)"
};
@ -1151,7 +1150,8 @@ static void RequireKeyedOrbitRuntimeWrappers(string root)
{
"public Orbit Orbit;",
"public Orbit Orbit { get; }",
"AsteroidBelts[belt.ID] = new AsteroidBelt(belt, Orbits[belt.Orbit]);",
"var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.Orbit]);",
"AsteroidBelts[belt.ID] = runtimeBelt;",
"belt.NewOrbitPosition = GetOrbitPosition(belt.Orbit.ParentOrbitKey);",
"public AsteroidBelt(AsteroidBeltConstructionData data, Orbit orbit)",
"Orbit = orbit;"
@ -1206,6 +1206,62 @@ static void RequireTypedFactionShellLinks(string root)
}
}
static void RequireNativeZoneKeyResolution(string root)
{
var zoneSourcePath = Path.Combine(root, "Assets", "Scripts", "ServerShared", "Zone.cs");
var zoneSource = File.Exists(zoneSourcePath)
? File.ReadAllText(zoneSourcePath)
: throw new InvalidOperationException("Cannot verify native Zone key resolution; Zone source is missing.");
var forbiddenSymbols = new[]
{
"public static Guid ParseOrbitGuid(string orbitKey)",
"public static Guid ParseBodyGuid(string bodyKey)",
"PlanetInstances.TryGetValue(ParseBodyGuid(bodyKey), out planet)",
"AsteroidBelts.TryGetValue(ParseBodyGuid(bodyKey), out belt)",
"Orbits.TryGetValue(ParseOrbitGuid(orbitKey), out orbit)",
"return GetOrbitPosition(ParseOrbitGuid(orbitKey));",
"return GetOrbitVelocity(ParseOrbitGuid(orbitKey));"
};
var hits = forbiddenSymbols
.Where(symbol => zoneSource.Contains(symbol, StringComparison.Ordinal))
.Select(symbol => $"Assets/Scripts/ServerShared/Zone.cs: contains {symbol}")
.ToArray();
if (hits.Length > 0)
{
throw new InvalidOperationException(
"Zone key-facing APIs must resolve typed body/orbit keys natively instead of reparsing GUIDs from those keys: " +
string.Join("; ", hits));
}
var requiredSymbols = new[]
{
"private readonly Dictionary<string, Planet> _planetsByBodyKey = new Dictionary<string, Planet>(StringComparer.Ordinal);",
"private readonly Dictionary<string, AsteroidBelt> _asteroidBeltsByBodyKey = new Dictionary<string, AsteroidBelt>(StringComparer.Ordinal);",
"private readonly Dictionary<string, Orbit> _orbitsByKey = new Dictionary<string, Orbit>(StringComparer.Ordinal);",
"_orbitsByKey[runtimeOrbit.OrbitKey] = runtimeOrbit;",
"_asteroidBeltsByBodyKey[runtimeBelt.BodyKey] = runtimeBelt;",
"_planetsByBodyKey[runtimeSun.BodyKey] = runtimeSun;",
"_planetsByBodyKey[runtimeGas.BodyKey] = runtimeGas;",
"_planetsByBodyKey[runtimePlanet.BodyKey] = runtimePlanet;",
"return _planetsByBodyKey.TryGetValue(bodyKey ?? \"\", out planet);",
"return _asteroidBeltsByBodyKey.TryGetValue(bodyKey ?? \"\", out belt);",
"return _orbitsByKey.TryGetValue(orbitKey ?? \"\", out orbit);",
"return TryGetOrbit(orbitKey, out var orbit)",
"? GetOrbitPosition(orbit.ID)",
"? GetOrbitVelocity(orbit.ID)"
};
var missingSymbols = requiredSymbols
.Where(symbol => !zoneSource.Contains(symbol, StringComparison.Ordinal))
.ToArray();
if (missingSymbols.Length > 0)
{
throw new InvalidOperationException(
"Zone must keep native key-indexed runtime lookup tables for body and orbit resolution: " +
string.Join(", ", missingSymbols));
}
}
static void RequireFactionKeyIdentity(string root)
{
var checkedFiles = new[]

View file

@ -146,6 +146,10 @@ reading wrapper GUID orbit fields directly.
orbit GUID fields either: they keep `OrbitKey` plus their `Orbit` runtime
object, and `Zone` lowers asteroid transforms through `belt.Orbit.ParentOrbitKey`
instead of reading a raw wrapper orbit GUID.
`Zone`'s own public key-facing lookup surface is also now natively keyed:
body and orbit lookups resolve through key-indexed runtime dictionaries rather
than reparsing GUIDs from `BodyKey` or `OrbitKey` strings before touching the
Guid-indexed substrate.
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

@ -26,6 +26,10 @@ public class Zone
public Dictionary<Guid, Orbit> Orbits = new Dictionary<Guid, Orbit>();
public Dictionary<Guid, AsteroidBelt> AsteroidBelts = new Dictionary<Guid, AsteroidBelt>();
public PlanetSettings Settings;
private readonly Dictionary<string, Planet> _planetsByBodyKey = new Dictionary<string, Planet>(StringComparer.Ordinal);
private readonly Dictionary<string, AsteroidBelt> _asteroidBeltsByBodyKey = new Dictionary<string, AsteroidBelt>(StringComparer.Ordinal);
private readonly Dictionary<string, Orbit> _orbitsByKey = new Dictionary<string, Orbit>(StringComparer.Ordinal);
private HashSet<Guid> _updatedOrbits = new HashSet<Guid>();
@ -58,7 +62,9 @@ public class Zone
foreach (var orbit in blueprint.Orbits)
{
Orbits.Add(orbit.ID, new Orbit(Settings, orbit));
var runtimeOrbit = new Orbit(Settings, orbit);
Orbits.Add(orbit.ID, runtimeOrbit);
_orbitsByKey[runtimeOrbit.OrbitKey] = runtimeOrbit;
}
foreach (var planet in blueprint.Bodies)
@ -66,16 +72,24 @@ public class Zone
switch (planet)
{
case AsteroidBeltConstructionData belt:
AsteroidBelts[belt.ID] = new AsteroidBelt(belt, Orbits[belt.Orbit]);
var runtimeBelt = new AsteroidBelt(belt, Orbits[belt.Orbit]);
AsteroidBelts[belt.ID] = runtimeBelt;
_asteroidBeltsByBodyKey[runtimeBelt.BodyKey] = runtimeBelt;
break;
case SunConstructionData sun:
PlanetInstances.Add(sun.ID, new Sun(settings, sun, Orbits[planet.Orbit]));
var runtimeSun = new Sun(settings, sun, Orbits[planet.Orbit]);
PlanetInstances.Add(sun.ID, runtimeSun);
_planetsByBodyKey[runtimeSun.BodyKey] = runtimeSun;
break;
case GasGiantConstructionData gas:
PlanetInstances.Add(gas.ID, new GasGiant(settings, gas, Orbits[planet.Orbit]));
var runtimeGas = new GasGiant(settings, gas, Orbits[planet.Orbit]);
PlanetInstances.Add(gas.ID, runtimeGas);
_planetsByBodyKey[runtimeGas.BodyKey] = runtimeGas;
break;
default:
PlanetInstances.Add(planet.ID, new Planet(settings, planet, Orbits[planet.Orbit]));
var runtimePlanet = new Planet(settings, planet, Orbits[planet.Orbit]);
PlanetInstances.Add(planet.ID, runtimePlanet);
_planetsByBodyKey[runtimePlanet.BodyKey] = runtimePlanet;
break;
}
}
@ -119,17 +133,6 @@ 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)
@ -141,30 +144,19 @@ public class Zone
return BodyKey(bodyId);
}
public static Guid ParseBodyGuid(string bodyKey)
{
if (string.IsNullOrWhiteSpace(bodyKey))
return Guid.Empty;
var runtimeBodyId = bodyKey.StartsWith(BodyKeyPrefix, StringComparison.OrdinalIgnoreCase)
? bodyKey.Substring(BodyKeyPrefix.Length)
: bodyKey;
return Guid.TryParse(runtimeBodyId, out var id) ? id : Guid.Empty;
}
public bool TryGetPlanet(string bodyKey, out Planet planet)
{
return PlanetInstances.TryGetValue(ParseBodyGuid(bodyKey), out planet);
return _planetsByBodyKey.TryGetValue(bodyKey ?? "", out planet);
}
public bool TryGetAsteroidBelt(string bodyKey, out AsteroidBelt belt)
{
return AsteroidBelts.TryGetValue(ParseBodyGuid(bodyKey), out belt);
return _asteroidBeltsByBodyKey.TryGetValue(bodyKey ?? "", out belt);
}
public bool TryGetOrbit(string orbitKey, out Orbit orbit)
{
return Orbits.TryGetValue(ParseOrbitGuid(orbitKey), out orbit);
return _orbitsByKey.TryGetValue(orbitKey ?? "", out orbit);
}
public void Update(float deltaTime)
@ -234,7 +226,9 @@ public class Zone
public float2 GetOrbitPosition(string orbitKey)
{
return GetOrbitPosition(ParseOrbitGuid(orbitKey));
return TryGetOrbit(orbitKey, out var orbit)
? GetOrbitPosition(orbit.ID)
: float2.zero;
}
public float2 GetOrbitVelocity(Guid orbit)
@ -246,7 +240,9 @@ public class Zone
public float2 GetOrbitVelocity(string orbitKey)
{
return GetOrbitVelocity(ParseOrbitGuid(orbitKey));
return TryGetOrbit(orbitKey, out var orbit)
? GetOrbitVelocity(orbit.ID)
: float2.zero;
}
public int NearestAsteroid(Guid planetDataID, float2 position)

View file

@ -1375,9 +1375,11 @@ First Aetheria surfaces to publish:
`ResourceScanner` orbit readers now also consume the wrapper `OrbitKey`
edge instead of wrapper GUID orbit fields. `Planet` and `AsteroidBelt`
runtime wrappers no longer publish separate wrapper orbit GUID fields
either; they retain `OrbitKey` plus their runtime `Orbit` object. Remaining
broader simulation paths still derive legacy GUIDs until those structures
move to typed keys.
either; they retain `OrbitKey` plus their runtime `Orbit` object. `Zone`'s
key-facing lookup surface also resolves through native key-indexed runtime
dictionaries instead of reparsing GUIDs from key strings. 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