Aetheria/Aetheria.State.Daemon.Smoke/Program.cs
2026-08-08 09:31:24 +01:00

9834 lines
546 KiB
C#

using Aetheria.State;
using Aetheria.State.Daemon;
using Aetheria.State.Documents;
using GameCult.Aetheria.State.Verse;
using GameCult.Caching;
using GameCult.Eve.PluginFields;
using GameCult.Eve.Surface;
using GameCult.Mesh;
using System.Globalization;
var checks = new AetheriaDaemonYmirSmokeChecks();
if (args.Contains("--gravity", StringComparer.Ordinal))
{
checks.RunGravity();
Console.WriteLine("Daemon Ymir gravity-sign smoke passed.");
}
else if (args.Contains("--loadout", StringComparer.Ordinal))
{
checks.RunLoadout();
Console.WriteLine("Daemon loadout hardpoint smoke passed.");
}
else if (args.Contains("--pickup", StringComparer.Ordinal))
{
checks.RunPickup();
Console.WriteLine("Daemon pickup-proximity smoke passed.");
}
else if (args.Contains("--payload", StringComparer.Ordinal))
{
checks.RunPayload();
Console.WriteLine("Daemon retained Ymir payload smoke passed.");
}
else if (args.Contains("--tutorial-topology", StringComparer.Ordinal))
{
checks.RunTutorialTopology();
Console.WriteLine("Daemon tutorial topology smoke passed.");
}
else if (args.Contains("--regular-topology", StringComparer.Ordinal))
{
checks.RunRegularTopology();
Console.WriteLine("Daemon regular-sector topology smoke passed.");
}
else if (args.Contains("--regular-run", StringComparer.Ordinal))
{
checks.RunRegularRun();
Console.WriteLine("Daemon persisted regular-sector run smoke passed.");
}
else if (args.Contains("--zone-bodies", StringComparer.Ordinal))
{
checks.RunZoneBodies();
Console.WriteLine("Daemon generated zone-body smoke passed.");
}
else if (args.Contains("--tutorial-world", StringComparer.Ordinal))
{
checks.RunTutorialWorld();
Console.WriteLine("Daemon tutorial world-plan smoke passed.");
}
else if (args.Contains("--tutorial-materialization", StringComparer.Ordinal))
{
checks.RunTutorialMaterialization();
Console.WriteLine("Daemon tutorial entity-materialization smoke passed.");
}
else if (args.Contains("--tutorial-run", StringComparer.Ordinal))
{
checks.RunTutorialRun();
Console.WriteLine("Daemon persisted tutorial-run smoke passed.");
}
else if (args.Contains("--orbits", StringComparer.Ordinal))
{
checks.RunOrbits();
Console.WriteLine("Daemon dynamic-orbit smoke passed.");
}
else if (args.Contains("--patrol", StringComparer.Ordinal))
{
checks.RunPatrol();
Console.WriteLine("Daemon generated-agent patrol smoke passed.");
}
else if (args.Contains("--combat-lock", StringComparer.Ordinal))
{
checks.RunCombatLock();
Console.WriteLine("Daemon look-direction weapon-lock smoke passed.");
}
else if (args.Contains("--hangar", StringComparer.Ordinal))
{
checks.RunHangar();
Console.WriteLine("Daemon Hangar deployment and continuation smoke passed.");
}
else
{
checks.Run();
Console.WriteLine("Daemon Ymir physical payload smoke passed.");
}
internal sealed class AetheriaDaemonYmirSmokeChecks
{
private static readonly List<AetheriaYmirWorldPhysics> OwnedPhysics = [];
public void RunGravity() => RunCheck(PositiveGravityDepthAttractsAndProjectsAsAWell);
public void RunCombatLock() => RunCheck(InstantWeaponRequestSurvivesLockAcquisition);
public void RunHangar() => RunCheck(HangarLaunchUsesConfiguredLoadoutAndContinuesSavedRun);
public void RunLoadout()
{
DaemonNativeDockyardMaterializesTypedCatalog();
DaemonLoadoutsRespectFactionAvailabilityAndHullRoles();
}
public void RunPickup()
{
RunCheck(RetainedWorldAdvancesEveryFixedSubstep);
RunCheck(StableEntityIdentitySurvivesCrossZoneReindex);
RunCheck(TractorRampsAndPullsThroughYmirWithoutTeleportingCargo);
RunCheck(PickupProximityPrecedesYmirCollisionShell);
RunCheck(CatalogItemIndexPreservesSnapshotLookupSemantics);
RunCheck(PickupIsCapacityCheckedExactlyOnceAndExpires);
RunCheck(StationBodiesCannotConsumePickups);
RunCheck(PickupProximityCollectsOrBounces);
RunCheck(PausedFirstFrameCreatesPersistableYmirWorld);
RunCheck(PickupRemovalSurvivesRestartWithoutReplay);
}
public void RunPayload()
{
RunCheck(YmirMovesProjectileAndReportsStableContact);
RunCheck(PayloadQueryExcludesItsSource);
RunCheck(PayloadBodiesDoNotCollideWithEachOther);
RunCheck(OverlappingMineRemainsQueryableAfterArming);
}
public void RunTutorialTopology() => RunCheck(TutorialTopologyIsDeterministicConnectedAndRoleOwned);
public void RunRegularTopology() => RunCheck(RegularTopologyOwnsFossilSectorStructure);
public void RunRegularRun() => RunCheck(RegularRunPersistsPlayableWorldTruth);
public void RunZoneBodies() => RunCheck(GeneratedZoneBodiesPreserveFossilHierarchy);
public void RunTutorialWorld() => RunCheck(TutorialWorldMaterializesEveryTopologyZone);
public void RunTutorialMaterialization() => RunCheck(TutorialPopulationPreservesFossilEntityMechanics);
public void RunTutorialRun() => RunCheck(TutorialRunPersistsCanonicalWorldTruth);
public void RunOrbits() => RunCheck(GeneratedOrbitsAdvanceOrbitalWorldTruth);
public void RunPatrol()
{
RunCheck(SecurityZonesOwnFossilTrespasserHostility);
RunCheck(AgentPatrolsHistoricalOrbitCircuitThroughMovementCommands);
RunCheck(PatrolCombatPreemptsAndResumesTheSameCircuit);
}
private static void HangarLaunchUsesConfiguredLoadoutAndContinuesSavedRun()
{
var runtimeCatalog = TutorialPopulationCatalog(out _);
runtimeCatalog.Items.Single(item => string.Equals(item.HullType, "Station", StringComparison.Ordinal)).ItemKey =
AetheriaDaemonNativeCatalog.DockyardHullItemKey;
var root = Path.Combine(Path.GetTempPath(), $"aetheria-hangar-{Guid.NewGuid():N}");
var statePath = Path.Combine(root, "aetheria.cc");
Directory.CreateDirectory(root);
try
{
string runKey;
string deploymentId;
string removedItemKey;
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
AetheriaDaemonHangarCoordinator.EnsureAsync(node, runtimeCatalog, "2026-08-08T00:00:00Z")
.GetAwaiter().GetResult();
var hangar = node.MutableDocument<AetheriaHangarState>(AetheriaStateNode.HangarKey)
.ReadAsync().GetAwaiter().GetResult()!;
var ship = hangar.Ships.Single();
var template = node.MutableDocument<AetheriaLoadoutTemplate>(new CultRecordKey(ship.LoadoutTemplateKey))
.ReadAsync().GetAwaiter().GetResult()!;
removedItemKey = template.RootEntity.Equipment[0].Item.ItemKey;
var mutation = AetheriaHangar.RemoveAsync(node, ship.ShipId, 0, hangar.Revision, "2026-08-08T00:00:01Z")
.GetAwaiter().GetResult();
Require(mutation.Accepted, "Hangar loadout customization must commit through the canonical Hangar owner");
hangar = node.MutableDocument<AetheriaHangarState>(AetheriaStateNode.HangarKey)
.ReadAsync().GetAwaiter().GetResult()!;
template = node.MutableDocument<AetheriaLoadoutTemplate>(new CultRecordKey(ship.LoadoutTemplateKey))
.ReadAsync().GetAwaiter().GetResult()!;
var surface = AetheriaRuntimeHangarSurfaceBuilder.Build(
hangar, ship.ShipId, AetheriaGameModes.Terminus, "2026-08-08T00:00:01Z", 1,
AetheriaRuntimeStateMapper.ToRuntimeLoadoutTemplate(template), runtimeCatalog);
var world = surface.Surface.Root.Children.Single(component => component.Kind == "world.scene3d");
Require(surface.Surface.Id == AetheriaRuntimeHangarCommands.SurfaceId &&
world.Props.Any(prop => prop.Key == "entityViewPointerId" && !string.IsNullOrWhiteSpace(prop.Value)) &&
world.Props.Any(prop => prop.Key == "entityBodyId" && !string.IsNullOrWhiteSpace(prop.Value)) &&
surface.Commands.Any(command => command.Command == AetheriaRuntimeHangarCommands.EquipItem) &&
surface.Commands.Any(command => command.Command == AetheriaRuntimeHangarCommands.RemoveItem) &&
surface.Commands.Any(command => command.Command == AetheriaRuntimeHangarCommands.Launch) &&
surface.Commands.Any(command => command.Command == AetheriaRuntimeHangarCommands.Continue),
"the daemon-owned Hangar surface must advertise loadout, launch, and continuation operations");
var receipt = AetheriaDaemonHangarCoordinator.LaunchTerminusAsync(
node, runtimeCatalog, "launch-configured", ship.ShipId, mutation.HangarRevision, "2026-08-08T00:00:02Z")
.GetAwaiter().GetResult();
Require(receipt.Accepted, "a valid configured Hangar deployment must launch Terminus");
deploymentId = receipt.DeploymentId;
var settings = node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReadAsync().GetAwaiter().GetResult()!;
runKey = settings.ActiveRunKey;
var run = node.MutableDocument<AetheriaRunState>(new CultRecordKey(runKey))
.ReadAsync().GetAwaiter().GetResult()!;
var player = node.MutableDocument<AetheriaEntitySnapshot>(new CultRecordKey(run.CurrentEntityKey))
.ReadAsync().GetAwaiter().GetResult()!;
Require(run.GameMode == AetheriaGameModes.Terminus &&
run.GenerationSeed == AetheriaDaemonRunFactory.StableSeed(deploymentId),
"the accepted Hangar deployment must own the new Terminus run identity");
Require(player.Equipment.All(slot => !string.Equals(slot.ItemKey, removedItemKey, StringComparison.Ordinal)),
"Terminus must instantiate the configured Hangar loadout, not regenerate the starter fit");
}
using (var reopened = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
var canContinue = AetheriaDaemonHangarCoordinator.CanContinueTerminusAsync(
reopened, AetheriaDaemonHangarCoordinator.StarterShipId, deploymentId)
.GetAwaiter().GetResult();
var settings = reopened.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReadAsync().GetAwaiter().GetResult();
var run = reopened.MutableDocument<AetheriaRunState>(new CultRecordKey(runKey))
.ReadAsync().GetAwaiter().GetResult();
Require(canContinue && settings?.ActiveRunKey == runKey && run != null,
"Continue must reopen the saved Terminus checkpoint without creating another run");
}
}
finally
{
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
}
private static void TutorialRunPersistsCanonicalWorldTruth()
{
var catalog = TutorialPopulationCatalog(out _);
var root = Path.Combine(Path.GetTempPath(), $"aetheria-tutorial-run-{Guid.NewGuid():N}");
var statePath = Path.Combine(root, "aetheria.cc");
Directory.CreateDirectory(root);
try
{
using var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult();
var written = AetheriaDaemonTutorialRunWriter.WriteAsync(
node, catalog, "2026-07-17T00:00:00.0000000Z").GetAwaiter().GetResult();
var run = node.MutableDocument<AetheriaRunState>(new CultRecordKey(written.RunKey))
.ReadAsync().GetAwaiter().GetResult();
var settings = node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReadAsync().GetAwaiter().GetResult();
Require(run != null && run.IsTutorial && run.ZoneKeys.Length == 64 &&
run.EntranceZoneIndex == run.CurrentZoneIndex && run.ExitZoneIndex == -1,
"persisted New Game truth must be the complete authored tutorial graph, not the Terminus witness fixture");
Require(run!.HomeZones.Length == 6 && run.HomeZones.All(entry => entry.ZoneIndex >= 0),
"persisted tutorial truth must retain every faction home landmark for the sector map");
Require(run!.AgentTasks.Length > 0 && run.AgentTasks.All(task =>
task.TaskType == "patrol" && task.Status == "assigned" && task.TargetOrbitKeys.Length == 4),
"persisted tutorial agents must retain their assigned four-orbit fossil patrols");
Require(settings != null && settings.ActiveRunKey == written.RunKey && !settings.TutorialPassed,
"tutorial persistence must select the new run without falsely awarding completion");
Require(run!.CurrentEntityKey == written.CurrentEntityKey,
"the run and session handoff must share one canonical controlled-entity identity");
var entrance = node.MutableDocument<AetheriaZoneState>(
new CultRecordKey(run.ZoneKeys[run.EntranceZoneIndex]))
.ReadAsync().GetAwaiter().GetResult();
Require(entrance != null && entrance.AdjacentZoneIndices.Length > 0 &&
entrance.EntityKeys.Contains(run.CurrentEntityKey, StringComparer.Ordinal) &&
entrance.Bodies.Length > 0 && entrance.Orbits.Length >= entrance.Bodies.Length,
"the persisted entrance must contain the starter ship, celestial hierarchy, and derived wormhole adjacency");
var player = node.MutableDocument<AetheriaEntitySnapshot>(new CultRecordKey(run.CurrentEntityKey))
.ReadAsync().GetAwaiter().GetResult();
Require(player != null && player.Kind == "ship" && player.LoadoutGeneration != null,
"the persisted current entity must be a playable daemon-generated ship with loadout provenance");
}
finally
{
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
}
private static void RegularTopologyOwnsFossilSectorStructure()
{
var factions = Enumerable.Range(0, 12)
.Select(index => new AetheriaDaemonTutorialFactionInput(
$"faction.{index}", $"Faction {index}", $"F{index}", 3 + index % 3)
{
BossHullItemKey = index < 3 ? $"boss-hull-{index}" : "",
TrainingNames = Enumerable.Range(0, 256)
.Select(value => string.Concat(
(char)('a' + value % 26),
(char)('a' + value / 26 % 26),
(char)('a' + index % 26),
(char)('a' + (value * 7 + index) % 26),
(char)('a' + (value * 11 + index) % 26),
(char)('a' + (value * 17 + index) % 26)))
.ToArray()
})
.ToArray();
const uint seed = 0x5EC7_0206u;
var first = AetheriaDaemonRegularTopologyGenerator.GenerateFossil(factions, seed);
var second = AetheriaDaemonRegularTopologyGenerator.GenerateFossil(factions, seed);
RequireEqual(196, first.Zones.Count,
"regular generation must use the fossil 196-zone sector setting");
RequireEqual(12, first.HomeZoneByFactionKey.Count,
"regular generation must place all twelve selected corporations");
RequireEqual(3, first.BossZoneByFactionKey.Count,
"regular generation must place the fossil three boss corporations");
Require(first.EntranceZoneIndex >= 0 && first.ExitZoneIndex >= 0 &&
first.EntranceZoneIndex != first.ExitZoneIndex,
"regular generation must own distinct entrance and exit zones");
Require(first.Zones.All(zone => !string.IsNullOrWhiteSpace(zone.Name)),
"regular generation must name every owned and catalogue zone");
Require(first.Zones.All(zone => zone.AdjacentZoneIndices.Count > 0 &&
zone.AdjacentZoneIndices.All(adjacent =>
first.Zones[adjacent].AdjacentZoneIndices.Contains(zone.ZoneIndex))),
"regular generation must publish one connected undirected link graph");
int[] DistancesFrom(int source)
{
var distances = Enumerable.Repeat(-1, first.Zones.Count).ToArray();
var queue = new Queue<int>();
distances[source] = 0;
queue.Enqueue(source);
while (queue.Count > 0)
{
var current = queue.Dequeue();
foreach (var adjacent in first.Zones[current].AdjacentZoneIndices)
{
if (distances[adjacent] >= 0) continue;
distances[adjacent] = distances[current] + 1;
queue.Enqueue(adjacent);
}
}
return distances;
}
var isolation = first.Zones
.Select(zone => DistancesFrom(zone.ZoneIndex).Sum())
.ToArray();
RequireEqual(isolation.Max(), isolation[first.ExitZoneIndex],
"regular exit must be a maximally isolated zone");
var exitDistances = DistancesFrom(first.ExitZoneIndex);
RequireEqual(exitDistances.Max(), exitDistances[first.EntranceZoneIndex],
"regular entrance must be furthest from the exit");
Require(first.DiscoveredZoneIndices.OrderBy(value => value).SequenceEqual(
new[] { first.EntranceZoneIndex }
.Concat(first.Zones[first.EntranceZoneIndex].AdjacentZoneIndices)
.Distinct()
.OrderBy(value => value)),
"regular discovery must begin with only the entrance and its neighbors");
foreach (var home in first.HomeZoneByFactionKey)
Require(first.Zones[home.Value].FactionKeys.Contains(home.Key),
"each regular corporation must be present at its daemon-owned home zone");
Require(first.Zones.Select(zone => (zone.X, zone.Y, Links: string.Join(',', zone.AdjacentZoneIndices),
zone.OwnerFactionKey, zone.Name)).SequenceEqual(
second.Zones.Select(zone => (zone.X, zone.Y, Links: string.Join(',', zone.AdjacentZoneIndices),
zone.OwnerFactionKey, zone.Name))) &&
first.HomeZoneByFactionKey.OrderBy(value => value.Key).SequenceEqual(
second.HomeZoneByFactionKey.OrderBy(value => value.Key)) &&
first.BossZoneByFactionKey.OrderBy(value => value.Key).SequenceEqual(
second.BossZoneByFactionKey.OrderBy(value => value.Key)),
"regular topology, faction placement, boss placement, and names must be seed deterministic");
}
private static void RegularRunPersistsPlayableWorldTruth()
{
var tutorialCatalog = TutorialPopulationCatalog(out var factions);
var manufacturer = factions[0].CorporationKey;
foreach (var item in tutorialCatalog.Items)
item.ManufacturerKey = manufacturer;
var corporations = factions.Select((faction, index) => new AetheriaRuntimeCorporation(
faction.CorporationKey,
faction.Name,
faction.ShortName,
"",
$"names.{faction.ShortName}",
index < 2 ? "tutorial-ship-hull" : "",
4,
1,
[new AetheriaRuntimeCorporationAllegiance(manufacturer, 1)])).ToArray();
var catalog = new AetheriaRuntimeCatalogSnapshot(
tutorialCatalog.Items.ToArray(), corporations, tutorialCatalog.NameFiles.ToArray());
var root = Path.Combine(Path.GetTempPath(), $"aetheria-regular-run-{Guid.NewGuid():N}");
var statePath = Path.Combine(root, "aetheria.cc");
Directory.CreateDirectory(root);
try
{
string runKey;
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReplaceAsync(new AetheriaPlayerSettings { TutorialPassed = true })
.GetAwaiter().GetResult();
var written = AetheriaDaemonRunFactory.WriteAsync(
node,
catalog,
"2026-07-17T00:00:00.0000000Z",
"regular-run-command",
new AetheriaDaemonRegularTopologySettings(24, 0.5f, 6, 2))
.GetAwaiter().GetResult();
runKey = written.RunKey;
var run = node.MutableDocument<AetheriaRunState>(new CultRecordKey(runKey))
.ReadAsync().GetAwaiter().GetResult();
var settings = node.MutableDocument<AetheriaPlayerSettings>(AetheriaStateNode.PlayerSettingsKey)
.ReadAsync().GetAwaiter().GetResult();
Require(run != null && !run.IsTutorial &&
run.GameMode == AetheriaGameSessionState.AetheriaMode &&
run.ZoneKeys.Length == 24 && run.ExitZoneIndex >= 0 &&
run.CurrentZoneIndex == run.EntranceZoneIndex,
"regular writer must persist an Aetheria-mode sector rather than a Terminus scenario");
RequireEqual(AetheriaDaemonRunFactory.StableSeed("regular-run-command"), run!.GenerationSeed,
"accepted New Game identity must deterministically own regular-sector generation");
Require(written.SessionMode == AetheriaGameSessionState.AetheriaMode &&
settings?.ActiveRunKey == runKey && settings.TutorialPassed,
"regular writer must select the new sector without erasing tutorial completion");
Require(run!.AgentTasks.Length > 0 && run.AgentTasks.All(task =>
task.TaskType == AetheriaRuntimeAgentTaskTypes.Patrol &&
task.Status == AetheriaRuntimeAgentTaskStatuses.Assigned),
"regular population must persist autonomous patrol inhabitants");
Require(run.HomeZones.Length == 6 && run.BossZones.Length == 2 &&
run.HomeZones.All(entry => entry.ZoneIndex >= 0) &&
run.BossZones.All(entry => entry.ZoneIndex >= 0),
"regular persistence must retain every home and executive sector-map landmark");
Require(run.ZoneKeys.All(key => key.Contains(run.RunId, StringComparison.Ordinal)),
"regular zone records must be namespaced by the generated run identity");
var entrance = node.MutableDocument<AetheriaZoneState>(
new CultRecordKey(run.ZoneKeys[run.EntranceZoneIndex]))
.ReadAsync().GetAwaiter().GetResult();
Require(entrance != null && entrance.EntityKeys.Contains(run.CurrentEntityKey) &&
entrance.EntityKeys.All(key => !key.Contains("tutorial", StringComparison.OrdinalIgnoreCase)),
"regular entrance must contain the controlled starter under regular identities");
var starter = node.MutableDocument<AetheriaEntitySnapshot>(
new CultRecordKey(run.CurrentEntityKey))
.ReadAsync().GetAwaiter().GetResult();
Require(starter != null && starter.IsActive &&
!string.IsNullOrWhiteSpace(starter.HullItemKey) &&
starter.Equipment.Length > 0 && starter.LoadoutGeneration.Selections.Length > 0,
"regular entrance must persist a generated, equipped, controllable starter ship");
}
using var reopened = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult();
var restored = reopened.MutableDocument<AetheriaRunState>(new CultRecordKey(runKey))
.ReadAsync().GetAwaiter().GetResult();
Require(restored != null && restored.GameMode == AetheriaGameSessionState.AetheriaMode &&
restored.ZoneKeys.Length == 24 && !string.IsNullOrWhiteSpace(restored.CurrentEntityKey) &&
restored.HomeZones.Length == 6 && restored.BossZones.Length == 2,
"regular sector selection, controlled identity, and map landmarks must survive a hard CultCache reopen");
}
finally
{
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
}
private static void TutorialPopulationPreservesFossilEntityMechanics()
{
var catalog = TutorialPopulationCatalog(out var factions);
var settings = AetheriaDaemonTutorialTopologySettings.Fossil;
var firstWorld = AetheriaDaemonTutorialWorldGenerator.Generate(factions, 0xA37E_2026u, settings);
var secondWorld = AetheriaDaemonTutorialWorldGenerator.Generate(factions, 0xA37E_2026u, settings);
var first = AetheriaDaemonTutorialWorldMaterializer.Materialize(firstWorld, factions, catalog);
var second = AetheriaDaemonTutorialWorldMaterializer.Materialize(secondWorld, factions, catalog);
RequireEqual(first.Topology.EntranceZoneIndex, first.PlayerZoneIndex,
"the starter ship must enter the authored tutorial entrance zone");
var entrance = first.Zones[first.PlayerZoneIndex];
Require(entrance.PlayerEntityIndex >= 0 &&
entrance.Entities[entrance.PlayerEntityIndex].Kind == "ship" &&
entrance.Entities[entrance.PlayerEntityIndex].FactionKey == factions[0].CorporationKey,
"the tutorial must materialize one protagonist starter ship as daemon world truth");
Require(first.Zones.Values.Any(zone => zone.Entities.Any(entity => entity.Kind == "station")) &&
first.Zones.Values.Any(zone => zone.Entities.Any(entity => entity.Kind == "turret")) &&
first.Zones.Values.Any(zone => zone.Entities.Any(entity =>
entity.Kind == "ship" && entity.AgentTaskCapabilities.Contains("attack", StringComparer.Ordinal))),
"fossil station, defensive turret, and hostile-agent population must all survive materialization");
Require(first.Zones.Values.SelectMany(zone => zone.Entities)
.Where(entity => entity.Kind == "ship" && entity.AgentTaskCapabilities.Contains("attack", StringComparer.Ordinal))
.All(entity => entity.Position.X != 0 || entity.Position.Z != 0),
"generated non-player ships must retain the fossil activation rule that distributes zero-pose ships through the zone");
Require(first.Zones.Values.All(zone =>
{
var orbitKeys = zone.Orbits.Select(orbit => orbit.OrbitKey).ToHashSet(StringComparer.Ordinal);
var agents = zone.Entities.Where(entity =>
entity.Kind == "ship" && entity.AgentTaskCapabilities.Contains("attack", StringComparer.Ordinal)).ToArray();
return zone.AgentTasks.Length == agents.Length &&
zone.AgentTasks.All(task => task.TaskType == "patrol" &&
task.Status == "assigned" && task.TargetOrbitKeys.Length == 4 &&
task.TargetOrbitKeys.Distinct(StringComparer.Ordinal).Count() == 4 &&
task.TargetOrbitKeys.All(orbitKeys.Contains)) &&
agents.All(entity => zone.AgentTasks.Any(task => task.TaskId == entity.AssignedAgentTaskId));
}), "every generated NPC must begin with one assigned four-orbit patrol owned by daemon task state");
Require(first.Zones.Values.SelectMany(zone => zone.Entities)
.Where(entity => !string.IsNullOrWhiteSpace(entity.OrbitKey))
.All(entity => entity.Position.X != 0 || entity.Position.Z != 0),
"orbital stations and turrets must enter world truth at their resolved orbit position, not at the player origin");
var player = first.Zones[first.PlayerZoneIndex].Entities[first.PlayerEntityIndex];
Require(player.Position.X == 0 && player.Position.Z == 0,
"the controlled starter ship alone must retain the tutorial entrance origin");
Require(first.Zones.Values.All(zone =>
{
var orbitKeys = zone.Orbits.Select(orbit => orbit.OrbitKey).ToHashSet(StringComparer.Ordinal);
return zone.Entities.Where(entity => entity.Kind is "station" or "turret")
.All(entity => !string.IsNullOrWhiteSpace(entity.OrbitKey) && orbitKeys.Contains(entity.OrbitKey));
}), "every orbital entity must reference an orbit owned by its canonical zone document");
Require(first.Zones.Values.SelectMany(zone => zone.Entities)
.Where(entity => entity.Kind is "station" or "turret" or "ship")
.All(entity => entity.LoadoutGeneration != null &&
entity.LoadoutGeneration.SourceZoneIndex >= 0 &&
entity.LoadoutGeneration.Selections.Any(selection => selection.Role == "hull")),
"every generated entity must retain its daemon loadout provenance receipt");
Require(first.Zones.OrderBy(pair => pair.Key)
.Select(pair => string.Join('|', pair.Value.Entities.Select(entity =>
$"{entity.Kind}:{entity.FactionKey}:{entity.HullItemKey}:{entity.OrbitKey}")))
.SequenceEqual(second.Zones.OrderBy(pair => pair.Key)
.Select(pair => string.Join('|', pair.Value.Entities.Select(entity =>
$"{entity.Kind}:{entity.FactionKey}:{entity.HullItemKey}:{entity.OrbitKey}")))),
"fixed topology, catalog, and post-body random state must reproduce entity population exactly");
}
private static AetheriaRuntimeCatalogSnapshot TutorialPopulationCatalog(
out AetheriaDaemonTutorialFactionInput[] factions)
{
var one = new[] { new AetheriaRuntimeShapeCell(0, 0) };
var hullCells = Enumerable.Range(0, 6).SelectMany(y => Enumerable.Range(0, 6)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
// The recovered tutorial catalog includes allied manufacturers which do
// not own one of the six prelude home zones. Prelude availability must
// retain those authored items without inventing another faction home.
var manufacturer = "manufacturer.common";
AetheriaRuntimeCatalogItem Item(string key, string category, string hardpointType = "", params string[] behaviors)
{
var item = CatalogItem(key, behaviors.Select((kind, index) =>
new AetheriaRuntimeBehaviorPayload(index, kind, 0, Array.Empty<AetheriaRuntimeBehaviorField>())).ToArray());
item.Category = category;
item.ManufacturerKey = manufacturer;
item.Price = 10;
item.HardpointType = hardpointType;
item.ShapeWidth = 1;
item.ShapeHeight = 1;
item.OccupiedCells = 1;
item.ShapeCells = one;
item.BehaviorKinds = behaviors;
return item;
}
AetheriaRuntimeCatalogItem Hull(string key, string hullType, string controller)
{
var hull = Item(key, AetheriaRuntimeItemCategories.Hull, "Hull");
hull.HullType = hullType;
hull.ShapeWidth = 6;
hull.ShapeHeight = 6;
hull.OccupiedCells = hullCells.Length;
hull.ShapeCells = hullCells;
hull.Hardpoints =
[
new AetheriaRuntimeHardpoint("ControlModule", 0, 0, 1, 1, 1, one, "", "None", 0),
new AetheriaRuntimeHardpoint("Weapon", 1, 0, 1, 1, 1, one, "", "None", 0)
];
return hull;
}
var shipHull = Hull("tutorial-ship-hull", "Ship", "Cockpit");
var stationHull = Hull("tutorial-station-hull", "Station", "TurretController");
var cockpit = Item("tutorial-cockpit", AetheriaRuntimeItemCategories.Gear, "ControlModule", "Cockpit");
var turret = Item("tutorial-turret-controller", AetheriaRuntimeItemCategories.Gear, "ControlModule", "TurretController");
var weapon = Item("tutorial-weapon", AetheriaRuntimeItemCategories.Weapon, "Weapon", "LockWeapon");
var cargo = Item("tutorial-cargo", AetheriaRuntimeItemCategories.CargoBay, "Internal");
cargo.InteriorShapeWidth = 4;
cargo.InteriorShapeHeight = 4;
cargo.InteriorOccupiedCells = 16;
cargo.InteriorShapeCells = Enumerable.Range(0, 4).SelectMany(y => Enumerable.Range(0, 4)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
var docking = Item("tutorial-docking", AetheriaRuntimeItemCategories.DockingBay, "Internal");
var reactor = Item("tutorial-reactor", AetheriaRuntimeItemCategories.Gear, "Internal", "Reactor");
var capacitor = Item("tutorial-capacitor", AetheriaRuntimeItemCategories.Gear, "Internal", "Capacitor");
var names = new[] { "Miss", "Zhe", "Luc", "Aero", "Finch", "Adras" };
var tutorialCorporations = names.Select(name => new AetheriaRuntimeCorporation(
$"faction.{name.ToLowerInvariant()}", name, name, "", $"names.{name}", "", 4, 1,
[new AetheriaRuntimeCorporationAllegiance(manufacturer, 1)])).ToArray();
var corporations = tutorialCorporations.Append(new AetheriaRuntimeCorporation(
manufacturer, "Common Manufacturer", "Common", "", "", "", 0, 0,
Array.Empty<AetheriaRuntimeCorporationAllegiance>())).ToArray();
factions = names.Select((name, index) => new AetheriaDaemonTutorialFactionInput(
tutorialCorporations[index].CorporationKey, name, name, 4)
{
TrainingNames = Enumerable.Range(0, 128).Select(value => $"{name}{value:D3}").ToArray()
}).ToArray();
var nameFiles = names.Select(name => new AetheriaRuntimeNameFile(
$"names.{name}", name, 128,
Enumerable.Range(0, 8).Select(value => $"{name}{value:D3}").ToArray(),
Enumerable.Range(0, 128).Select(value => $"{name}{value:D3}").ToArray())).ToArray();
return new AetheriaRuntimeCatalogSnapshot(
[shipHull, stationHull, cockpit, turret, weapon, cargo, docking, reactor, capacitor],
corporations,
nameFiles);
}
private static void TutorialWorldMaterializesEveryTopologyZone()
{
AetheriaDaemonTutorialFactionInput Faction(string shortName) =>
new($"faction.{shortName.ToLowerInvariant()}", shortName, shortName, 2)
{
TrainingNames = Enumerable.Range(0, 512).Select(index =>
$"{(char)('a' + index % 26)}{(char)('a' + index / 26 % 26)}{(char)('a' + index / 676 % 26)}{shortName.ToLowerInvariant()}")
.ToArray()
};
var factions = new[]
{
Faction("Miss"), Faction("Zhe"), Faction("Luc"),
Faction("Aero"), Faction("Finch"), Faction("Adras")
};
var world = AetheriaDaemonTutorialWorldGenerator.Generate(factions, 0xA37E_2026u);
RequireEqual(world.Topology.Zones.Count, world.Zones.Count,
"the daemon tutorial world plan must materialize every topology zone exactly once");
Require(world.Topology.Zones.All(zone => world.Zones.ContainsKey(zone.ZoneIndex)),
"tutorial topology identities must be the sole keys of generated zone plans");
Require(world.Zones.Values.All(zone => zone.Orbits.Length > 0 && zone.Bodies.Length > 0),
"no selectable tutorial zone may be published as an empty celestial shell");
Require(world.Zones.Values.All(zone =>
Math.Abs(zone.Mass - zone.HierarchyMass) <= Math.Max(0.01, zone.Mass * 0.00001)),
"every tutorial zone must conserve its authored mass independently");
Require(world.Zones.Values.SelectMany(zone => zone.Bodies).Any(body => body.Kind == "asteroid_belt"),
"the complete tutorial world must retain fossil asteroid-belt generation");
Require(world.Zones.Values.SelectMany(zone => zone.Bodies).Count(body => body.Kind == "sun") >= world.Zones.Count,
"every generated tutorial zone must contain at least one stellar primary");
}
private static void GeneratedZoneBodiesPreserveFossilHierarchy()
{
var zone = new AetheriaDaemonTutorialZoneTopology(
7,
"Morrowind",
0.42f,
0.63f,
new[] { 3, 8 },
new[] { "faction.miss" },
"faction.miss");
var first = AetheriaDaemonZoneBodyGenerator.Generate(0xA37E_2026u, zone, 1.25f);
var second = AetheriaDaemonZoneBodyGenerator.Generate(0xA37E_2026u, zone, 1.25f);
RequireEqual(first.GenerationSeed, second.GenerationSeed,
"daemon zone generation must derive one stable seed from galaxy and zone identity");
RequireNear(first.Radius, second.Radius, 0,
"fixed zone inputs must reproduce the authored radius curve exactly");
RequireNear(first.Mass, first.HierarchyMass, Math.Max(0.01, first.Mass * 0.00001),
"planet hierarchy normalization must conserve the generated zone mass");
Require(first.Radius >= 1000 && first.Radius <= 10000 && first.Mass >= 10000 && first.Mass <= 500000,
"zone radius and mass must remain inside the authored exponential-lerp bounds");
Require(first.Orbits.Length >= first.Bodies.Length && first.Orbits.Length > 1,
"rosette placeholders may own orbits without bodies, but every generated zone must retain its hierarchy");
var orbitKeys = first.Orbits.Select(orbit => orbit.OrbitKey).ToHashSet(StringComparer.Ordinal);
Require(first.Orbits.All(orbit => orbit.ParentOrbitKey.Length == 0 || orbitKeys.Contains(orbit.ParentOrbitKey)) &&
first.Bodies.All(body => orbitKeys.Contains(body.OrbitKey)),
"every generated parent and body orbit reference must resolve inside the daemon plan");
Require(first.Bodies.All(body => body.GravityInfluenceRadius > 0 && body.GravityWellDepth > 0 &&
body.GravityWaveRadius > 0 && body.GravityWaveDepth > 0),
"generated body mass must project the authored positive gravity and wave curves for Ymir and Eve");
Require(first.Bodies.Select(body => (body.BodyKey, body.Kind, body.Mass, body.OrbitKey, body.Asteroids.Length))
.SequenceEqual(second.Bodies.Select(body => (body.BodyKey, body.Kind, body.Mass, body.OrbitKey, body.Asteroids.Length))),
"body kinds, masses, identities, orbits, and asteroid cardinality must be deterministic");
Require(first.Bodies.Any(body => body.Kind == "sun") && first.Bodies.Any(body => body.Kind != "sun"),
"the fossil solar hierarchy must produce both a stellar primary and subordinate bodies");
}
private static void TutorialTopologyIsDeterministicConnectedAndRoleOwned()
{
AetheriaDaemonTutorialFactionInput Faction(
string selector,
string name,
string shortName,
int influence = 2) =>
new($"faction.{selector.ToLowerInvariant()}", name, shortName, influence)
{
TrainingNames = Enumerable.Range(0, 512).Select(index =>
$"{(char)('a' + index % 26)}{(char)('a' + index / 26 % 26)}{(char)('a' + index / 676 % 26)}{selector.ToLowerInvariant()}")
.ToArray()
};
var factions = new[]
{
Faction("Miss", "Miss Terri's Sugariffic Snack Company", "Miss Terri's"),
Faction("Zhe", "Zhestokost", "Zhestokost"),
Faction("Luc", "Lucent Media", "Lucent"),
Faction("Aero", "Aeronautics Unlimited", "AU"),
Faction("Finch", "Finch Cybernetics", "Finch"),
Faction("Adras", "Adrasteia", "Adrasteia")
};
var first = AetheriaDaemonTutorialTopologyGenerator.GenerateFossil(factions, 0xA37E_2026u);
var second = AetheriaDaemonTutorialTopologyGenerator.GenerateFossil(factions, 0xA37E_2026u);
RequireEqual(64, first.Zones.Count,
"the daemon tutorial must retain the authored 64-zone topology size");
Require(first.Zones.All(zone => !string.IsNullOrWhiteSpace(zone.Name)) &&
first.Zones.Select(zone => zone.Name).Distinct(StringComparer.Ordinal).Count() == first.Zones.Count,
"daemon tutorial naming must produce one stable unique catalog name per zone");
Require(first.Zones.Select(zone => (zone.X, zone.Y, string.Join(',', zone.AdjacentZoneIndices)))
.SequenceEqual(second.Zones.Select(zone => (zone.X, zone.Y, string.Join(',', zone.AdjacentZoneIndices)))),
"tutorial topology must be deterministic for a fixed daemon seed and density field");
RequireNear(first.NoisePosition, second.NoisePosition, 0,
"the daemon seed must reproduce the authored tutorial cloud field position");
Require(AetheriaDaemonTutorialTopologyGenerator.TutorialCloudDensity(0.5f, 0.5f, first.NoisePosition) >= 0.5f,
"tutorial cloud selection must retain the fossil's dense-center admission gate");
RequireEqual(6, first.HomeZoneByFactionKey.Count,
"every authored tutorial faction role must receive one daemon-owned home zone");
Require(first.HomeZoneByFactionKey["faction.miss"] != first.HomeZoneByFactionKey["faction.zhe"],
"tutorial protagonist and antagonist homes must not collapse onto one zone");
Require(first.Zones[first.EntranceZoneIndex].OwnerFactionKey.Length == 0,
"the tutorial entrance must retain the fossil's unowned-zone rule");
Require(first.DiscoveredZoneIndices.SequenceEqual(
new[] { first.EntranceZoneIndex }
.Concat(first.Zones[first.EntranceZoneIndex].AdjacentZoneIndices)
.Distinct()
.OrderBy(value => value)),
"initial tutorial discovery must contain exactly the entrance and its adjacent zones");
Require(first.Zones.All(zone => zone.AdjacentZoneIndices.Count > 0) &&
first.Zones.All(zone => zone.AdjacentZoneIndices.All(neighbor =>
first.Zones[neighbor].AdjacentZoneIndices.Contains(zone.ZoneIndex))),
"tutorial link pruning must preserve one connected undirected graph");
var reachable = new HashSet<int> { first.EntranceZoneIndex };
var queue = new Queue<int>();
queue.Enqueue(first.EntranceZoneIndex);
while (queue.Count > 0)
{
foreach (var neighbor in first.Zones[queue.Dequeue()].AdjacentZoneIndices)
if (reachable.Add(neighbor)) queue.Enqueue(neighbor);
}
RequireEqual(first.Zones.Count, reachable.Count,
"tutorial link elimination must never strand a zone outside the playable graph");
}
private static void RetainedWorldAdvancesEveryFixedSubstep()
{
var ship = Entity(0, 0, "player");
ship.VelocityX = 10;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "retained-substep-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship] }]
};
using var physics = NewPhysics();
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-retained-substeps.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationStepCount = 3,
SimulationTimeSeconds = 0.3,
BuildPublications = false
});
RequireNear(3, ship.PositionX, 0.05,
"one retained Box3D world must advance once for every fixed substep, not once per publication frame");
}
private static void GeneratedOrbitsAdvanceOrbitalWorldTruth()
{
var orbital = Entity(0, 0, "neutral");
orbital.Kind = "station";
orbital.OrbitKey = "orbit.station";
orbital.PositionX = 10;
var orbit = new AetheriaRuntimeOrbitSnapshotCommit
{
OrbitKey = orbital.OrbitKey,
Distance = 10,
Phase = 0,
Period = 10
};
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [orbital],
Orbits = [orbit]
};
AetheriaRuntimeOrbitSimulation.StepZone(zone, [orbital], 2);
RequireNear(0.2, orbit.Phase, 0.000001,
"the canonical orbit phase must advance by fixed delta divided by authored period");
var expectedX = Math.Cos(0.2 * Math.PI * 2) * 10;
var expectedZ = Math.Sin(0.2 * Math.PI * 2) * 10;
RequireNear(expectedX, orbital.PositionX, 0.000001,
"orbital entity X must derive from the same advanced orbit observed by render queries");
RequireNear(expectedZ, orbital.PositionZ, 0.000001,
"orbital entity Z must derive from the same advanced orbit observed by render queries");
RequireNear((expectedX - 10) / 2, orbital.VelocityX, 0.000001,
"orbital X velocity must be the fixed-step pose delta consumed by Ymir");
RequireNear(expectedZ / 2, orbital.VelocityY, 0.000001,
"orbital Z velocity must be the fixed-step pose delta consumed by Ymir");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "dynamic-orbit-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [zone]
};
using var physics = NewPhysics();
AetheriaRuntimeDaemonSimulation.Step(
run,
new AetheriaRuntimeDaemonIntentState(),
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics,
frameId: 1,
simulationTimeSeconds: 2.1);
var integratedPhase = 0.21;
RequireNear(integratedPhase, orbit.Phase, 0.000001,
"the complete daemon step must advance the canonical orbit before Ymir");
RequireNear(Math.Cos(integratedPhase * Math.PI * 2) * 10, orbital.PositionX, 0.05,
"Ymir must retain the daemon's advanced orbital X pose instead of restoring a stale body transform");
RequireNear(Math.Sin(integratedPhase * Math.PI * 2) * 10, orbital.PositionZ, 0.05,
"Ymir must retain the daemon's advanced orbital Z pose instead of restoring a stale body transform");
}
private static void StableEntityIdentitySurvivesCrossZoneReindex()
{
var departing = Entity(0, 0, "player");
departing.EntityId = "cross-zone.departing";
var survivor = Entity(1, 100, "player");
survivor.EntityId = "cross-zone.survivor";
var source = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [departing, survivor]
};
var destination = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 1, Entities = [] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "cross-zone-retained-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [source, destination]
};
using var physics = NewPhysics();
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-cross-zone-before.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.1,
BuildPublications = false
});
survivor.EntityIndex = 0;
departing.EntityIndex = 0;
departing.PositionX = 250;
source.Entities = [survivor];
destination.Entities = [departing];
run.CurrentZoneIndex = 1;
run.CurrentEntityKey = "zone.1.entity.0";
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-cross-zone-after.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = 2,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.2,
BuildPublications = false
});
RequireNear(100, survivor.PositionX, 0.05,
"source-zone reindex must not give a survivor the departed entity's retained Box3D body");
RequireNear(250, departing.PositionX, 0.05,
"a cross-zone arrival must retain its stable identity while spawning in the destination session");
}
public void Run()
{
Action[] checks =
[
PositiveGravityDepthAttractsAndProjectsAsAWell,
TutorialTopologyIsDeterministicConnectedAndRoleOwned,
RegularTopologyOwnsFossilSectorStructure,
RegularRunPersistsPlayableWorldTruth,
GeneratedZoneBodiesPreserveFossilHierarchy,
TutorialWorldMaterializesEveryTopologyZone,
GeneratedOrbitsAdvanceOrbitalWorldTruth,
SectorMapSurfacePublishesDiscoveredGraphAndVisualLevers,
VolumeSurfaceKeepsNativeShaderAbiInAssetVariant,
YmirMovesProjectileAndReportsStableContact,
CompressedTerminusStopsAtAttentionBoundary,
InstantWeaponRequestSurvivesLockAcquisition,
AutoWeaponRetriggersWhileHeldAndStopsOnRelease,
GuidedWeaponPublishesPresentationProfileWithoutPhysicalAuthority,
CombatLockSurvivesPublicFrameRestart,
ConstantWeaponRunsOnDaemonThroughYmirBeamContact,
ChargedWeaponCannotBypassChargeLifecycle,
ChargedWeaponHoldRiskMalfunctionsDeterministically,
DeployableWeaponRunsThroughYmirAndDetonatesOnDaemon,
DeployableRangeExpiryDetonatesAfterYmirMovement,
CanonicalCatalogPublishesRecoveredMineLauncher,
EnergyFundedShieldInterceptsDamageBeforeHull,
ArmorCellsResolveBeforeEquipmentAndHull,
PenetrationStopsAtFirstHullGap,
ShipSchematicProjectsDaemonTopologyAndEquipmentState,
EquipmentDestructionDisablesBehaviorAndCockpitKillsBeforeHull,
DestructionDropsLootExactlyOnce,
DaemonSimulationTreatsYmirHitAsPresentationOnly,
ProjectileContactCannotKill,
MissingWorldPhysicsOwnerCannotAdvanceShips,
DockingUsesRealBaysAndFossilUndockRules,
DockedEntitiesCannotAttackOrBeTargeted,
RefitIsAtomicAndUsesTypedPlacement,
CargoTransferUsesSpatialAtomicTransaction,
DockedRefitSurfacePublishesGenericCommands,
FossilVelocityBehaviorsRunInDaemonFlightStep,
BehaviorChainsGateAuthoritativeStatModifiers,
ResourceBehaviorsStopEquipmentChainsInAuthoredOrder,
MiningAndScanningResultsGateTrailingBehaviorChains,
BehaviorStateProjectionIsReadStableAndReconcilesShapeChanges,
ThermotoggleSeedsAndGatesFromDaemonOwnedTarget,
InputCapabilityPublishesExactBehaviorLevers,
InputCapabilityPublishesConsumableActionBarState,
InputCapabilityPublishesHeldWeaponGroupLever,
InputCapabilityPublishesNativeTargetAndInteractControls,
ReactiveGameplayPublishesOnlyNewEventFacts,
ReflectorUsesSharedStellarLightFieldWithoutAccumulating,
SensorPingIsActorScopedEnergyGatedAndReconnectable,
TurretControllerAcquiresAimsAndTriggersExactWeapons,
TractorRampsAndPullsThroughYmirWithoutTeleportingCargo,
PickupIsCapacityCheckedExactlyOnceAndExpires,
TradePurchaseDerivesAcceptanceFromDaemonState,
TradeSaleIsDaemonPricedSpatialAndAtomic,
StationBodiesCannotConsumePickups,
PickupProximityCollectsOrBounces,
CultCacheRestartPreservesLoadoutAndBehaviorState,
PickupRemovalSurvivesRestartWithoutReplay,
ThermalCellsUseFossilConductionAndRadiation,
EnergyNetworkSettlesReactorAfterConsumers,
RadiatorPumpsHeatBeforeReactorSettlement,
EquipmentThermalPerformanceOwnsShutdownAndWear,
ThermalTopologyComesFromHullAndEquipment,
ThermalMedicalExposureUsesCockpitTemperature,
ThermalMedicalDeathUsesOrdinaryDestructionPath,
MultipleActorsUseTheSameMovementLever,
DirectionalThrustersOwnOrdinaryFlightFeedback,
UnpoweredThrustersCannotMoveOrAdvertisePlume,
SpecializedBehaviorSuccessGatesTrailingWear,
RareAetherDriveSpoolsOnlyOnModifiedHullFixture,
LookDirectionRejectsInvalidVectorsWithoutMutatingTheShip,
AgentClaimsAndCompletesExploreTaskThroughCommands,
AgentTaskSurvivesCultCacheRestart,
SchedulerAssignsHighestPriorityCompatibleTask,
SchedulerRequeuesTaskFromDeadAgent,
SchedulerCollapsesDuplicateAssignmentMarkers,
CancellingAgentTaskReleasesSchedulerControls,
SchedulerAssignsShortestGalaxyRoute,
WormholeTraversalUsesPersistedEnterTransferExitPhases,
AgentTraversesGalaxyRouteBeforeExecutingTask,
IdleAgentReturnsToCanonicalHomeAndDocks,
ControlledShipDoesNotReceiveAutonomousHelmCommands,
AttackAgentControlsOptimumRangeThroughMovementLever,
AttackTaskAdmissionRejectsImpossibleWork,
AgentCompletesAttackTaskThroughTargetFireAndYmir,
AgentCompletesHaulTaskThroughMovementAndCargoCommands,
RejectedHaulTransferDoesNotAdvanceTask,
AgentPatrolsHistoricalOrbitCircuitThroughMovementCommands,
TickReconcilesAndEvaluatesCatalogBehaviors,
DaemonLoadoutsRespectFactionAvailabilityAndHullRoles,
AgentMinesAsteroidThroughEquippedBehavior,
CargoCapacityComesFromHullAndCatalogVolumes,
AgentSurveysBodyIntoCorporationKnowledge,
AgentTowsStationIntoPersistentOrbit
];
foreach (var check in checks)
RunCheck(check);
}
private static void CompressedTerminusStopsAtAttentionBoundary()
{
var source = Entity(0, -100, "player");
var target = Entity(1, 0, "raider");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "terminus-attention-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [source, target],
PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "attention-mine",
PayloadKind = "mine",
WeaponItemKey = "attention-charge",
SourceEntityIndex = source.EntityIndex,
PositionX = target.PositionX,
PositionZ = target.PositionZ,
LifetimeSeconds = 30,
BlastRadius = 200,
PayloadMagnitude = 10,
DamageType = "Kinetic",
TriggeredAtSeconds = 0,
DetonationDelaySeconds = 0,
Stationary = true,
Active = true
}]
}]
};
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-terminus-attention-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
FrameId = 7,
SimulationTimeSeconds = 0.4,
FixedDeltaSeconds = 0.1,
AdvanceSimulation = true,
SimulationStepCount = 4,
StopCompressedSimulationOnAttention = true,
WorldPhysics = NewPhysics(),
BuildPublications = false
});
RequireEqual(1, tick.SimulationStepsExecuted,
"compressed Terminus must stop on the internal step that produces a decision event");
RequireNear(0.1, tick.Frame.SimulationTimeSeconds, 0.000001,
"authoritative frame time must stop at the attention boundary instead of claiming the requested batch horizon");
Require(tick.AttentionInterruption != null &&
tick.AttentionInterruption.CauseKind == "entity.damaged" &&
Math.Abs(tick.AttentionInterruption.SimulationTimeSeconds - 0.1) < 0.000001,
"tick result must retain the exact event and simulation time that interrupted compression");
RequireNear(90, Stat(target, "hull"), 0.000001,
"the causal damage step must commit before compressed simulation pauses");
var interruption = run.GameEvents.Single(value => value.Kind == "simulation.interrupted");
Require(interruption.Reason == "entity.damaged" && interruption.ScalarValue == 4 &&
interruption.AuxiliaryValue == 1,
"daemon must publish requested versus executed steps with the attention cause");
tick.Frame.GameMode = "terminus";
tick.Frame.RequestedSimulationRate = 4;
tick.Frame.EffectiveSimulationRate = 0;
tick.Frame.SimulationStepsExecuted = tick.SimulationStepsExecuted;
tick.Frame.AttentionCauseKind = tick.AttentionInterruption?.CauseKind ?? "";
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
tick.Frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var reactiveMount = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.reactive");
var reactiveSlot = reactiveMount.EmbeddedDocuments.Single();
Require(reactiveSlot.DocumentId == AetheriaRuntimeVerseRecordKeys.DaemonGameReactiveSurface.ToString() &&
reactiveSlot.SchemaId == EveSurfaceDocument.SchemaId &&
reactiveSlot.RouteHint.Kind == CultMeshLocalityKind.SharedMemory,
"the static pilot surface must advertise one exact, mapped-friendly reactive gameplay document");
Require(!Flatten(surface.Surface.Root).Any(node =>
node.Kind is "feedback.event" or "shot.receipt"),
"the static pilot surface must not retain mutable gameplay facts");
var reactive = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(tick.Frame);
Require(reactive.Version == interruption.FrameId,
"reactive Eve surfaces and command receipts must share daemon frame-version semantics");
Require(Flatten(reactive.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "simulation.interrupted" &&
node.Props["reason"] == "entity.damaged" &&
node.Props["scalarValue"] == "4" && node.Props["auxiliaryValue"] == "1"),
"generic Eve feedback must expose why compressed world time stopped");
var world = Flatten(surface.Surface.Root).Single(node => node.Kind == "world.scene3d");
Require(world.Props["gameMode"] == "terminus" &&
world.Props["requestedSimulationRate"] == "4" &&
world.Props["effectiveSimulationRate"] == "0" &&
world.Props["simulationStepsExecuted"] == "1" &&
world.Props["attentionCauseKind"] == "entity.damaged",
"generic Eve world state must expose requested and effective clock state without owning it");
var restoredFrame = MessagePack.MessagePackSerializer.Deserialize<AetheriaRuntimeDaemonFrameDocument>(
MessagePack.MessagePackSerializer.Serialize(tick.Frame));
Require(restoredFrame.RequestedSimulationRate == 4 && restoredFrame.EffectiveSimulationRate == 0 &&
restoredFrame.SimulationStepsExecuted == 1 && restoredFrame.AttentionCauseKind == "entity.damaged",
"clock interruption state must survive authoritative frame serialization");
}
private static void BehaviorChainsGateAuthoritativeStatModifiers()
{
var thruster = CatalogItem("modifier-target", new AetheriaRuntimeBehaviorPayload(
0, "Thruster", 0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(10))]));
var controls = CatalogItem("modifier-controls",
new AetheriaRuntimeBehaviorPayload(0, "Switch", 0, []),
new AetheriaRuntimeBehaviorPayload(1, "StatModifier", 0,
[
new AetheriaRuntimeBehaviorField(1, StatReference("ThrusterData", "Thrust")),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(3, StringValue("Multiplier"))
]),
new AetheriaRuntimeBehaviorPayload(2, "Trigger", 1, []),
new AetheriaRuntimeBehaviorPayload(3, "StatModifier", 1,
[
new AetheriaRuntimeBehaviorField(1, StatReference("Thruster", "Thrust")),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(3, StringValue("Constant"))
]));
var catalog = new AetheriaRuntimeCatalogSnapshot([thruster, controls], [], []);
var entity = Entity(0, 0, "player");
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = thruster.ItemKey, Enabled = true, Durability = 1, Quality = 1 } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = controls.ItemKey, Enabled = true, Durability = 1, Quality = 1 } }
];
var activate = new[]
{
new AetheriaRuntimeDaemonBehaviorIntent
{ ActorEntityKey = "zone.0.entity.0", EquipmentIndex = 1, BehaviorIndex = 0, Active = true },
new AetheriaRuntimeDaemonBehaviorIntent
{ ActorEntityKey = "zone.0.entity.0", EquipmentIndex = 1, BehaviorIndex = 2, Active = true }
};
AetheriaRuntimeBehaviorSimulation.Step(0, [entity],
[new AetheriaRuntimeDaemonBehaviorIntent
{ ActorEntityKey = "zone.1.entity.0", EquipmentIndex = 1, BehaviorIndex = 0, Active = true }],
catalog, 0.1);
var target = AetheriaRuntimeEquippedBehaviorQueries.FindOperational(entity, catalog, "Thruster").Single();
RequireNear(10, target.EvaluateStat(1), 0.000001,
"a behavior command for the same entity index in another zone must not cross-activate this ship");
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], activate, catalog, 0.1);
RequireNear(25, target.EvaluateStat(1), 0.000001,
"switch and one-shot trigger groups must multiply then add through the shared daemon stat path");
var modifierStates = entity.BehaviorStates.Where(value => value.BehaviorKind == "StatModifier").ToArray();
Require(modifierStates.All(value => value.StatModifierApplied && value.StatModifierTargetStatCount == 1),
"applied modifier state must name the exact live target count");
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
RequireNear(20, target.EvaluateStat(1), 0.000001,
"trigger modifiers must disappear on the next tick without another pull");
AetheriaRuntimeBehaviorSimulation.Step(0, [entity],
[new AetheriaRuntimeDaemonBehaviorIntent
{ ActorEntityKey = "zone.0.entity.0", EquipmentIndex = 1, BehaviorIndex = 0, Active = false }],
catalog, 0.1);
RequireNear(10, target.EvaluateStat(1), 0.000001,
"deactivating the switch must restore the unmodified authored stat exactly");
}
private static void ResourceBehaviorsStopEquipmentChainsInAuthoredOrder()
{
var targetItem = CatalogItem("resource-target", new AetheriaRuntimeBehaviorPayload(
0, "Thruster", 0, [new AetheriaRuntimeBehaviorField(1, PerformanceStat(10))]));
var controlItem = CatalogItem("resource-controls",
new AetheriaRuntimeBehaviorPayload(0, "Switch", 0, []),
new AetheriaRuntimeBehaviorPayload(1, "Cooldown", 0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(1))]),
new AetheriaRuntimeBehaviorPayload(2, "EnergyDraw", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false))
]),
new AetheriaRuntimeBehaviorPayload(3, "Heat", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false))
]),
new AetheriaRuntimeBehaviorPayload(4, "ItemUsage", 0,
[new AetheriaRuntimeBehaviorField(1, ItemKeyValue("resource-ammo"))]),
new AetheriaRuntimeBehaviorPayload(5, "Wear", 0,
[new AetheriaRuntimeBehaviorField(1, BoolValue(true))]),
new AetheriaRuntimeBehaviorPayload(6, "StatModifier", 0,
[
new AetheriaRuntimeBehaviorField(1, StatReference("Thruster", "Thrust")),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(3, StringValue("Multiplier"))
]));
var capacitorItem = CatalogItem("resource-capacitor", CapacitorPayload(10, 1));
var ammoItem = CatalogItem("resource-ammo");
var catalog = new AetheriaRuntimeCatalogSnapshot(
[targetItem, controlItem, capacitorItem, ammoItem], [], []);
var entity = Entity(0, 0, "player");
entity.Equipment =
[
Equipment(targetItem.ItemKey),
Equipment(controlItem.ItemKey),
Equipment(capacitorItem.ItemKey)
];
entity.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit
{
Items = [new AetheriaRuntimeLoadoutItemSlotCommit
{ Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = ammoItem.ItemKey, Quantity = 2 } }]
}];
AetheriaRuntimeThermalSimulation.EnsureTopology(entity, catalog);
AetheriaRuntimeThermalSimulation.EnsureState(entity);
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(entity, catalog, 0.1);
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, catalog);
AetheriaRuntimeEnergySimulation.BeginTick(entity, catalog);
var capacitor = AetheriaRuntimeEquippedBehaviorQueries.Find(entity, catalog, "Capacitor").Single();
capacitor.State.CapacitorCharge = 10;
var controlEquipmentState = entity.EquipmentStates.Single(value => value.EquipmentIndex == 1);
controlEquipmentState.Wear = 2;
var initialTemperature = AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 1);
var target = AetheriaRuntimeEquippedBehaviorQueries.FindOperational(entity, catalog, "Thruster").Single();
AetheriaRuntimeBehaviorSimulation.Step(0, [entity],
[new AetheriaRuntimeDaemonBehaviorIntent
{ ActorEntityKey = "zone.0.entity.0", EquipmentIndex = 1, BehaviorIndex = 0, Active = true }],
catalog, 0.1);
RequireNear(10, capacitor.State.CapacitorCharge, 0.000001,
"a newly constructed cooldown must stop its group before any resource transaction");
RequireEqual(2, CargoQuantity(entity, ammoItem.ItemKey),
"cooldown must stop item usage before cargo mutation");
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
RequireNear(5, capacitor.State.CapacitorCharge, 0.000001,
"energy draw must debit the daemon capacitor exactly once when the cooldown opens");
RequireEqual(1, CargoQuantity(entity, ammoItem.ItemKey),
"item usage must remove exactly one authored cargo item");
RequireNear(0.8, entity.Equipment[1].Item.Durability, 0.000001,
"wear must use the thermal owner's pressure only after preceding gates succeed");
Require(AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 1) > initialTemperature,
"heat must enter the source equipment's authoritative thermal cells");
RequireNear(20, target.EvaluateStat(1), 0.000001,
"downstream modifiers must apply only after every preceding resource gate succeeds");
RequireNear(0.9, entity.BehaviorProgress.Single(value =>
value.OwnerIndex == 1 && value.BehaviorKind == "Cooldown").Progress, 0.000001,
"cooldown state must project a generic Eve progress lever from the authoritative behavior row");
var cooldown = entity.BehaviorStates.Single(value =>
value.OwnerIndex == 1 && value.BehaviorKind == "Cooldown");
cooldown.CooldownProgress = -1;
capacitor.State.CapacitorCharge = 0;
var temperatureBeforeRefusal = AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 1);
var durabilityBeforeRefusal = entity.Equipment[1].Item.Durability;
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
RequireEqual(1, CargoQuantity(entity, ammoItem.ItemKey),
"an empty capacitor must stop the group before item usage");
RequireNear(durabilityBeforeRefusal, entity.Equipment[1].Item.Durability, 0.000001,
"an empty capacitor must stop the group before wear");
RequireNear(temperatureBeforeRefusal,
AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 1), 0.000001,
"an empty capacitor must stop the group before heat");
RequireNear(10, target.EvaluateStat(1), 0.000001,
"an empty capacitor must remove a downstream modifier instead of leaving stale application state");
static AetheriaRuntimeLoadoutItemSlotCommit Equipment(string itemKey) => new()
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = itemKey, Enabled = true, Durability = 1, Quality = 1 }
};
}
private static void MiningAndScanningResultsGateTrailingBehaviorChains()
{
var miningSuccess = MiningScenario(0, "mining-chain-success");
AetheriaRuntimeDaemonSimulation.Step(
miningSuccess.Run,
miningSuccess.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(),
miningSuccess.Catalog,
1,
0.1);
RequireEqual(0, CargoQuantity(miningSuccess.Entity, "mining-chain-charge"),
"a successful in-range MiningTool must resume its authored trailing ItemUsage exactly once");
var successfulMiningState = miningSuccess.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "MiningTool");
Require(successfulMiningState.ChainReached && successfulMiningState.ChainSucceeded &&
successfulMiningState.ChainCompleted,
"MiningTool success must be the explicit owner that completes its deferred behavior group");
var miningFailure = MiningScenario(100, "mining-chain-failure");
AetheriaRuntimeDaemonSimulation.Step(
miningFailure.Run,
miningFailure.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(),
miningFailure.Catalog,
1,
0.1);
RequireEqual(1, CargoQuantity(miningFailure.Entity, "mining-chain-charge"),
"an out-of-range MiningTool must stop before trailing cargo mutation");
Require(!miningFailure.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "MiningTool").ChainSucceeded &&
!miningFailure.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "ItemUsage").ChainReached,
"MiningTool failure must leave every trailing behavior unreachable");
var scanSuccess = ScannerScenario(0, "scanner-chain-success");
AetheriaRuntimeDaemonSimulation.Step(
scanSuccess.Run,
scanSuccess.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(),
scanSuccess.Catalog,
1,
0.1);
RequireEqual(0, CargoQuantity(scanSuccess.Entity, "scanner-chain-charge"),
"a successful in-range ResourceScanner must resume its authored trailing ItemUsage exactly once");
var successfulScannerState = scanSuccess.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "ResourceScanner");
Require(successfulScannerState.ChainReached && successfulScannerState.ChainSucceeded &&
successfulScannerState.ChainCompleted,
"ResourceScanner success must be the explicit owner that completes its deferred behavior group");
var scanFailure = ScannerScenario(100, "scanner-chain-failure");
AetheriaRuntimeDaemonSimulation.Step(
scanFailure.Run,
scanFailure.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(),
scanFailure.Catalog,
1,
0.1);
RequireEqual(1, CargoQuantity(scanFailure.Entity, "scanner-chain-charge"),
"an out-of-range ResourceScanner must stop before trailing cargo mutation");
Require(!scanFailure.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "ResourceScanner").ChainSucceeded &&
!scanFailure.Entity.BehaviorStates.Single(value =>
value.BehaviorKind == "ItemUsage").ChainReached,
"ResourceScanner failure must leave every trailing behavior unreachable");
static (AetheriaRuntimeRunCheckpointCommit Run,
AetheriaRuntimeDaemonIntentState Intents,
AetheriaRuntimeCatalogSnapshot Catalog,
AetheriaRuntimeEntitySnapshotCommit Entity) MiningScenario(double entityX, string runId)
{
var entity = Entity(0, entityX, "workers");
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "mining-chain-tool", Enabled = true, Durability = 1, Quality = 1 }
}];
entity.CargoContents = [Cargo(("mining-chain-charge", 1, 0, 0))];
var tool = CatalogItem("mining-chain-tool",
new AetheriaRuntimeBehaviorPayload(0, "MiningTool", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(10))
]),
new AetheriaRuntimeBehaviorPayload(1, "ItemUsage", 0,
[new AetheriaRuntimeBehaviorField(1, ItemKeyValue("mining-chain-charge"))]));
var catalog = new AetheriaRuntimeCatalogSnapshot(
[tool, CatalogItem("mining-chain-charge")], [], []);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = runId,
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [entity],
Bodies = [new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "mining-chain-belt",
Kind = "asteroid_belt",
Asteroids = [new AetheriaRuntimeAsteroidCommit { Distance = 0, Size = 6 }]
}]
}]
};
var intents = new AetheriaRuntimeDaemonIntentState();
intents.Behaviors.Add(new AetheriaRuntimeDaemonBehaviorIntent
{
ActorEntityKey = run.CurrentEntityKey,
EquipmentIndex = 0,
BehaviorIndex = 0,
Active = true,
TargetBodyKey = "mining-chain-belt",
TargetAsteroidIndex = 0
});
return (run, intents, catalog, entity);
}
static (AetheriaRuntimeRunCheckpointCommit Run,
AetheriaRuntimeDaemonIntentState Intents,
AetheriaRuntimeCatalogSnapshot Catalog,
AetheriaRuntimeEntitySnapshotCommit Entity) ScannerScenario(double entityX, string runId)
{
var entity = Entity(0, entityX, "workers");
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "scanner-chain-tool", Enabled = true, Durability = 1, Quality = 1 }
}];
entity.CargoContents = [Cargo(("scanner-chain-charge", 1, 0, 0))];
var scanner = CatalogItem("scanner-chain-tool",
new AetheriaRuntimeBehaviorPayload(0, "ResourceScanner", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(10))
]),
new AetheriaRuntimeBehaviorPayload(1, "ItemUsage", 0,
[new AetheriaRuntimeBehaviorField(1, ItemKeyValue("scanner-chain-charge"))]));
var catalog = new AetheriaRuntimeCatalogSnapshot(
[scanner, CatalogItem("scanner-chain-charge")], [], []);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = runId,
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [entity],
Orbits = [new AetheriaRuntimeOrbitSnapshotCommit
{ OrbitKey = "scanner-chain-orbit", FixedPositionX = 0, FixedPositionY = 0 }],
Bodies = [new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "scanner-chain-world",
Kind = "planet",
OrbitKey = "scanner-chain-orbit"
}]
}]
};
var intents = new AetheriaRuntimeDaemonIntentState();
intents.Behaviors.Add(new AetheriaRuntimeDaemonBehaviorIntent
{
ActorEntityKey = run.CurrentEntityKey,
EquipmentIndex = 0,
BehaviorIndex = 0,
Active = true,
TargetBodyKey = "scanner-chain-world",
TargetAsteroidIndex = -1
});
return (run, intents, catalog, entity);
}
}
private static void BehaviorStateProjectionIsReadStableAndReconcilesShapeChanges()
{
var thermostat = CatalogItem("projection-thermostat",
new AetheriaRuntimeBehaviorPayload(0, "Thermotoggle", 0,
[
new AetheriaRuntimeBehaviorField(1, Number(300))
]));
var catalog = new AetheriaRuntimeCatalogSnapshot([thermostat], [], []);
var entity = Entity(0, 0, "projection-player");
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = thermostat.ItemKey, Enabled = true, Durability = 1, Quality = 1 }
}];
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, catalog);
var originalStates = entity.BehaviorStates;
var originalThermostat = originalStates.Single();
originalThermostat.ThermotoggleTargetTemperature = 412;
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, catalog);
Require(ReferenceEquals(originalStates, entity.BehaviorStates),
"an unchanged equipment graph must remain a read and preserve the behavior-state array");
RequireNear(412, entity.BehaviorStates.Single().ThermotoggleTargetTemperature, 0.000001,
"an unchanged equipment graph must preserve daemon-owned mutable behavior state");
var expandedThermostat = CatalogItem("projection-thermostat",
new AetheriaRuntimeBehaviorPayload(0, "Thermotoggle", 0,
[
new AetheriaRuntimeBehaviorField(1, Number(300))
]),
new AetheriaRuntimeBehaviorPayload(1, "Radiator", 0, []));
var expandedCatalog = new AetheriaRuntimeCatalogSnapshot([expandedThermostat], [], []);
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, expandedCatalog);
Require(!ReferenceEquals(originalStates, entity.BehaviorStates),
"a changed catalog behavior shape must reconcile the projected behavior-state array");
RequireEqual(2, entity.BehaviorStates.Count,
"catalog reconciliation must add the newly authored behavior state exactly once");
Require(ReferenceEquals(originalThermostat,
entity.BehaviorStates.Single(state => state.BehaviorKind == "Thermotoggle")),
"catalog reconciliation must retain matching daemon-owned behavior state");
RequireNear(412,
entity.BehaviorStates.Single(state => state.BehaviorKind == "Thermotoggle")
.ThermotoggleTargetTemperature,
0.000001,
"catalog reconciliation must not reset retained mutable behavior state");
}
private static void ThermotoggleSeedsAndGatesFromDaemonOwnedTarget()
{
var thermostat = CatalogItem("thermostat",
new AetheriaRuntimeBehaviorPayload(0, "Thermotoggle", 0,
[
new AetheriaRuntimeBehaviorField(1, Number(300)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false)),
new AetheriaRuntimeBehaviorField(3, BoolValue(true))
]),
new AetheriaRuntimeBehaviorPayload(1, "Radiator", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(3, Number(200)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(10))
]),
new AetheriaRuntimeBehaviorPayload(2, "Heat", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false))
]),
new AetheriaRuntimeBehaviorPayload(3, "Wear", 0,
[new AetheriaRuntimeBehaviorField(1, BoolValue(true))]));
var catalog = new AetheriaRuntimeCatalogSnapshot([thermostat], [], []);
var entity = Entity(0, 0, "player");
entity.HeatsinksEnabled = true;
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = thermostat.ItemKey, Enabled = true, Durability = 1, Quality = 1 }
}];
AetheriaRuntimeThermalSimulation.EnsureTopology(entity, catalog);
AetheriaRuntimeThermalSimulation.EnsureState(entity);
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(entity, catalog, 0.1);
entity.EquipmentStates.Single().Wear = 1;
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, catalog);
var thermostatState = entity.BehaviorStates.Single(value => value.BehaviorKind == "Thermotoggle");
RequireNear(300, thermostatState.ThermotoggleTargetTemperature, 0.000001,
"new thermotoggle state must seed from the authored target instead of zero");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "thermotoggle-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
var context = new AetheriaRuntimeDaemonOperationContext { Catalog = catalog };
SetTarget(200, 1);
var before = AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 0);
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
AetheriaRuntimeEnergySimulation.StepRadiators(entity, catalog, 0.1);
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(entity, catalog, 0.1);
RequireNear(before, AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 0), 0.000001,
"a low-pass thermotoggle above its target must stop downstream heat");
Require(!entity.BehaviorStates.Single(value => value.BehaviorKind == "Heat").ChainReached,
"a closed thermostat must keep downstream specialized and common behaviors unreachable");
Require(!entity.BehaviorStates.Single(value => value.BehaviorKind == "Radiator").ChainSucceeded,
"a closed thermostat must keep the radiator Execute path from succeeding");
RequireNear(0, entity.BehaviorStates.Single(value => value.BehaviorKind == "Radiator").PumpedHeat, 0.000001,
"a closed thermostat must not refresh Execute-owned radiator throughput");
RequireNear(1, entity.Equipment[0].Item.Durability, 0.000001,
"a closed thermostat must not apply trailing wear");
SetTarget(300, 2);
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
AetheriaRuntimeEnergySimulation.StepRadiators(entity, catalog, 0.1);
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(entity, catalog, 0.1);
Require(AetheriaRuntimeThermalSimulation.EquipmentTemperature(entity, catalog, 0) > before,
"a low-pass thermotoggle below its target must admit downstream heat");
Require(entity.BehaviorStates.Single(value => value.BehaviorKind == "Heat").ChainReached,
"an open thermostat must publish that the downstream behavior was reached this tick");
RequireNear(1, entity.BehaviorStates.Single(value => value.BehaviorKind == "Radiator").PumpedHeat, 0.000001,
"an open thermostat must admit the radiator subsystem through the same chain state");
RequireNear(0.9, entity.Equipment[0].Item.Durability, 0.000001,
"a successful radiator must apply its authored trailing wear exactly once");
var radiatorPayload = thermostat.BehaviorPayloads.Single(value => value.Kind == "Radiator");
radiatorPayload.Fields = radiatorPayload.Fields.Select(field => field.Key == 5
? new AetheriaRuntimeBehaviorField(5, PerformanceStat(10))
: field).ToArray();
var durabilityBeforeEnergyRefusal = entity.Equipment[0].Item.Durability;
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 0.1);
AetheriaRuntimeEnergySimulation.StepRadiators(entity, catalog, 0.1);
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(entity, catalog, 0.1);
Require(!entity.BehaviorStates.Single(value => value.BehaviorKind == "Radiator").ChainSucceeded,
"an unfunded radiator must report its fossil Execute failure");
RequireNear(durabilityBeforeEnergyRefusal, entity.Equipment[0].Item.Durability, 0.000001,
"an unfunded radiator must stop the chain before trailing wear");
void SetTarget(double target, long sequence)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetThermotoggleTargetTemperature,
"smoke", "thermostat-session", sequence, "zone.0.entity.0");
command.TargetEntityKey = "zone.0.entity.0";
command.EquipmentIndex = 0;
command.BehaviorIndex = 0;
command.ScalarValue = target;
var result = AetheriaRuntimeDaemonOperations.Execute(run, [command], context);
RequireEqual(1, result.AppliedCommandIds.Count,
"an installed adjustable thermotoggle must accept its typed daemon command");
}
}
private static void InputCapabilityPublishesExactBehaviorLevers()
{
var controls = CatalogItem("behavior-controls",
new AetheriaRuntimeBehaviorPayload(0, "Thruster", 0, []),
new AetheriaRuntimeBehaviorPayload(1, "Switch", 1, []),
new AetheriaRuntimeBehaviorPayload(2, "Trigger", 2, []),
new AetheriaRuntimeBehaviorPayload(3, "Thermotoggle", 3,
[
new AetheriaRuntimeBehaviorField(1, Number(300)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false)),
new AetheriaRuntimeBehaviorField(3, BoolValue(true))
]),
new AetheriaRuntimeBehaviorPayload(4, "Thermotoggle", 4,
[
new AetheriaRuntimeBehaviorField(1, Number(350)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false)),
new AetheriaRuntimeBehaviorField(3, BoolValue(false))
]));
controls.Name = "Climate Control";
controls.ActionBarIcon = "Assets/Resources/Sprites/Icons/Stroked/Power.png";
var catalog = new AetheriaRuntimeCatalogSnapshot([controls], [], []);
var entity = Entity(0, 0, "player");
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = controls.ItemKey, Enabled = true, Durability = 1, Quality = 1 }
}];
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(entity, catalog);
entity.EquipmentStates = [new AetheriaRuntimeEquipmentStateCommit
{ EquipmentIndex = 0, Online = true }];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "behavior-input-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, entity.EntityIndex);
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "smoke", "behavior-input", 1, 0.1, 0.1);
var capability = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame, catalog: catalog);
RequireEqual("aetheria", capability.ProviderId,
"input capability must retain provider identity instead of substituting daemon service identity");
var actions = capability.Actions.Where(value => value.Category == "equipment").ToArray();
RequireEqual(3, actions.Length,
"input capability must omit non-interactive and fixed behaviors instead of activating behavior zero blindly");
var switchAction = actions.Single(value => value.ActionId == "equipment.0.behavior.1");
var triggerAction = actions.Single(value => value.ActionId == "equipment.0.behavior.2");
var thermostatAction = actions.Single(value => value.ActionId == "equipment.0.behavior.3");
RequireEqual("button-hold.v1", switchAction.InputValue?.Model ?? "",
"switch controls must advertise the fossil press/release input model");
RequireEqual("active", triggerAction.InputValue?.PayloadKey ?? "",
"trigger controls must route their input value into the typed active field");
RequireEqual("scalar.v1", thermostatAction.InputValue?.Model ?? "",
"adjustable thermostats must advertise a runtime-neutral scalar lever");
RequireNear(300, thermostatAction.InputValue?.CurrentValue ?? double.NaN, 0.000001,
"thermostat controls must publish the daemon-owned current target as typed scalar state");
RequireEqual("kelvin", thermostatAction.InputValue?.Unit ?? "",
"thermostat controls must publish the scalar unit without hiding it in command payload strings");
Require(actions.All(value => value.ActionBar && value.IconRef == "item.behavior-controls.icon"),
"interactive equipment behaviors must expose provider-icon action-bar suggestions");
var portableThermostat = capability.ToEveDocument().Actions
.Single(value => value.ActionId == thermostatAction.ActionId);
RequireNear(300, portableThermostat.InputValue?.CurrentValue ?? double.NaN, 0.000001,
"the portable Eve document must retain typed scalar current state");
Require(portableThermostat.ActionBar && portableThermostat.IconRef == "item.behavior-controls.icon",
"the portable Eve action must retain the provider action-bar suggestion and asset key");
var switchRequest = new EveSurfaceCommandRequest(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(switchAction.Operation),
CultMesh.OperationPayload(
("entityId", run.CurrentEntityKey),
("equipmentIndex", "0"),
("behaviorIndex", "1"),
("active", "1")),
DateTimeOffset.UtcNow,
"behavior-input-smoke");
var switchTranslated = AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
switchRequest, frame, ".", "behavior-input-smoke", "behavior-input", out var switchCommand);
Require(switchTranslated &&
switchCommand?.Kind == AetheriaRuntimeDaemonCommandKinds.SetBehaviorActive &&
switchCommand.EquipmentIndex == 0 && switchCommand.BehaviorIndex == 1 &&
switchCommand.ScalarValue > 0.5,
$"the generic Eve action must translate into the exact typed switch command; accepted={switchTranslated}, kind={switchCommand?.Kind}, equipment={switchCommand?.EquipmentIndex}, behavior={switchCommand?.BehaviorIndex}, scalar={switchCommand?.ScalarValue}");
var switchResult = AetheriaRuntimeDaemonOperations.Execute(
run, [switchCommand!], new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], switchResult.Intents.Behaviors, catalog, 0.1);
Require(entity.BehaviorStates.Single(value => value.BehaviorIndex == 1).SwitchActivated,
"the advertised switch lever must reach the daemon behavior owner");
var thermostatRequest = new EveSurfaceCommandRequest(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(thermostatAction.Operation),
CultMesh.OperationPayload(
("entityId", run.CurrentEntityKey),
("targetEntityKey", run.CurrentEntityKey),
("equipmentIndex", "0"),
("behaviorIndex", "3"),
("scalarValue", "425")),
DateTimeOffset.UtcNow,
"behavior-input-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
thermostatRequest, frame, ".", "behavior-input-smoke", "behavior-input", out var thermostatCommand) &&
thermostatCommand?.Kind == AetheriaRuntimeDaemonCommandKinds.SetThermotoggleTargetTemperature,
"the generic Eve scalar lever must translate into the typed thermostat command");
var thermostatResult = AetheriaRuntimeDaemonOperations.Execute(
run, [thermostatCommand!], new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
RequireEqual(1, thermostatResult.AppliedCommandIds.Count,
"the translated thermostat command must pass daemon catalog authorization");
RequireNear(425, entity.BehaviorStates.Single(value => value.BehaviorIndex == 3)
.ThermotoggleTargetTemperature, 0.000001,
"the advertised scalar lever must mutate only daemon-owned thermostat state");
entity.EquipmentStates[0].Online = false;
var offline = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame, catalog: catalog);
Require(offline.Actions.Where(value => value.Category == "equipment")
.All(value => value.Availability == "unavailable"),
"offline equipment must retain stable action identity while advertising current unavailability");
}
private static void InputCapabilityPublishesConsumableActionBarState()
{
var repairGel = CatalogItem("repair-gel");
repairGel.Name = "Repair Gel";
repairGel.Category = AetheriaRuntimeItemCategories.Consumable;
repairGel.Duration = 4;
repairGel.Stackable = false;
repairGel.ActionBarIcon = "Assets/Resources/Sprites/Icons/Stroked/Repair.png";
var ore = CatalogItem("ore");
ore.Category = AetheriaRuntimeItemCategories.SimpleCommodity;
var catalog = new AetheriaRuntimeCatalogSnapshot([repairGel, ore], [], []);
var entity = Entity(0, 0, "player");
entity.CargoContents =
[
new AetheriaRuntimeCargoBayLoadoutCommit
{
Items =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "repair-gel", Quantity = 3 }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "ore", Quantity = 7 }
}
]
}
];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "consumable-input-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, entity.EntityIndex);
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "smoke", "consumable-input", 1, 0.1, 0.1);
var capability = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame, catalog: catalog);
var action = capability.Actions.Single(value => value.Category == "consumable");
RequireEqual("3", action.Payload["quantityRemaining"],
"consumable action bars must derive quantity from daemon cargo truth");
RequireEqual("0", action.Payload["fillValue"],
"inactive consumables must advertise an empty remaining-duration fill");
RequireEqual("remaining-ratio.v1", action.Payload["fillModel"],
"generic lowerers must receive the fossil action-bar fill semantics explicitly");
RequireEqual("available", action.Availability,
"a valid consumable in cargo must advertise current command availability");
Require(action.ActionBar && action.IconRef == "item.repair-gel.icon",
"consumables must suggest an action-bar lever using their provider-owned catalog icon");
Require(!capability.Actions.Any(value => value.Category == "consumable" && value.Payload["itemKey"] == "ore"),
"non-consumable cargo must not become an action-bar command");
entity.CargoContents = Array.Empty<AetheriaRuntimeCargoBayLoadoutCommit>();
entity.ActiveConsumables =
[
new AetheriaRuntimeActiveConsumableCommit
{
ItemKey = "repair-gel",
Duration = 4,
RemainingDuration = 1
}
];
var activeAction = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame, catalog: catalog)
.ToEveDocument().Actions.Single(value => value.Category == "consumable");
RequireEqual("0", activeAction.Payload["quantityRemaining"],
"the action must survive depletion while its daemon-owned effect remains active");
RequireEqual("1", activeAction.Payload["activeEffectCount"],
"the portable Eve action must expose daemon-owned active effect state");
RequireEqual("1", activeAction.Payload["remainingDurationSeconds"],
"generic clients must receive exact remaining duration without querying a Unity effect object");
RequireEqual("4", activeAction.Payload["durationSeconds"],
"generic clients must receive exact authored consumable duration");
RequireEqual("0.25", activeAction.Payload["fillValue"],
"remaining duration must lower to the fossil action-bar ratio exactly");
RequireEqual("unavailable", activeAction.Availability,
"a depleted non-stackable active consumable must remain visible but reject activation");
}
private static void InputCapabilityPublishesHeldWeaponGroupLever()
{
var entity = Entity(0, 0, "player");
entity.WeaponGroups = [new[] { 0 }];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "weapon-input-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, entity.EntityIndex);
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "smoke", "weapon-input", 1, 0.1, 0.1);
var capability = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame);
var action = capability.Actions.Single(value => value.ActionId == "weapon-group.0.fire");
RequireEqual("aetheria.daemon.commands.SetWeaponGroupActive", action.Operation,
"weapon input must advertise the daemon's press/release owner instead of the one-shot compatibility command");
RequireEqual("button-hold.v1", action.InputValue?.Model ?? "",
"generic Eve clients must know that weapon firing is a held control");
RequireEqual("active", action.InputValue?.PayloadKey ?? "",
"weapon press/release values must route into the typed active field");
Require(action.ActionBar,
"weapon groups must remain suggested action-bar levers without making the client own firing");
AetheriaRuntimeDaemonCommandDocument Translate(string active)
{
var request = new EveSurfaceCommandRequest(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(action.Operation),
CultMesh.OperationPayload(
("entityId", run.CurrentEntityKey),
("weaponGroup", "0"),
("active", active)),
DateTimeOffset.UtcNow,
"weapon-input-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
request, frame, ".", "weapon-input-smoke", "weapon-input", out var command) &&
command?.Kind == AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
$"generic weapon input value {active} must translate into the typed held-fire command");
return command!;
}
var pressed = AetheriaRuntimeDaemonOperations.Execute(
run, [Translate("1")], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, pressed.Intents.WeaponGroups.Count,
"weapon press must reach the daemon intent owner exactly once");
Require(pressed.Intents.WeaponGroups[0].Active,
"weapon press must activate the exact advertised group");
Require(entity.ActiveWeaponGroups.SequenceEqual(new[] { true }),
"weapon press must persist the daemon-owned held state beyond its command frame");
var heldCapability = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame);
RequireEqual("1", heldCapability.Actions.Single(value => value.ActionId == "weapon-group.0.fire")
.Payload["currentValue"],
"the Eve lever must derive its current value from the daemon-owned held state");
var restartedRun = MessagePack.MessagePackSerializer.Deserialize<AetheriaRuntimeRunCheckpointCommit>(
MessagePack.MessagePackSerializer.Serialize(run));
var restartedFrame = AetheriaRuntimeDaemonFrameDocument.Create(
restartedRun, "smoke", "weapon-input-restart", 2, 0.2, 0.1);
Require(restartedRun.Zones[0].Entities[0].ActiveWeaponGroups.SequenceEqual(new[] { true }) &&
AetheriaRuntimeInputCapabilityDocument.FromFrame(restartedFrame).Actions
.Single(value => value.ActionId == "weapon-group.0.fire").Payload["currentValue"] == "1",
"daemon restart must retain held weapon-group truth and re-advertise it through Eve");
var released = AetheriaRuntimeDaemonOperations.Execute(
run, [Translate("0")], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, released.Intents.WeaponGroups.Count,
"weapon release must reach the daemon intent owner exactly once");
Require(!released.Intents.WeaponGroups[0].Active,
"weapon release must deactivate the exact advertised group");
Require(entity.ActiveWeaponGroups.SequenceEqual(new[] { false }),
"weapon release must clear the daemon-owned held state");
}
private static void InputCapabilityPublishesNativeTargetAndInteractControls()
{
var actor = Entity(0, 0, "player");
actor.VelocityX = 12;
actor.VelocityY = -7;
actor.HelmStrafe = 0.5;
actor.HelmForward = 1;
var near = Entity(1, 0, "enemy");
near.PositionX = 4;
var middle = Entity(2, 0, "enemy");
middle.PositionX = 8;
var far = Entity(3, 0, "enemy");
far.PositionZ = 12;
var hidden = Entity(4, 0, "enemy");
hidden.PositionX = 2;
actor.Contacts =
[
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = near.EntityIndex, Visible = true, Hostile = true },
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = middle.EntityIndex, Visible = true, Hostile = true },
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = far.EntityIndex, Visible = true, Hostile = true }
];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "native-input-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [actor, near, middle, far, hidden] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, actor.EntityIndex);
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "smoke", "native-input", 1, 0.1, 0.1);
var capability = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame);
var targeting = capability.Actions.Where(value => value.Category == "targeting").ToArray();
Require(targeting.Select(value => value.ActionId).ToHashSet(StringComparer.Ordinal).SetEquals(
["pilot.target-nearest", "pilot.target-previous", "pilot.target-next", "pilot.target-reticle", "pilot.target-clear"]),
"portable input must advertise every daemon target-selection lever");
Require(targeting.Where(value => value.ActionId != "pilot.target-clear")
.All(value => value.Availability == "available" &&
value.Payload["visibleHostileCount"] == "3" &&
value.Payload["currentTargetEntityIndex"] == "-1") &&
targeting.Single(value => value.ActionId == "pilot.target-clear").Availability == "unavailable",
"target controls must derive availability and current selection from daemon contact truth");
var keyboard = capability.DefaultProfiles.Single(value => value.DeviceClass == "keyboard-mouse");
string Control(string actionId) => keyboard.Bindings.Single(value => value.ActionId == actionId).Gesture.Controls.Single();
RequireEqual("keyboard.f", Control("pilot.interact"),
"the generic default profile must preserve the fossil interaction key");
RequireEqual("keyboard.t", Control("pilot.target-nearest"),
"the generic default profile must preserve the fossil nearest-target key");
RequireEqual("keyboard.y", Control("pilot.target-previous"),
"the generic default profile must preserve the fossil previous-target key");
RequireEqual("keyboard.u", Control("pilot.target-next"),
"the generic default profile must preserve the fossil next-target key");
RequireEqual("keyboard.r", Control("pilot.target-reticle"),
"the generic default profile must preserve the fossil reticle-target key");
Require(keyboard.Bindings.Where(value => value.ActionId == "pilot.interact" ||
value.ActionId.StartsWith("pilot.target-", StringComparison.Ordinal))
.All(value => !value.ActionBar),
"ordinary fossil shortcuts must not masquerade as action-bar slot suggestions");
var reticleAction = capability.ToEveDocument().Actions.Single(value => value.ActionId == "pilot.target-reticle");
RequireEqual("view-direction.v1", reticleAction.InputValue?.Model ?? "",
"reticle targeting must advertise the generic active-view direction model");
Require((reticleAction.InputValue?.PayloadKeys ?? Array.Empty<string>()).SequenceEqual(
["directionX", "directionY", "directionZ"]),
"reticle targeting must preserve the portable world-direction payload order");
AetheriaRuntimeDaemonCommandDocument Translate(AetheriaRuntimeInputActionDocument action)
{
var request = new EveSurfaceCommandRequest(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(action.Operation),
CultMesh.OperationPayload(("entityId", run.CurrentEntityKey)),
DateTimeOffset.UtcNow,
"native-input-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
request, frame, ".", "native-input-smoke", "native-input", out var command),
$"advertised action {action.ActionId} must translate through the generic Eve command path");
return command!;
}
var previousCommand = Translate(targeting.Single(value => value.ActionId == "pilot.target-previous"));
var previousResult = AetheriaRuntimeDaemonOperations.Execute(
run, [previousCommand], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, previousResult.AppliedCommandIds.Count,
"the advertised previous-target action must reach the daemon owner");
RequireEqual(middle.EntityIndex, actor.TargetEntityIndex,
"previous from no selection must preserve the fossil's second-to-last index arithmetic");
var clearCommand = Translate(targeting.Single(value => value.ActionId == "pilot.target-clear"));
var clearResult = AetheriaRuntimeDaemonOperations.Execute(
run, [clearCommand], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, clearResult.AppliedCommandIds.Count,
"the generic clear-target action must return selection ownership to the daemon");
var nearestCommand = Translate(targeting.Single(value => value.ActionId == "pilot.target-nearest"));
RequireEqual(AetheriaRuntimeDaemonCommandKinds.TargetNearest, nearestCommand.Kind,
"nearest target action must preserve daemon command identity");
var result = AetheriaRuntimeDaemonOperations.Execute(
run, [nearestCommand], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, result.AppliedCommandIds.Count,
"the advertised target action must reach the daemon owner");
RequireEqual(far.EntityIndex, actor.TargetEntityIndex,
"the historically misnamed nearest action must preserve the fossil's MaxBy-distance behavior");
var selected = AetheriaRuntimeInputCapabilityDocument.FromFrame(frame);
Require(selected.Actions.Single(value => value.ActionId == "pilot.target-clear").Availability == "available" &&
selected.Actions.Where(value => value.Category == "targeting")
.All(value => value.Payload["currentTargetEntityIndex"] == far.EntityIndex.ToString()),
"all portable target levers must refresh from the newly selected daemon target");
var clearAfterNearest = AetheriaRuntimeDaemonOperations.Execute(
run, [Translate(selected.Actions.Single(value => value.ActionId == "pilot.target-clear"))],
new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, clearAfterNearest.AppliedCommandIds.Count,
"generic Eve must clear the fossil-selected target before the independent reticle selection proof");
var reticleRequest = new EveSurfaceCommandRequest(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(reticleAction.Operation),
CultMesh.OperationPayload(
("entityId", run.CurrentEntityKey),
("directionX", "0"),
("directionY", "0"),
("directionZ", "1")),
DateTimeOffset.UtcNow,
"native-input-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
reticleRequest, frame, ".", "native-input-smoke", "native-input", out var reticleCommand) &&
reticleCommand?.Kind == AetheriaRuntimeDaemonCommandKinds.TargetReticle,
"the generic Eve view-direction action must translate into the typed reticle command");
var reticleResult = AetheriaRuntimeDaemonOperations.Execute(
run, [reticleCommand!], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(1, reticleResult.AppliedCommandIds.Count,
"the translated reticle action must reach the daemon target owner");
RequireEqual(far.EntityIndex, actor.TargetEntityIndex,
"reticle selection must rank visible hostile contacts from the advertised view ray inside the daemon");
EveSurfaceCommandRequest ExplicitTarget(string targetEntityKey) => new(
capability.ProviderId,
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(
AetheriaRuntimeDaemonCommandKinds.SetTarget)),
CultMesh.OperationPayload(
("sourceEntityId", run.CurrentEntityKey),
("targetEntityId", targetEntityKey)),
DateTimeOffset.UtcNow,
"native-input-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
ExplicitTarget(run.EntityRecordKey(0, near.EntityIndex)),
frame,
".",
"native-input-smoke",
"native-input",
out var explicitTarget) &&
explicitTarget?.ActorEntityKey == run.CurrentEntityKey &&
explicitTarget.TargetEntityKey == run.EntityRecordKey(0, near.EntityIndex),
"generic Eve explicit targeting must preserve both source and target entity identities");
var explicitResult = AetheriaRuntimeDaemonOperations.Execute(
run, [explicitTarget!], new AetheriaRuntimeDaemonOperationContext());
Require(explicitResult.AppliedCommandIds.Contains(explicitTarget!.CommandId) &&
actor.TargetEntityIndex == near.EntityIndex,
"a generic scene click must select the visible entity through daemon contact truth");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
ExplicitTarget(run.EntityRecordKey(0, hidden.EntityIndex)),
frame,
".",
"native-input-smoke",
"native-input",
out var hiddenTarget),
"generic Eve must translate an explicit target attempt without deciding visibility client-side");
var hiddenResult = AetheriaRuntimeDaemonOperations.Execute(
run, [hiddenTarget!], new AetheriaRuntimeDaemonOperationContext());
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.TargetNotVisible,
hiddenResult.RejectedCommandReasons[hiddenTarget!.CommandId],
"the daemon must reject a generic explicit target absent from authoritative visible contacts");
RequireEqual(near.EntityIndex, actor.TargetEntityIndex,
"a rejected explicit target must not disturb the prior valid selection");
var rejectedFrame = AetheriaRuntimeDaemonFrameDocument.Create(
run, "smoke", "native-input", 2, 0.2, 0.1);
rejectedFrame.RejectedCommandIds = hiddenResult.RejectedCommandIds;
rejectedFrame.RejectedCommandReasons = hiddenResult.RejectedCommandReasons;
var targetReceipt = AetheriaRuntimeDaemonReceiptProjector.Project(
AetheriaRuntimeCommittedCommandFactDocument.FromRejectedCommand(
rejectedFrame, hiddenTarget!, "aetheria.local"),
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId);
Require(targetReceipt.State == "denied" &&
targetReceipt.Message.Contains(
AetheriaRuntimeDaemonRejectionReasons.TargetNotVisible,
StringComparison.Ordinal),
"generic Eve must receive the daemon's exact explicit-target rejection reason");
var interact = capability.Actions.Single(value => value.ActionId == "pilot.interact");
RequireEqual(AetheriaRuntimeDaemonCommandKinds.Interact, Translate(interact).Kind,
"the fossil interaction binding must reach the shared wormhole-first/then-dock daemon command");
}
private static void DockedRefitSurfacePublishesGenericCommands()
{
var hull = HullCatalogItem("surface-hull", 9, 7, 100);
var cargoBay = CatalogItem("surface-cargo-bay");
cargoBay.InteriorShapeWidth = 6;
cargoBay.InteriorShapeHeight = 4;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, cargoBay, CatalogItem("ore")], [], []);
var ship = Entity(0, 0, "player");
ship.Name = "Pilot";
ship.HullItemKey = hull.ItemKey;
ship.CargoBays = [ItemSlot(cargoBay.ItemKey)];
ship.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit { Items = [ItemSlot("ore", 2, 3)] }];
var station = Entity(1, 0, "player");
station.Kind = "station";
station.Name = "Station";
station.DockingBayAssignments = [ship.EntityIndex];
station.CargoBays = [ItemSlot(cargoBay.ItemKey)];
station.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit()];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "generic-refit-surface-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship, station] }]
};
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "smoke", "surface", 1, 0.02, 0.02);
var shipKey = run.EntityRecordKey(0, ship.EntityIndex);
var stationKey = run.EntityRecordKey(0, station.EntityIndex);
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("smoke"),
catalog: catalog);
var equipment = Find(surface.Surface.Root, "aetheria.daemon.game.refit.ship.equipment");
var shipCargo = Find(surface.Surface.Root, "aetheria.daemon.game.refit.ship.cargo.0");
var stationCargo = Find(surface.Surface.Root, "aetheria.daemon.game.refit.station.cargo.0");
Require(equipment != null && equipment.Kind == "inventory.grid" &&
equipment.Props["columns"] == "9" && equipment.Props["rows"] == "7",
"docked pilot surface must publish authored ship equipment geometry");
Require(shipCargo != null && stationCargo != null &&
shipCargo.Props["targetEntityKey"] == shipKey &&
stationCargo.Props["targetEntityKey"] == stationKey,
"docked pilot surface must publish both ship and station cargo targets");
Require(surface.Commands.Any(value => value.Command ==
AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(AetheriaRuntimeDaemonCommandKinds.TransferCargoItem)) &&
surface.Commands.Any(value => value.Command ==
AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(AetheriaRuntimeDaemonCommandKinds.EquipItem)) &&
surface.Commands.Any(value => value.Command ==
AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(AetheriaRuntimeDaemonCommandKinds.StoreItem)),
"docked pilot surface must advertise all atomic refit operations");
var request = new EveSurfaceCommandRequest(
"aetheria",
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(
AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(AetheriaRuntimeDaemonCommandKinds.TransferCargoItem)),
CultMesh.OperationPayload(
("entityId", shipKey),
("originEntityKey", shipKey),
("originCargoIndex", "0"),
("destinationEntityKey", stationKey),
("destinationCargoIndex", "0"),
("itemKey", "ore"),
("quantity", "1"),
("sourceX", "2"),
("sourceY", "3"),
("destinationX", "0"),
("destinationY", "0"),
("hasDestinationPosition", "true")),
DateTimeOffset.UtcNow,
"generic-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
request, frame, ".", "generic-smoke", "surface", out var translated) &&
translated?.CargoTransfer.OriginCargoIndex == 0 &&
translated.CargoTransfer.DestinationEntityKey == stationKey &&
translated.CargoTransfer.HasDestinationPosition,
"generic Eve inventory drop must translate into the exact typed daemon cargo command");
var unauthorized = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TransferCargoItem,
"generic-smoke", "surface", 1, shipKey);
unauthorized.TextValue = "ore";
unauthorized.CargoTransfer.OriginEntityKey = stationKey;
unauthorized.CargoTransfer.DestinationEntityKey = stationKey;
unauthorized.CargoTransfer.OriginCargoIndex = 0;
unauthorized.CargoTransfer.DestinationCargoIndex = 0;
var rejected = AetheriaRuntimeDaemonOperations.Execute(run, [unauthorized],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(rejected.RejectedCommandReasons[unauthorized.CommandId] ==
AetheriaRuntimeDaemonRejectionReasons.CargoAccessDenied,
"cargo transfer actor must own one endpoint even when the target is physically nearby");
station.DockingBayAssignments = [];
var undocked = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("smoke"),
catalog: catalog);
Require(Find(undocked.Surface.Root, "aetheria.daemon.game.refit") == null,
"undocked pilot surfaces must not expose station refit controls");
static AetheriaRuntimeSurfaceComponent? Find(AetheriaRuntimeSurfaceComponent component, string id) =>
component.Id == id
? component
: component.Children.Select(child => Find(child, id)).FirstOrDefault(value => value != null);
static AetheriaRuntimeLoadoutItemSlotCommit ItemSlot(string itemKey, int x = 0, int y = 0) => new()
{
X = x,
Y = y,
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = itemKey,
Quantity = 1,
Quality = 1,
Durability = 1,
Enabled = true
}
};
}
private static void FossilVelocityBehaviorsRunInDaemonFlightStep()
{
var conversion = new AetheriaRuntimeBehaviorPayload(
0,
AetheriaRuntimeBehaviorKinds.VelocityConversion,
0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(Math.Log(2)))]);
var limit = new AetheriaRuntimeBehaviorPayload(
1,
AetheriaRuntimeBehaviorKinds.VelocityLimit,
0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(4))]);
var hull = HullCatalogItem("velocity-hull", 1, 1, 100);
var controller = CatalogItem("velocity-controller", conversion, limit);
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, controller], [], []);
var ship = Entity(0, 0, "player");
ship.HullItemKey = hull.ItemKey;
ship.DirectionX = 1;
ship.DirectionY = 0;
ship.VelocityX = 0;
ship.VelocityY = 10;
ship.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = controller.ItemKey,
Quantity = 1,
Quality = 1,
Durability = 1,
Enabled = true
}
}
];
AetheriaRuntimeBehaviorStateProjector.EnsureEquipmentBehaviorStates(ship, catalog);
AetheriaRuntimeFlightSimulation.Step([ship], [], catalog, 1, 1, 1, 0, 1);
RequireNear(4, Math.Sqrt(ship.VelocityX * ship.VelocityX + ship.VelocityY * ship.VelocityY), 0.000001,
"VelocityLimit must clamp the post-conversion speed in the authoritative flight step");
RequireNear(ship.VelocityX, ship.VelocityY, 0.000001,
"VelocityConversion must exponentially align velocity toward the ship direction before limiting");
var limitState = ship.BehaviorStates.Single(state =>
state.BehaviorKind == AetheriaRuntimeBehaviorKinds.VelocityLimit);
RequireNear(4, limitState.VelocityLimit, 0.000001,
"VelocityLimit must publish its evaluated runtime value for Eve and reconnect");
}
private static void ReflectorUsesSharedStellarLightFieldWithoutAccumulating()
{
var reflector = new AetheriaRuntimeBehaviorPayload(
0,
AetheriaRuntimeBehaviorKinds.Reflector,
0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(2))]);
var constantVisibility = new AetheriaRuntimeBehaviorPayload(
1,
AetheriaRuntimeBehaviorKinds.Visibility,
0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(5))]);
var controller = CatalogItem("reflector-controller", reflector, constantVisibility);
var catalog = new AetheriaRuntimeCatalogSnapshot([controller], [], []);
var ship = Entity(0, 250, "player");
ship.Visibility = 3;
ship.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = controller.ItemKey,
Quantity = 1,
Quality = 1,
Durability = 1,
Enabled = true
}
}
];
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [ship],
Orbits =
[
new AetheriaRuntimeOrbitSnapshotCommit
{
OrbitKey = "sun-orbit",
FixedPositionX = 100
}
],
Bodies =
[
new AetheriaRuntimeBodySnapshotCommit
{
Kind = "sun",
Mass = 16,
OrbitKey = "sun-orbit",
SunVisual = new AetheriaRuntimeSunVisualCommit { LightRadiusMultiplier = 1 }
}
]
};
var settings = AetheriaRuntimeDaemonRenderSettings.AetheriaDefault;
var radius = settings.ResolveLightRadius(16);
var pulseX = Math.Min(1, 150 / radius * 2);
var expectedReflection = 2 * Math.Pow((pulseX + 1) * (1 - pulseX), 8);
AetheriaRuntimeVisibilitySimulation.StepZone(zone, [ship], catalog, settings);
AetheriaRuntimeVisibilitySimulation.StepZone(zone, [ship], catalog, settings);
RequireNear(3 + 5 + expectedReflection, ship.Visibility, 0.000001,
"Visibility and Reflector must publish their fossil sources without accumulating per tick");
var equipmentSource = ship.StatGrids.Single(grid => grid.Name == "equipment-visibility");
RequireNear(5, equipmentSource.Values.Single(), 0.000001,
"Operational Visibility equipment must publish its evaluated signature as an inspectable source");
var source = ship.StatGrids.Single(grid => grid.Name == "reflector-visibility");
RequireNear(expectedReflection, source.Values.Single(), 0.000001,
"Reflector visibility must remain an inspectable daemon-owned source for Eve and reconnect");
}
private static void SensorPingIsActorScopedEnergyGatedAndReconnectable()
{
var sensorItem = CatalogItem("sensor-ping-array", SensorPayload(
sensitivity: 0.01,
pingBoost: 1,
pingEnergy: 5,
pingVisibility: 4,
pingRange: 200,
pingCooldown: 1,
pingDuration: 1,
pingRadiusExponent: 1));
var capacitorItem = CatalogItem("sensor-ping-capacitor", CapacitorPayload(10, 1));
var catalog = new AetheriaRuntimeCatalogSnapshot([sensorItem, capacitorItem], [], []);
static AetheriaRuntimeEntitySnapshotCommit Observer(int index, string sensorKey, string capacitorKey)
{
var entity = Entity(index, 0, "player");
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = sensorKey, Quality = 1, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = capacitorKey, Quality = 1, Durability = 1, Enabled = true } }
];
entity.BehaviorStates =
[
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 1,
BehaviorIndex = 0,
BehaviorKind = "Capacitor",
CapacitorCharge = 10,
CapacitorCapacity = 10,
CapacitorEfficiency = 1
}
];
return entity;
}
var actor = Observer(0, sensorItem.ItemKey, capacitorItem.ItemKey);
var other = Observer(1, sensorItem.ItemKey, capacitorItem.ItemKey);
var target = Entity(2, 40, "raider");
target.Visibility = 1;
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [actor, other, target] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "sensor-ping-smoke",
Zones = [zone]
};
AetheriaRuntimeSensorSimulation.StepZone(
run, zone, [actor, other, target], [], catalog, 0.25, 0.5, 0.1, 1);
AetheriaRuntimeSensorSimulation.StepZone(
run,
zone,
[actor, other, target],
[new AetheriaRuntimeDaemonSensorPingIntent { ActorEntityKey = "zone.0.entity.0" }],
catalog,
0.25,
0.5,
0.1,
2);
var sensor = actor.BehaviorStates.Single(state => state.BehaviorKind == AetheriaRuntimeBehaviorKinds.Sensor);
var otherSensor = other.BehaviorStates.Single(state => state.BehaviorKind == AetheriaRuntimeBehaviorKinds.Sensor);
Require(sensor.Pinging && !otherSensor.Pinging,
"sensor ping intent must retain actor identity instead of activating every sensor");
RequireEqual(2, sensor.PingedEntityCount,
"expanding ping must persist every crossed target index for reconnect-safe exactly-once boost");
Require(sensor.PingedEntityIndices.SequenceEqual(new[] { 1, 2 }),
"pinged target indices must survive in stable order rather than only an unverifiable count");
RequireNear(5, actor.BehaviorStates.Single(state => state.BehaviorKind == "Capacitor").CapacitorCharge,
0.000001, "accepted ping must consume its authored energy exactly once");
Require(actor.Contacts.Single(contact => contact.TargetEntityIndex == 2).Visible,
"ping boost must cross the daemon-owned detection threshold");
RequireNear(4, actor.StatGrids.Single(grid => grid.Name == "transient-visibility:sensor:0:0").Values.Single(),
0.000001, "ping must publish its authored transient visibility source");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "sensor.ping.started"),
"accepted ping must emit one provider-owned start event");
}
private static void TurretControllerAcquiresAimsAndTriggersExactWeapons()
{
var weaponPayload = new AetheriaRuntimeBehaviorPayload(
0,
AetheriaRuntimeBehaviorKinds.InstantWeapon,
0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(7)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.1)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(22, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(180)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1))
]);
var weaponItem = WearableWeaponCatalogItem("turret-smoke-weapon", weaponPayload);
var controllerItem = CatalogItem(
"turret-smoke-controller",
new AetheriaRuntimeBehaviorPayload(0, "EnergyDraw", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false))
]),
new AetheriaRuntimeBehaviorPayload(1, AetheriaRuntimeBehaviorKinds.TurretController, 0, []));
var capacitorItem = CatalogItem("turret-smoke-capacitor", CapacitorPayload(20, 1));
var catalog = new AetheriaRuntimeCatalogSnapshot([controllerItem, weaponItem, capacitorItem], [], []);
var uncontrolled = Entity(0, 0, "raider");
uncontrolled.DirectionX = 1;
uncontrolled.WeaponGroups = [new[] { 0 }];
uncontrolled.TargetEntityIndex = 1;
uncontrolled.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = weaponItem.ItemKey, Quality = 0, Durability = 1, Enabled = true }
}];
uncontrolled.Contacts = [new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = 1, InfoGathered = 1, Visible = true, Hostile = true }];
var uncontrolledTarget = Entity(1, 100, "player");
var uncontrolledZone = new AetheriaRuntimeZoneSnapshotCommit
{ ZoneIndex = 0, Entities = [uncontrolled, uncontrolledTarget] };
var uncontrolledRun = new AetheriaRuntimeRunCheckpointCommit
{ RunId = "uncontrolled-hostile-smoke", Zones = [uncontrolledZone] };
using (var physics = NewPhysics())
{
for (var frame = 0; frame < 3; frame++)
AetheriaRuntimeDaemonSimulation.Step(
uncontrolledRun,
new AetheriaRuntimeDaemonIntentState(),
0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
physics,
catalog,
frame,
frame * 0.1);
}
RequireEqual(0, uncontrolledRun.ShotReceipts.Count,
"non-player entities without a controller or command must not inherit the removed group-zero fire authority");
var turret = Entity(0, 0, "player");
turret.Kind = "station";
turret.DirectionX = 1;
turret.DirectionY = 0;
turret.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = controllerItem.ItemKey, Quality = 1, Durability = 1, Enabled = true }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
Rotation = "None",
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = weaponItem.ItemKey, Quality = 0, Durability = 1, Enabled = true }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
Rotation = "Half",
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = weaponItem.ItemKey, Quality = 0, Durability = 1, Enabled = true }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = capacitorItem.ItemKey, Quality = 1, Durability = 1, Enabled = true }
}
];
turret.WeaponGroups = [new[] { 1, 2 }];
turret.Contacts = [new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = 1, InfoGathered = 1, Visible = true, Hostile = true }];
var target = Entity(1, 100, "raider");
target.VelocityY = 5;
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [turret, target] };
var run = new AetheriaRuntimeRunCheckpointCommit { RunId = "turret-controller-smoke", Zones = [zone] };
using (var physics = NewPhysics())
{
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(), physics, catalog, 0, 0);
RequireEqual(-1, turret.TargetEntityIndex,
"an unfunded controller must remain unreachable behind its authored EnergyDraw gate");
RequireEqual(0, run.ShotReceipts.Count,
"an unfunded controller must not aim or trigger weapons behind the behavior chain");
var capacitor = turret.BehaviorStates.Single(state => state.BehaviorKind == "Capacitor");
capacitor.CapacitorCharge = 20;
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(), physics, catalog, 1, 0.1);
RequireEqual(target.EntityIndex, turret.TargetEntityIndex,
"an idle turret controller must acquire the first visible hostile ship without firing on the acquisition tick");
RequireEqual(0, run.ShotReceipts.Count,
"target acquisition must not smuggle an extra trigger into the same fossil controller tick");
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(), physics, catalog, 2, 0.2);
}
var controllerState = turret.BehaviorStates.Single(state =>
state.BehaviorKind == AetheriaRuntimeBehaviorKinds.TurretController);
RequireEqual(2, controllerState.TurretControllerWeaponCount,
"turret controller initialization must publish its exact operational weapon count");
Require(controllerState.TurretControllerPredictShots &&
Math.Abs(controllerState.TurretControllerShotSpeed - 100) < 0.000001 &&
turret.LookDirectionY > 0,
"projectile velocity must enable the fossil first-order intercept aim and persist its controller state");
Require(run.ShotReceipts.Count > 0 && run.ShotReceipts.All(receipt => receipt.WeaponOwnerIndex == 1),
"turret controller must trigger only the exact in-range aligned weapon behavior, not its group or reversed neighbor");
RequireNear(7, turret.StatGrids.Single(grid =>
grid.Name == "transient-visibility:weapon:equipment:1:0").Values.Single(), 0.000001,
"turret-triggered fire must publish the selected weapon's authored visibility through the shared shot transaction");
Require(turret.Equipment[1].Item!.Durability < 1 &&
Math.Abs(turret.Equipment[2].Item!.Durability - 1) < 0.000001,
"turret-triggered fire must wear only the exact weapon that committed a shot");
}
private static void DockingUsesRealBaysAndFossilUndockRules()
{
var catalog = new AetheriaRuntimeCatalogSnapshot(
[
CatalogItem("dock-cockpit", new AetheriaRuntimeBehaviorPayload(0, "Cockpit", 0, [])),
CatalogItem("dock-thruster", new AetheriaRuntimeBehaviorPayload(0, "Thruster", 0, [])),
CatalogItem("dock-drive", new AetheriaRuntimeBehaviorPayload(0, "AetherDrive", 0, [])),
CatalogItem("dock-reactor", new AetheriaRuntimeBehaviorPayload(0, "Reactor", 0, []))
], [], []);
var context = new AetheriaRuntimeDaemonOperationContext { Catalog = catalog };
var physicsShip = Entity(0, 24, "player");
var physicsStation = Entity(1, 0, "player");
physicsStation.Kind = "station";
physicsStation.DockingBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "dock-bay", Quantity = 1 }
}
];
physicsStation.DockingBayAssignments = [-1];
physicsStation.DockingBayContents = [Cargo()];
var physicsZone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [physicsShip, physicsStation]
};
var physicsStep = NewPhysics().Step(
"dock-collision-exclusion-smoke",
1,
0,
physicsZone,
physicsZone.Entities,
0.1);
var steppedShip = physicsStep.Bodies.Single(body => body.EntityIndex == physicsShip.EntityIndex);
RequireNear(24, steppedShip.PositionX, 0.000001,
"Ymir must not invent ship/station collision separation that pushes the fossil docking radius out of reach");
physicsShip.PositionX = steppedShip.PositionX;
physicsShip.PositionZ = steppedShip.PositionZ;
var physicsRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "dock-collision-exclusion-smoke",
CurrentZoneIndex = 0,
Zones = [physicsZone]
};
physicsRun.CurrentEntityKey = physicsRun.EntityRecordKey(0, physicsShip.EntityIndex);
var physicsDock = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.DockNearest,
"pilot",
physicsRun.RunId,
1,
physicsRun.CurrentEntityKey);
var physicsDockResult = AetheriaRuntimeDaemonOperations.Execute(physicsRun, [physicsDock], context);
Require(physicsDockResult.AppliedCommandIds.Contains(physicsDock.CommandId, StringComparer.Ordinal) &&
physicsStation.DockingBayAssignments.Single() == physicsShip.EntityIndex,
"the daemon must still accept docking after Ymir advances the overlapping ship/station bodies: " +
string.Join(",", physicsDockResult.RejectedCommandReasons.Values));
static AetheriaRuntimeEntitySnapshotCommit DockTarget(int index, double x, bool hasBay)
{
var target = Entity(index, x, "station");
target.PositionZ = 0;
if (hasBay)
{
target.DockingBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "dock-bay" }
}];
target.DockingBayAssignments = [-1];
target.DockingBayContents = [Cargo()];
}
return target;
}
var actor = Entity(0, 0, "player");
actor.WeaponGroups = [new[] { 0 }];
actor.ActiveWeaponGroups = [true];
actor.WeaponStates = [new AetheriaRuntimeWeaponStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0,
BehaviorIndex = 0,
BehaviorKind = AetheriaRuntimeBehaviorKinds.ChargedWeapon,
Firing = true,
TriggerPending = true,
BurstRemaining = 2,
Charging = true,
Charged = true,
Charge = 1,
ChargeHoldSeconds = 3,
ChargeRiskChecks = 2,
ChargeMalfunctionRisk = 0.5,
LockTargetEntityIndex = 4,
LockProgress = 1
}];
var ineligible = DockTarget(1, 5, false);
var outOfRange = DockTarget(2, 30, true);
var firstEligible = DockTarget(3, 20, true);
var nearerButLater = DockTarget(4, 10, true);
var selectionRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "dock-selection-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [actor, ineligible, outOfRange, firstEligible, nearerButLater]
}]
};
var dock = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.DockNearest,
"pilot",
"dock-selection-smoke",
0,
selectionRun.CurrentEntityKey);
dock.ScalarValue = 100;
var dockResult = AetheriaRuntimeDaemonOperations.Execute(selectionRun, [dock], context);
RequireEqual(1, dockResult.AppliedCommandIds.Count,
"DockNearest must accept the first entity with a real empty bay inside the fossil radius");
RequireEqual(actor.EntityIndex, firstEligible.DockingBayAssignments.Single(),
"docking must use the first eligible entity in stable zone order instead of the closest entity");
RequireEqual(-1, nearerButLater.DockingBayAssignments.Single(),
"a later closer station must not override the fossil first-eligible selection");
RequireEqual(0, ineligible.DockingBayAssignments.Count,
"docking must not manufacture a bay assignment on an entity with no docking-bay equipment");
RequireEqual(-1, outOfRange.DockingBayAssignments.Single(),
"a client scalar must not widen the daemon-owned fossil docking radius");
var dockedEvent = selectionRun.GameEvents.Single(value => value.Kind == "ship.docked");
Require(dockedEvent.SourceEntityIndex == actor.EntityIndex &&
dockedEvent.TargetEntityIndex == firstEligible.EntityIndex &&
dockedEvent.ScalarValue == 0 && dockedEvent.SubjectKey == selectionRun.CurrentEntityKey,
"accepted docking must publish one retained actor/parent/bay transition fact");
var disarmed = actor.WeaponStates.Single();
RequireEqual(0, actor.ActiveWeaponGroups.Count,
"docking must clear the daemon-owned held weapon-group latch");
RequireNear(0, actor.VelocityX, 0.000001,
"docking must clear retained X velocity before Ymir excludes the attached child body");
RequireNear(0, actor.VelocityY, 0.000001,
"docking must clear retained Z velocity before Ymir excludes the attached child body");
RequireNear(0, actor.HelmStrafe, 0.000001,
"docking must clear retained strafe intent");
RequireNear(0, actor.HelmForward, 0.000001,
"docking must clear retained forward intent");
Require(!disarmed.Firing && !disarmed.TriggerPending && disarmed.BurstRemaining == 0 &&
!disarmed.Charging && !disarmed.Charged && disarmed.Charge == 0 &&
disarmed.LockTargetEntityIndex < 0 && disarmed.LockProgress == 0,
"docking must cancel every transient weapon lifecycle before the ship enters its bay");
var dockedFire = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
"pilot",
selectionRun.RunId,
1,
selectionRun.CurrentEntityKey);
dockedFire.CommandId = "docked-fire-forbidden";
dockedFire.WeaponGroup = 0;
dockedFire.ScalarValue = 1;
var dockedFireResult = AetheriaRuntimeDaemonOperations.Execute(selectionRun, [dockedFire], context);
Require(dockedFireResult.RejectedCommandIds.Contains(dockedFire.CommandId) &&
dockedFireResult.Intents.WeaponGroups.Count == 0 && actor.ActiveWeaponGroups.Count == 0,
"a docked actor must reject held-fire commands without rearming daemon state");
AetheriaRuntimeRunCheckpointCommit DockedRun(
IReadOnlyList<string> itemKeys,
bool bayHasCargo = false)
{
var ship = Entity(0, 7, "player");
ship.PositionZ = 9;
ship.VelocityX = 3;
ship.VelocityY = -4;
ship.DirectionX = 0.6;
ship.DirectionY = 0.8;
ship.Equipment = itemKeys.Select(itemKey => new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = itemKey,
Durability = 1,
Enabled = true
}
}).ToArray();
var station = DockTarget(1, 7, true);
station.PositionZ = 9;
station.ChildEntityIndices = [0];
station.DockingBayAssignments = [0];
station.DockingBayContents = [bayHasCargo
? Cargo(("forgotten-cargo", 1, 0, 0))
: Cargo()];
return new AetheriaRuntimeRunCheckpointCommit
{
RunId = "undock-parity-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship, station] }]
};
}
AetheriaRuntimeDaemonOperationResult RejectUndock(
IReadOnlyList<string> itemKeys,
string expectedReason,
bool bayHasCargo = false)
{
var run = DockedRun(itemKeys, bayHasCargo);
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.Undock,
"pilot",
"undock-parity-smoke",
0,
run.CurrentEntityKey);
command.CommandId = $"undock:{expectedReason}";
var result = AetheriaRuntimeDaemonOperations.Execute(run, [command], context);
RequireEqual(expectedReason, result.RejectedCommandReasons[command.CommandId],
"undock rejection must retain the exact daemon-owned fossil reason");
Require(run.Zones[0].Entities[1].DockingBayAssignments.Contains(0),
"a rejected undock must leave parentage and bay assignment untouched");
Require(!run.GameEvents.Any(value => value.Kind == "ship.undocked"),
"a rejected undock must not publish a false presentation transition");
return result;
}
RejectUndock(["dock-thruster", "dock-reactor"],
AetheriaRuntimeDaemonRejectionReasons.MissingCockpit);
RejectUndock(["dock-cockpit", "dock-reactor"],
AetheriaRuntimeDaemonRejectionReasons.MissingPropulsion);
RejectUndock(["dock-cockpit", "dock-thruster"],
AetheriaRuntimeDaemonRejectionReasons.MissingReactor);
var cargoRejected = RejectUndock(
["dock-cockpit", "dock-thruster", "dock-reactor"],
AetheriaRuntimeDaemonRejectionReasons.DockingBayCargoNotEmpty,
bayHasCargo: true);
var cargoFrame = AetheriaRuntimeDaemonFrameDocument.Create(
cargoRejected.Run, "daemon", "session", 4, 0.08, 0.02);
cargoFrame.RejectedCommandIds = cargoRejected.RejectedCommandIds;
cargoFrame.RejectedCommandReasons = cargoRejected.RejectedCommandReasons;
var cargoCommand = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.Undock,
"pilot",
"undock-parity-smoke",
0,
cargoRejected.Run.CurrentEntityKey);
cargoCommand.CommandId = "undock:docking-bay-cargo-not-empty";
var fact = AetheriaRuntimeCommittedCommandFactDocument.FromRejectedCommand(
cargoFrame, cargoCommand, "aetheria.local");
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.DockingBayCargoNotEmpty, fact.RejectionReason,
"the committed command fact must carry the daemon-owned undock rejection reason");
var receipt = AetheriaRuntimeDaemonReceiptProjector.Project(
fact, AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId);
Require(receipt.State == "denied" &&
receipt.Message.Contains(AetheriaRuntimeDaemonRejectionReasons.DockingBayCargoNotEmpty,
StringComparison.Ordinal),
"the generic Eve receipt must expose the exact rejection reason without client reconstruction");
var successRun = DockedRun(["dock-cockpit", "dock-drive", "dock-reactor"]);
var successShip = successRun.Zones[0].Entities[0];
var poseBefore = (
successShip.PositionX,
successShip.PositionZ,
successShip.VelocityX,
successShip.VelocityY,
successShip.DirectionX,
successShip.DirectionY);
var undock = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.Undock,
"pilot",
"undock-parity-smoke",
0,
successRun.CurrentEntityKey);
var undockResult = AetheriaRuntimeDaemonOperations.Execute(successRun, [undock], context);
RequireEqual(1, undockResult.AppliedCommandIds.Count,
"AetherDrive must satisfy the fossil propulsion alternative for rare compatible ships");
RequireEqual(poseBefore, (
successShip.PositionX,
successShip.PositionZ,
successShip.VelocityX,
successShip.VelocityY,
successShip.DirectionX,
successShip.DirectionY),
"undocking must preserve the ship pose, velocity, and direction instead of applying an invented launch");
Require(!successRun.Zones[0].Entities[1].DockingBayAssignments.Contains(0),
"successful undocking must release the exact occupied bay");
var undockedEvent = successRun.GameEvents.Single(value => value.Kind == "ship.undocked");
Require(undockedEvent.SourceEntityIndex == successShip.EntityIndex &&
undockedEvent.TargetEntityIndex == successRun.Zones[0].Entities[1].EntityIndex &&
undockedEvent.ScalarValue == 0,
"accepted undocking must retain the released parent and bay after state removal");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = successRun },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"),
catalog: catalog);
var reactive = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = successRun });
Require(Flatten(reactive.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "ship.undocked" &&
node.Props["sourceEntityIndex"] == "0" && node.Props["targetEntityIndex"] == "1" &&
node.Props["scalarValue"] == "0"),
"generic Eve feedback must expose authoritative docking chronology and bay identity");
}
private static void DockedEntitiesCannotAttackOrBeTargeted()
{
var weaponPayload = new AetheriaRuntimeBehaviorPayload(
0,
AetheriaRuntimeBehaviorKinds.ConstantWeapon,
0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(9, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(10, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(13, Number(0)),
new AetheriaRuntimeBehaviorField(14, Number(1)),
new AetheriaRuntimeBehaviorField(17, Number(1))
]);
var weapon = CatalogItem("docked-combat-weapon", weaponPayload);
var catalog = new AetheriaRuntimeCatalogSnapshot([weapon], [], []);
static AetheriaRuntimeEntitySnapshotCommit ArmedShip(int index, double x, string faction, string itemKey)
{
var ship = Entity(index, x, faction);
ship.WeaponGroups = [new[] { 0 }];
ship.ActiveWeaponGroups = [true];
ship.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = itemKey, Quality = 1, Durability = 1, Enabled = true }
}];
return ship;
}
var dockedAttacker = ArmedShip(0, 0, "player", weapon.ItemKey);
dockedAttacker.TargetEntityIndex = 2;
var station = Entity(1, 0, "player");
station.ChildEntityIndices = [0];
station.DockingBayAssignments = [0];
var exposedTarget = Entity(2, 50, "raider");
var attackerRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "docked-attacker-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{ ZoneIndex = 0, Entities = [dockedAttacker, station, exposedTarget] }]
};
var forgedIntent = new AetheriaRuntimeDaemonIntentState();
forgedIntent.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{ ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true });
AetheriaRuntimeDaemonSimulation.Step(
attackerRun, forgedIntent, 0.1, new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(), catalog, 0, 0);
RequireEqual(0, attackerRun.ShotReceipts.Count,
"combat must subtract docked attackers even when stale state and a forged pulse both request fire");
RequireNear(100, Stat(exposedTarget, "hull"), 0.000001,
"a docked child must not damage an exposed world entity");
var exposedAttacker = ArmedShip(0, 0, "player", weapon.ItemKey);
exposedAttacker.TargetEntityIndex = 1;
var dockedTarget = Entity(1, 50, "raider");
var hostileStation = Entity(2, 50, "raider");
hostileStation.ChildEntityIndices = [1];
hostileStation.DockingBayAssignments = [1];
var targetRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "docked-target-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{ ZoneIndex = 0, Entities = [exposedAttacker, dockedTarget, hostileStation] }]
};
AetheriaRuntimeDaemonSimulation.Step(
targetRun, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(), NewPhysics(), catalog, 0, 0);
RequireEqual(0, targetRun.ShotReceipts.Count,
"combat target lookup must subtract docked children from world participants");
RequireNear(100, Stat(dockedTarget, "hull"), 0.000001,
"a docked child must not receive ordinary world combat damage through stale targeting");
}
private static void RefitIsAtomicAndUsesTypedPlacement()
{
static AetheriaRuntimeCatalogItem ShapedItem(
string key,
string category,
string hardpointType,
int width,
int height,
params (int X, int Y)[] cells)
{
var item = CatalogItem(key);
item.Category = category;
item.HardpointType = hardpointType;
item.ShapeWidth = width;
item.ShapeHeight = height;
item.OccupiedCells = cells.Length;
item.ShapeCells = cells.Select(cell => new AetheriaRuntimeShapeCell(cell.X, cell.Y)).ToArray();
return item;
}
var hull = HullCatalogItem("refit-hull", 4, 3, 1);
hull.Hardpoints =
[
new AetheriaRuntimeHardpoint(
"Weapon",
2,
0,
1,
2,
2,
[new AetheriaRuntimeShapeCell(0, 0), new AetheriaRuntimeShapeCell(0, 1)],
"",
"Clockwise",
0)
];
var weapon = ShapedItem(
"refit-weapon",
AetheriaRuntimeItemCategories.Weapon,
"Weapon",
2,
1,
(0, 0),
(1, 0));
var blocker = ShapedItem("refit-blocker", AetheriaRuntimeItemCategories.Gear, "", 1, 1, (0, 0));
var cargoBay = ShapedItem("refit-cargo-bay", AetheriaRuntimeItemCategories.CargoBay, "", 1, 1, (0, 0));
cargoBay.InteriorShapeWidth = 4;
cargoBay.InteriorShapeHeight = 3;
cargoBay.InteriorOccupiedCells = 12;
cargoBay.InteriorShapeCells = Enumerable.Range(0, 3)
.SelectMany(y => Enumerable.Range(0, 4).Select(x => new AetheriaRuntimeShapeCell(x, y)))
.ToArray();
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, weapon, blocker, cargoBay], [], []);
var station = Entity(0, 0, "player");
station.Kind = "station";
station.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}
];
station.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit()];
station.DockingBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "station-refit-dock", Quantity = 1 }
}
];
station.DockingBayAssignments = [1];
station.DockingBayContents = [new AetheriaRuntimeCargoBayLoadoutCommit()];
station.ChildEntityIndices = [1];
var ship = Entity(1, 0, "player");
ship.HullItemKey = hull.ItemKey;
ship.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = blocker.ItemKey,
Quality = 0.8,
Durability = 0.7,
Quantity = 1,
Enabled = false,
OverrideShutdown = true,
Temperature = 321
}
}
];
ship.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 3,
Y = 2,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}
];
ship.CargoContents =
[
new AetheriaRuntimeCargoBayLoadoutCommit
{
Items =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = weapon.ItemKey,
Quality = 0.9,
Durability = 0.6,
Quantity = 1,
Enabled = true,
Temperature = 444
}
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 1,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = blocker.ItemKey, Quantity = 1 }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 2,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}
]
}
];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "refit-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.1",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [station, ship] }]
};
var context = new AetheriaRuntimeDaemonOperationContext { Catalog = catalog };
AetheriaRuntimeDaemonCommandDocument Equip(
string id,
string sourceKind,
int sourceIndex,
string itemKey,
int sourceX,
int sourceY,
int destinationX,
int destinationY)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.EquipItem,
"pilot",
run.RunId,
1,
run.CurrentEntityKey);
command.CommandId = id;
command.TextValue = itemKey;
command.EquipmentTransfer = new AetheriaRuntimeEquipmentTransferCommand
{
SourceKind = sourceKind,
OriginEntityKey = run.CurrentEntityKey,
OriginIndex = sourceIndex,
SourceX = sourceX,
SourceY = sourceY,
DestinationEntityKey = run.CurrentEntityKey,
DestinationX = destinationX,
DestinationY = destinationY,
HasDestinationPosition = true
};
return command;
}
var equipWeapon = AetheriaRuntimeDaemonOperations.Execute(
run,
[Equip("refit-weapon", AetheriaRuntimeRefitSourceKinds.Cargo, 0, weapon.ItemKey, 0, 0, 2, 0)],
context);
Require(equipWeapon.AppliedCommandIds.Contains("refit-weapon"),
"a docked cargo weapon must equip through the daemon transaction");
var equippedWeapon = ship.Equipment.Single(slot => slot.Item.ItemKey == weapon.ItemKey);
RequireEqual("Clockwise", equippedWeapon.Rotation,
"hardpoint equipment must inherit the authored hardpoint rotation");
RequireNear(0.9, equippedWeapon.Item.Quality, 0.000001,
"refit must preserve the exact item instance quality");
RequireNear(444, equippedWeapon.Item.Temperature, 0.000001,
"refit must preserve the exact item instance temperature");
var rejectedSource = ship.CargoContents[0].Items.Single(slot => slot.Item.ItemKey == blocker.ItemKey);
var rejectedBefore = (
Count: ship.CargoContents[0].Items.Count,
X: rejectedSource.X,
Y: rejectedSource.Y,
Quality: rejectedSource.Item.Quality,
Durability: rejectedSource.Item.Durability,
Temperature: rejectedSource.Item.Temperature);
var overlap = AetheriaRuntimeDaemonOperations.Execute(
run,
[Equip("refit-overlap", AetheriaRuntimeRefitSourceKinds.Cargo, 0, blocker.ItemKey, 1, 0, 0, 0)],
context);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.RefitNoFit,
overlap.RejectedCommandReasons["refit-overlap"],
"occupied interior cells must reject with the daemon-owned fit reason");
var rejectedAfter = ship.CargoContents[0].Items.Single(slot => slot.Item.ItemKey == blocker.ItemKey);
RequireEqual(rejectedBefore.Count, ship.CargoContents[0].Items.Count,
"rejected refit must not remove the source item");
Require(rejectedBefore.X == rejectedAfter.X && rejectedBefore.Y == rejectedAfter.Y &&
rejectedBefore.Quality == rejectedAfter.Item.Quality &&
rejectedBefore.Durability == rejectedAfter.Item.Durability &&
rejectedBefore.Temperature == rejectedAfter.Item.Temperature,
"rejected refit must preserve every source instance field");
var equipBay = AetheriaRuntimeDaemonOperations.Execute(
run,
[Equip("refit-bay", AetheriaRuntimeRefitSourceKinds.Cargo, 0, cargoBay.ItemKey, 2, 0, 1, 2)],
context);
Require(equipBay.AppliedCommandIds.Contains("refit-bay") && ship.CargoBays.Count == 2 && ship.CargoContents.Count == 2,
"catalog cargo-bay category must route into aligned typed bay and contents collections");
ship.CargoContents[1].Items =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = blocker.ItemKey, Quantity = 1 }
}
];
var storeBay = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.StoreItem,
"pilot",
run.RunId,
1,
run.CurrentEntityKey);
storeBay.CommandId = "refit-nonempty-bay";
storeBay.TextValue = cargoBay.ItemKey;
storeBay.StoreItem = new AetheriaRuntimeStoreItemCommand
{
SourceKind = AetheriaRuntimeRefitSourceKinds.CargoBay,
OriginEntityKey = run.CurrentEntityKey,
SourceEquipmentIndex = 1,
DestinationEntityKey = "zone.0.entity.0",
DestinationCargoIndex = 0,
DestinationX = 0,
DestinationY = 0,
HasDestinationPosition = true
};
var nonempty = AetheriaRuntimeDaemonOperations.Execute(run, [storeBay], context);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.RefitBayNotEmpty,
nonempty.RejectedCommandReasons[storeBay.CommandId],
"a nonempty cargo bay must reject unequip before any mutation");
RequireEqual(2, ship.CargoBays.Count,
"nonempty bay rejection must preserve the installed bay");
var frame = AetheriaRuntimeDaemonFrameDocument.Create(run, "daemon", "session", 8, 0.16, 0.02);
frame.RejectedCommandIds = nonempty.RejectedCommandIds;
frame.RejectedCommandReasons = nonempty.RejectedCommandReasons;
var fact = AetheriaRuntimeCommittedCommandFactDocument.FromRejectedCommand(frame, storeBay, "aetheria.local");
var receipt = AetheriaRuntimeDaemonReceiptProjector.Project(
fact,
AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.RefitBayNotEmpty, fact.RejectionReason,
"refit rejection reason must survive the committed command fact");
Require(receipt.State == "denied" &&
receipt.Message.Contains(AetheriaRuntimeDaemonRejectionReasons.RefitBayNotEmpty, StringComparison.Ordinal),
"generic Eve receipt must expose the exact refit rejection reason");
station.DockingBayAssignments = [-1];
station.ChildEntityIndices = [];
var deployed = AetheriaRuntimeDaemonOperations.Execute(
run,
[Equip("refit-deployed", AetheriaRuntimeRefitSourceKinds.Equipment, 0, blocker.ItemKey, int.MinValue, int.MinValue, 1, 1)],
context);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.RefitRequiresDocked,
deployed.RejectedCommandReasons["refit-deployed"],
"deployed ships must not refit even when the requested placement would fit");
Require(ship.Equipment.Any(slot => slot.Item.ItemKey == blocker.ItemKey),
"deployed refit rejection must preserve installed equipment");
}
private static void CargoTransferUsesSpatialAtomicTransaction()
{
var cargoBay = CatalogItem("grid-cargo-bay");
cargoBay.Category = AetheriaRuntimeItemCategories.CargoBay;
cargoBay.ShapeWidth = 1;
cargoBay.ShapeHeight = 1;
cargoBay.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
cargoBay.InteriorShapeWidth = 2;
cargoBay.InteriorShapeHeight = 1;
cargoBay.InteriorOccupiedCells = 2;
cargoBay.InteriorShapeCells = [new AetheriaRuntimeShapeCell(0, 0), new AetheriaRuntimeShapeCell(1, 0)];
var commodity = CatalogItem("grid-commodity");
commodity.Category = AetheriaRuntimeItemCategories.SimpleCommodity;
commodity.ShapeWidth = 1;
commodity.ShapeHeight = 1;
commodity.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
commodity.Stackable = true;
commodity.MaxStack = 10;
var blocker = CatalogItem("grid-blocker");
blocker.ShapeWidth = 1;
blocker.ShapeHeight = 1;
blocker.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([cargoBay, commodity, blocker], [], []);
AetheriaRuntimeEntitySnapshotCommit CargoEntity(int index, params AetheriaRuntimeLoadoutItemSlotCommit[] items)
{
var entity = Entity(index, 0, "player");
entity.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}
];
entity.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit { Items = items }];
return entity;
}
var source = CargoEntity(0, new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0,
Y = 0,
Rotation = "None",
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = commodity.ItemKey,
Quantity = 4,
Quality = 0.75,
Durability = 0.5,
Temperature = 333
}
});
var destination = CargoEntity(
1,
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = blocker.ItemKey, Quantity = 1 }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 1,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = commodity.ItemKey, Quantity = 8 }
});
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "cargo-grid-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, destination] }]
};
var context = new AetheriaRuntimeDaemonOperationContext { Catalog = catalog };
AetheriaRuntimeDaemonCommandDocument Transfer(string id, int quantity, int x, bool positioned = true)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TransferCargoItem,
"pilot",
run.RunId,
0,
run.CurrentEntityKey);
command.CommandId = id;
command.TextValue = commodity.ItemKey;
command.CargoTransfer = new AetheriaRuntimeCargoTransferCommand
{
OriginEntityKey = "zone.0.entity.0",
OriginCargoIndex = 0,
DestinationEntityKey = "zone.0.entity.1",
DestinationCargoIndex = 0,
SourceX = 0,
SourceY = 0,
DestinationX = x,
DestinationY = 0,
HasDestinationPosition = positioned,
Quantity = quantity
};
return command;
}
var overfill = AetheriaRuntimeDaemonOperations.Execute(run, [Transfer("cargo-overfill", 3, 1)], context);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.CargoStackLimit,
overfill.RejectedCommandReasons["cargo-overfill"],
"an explicit commodity stack overfill must reject atomically");
RequireEqual(4, source.CargoContents[0].Items.Single().Item.Quantity,
"stack-limit rejection must not decrement the source stack");
RequireEqual(8, destination.CargoContents[0].Items.Single(slot => slot.Item.ItemKey == commodity.ItemKey).Item.Quantity,
"stack-limit rejection must not partially fill the destination stack");
var stacked = AetheriaRuntimeDaemonOperations.Execute(run, [Transfer("cargo-stack", 2, 1)], context);
Require(stacked.AppliedCommandIds.Contains("cargo-stack"),
"a complete explicit stack merge must commit");
RequireEqual(2, source.CargoContents[0].Items.Single().Item.Quantity,
"accepted stack merge must remove exactly the requested source quantity");
RequireEqual(10, destination.CargoContents[0].Items.Single(slot => slot.Item.ItemKey == commodity.ItemKey).Item.Quantity,
"accepted stack merge must fill the destination to the typed maximum");
var occupied = AetheriaRuntimeDaemonOperations.Execute(run, [Transfer("cargo-overlap", 1, 0)], context);
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.CargoNoFit,
occupied.RejectedCommandReasons["cargo-overlap"],
"an occupied cell containing another item must reject spatial placement");
RequireEqual(2, source.CargoContents[0].Items.Single().Item.Quantity,
"spatial rejection must leave the source stack untouched");
destination.CargoContents[0].Items = [];
var placed = AetheriaRuntimeDaemonOperations.Execute(run, [Transfer("cargo-place", 2, 0, positioned: false)], context);
Require(placed.AppliedCommandIds.Contains("cargo-place") && source.CargoContents[0].Items.Count == 0,
"unpositioned transfer must commit only after finding the first authored interior cell");
var moved = destination.CargoContents[0].Items.Single();
Require(moved.X == 0 && moved.Y == 0 && moved.Item.Quantity == 2,
"unpositioned cargo placement must use deterministic interior order");
RequireNear(0.75, moved.Item.Quality, 0.000001,
"cargo transfer must preserve source instance quality");
RequireNear(333, moved.Item.Temperature, 0.000001,
"cargo transfer must preserve source instance temperature");
}
private static void SectorMapSurfacePublishesDiscoveredGraphAndVisualLevers()
{
var map = new AetheriaRuntimeSectorMapDocument
{
FrameId = 42,
RunId = "run.map",
CurrentZoneIndex = 1,
EntranceZoneIndex = 0,
ExitZoneIndex = 2,
DiscoveredZoneIndices = new[] { 0, 1 },
Zones = new[]
{
new AetheriaRuntimeSectorMapZone
{
ZoneIndex = 0,
Name = "Gate",
X = -10,
Y = 5,
Entrance = true,
Discovered = true
},
new AetheriaRuntimeSectorMapZone
{
ZoneIndex = 1,
Name = "Here",
X = 4,
Y = 7,
Current = true,
Discovered = true,
OwnerFactionIndex = 3,
FactionIndices = new[] { 3, 4 }
},
new AetheriaRuntimeSectorMapZone
{
ZoneIndex = 2,
Name = "Unseen Exit",
X = 20,
Y = 12,
Exit = true
}
},
Links = new[]
{
new AetheriaRuntimeSectorMapLink
{
FromZoneIndex = 0,
ToZoneIndex = 1,
Discovered = true
},
new AetheriaRuntimeSectorMapLink
{
FromZoneIndex = 1,
ToZoneIndex = 2,
Discovered = false
}
},
HomeZones = new[]
{
new AetheriaRuntimeFactionZoneCommit { FactionIndex = 3, ZoneIndex = 1 }
},
BossZones = new[]
{
new AetheriaRuntimeFactionZoneCommit { FactionIndex = 3, ZoneIndex = 1 }
}
};
var surface = AetheriaRuntimeSectorMapSurfaceBuilder.Build(
map,
"2026-07-17T00:00:00.0000000Z");
var graph = surface.Surface.Root.Children.Single(child => child.Kind == "graph");
var legend = surface.Surface.Root.Children.Single(child => child.Kind == "graph.legend");
var nodes = graph.Children.Where(child => child.Kind == "graph.node").ToArray();
var edges = graph.Children.Where(child => child.Kind == "graph.edge").ToArray();
var regions = graph.Children.Where(child => child.Kind == "graph.region").ToArray();
RequireEqual(AetheriaRuntimeSectorMapSurfaceBuilder.SurfaceId, surface.Surface.Id,
"the portable sector map must have one stable advertised surface identity");
RequireEqual("graph", graph.Kind,
"the sector map must lower through Eve's generic graph component");
RequireEqual(2, nodes.Length,
"the sector-map surface must not reveal an undiscovered zone through presentation state");
RequireEqual(1, edges.Length,
"the sector-map surface must publish only discovered links whose endpoints are visible");
Require(edges.Single().Props["critical"] == "true",
"links on the weighted entrance-to-Terminus route must carry the historical critical-path role");
Require(nodes.Single(node => node.Props["zoneIndex"] == "1").Props["role"] == "current",
"the daemon current-zone fact must select the graph's current semantic role");
Require(nodes.Single(node => node.Props["zoneIndex"] == "1").Props["landmarkAssetKeys"]
.Contains(AetheriaRuntimeSectorMapSurfaceBuilder.HomeIconAssetKey, StringComparison.Ordinal) &&
nodes.Single(node => node.Props["zoneIndex"] == "1").Props["landmarkAssetKeys"]
.Contains(AetheriaRuntimeSectorMapSurfaceBuilder.ExecutiveIconAssetKey, StringComparison.Ordinal),
"home and executive landmarks must survive daemon persistence as independent map semantics");
Require(!nodes.Any(node => node.Props["label"] == "Unseen Exit"),
"provider presentation must not leak undiscovered zone labels");
RequireEqual(2, regions.Length,
"every visible faction must publish its own historical influence scalar field");
Require(regions.Single(region => region.Props["factionIndex"] == "3").Children
.Single(sample => sample.Props["nodeId"].EndsWith(".zone.1", StringComparison.Ordinal))
.Props["value"] == "10",
"an owning faction must contribute the fossil owner influence value at its zone");
Require(legend.Children.Any(item => item.Props.TryGetValue("role", out var role) && role == "executive"),
"the portable map legend must retain the historical executive landmark semantics");
Require(surface.Surface.Styles.Any(style => style.Name == "sectorMap.zone.current.labelColor") &&
surface.Surface.Styles.Any(style => style.Name == "sectorMap.link.critical.width") &&
surface.Surface.Styles.Any(style => style.Name == "sectorMap.reveal.linkDurationSeconds") &&
surface.Surface.Styles.Any(style => style.Name == "sectorMap.zoom.maximum") &&
surface.Surface.Styles.Any(style => style.Name == "sectorMap.faction.3.primary"),
"provider-owned map color, geometry, motion, and camera levers must travel with the surface");
Require(surface.Surface.Styles.Single(style => style.Name == "sectorMap.link.width").Value == "0.002" &&
surface.Surface.Styles.Single(style => style.Name == "sectorMap.influence.fillTiling").Value == "1024" &&
graph.Props["backgroundAssetKey"] == AetheriaRuntimeSectorMapSurfaceBuilder.BackgroundAssetKey,
"the surface must preserve the authored scene and influence-shader values rather than generic substitutes");
var assets = AetheriaRuntimeAssets.ProjectManifest(new AetheriaRuntimeCatalogSnapshot());
Require(assets.Assets.Any(asset => asset.Ref.AssetKey == AetheriaRuntimeSectorMapSurfaceBuilder.BackgroundAssetKey) &&
assets.Assets.Any(asset => asset.Ref.AssetKey == AetheriaRuntimeSectorMapSurfaceBuilder.InfluenceAssetKey) &&
assets.Assets.Any(asset => asset.Ref.AssetKey == AetheriaRuntimeSectorMapSurfaceBuilder.HomeIconAssetKey) &&
assets.Assets.Any(asset => asset.Ref.AssetKey == AetheriaRuntimeSectorMapSurfaceBuilder.ExecutiveIconAssetKey),
"provider asset delivery must include the historical galaxy, influence, and landmark assets");
}
private static void VolumeSurfaceKeepsNativeShaderAbiInAssetVariant()
{
var player = Entity(0, 0, "player");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "volume-program-ownership-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [player] }]
};
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var volume = Flatten(surface.Surface.Root).Single(node => node.Kind == "field.volume3d");
var stardust = Flatten(surface.Surface.Root).Single(node => node.Kind == "field.particles3d");
var gravityField = Flatten(surface.Surface.Root).Single(node => node.Kind == "field.surface2d");
Require(volume.Props.Values.All(value => value == null || !value.Contains("_Nebula", StringComparison.Ordinal)) &&
volume.Props["layerBindings"].Contains("fog.surface_height=surfaceHeight", StringComparison.Ordinal) &&
volume.Props["layerTargetDescriptors"].Contains("fog.surface_height=2,2,false,bilinear", StringComparison.Ordinal) &&
volume.Props["layerTargetDescriptors"].Contains("fog.tint=0.5,0.5,true,trilinear", StringComparison.Ordinal) &&
volume.Props["viewportTextureScaleBindings"] == "ditherCoordinates=dither" &&
volume.Props["viewportAnchor"] == stardust.Props["viewportAnchor"] &&
volume.Props["span"] == stardust.Props["span"] &&
volume.Props["cellWorldSize"] == stardust.Props["cellWorldSize"] &&
volume.Props["gravityTexelsPerCell"] == stardust.Props["gravityTexelsPerCell"] &&
volume.Props["viewportSnapLayer"] == stardust.Props["viewportSnapLayer"] &&
volume.Props["viewportSnapTexels"] == stardust.Props["viewportSnapTexels"] &&
volume.Props["documentFloatBindings"] == "simulationTimeSeconds=flowScroll,0.025,0" &&
!volume.Props.ContainsKey("vectorParameters"),
"portable Eve volume surfaces must name logical ports rather than Unity shader properties");
Require(gravityField.Props["minX"] == "-768" &&
gravityField.Props["minY"] == "-768" &&
gravityField.Props["maxX"] == "768" &&
gravityField.Props["maxY"] == "768" &&
volume.Props["documentRef"] == gravityField.Props["renderSplatsDocumentId"],
"flight fog rasterization must share the exact 256-by-6 Stardust lattice viewport");
Require(stardust.Props["documentRef"] == gravityField.Props["renderSplatsDocumentId"] &&
stardust.Props["computeProgramAssetRef"] == "compute.environment.stardust" &&
stardust.Props["materialAssetRef"] == "material.environment.stardust" &&
stardust.Props["span"] == "256" &&
stardust.Props["threadGroupSize"] == "128" &&
stardust.Props["particleStrideBytes"] == "28" &&
stardust.Props["viewportAnchor"] == "active-camera.xz" &&
stardust.Props["layerBindings"] == "fog.surface_height=surfaceHeight;fog.tint=tint" &&
stardust.Props["materialAssetTextureBindings"] == "texture.environment.volume-dither=dither" &&
stardust.Props["materialViewportTextureScaleBindings"] == "ditherCoordinates=dither" &&
stardust.Props["materialRenderFrameIndexPort"] == "frameIndex" &&
stardust.Props["cellWorldSize"] == "6" &&
stardust.Props["gravityTexelsPerCell"] == "8" &&
stardust.Props["viewportSnapLayer"] == "fog.surface_height" &&
stardust.Props["viewportSnapTexels"] == "8" &&
stardust.Props["documentFloatBindings"] ==
"simulationTimeSeconds=time,1,0;simulationTimeSeconds=flowScroll,0.025,0" &&
stardust.Props["documentTimeVectorPort"] == "timeVector" &&
stardust.Props["floatParameters"].Contains(
"period=2;minimumSize=0.25;maximumSize=0.75;spacing=6;ceilingHeight=0;" +
"floorHeight=-10;minHeadroom=25;maxHeadroom=100;heightExponent=3",
StringComparison.Ordinal) &&
!stardust.Props["floatParameters"].Contains("patch", StringComparison.OrdinalIgnoreCase),
"portable Stardust semantics must preserve the grid/gravity lattice, dispatch, camera snap, time, and particle values exactly");
var shader = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "shader.environment.gravity-fog");
var postProcess = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "profile.environment.flight");
var stardustCompute = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "compute.environment.stardust");
var stardustMaterial = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "material.environment.stardust");
var stardustColors = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "texture.environment.stardust-colors");
Require(postProcess.Ref.Kind == AetheriaRuntimeAssetKinds.VolumeProfile &&
postProcess.Ref.Metadata["presentationRole"] == "environment.post-process.flight",
"provider asset catalog must carry the advertised flight post-process profile variant");
Require(shader.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" &&
shader.Ref.Metadata["unity.volume.matrixPort.cameraToWorld"] == "_CamToWorld" &&
shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0" &&
shader.Ref.Metadata["unity.volume.pass.composite"] == "2" &&
!shader.Ref.Metadata.ContainsKey("unity.volume.pass.temporal") &&
!shader.Ref.Metadata.ContainsKey("unity.volume.texturePort.currentSample") &&
!shader.Ref.Metadata.ContainsKey("unity.volume.texturePort.history") &&
!shader.Ref.Metadata.ContainsKey("unity.volume.matrixPort.previousViewProjection") &&
!shader.Ref.Metadata.ContainsKey("unity.volume.floatPort.resetHistory"),
"provider asset metadata must expose current-frame raymarch and composite while URP owns temporal history");
Require(stardustCompute.Ref.Kind == AetheriaRuntimeAssetKinds.ComputeShader &&
stardustCompute.Ref.Metadata["unity.particles.kernel.update"] == "UpdateParticles" &&
stardustCompute.Ref.Metadata["unity.particles.bufferPort.particles"] == "particles" &&
!stardustCompute.Ref.Metadata.ContainsKey("unity.particles.texturePort.patch") &&
!stardustCompute.Ref.Metadata.ContainsKey("unity.particles.texturePort.patchHeight") &&
!stardustCompute.Ref.Metadata.ContainsKey("unity.particles.floatPort.patchDensity") &&
!stardustCompute.Ref.Metadata.ContainsKey("unity.particles.floatPort.patchBlend") &&
stardustCompute.Ref.Metadata["unity.particles.vectorPort.viewportTransform"] == "_GridTransform" &&
stardustCompute.Ref.Metadata["unity.particles.vectorPort.timeVector"] == "_Time" &&
stardustCompute.Ref.Metadata["unity.particles.intPort.span"] == "span" &&
stardustMaterial.Ref.Metadata["unity.particles.bufferPort.particles"] == "particles" &&
stardustMaterial.Ref.Metadata["unity.particles.bufferPort.quadPoints"] == "quadPoints" &&
stardustMaterial.Ref.Metadata["unity.particles.texturePort.dither"] == "_DitheringTex" &&
stardustMaterial.Ref.Metadata["unity.particles.vectorPort.ditherCoordinates"] == "_DitheringCoords" &&
stardustMaterial.Ref.Metadata["unity.particles.intPort.frameIndex"] == "_FrameNumber" &&
stardustColors.Ref.Metadata["unityAssetPath"] == "Assets/Resources/Gradients/blackbody.png",
"provider asset metadata must own the exact Stardust compute/render ABI and authored color ramp");
var fogVolume = Flatten(surface.Surface.Root)
.Single(component => string.Equals(component.Kind, "field.volume3d", StringComparison.Ordinal));
Require(fogVolume.Props.TryGetValue("floatParameters", out var fogParameters) &&
fogParameters.Contains("compositeOpacity=1", StringComparison.Ordinal),
"the portable fog surface must retain the fossil cloud shader's full-strength composite default");
}
private static AetheriaYmirWorldPhysics NewPhysics()
{
AetheriaYmirWorldPhysics physics = new();
OwnedPhysics.Add(physics);
return physics;
}
private static void RunCheck(Action check)
{
try
{
check();
}
finally
{
foreach (var physics in OwnedPhysics)
physics.Dispose();
OwnedPhysics.Clear();
}
}
private static void PositiveGravityDepthAttractsAndProjectsAsAWell()
{
var body = new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "gravity-owner",
Kind = "gas_giant",
GravityInfluenceCenterX = 0,
GravityInfluenceCenterZ = 0,
GravityInfluenceRadius = 100,
GravityWellDepth = 20,
GravityWaveRadius = 80,
GravityWaveDepth = 3,
GravityWaveSpeed = 2,
GravityWaveFrequency = 6
};
var actor = Entity(0, 10, "player");
actor.PositionZ = 0;
actor.VelocityX = 0;
actor.VelocityY = 0;
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Bodies = [body],
Entities = [actor],
GravityTerrainRadius = 100,
GravityTerrainDepth = 5
};
using var physics = NewPhysics();
physics.RetainWorlds("gravity-sign-smoke", [zone.ZoneIndex]);
var step = physics.Step("gravity-sign-smoke", 1, 0, zone, zone.Entities, 0.1);
var steppedActor = step.Bodies.Single(value => value.EntityIndex == actor.EntityIndex);
Require(steppedActor.VelocityX < 0,
"positive authored gravity depth must accelerate a body toward the well center through Ymir");
Require(AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(zone, 10, 0, 0) < 0,
"the same positive gravity-depth magnitude must project as a negative terrain well");
var splats = AetheriaRuntimeGameDocuments.RenderSplatsViewport(
new AetheriaRuntimeDaemonFrameDocument
{
Run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "gravity-sign-smoke",
CurrentZoneIndex = 0,
Zones = [zone]
}
},
new AetheriaRuntimeViewportBounds { MinX = -20, MinY = -20, MaxX = 20, MaxY = 20 });
var bodySplatIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == body.BodyKey).index;
var terrainSplatIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == "environment.gravity_terrain").index;
var fogSurfaceSplatIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == "environment.gravity_terrain:fog.surface_height").index;
var gravityWaveIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == body.BodyKey + ":gravity.wave").index;
var fogSurfaceWaveIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == body.BodyKey + ":fog.surface_height.wave").index;
var fogPatchWaveIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == body.BodyKey + ":fog.patch_height.wave").index;
Require(splats.Splats.ValueR[bodySplatIndex] < 0,
"render-splat projection must convert positive canonical body depth into a negative gravity-height value");
Require(splats.Splats.Falloff[terrainSplatIndex] == EveFieldsSplatFalloffs.PowerPulse &&
splats.Splats.CenterX[terrainSplatIndex] == 0 &&
splats.Splats.CenterY[terrainSplatIndex] == 0 &&
splats.Splats.HalfExtentX[terrainSplatIndex] == zone.GravityTerrainRadius &&
splats.Splats.FalloffScale[terrainSplatIndex] == 1,
"zone gravity terrain must remain the fossil radial PowerPulse rather than a viewport-sized solid slab");
Require(splats.Splats.Falloff[bodySplatIndex] == EveFieldsSplatFalloffs.PowerPulse &&
splats.Splats.FalloffScale[bodySplatIndex] == 2 &&
splats.Splats.FalloffExponent[bodySplatIndex] == body.GravityDepthExponent,
"body gravity wells must publish the fossil radial PowerPulse scale and exponent");
Require(splats.Splats.LayerIndex[fogSurfaceSplatIndex] != splats.Splats.LayerIndex[terrainSplatIndex] &&
splats.Splats.ValueR[fogSurfaceSplatIndex] == -splats.Splats.ValueR[terrainSplatIndex] &&
splats.Splats.ValueR[fogSurfaceSplatIndex] > 0 &&
splats.Splats.Falloff[fogSurfaceSplatIndex] == EveFieldsSplatFalloffs.PowerPulse,
"the fog surface must project positive fossil PowerBrush depth without changing negative gameplay terrain height");
foreach (var waveIndex in new[] { gravityWaveIndex, fogSurfaceWaveIndex, fogPatchWaveIndex })
{
Require(splats.Splats.SourceKind[waveIndex] == EveFieldsSplatSourceKinds.AnimatedRadialCosine &&
splats.Splats.HalfExtentX[waveIndex] == body.GravityWaveRadius * 0.5 &&
splats.Splats.FrequencyX[waveIndex] == body.GravityWaveFrequency &&
splats.Splats.FrequencyY[waveIndex] == 1.25 &&
splats.Splats.AnimationSpeed[waveIndex] == body.GravityWaveSpeed &&
splats.Splats.Falloff[waveIndex] == EveFieldsSplatFalloffs.PowerPulse &&
splats.Splats.FalloffExponent[waveIndex] == 8,
"gravity, fog surface, and fog patch height must share the exact fossil radial-wave source");
}
var patchLayerIndex = splats.Layers
.Select((layer, index) => (layer, index))
.Single(value => value.layer.LayerKey == EveFieldsSplatLayerKeys.FogPatch).index;
var patchLayer = splats.Layers[patchLayerIndex];
var patchMicroIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == "environment.fog_patch.depth_micro").index;
var patchMacroIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == "environment.fog_patch.depth_macro").index;
var ambientTintIndex = splats.Splats.SourceKey
.Select((sourceKey, index) => (sourceKey, index))
.Single(value => value.sourceKey == "environment.fog_tint.ambient").index;
Require(patchLayer.BlendMode == EveFieldsSplatBlendModes.Add,
"fossil patch depth brushes must add instead of taking the maximum");
Require(splats.Splats.SourceKind[patchMicroIndex] == EveFieldsSplatSourceKinds.AnimatedSimplexNoise &&
splats.Splats.SourceFlags[patchMicroIndex] == EveFieldsSplatSourceFlags.AbsoluteValue &&
splats.Splats.FrequencyX[patchMicroIndex] == 0.005 &&
splats.Splats.AnimationSpeed[patchMicroIndex] == 0.025 &&
splats.Splats.HalfExtentX[patchMicroIndex] == 1500,
"fog micro-patch must retain the fossil world-space animated absolute-simplex brush");
Require(splats.Splats.SourceKind[patchMacroIndex] == EveFieldsSplatSourceKinds.AnimatedCellNoiseB &&
splats.Splats.FrequencyX[patchMacroIndex] == 0.001 &&
splats.Splats.AnimationSpeed[patchMacroIndex] == 0.025 &&
splats.Splats.ValueR[patchMacroIndex] == 20,
"fog macro-patch must retain the fossil moving cellular-B brush");
Require(splats.Splats.ValueR[ambientTintIndex] > 0 &&
splats.Splats.ValueB[ambientTintIndex] > splats.Splats.ValueG[ambientTintIndex] &&
splats.Splats.ValueG[ambientTintIndex] > splats.Splats.ValueR[ambientTintIndex],
"fog tint must include the fossil ambient blue brush before local body lights");
Require(!splats.Splats.SourceKey.Contains("environment.fog_patch") &&
!splats.Splats.SourceKey.Contains("environment.fog_patch_height"),
"the viewport-local placeholder fog producers must remain deleted");
var ordinaryPlanetSplats = AetheriaRuntimeGameDocuments.RenderSplatsViewport(
new AetheriaRuntimeDaemonFrameDocument
{
Run = new AetheriaRuntimeRunCheckpointCommit
{
CurrentZoneIndex = 0,
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Bodies =
[
new AetheriaRuntimeBodySnapshotCommit
{
BodyKey = "ordinary-planet",
Kind = "planet",
GravityInfluenceRadius = 20,
GravityWaveRadius = 20,
GravityWaveDepth = 4
}
]
}
]
}
},
new AetheriaRuntimeViewportBounds { MinX = -30, MinY = -30, MaxX = 30, MaxY = 30 });
Require(!ordinaryPlanetSplats.Splats.SourceKey.Any(key =>
key.StartsWith("ordinary-planet:", StringComparison.Ordinal) &&
key.EndsWith(".wave", StringComparison.Ordinal)),
"ordinary planets must not acquire gas-giant gravity-wave visuals merely because compatibility rows contain wave values");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "gravity-height-smoke",
CurrentZoneIndex = zone.ZoneIndex,
Zones = [zone]
};
AetheriaRuntimeDaemonSimulation.Step(
run,
new AetheriaRuntimeDaemonIntentState(),
0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
physics,
simulationTimeSeconds: 0.1);
RequireNear(
AetheriaRuntimeDaemonRenderQueries.EvaluateGravityTerrainHeight(
zone, actor.PositionX, actor.PositionZ, 0.1),
actor.PositionY,
0.0001,
"daemon entities must retain the fossil terrain-height projection while Ymir owns XZ physics");
}
private static void ConstantWeaponRunsOnDaemonThroughYmirBeamContact()
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.DirectionY = 0;
source.TargetEntityIndex = 2;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 2, InfoGathered = 1, Visible = true, Hostile = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-beam", Quality = 0, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-beam-capacitor", Quality = 1, Durability = 1, Enabled = true } }
];
source.BehaviorStates = [new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind, OwnerIndex = 1, BehaviorIndex = 0,
BehaviorKind = "Capacitor", CapacitorCharge = 10, CapacitorCapacity = 10, CapacitorEfficiency = 1
}];
source.CargoContents = [Cargo(("beam-ammo", 1, 0, 0))];
var blocker = Entity(1, 40, "neutral");
var selectedTarget = Entity(2, 100, "raider");
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, blocker, selectedTarget] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "constant-weapon-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = AetheriaRuntimeRunCheckpointCommit.EntityRecordKey("constant-weapon-smoke", 0, 0),
Zones = [zone]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.ConstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(9, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(10, PerformanceStat(3)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(6)),
new AetheriaRuntimeBehaviorField(12, new AetheriaRuntimeBehaviorValue(
"item-key", "", 0, false, "", "beam-ammo", [], [])),
new AetheriaRuntimeBehaviorField(13, Number(3)),
new AetheriaRuntimeBehaviorField(14, Number(0.3)),
new AetheriaRuntimeBehaviorField(17, Number(0.15))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("test-beam", payload), CatalogItem("test-beam-capacitor", CapacitorPayload(10, 1))], [], []);
var press = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
"pilot",
run.RunId,
0,
run.CurrentEntityKey);
press.CommandId = "constant-weapon-press";
press.WeaponGroup = 0;
press.ScalarValue = 1;
var pressed = AetheriaRuntimeDaemonOperations.Execute(
run, [press], new AetheriaRuntimeDaemonOperationContext());
for (var frame = 0; frame < 7; frame++)
AetheriaRuntimeDaemonSimulation.Step(run,
frame == 0 ? pressed.Intents : new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(), catalog, frame, frame * 0.1);
var state = source.WeaponStates.Single(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.ConstantWeapon);
Require(run.GameEvents.Count(value => value.Kind == "weapon.firing.started") == 1,
"held constant fire must publish one start transition");
Require(run.GameEvents.Count(value => value.Kind == "weapon.reload.started") == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.firing.stopped") == 1,
"empty constant weapon magazine must start reload and stop the held effect once");
Require(state.Reloading && !state.Firing,
"constant weapon reload must remain authoritative persisted state");
Require(Math.Abs(Stat(blocker, "hull") - 100) < 0.000001 && Stat(selectedTarget, "hull") < 100,
"constant weapon must resolve against its firing solution rather than a presentation collider");
Require(run.ShotReceipts.Any(value => value.TargetEntityIndex == selectedTarget.EntityIndex &&
value.WeaponItemKey == "test-beam" && value.Hit),
"daemon must publish accepted continuous shot receipts with authored beam identity");
Require(CargoQuantity(source, "beam-ammo") == 0,
"constant weapon reload must consume its reserve cargo through the shared transaction");
Require(source.BehaviorStates.Single(value => value.BehaviorKind == "Capacitor").CapacitorCharge < 10,
"constant weapon must pay elapsed-time energy through canonical capacitor state");
RequireNear(6, source.StatGrids.Single(grid =>
grid.Name == "transient-visibility:weapon:equipment:0:0").Values.Single(), 0.000001,
"constant fire must refresh its authored weapon visibility while the beam is committed");
Require(source.Equipment[0].Item!.Durability < 1,
"constant fire must apply elapsed-time wear only while its beam transaction commits");
for (var frame = 7; frame < 11; frame++)
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(), catalog, frame, frame * 0.1);
var release = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
"pilot",
run.RunId,
11,
run.CurrentEntityKey);
release.CommandId = "constant-weapon-release";
release.WeaponGroup = 0;
release.ScalarValue = 0;
var released = AetheriaRuntimeDaemonOperations.Execute(
run, [release], new AetheriaRuntimeDaemonOperationContext());
AetheriaRuntimeDaemonSimulation.Step(run, released.Intents, 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(), catalog, 11, 1.1);
Require(!state.Firing && run.GameEvents.Count(value => value.Kind == "weapon.firing.stopped") == 2,
"one release command must stop daemon-latched constant fire and publish its transition");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 11, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var weaponNodes = Flatten(surface.Surface.Root).Where(node => node.Kind == "weapon.state").ToArray();
Require(weaponNodes.Any(node =>
node.Props["behaviorKind"] == AetheriaRuntimeBehaviorKinds.ConstantWeapon &&
node.Props["itemKey"] == "test-beam" && node.Props["firing"] == "false"),
$"Eve world entity must project recoverable generic ConstantWeapon state; nodes={string.Join(";", weaponNodes.Select(node => string.Join(",", node.Props.Select(pair => $"{pair.Key}={pair.Value}"))))}");
var reactive = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 11, Run = run });
Require(Flatten(reactive.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "weapon.firing.started" && node.Props["itemKey"] == "test-beam"),
"Eve feedback stream must project continuous weapon transition chronology");
var cockpit = Flatten(surface.Surface.Root).Single(node => node.Id == "aetheria.daemon.game.cockpit");
Require(cockpit.Kind == "pane" && cockpit.Props["role"] == "pilot.cockpit" &&
cockpit.Layout["position"] == "absolute" && cockpit.Style["background"] == "transparent",
"pilot surface must publish a transparent provider-authored cockpit overlay");
Require(Flatten(cockpit).Count(node => node.Kind == "progress") >= 7 &&
Flatten(cockpit).Any(node => node.Id == "aetheria.daemon.game.cockpit.capacitor") &&
Flatten(cockpit).Any(node => node.Id == "aetheria.daemon.game.cockpit.targetLock" &&
node.Props["label"] == "TARGET LOCK") &&
Flatten(cockpit).Any(node => node.Id == "aetheria.daemon.game.cockpit.targetHull"),
"pilot cockpit must expose native ship and target instrumentation through generic Eve progress components");
Require(Flatten(surface.Surface.Root).Where(node =>
node.Id is "aetheria.daemon.game.frame" or "aetheria.daemon.game.player" or "aetheria.daemon.game.commands")
.All(node => node.Layout.TryGetValue("display", out var display) && display == "none"),
"operator diagnostics must remain published state without becoming pilot-camera UI");
var mainMenu = Flatten(surface.Surface.Root).Single(node => node.Id == "aetheria.daemon.game.main_menu");
Require(mainMenu.Layout.TryGetValue("display", out var menuDisplay) && menuDisplay == "none" &&
mainMenu.EmbeddedDocuments.Count == 0,
"an active pilot surface must not embed or display the main menu until daemon menu state opens it");
}
private static void ChargedWeaponCannotBypassChargeLifecycle()
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.TargetEntityIndex = -1;
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-charged", Quality = 0, Durability = 1, Enabled = true }
}];
var target = Entity(1, 80, "raider");
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "charged-weapon-admission-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.ChargedWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(20)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(15, PerformanceStat(1000000000)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(24, new AetheriaRuntimeBehaviorValue("bool", "", 0, true, "", "", [], [])),
new AetheriaRuntimeBehaviorField(27, Number(2)),
new AetheriaRuntimeBehaviorField(30, Number(3))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("test-charged", payload)], [], []);
var intents = new AetheriaRuntimeDaemonIntentState();
intents.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{
ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true
});
AetheriaRuntimeDaemonSimulation.Step(run, intents, 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(),
catalog, 0, 0);
Require(zone.PhysicalPayloads.Count == 0 && !run.GameEvents.Any(value => value.Kind == "shot.committed"),
"pressing an authored ChargedWeapon must not bypass charging through the instant weapon resolver");
Require(Math.Abs(Stat(target, "hull") - 100) < 0.000001,
"charged weapon admission must not apply damage before a daemon-owned release commits the shot");
var state = source.WeaponStates.Single(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.ChargedWeapon);
for (var frame = 1; frame < 11; frame++)
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(),
catalog, frame, frame * 0.1);
Require(state.Charging && state.Charged && state.Charge >= 1 && state.ChargeHoldSeconds > 0,
"one semantic request must precharge without a firing solution and enter persisted hold state");
Require(!run.GameEvents.Any(value => value.Kind == "shot.committed"),
"ready charged weapon must hold rather than invent a firing solution");
source.TargetEntityIndex = target.EntityIndex;
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(),
catalog, 11, 1.1);
Require(run.GameEvents.Count(value => value.Kind == "weapon.charge.committed") == 1 &&
run.GameEvents.Any(value => value.Kind == "shot.committed" &&
value.ItemKey == "test-charged" && Math.Abs(value.ScalarValue - 40) < 0.000001),
"stored full charge must commit automatically when a firing solution becomes available");
RequireNear(15, source.StatGrids.Single(grid =>
grid.Name == "transient-visibility:weapon:equipment:0:0").Values.Single(), 0.000001,
"charged fire must publish base visibility multiplied by its committed charge modifier");
Require(source.Equipment[0].Item!.Durability < 1,
"charged hold must not suppress wear when the stored shot finally commits");
var miss = run.ShotReceipts.Single(value => value.WeaponItemKey == "test-charged");
var missDx = miss.EndpointX - target.PositionX;
var missDz = miss.EndpointZ - target.PositionZ;
Require(!miss.Hit && miss.AppliedDamage == 0 && miss.HitRoll > miss.HitProbability,
"extreme authored dispersion must deterministically resolve a miss without damage");
RequireEqual("none", miss.ImpactKind, "a miss must not advertise a fabricated impact response");
RequireEqual("bolt", miss.PresentationKind, "instant weapon receipts must advertise a runtime-neutral travelling effect");
Require(Math.Sqrt(missDx * missDx + missDz * missDz) > 20 &&
miss.ImpactAngleRoll >= 0 && miss.ImpactAngleRoll < 1 &&
miss.ImpactRadiusRoll >= 0 && miss.ImpactRadiusRoll < 1,
"miss receipt must carry independent named impact rolls and an endpoint outside the target silhouette");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 11, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
Require(Flatten(surface.Surface.Root).Any(node => node.Kind == "weapon.state" &&
node.Props.ContainsKey("chargeMalfunctionRisk")),
"Eve must expose charged hold duration and malfunction risk generically");
var reactive = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 11, Run = run });
Require(Flatten(reactive.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "weapon.charge.committed"),
"Eve feedback must expose charged solution commit chronology");
}
private static void InstantWeaponRequestSurvivesLockAcquisition()
{
var source = Entity(0, 0, "player");
source.DirectionX = 0;
source.DirectionY = 1;
source.LookDirectionX = 1;
source.LookDirectionY = 0;
source.VelocityX = 1;
source.TargetEntityIndex = 1;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-instant", Quality = 0, Durability = 1, Enabled = true }
}];
var target = Entity(1, 80, "raider");
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [source, target],
Bodies =
[
new AetheriaRuntimeBodySnapshotCommit
{
Kind = "sun",
Mass = 16,
SunVisual = new AetheriaRuntimeSunVisualCommit
{
LightColorX = 0.5,
LightColorY = 0.75,
LightColorZ = 1,
LightRadiusMultiplier = 1
}
}
]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "instant-trigger-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.InstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(20)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(8)),
new AetheriaRuntimeBehaviorField(15, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(45)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot([WearableWeaponCatalogItem("test-instant", payload)], [], []);
RequireNear(45,
AetheriaRuntimeEquippedBehaviorQueries.Find(source, catalog, AetheriaRuntimeBehaviorKinds.InstantWeapon)
.Single().EvaluateStat(23),
0.000001,
"lock-transition fixture must retain its authored 45-degree acquisition cone");
var fire = new AetheriaRuntimeDaemonIntentState();
fire.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{
ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true
});
AetheriaRuntimeDaemonSimulation.Step(run, fire, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 0, 0);
for (var frame = 1; frame < 12 && run.ShotReceipts.Count == 0; frame++)
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
Require(run.ShotReceipts.Count == 1 && run.ShotReceipts[0].SourceEntityIndex == source.EntityIndex,
$"one instant weapon request must remain pending until daemon lock acquisition commits its shot; " +
$"receipts={run.ShotReceipts.Count}, hull={source.DirectionX},{source.DirectionY}, look={source.LookDirectionX},{source.LookDirectionY}, contacts={string.Join(";", (source.Contacts ?? []).Select(value => $"{value.TargetEntityIndex}:{value.InfoGathered}"))}, states={string.Join(";", (source.WeaponStates ?? []).Select(value => $"{value.BehaviorKind}:pending={value.TriggerPending}:lock={value.LockProgress}:target={value.LockTargetEntityIndex}:burst={value.BurstRemaining}"))}");
Require(run.GameEvents.Count(value => value.Kind == "weapon.lock.started" &&
value.SourceEntityIndex == 0 && value.TargetEntityIndex == 1) == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.acquired" &&
value.SourceEntityIndex == 0 && value.TargetEntityIndex == 1) == 1,
"daemon lock acquisition must publish one start and one completed transition instead of client-timed feedback");
var state = source.WeaponStates.Single(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.InstantWeapon);
Require(!state.TriggerPending,
"instant weapon request must clear after the committed burst begins");
var weaponVisibility = source.StatGrids.Single(grid =>
grid.Name == "transient-visibility:weapon:equipment:0:0");
RequireNear(8, weaponVisibility.Values.Single(), 0.000001,
"committed instant shot must publish its authored visibility as a named transient source");
Require(source.Equipment[0].Item!.Durability < 1,
"committed instant shot must apply one round of thermal wear to its owning equipment");
AetheriaRuntimeVisibilitySimulation.BeginTick([source], 1, 0.5);
RequireNear(8 * Math.Exp(-0.5), weaponVisibility.Values.Single(), 0.000001,
"weapon visibility must use the fossil exponential source decay rather than vanish with the effect");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 12, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var reactiveSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 12, Run = run });
Require(!Flatten(surface.Surface.Root).Any(node => node.Kind == "combat.presentation"),
"the topology surface must not retain dynamic combat presentation authority");
var combat = Flatten(reactiveSurface.Surface.Root).Single(node => node.Kind == "combat.presentation");
var world = Flatten(surface.Surface.Root).Single(node => node.Kind == "world.scene3d");
var aim = Flatten(surface.Surface.Root).Single(node => node.Kind == "aim.presentation");
Require(!string.IsNullOrWhiteSpace(world.Props["lookCommand"]) &&
world.Props["lookModel"] == "planar-yaw.v1" &&
world.Props["lookSensitivityRadians"] == "-0.001",
"playable world must advertise the daemon-owned continuous look command and fossil pointer response");
Require(world.Props["cameraRig"] == "perspective.entity-forward-follow.v1" &&
world.Props["cameraLookAt"] == "aim.convergence-point.v1" &&
world.Props["cameraDistance"] == "30" &&
world.Props["cameraVerticalFieldOfViewDegrees"] == "60" &&
world.Props["cameraTargetScreenX"] == "0.64" &&
world.Props["cameraTargetScreenY"] == "0.19" &&
world.Props["cameraPositionDamping"] == "0" &&
world.Props["cameraNearClipPlane"] == "1" &&
world.Props["cameraFarClipPlane"] == "4096",
"undocked world must translate the authored ARPG Third Person Rig into bottom-origin Eve viewport coordinates");
Require(!world.Props.ContainsKey("skyboxAssetRef") &&
world.Props["ambientLightColor"] == "0.5,0.75,1" &&
world.Props["reflectionAssetRef"] == "texture.environment.reflection" &&
world.Props["postProcessProfileAssetRef"] == "profile.environment.flight" &&
world.Props["colorGradingSpace"] == "hdr-before-tonemap.v1" &&
world.Props["exposureMode"] == "histogram.v1" &&
world.Props["exposureLowPercent"] == "47.37294" &&
world.Props["exposureHighPercent"] == "99" &&
world.Props["exposureMinimumEv"] == "-3" &&
world.Props["exposureMaximumEv"] == "0.3" &&
world.Props["exposureKeyValue"] == "0.5" &&
world.Props["exposureAdaptation"] == "progressive" &&
world.Props["exposureSpeedUp"] == "2" &&
world.Props["exposureSpeedDown"] == "1" &&
world.Props["cameraReconstruction"] == "temporal-reprojection.v1" &&
world.Props["temporalQuality"] == "high" &&
world.Props["temporalHistoryBlend"] == "0.99" &&
world.Props["temporalJitterScale"] == "0.1" &&
world.Props["temporalSharpening"] == "0" &&
world.Props["reflectionIntensity"] == "1" &&
world.Props["ambientLightIntensity"] == "1.46" &&
!world.Props.ContainsKey("keyLightDirection") &&
!world.Props.ContainsKey("keyLightColor") &&
!world.Props.ContainsKey("keyLightIntensity"),
"playable world must light from stellar tint, use the studio HDRI only for reflections, and leave the raymarch pass in charge of the frame");
Require(aim.Props["controlledEntityIndex"] == "0" &&
aim.Props["convergenceTargetEntityId"] == run.EntityRecordKey(0, 1) &&
aim.Props["minimumConvergenceDistance"] == "50",
"Eve aim presentation must bind the controlled body direction to the fossil convergence semantic");
Require(combat.Props["controlledEntityIndex"] == "0" &&
combat.Props["selectedTargetEntityIndex"] == "1" &&
combat.Props["targetVisible"] == "true" &&
combat.Props["targetHostile"] == "true" &&
double.Parse(combat.Props["lockProgress"], CultureInfo.InvariantCulture) > 0.99,
"Eve combat presentation must expose daemon-owned selection, contact, and completed lock without duplicating body transforms");
var cockpitLock = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.cockpit.targetLock");
Require(cockpitLock.Kind == "progress" && cockpitLock.Props["label"] == "TARGET LOCK" &&
double.Parse(cockpitLock.Props["value"], CultureInfo.InvariantCulture) > 0.99,
"provider cockpit must project the same completed daemon lock through a generic Eve progress component");
Require(combat.Props["hitMarkerDurationSeconds"] == "0.25" &&
combat.Props["radialFillMinimum"] == "0.25" &&
combat.Props["radialFillMaximum"] == "0.75",
"Eve combat presentation must preserve the fossil's presentation timing and radial meter semantics");
var secondTarget = Entity(2, 100, "raider");
zone.Entities = [source, target, secondTarget];
source.TargetEntityIndex = secondTarget.EntityIndex;
source.Contacts =
[
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true },
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = 2, InfoGathered = 1, Hostile = true, Visible = true }
];
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 13, 1.3);
Require(state.LockTargetEntityIndex == 2 && state.LockProgress > 0 && state.LockProgress < 0.99 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.lost" &&
value.TargetEntityIndex == 1 && value.Reason == "target-changed") == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.started" &&
value.TargetEntityIndex == 2) == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.acquired" &&
value.TargetEntityIndex == 2) == 0,
$"idle authored lock behavior must tick every daemon step and transfer chronology exactly once on target change; " +
$"target={state.LockTargetEntityIndex} progress={state.LockProgress:0.###} events={string.Join('|', run.GameEvents.Where(value => value.Kind.StartsWith("weapon.lock.", StringComparison.Ordinal)).Select(value => $"{value.Kind}:{value.TargetEntityIndex}:{value.Reason}:{value.AuxiliaryValue:0.###}->{value.ScalarValue:0.###}"))}");
for (var frame = 14; frame <= 17; frame++)
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
Require(state.LockProgress > 0.99 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.started" &&
value.TargetEntityIndex == 2) == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.acquired" &&
value.TargetEntityIndex == 2) == 1,
"idle authored lock behavior must continue progressive acquisition without duplicating transition events");
source.LookDirectionX = -1;
source.LookDirectionY = 0;
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 18, 1.8);
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 19, 1.9);
Require(state.LockProgress < 0.99 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.lost" &&
value.TargetEntityIndex == 2 && value.Reason == "angle") == 1,
$"crossing the authored lock angle must publish one loss transition while continued decay remains silent; " +
$"hull={source.DirectionX},{source.DirectionY} look={source.LookDirectionX},{source.LookDirectionY} source={source.PositionX:0.###},{source.PositionZ:0.###} target={secondTarget.PositionX:0.###},{secondTarget.PositionZ:0.###} progress={state.LockProgress:0.###} events={string.Join('|', run.GameEvents.Where(value => value.Kind.StartsWith("weapon.lock.", StringComparison.Ordinal)).Select(value => $"{value.Kind}:{value.TargetEntityIndex}:{value.Reason}:{value.AuxiliaryValue:0.###}->{value.ScalarValue:0.###}"))}");
source.LookDirectionX = 1;
source.LookDirectionY = 0;
for (var frame = 20; frame <= 21; frame++)
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
Require(state.LockProgress > 0.99 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.acquired" &&
value.TargetEntityIndex == 2) == 2,
"restored facing must reacquire through a second completed transition without replaying lock start");
zone.Entities = [source, target];
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 22, 2.2);
Require(state.LockTargetEntityIndex == -1 && state.LockProgress == 0 &&
run.ShotReceipts.Count == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.lock.lost" &&
value.TargetEntityIndex == 2 && value.Reason == "target-invalid") == 1,
$"an invalid target must clear an idle weapon's authoritative lock and publish one loss transition; " +
$"target={state.LockTargetEntityIndex} progress={state.LockProgress:0.###} shots={run.ShotReceipts.Count} events={string.Join('|', run.GameEvents.Where(value => value.Kind.StartsWith("weapon.lock.", StringComparison.Ordinal)).Select(value => $"{value.Kind}:{value.TargetEntityIndex}:{value.Reason}:{value.AuxiliaryValue:0.###}->{value.ScalarValue:0.###}"))}");
var resetSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 22, Run = run });
var resetCombat = Flatten(resetSurface.Surface.Root).Single(node => node.Kind == "combat.presentation");
Require(resetSurface.Version == 22 &&
resetCombat.Props["selectedTargetEntityId"] == "" &&
resetCombat.Props["selectedTargetEntityIndex"] == "-1" &&
resetCombat.Props["targetVisible"] == "false",
"reactive combat presentation must advance with the daemon frame and clear an invalid target");
Require(Flatten(resetSurface.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "weapon.lock.lost" &&
node.Props["targetEntityIndex"] == "2" &&
node.Props["reason"] == "target-invalid" &&
double.Parse(node.Props["auxiliaryValue"], CultureInfo.InvariantCulture) > 0.99),
"Eve feedback must project the daemon lock-loss transition and its prior completed progress");
}
private static void AutoWeaponRetriggersWhileHeldAndStopsOnRelease()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-auto-weapon-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.LookDirectionX = 1;
source.TargetEntityIndex = 1;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-auto", Quality = 1, Durability = 1, Enabled = true }
}];
var target = Entity(1, 50, "raider");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "auto-weapon-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, source.EntityIndex);
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.AutoWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(15, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(18, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.2)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(180)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(0))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("test-auto", payload)], [], []);
var press = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
"pilot", run.RunId, 0, run.CurrentEntityKey);
press.CommandId = "auto-weapon-press";
press.WeaponGroup = 0;
press.ScalarValue = 1;
var pressed = AetheriaRuntimeDaemonOperations.Execute(
run, [press], new AetheriaRuntimeDaemonOperationContext());
AetheriaRuntimeDaemonSimulation.Step(run, pressed.Intents, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 0, 0);
RequireEqual(1, run.ShotReceipts.Count,
"pressing an AutoWeapon group must commit its first burst exactly once");
var state = source.WeaponStates.Single(value =>
value.BehaviorKind == AetheriaRuntimeBehaviorKinds.AutoWeapon);
Require(state.CoolingDown && source.ActiveWeaponGroups.Single(),
"AutoWeapon cooldown and the held group latch must be durable daemon state");
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(AetheriaRuntimeDaemonFrameDocument.Create(
run, "smoke", "auto-weapon", 0, 0, 0.1))
.GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
AetheriaRuntimeRunCheckpointCommit restoredRun;
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
restoredRun = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()?.Run
?? throw new InvalidOperationException("Durable AutoWeapon frame is missing.");
}
var restoredSource = restoredRun.Zones.Single().Entities.Single(value => value.EntityIndex == 0);
for (var frame = 1; frame <= 3; frame++)
AetheriaRuntimeDaemonSimulation.Step(restoredRun, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
RequireEqual(2, restoredRun.ShotReceipts.Count,
"a held AutoWeapon must retrigger on the fixed step after its cooldown completes, including after restart");
var release = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetWeaponGroupActive,
"pilot", restoredRun.RunId, 4, restoredRun.CurrentEntityKey);
release.CommandId = "auto-weapon-release";
release.WeaponGroup = 0;
release.ScalarValue = 0;
var released = AetheriaRuntimeDaemonOperations.Execute(
restoredRun, [release], new AetheriaRuntimeDaemonOperationContext());
for (var frame = 4; frame < 10; frame++)
AetheriaRuntimeDaemonSimulation.Step(restoredRun,
frame == 4 ? released.Intents : new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
RequireEqual(2, restoredRun.ShotReceipts.Count,
"releasing an AutoWeapon group must prevent every later automatic burst");
Require(!restoredSource.ActiveWeaponGroups.Single() &&
!restoredSource.WeaponStates.Single(value =>
value.BehaviorKind == AetheriaRuntimeBehaviorKinds.AutoWeapon).TriggerPending,
"release must clear the only daemon input that authorizes AutoWeapon retriggering");
var ordinaryPayload = new AetheriaRuntimeBehaviorPayload(
payload.UnionKey,
AetheriaRuntimeBehaviorKinds.InstantWeapon,
payload.Group,
payload.Fields);
var ordinary = Entity(0, 0, "player");
ordinary.DirectionX = 1;
ordinary.LookDirectionX = 1;
ordinary.TargetEntityIndex = 1;
ordinary.Contacts = source.Contacts;
ordinary.WeaponGroups = [new[] { 0 }];
ordinary.ActiveWeaponGroups = [true];
ordinary.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-instant-held", Quality = 1, Durability = 1, Enabled = true }
}];
var ordinaryTarget = Entity(1, 50, "raider");
var ordinaryRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "ordinary-held-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ordinary, ordinaryTarget] }]
};
var ordinaryCatalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("test-instant-held", ordinaryPayload)], [], []);
var pulse = new AetheriaRuntimeDaemonIntentState();
pulse.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{
ActorEntityKey = ordinaryRun.CurrentEntityKey,
WeaponGroup = 0,
Fire = true,
Active = true
});
for (var frame = 0; frame < 10; frame++)
AetheriaRuntimeDaemonSimulation.Step(ordinaryRun,
frame == 0 ? pulse : new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), ordinaryCatalog, frame, frame * 0.1);
RequireEqual(1, ordinaryRun.ShotReceipts.Count,
"an ordinary InstantWeapon must remain press-edge driven even while its group latch is held");
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void GuidedWeaponPublishesPresentationProfileWithoutPhysicalAuthority()
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.LookDirectionX = 1;
source.TargetEntityIndex = 1;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-launcher", Quality = 1, Durability = 1, Enabled = true }
}];
var target = Entity(1, 80, "raider");
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "guided-presentation-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
var guidance = Curve(
new AetheriaRuntimeCurveKey(0, 0.25, 0, 0),
new AetheriaRuntimeCurveKey(1, 1, 0, 0));
var thrust = Curve(
new AetheriaRuntimeCurveKey(0, 1, 0, 0),
new AetheriaRuntimeCurveKey(1, 0.5, 0, 0));
var lift = Curve(
new AetheriaRuntimeCurveKey(0, 0, 0, 0),
new AetheriaRuntimeCurveKey(1, 0.75, 0, 0));
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.Launcher, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(20)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.1)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(22, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(180)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(26, guidance),
new AetheriaRuntimeBehaviorField(27, thrust),
new AetheriaRuntimeBehaviorField(28, lift),
new AetheriaRuntimeBehaviorField(29, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(30, Number(2)),
new AetheriaRuntimeBehaviorField(31, PerformanceStat(40))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot([CatalogItem("test-launcher", payload)], [], []);
var fire = new AetheriaRuntimeDaemonIntentState();
fire.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{ ActorEntityKey = run.CurrentEntityKey, WeaponGroup = 0, Fire = true, Active = true });
for (var frame = 0; frame < 4 && run.ShotReceipts.Count == 0; frame++)
AetheriaRuntimeDaemonSimulation.Step(run,
frame == 0 ? fire : new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
RequireEqual(0, zone.PhysicalPayloads.Count,
"fossil guided presentation must not be promoted into a damage-owning Ymir body");
var receipt = run.ShotReceipts.Single(value => value.WeaponItemKey == "test-launcher");
RequireEqual("guided", receipt.PresentationKind,
"Launcher receipts must select the generic guided presentation path");
RequireEqual("target-entity", receipt.GuidanceMode,
"Launcher guidance must retain its fossil target-entity mode");
RequireEqual(2, receipt.GuidanceCurve.Count,
"guided receipt must retain the complete authored guidance curve");
RequireNear(5, receipt.GuidanceThrust, 0.000001,
"guided receipt must retain evaluated authored thrust");
RequireNear(40, receipt.GuidanceTopSpeed, 0.000001,
"guided receipt must retain evaluated authored missile velocity");
RequireNear(2, receipt.GuidanceDodgeFrequency, 0.000001,
"guided receipt must retain authored dodge frequency");
var restored = MessagePack.MessagePackSerializer.Deserialize<AetheriaRuntimeRunCheckpointCommit>(
MessagePack.MessagePackSerializer.Serialize(run));
RequireEqual("target-entity", restored.ShotReceipts.Single().GuidanceMode,
"guided presentation levers must survive daemon checkpoint restart");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 4, Run = run });
var shot = Flatten(surface.Surface.Root).Single(node =>
node.Kind == "shot.receipt" && node.Props.TryGetValue("itemKey", out var itemKey) &&
itemKey == "test-launcher");
Require(shot.Props["guidanceMode"] == "target-entity" &&
shot.Props["guidanceCurve"] == "0,0.25,0,0;1,1,0,0" &&
shot.Props["guidanceThrustCurve"] == "0,1,0,0;1,0.5,0,0" &&
shot.Props["guidanceLiftCurve"] == "0,0,0,0;1,0.75,0,0",
"generic Eve shot facts must expose the authored guidance curves without provider internals");
}
private static void DeployableWeaponRunsThroughYmirAndDetonatesOnDaemon()
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-mine-layer", Quality = 0, Durability = 1, Enabled = true }
}];
var target = Entity(1, 50, "neutral");
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "deployable-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.DeployableWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(35)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(11, PerformanceStat(4)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(26, Number(0.2)),
new AetheriaRuntimeBehaviorField(27, Number(5)),
new AetheriaRuntimeBehaviorField(28, PerformanceStat(30)),
new AetheriaRuntimeBehaviorField(29, Number(0.2)),
new AetheriaRuntimeBehaviorField(30, PerformanceStat(50))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("test-mine-layer", payload)], [], []);
var fire = new AetheriaRuntimeDaemonIntentState();
fire.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{ ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true });
AetheriaRuntimeDaemonSimulation.Step(run, fire, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 1, 0.1);
RequireEqual(1, zone.PhysicalPayloads.Count, "deployable fire must create one persistent physical payload");
Require(!run.ShotReceipts.Any(), "deployable fire must not masquerade as an ordinary shot receipt");
RequireNear(4, source.StatGrids.Single(grid =>
grid.Name == "transient-visibility:weapon:equipment:0:0").Values.Single(), 0.000001,
"successful deployment must publish the launcher's authored weapon visibility");
Require(source.Equipment[0].Item!.Durability < 1,
"successful deployment must apply one round of thermal wear to its launcher");
var mine = zone.PhysicalPayloads.Single();
Require(mine.PositionX > 2 && mine.TriggeredAtSeconds < 0,
"Ymir must advance the unarmed mine without inventing a trigger");
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 2, 0.2);
mine = zone.PhysicalPayloads.Single();
Require(mine.Stationary && mine.TriggeredAtSeconds >= 0 &&
run.GameEvents.Count(value => value.Kind == "deployable.triggered") == 1,
"an armed Ymir contact must become one daemon-owned trigger transition");
RequireNear(100, Stat(target, "hull"), 0.000001,
"trigger contact must not bypass the authored detonation delay");
using var soaPublisher = new AetheriaRuntimeDaemonSoaFramePublisher(producerEpoch: 1);
using var soaFrame = soaPublisher.BuildCurrentZoneEntities(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 2, Run = run });
var mineIdentity = soaFrame.View.Identities.Single(identity =>
identity.EntityId == $"{run.RunId}:zone:0:physical-payload:{mine.PayloadId}");
Require(mineIdentity.Kind == "physical-payload" && mineIdentity.AssetRef == "prefab.entity.mine" &&
!mineIdentity.Selectable && mineIdentity.EntityIndex < -1,
"the playable SoA must carry the retained Ymir mine through its provider-owned asset identity");
var activeBodyExtent = soaFrame.View.Buffers[0].ByteLength;
var activeColumns = soaFrame.View.Columns.ToDictionary(
column => column.ColumnId,
column => (column.ByteOffset, column.ElementStride),
StringComparer.Ordinal);
soaFrame.Dispose();
var objects = AetheriaRuntimeGameDocuments.ObjectsViewport(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 2, Run = run },
new AetheriaRuntimeViewportBounds { MinX = -1000, MinY = -1000, MaxX = 1000, MaxY = 1000 });
var mineObject = objects.Objects.Single(value => value.EntityKey == mine.PayloadId);
RequireEqual("projectile", mineObject.Kind,
"the viewport projection must expose the retained physical payload without reviving world.scene3d as a transform authority");
Require(mineObject.IsActive && mineObject.IconAsset != null,
"the viewport projection must expose an active provider-authored presentation for the retained mine body");
Require(mine.Stationary && mine.TriggeredAtSeconds >= 0,
"daemon state must retain Ymir-derived motion and trigger state independently of presentation");
RequireNear(35, mine.PayloadMagnitude, 0.000001, "deployable must retain authored payload magnitude");
RequireNear(50, mine.BlastRadius, 0.000001, "deployable must retain authored blast radius");
Require(Math.Abs(target.PositionX - mine.PositionX) <= mine.BlastRadius,
$"triggered target must remain inside authored blast radius (mine {mine.PositionX}, target {target.PositionX})");
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.21,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 3, 0.41);
RequireEqual(0, zone.PhysicalPayloads.Count, "detonated mine must leave canonical payload state");
RequireNear(65, Stat(target, "hull"), 0.000001,
"Aetheria must apply authored blast damage after the delay");
Require(run.GameEvents.Count(value => value.Kind == "deployable.detonated") == 1,
"daemon must publish one authoritative detonation event");
using var settledSoaFrame = soaPublisher.BuildCurrentZoneEntities(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 3, Run = run });
Require(activeBodyExtent == settledSoaFrame.View.Buffers[0].ByteLength,
"one SoA layout version must retain a fixed body extent when active entity count changes");
foreach (var column in settledSoaFrame.View.Columns)
{
var activeColumn = activeColumns[column.ColumnId];
Require(activeColumn.ByteOffset == column.ByteOffset &&
activeColumn.ElementStride == column.ElementStride,
$"SoA column '{column.ColumnId}' must retain its byte interpretation across active row changes");
}
}
private static void DeployableRangeExpiryDetonatesAfterYmirMovement()
{
var source = Entity(0, -50, "player");
var target = Entity(1, 35, "neutral");
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [source, target],
PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "range-expiry-mine",
PayloadKind = "mine",
WeaponItemKey = "canonical-mine",
SourceEntityIndex = source.EntityIndex,
PositionX = 5,
VelocityX = 20,
Radius = 1,
ActivationDelaySeconds = 10,
LifetimeSeconds = 30,
MaximumSourceDistance = 6,
BlastRadius = 50,
PayloadMagnitude = 15,
TriggeredAtSeconds = -1,
Active = true
}]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "range-expiry-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), frameId: 4,
simulationTimeSeconds: 0.1);
RequireEqual(0, zone.PhysicalPayloads.Count,
"mine crossing source-relative range after Ymir movement must detonate in the same tick");
RequireNear(85, Stat(target, "hull"), 0.000001,
"range expiry must preserve the fossil splash consequence");
Require(run.GameEvents.Any(value => value.Kind == "deployable.expired" &&
value.Reason == "range" && value.ItemKey == "canonical-mine"),
"daemon must explain range-triggered expiry with stable item identity");
Require(run.GameEvents.Any(value => value.Kind == "deployable.detonated" &&
value.Reason == "range" && value.ItemKey == "canonical-mine"),
"range expiry must flow through the shared detonation transaction");
}
private static void CanonicalCatalogPublishesRecoveredMineLauncher()
{
var catalog = AetheriaRuntimeCatalogStore.OpenReadOnly(
Path.Combine(Directory.GetCurrentDirectory(), "GameData", "aetheria-world.cc"));
var drives = catalog.Items
.Where(item => item.BehaviorKinds.Contains(AetheriaRuntimeBehaviorKinds.AetherDrive))
.ToArray();
var driveHulls = catalog.Items
.Where(item => item.Hardpoints.Any(point => point.Type == "AetherDrive"))
.ToArray();
Require(drives.Length == 1 && drives[0].Name == "Traction" &&
drives[0].HardpointType == "AetherDrive" && drives[0].Price == 250000 &&
drives[0].Tags.Contains("rarity:rare", StringComparer.Ordinal),
"canonical AetherDrive must remain the singular expensive Traction retrofit");
Require(driveHulls.Length == 1 && driveHulls[0].Name == "LonginusX" &&
driveHulls[0].Price == 7500000 &&
driveHulls[0].Tags.Contains("rarity:rare", StringComparer.Ordinal),
"canonical AetherDrive compatibility must remain confined to the rare modified LonginusX hull");
var ordinaryShipHulls = catalog.Items.Where(item => item.HullType == "Ship" &&
item.Hardpoints.All(point => point.Type != "AetherDrive") &&
!item.Tags.Contains("rarity:rare", StringComparer.Ordinal))
.ToArray();
Require(ordinaryShipHulls.Any(item => item.Name == "Longinus") &&
ordinaryShipHulls.Any(item => item.Name == "Djinni"),
"the recovered common Longinus and Djinni must own ordinary ship generation");
var mine = catalog.Items.Single(item =>
item.ItemKey == "aetheria.item_definition:legacy:e78d3670-3ac6-4d9b-834c-1da4228ac311");
RequireEqual("Mine Launcher", mine.Name, "recovered item must retain fossil identity");
RequireEqual("Mine", mine.WeaponType, "recovered item must publish normalized semantic weapon type");
var behavior = mine.BehaviorPayloads.Single(value =>
value.Kind == AetheriaRuntimeBehaviorKinds.DeployableWeapon);
RequireNear(2, behavior.Fields.Single(field => field.Key == 26).Value.NumberValue, 0.000001,
"catalog mine must retain two-second activation delay");
RequireNear(30, behavior.Fields.Single(field => field.Key == 27).Value.NumberValue, 0.000001,
"catalog mine must retain thirty-second lifetime");
var manifest = AetheriaRuntimeAssets.ProjectManifest(catalog);
Require(!manifest.Assets.Any(value => value.Ref.AssetKey == "material.environment.skybox"),
"provider catalog must not retain the obsolete skybox authority when raymarched fog owns the frame");
var reflection = manifest.Assets.Single(value => value.Ref.AssetKey == "texture.environment.reflection");
RequireEqual(AetheriaRuntimeAssetKinds.Texture, reflection.Ref.Kind,
"provider must type the pre-generated environment map as a texture asset");
RequireEqual("environment.reflection", reflection.Ref.Metadata["presentationRole"],
"provider must advertise the generic reflection presentation role");
RequireEqual("Assets/Textures/studio2.hdr", reflection.Ref.Metadata["unityAssetPath"],
"provider bundle must own the pre-generated reflection map without an importer plugin");
var asset = manifest.Assets.Single(value => value.Ref.AssetKey == "prefab.entity.mine");
RequireEqual("physical-payload.mine", asset.Ref.Metadata["presentationRole"],
"provider must advertise the script-free mine presentation role");
RequireEqual("0.25", asset.Ref.Metadata["triggeredPulseSeconds"],
"mine presentation must retain the fossil triggered pulse cadence");
var rareHull = driveHulls.Single();
var rareHullAssetKey = AetheriaRuntimeAssets.HullPrefabAssetKey(rareHull.ItemKey);
var rareHullAsset = manifest.Assets.Single(value => value.Ref.AssetKey == rareHullAssetKey);
var expectedHullResourcePath = rareHull.HullPrefab.Replace('\\', '/');
const string resourcesPrefix = "Assets/Resources/";
if (expectedHullResourcePath.StartsWith(resourcesPrefix, StringComparison.OrdinalIgnoreCase))
expectedHullResourcePath = expectedHullResourcePath[resourcesPrefix.Length..];
if (expectedHullResourcePath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase))
expectedHullResourcePath = expectedHullResourcePath[..^".prefab".Length];
RequireEqual("entity.hull", rareHullAsset.Ref.Metadata["presentationRole"],
"provider manifest must type catalog hull prefabs as generic hull presentation assets");
RequireEqual(expectedHullResourcePath, rareHullAsset.Ref.Metadata["resourcesPath"],
"provider hull asset must retain the catalog-authored prefab instead of a faction fallback");
var rareShip = Entity(0, 0, "player");
rareShip.HullItemKey = rareHull.ItemKey;
var rareRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "rare-hull-presentation-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [rareShip] }]
};
var rareFrame = new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = rareRun };
var rareSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
rareFrame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"),
catalog: catalog);
var rareSurfaceEntity = Flatten(rareSurface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.0");
RequireEqual(rareHullAssetKey, rareSurfaceEntity.Props["assetRef"],
"Eve scene projection must select the typed hull asset rather than the player Djinni fallback");
using var rareSoaPublisher = new AetheriaRuntimeDaemonSoaFramePublisher(producerEpoch: 1);
var rareSoa = rareSoaPublisher.BuildCurrentZoneEntities(rareFrame, catalog);
RequireEqual(rareHullAssetKey, rareSoa.View.Identities.Single().AssetRef,
"SoA identity and Eve scene projection must share the catalog-owned hull asset key");
}
private static void EnergyFundedShieldInterceptsDamageBeforeHull()
{
var source = Entity(0, -50, "player");
var target = Entity(1, 0, "neutral");
target.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-energy-shield", Quality = 1, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-shield-capacitor", Quality = 1, Durability = 1, Enabled = true } }
];
target.BehaviorStates = [new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind, OwnerIndex = 1, BehaviorIndex = 0,
BehaviorKind = "Capacitor", CapacitorCharge = 100, CapacitorCapacity = 100,
CapacitorEfficiency = 1
}];
var shield = new AetheriaRuntimeBehaviorPayload(0, "Shield", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(3))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[CatalogItem("test-energy-shield", shield), CatalogItem("test-shield-capacitor", CapacitorPayload(100, 1))], [], []);
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "energy-shield-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
void AddDetonatingPayload(string id, double damage)
{
zone.PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = id, PayloadKind = "mine", WeaponItemKey = "test-blast",
SourceEntityIndex = source.EntityIndex, PositionX = target.PositionX,
LifetimeSeconds = 30, BlastRadius = 10, PayloadMagnitude = damage,
TriggeredAtSeconds = 0, DetonationDelaySeconds = 0, Stationary = true, Active = true
}];
}
AddDetonatingPayload("shielded-blast", 20);
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 1, 0.1);
var capacitor = target.BehaviorStates.Single(value => value.BehaviorKind == "Capacitor");
RequireNear(100, Stat(target, "hull"), 0.000001,
"funded shield must intercept damage before hull");
RequireNear(40, capacitor.CapacitorCharge, 0.000001,
"shield must consume damage multiplied by authored energy usage");
RequireNear(40.0 / 3.0, Stat(target, "shield"), 0.000001,
"legacy shield meter must be derived from remaining funded absorption, not own hit points");
Require(run.GameEvents.Any(value => value.Kind == "shield.absorbed" &&
Math.Abs(value.ScalarValue - 20) < 0.000001 &&
Math.Abs(value.AuxiliaryValue - 10) < 0.000001),
"shield outcome must publish absorbed damage and authored heat contribution");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run });
Require(Flatten(surface.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "shield.absorbed" &&
node.Props["scalarValue"] == "20" && node.Props["auxiliaryValue"] == "10"),
"Eve feedback must expose exact shield absorption and heat facts");
var shieldAsset = AetheriaRuntimeAssets.ProjectManifest(catalog).Assets.Single(value =>
value.Ref.Metadata.TryGetValue("presentationRole", out var role) && role == "effect.impact.shield");
RequireEqual("prefab.effect.impact.shield", shieldAsset.Ref.AssetKey,
"provider must advertise the authored shield visual by semantic impact role");
capacitor.CapacitorCharge = 0;
AddDetonatingPayload("unfunded-blast", 20);
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 2, 0.2);
RequireNear(80, Stat(target, "hull"), 0.000001,
"unfunded shield must not manufacture absorption");
}
private static void ArmorCellsResolveBeforeEquipmentAndHull()
{
var source = Entity(0, -100, "player");
var target = Entity(1, 0, "neutral");
target.HullItemKey = "test-cell-hull";
target.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0, Y = 0, Rotation = "Clockwise",
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-cell-gear", Quality = 1, Durability = 2, Enabled = true }
}];
var hull = HullCatalogItem("test-cell-hull", 3, 3, 5);
var gear = CatalogItem("test-cell-gear");
gear.ShapeWidth = 2;
gear.ShapeHeight = 1;
gear.ShapeCells = [new AetheriaRuntimeShapeCell(1, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, gear], [], []);
var zone = new AetheriaRuntimeZoneSnapshotCommit
{ ZoneIndex = 0, Entities = [source, target], PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "armor-blast", PayloadKind = "mine", WeaponItemKey = "test-cannon",
SourceEntityIndex = source.EntityIndex, PositionX = target.PositionX, PositionZ = target.PositionZ,
LifetimeSeconds = 30, BlastRadius = 200, PayloadMagnitude = 20,
DamageType = "Corrosive", Penetration = 0, DamageSpread = 0,
TriggeredAtSeconds = 0, DetonationDelaySeconds = 0, Stationary = true, Active = true
}] };
var run = new AetheriaRuntimeRunCheckpointCommit
{ RunId = "armor-cell-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone] };
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 1, 0.1);
var armor = target.StatGrids.Single(value => value.Name == "armor");
RequireNear(0, armor.Values[3], 0.000001, "source-facing splash cells must absorb damage first");
RequireNear(1.0 / 3.0, target.Equipment.Single().Item.Durability, 0.000001,
"rotated installed equipment at the impact cell must receive residual damage second");
RequireNear(100 - (10.0 / 3.0), Stat(target, "hull"), 0.000001,
"only residual damage after armor and equipment may reach hull durability");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var targetNode = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.1");
RequireEqual("entity.presentation", targetNode.Kind,
"Eve semantic entity facts must not become a second body-transform authority");
RequireEqual("30", targetNode.Props["armor"],
"Eve world projection must expose aggregate armor as derived presentation state");
RequireEqual("0,5,5,0,5,5,0,5,5", targetNode.Props["armorGrid"],
"Eve world projection must expose the exact daemon-owned schematic grid");
}
private static void PenetrationStopsAtFirstHullGap()
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.LookDirectionX = 1;
source.VelocityX = 1;
source.TargetEntityIndex = 1;
source.Contacts = [new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true
}];
source.WeaponGroups = [new[] { 0 }];
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "gap-penetrator", Quality = 1, Durability = 10, Enabled = true }
}];
var target = Entity(1, 80, "raider");
target.HullItemKey = "disconnected-hull";
var hull = HullCatalogItem("disconnected-hull", 4, 1, 0);
hull.ShapeCells =
[
new AetheriaRuntimeShapeCell(0, 0),
new AetheriaRuntimeShapeCell(1, 0),
new AetheriaRuntimeShapeCell(3, 0)
];
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.InstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(3)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.1)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(180)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("gap-penetrator", payload), hull], [], []);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "penetration-gap-smoke", GenerationSeed = 91, CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] }]
};
var fire = new AetheriaRuntimeDaemonIntentState();
fire.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{ ActorEntityKey = run.CurrentEntityKey, WeaponGroup = 0, Fire = true, Active = true });
for (var frame = 0; frame < 12 && run.ShotReceipts.Count == 0; frame++)
AetheriaRuntimeDaemonSimulation.Step(run,
frame == 0 ? fire : new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frame, frame * 0.1);
var receipt = run.ShotReceipts.Single(value => value.WeaponItemKey == "gap-penetrator");
Require(receipt.Hit && receipt.DamageCells.Count == 1 &&
receipt.DamageCells[0].X == 1 && receipt.DamageCells[0].Y == 0,
$"impact UV must scale by the full fossil schematic dimensions and penetration must stop " +
$"at the first empty hull sample instead of damaging a disconnected island; " +
$"endpoint={receipt.EndpointX:0.###},{receipt.EndpointZ:0.###} " +
$"cells={string.Join(';', receipt.DamageCells.Select(cell => $"{cell.X},{cell.Y}"))}");
RequireNear(90, Stat(target, "hull"), 0.000001,
"a disconnected hull island must not divide or receive penetration damage beyond a gap");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = receipt.FrameId, Run = run });
var shot = Flatten(surface.Surface.Root).Single(node =>
node.Kind == "shot.receipt" && node.Props["itemKey"] == "gap-penetrator");
RequireEqual("1", shot.Props["damageCellCount"],
"generic Eve shot receipts must expose the daemon's contiguous penetration result");
var damageCell = shot.Children.Single(node => node.Kind == "shot.damage-cell");
Require(damageCell.Props["x"] == "1" && damageCell.Props["y"] == "0" &&
damageCell.Props["armorAppliedDamage"] == "0" &&
damageCell.Props["equipmentIndex"] == "-1" &&
damageCell.Props["equipmentAppliedDamage"] == "0" &&
damageCell.Props["hullAppliedDamage"] == "10",
"generic Eve must retain the exact per-cell layer outcome without reconstructing damage");
}
private static void ShipSchematicProjectsDaemonTopologyAndEquipmentState()
{
var player = Entity(0, -50, "player");
player.TargetEntityIndex = 1;
var target = Entity(1, 0, "raider");
var observed = Entity(2, 50, "neutral");
target.HullItemKey = "schematic-hull";
target.StatGrids =
[
Grid("hull", 100), Grid("shield", 0), Grid("heat", 400),
Grid("armor", 3, 2, 10, 8, 6, 4, 2, 0),
Grid("maximumArmor", 3, 2, 10, 10, 10, 10, 10, 10)
];
target.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 1, Y = 0, Rotation = "Clockwise",
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = "schematic-launcher", Quality = 1, Durability = 5,
Temperature = 400, Enabled = true
}
}];
target.EquipmentStates = [new AetheriaRuntimeEquipmentStateCommit
{
EquipmentIndex = 0, Temperature = 400, ThermalPerformance = 0.75,
DurabilityPerformance = 0.5, Wear = 0.02, Online = true,
ThermalOnline = true, DurabilityOnline = true
}];
target.WeaponStates = [new AetheriaRuntimeWeaponStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0, BehaviorIndex = 0, BehaviorKind = AetheriaRuntimeBehaviorKinds.Launcher,
Ammo = 3, CooldownProgress = 0.4, CoolingDown = true
}];
var hull = HullCatalogItem("schematic-hull", 3, 2, 10);
var launcher = CatalogItem("schematic-launcher", new AetheriaRuntimeBehaviorPayload(
0, AetheriaRuntimeBehaviorKinds.Launcher, 0, []));
launcher.ShapeWidth = 2;
launcher.ShapeHeight = 1;
launcher.OccupiedCells = 2;
launcher.ShapeCells =
[
new AetheriaRuntimeShapeCell(0, 0),
new AetheriaRuntimeShapeCell(1, 0)
];
launcher.Durability = 10;
launcher.MinimumTemperature = 200;
launcher.MaximumTemperature = 600;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, launcher], [], []);
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [player, target, observed] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "schematic-surface-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = AetheriaRuntimeRunCheckpointCommit.EntityRecordKey(
"schematic-surface-smoke", 0, player.EntityIndex),
Zones = [zone]
};
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"),
catalog: catalog);
var schematic = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.1.schematic");
Require(schematic.Kind == "ship.schematic" && schematic.Props["role"] == "target" &&
schematic.Props["width"] == "3" && schematic.Props["height"] == "2",
"selected target must publish its daemon-owned hull topology as a generic ship schematic");
var hullCells = Flatten(schematic).Where(node => node.Kind == "schematic.hull-cell").ToArray();
RequireEqual(6, hullCells.Length,
"schematic must publish every occupied hull cell rather than a renderer-invented rectangle");
var damagedCell = hullCells.Single(node => node.Props["x"] == "1" && node.Props["y"] == "0");
Require(damagedCell.Props["armor"] == "8" && damagedCell.Props["maximumArmor"] == "10" &&
damagedCell.Props["armorRatio"] == "0.8",
"schematic cell must expose exact current and maximum daemon armor");
var item = Flatten(schematic).Single(node => node.Kind == "schematic.item");
Require(item.Props["itemKey"] == "schematic-launcher" && item.Props["durabilityRatio"] == "0.5" &&
item.Props["temperatureRatio"] == "0.5" && item.Props["thermalPerformance"] == "0.75" &&
item.Props["online"] == "true",
"schematic item must expose daemon-derived durability and thermal state");
var itemCells = Flatten(item).Where(node => node.Kind == "schematic.item-cell")
.Select(node => $"{node.Props["x"]},{node.Props["y"]}").ToArray();
RequireEqual("1,0;1,1", string.Join(";", itemCells),
"schematic footprint must use the same clockwise placement geometry as refit and damage");
var weapon = Flatten(item).Single(node => node.Kind == "schematic.weapon");
Require(weapon.Props["ammo"] == "3" && weapon.Props["cooldownProgress"] == "0.4",
"schematic weapon row must expose authoritative ammo and cooldown levers");
Require(Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.0.schematic").Props["role"] == "self",
"controlled entity must publish the same schematic contract with a self role");
Require(!Flatten(surface.Surface.Root).Any(node =>
node.Id == "aetheria.daemon.game.world.entity.2.schematic"),
"unselected observed entities must not bloat the live surface with unused schematic trees");
}
private static void EquipmentDestructionDisablesBehaviorAndCockpitKillsBeforeHull()
{
var source = Entity(0, -100, "player");
var target = Entity(1, 0, "neutral");
target.HullItemKey = "equipment-damage-hull";
target.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0, Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "damageable-weapon", Quality = 1, Durability = 1, Enabled = true }
}];
target.WeaponStates = [new AetheriaRuntimeWeaponStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0, BehaviorIndex = 0, BehaviorKind = "InstantWeapon",
Firing = true, TriggerPending = true, BurstRemaining = 2,
Charging = true, Charged = true, Charge = 1,
LockProgress = 1, LockTargetEntityIndex = source.EntityIndex
}];
var hull = HullCatalogItem("equipment-damage-hull", 1, 1, 0);
var weapon = CatalogItem("damageable-weapon", new AetheriaRuntimeBehaviorPayload(
0, "InstantWeapon", 0, []));
weapon.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, weapon], [], []);
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, Entities = [source, target], PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "equipment-destruction", PayloadKind = "mine", WeaponItemKey = "test-cannon",
SourceEntityIndex = source.EntityIndex, PositionX = target.PositionX, PositionZ = target.PositionZ,
LifetimeSeconds = 30, BlastRadius = 200, PayloadMagnitude = 1,
TriggeredAtSeconds = 0, DetonationDelaySeconds = 0, Stationary = true, Active = true
}]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "equipment-destruction-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault, NewPhysics(), catalog, 1, 0.1);
Require(target.IsActive && Math.Abs(Stat(target, "hull") - 100) < 0.000001,
"destroying ordinary equipment must not manufacture hull damage or destroy the entity");
RequireNear(0, target.Equipment.Single().Item.Durability, 0.000001,
"equipment damage must durably cross into the fossil destroyed state");
RequireEqual(1, AetheriaRuntimeEquippedBehaviorQueries.Find(target, catalog, "InstantWeapon").Count,
"destroyed equipment must remain installed as durable loadout state");
RequireEqual(0, AetheriaRuntimeEquippedBehaviorQueries.FindOperational(target, catalog, "InstantWeapon").Count,
"destroyed equipment behavior must stop contributing immediately");
var destroyedWeaponState = target.WeaponStates.Single();
Require(!destroyedWeaponState.Firing && !destroyedWeaponState.TriggerPending &&
!destroyedWeaponState.Charging && !destroyedWeaponState.Charged &&
destroyedWeaponState.BurstRemaining == 0 && destroyedWeaponState.LockTargetEntityIndex < 0,
"destroyed weapon state must deactivate atomically instead of publishing a ghost firing effect");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "equipment.destroyed" &&
value.TargetEntityIndex == target.EntityIndex && value.ItemKey == "damageable-weapon"),
"the durability crossing must publish exactly one generic equipment destruction fact");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run });
Require(Flatten(surface.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "equipment.destroyed" &&
node.Props["itemKey"] == "damageable-weapon"),
"generic Eve clients must receive equipment destruction through provider feedback facts");
var cockpitTarget = Entity(2, 0, "neutral");
cockpitTarget.HullItemKey = "cockpit-damage-hull";
cockpitTarget.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0, Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "damageable-cockpit", Quality = 1, Durability = 1, Enabled = true }
}];
var cockpitHull = HullCatalogItem("cockpit-damage-hull", 1, 1, 0);
var cockpit = CatalogItem("damageable-cockpit", new AetheriaRuntimeBehaviorPayload(0, "Cockpit", 0, []));
cockpit.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var cockpitCatalog = new AetheriaRuntimeCatalogSnapshot([cockpitHull, cockpit], [], []);
var cockpitZone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, Entities = [source, cockpitTarget], PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "cockpit-destruction", PayloadKind = "mine", WeaponItemKey = "test-cannon",
SourceEntityIndex = source.EntityIndex, PositionX = cockpitTarget.PositionX, PositionZ = cockpitTarget.PositionZ,
LifetimeSeconds = 30, BlastRadius = 200, PayloadMagnitude = 1,
TriggeredAtSeconds = 0, DetonationDelaySeconds = 0, Stationary = true, Active = true
}]
};
var cockpitRun = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "cockpit-destruction-smoke", CurrentZoneIndex = 0,
CurrentEntityKey = AetheriaRuntimeRunCheckpointCommit.EntityRecordKey(
"cockpit-destruction-smoke", 0, cockpitTarget.EntityIndex),
Zones = [cockpitZone]
};
AetheriaRuntimeDaemonSimulation.Step(cockpitRun, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault, NewPhysics(), cockpitCatalog, 1, 0.1);
Require(!cockpitTarget.IsActive && cockpitTarget.CauseOfDeath == "cockpit-destroyed",
"cockpit durability crossing must use the fossil cockpit death path before hull depletion");
RequireNear(100, Stat(cockpitTarget, "hull"), 0.000001,
"cockpit destruction must not counterfeit scalar hull depletion");
var chronology = cockpitRun.GameEvents.Where(value =>
value.Kind == "equipment.destroyed" || value.Kind == "entity.destroyed")
.Select(value => value.Kind).ToArray();
RequireEqual("equipment.destroyed,entity.destroyed", string.Join(",", chronology),
"equipment destruction feedback must precede the cause-specific entity destruction fact");
RequireEqual(AetheriaRuntimeRunLifecycle.Failed, cockpitRun.LifecyclePhase,
"destruction of the controlled entity must end the daemon-owned run");
RequireEqual("cockpit-destroyed", cockpitRun.TerminalReason,
"the run terminal reason must preserve the fossil cause of death");
RequireEqual(1L, cockpitRun.TerminalFrameId,
"the run terminal frame must be committed with the lethal transaction");
RequireEqual("", cockpitRun.CurrentEntityKey,
"the failed run must release its controlled entity after recording the terminal cause");
RequireEqual(1, cockpitRun.GameEvents.Count(value => value.Kind == "run.failed" &&
value.Reason == "cockpit-destroyed"),
"the lethal transaction must publish one durable run failure fact");
var restartedRun = MessagePack.MessagePackSerializer.Deserialize<AetheriaRuntimeRunCheckpointCommit>(
MessagePack.MessagePackSerializer.Serialize(cockpitRun));
restartedRun.Zones[0].Entities[0].VelocityX = 10;
var sourcePosition = restartedRun.Zones[0].Entities[0].PositionX;
var rejectedAfterFailure = MovementCommand("move-after-run-failure", "zone.0.entity.0", 1, 0);
using var restartedPhysics = NewPhysics();
var restarted = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-failed-run-restart.cc"),
restartedRun,
new AetheriaRuntimeDaemonTickOptions
{
FrameId = 2,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.2,
ObservedCommands = [rejectedAfterFailure],
WorldPhysics = restartedPhysics,
BuildPublications = false
});
Require(restarted.OperationResult.RejectedCommandIds.Contains("move-after-run-failure"),
"a terminal run must reject later gameplay commands after restart");
RequireNear(sourcePosition, restartedRun.Zones[0].Entities[0].PositionX, 0.000001,
"a terminal run must not advance surviving world bodies after restart");
RequireEqual(1, restartedRun.GameEvents.Count(value => value.Kind == "run.failed"),
"restart must not replay the run failure transition");
var failedSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
restarted.Frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var failedWorld = Flatten(failedSurface.Surface.Root)
.Single(node => node.Id == "aetheria.daemon.game.world");
RequireEqual("terminal", failedWorld.Props["presentationMode"],
"the generic Eve world must receive a terminal presentation lever");
RequireEqual("false", failedWorld.Props["movementEnabled"],
"the generic Eve world must not advertise movement after run failure");
RequireEqual("cockpit-destroyed", failedWorld.Props["runTerminalReason"],
"the generic Eve world must receive the authoritative terminal cause");
Require(AetheriaRuntimeStateRefResolver.TryResolveDaemonStateRef(
restarted.Frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"),
AetheriaRuntimeDaemonStateRefs.CurrentRunLifecycle,
out var projectedLifecycle) &&
projectedLifecycle == AetheriaRuntimeRunLifecycle.Failed,
"Eve state bindings must resolve the daemon-owned run lifecycle without client inference");
}
private static void DestructionDropsLootExactlyOnce()
{
var source = Entity(0, -100, "player");
var target = Entity(1, 0, "raider");
target.HullItemKey = "drop-hull";
target.StatGrids = [Grid("hull", 1), Grid("shield", 0), Grid("heat", 0)];
target.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "drop-gear", Quality = 0.8, Durability = 0.6, Quantity = 1, Enabled = true }
}];
target.CargoContents = [Cargo(("drop-cargo", 4, 0, 0))];
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, Entities = [source, target],
PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "destruction-mine", PayloadKind = "mine", WeaponItemKey = "destruction-charge",
SourceEntityIndex = 0, PositionX = 0, PositionZ = 0, BlastRadius = 200,
PayloadMagnitude = 20, TriggeredAtSeconds = 0, DetonationDelaySeconds = 0,
LifetimeSeconds = 30, Active = true, Stationary = true
}]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "destruction-smoke", GenerationSeed = 29, CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
var settings = LootSettings(1);
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1, settings,
NewPhysics(), null, 1, 0.1);
Require(!target.IsActive && target.DestroyedFrameId == 1 &&
!string.IsNullOrWhiteSpace(target.DestructionId),
"lethal daemon damage must commit one durable destruction identity and deactivate the entity");
RequireEqual(2, zone.DroppedPickups.Count,
"guaranteed fossil loot roll must drop non-hull equipment and every cargo stack");
Require(!zone.DroppedPickups.Any(value => value.Item.ItemKey == "drop-hull"),
"the equipped hull must remain excluded from fossil destruction loot");
RequireEqual(4, zone.DroppedPickups.Single(value => value.Item.ItemKey == "drop-cargo").Item.Quantity,
"cargo drops must preserve exact stack quantity");
Require(zone.DroppedPickups.All(value => Math.Abs(Math.Sqrt(
value.VelocityX * value.VelocityX + value.VelocityY * value.VelocityY +
value.VelocityZ * value.VelocityZ) - 25) < 0.000001 &&
Math.Abs(value.LifetimeSeconds - 30) < 0.000001),
"destruction pickups must retain fossil launch speed and lifetime");
RequireEqual(2, run.GameEvents.Count(value => value.Kind == "pickup.dropped"),
"each durable pickup must publish one drop event");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "entity.destroyed"),
"destruction chronology must publish exactly once");
RequireEqual(AetheriaRuntimeRunLifecycle.Active, run.LifecyclePhase,
"destroying a non-controlled entity must not counterfeit a run failure");
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
Require(!Flatten(surface.Surface.Root).Any(node => node.Id == "aetheria.daemon.game.world.entity.1"),
"Eve must not project a destroyed tombstone as an active world entity");
var destructionAsset = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(value =>
value.Ref.Metadata.TryGetValue("presentationRole", out var role) &&
role == "effect.feedback.entity.destroyed");
RequireEqual("prefab.effect.entity.destroyed", destructionAsset.Ref.AssetKey,
"provider must advertise the original destruction effect by semantic feedback role");
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1, settings,
NewPhysics(), null, 2, 0.2);
RequireEqual(2, zone.DroppedPickups.Count,
"later ticks must not duplicate an already committed destruction transaction");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "entity.destroyed"),
"later ticks must not duplicate destruction chronology");
}
private static void ChargedWeaponHoldRiskMalfunctionsDeterministically()
{
var source = Entity(0, 0, "player");
source.WeaponGroups = [new[] { 0 }];
var item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "risk-charger", Quality = 1, Durability = 1, Enabled = true };
source.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = item }];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "charged-risk-smoke", GenerationSeed = 17,
CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source] }]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.ChargedWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(20)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(25, Number(1)),
new AetheriaRuntimeBehaviorField(26, Number(0.25))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot([CatalogItem(item.ItemKey, payload)], [], []);
for (var frame = 0; frame < 21; frame++)
{
var intents = new AetheriaRuntimeDaemonIntentState();
if (frame == 0) intents.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{ ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true });
AetheriaRuntimeDaemonSimulation.Step(run, intents, 0.1,
new AetheriaRuntimeDaemonSimulationSettings(),
NewPhysics(), catalog, frame, frame * 0.1);
}
var state = source.WeaponStates.Single(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.ChargedWeapon);
Require(!state.Charging && !state.Charged && state.CoolingDown && state.ChargeRiskChecks == 1 &&
Math.Abs(state.ChargeMalfunctionRisk - 1) < 0.000001,
"held full charge must persist its risk check and fail when authored risk reaches certainty");
RequireNear(0.75, item.Durability, 0.000001,
"charged malfunction must damage canonical equipped-item durability");
Require(run.GameEvents.Count(value => value.Kind == "weapon.charge.malfunctioned" &&
value.SubjectKey == "hold-risk" && value.ItemKey == item.ItemKey) == 1,
"charged hold risk must emit one authoritative malfunction event");
Require(!run.GameEvents.Any(value => value.Kind == "shot.committed"),
"malfunction without a firing solution must never leak a projectile");
}
private static void AgentTowsStationIntoPersistentOrbit()
{
var tug = Entity(0, 0, "workers");
var station = Entity(1, 0, "workers");
station.Kind = "station";
tug.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Tow];
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, Entities = [tug, station],
Orbits = [new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "parent-orbit", FixedPositionX = 20 }],
Bodies = [new AetheriaRuntimeBodySnapshotCommit { BodyKey = "parent-body", Kind = "planet", OrbitKey = "parent-orbit" }]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
CurrentZoneIndex = 0, CurrentEntityKey = "", Zones = [zone],
AgentTasks = [new AetheriaRuntimeAgentTaskCommit { TaskId = "tow-1", CorporationKey = "workers", TaskType = AetheriaRuntimeAgentTaskTypes.Tow, ZoneIndex = 0, Status = AetheriaRuntimeAgentTaskStatuses.Queued, TargetEntityIndex = 1, OrbitParentKey = "parent-body", OrbitDistance = 20, CompletionRadius = 5 }]
};
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-tow-attach-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 1, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 1, BuildPublications = false });
Require(run.Zones[0].Entities[0].ChildEntityIndices.Contains(1), "tow pickup must attach station to tug parentage");
RequireEqual("", run.Zones[0].Entities[1].OrbitKey, "attached station must no longer own an orbit");
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-tow-detach-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 2, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 2, BuildPublications = false });
Require(!run.Zones[0].Entities[0].ChildEntityIndices.Contains(1), "tow delivery must detach station parentage");
Require(!string.IsNullOrWhiteSpace(run.Zones[0].Entities[1].OrbitKey), "delivered station must own a persistent orbit");
Require(run.Zones[0].Orbits.Any(orbit => orbit.OrbitKey == run.Zones[0].Entities[1].OrbitKey && orbit.ParentOrbitKey == "parent-orbit" && Math.Abs(orbit.Distance - 20) < 0.001),
"delivered orbit must preserve requested parent and radius");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, run.AgentTasks[0].Status, "tow task must complete only after detach applies");
}
private static void AgentSurveysBodyIntoCorporationKnowledge()
{
var surveyor = Entity(0, 0, "workers");
surveyor.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
surveyor.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "scanner", Enabled = true } }];
var scanner = CatalogItem("scanner", new AetheriaRuntimeBehaviorPayload(0, "ResourceScanner", 0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(100)), new AetheriaRuntimeBehaviorField(2, PerformanceStat(4)), new AetheriaRuntimeBehaviorField(3, PerformanceStat(0.5))]));
var catalog = new AetheriaRuntimeCatalogSnapshot([scanner], [], []);
var run = new AetheriaRuntimeRunCheckpointCommit
{
CurrentZoneIndex = 0, CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, Entities = [surveyor],
Orbits = [new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "survey-orbit", FixedPositionX = 10 }],
Bodies = [new AetheriaRuntimeBodySnapshotCommit { BodyKey = "survey-world", Kind = "planet", OrbitKey = "survey-orbit" }]
}],
AgentTasks = [new AetheriaRuntimeAgentTaskCommit { TaskId = "survey-1", CorporationKey = "workers", TaskType = AetheriaRuntimeAgentTaskTypes.Explore, ZoneIndex = 0, Status = AetheriaRuntimeAgentTaskStatuses.Queued, TargetBodyKeys = ["survey-world"] }]
};
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-survey-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 1, FixedDeltaSeconds = 1, SimulationTimeSeconds = 1, Catalog = catalog, BuildPublications = false });
var knowledge = run.CorporationSurveys.Single();
RequireEqual("workers", knowledge.CorporationKey, "survey knowledge must belong to the agent corporation");
RequireEqual("survey-world", knowledge.BodyKey, "survey knowledge must identify the scanned body");
RequireNear(4, knowledge.DensityFloor, 0.000001, "survey must publish the scanner minimum density");
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-survey-complete-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 2, FixedDeltaSeconds = 1, SimulationTimeSeconds = 2, Catalog = catalog, BuildPublications = false });
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, run.AgentTasks[0].Status,
"survey order must complete when corporation knowledge satisfies the scanner threshold");
}
private static void CargoCapacityComesFromHullAndCatalogVolumes()
{
var hull = CatalogItem("hauler-hull");
hull.HullCapacity = PerformanceStat(20);
var gear = CatalogItem("scanner");
gear.Volume = 3;
var cargoBay = CatalogItem("hauler-cargo");
cargoBay.Volume = 0;
cargoBay.InteriorOccupiedCells = 16;
var ore = CatalogItem("ore");
ore.Volume = 2;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, gear, cargoBay, ore], [], []);
var entity = Entity(0, 0, "workers");
entity.HullItemKey = hull.ItemKey;
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = gear.ItemKey } }
];
entity.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey } }];
entity.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit { Items = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = ore.ItemKey, Quantity = 4 } }] }];
RequireNear(20, AetheriaRuntimeCargoCapacityQueries.Capacity(entity, catalog), 0.000001,
"cargo capacity must evaluate the authored hull performance stat");
RequireNear(11, AetheriaRuntimeCargoCapacityQueries.Occupied(entity, catalog), 0.000001,
"equipment and stacked cargo must consume catalog volume");
RequireEqual(4, AetheriaRuntimeCargoCapacityQueries.UnitsThatFit(entity, catalog, ore.ItemKey),
"the installed cargo bay's remaining volume must determine whole units that fit");
}
private static void AgentMinesAsteroidThroughEquippedBehavior()
{
var miner = Entity(0, 0, "workers");
var home = Entity(1, 0, "workers");
miner.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Mine];
miner.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "mining-tool", Enabled = true, Quality = 1, Durability = 1 } }
];
miner.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "miner-cargo", Enabled = true, Quality = 1, Durability = 1 } }];
home.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "home-cargo", Enabled = true, Quality = 1, Durability = 1 } }];
var minerHull = CatalogItem("miner-hull");
minerHull.HullCapacity = PerformanceStat(3);
var homeHull = CatalogItem("home-hull");
homeHull.HullCapacity = PerformanceStat(100);
var iron = CatalogItem("iron");
iron.SimpleCommodityCategory = "ore";
iron.Category = AetheriaRuntimeItemCategories.SimpleCommodity;
iron.ShapeWidth = 1;
iron.ShapeHeight = 1;
iron.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
iron.Stackable = true;
iron.MaxStack = 100;
iron.Volume = 1;
var minerCargo = CatalogItem("miner-cargo");
minerCargo.Category = AetheriaRuntimeItemCategories.CargoBay;
minerCargo.ShapeWidth = 1;
minerCargo.ShapeHeight = 1;
minerCargo.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
minerCargo.InteriorShapeWidth = 2;
minerCargo.InteriorShapeHeight = 1;
minerCargo.InteriorOccupiedCells = 2;
minerCargo.InteriorShapeCells = [new AetheriaRuntimeShapeCell(0, 0), new AetheriaRuntimeShapeCell(1, 0)];
var homeCargo = CatalogItem("home-cargo");
homeCargo.Category = AetheriaRuntimeItemCategories.CargoBay;
homeCargo.ShapeWidth = 1;
homeCargo.ShapeHeight = 1;
homeCargo.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
homeCargo.InteriorShapeWidth = 10;
homeCargo.InteriorShapeHeight = 10;
homeCargo.InteriorOccupiedCells = 100;
homeCargo.InteriorShapeCells = Enumerable.Range(0, 10)
.SelectMany(y => Enumerable.Range(0, 10).Select(x => new AetheriaRuntimeShapeCell(x, y)))
.ToArray();
miner.HullItemKey = minerHull.ItemKey;
home.HullItemKey = homeHull.ItemKey;
miner.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit()];
home.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit()];
var catalog = new AetheriaRuntimeCatalogSnapshot(
[minerHull, homeHull, iron, minerCargo, homeCargo, CatalogItem("mining-tool", new AetheriaRuntimeBehaviorPayload(0, "MiningTool", 0,
[new AetheriaRuntimeBehaviorField(1, PerformanceStat(1000)), new AetheriaRuntimeBehaviorField(2, PerformanceStat(1000000000)), new AetheriaRuntimeBehaviorField(3, PerformanceStat(2)), new AetheriaRuntimeBehaviorField(4, PerformanceStat(1000))]))],
[], []);
var asteroid = new AetheriaRuntimeAsteroidCommit { Distance = 0, Size = 6 };
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [miner, home],
Bodies = [new AetheriaRuntimeBodySnapshotCommit { BodyKey = "belt", Kind = "asteroid_belt", Asteroids = [asteroid], Resources = [new AetheriaRuntimeBodyResourceCommit { ItemKey = "iron", Amount = 1 }] }]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
GenerationSeed = 7,
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [zone],
AgentTasks = [new AetheriaRuntimeAgentTaskCommit { TaskId = "mine-1", CorporationKey = "workers", TaskType = AetheriaRuntimeAgentTaskTypes.Mine, ZoneIndex = 0, OriginEntityIndex = 1, CompletionRadius = 10, Status = AetheriaRuntimeAgentTaskStatuses.Queued, TargetBodyKeys = ["belt"] }]
};
var result = AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-mining-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 1, FixedDeltaSeconds = 1, SimulationTimeSeconds = 1, Catalog = catalog, BuildPublications = false });
Require(run.AgentTasks[0].Phase == "mining", "mining task must activate its equipped tool");
Require(result.OperationResult.Intents.Behaviors.Count == 1,
$"mining behavior command must become daemon intent (count={result.OperationResult.Intents.Behaviors.Count}, applied={string.Join(',', result.Frame.AppliedCommandIds)}, rejected={string.Join(',', result.Frame.RejectedCommandIds)})");
var committedMiner = run.Zones[0].Entities.Single(entity => entity.EntityIndex == 0);
var committedAsteroid = run.Zones[0].Bodies[0].Asteroids[0];
Require(committedAsteroid.RespawnTimer > 0, $"mining damage must deplete the asteroid and start historical respawn (damage={committedAsteroid.Damage}, respawn={committedAsteroid.RespawnTimer})");
Require(committedMiner.CargoContents.SelectMany(bay => bay.Items).Any(slot => slot.Item.ItemKey == "iron" && slot.Item.Quantity > 0),
"historical mining yield must enter daemon-owned cargo");
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-mining-offload-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 2, FixedDeltaSeconds = 1, SimulationTimeSeconds = 2, Catalog = catalog, BuildPublications = false });
Require(!run.Zones[0].Entities[0].CargoContents.SelectMany(bay => bay.Items).Any(),
"full miner must offload through the shared cargo transfer command");
Require(run.Zones[0].Entities[1].CargoContents.SelectMany(bay => bay.Items).Any(slot => slot.Item.ItemKey == "iron" && slot.Item.Quantity == 1),
"home storage must receive the mined commodity");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, run.AgentTasks[0].Status,
"miner must remain assigned after a successful offload");
}
private static void TickReconcilesAndEvaluatesCatalogBehaviors()
{
var entity = Entity(0, 0, "workers");
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = "mining-tool",
Quality = 1,
Durability = 1,
Enabled = true
}
}
];
entity.BehaviorStates = Array.Empty<AetheriaRuntimeBehaviorStateCommit>();
var payload = new AetheriaRuntimeBehaviorPayload(
0,
"MiningTool",
0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(12)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(0.8)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(50))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[CatalogItem("mining-tool", payload)],
Array.Empty<AetheriaRuntimeCorporation>(),
Array.Empty<AetheriaRuntimeNameFile>());
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "behavior-query-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-behavior-query-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = NewPhysics(),
FrameId = 1,
FixedDeltaSeconds = 0,
Catalog = catalog,
BuildPublications = false
});
var behavior = AetheriaRuntimeEquippedBehaviorQueries.Find(entity, catalog, "MiningTool").Single();
RequireEqual("MiningTool", behavior.State.BehaviorKind,
"tick must reconcile equipped catalog payloads into persistent behavior state");
RequireNear(12, behavior.EvaluateStat(1), 0.000001, "behavior query must evaluate mining damage from catalog");
RequireNear(50, behavior.EvaluateStat(4), 0.000001, "behavior query must evaluate mining range from catalog");
}
private static void DaemonLoadoutsRespectFactionAvailabilityAndHullRoles()
{
var cells4 = Enumerable.Range(0, 4).SelectMany(y => Enumerable.Range(0, 4)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
var one = new[] { new AetheriaRuntimeShapeCell(0, 0) };
var cells2 = Enumerable.Range(0, 2).SelectMany(y => Enumerable.Range(0, 2)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
AetheriaRuntimeCatalogItem Item(string key, string category, string manufacturer, int price,
string hardpointType = "", params string[] behaviors)
{
var item = CatalogItem(key, behaviors.Select((kind, index) =>
new AetheriaRuntimeBehaviorPayload(index, kind, 0, Array.Empty<AetheriaRuntimeBehaviorField>())).ToArray());
item.Category = category; item.ManufacturerKey = manufacturer; item.Price = price;
item.HardpointType = hardpointType; item.ShapeWidth = 1; item.ShapeHeight = 1;
item.OccupiedCells = 1; item.ShapeCells = one; item.BehaviorKinds = behaviors;
return item;
}
var availableHull = Item("available-hull", AetheriaRuntimeItemCategories.Hull, "forge", 100);
availableHull.HardpointType = "Hull";
availableHull.HullType = "Ship"; availableHull.ShapeWidth = 4; availableHull.ShapeHeight = 4;
availableHull.OccupiedCells = 16; availableHull.ShapeCells = cells4;
availableHull.Hardpoints =
[
new AetheriaRuntimeHardpoint("ControlModule", 0, 0, 1, 1, 1, one, "", "None", 0),
new AetheriaRuntimeHardpoint("Weapon", 1, 0, 1, 1, 1, one, "", "Clockwise", 0),
new AetheriaRuntimeHardpoint("Sensors", 2, 0, 1, 1, 1, one, "", "None", 0)
];
var unavailableHull = Item("cheap-foreign-hull", AetheriaRuntimeItemCategories.Hull, "foreign", 1);
unavailableHull.HardpointType = "Hull";
unavailableHull.HullType = "Ship"; unavailableHull.ShapeWidth = 4; unavailableHull.ShapeHeight = 4;
unavailableHull.OccupiedCells = 16; unavailableHull.ShapeCells = cells4;
var stationHull = Item("station-hull", AetheriaRuntimeItemCategories.Hull, "forge", 120);
stationHull.HardpointType = "Hull"; stationHull.HullType = "Station";
stationHull.ShapeWidth = 4; stationHull.ShapeHeight = 4; stationHull.OccupiedCells = 16;
stationHull.ShapeCells = cells4;
stationHull.Hardpoints =
[
new AetheriaRuntimeHardpoint("ControlModule", 0, 0, 1, 1, 1, one, "", "None", 0),
new AetheriaRuntimeHardpoint("Sensors", 1, 1, 2, 2, 4, cells2, "", "None", 0)
];
var cockpit = Item("cockpit", AetheriaRuntimeItemCategories.Gear, "forge", 20, "ControlModule", "Cockpit");
var wrongController = Item("cheap-turret-controller", AetheriaRuntimeItemCategories.Gear, "forge", 1,
"ControlModule", "TurretController");
var weapon = Item("cannon", AetheriaRuntimeItemCategories.Weapon, "forge", 40, "Weapon", "LockWeapon");
var shipSensor = Item("ship-sensor", AetheriaRuntimeItemCategories.Gear, "forge", 15, "Sensors", "Sensor");
var stationSensor = Item("station-sensor", AetheriaRuntimeItemCategories.Gear, "forge", 80, "Sensors", "Sensor");
stationSensor.ShapeWidth = 2; stationSensor.ShapeHeight = 2;
stationSensor.OccupiedCells = 4; stationSensor.ShapeCells = cells2;
var cargo = Item("cargo-bay", AetheriaRuntimeItemCategories.CargoBay, "forge", 30);
cargo.HardpointType = "Internal";
cargo.InteriorShapeWidth = 3; cargo.InteriorShapeHeight = 3; cargo.InteriorOccupiedCells = 9;
cargo.InteriorShapeCells = Enumerable.Range(0, 3).SelectMany(y => Enumerable.Range(0, 3)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
var docking = Item("docking-bay", AetheriaRuntimeItemCategories.DockingBay, "forge", 35);
docking.HardpointType = "Tool";
var dockyardBay = Item("dockyard-bay", AetheriaRuntimeItemCategories.DockingBay, "forge", 70, "DockingBay");
var dockyardHull = Item("dockyard-hull", AetheriaRuntimeItemCategories.Hull, "forge", 600);
dockyardHull.HardpointType = "Hull";
dockyardHull.HullType = "Station";
dockyardHull.ShapeWidth = 6;
dockyardHull.ShapeHeight = 4;
dockyardHull.ShapeCells = Enumerable.Range(0, 4).SelectMany(y => Enumerable.Range(0, 6)
.Select(x => new AetheriaRuntimeShapeCell(x, y))).ToArray();
dockyardHull.OccupiedCells = dockyardHull.ShapeCells.Count;
dockyardHull.Hardpoints = Enumerable.Range(0, 6)
.SelectMany(x => new[]
{
new AetheriaRuntimeHardpoint("DockingBay", x, 0, 1, 1, 1, one, "", "None", 0),
new AetheriaRuntimeHardpoint("DockingBay", x, 3, 1, 1, 1, one, "", "None", 0)
})
.ToArray();
var capacitor = Item("capacitor", AetheriaRuntimeItemCategories.Gear, "forge", 25, "", "Capacitor");
capacitor.HardpointType = "Internal";
var faction = new AetheriaRuntimeCorporation("forge", "Forge", "F", "", "", "", 1, 1,
[new AetheriaRuntimeCorporationAllegiance("forge", 1)]);
var foreign = new AetheriaRuntimeCorporation("foreign", "Foreign", "X", "", "", "", 1, 2,
[new AetheriaRuntimeCorporationAllegiance("foreign", 1), new AetheriaRuntimeCorporationAllegiance("forge", 0.5)]);
var reactor = Item("reactor", AetheriaRuntimeItemCategories.Gear, "forge", 25, "", "Reactor");
reactor.HardpointType = "Internal";
var catalog = new AetheriaRuntimeCatalogSnapshot(
[availableHull, unavailableHull, stationHull, dockyardHull, cockpit, wrongController, weapon, shipSensor, stationSensor, cargo, docking, dockyardBay, capacitor, reactor],
[faction, foreign], Array.Empty<AetheriaRuntimeNameFile>());
var fallbackCatalog = new AetheriaRuntimeCatalogSnapshot(
[availableHull, unavailableHull, stationHull, cockpit, wrongController, weapon, shipSensor, cargo, docking, capacitor, reactor],
[faction, foreign], Array.Empty<AetheriaRuntimeNameFile>());
var homes = new Dictionary<string, int> { ["forge"] = 0, ["foreign"] = 1 };
var adjacency = new Dictionary<int, IReadOnlyList<int>> { [0] = [1], [1] = [0] };
var rareHull = Item("rare-hull", AetheriaRuntimeItemCategories.Hull, "forge", 100);
rareHull.HardpointType = availableHull.HardpointType;
rareHull.HullType = availableHull.HullType;
rareHull.ShapeWidth = availableHull.ShapeWidth;
rareHull.ShapeHeight = availableHull.ShapeHeight;
rareHull.OccupiedCells = availableHull.OccupiedCells;
rareHull.ShapeCells = availableHull.ShapeCells;
rareHull.Hardpoints = availableHull.Hardpoints;
rareHull.Tags = ["rarity:rare"];
var rarityCatalog = new AetheriaRuntimeCatalogSnapshot(
[availableHull, rareHull, cockpit, wrongController, weapon, shipSensor, cargo, capacitor, reactor],
[faction], Array.Empty<AetheriaRuntimeNameFile>());
var first = new AetheriaDaemonLoadoutGenerator(catalog, 42, 0, homes, adjacency).Build("ship", "forge");
var second = new AetheriaDaemonLoadoutGenerator(catalog, 42, 0, homes, adjacency).Build("ship", "forge");
var station = new AetheriaDaemonLoadoutGenerator(catalog, 84, 0, homes, adjacency)
.Build("station", "forge", "station-hull");
var fallbackStation = new AetheriaDaemonLoadoutGenerator(fallbackCatalog, 84, 0, homes, adjacency)
.Build("station", "forge", "station-hull");
var dockyard = new AetheriaDaemonLoadoutGenerator(catalog, 85, 0, homes, adjacency)
.Build("station", "forge", "dockyard-hull");
var uninterruptedForge = new AetheriaDaemonLoadoutGenerator(catalog, 101, 0, homes, adjacency);
var interleavedForge = new AetheriaDaemonLoadoutGenerator(catalog, 101, 0, homes, adjacency);
var foreignStream = new AetheriaDaemonLoadoutGenerator(catalog, 202, 1, homes, adjacency);
var uninterruptedSequence = new[]
{
uninterruptedForge.Build("ship", "forge"),
uninterruptedForge.Build("ship", "forge")
};
var interleavedFirst = interleavedForge.Build("ship", "forge");
foreignStream.Build("ship", "foreign");
var interleavedSecond = interleavedForge.Build("ship", "forge");
var rarityGenerator = new AetheriaDaemonLoadoutGenerator(
rarityCatalog, 9001, 0, homes, adjacency);
var rareSelections = Enumerable.Range(0, 2000)
.Select(_ => rarityGenerator.Build("ship", "forge").HullItemKey)
.Count(key => key == "rare-hull");
RequireEqual("available-hull", first.HullItemKey,
"loadout generation must exclude manufacturers outside the faction allegiance graph");
Require(first.Equipment.Any(value => value.ItemKey == "cockpit") &&
first.Equipment.All(value => value.ItemKey != "cheap-turret-controller"),
"ship control hardpoints must require the cockpit role even when the wrong controller is cheaper");
Require(first.Equipment.Any(value => value.ItemKey == "cannon") &&
first.CargoBays.Any(value => value.ItemKey == "cargo-bay") &&
first.Equipment.Any(value => value.ItemKey == "capacitor") &&
first.Equipment.Any(value => value.ItemKey == "ship-sensor") &&
first.Equipment.All(value => value.ItemKey != "station-sensor"),
"generated ships must fit hardpoint equipment and reject station-sized sensor gear");
RequireEqual("Clockwise", first.Equipment.Single(value => value.ItemKey == "cannon").Rotation,
"generated equipment must preserve the placement rotation used by hardpoint fitting");
Require(first.HullItemKey == second.HullItemKey &&
first.Equipment.Select(value => value.ItemKey).SequenceEqual(second.Equipment.Select(value => value.ItemKey)),
"same seed, map, faction and catalog must produce the same loadout");
Require(first.Cargo.Length == 0 && first.Receipt.Selections.All(value => value.Role != "cargo"),
"ordinary generated ships must not acquire cargo from non-canonical scenario aliases");
Require(first.Receipt.Seed == 42 && first.Receipt.SourceZoneIndex == 0 &&
first.Receipt.AvailabilityFactionKey == "forge" &&
first.Receipt.Selections.Any(value => value.Role == "hull" && value.ItemKey == "available-hull" &&
value.ManufacturerKey == "forge" && value.ManufacturerDistance == 1 && value.Allegiance == 1),
"generated loadouts must carry daemon-authored source and selection provenance");
Require(rareSelections > 0 && rareSelections < 200,
$"rare catalog candidates must remain possible without becoming a default loadout path (selected {rareSelections}/2000)");
Require(station.DockingBays.Any(value => value.ItemKey == "docking-bay") &&
station.CargoBays.Any(value => value.ItemKey == "cargo-bay") &&
station.Equipment.Any(value => value.ItemKey == "capacitor") &&
station.Equipment.Any(value => value.ItemKey == "cheap-turret-controller") &&
station.Equipment.Any(value => value.ItemKey == "station-sensor") &&
station.Equipment.All(value => value.ItemKey != "ship-sensor"),
"station generation must fill a large sensor hardpoint with the largest available compatible array");
Require(fallbackStation.Equipment.Any(value => value.ItemKey == "ship-sensor"),
"a smaller sensor must remain a valid fallback when it fits inside a larger same-type hardpoint");
Require(fallbackStation.DockingBays.Single().ItemKey == "docking-bay",
"a station without dedicated docking mounts must retain the canonical non-dedicated free-space bay path");
RequireEqual("dockyard-hull", dockyard.HullItemKey,
"an authored station variant must be selectable without bypassing hull generation");
RequireEqual(12, dockyard.DockingBays.Length,
"the dockyard hull must fill every authored docking hardpoint through normal loadout generation");
Require(dockyard.DockingBays.All(value => value.ItemKey == "dockyard-bay"),
"dedicated docking hardpoints must only accept the matching docking-bay equipment type");
Require(station.Cargo.Length > 0 && station.Cargo.Length <= cargo.InteriorOccupiedCells &&
station.Cargo.All(value => value.Item.ItemKey != "cheap-foreign-hull" &&
catalog.FindItem(value.Item.ItemKey) != null) &&
station.Receipt.Selections.Where(value => value.Role == "cargo")
.Select(value => value.ItemKey)
.SequenceEqual(station.Cargo.Select(value => value.Item.ItemKey)),
"station inventory draws must use canonical catalog keys, respect cargo capacity, exclude unavailable manufacturers, and preserve exact provenance");
Require(LoadoutKeys(uninterruptedSequence[0]).SequenceEqual(LoadoutKeys(interleavedFirst)) &&
LoadoutKeys(uninterruptedSequence[1]).SequenceEqual(LoadoutKeys(interleavedSecond)),
"generation in another faction stream must not perturb this faction's continuing loadout sequence");
}
private static IEnumerable<string> LoadoutKeys(AetheriaDaemonLoadout loadout) =>
new[] { loadout.HullItemKey }
.Concat(loadout.Equipment.Select(value => value.ItemKey))
.Concat(loadout.CargoBays.Select(value => value.ItemKey))
.Concat(loadout.DockingBays.Select(value => value.ItemKey))
.Concat(loadout.Cargo.Select(value => value.Item.ItemKey));
private static AetheriaRuntimeBehaviorValue PerformanceStat(double value) => new(
"performance-stat",
"",
0,
false,
"",
"",
[Number(value), Number(value), Number(0), Number(0), Number(0)],
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue Number(double value) => new(
"number",
"",
value,
false,
"",
"",
Array.Empty<AetheriaRuntimeBehaviorValue>(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue StringValue(string value) => new(
"string", value, 0, false, "", "",
Array.Empty<AetheriaRuntimeBehaviorValue>(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue BoolValue(bool value) => new(
"bool", "", 0, value, "", "",
Array.Empty<AetheriaRuntimeBehaviorValue>(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue ItemKeyValue(string value) => new(
"item-key", "", 0, false, "", value,
Array.Empty<AetheriaRuntimeBehaviorValue>(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue StatReference(string target, string stat) => new(
"stat-reference", "", 0, false, "", "",
[StringValue(""), StringValue(target), StringValue(stat)],
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue Vector(params double[] values) => new(
"float3",
"",
0,
false,
"",
"",
values.Select(Number).ToArray(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorValue Curve(params AetheriaRuntimeCurveKey[] keys) => new(
"bezier-curve",
"",
0,
false,
"",
"",
[new AetheriaRuntimeBehaviorValue(
"array",
"",
0,
false,
"",
"",
keys.Select(key => Vector(key.Time, key.Value, key.InTangent, key.OutTangent)).ToArray(),
Array.Empty<AetheriaRuntimeBehaviorMapEntry>())],
Array.Empty<AetheriaRuntimeBehaviorMapEntry>());
private static AetheriaRuntimeBehaviorPayload AetherDrivePayload(
double couplingEfficiency = 1,
double energyDraw = 0) => new(
0,
AetheriaRuntimeBehaviorKinds.AetherDrive,
0,
[
new AetheriaRuntimeBehaviorField(1, Vector(100, 100, 100)),
new AetheriaRuntimeBehaviorField(2, Vector(10, 10, 0.1)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(100)),
new AetheriaRuntimeBehaviorField(4, Vector(10, 10, 10)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(couplingEfficiency)),
new AetheriaRuntimeBehaviorField(7, PerformanceStat(10000)),
new AetheriaRuntimeBehaviorField(8, Curve(
new AetheriaRuntimeCurveKey(0, 1, 0, 0),
new AetheriaRuntimeCurveKey(1, 1, 0, 0))),
new AetheriaRuntimeBehaviorField(9, PerformanceStat(energyDraw)),
new AetheriaRuntimeBehaviorField(10, PerformanceStat(0))
]);
private static AetheriaRuntimeBehaviorPayload ThrusterPayload() => new(
0,
AetheriaRuntimeBehaviorKinds.Thruster,
0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(90)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(0))
]);
private static AetheriaRuntimeCatalogSnapshot EquipThrusterBank(
IEnumerable<AetheriaRuntimeEntitySnapshotCommit> entities,
params AetheriaRuntimeCatalogItem[] additionalItems)
{
var hull = HullCatalogItem("smoke-standard-thruster-hull", 5, 5, 0);
hull.HullDrag = 1;
var thruster = CatalogItem("smoke-directional-thruster", ThrusterPayload());
var placements = new[]
{
(0, 2, "None"), (4, 2, "None"),
(0, 1, "Half"), (4, 1, "Half"),
(2, 0, "CounterClockwise"), (2, 4, "CounterClockwise"),
(1, 0, "Clockwise"), (1, 4, "Clockwise")
};
foreach (var entity in entities.Where(entity => entity.Kind == "ship"))
{
if (string.IsNullOrWhiteSpace(entity.HullItemKey))
entity.HullItemKey = hull.ItemKey;
entity.Equipment = (entity.Equipment ?? Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>())
.Concat(placements.Select(value => new AetheriaRuntimeLoadoutItemSlotCommit
{
X = value.Item1,
Y = value.Item2,
Rotation = value.Item3,
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = thruster.ItemKey,
Quantity = 1,
Durability = 1,
Enabled = true
}
}))
.ToArray();
}
return new AetheriaRuntimeCatalogSnapshot(
additionalItems.Concat([hull, thruster]).ToArray(), [], []);
}
private static void DaemonNativeDockyardMaterializesTypedCatalog()
{
Require(AetheriaDocumentRegistry.DocumentTypes.Contains(
typeof(GameCult.Eve.PluginFields.EveFieldsSplatsDocument)),
"Aetheria's public registry must include every typed Eve Fields document the daemon persists");
const string zenithId = "82efc0a5-1ba5-4ff3-a281-b2e6e247521d";
const string mediumBayId = "3e930a2c-ac72-4385-98aa-1c5b0b90db46";
var root = Path.Combine(Path.GetTempPath(), $"aetheria-dockyard-catalog-{Guid.NewGuid():N}");
var statePath = Path.Combine(root, "aetheria.cc");
Directory.CreateDirectory(root);
try
{
using var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult();
node.MutableDocument<AetheriaItemDefinition>(AetheriaCatalogKeys.ItemDefinitionFromLegacyId(zenithId))
.ReplaceAsync(new AetheriaItemDefinition
{
Name = "Zenith",
LegacyId = zenithId,
Category = AetheriaRuntimeItemCategories.Hull,
HullType = "Station",
HardpointType = "Hull",
ManufacturerLegacyId = "11111111-1111-1111-1111-111111111111",
Price = 100,
Mass = 100,
ShapeWidth = 12,
ShapeHeight = 12,
ShapeCells = Enumerable.Range(0, 12).SelectMany(y => Enumerable.Range(0, 12)
.Select(x => new AetheriaShapeCell { X = x, Y = y })).ToArray(),
HullPrefab = "Assets/Prefabs/Zenith.prefab",
HullArmor = 4,
HullDrag = 2
}).GetAwaiter().GetResult();
node.MutableDocument<AetheriaItemDefinition>(AetheriaCatalogKeys.ItemDefinitionFromLegacyId(mediumBayId))
.ReplaceAsync(new AetheriaItemDefinition
{
Name = "Medium Docking Bay",
LegacyId = mediumBayId,
Category = AetheriaRuntimeItemCategories.DockingBay,
HardpointType = "Tool",
ManufacturerLegacyId = "11111111-1111-1111-1111-111111111111",
Price = 20,
ShapeWidth = 4,
ShapeHeight = 4,
OccupiedCells = 16,
ShapeCells = Enumerable.Range(0, 4).SelectMany(y => Enumerable.Range(0, 4)
.Select(x => new AetheriaShapeCell { X = x, Y = y })).ToArray(),
DockingMaxSizeX = 12,
DockingMaxSizeY = 12
}).GetAwaiter().GetResult();
Require(AetheriaDaemonNativeCatalog.EnsureAsync(node).GetAwaiter().GetResult(),
"daemon-native catalog must report its first materialization");
node.FlushAsync().GetAwaiter().GetResult();
node.RefreshRuntimeCatalogAsync().GetAwaiter().GetResult();
Require(!AetheriaDaemonNativeCatalog.EnsureAsync(node).GetAwaiter().GetResult(),
"daemon-native catalog must not rewrite an unchanged generated revision");
var catalog = node.RuntimeCatalog().Latest();
var hull = catalog.FindItem(AetheriaDaemonNativeCatalog.DockyardHullItemKey);
var bay = catalog.FindItem(AetheriaDaemonNativeCatalog.DockyardBayItemKey);
Require(hull != null && hull.HullType == "Station" && hull.ShapeWidth == 24 && hull.ShapeHeight == 24,
"daemon-native station content must materialize as a distinct typed hull definition");
RequireEqual(12, hull!.Hardpoints.Count(hardpoint => hardpoint.Type == "DockingBay"),
"dockyard capacity must be authored by twelve docking hardpoints on the hull");
Require(bay != null && bay.Category == AetheriaRuntimeItemCategories.DockingBay &&
bay.HardpointType == "DockingBay" && bay.DockingMaxSizeX == 12 && bay.DockingMaxSizeY == 12,
"dockyard berths must remain typed docking-bay equipment with the source bay's ship-size contract");
var dockyardEntity = Entity(0, 0, "neutral");
dockyardEntity.Kind = "station";
dockyardEntity.HullItemKey = hull.ItemKey;
var zenithItemKey = $"aetheria.item_definition:legacy:{zenithId}";
var zenithAssetKey = AetheriaRuntimeAssets.HullPrefabAssetKey(zenithItemKey);
RequireEqual(zenithAssetKey, AetheriaRuntimeAssets.ResolveEntityPrefabAssetRef(dockyardEntity, catalog),
"daemon-native hull variants must explicitly reuse their provider-owned presentation hull asset");
var manifest = AetheriaRuntimeAssets.ProjectManifest(catalog);
Require(manifest.Assets.Any(asset => asset.Ref.AssetKey == zenithAssetKey),
"provider manifest must advertise the canonical Zenith presentation asset used by the dockyard");
Require(!manifest.Assets.Any(asset => asset.Ref.AssetKey ==
AetheriaRuntimeAssets.HullPrefabAssetKey(hull.ItemKey)),
"provider manifest must not invent an unbaked prefab for a hull with an explicit presentation alias");
}
finally
{
if (Directory.Exists(root)) Directory.Delete(root, recursive: true);
}
}
private static AetheriaRuntimeCatalogSnapshot EquipAetherDrive(
IEnumerable<AetheriaRuntimeEntitySnapshotCommit> entities,
params AetheriaRuntimeCatalogItem[] additionalItems)
{
var hull = HullCatalogItem("smoke-rare-aether-drive-hull", 3, 3, 0);
hull.Hardpoints =
[
new AetheriaRuntimeHardpoint(
"AetherDrive", 1, 1, 1, 1, 1,
[new AetheriaRuntimeShapeCell(0, 0)], "", "None", 0)
];
var drive = CatalogItem("smoke-aether-drive", AetherDrivePayload());
drive.HardpointType = "AetherDrive";
foreach (var entity in entities.Where(entity => entity.Kind == "ship"))
{
if (string.IsNullOrWhiteSpace(entity.HullItemKey))
entity.HullItemKey = hull.ItemKey;
entity.Equipment = (entity.Equipment ?? Array.Empty<AetheriaRuntimeLoadoutItemSlotCommit>())
.Concat([new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 1,
Y = 1,
Rotation = "None",
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = drive.ItemKey,
Quantity = 1,
Durability = 1,
Enabled = true
}
}])
.ToArray();
}
return new AetheriaRuntimeCatalogSnapshot(
additionalItems.Concat([hull, drive]).ToArray(), [], []);
}
private static AetheriaRuntimeBehaviorPayload CapacitorPayload(double capacity, double efficiency) => new(
0, "Capacitor", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(capacity)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(efficiency))
]);
private static AetheriaRuntimeBehaviorPayload SensorPayload(
double sensitivity = 1,
double pingBoost = 1,
double pingEnergy = 0,
double pingVisibility = 3,
double pingRange = 200,
double pingCooldown = 1,
double pingDuration = 2,
double pingRadiusExponent = 0.5) => new(
0, AetheriaRuntimeBehaviorKinds.Sensor, 0,
[
new AetheriaRuntimeBehaviorField(3, PerformanceStat(sensitivity)),
new AetheriaRuntimeBehaviorField(4, Curve(
new AetheriaRuntimeCurveKey(0, 1, 0, 0),
new AetheriaRuntimeCurveKey(1, 1, 0, 0))),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(pingBoost)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(pingEnergy)),
new AetheriaRuntimeBehaviorField(7, PerformanceStat(pingVisibility)),
new AetheriaRuntimeBehaviorField(8, PerformanceStat(pingRange)),
new AetheriaRuntimeBehaviorField(9, PerformanceStat(pingCooldown)),
new AetheriaRuntimeBehaviorField(10, Number(pingDuration)),
new AetheriaRuntimeBehaviorField(11, Number(pingRadiusExponent))
]);
private static AetheriaRuntimeCatalogItem CatalogItem(string itemKey, params AetheriaRuntimeBehaviorPayload[] payloads) => new(
itemKey, itemKey, "equipment", "", "", 0, 1, 1, 1, 1,
1, 1, 1, Array.Empty<AetheriaRuntimeShapeCell>(),
0, 0, 0, Array.Empty<AetheriaRuntimeShapeCell>(),
Array.Empty<AetheriaRuntimeHardpoint>(), payloads,
"utility", "", payloads.Select(payload => payload.Kind).ToArray(),
1, false, 0, 1, "", "", "", "", "",
0, 1000, Array.Empty<AetheriaRuntimeCurveKey>(), "", 1, 0, 0, 0, false,
0, 0, "", Array.Empty<AetheriaRuntimeAudioStat>(), Array.Empty<AetheriaRuntimeCurveKey>(), "", "");
private static AetheriaRuntimeCatalogItem WearableWeaponCatalogItem(
string itemKey, params AetheriaRuntimeBehaviorPayload[] payloads)
{
var item = CatalogItem(itemKey, payloads);
item.Durability = 1;
item.ThermalResilience = 1;
item.MinimumTemperature = 0;
item.MaximumTemperature = 1;
item.ThermalPerformanceCurveKeys =
[
new AetheriaRuntimeCurveKey(0, 0.9, 0, 0),
new AetheriaRuntimeCurveKey(1, 0.9, 0, 0)
];
return item;
}
private static AetheriaRuntimeCatalogItem HullCatalogItem(string itemKey, int width, int height, double armor)
{
var cells = Enumerable.Range(0, height)
.SelectMany(y => Enumerable.Range(0, width).Select(x => new AetheriaRuntimeShapeCell(x, y)))
.ToArray();
return new AetheriaRuntimeCatalogItem(
itemKey, itemKey, "hull", "", "", 0, 1, 1, 1, 1,
width, height, cells.Length, cells,
width, height, cells.Length, cells,
Array.Empty<AetheriaRuntimeHardpoint>(), Array.Empty<AetheriaRuntimeBehaviorPayload>(),
"", "ship", Array.Empty<string>(),
1, false, 0, 1, "", "", "", "", "",
0, 1000, Array.Empty<AetheriaRuntimeCurveKey>(), "", 1, 0, armor, 0, false,
0, 0, "", Array.Empty<AetheriaRuntimeAudioStat>(), Array.Empty<AetheriaRuntimeCurveKey>(), "", "");
}
private static AetheriaRuntimeDaemonSimulationSettings LootSettings(double probability)
{
var value = AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault;
return new AetheriaRuntimeDaemonSimulationSettings(
value.PawnSpeed, value.RaiderSpeed, value.AttackRange, value.AttackHoldRatio,
value.PawnProjectileDamage, value.RaiderProjectileDamage, value.WeaponCooldownSeconds,
value.ProjectileSpeed, value.ProjectileRadius, value.ProjectileLifetimeSeconds,
value.ProjectileSpawnOffset, value.ProjectileHeatScale, value.HeatDissipationPerSecond,
value.StationSensorRange, value.EntitySensorRange, value.PlayerStationHull,
value.HostileStationHull, value.PlayerEntityHull, value.RaiderEntityHull,
value.StationShield, value.EntityShield, value.WeaponLockSpeed,
value.WeaponLockSensorImpact, value.WeaponLockAngleDegrees,
value.WeaponLockDirectionImpact, value.WeaponLockDecayPerSecond,
probability, value.LootDropVelocity, value.PickupLifetimeSeconds);
}
private static void AgentPatrolsHistoricalOrbitCircuitThroughMovementCommands()
{
var agent = Entity(0, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Patrol];
var catalog = EquipThrusterBank([agent]);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-patrol-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [agent],
Orbits =
[
new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "orbit:east", FixedPositionX = 20 },
new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "orbit:west", FixedPositionX = -20 }
],
Bodies =
[
new AetheriaRuntimeBodySnapshotCommit { BodyKey = "body:east", OrbitKey = "orbit:east", Kind = "planet" },
new AetheriaRuntimeBodySnapshotCommit { BodyKey = "body:west", OrbitKey = "orbit:west", Kind = "planet" }
]
}
]
};
var issue = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask,
"commander-smoke",
"starbridge-smoke",
0,
"");
issue.CommandId = "issue-patrol";
issue.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = "patrol-orbits",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Patrol,
Priority = 10,
ZoneIndex = 0,
CompletionRadius = 5,
TargetOrbitKeys = ["orbit:east", "orbit:west"]
};
var sawWestLeg = false;
var sawReturnToEast = false;
var physics = NewPhysics();
for (var frame = 0; frame < 100; frame++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-patrol-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
Catalog = catalog,
FrameId = frame,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frame * 0.1,
ObservedCommands = frame == 0 ? [issue] : Array.Empty<AetheriaRuntimeDaemonCommandDocument>(),
BuildPublications = false
});
Require(tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":move", StringComparison.Ordinal)),
"patrol controller must drive the shared movement command every tick");
var cursor = run.AgentTasks.Single().CircuitIndex;
sawWestLeg |= cursor == 1;
sawReturnToEast |= sawWestLeg && cursor == 0;
if (sawReturnToEast)
break;
}
Require(sawWestLeg && sawReturnToEast, "patrol must advance through and wrap its authored orbit circuit");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, run.AgentTasks.Single().Status,
"patrol is persistent work and must remain assigned after one circuit");
}
private static void SecurityZonesOwnFossilTrespasserHostility()
{
var guard = Entity(0, 0, "owners");
guard.SecurityLevel = 2;
guard.SecurityRadius = 100;
var visitor = Entity(1, 10, "visitors");
guard.Contacts = [new AetheriaRuntimeEntityContactCommit { TargetEntityIndex = 1, InfoGathered = 1 }];
visitor.Contacts = [new AetheriaRuntimeEntityContactCommit { TargetEntityIndex = 0, InfoGathered = 1 }];
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
OwnerFactionIndex = 0,
Entities = [guard, visitor]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
CurrentEntityKey = "zone.0.entity.1",
FactionRelationships =
[
new AetheriaRuntimeFactionRelationshipCommit
{
FactionKey = "owners", Relationship = "Neutral"
}
],
Zones = [zone]
};
var catalog = new AetheriaRuntimeCatalogSnapshot([], [new AetheriaRuntimeCorporation(
"owners", "Owners", "Owners", "", "", "", 4, 0,
Array.Empty<AetheriaRuntimeCorporationAllegiance>())], []);
AetheriaRuntimeSensorSimulation.StepZone(
run, zone, [guard, visitor], null, catalog, 0.1, 0, 0.5, 1);
Require(guard.Contacts.Single().Hostile && visitor.Contacts.Single().Hostile,
"a neutral visitor inside an owner critical-security field must be mutually hostile");
visitor.PositionX = 1000;
AetheriaRuntimeSensorSimulation.StepZone(
run, zone, [guard, visitor], null, catalog, 0.1, 0, 0.5, 2);
Require(!guard.Contacts.Single().Hostile && !visitor.Contacts.Single().Hostile,
"the same neutral visitor outside security fields must cease being hostile");
}
private static void PatrolCombatPreemptsAndResumesTheSameCircuit()
{
var patrol = Entity(0, 0, "owners");
patrol.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Patrol];
patrol.WeaponGroups = [new[] { 0 }];
patrol.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = "patrol-cannon", Quality = 1, Durability = 1, Enabled = true
}
}
];
patrol.Contacts =
[
new AetheriaRuntimeEntityContactCommit
{
TargetEntityIndex = 1, InfoGathered = 1, Visible = true, Hostile = true
}
];
var hostile = Entity(1, 0, "visitors");
hostile.PositionZ = 50;
var weapon = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.InstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.5))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[WearableWeaponCatalogItem("patrol-cannon", weapon)], [], []);
var task = new AetheriaRuntimeAgentTaskCommit
{
TaskId = "patrol-combat",
CorporationKey = "owners",
TaskType = AetheriaRuntimeAgentTaskTypes.Patrol,
Status = AetheriaRuntimeAgentTaskStatuses.Assigned,
ZoneIndex = 0,
AssignedEntityIndex = 0,
CompletionRadius = 5,
TargetOrbitKeys = ["orbit:east", "orbit:west"],
CircuitIndex = 1
};
patrol.AssignedAgentTaskId = task.TaskId;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "patrol-combat-smoke",
CurrentEntityKey = "",
AgentTasks = [task],
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [patrol, hostile],
Orbits =
[
new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "orbit:east", FixedPositionX = 20 },
new AetheriaRuntimeOrbitSnapshotCommit { OrbitKey = "orbit:west", FixedPositionX = -20 }
]
}
]
};
var combatCommands = AetheriaRuntimeAgentScheduler.AssignAndPlan(
run, 1, catalog, 0.1, AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault);
Require(combatCommands.Any(command =>
command.Kind == AetheriaRuntimeDaemonCommandKinds.SetTarget &&
AetheriaRuntimeRunCheckpointCommit.TryParseEntityKey(
command.TargetEntityKey, out var zoneIndex, out var entityIndex) &&
zoneIndex == 0 && entityIndex == 1),
"a visible hostile ship must pre-empt patrol movement through the shared target command");
Require(combatCommands.Any(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.FireWeaponGroup),
"an aligned in-range patrol agent must use the shared weapon command");
RequireEqual(1, task.CircuitIndex,
"opportunistic combat must not replace or advance the patrol circuit");
patrol.Contacts = Array.Empty<AetheriaRuntimeEntityContactCommit>();
var resumed = AetheriaRuntimeAgentScheduler.AssignAndPlan(
run, 2, catalog, 0.2, AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault);
Require(resumed.Any(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.SetMoveVector),
"loss of hostile visibility must resume the same patrol circuit");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, task.Status,
"opportunistic combat must never complete or fail the patrol task");
}
private static void RejectedHaulTransferDoesNotAdvanceTask()
{
var origin = Entity(0, 0, "workers");
origin.CargoContents = [Cargo(("ore", 2, 0, 0))];
var agent = Entity(1, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Haul];
agent.CargoContents = Array.Empty<AetheriaRuntimeCargoBayLoadoutCommit>();
var destination = Entity(2, 20, "workers");
destination.CargoContents = [Cargo()];
var task = new AetheriaRuntimeAgentTaskCommit
{
TaskId = "rejected-haul",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Haul,
Priority = 1,
ZoneIndex = 0,
OriginEntityIndex = 0,
TargetEntityIndex = 2,
ItemKey = "ore",
RequestedQuantity = 1,
CompletionRadius = 5,
Phase = "pickup"
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "rejected-haul-smoke",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [origin, agent, destination] }],
AgentTasks = [task]
};
var planned = AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 1);
var reduced = AetheriaRuntimeDaemonOperations.Execute(run, planned);
AetheriaRuntimeAgentScheduler.Reconcile(run, 1, reduced.AppliedCommandIds, reduced.RejectedCommandIds);
Require(reduced.RejectedCommandIds.Any(id => id.EndsWith(":pickup", StringComparison.Ordinal)),
"invalid pickup must be rejected by the normal cargo reducer");
RequireEqual("pickup", task.Phase, "rejected pickup must not advance the haul task");
RequireEqual(0, task.PendingQuantity, "rejected pickup must clear pending transfer state");
RequireEqual(2, CargoQuantity(origin, "ore"), "rejected pickup must leave origin cargo untouched");
}
private static void AgentCompletesHaulTaskThroughMovementAndCargoCommands()
{
var cargoBay = CatalogItem("haul-cargo-bay");
cargoBay.Category = AetheriaRuntimeItemCategories.CargoBay;
cargoBay.ShapeWidth = 1;
cargoBay.ShapeHeight = 1;
cargoBay.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
cargoBay.InteriorShapeWidth = 4;
cargoBay.InteriorShapeHeight = 4;
cargoBay.InteriorOccupiedCells = 16;
cargoBay.InteriorShapeCells = Enumerable.Range(0, 4)
.SelectMany(y => Enumerable.Range(0, 4).Select(x => new AetheriaRuntimeShapeCell(x, y)))
.ToArray();
var ore = CatalogItem("ore");
ore.Category = AetheriaRuntimeItemCategories.SimpleCommodity;
ore.ShapeWidth = 1;
ore.ShapeHeight = 1;
ore.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
ore.Stackable = true;
ore.MaxStack = 100;
var catalog = new AetheriaRuntimeCatalogSnapshot([cargoBay, ore], [], []);
AetheriaRuntimeLoadoutItemSlotCommit InstalledCargoBay() => new()
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
};
var origin = Entity(0, 0, "workers");
origin.Kind = "station";
origin.CargoBays = [InstalledCargoBay()];
origin.CargoContents = [Cargo(("ore", 5, 2, 3))];
var agent = Entity(1, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Haul];
agent.CargoBays = [InstalledCargoBay()];
agent.CargoContents = [Cargo()];
var destination = Entity(2, 50, "workers");
destination.Kind = "station";
destination.CargoBays = [InstalledCargoBay()];
destination.CargoContents = [Cargo()];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-haul-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [origin, agent, destination] }]
};
var issue = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask,
"commander-smoke",
"starbridge-smoke",
0,
"");
issue.CommandId = "issue-haul";
issue.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = "haul-ore",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Haul,
Priority = 40,
ZoneIndex = 0,
OriginEntityIndex = 0,
TargetEntityIndex = 2,
ItemKey = "ore",
Quantity = 3,
CompletionRadius = 5
};
var sawPickup = false;
var sawDelivery = false;
for (var frame = 0; frame < 30; frame++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-haul-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = NewPhysics(),
FrameId = frame,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frame * 0.1,
ObservedCommands = frame == 0 ? [issue] : Array.Empty<AetheriaRuntimeDaemonCommandDocument>(),
Catalog = catalog,
BuildPublications = false
});
sawPickup |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":pickup", StringComparison.Ordinal));
sawDelivery |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":delivery", StringComparison.Ordinal));
if (string.Equals(run.AgentTasks.Single().Status, AetheriaRuntimeAgentTaskStatuses.Completed, StringComparison.Ordinal))
break;
}
Require(sawPickup && sawDelivery, "haul task must use accepted pickup and delivery cargo commands");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, run.AgentTasks.Single().Status,
"haul task must complete only after accepted delivery");
RequireEqual(2, CargoQuantity(origin, "ore"), "origin must retain the unrequested stack quantity");
RequireEqual(3, CargoQuantity(destination, "ore"), "destination must receive exactly the requested quantity");
RequireEqual(0, CargoQuantity(agent, "ore"), "hauler must finish with no in-transit cargo");
}
private static AetheriaRuntimeCargoBayLoadoutCommit Cargo(params (string ItemKey, int Quantity, int X, int Y)[] items) => new()
{
Items = items.Select(item => new AetheriaRuntimeLoadoutItemSlotCommit
{
X = item.X,
Y = item.Y,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = item.ItemKey, Quantity = item.Quantity }
}).ToArray()
};
private static int CargoQuantity(AetheriaRuntimeEntitySnapshotCommit entity, string itemKey) =>
(entity.CargoContents ?? Array.Empty<AetheriaRuntimeCargoBayLoadoutCommit>())
.SelectMany(bay => bay.Items)
.Where(slot => string.Equals(slot.Item.ItemKey, itemKey, StringComparison.Ordinal))
.Sum(slot => slot.Item.Quantity);
private static void AgentCompletesAttackTaskThroughTargetFireAndYmir()
{
var agent = Entity(0, 0, "player");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Attack];
agent.WeaponGroups = [new[] { 0 }];
agent.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = "test-lock-cannon", Quality = 1, Durability = 1, Enabled = true
}
},
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-attack-capacitor", Quality = 1, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "test-attack-sensor", Quality = 1, Durability = 1, Enabled = true } }
];
agent.BehaviorStates =
[
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind, OwnerIndex = 1, BehaviorIndex = 0,
BehaviorKind = "Capacitor", CapacitorCharge = 50, CapacitorCapacity = 50, CapacitorEfficiency = 1
}
];
agent.CargoContents = [Cargo(("test-ammo", 2, 0, 0))];
var weaponPayload = new AetheriaRuntimeBehaviorPayload(
0,
"LockWeapon",
0,
[
new AetheriaRuntimeBehaviorField(1, Number(1)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(7)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(1.5)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(145)),
new AetheriaRuntimeBehaviorField(9, PerformanceStat(5)),
new AetheriaRuntimeBehaviorField(10, PerformanceStat(3)),
new AetheriaRuntimeBehaviorField(12, new AetheriaRuntimeBehaviorValue(
"item-key", "", 0, false, "", "test-ammo",
Array.Empty<AetheriaRuntimeBehaviorValue>(), Array.Empty<AetheriaRuntimeBehaviorMapEntry>())),
new AetheriaRuntimeBehaviorField(13, Number(3)),
new AetheriaRuntimeBehaviorField(14, Number(0.3)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(330)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(3)),
new AetheriaRuntimeBehaviorField(18, PerformanceStat(0.3)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.2)),
new AetheriaRuntimeBehaviorField(21, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(22, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(45)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1))
]);
var catalog = EquipThrusterBank(
[agent],
CatalogItem("test-lock-cannon", weaponPayload),
CatalogItem("test-attack-capacitor", CapacitorPayload(50, 1)),
CatalogItem("test-attack-sensor", SensorPayload()));
var target = Entity(1, 105, "raider");
target.Kind = "station";
target.Visibility = 10;
target.StatGrids = [Grid("hull", 14), Grid("shield", 0), Grid("heat", 0)];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-attack-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent, target] }]
};
var issue = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask,
"commander-smoke",
"starbridge-smoke",
0,
"");
issue.CommandId = "issue-attack";
issue.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = "attack-raider",
CorporationKey = "player",
TaskType = AetheriaRuntimeAgentTaskTypes.Attack,
Priority = 100,
ZoneIndex = 0,
TargetEntityIndex = 1,
CompletionRadius = 25,
WeaponGroup = 0
};
var sawTargetCommand = false;
var sawFireCommand = false;
var sawPartialLock = false;
var lockTrace = new List<double>();
var attackTrace = new List<string>();
var firstLaunchFrame = -1;
var physics = NewPhysics();
for (var frame = 0; frame < 60; frame++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-attack-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = frame,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frame * 0.1,
ObservedCommands = frame == 0 ? [issue] : Array.Empty<AetheriaRuntimeDaemonCommandDocument>(),
Catalog = catalog,
BuildPublications = false
});
sawTargetCommand |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":target", StringComparison.Ordinal));
sawFireCommand |= tick.OperationResult.AppliedCommandIds.Any(id => id.Contains(":fire-", StringComparison.Ordinal));
var weapon = (agent.WeaponStates ?? Array.Empty<AetheriaRuntimeWeaponStateCommit>())
.Single(value => value.OwnerKind == AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind);
lockTrace.Add(weapon.LockProgress);
attackTrace.Add($"{frame}:{agent.TargetEntityIndex}:{agent.PositionX:0.##},{agent.PositionZ:0.##}:{agent.DirectionX:0.##},{agent.DirectionY:0.##}:{weapon.LockTargetEntityIndex}");
sawPartialLock |= weapon.LockProgress > 0 && weapon.LockProgress < 0.99;
if (firstLaunchFrame < 0 && run.GameEvents.Any(value => value.Kind == "shot.committed"))
firstLaunchFrame = frame;
if (string.Equals(run.AgentTasks.Single().Status, AetheriaRuntimeAgentTaskStatuses.Completed, StringComparison.Ordinal))
break;
}
Require(sawTargetCommand, "attack agent must target through the shared target command");
Require(sawFireCommand, "attack agent must fire through the shared weapon-group command");
Require(sawPartialLock,
$"attack agent must acquire a persisted partial weapon lock before firing; locks={string.Join(',', lockTrace.Select(value => value.ToString("0.###", CultureInfo.InvariantCulture)))} states={string.Join('|', attackTrace)}");
Require(firstLaunchFrame > 1, "accepted fire intent must not bypass progressive weapon lock acquisition");
Require(run.GameEvents.Any(value => value.Kind == "shot.committed" && value.SourceEntityIndex == agent.EntityIndex),
"accepted fire control must emit authoritative shot commitment chronology");
Require(run.GameEvents.Any(value => value.Kind == "shot.committed" &&
value.ItemKey == "test-lock-cannon" && Math.Abs(value.ScalarValue - (7.0 / 3.0)) < 0.000001),
"daemon combat must divide authored damage across the configured burst rounds");
Require((agent.WeaponStates ?? Array.Empty<AetheriaRuntimeWeaponStateCommit>()).Any(value => value.OwnerKind == AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind &&
value.OwnerIndex == 0 && value.BehaviorKind == "LockWeapon"),
"weapon progress must belong to the equipped authored behavior instead of a synthetic entity weapon");
var authoredWeapon = agent.WeaponStates.Single(value =>
value.OwnerKind == AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind);
RequireEqual(0, authoredWeapon.Ammo,
"two three-round authored bursts must consume both complete magazines");
RequireNear(40, agent.BehaviorStates.Single(value => value.BehaviorKind == "Capacitor").CapacitorCharge,
0.000001, "two authored bursts must drain exactly their combined energy from canonical capacitor state");
RequireEqual(1, CargoQuantity(agent, "test-ammo"),
"one empty magazine must consume exactly one reserve cargo commodity before reload");
Require(run.GameEvents.Count(value => value.Kind == "shot.committed" && value.ItemKey == "test-lock-cannon") >= 6,
"two authored bursts must commit all six due rounds through the shot resolver");
Require(run.ShotReceipts.Count(value => value.WeaponItemKey == "test-lock-cannon") >= 6 &&
run.ShotReceipts.All(value => value.Hit && value.HullAppliedDamage >= 0 &&
value.HullAppliedDamage <= value.NominalDamage && value.ShieldAbsorbedDamage == 0 &&
value.DamageType == "Corrosive" && Math.Abs(value.Penetration - 1.5) < 0.000001 &&
Math.Abs(value.DamageSpread - 2) < 0.000001),
"authored rounds must retain damage type, penetration, spread, and exact damage receipts before presentation travel");
Require(run.ShotReceipts.Select(value => value.ShotId).Distinct(StringComparer.Ordinal).Count() ==
run.ShotReceipts.Count,
"every burst round must own a stable independent shot identity");
Require(run.GameEvents.Count(value => value.Kind == "weapon.reload.started") == 1 &&
run.GameEvents.Count(value => value.Kind == "weapon.reload.completed") == 1,
"empty magazine transition must publish one authoritative reload start and completion pair");
var reloadStarted = run.GameEvents.Single(value => value.Kind == "weapon.reload.started").FrameId;
var reloadCompleted = run.GameEvents.Single(value => value.Kind == "weapon.reload.completed").FrameId;
Require(reloadCompleted > reloadStarted && !run.GameEvents.Any(value =>
value.Kind == "shot.committed" && value.FrameId > reloadStarted && value.FrameId < reloadCompleted),
"reload interval must be a shot-free authoritative timeline, not a cosmetic client delay");
Require(!target.IsActive,
$"attack task must end through daemon shot resolution before presentation projectile contacts; hull={Stat(target, "hull"):0.###} " +
$"agent={agent.PositionX:0.###},{agent.PositionZ:0.###} target={target.PositionX:0.###},{target.PositionZ:0.###} " +
$"projectiles={run.Zones[0].PhysicalPayloads.Count} task={run.AgentTasks.Single().Status}");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, run.AgentTasks.Single().Status,
"attack task must complete when its target dies");
var shotSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 60, Run = run });
Require(Flatten(shotSurface.Surface.Root).Any(node => node.Kind == "shot.receipt" &&
node.Props["itemKey"] == "test-lock-cannon" && node.Props["outcome"] == "hit" &&
node.Props["presentationKind"] == "bolt" &&
node.Props.ContainsKey("presentationIntensity") &&
node.Props.ContainsKey("impactKind")),
"Eve must project inspectable deterministic shot receipts without reconstructing combat");
}
private static void AttackTaskAdmissionRejectsImpossibleWork()
{
var agent = Entity(0, 0, "player");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Attack];
agent.WeaponGroups = [new[] { 0 }];
var hostile = Entity(1, 100, "raider");
var friendly = Entity(2, 100, "player");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "attack-admission-smoke",
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent, hostile, friendly] }]
};
AetheriaRuntimeDaemonCommandDocument Command(string id, int targetIndex, int weaponGroup)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask, "commander-smoke", "starbridge-smoke", 0, "");
command.CommandId = id;
command.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = id,
CorporationKey = "player",
TaskType = AetheriaRuntimeAgentTaskTypes.Attack,
ZoneIndex = 0,
TargetEntityIndex = targetIndex,
WeaponGroup = weaponGroup
};
return command;
}
var result = AetheriaRuntimeDaemonOperations.Execute(run,
[
Command("missing-target", 99, 0),
Command("friendly-target", 2, 0),
Command("missing-group", 1, 1),
Command("valid-attack", 1, 0)
]);
Require(result.RejectedCommandIds.SequenceEqual(new[] { "missing-target", "friendly-target", "missing-group" }),
"attack admission must reject missing/friendly targets and unavailable weapon groups");
Require(result.AppliedCommandIds.SequenceEqual(new[] { "valid-attack" }),
"attack admission must accept executable hostile work");
Require(run.AgentTasks.Count == 1 && run.AgentTasks[0].TaskId == "valid-attack",
"rejected attack orders must not enter the durable corporation queue");
}
private static void AttackAgentControlsOptimumRangeThroughMovementLever()
{
var closing = AttackPlanAtDistance(250, facingTarget: false);
var closingMove = closing.Commands.Single(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.SetMoveVector);
Require(closingMove.DirectionX > 0.5 && closingMove.DirectionY < -0.5 && closingMove.ScalarValue > 0.99,
"loadout-derived long-range attack must blend closing motion with the fossil target-relative strafe");
var retreating = AttackPlanAtDistance(50, facingTarget: false);
var retreatingMove = retreating.Commands.Single(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.SetMoveVector);
Require(retreatingMove.DirectionX < -0.5 && retreatingMove.DirectionY < -0.5,
"loadout-derived short-range attack must blend opening motion with the fossil target-relative strafe");
var broadside = AttackPlanAtDistance(150, facingTarget: false);
Require(!broadside.Commands.Any(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.FireWeaponGroup),
"an attack agent must not fire a selected group through a hardpoint facing away from its aim");
var firing = AttackPlanAtDistance(150, facingTarget: true, targetVelocityZ: 8);
RequireEqual(1, firing.Task.WeaponGroup,
"agent combat must select the highest current range-DPS group from operational equipment");
var fire = firing.Commands.Single(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.FireWeaponGroup);
RequireEqual(1, fire.WeaponGroup,
"the daemon must fire the derived group instead of the task's stale authored group");
var look = firing.Commands.Single(command => command.Kind == AetheriaRuntimeDaemonCommandKinds.SetLookDirection);
Require(look.DirectionX > 0.9 && look.PositionZ > 0.05,
"projectile agent aim must lead target velocity through CultMath first-order intercept semantics");
}
private static (IReadOnlyList<AetheriaRuntimeDaemonCommandDocument> Commands, AetheriaRuntimeAgentTaskCommit Task)
AttackPlanAtDistance(double distance, bool facingTarget, double targetVelocityZ = 0)
{
var agent = Entity(0, 0, "workers");
agent.DirectionX = facingTarget ? 1 : 0;
agent.DirectionY = facingTarget ? 0 : 1;
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Attack];
agent.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Rotation = "None",
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "agent-short-weapon", Quality = 1, Durability = 1, Enabled = true }
},
new AetheriaRuntimeLoadoutItemSlotCommit
{
Rotation = "None",
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "agent-long-weapon", Quality = 1, Durability = 1, Enabled = true }
}
];
agent.WeaponGroups = [new[] { 0 }, new[] { 1 }];
var target = Entity(1, distance, "raiders");
target.DirectionY = 1;
target.VelocityY = targetVelocityZ;
var task = new AetheriaRuntimeAgentTaskCommit
{
TaskId = "range-control",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Attack,
ZoneIndex = 0,
TargetEntityIndex = 1,
WeaponGroup = 0
};
AetheriaRuntimeBehaviorPayload Weapon(double damage, double minimumRange, double range, double velocity) =>
new(0, AetheriaRuntimeBehaviorKinds.InstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(damage)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(minimumRange)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(range)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(velocity)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(1))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot(
[
CatalogItem("agent-short-weapon", Weapon(10, 5, 100, 80)),
CatalogItem("agent-long-weapon", Weapon(40, 25, 200, 100))
], [], []);
var run = new AetheriaRuntimeRunCheckpointCommit
{
GenerationSeed = 123,
RunId = "agent-range-control-smoke",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent, target] }],
AgentTasks = [task]
};
return (AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 1, catalog), task);
}
private static void SchedulerAssignsHighestPriorityCompatibleTask()
{
var agent = Entity(0, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var low = AgentTask("low", 1);
var high = AgentTask("high", 99);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-priority-smoke",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent] }],
AgentTasks = [low, high]
};
AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 4);
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, high.Status,
"highest priority compatible task must claim the available agent");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Queued, low.Status,
"lower priority task must remain queued when no controller remains");
RequireEqual("high", agent.AssignedAgentTaskId, "agent assignment must point at the selected task");
}
private static void SchedulerRequeuesTaskFromDeadAgent()
{
var dead = Entity(0, 0, "workers");
dead.IsActive = false;
dead.AssignedAgentTaskId = "recover-work";
var replacement = Entity(1, 0, "workers");
replacement.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var task = new AetheriaRuntimeAgentTaskCommit
{
TaskId = "recover-work",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Explore,
ZoneIndex = 0,
Status = AetheriaRuntimeAgentTaskStatuses.Assigned,
AssignedEntityIndex = 0,
TargetPositionX = 100,
DeliveredQuantity = 3,
RequestedQuantity = 5
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-recovery-smoke",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [dead, replacement] }],
AgentTasks = [task]
};
AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 8);
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, task.Status,
"work abandoned by an inactive agent must return to the corporation queue and be reassigned");
RequireEqual(replacement.EntityIndex, task.AssignedEntityIndex,
"compatible active replacement must receive abandoned work");
RequireEqual(3, task.DeliveredQuantity,
"reassignment must preserve authoritative task progress");
Require(string.IsNullOrWhiteSpace(dead.AssignedAgentTaskId),
"inactive agent must not retain reservation authority");
}
private static void SchedulerCollapsesDuplicateAssignmentMarkers()
{
var first = Entity(0, 0, "workers");
first.AssignedAgentTaskId = "single-owner";
var duplicate = Entity(1, 0, "workers");
duplicate.AssignedAgentTaskId = "single-owner";
duplicate.HelmForward = 1;
duplicate.TargetEntityIndex = 7;
var task = AgentTask("single-owner", 1);
task.Status = AetheriaRuntimeAgentTaskStatuses.Assigned;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-single-owner-smoke",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [first, duplicate] }],
AgentTasks = [task]
};
AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 9);
RequireEqual(1, run.Zones.SelectMany(zone => zone.Entities)
.Count(entity => string.Equals(entity.AssignedAgentTaskId, task.TaskId, StringComparison.Ordinal)),
"one task must have exactly one active carrier after reconciliation");
RequireEqual(first.EntityIndex, task.AssignedEntityIndex,
"duplicate assignment reconciliation must be deterministic");
RequireNear(0, duplicate.HelmForward, 0.000001,
"duplicate assignment reconciliation must release the losing scheduler controls");
RequireEqual(-1, duplicate.TargetEntityIndex,
"duplicate assignment reconciliation must release the losing scheduler target");
}
private static void CancellingAgentTaskReleasesSchedulerControls()
{
var agent = Entity(0, 0, "workers");
agent.AssignedAgentTaskId = "tow-station";
agent.HelmStrafe = 0.75;
agent.HelmForward = 1;
agent.TargetEntityIndex = 2;
agent.ChildEntityIndices = [2];
var station = Entity(2, 0, "workers");
station.Kind = "station";
var task = new AetheriaRuntimeAgentTaskCommit
{
TaskId = "tow-station",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Tow,
Status = AetheriaRuntimeAgentTaskStatuses.Assigned,
AssignedEntityIndex = 0,
TargetEntityIndex = 2,
PendingQuantity = 4,
Phase = "delivery"
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-cancel-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
AgentTasks = [task],
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [agent, Entity(1, 0, "workers"), station]
}
]
};
var cancel = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.CancelAgentTask,
"commander-smoke",
"starbridge-smoke",
12,
"");
cancel.CommandId = "cancel-tow-station";
cancel.AgentTask = new AetheriaRuntimeAgentTaskCommand { TaskId = task.TaskId };
var result = AetheriaRuntimeDaemonOperations.Execute(
run,
[cancel],
new AetheriaRuntimeDaemonOperationContext());
Require(result.AppliedCommandIds.Contains(cancel.CommandId, StringComparer.Ordinal),
"task cancellation must commit through the daemon command boundary");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Cancelled, task.Status,
"cancelled task must become terminal immediately");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Cancelled, task.Phase,
"cancelled task must not retain a counterfeit in-progress phase");
RequireEqual(-1, task.AssignedEntityIndex,
"cancelled task must release its assigned agent identity");
RequireEqual(0, task.PendingQuantity,
"cancelled transfer work must not retain pending quantity");
RequireEqual("", agent.AssignedAgentTaskId,
"cancelled agent must release the durable assignment marker");
RequireNear(0, agent.HelmStrafe, 0.000001,
"cancelled agent must release scheduler-owned strafe input");
RequireNear(0, agent.HelmForward, 0.000001,
"cancelled agent must release scheduler-owned forward input");
RequireEqual(-1, agent.TargetEntityIndex,
"cancelled agent must release scheduler-owned targeting");
Require(!agent.ChildEntityIndices.Contains(station.EntityIndex),
"cancelling an active tow must release the station at its authoritative world position");
RequireEqual(0, AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 13).Count,
"cancelled work must not produce another scheduler command on the next frame");
}
private static AetheriaRuntimeAgentTaskCommit AgentTask(string id, int priority) => new()
{
TaskId = id,
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Explore,
Priority = priority,
ZoneIndex = 0,
TargetPositionX = 100,
CompletionRadius = 5
};
private static void SchedulerAssignsShortestGalaxyRoute()
{
var distant = Entity(0, 0, "workers");
distant.EntityId = "worker.route.distant";
distant.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var nearby = Entity(0, 0, "workers");
nearby.EntityId = "worker.route.nearby";
nearby.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var task = AgentTask("route-choice", 10);
task.ZoneIndex = 2;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-route-choice-smoke",
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, PositionX = 0, GravityTerrainRadius = 100, AdjacentZoneIndices = [1], Entities = [distant] },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 1, PositionX = 100, GravityTerrainRadius = 100, AdjacentZoneIndices = [0, 2], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>() },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 2, PositionX = 200, GravityTerrainRadius = 100, AdjacentZoneIndices = [1, 3], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>() },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 3, PositionX = 300, GravityTerrainRadius = 100, AdjacentZoneIndices = [2], Entities = [nearby] }
],
AgentTasks = [task]
};
AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 1);
RequireEqual("route-choice", nearby.AssignedAgentTaskId,
"scheduler must reserve the controller with the shortest galaxy route to the task zone");
Require(string.IsNullOrWhiteSpace(distant.AssignedAgentTaskId),
"longer-route controller must remain available");
}
private static void AgentTraversesGalaxyRouteBeforeExecutingTask()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-agent-route-restart-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var agent = Entity(0, 0, "workers");
agent.EntityId = "worker.cross-zone.stable";
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var catalog = EquipThrusterBank([agent]);
var task = AgentTask("cross-zone", 10);
task.ZoneIndex = 2;
task.TargetPositionX = 0;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-route-travel-smoke",
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, PositionX = 0, GravityTerrainRadius = 100, AdjacentZoneIndices = [1], Entities = [agent] },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 1, PositionX = 100, GravityTerrainRadius = 100, AdjacentZoneIndices = [0, 2], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>() },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 2, PositionX = 200, GravityTerrainRadius = 100, AdjacentZoneIndices = [1], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>() }
],
AgentTasks = [task]
};
var appliedTravelCommands = 0;
var appliedApproachCommands = 0;
var restarted = false;
var physics = NewPhysics();
for (var frameId = 0; frameId < 400; frameId++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
statePath,
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
Catalog = catalog,
FrameId = frameId,
FixedDeltaSeconds = 0.25,
SimulationTimeSeconds = frameId * 0.25,
BuildPublications = false
});
appliedTravelCommands += tick.OperationResult.AppliedCommandIds.Count(id => id.EndsWith(":travel", StringComparison.Ordinal));
appliedApproachCommands += tick.OperationResult.AppliedCommandIds.Count(id => id.EndsWith(":travel-approach", StringComparison.Ordinal));
agent = run.Zones.SelectMany(zone => zone.Entities)
.Single(entity => entity.EntityId == "worker.cross-zone.stable");
if (!restarted && string.Equals(agent.WormholeTransition?.Phase, "entering", StringComparison.Ordinal))
{
var eventCountsBefore = (
Started: run.GameEvents.Count(value => value.Kind == "wormhole.enter.started"),
Transferred: run.GameEvents.Count(value => value.Kind == "wormhole.transferred"),
Completed: run.GameEvents.Count(value => value.Kind == "wormhole.exit.completed"));
var durableFrame = new AetheriaRuntimeDaemonFrameDocument
{
FrameId = frameId,
FixedDeltaSeconds = 0.25,
SimulationTimeSeconds = frameId * 0.25,
Run = run
};
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(durableFrame).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
durableFrame = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable cross-zone agent frame is missing.");
}
run = durableFrame.Run;
task = run.AgentTasks.Single();
agent = run.Zones.SelectMany(zone => zone.Entities)
.Single(entity => entity.EntityId == "worker.cross-zone.stable");
physics.Dispose();
physics = NewPhysics();
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, task.Status,
"cross-zone restart must preserve the assigned task during wormhole entry");
RequireEqual(task.TaskId, agent.AssignedAgentTaskId,
"cross-zone restart must preserve the task carrier edge");
RequireEqual("entering", agent.WormholeTransition?.Phase ?? "",
"cross-zone restart must preserve the exact wormhole phase");
Require(eventCountsBefore == (
run.GameEvents.Count(value => value.Kind == "wormhole.enter.started"),
run.GameEvents.Count(value => value.Kind == "wormhole.transferred"),
run.GameEvents.Count(value => value.Kind == "wormhole.exit.completed")),
"CultCache reopen must not replay wormhole chronology before the next daemon tick");
restarted = true;
}
if (string.Equals(task.Status, AetheriaRuntimeAgentTaskStatuses.Completed, StringComparison.Ordinal))
break;
}
Require(restarted,
"cross-zone task proof must hard-restart while the worker is inside the wormhole timeline");
Require(appliedApproachCommands > 0,
"agent must approach wormholes through shared movement commands and Ymir before transition");
RequireEqual(2, appliedTravelCommands,
"agent must traverse each galaxy edge through the shared wormhole command boundary");
RequireEqual(2, run.GameEvents.Count(value => value.Kind == "wormhole.enter.started"),
"two route edges must publish exactly two retained entry events across restart");
RequireEqual(2, run.GameEvents.Count(value => value.Kind == "wormhole.transferred"),
"two route edges must publish exactly two retained transfer events across restart");
RequireEqual(2, run.GameEvents.Count(value => value.Kind == "wormhole.exit.completed"),
"two route edges must publish exactly two retained completion events across restart");
RequireEqual(1, run.Zones.Sum(zone => zone.Entities.Count),
"cross-zone restart must not clone the durable worker into another zone");
Require(run.Zones.Single(zone => zone.ZoneIndex == 2).Entities.Count == 1,
"assigned agent must arrive in the task zone without a parallel teleport owner");
RequireEqual("worker.cross-zone.stable", run.Zones.Single(zone => zone.ZoneIndex == 2).Entities.Single().EntityId,
"zone transfer must preserve stable entity identity while projection indices change");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, task.Status,
"agent must execute the task only after arriving in its destination zone");
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void WormholeTraversalUsesPersistedEnterTransferExitPhases()
{
var ship = Entity(0, 75, "player");
ship.EntityId = "wormhole-traveler";
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "wormhole-transition-smoke",
GenerationSeed = 77,
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
DiscoveredZoneIndices = [0],
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0, PositionX = 0, GravityTerrainRadius = 100,
AdjacentZoneIndices = [1], Entities = [ship]
},
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 1, PositionX = 100, GravityTerrainRadius = 100,
AdjacentZoneIndices = [0, 2], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>()
},
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 2, PositionX = 200, GravityTerrainRadius = 100,
AdjacentZoneIndices = [1], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>()
}
]
};
AetheriaRuntimeDaemonCommandDocument Travel(string id)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.EnterWormhole,
"pilot",
id,
0,
run.CurrentEntityKey);
command.CommandId = id;
command.TargetZoneIndex = 1;
return command;
}
var physics = NewPhysics();
AetheriaRuntimeDaemonTickResult Tick(int frame, params AetheriaRuntimeDaemonCommandDocument[] commands) =>
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-wormhole-transition-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = frame,
FixedDeltaSeconds = 1,
SimulationTimeSeconds = frame + 1,
ObservedCommands = commands,
BuildPublications = false
});
var entering = Tick(0, Travel("wormhole-start"), Travel("wormhole-same-batch"));
Require(entering.OperationResult.RejectedCommandIds.Any(id => id.Contains("wormhole-same-batch", StringComparison.Ordinal)),
"one command batch must not admit two competing transition intents for the same entity");
RequireEqual(0, run.CurrentZoneIndex,
"wormhole command acceptance must not splice the ship into the destination immediately");
RequireEqual("entering", ship.WormholeTransition?.Phase ?? "",
"daemon must persist the entering phase on the authoritative entity");
Require(Math.Abs((ship.WormholeTransition?.Progress ?? 0) - 0.25) < 0.0001,
"fossil four-second entry timing must advance from fixed daemon time");
Require(Math.Abs((ship.WormholeTransition?.VisualDepthOffset ?? 0) + 250) < 0.0001,
"transition must expose the fossil depth lever without making the client own motion");
var enteringView = AetheriaRuntimeGameDocuments.CurrentEntity(entering.Frame).WormholeTransition;
RequireEqual("entering", enteringView?.Phase ?? "",
"current-entity projection must expose the same transition state to Eve consumers");
run = MessagePack.MessagePackSerializer.Deserialize<AetheriaRuntimeRunCheckpointCommit>(
MessagePack.MessagePackSerializer.Serialize(run));
ship = run.Zones.Single(zone => zone.ZoneIndex == 0).Entities.Single();
physics.Dispose();
physics = NewPhysics();
RequireEqual("entering", ship.WormholeTransition?.Phase ?? "",
"daemon restart must retain the exact mid-entry phase instead of repairing it from client state");
var duplicate = Tick(1, Travel("wormhole-duplicate"));
Require(duplicate.OperationResult.RejectedCommandIds.Any(id => id.Contains("wormhole-duplicate", StringComparison.Ordinal)),
"an entity already inside a wormhole transition must reject a second traversal owner");
Tick(2);
Tick(3);
RequireEqual(1, run.CurrentZoneIndex,
"zone ownership must transfer only after the entering phase completes");
var arrived = run.Zones.Single(zone => zone.ZoneIndex == 1).Entities.Single();
RequireEqual("exiting", arrived.WormholeTransition?.Phase ?? "",
"transferred ship must persist a distinct exiting phase");
Require(run.DiscoveredZoneIndices.Contains(2),
"arrival must reveal the destination adjacency using the fossil discovery rule");
Tick(4);
Tick(5);
Tick(6);
Tick(7);
RequireEqual("completed", arrived.WormholeTransition?.Phase ?? "",
"the completion frame must preserve a presentation lever before normal physics resumes");
Require(Math.Abs(Math.Sqrt(arrived.VelocityX * arrived.VelocityX + arrived.VelocityY * arrived.VelocityY) - 20) < 0.0001,
"completed exit must restore the fossil twenty-unit launch velocity");
Require(Math.Abs(Math.Sqrt(Math.Pow(arrived.PositionX + 75, 2) + Math.Pow(arrived.PositionZ, 2)) - 50) < 0.001,
"completed exit must place the ship fifty units from the destination wormhole");
Tick(8);
Require(arrived.WormholeTransition == null,
"the tick after completion must release input and Ymir instead of leaving a permanent transition flag");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "wormhole.enter.started"),
"entry feedback must have stable exactly-once chronology");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "wormhole.transferred"),
"transfer feedback must have stable exactly-once chronology");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "wormhole.exit.completed"),
"exit feedback must have stable exactly-once chronology");
}
private static void IdleAgentReturnsToCanonicalHomeAndDocks()
{
var worker = Entity(0, 0, "workers");
worker.EntityId = "worker.return-home";
worker.HomeEntityId = "station.home";
worker.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Haul];
var home = Entity(0, 60, "workers");
home.EntityId = "station.home";
home.Kind = "station";
home.DockingBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "station-docking-bay" }
}];
home.DockingBayAssignments = [-1];
home.DockingBayContents = [Cargo()];
var catalog = EquipThrusterBank([worker]);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-return-home-smoke",
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, PositionX = 0, GravityTerrainRadius = 100, AdjacentZoneIndices = [1], Entities = [worker] },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 1, PositionX = 100, GravityTerrainRadius = 100, AdjacentZoneIndices = [0, 2], Entities = Array.Empty<AetheriaRuntimeEntitySnapshotCommit>() },
new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 2, PositionX = 200, GravityTerrainRadius = 100, AdjacentZoneIndices = [1], Entities = [home] }
]
};
var sawApproach = false;
var travelCount = 0;
var sawDock = false;
var rejectedHomeCommands = new List<string>();
var minimumHomeDistance = double.PositiveInfinity;
var maximumHomeSpeed = 0.0;
var physics = NewPhysics();
for (var frame = 0; frame < 500; frame++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-return-home-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
Catalog = catalog,
FrameId = frame,
FixedDeltaSeconds = 0.25,
SimulationTimeSeconds = frame * 0.25,
BuildPublications = false
});
sawApproach |= tick.OperationResult.AppliedCommandIds.Any(id => id.Contains(":home-approach", StringComparison.Ordinal));
travelCount += tick.OperationResult.AppliedCommandIds.Count(id => id.EndsWith(":home-travel", StringComparison.Ordinal));
sawDock |= tick.OperationResult.AppliedCommandIds.Any(id => id.EndsWith(":home-dock", StringComparison.Ordinal));
rejectedHomeCommands.AddRange(tick.OperationResult.RejectedCommandIds.Where(id => id.Contains(":home-", StringComparison.Ordinal)));
var currentHomeZone = run.Zones.Single(zone => zone.ZoneIndex == 2);
var currentWorker = currentHomeZone.Entities.FirstOrDefault(entity => entity.EntityId == "worker.return-home");
var currentHome = currentHomeZone.Entities.FirstOrDefault(entity => entity.EntityId == "station.home");
if (currentWorker != null && currentHome != null)
{
minimumHomeDistance = Math.Min(minimumHomeDistance, Math.Sqrt(
Math.Pow(currentWorker.PositionX - currentHome.PositionX, 2) +
Math.Pow(currentWorker.PositionZ - currentHome.PositionZ, 2)));
maximumHomeSpeed = Math.Max(maximumHomeSpeed, Math.Sqrt(
currentWorker.VelocityX * currentWorker.VelocityX +
currentWorker.VelocityY * currentWorker.VelocityY));
}
if (sawDock)
break;
}
var homeZone = run.Zones.Single(zone => zone.ZoneIndex == 2);
var arrivedWorker = homeZone.Entities.Single(entity => entity.EntityId == "worker.return-home");
var arrivedHome = homeZone.Entities.Single(entity => entity.EntityId == "station.home");
Require(sawApproach, "idle worker must approach its route and home through shared movement commands");
RequireEqual(2, travelCount, "idle worker must traverse the shortest route to its canonical home");
Require(sawDock,
$"idle worker must dock through the daemon docking command; worker={arrivedWorker.PositionX:0.###},{arrivedWorker.PositionZ:0.###} home={arrivedHome.PositionX:0.###},{arrivedHome.PositionZ:0.###} minDistance={minimumHomeDistance:0.###} maxSpeed={maximumHomeSpeed:0.###} rejected={string.Join(",", rejectedHomeCommands)}");
Require(arrivedHome.DockingBayAssignments.Contains(arrivedWorker.EntityIndex),
"home docking parentage must be the authoritative completion fact");
var settled = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-return-home-settled-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics, Catalog = catalog, FrameId = 501,
FixedDeltaSeconds = 0.25, SimulationTimeSeconds = 125.25, BuildPublications = false
});
Require(!settled.OperationResult.AppliedCommandIds.Any(id => id.Contains(":home-", StringComparison.Ordinal)),
"docked worker must not emit a perpetual homecoming repair loop");
}
private static void ControlledShipDoesNotReceiveAutonomousHelmCommands()
{
var controlled = Entity(0, 100, "workers");
controlled.EntityId = "controlled-worker";
controlled.HomeEntityId = "controlled-home";
controlled.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var home = Entity(1, 0, "workers");
home.EntityId = "controlled-home";
home.Kind = "station";
var task = AgentTask("controlled-task", 10);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "controlled-agent-authority-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [controlled, home] }],
AgentTasks = [task]
};
var commands = AetheriaRuntimeAgentScheduler.AssignAndPlan(run, 1);
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Queued, task.Status,
"manual helm owner must not be conscripted into autonomous corporation work");
Require(commands.Count == 0,
"controlled ship must emit no task or return-home commands without delegated helm authority");
}
private static void AgentClaimsAndCompletesExploreTaskThroughCommands()
{
var agent = Entity(0, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var catalog = EquipThrusterBank([agent]);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-task-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent] }]
};
var issue = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask,
"commander-smoke",
"starbridge-smoke",
0,
"");
issue.CommandId = "issue-explore";
issue.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = "explore-east",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Explore,
Priority = 50,
ZoneIndex = 0,
TargetPositionX = 50,
TargetPositionZ = 0,
CompletionRadius = 5
};
var completed = false;
var physics = NewPhysics();
for (var frame = 0; frame <= 60; frame++)
{
var tick = AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-agent-task-smoke.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = frame,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frame * 0.1,
ObservedCommands = frame == 0 ? [issue] : Array.Empty<AetheriaRuntimeDaemonCommandDocument>(),
Catalog = catalog,
BuildPublications = false
});
if (frame == 0)
{
Require(tick.OperationResult.AppliedCommandIds.Contains("issue-explore"),
"commander task command must enter the normal tick reducer");
Require(tick.OperationResult.AppliedCommandIds.Any(id => id.Contains(":explore-east:0:move", StringComparison.Ordinal)),
"agent movement must return through the same command receipts");
}
completed = string.Equals(run.AgentTasks.Single().Status, AetheriaRuntimeAgentTaskStatuses.Completed, StringComparison.Ordinal);
if (completed)
break;
}
Require(completed,
$"agent must complete an explore task without direct position mutation; position={agent.PositionX:0.###},{agent.PositionZ:0.###} velocity={agent.VelocityX:0.###},{agent.VelocityY:0.###}");
Require(string.IsNullOrWhiteSpace(agent.AssignedAgentTaskId), "completed task must release the agent");
Require(agent.PositionX >= 45, "agent must reach the task through repeated movement commands");
var frameDocument = new AetheriaRuntimeDaemonFrameDocument { FrameId = 21, Run = run };
var commander = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildCommander(
frameDocument,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("agent-task-smoke"));
var taskNode = Flatten(commander.Surface.Root)
.Single(component => string.Equals(component.Id, "aetheria.starbridge.commander.tasks.explore_east", StringComparison.Ordinal));
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, taskNode.Props["status"],
"commander Eve surface must publish authoritative task status");
Require(commander.Commands.Any(command => string.Equals(command.Command, "aetheria.daemon.issue_agent_task", StringComparison.Ordinal)),
"commander Eve surface must advertise task issue command");
var roster = Flatten(commander.Surface.Root)
.Single(component => string.Equals(component.Kind, "agent.roster", StringComparison.Ordinal));
var worker = Flatten(roster)
.Single(component => string.Equals(component.Kind, "agent.item", StringComparison.Ordinal));
Require(!string.IsNullOrWhiteSpace(worker.Props["entityId"]),
"commander Eve roster must expose stable provider-owned worker identity");
Require(worker.Props["capabilities"].Split(',').Contains(AetheriaRuntimeAgentTaskTypes.Explore),
"commander Eve roster must expose provider-owned capability claims");
RequireEqual("idle", worker.Props["status"],
"commander Eve roster must publish generic status for runtime-independent lowering");
Require(worker.Props["badges"].Split(',').Contains(AetheriaRuntimeAgentTaskTypes.Explore),
"commander Eve roster must publish capability badges through the generic item contract");
RequireEqual("", worker.Props["assignedTaskId"],
"completed worker assignment must be visible as released on the Eve roster");
RequireEqual("false", worker.Props["controlled"],
"commander Eve roster must distinguish autonomous workers from manual helm authority");
}
private static void AgentTaskSurvivesCultCacheRestart()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-agent-restart-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var agent = Entity(0, 0, "workers");
agent.AgentTaskCapabilities = [AetheriaRuntimeAgentTaskTypes.Explore];
var catalog = EquipThrusterBank([agent]);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "agent-task-restart-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [agent] }]
};
var issue = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.IssueAgentTask,
"commander-smoke",
run.RunId,
0,
"");
issue.CommandId = "issue-restart-explore";
issue.AgentTask = new AetheriaRuntimeAgentTaskCommand
{
TaskId = "restart-explore-east",
CorporationKey = "workers",
TaskType = AetheriaRuntimeAgentTaskTypes.Explore,
Priority = 50,
ZoneIndex = 0,
TargetPositionX = 80,
TargetPositionZ = 0,
CompletionRadius = 5
};
var physics = NewPhysics();
for (var frameId = 0; frameId <= 8; frameId++)
AetheriaRuntimeDaemonTickRunner.Tick(
statePath,
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = frameId,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frameId * 0.1,
ObservedCommands = frameId == 0
? [issue]
: Array.Empty<AetheriaRuntimeDaemonCommandDocument>(),
Catalog = catalog,
BuildPublications = false
});
var taskBeforeRestart = run.AgentTasks.Single();
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, taskBeforeRestart.Status,
"restart fixture must persist an actively assigned task rather than a queued or completed shell");
RequireEqual(taskBeforeRestart.TaskId, agent.AssignedAgentTaskId,
"restart fixture must persist the scheduler's single agent assignment");
Require(agent.PositionX > 0 && agent.PositionX < 75,
"restart fixture must stop during commanded Ymir travel");
var positionBeforeRestart = agent.PositionX;
var frame = new AetheriaRuntimeDaemonFrameDocument
{
FrameId = 8,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.8,
Run = run
};
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(frame).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
frame = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable agent-task frame is missing.");
}
run = frame.Run;
agent = run.Zones.Single().Entities.Single();
var restoredTask = run.AgentTasks.Single();
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Assigned, restoredTask.Status,
"CultCache restart must preserve the active task status");
RequireEqual(restoredTask.TaskId, agent.AssignedAgentTaskId,
"CultCache restart must preserve the task-to-agent ownership edge");
RequireNear(positionBeforeRestart, agent.PositionX, 0.000001,
"CultCache restart must preserve the last daemon/Ymir committed pose");
physics = NewPhysics();
var completed = false;
for (var frameId = 9; frameId <= 100; frameId++)
{
AetheriaRuntimeDaemonTickRunner.Tick(
statePath,
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = frameId,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frameId * 0.1,
Catalog = catalog,
BuildPublications = false
});
completed = string.Equals(
run.AgentTasks.Single().Status,
AetheriaRuntimeAgentTaskStatuses.Completed,
StringComparison.Ordinal);
if (completed)
break;
}
Require(completed && agent.PositionX >= 75,
"restored task must reconstruct Ymir from committed world truth and finish through scheduler movement commands");
RequireEqual(1, run.AgentTasks.Count,
"restart must not duplicate the durable task when the scheduler resumes");
RequireEqual("", agent.AssignedAgentTaskId,
"completed restored task must release scheduler control exactly once");
var commander = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildCommander(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 100, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("agent-task-restart-smoke"));
var taskNode = Flatten(commander.Surface.Root).Single(node =>
node.Id == "aetheria.starbridge.commander.tasks.restart_explore_east");
RequireEqual(AetheriaRuntimeAgentTaskStatuses.Completed, taskNode.Props["status"],
"generic Eve commander reconnect must observe the restored task's authoritative completion");
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void MultipleActorsUseTheSameMovementLever()
{
var player = Entity(0, 0, "player");
var agent = Entity(1, 200, "worker");
var catalog = EquipThrusterBank([player, agent]);
player.TargetEntityIndex = agent.EntityIndex;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "shared-lever-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [player, agent] }]
};
var commands = new[]
{
MovementCommand("player-move", "zone.0.entity.0", 1, 0),
MovementCommand("agent-move", "zone.0.entity.1", 0, 1)
};
var operation = AetheriaRuntimeDaemonOperations.Execute(run, commands);
RequireEqual(2, operation.Intents.Movements.Count, "movement intent must retain one lever position per actor");
var physics = NewPhysics();
for (var step = 0; step < 2; step++)
AetheriaRuntimeDaemonSimulation.Step(
run,
operation.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics,
catalog,
step + 1);
Require(player.VelocityX > 0 && Math.Abs(player.VelocityY) < Math.Abs(player.VelocityX) * 0.1,
$"player command must drive its actor through the shared movement lever; velocity={player.VelocityX:0.###},{player.VelocityY:0.###}");
Require(agent.VelocityY > 0 && Math.Abs(agent.VelocityX) < Math.Abs(agent.VelocityY) * 0.1,
$"agent command must drive its actor through the shared movement lever; velocity={agent.VelocityX:0.###},{agent.VelocityY:0.###}");
var release = MovementCommand("player-release", "zone.0.entity.0", 0, 0);
release.ScalarValue = 0;
var releaseOperation = AetheriaRuntimeDaemonOperations.Execute(run, [release]);
AetheriaRuntimeDaemonSimulation.Step(
run,
releaseOperation.Intents,
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics,
catalog,
3);
RequireEqual(agent.EntityIndex, player.TargetEntityIndex,
"releasing the movement lever must not steal targeting authority");
}
private static void DirectionalThrustersOwnOrdinaryFlightFeedback()
{
var ship = Entity(0, 0, "player");
var catalog = EquipThrusterBank([ship]);
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "ordinary-thruster-flight-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship] }]
};
var operation = AetheriaRuntimeDaemonOperations.Execute(
run, [MovementCommand("forward-thrust", "zone.0.entity.0", 0, 1)]);
var physics = NewPhysics();
AetheriaRuntimeDaemonSimulation.Step(
run, operation.Intents, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics, catalog, 1);
var thrusterStates = ship.BehaviorStates
.Where(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.Thruster)
.ToArray();
RequireEqual(8, thrusterStates.Length,
"ordinary hull fixture must fly through its directional thruster bank");
Require(thrusterStates.Count(value => value.ThrusterAxis > 0.99) == 2 &&
thrusterStates.Where(value => value.ThrusterAxis > 0.99)
.All(value => ship.Equipment[value.OwnerIndex].Rotation == "Half"),
"forward helm must fire only the reversed forward-thrust pair when no turn is requested");
Require(thrusterStates.Where(value => value.ThrusterAxis > 0.99)
.All(value => value.ThrusterThrust > 0 && Math.Abs(value.ThrusterTorque) > 0.5),
"thruster state must expose evaluated thrust and placement-derived torque");
Require(ship.VelocityY > 0 && Math.Abs(ship.VelocityX) < Math.Abs(ship.VelocityY) * 0.1,
"ordinary thrust must become Ymir-integrated forward velocity");
Require(ship.Visibility > 1.9,
"successful thruster execution must publish its maximum plume visibility");
Require(thrusterStates.Where(value => value.ThrusterAxis > 0.99)
.Any(value => AetheriaRuntimeThermalSimulation.EquipmentTemperature(
ship, catalog, value.OwnerIndex) > ship.EquipmentStates
.Single(state => state.EquipmentIndex == value.OwnerIndex).Temperature),
"successful thruster execution must deposit authored heat into daemon equipment cells");
Require(!ship.BehaviorStates.Any(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.AetherDrive),
"ordinary flight must not manufacture an AetherDrive behavior");
}
private static void UnpoweredThrustersCannotMoveOrAdvertisePlume()
{
var ship = Entity(0, 0, "player");
var catalog = EquipThrusterBank([ship]);
var thruster = catalog.Items.Single(value => value.ItemKey == "smoke-directional-thruster");
thruster.BehaviorPayloads.Single().Fields = thruster.BehaviorPayloads.Single().Fields
.Select(field => field.Key == 4
? new AetheriaRuntimeBehaviorField(4, PerformanceStat(10))
: field)
.ToArray();
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "unpowered-thruster-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship] }]
};
var operation = AetheriaRuntimeDaemonOperations.Execute(
run, [MovementCommand("unpowered-forward", "zone.0.entity.0", 0, 1)]);
AetheriaRuntimeDaemonSimulation.Step(
run, operation.Intents, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, 1);
RequireNear(0, ship.VelocityX, 0.000001,
"an unfunded thruster bank must not create lateral velocity");
RequireNear(0, ship.VelocityY, 0.000001,
"an unfunded thruster bank must not create forward velocity");
RequireNear(0, ship.Visibility, 0.000001,
"an unfunded thruster bank must not advertise a plume that did not execute");
}
private static void SpecializedBehaviorSuccessGatesTrailingWear()
{
var ship = Entity(0, 0, "player");
var catalog = EquipThrusterBank([ship]);
var thrusterItem = catalog.Items.Single(value => value.ItemKey == "smoke-directional-thruster");
thrusterItem.BehaviorPayloads =
[
ThrusterPayload(),
new AetheriaRuntimeBehaviorPayload(1, "Wear", 0,
[new AetheriaRuntimeBehaviorField(1, BoolValue(true))])
];
AetheriaRuntimeThermalSimulation.EnsureTopology(ship, catalog);
AetheriaRuntimeThermalSimulation.EnsureState(ship);
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(ship, catalog, 0.1);
foreach (var state in ship.EquipmentStates)
state.Wear = 1;
AetheriaRuntimeEnergySimulation.BeginTick(ship, catalog);
AetheriaRuntimeBehaviorSimulation.Step(0, [ship], [], catalog, 0.1);
AetheriaRuntimeFlightSimulation.Step([ship], [], catalog, 0.1, 1, 1, 0.001, 1);
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(ship, catalog, 0.1);
Require(ship.Equipment.All(slot => Math.Abs(slot.Item.Durability - 1) < 0.000001),
"idle thrusters must return false and leave every trailing wear behavior unreachable");
ship.HelmForward = 1;
AetheriaRuntimeBehaviorSimulation.Step(0, [ship], [], catalog, 0.1);
AetheriaRuntimeFlightSimulation.Step([ship], [], catalog, 0.1, 1, 1, 0.001, 1);
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(ship, catalog, 0.1);
RequireEqual(2, ship.Equipment.Count(slot => slot.Item.Durability < 1),
"only the two successful forward thrusters may resume into trailing wear");
Require(ship.Equipment.Where(slot => slot.Item.Durability < 1)
.All(slot => Math.Abs(slot.Item.Durability - 0.9) < 0.000001),
"each successful specialized behavior must resume its tail exactly once");
AetheriaRuntimeBehaviorSimulation.CompleteDeferredChains(ship, catalog, 0.1);
Require(ship.Equipment.Where(slot => slot.Item.Durability < 1)
.All(slot => Math.Abs(slot.Item.Durability - 0.9) < 0.000001),
"replaying deferred completion in the same tick must not apply trailing wear twice");
}
private static void RareAetherDriveSpoolsOnlyOnModifiedHullFixture()
{
var ship = Entity(0, 0, "player");
var catalog = EquipAetherDrive([ship]);
var hull = catalog.FindItem(ship.HullItemKey)!;
var drive = catalog.Items.Single(value => value.ItemKey == "smoke-aether-drive");
Require(drive.HardpointType == "AetherDrive" &&
hull.Hardpoints.Count(value => value.Type == drive.HardpointType) == 1,
"rare AetherDrive fixture must use an explicit modified-hull hardpoint instead of the ordinary hull path");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "rare-aether-drive-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship] }]
};
var operation = AetheriaRuntimeDaemonOperations.Execute(
run, [MovementCommand("rare-drive-forward", "zone.0.entity.0", 0, 1)]);
var physics = NewPhysics();
for (var frame = 1; frame <= 2; frame++)
AetheriaRuntimeDaemonSimulation.Step(
run, operation.Intents, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics, catalog, frame);
var state = ship.BehaviorStates.Single(value =>
value.BehaviorKind == AetheriaRuntimeBehaviorKinds.AetherDrive);
Require(state.AetherDriveRpmX > 0 && state.AetherDriveMaximumRpm > 0,
"rare installed drive must persist rotor spool state");
Require(ship.VelocityY > 0,
"rare installed drive must contribute force only after its rotor has spooled");
Require(!ship.BehaviorStates.Any(value => value.BehaviorKind == AetheriaRuntimeBehaviorKinds.Thruster),
"isolated AetherDrive proof must not conceal its force behind ordinary thrusters");
}
private static void LookDirectionRejectsInvalidVectorsWithoutMutatingTheShip()
{
var player = Entity(0, 0, "player");
player.DirectionX = 0.6;
player.DirectionY = 0.8;
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "invalid-look-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [player] }]
};
AetheriaRuntimeDaemonCommandDocument Look(string id, double x, double z)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetLookDirection,
"look-smoke",
"look-session",
0,
"zone.0.entity.0");
command.CommandId = id;
command.DirectionX = x;
command.PositionZ = z;
return command;
}
var operation = AetheriaRuntimeDaemonOperations.Execute(run,
[
Look("zero-look", 0, 0),
Look("nan-look", double.NaN, 1),
Look("infinite-look", 1, double.PositiveInfinity)
]);
Require(operation.RejectedCommandIds.SequenceEqual(new[] { "zero-look", "nan-look", "infinite-look" }),
"zero and non-finite look intents must be rejected at the daemon authority boundary");
RequireNear(0.6, player.DirectionX, 0.000001,
"rejected look intents must not mutate the authoritative ship direction X");
RequireNear(0.8, player.DirectionY, 0.000001,
"rejected look intents must not mutate the authoritative ship direction Z");
}
private static AetheriaRuntimeDaemonCommandDocument MovementCommand(
string commandId,
string actor,
double x,
double y)
{
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetMoveVector,
"shared-control-smoke",
"shared-control-session",
0,
actor);
command.CommandId = commandId;
command.DirectionX = x;
command.DirectionY = y;
command.ScalarValue = 1;
return command;
}
private static void EnergyNetworkSettlesReactorAfterConsumers()
{
var entity = Entity(0, 0, "player");
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "ledger-capacitor", Quality = 1, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "ledger-reactor", Quality = 1, Durability = 1, Enabled = true } }
];
entity.BehaviorStates =
[
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0, BehaviorIndex = 0, BehaviorKind = "Capacitor",
CapacitorCharge = 2, CapacitorCapacity = 10, CapacitorEfficiency = 0.5
},
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 1, BehaviorIndex = 0, BehaviorKind = "Reactor"
}
];
entity.StatGrids =
[
Grid("hull", 100), Grid("shield", 0), Grid("heat", 280),
Grid("temperature", 280), Grid("thermal-mass", 1), Grid("conductivity", 1)
];
var capacitorItem = CatalogItem("ledger-capacitor", CapacitorPayload(10, 0.5));
capacitorItem.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var reactorPayload = new AetheriaRuntimeBehaviorPayload(0, "Reactor", 100,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(4)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(2)),
new AetheriaRuntimeBehaviorField(3, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(2))
]);
var reactorItem = CatalogItem("ledger-reactor", reactorPayload);
reactorItem.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([capacitorItem, reactorItem], [], []);
AetheriaRuntimeEnergySimulation.BeginTick(entity, catalog);
Require(AetheriaRuntimeEnergySimulation.TryConsume(entity, catalog, 5),
"online reactor must accept residual demand after capacitors drain");
var capacitor = entity.BehaviorStates.Single(value => value.BehaviorKind == "Capacitor");
var reactor = entity.BehaviorStates.Single(value => value.BehaviorKind == "Reactor");
RequireNear(0, capacitor.CapacitorCharge, 0.000001,
"consumers must drain capacitors before adding reactor draw");
RequireNear(3, reactor.ReactorDraw, 0.000001,
"residual demand must accumulate on the last-running reactor");
AetheriaRuntimeEnergySimulation.SettleReactors(entity, catalog, 1);
RequireNear(1, capacitor.CapacitorCharge, 0.000001,
"baseline reactor surplus must refill non-full capacitors after demand settles");
RequireNear(0, reactor.ReactorDraw, 0.000001,
"reactor settlement must consume and reset the per-tick demand ledger");
RequireNear(283.5, GridValue(entity, "temperature", 0), 0.000001,
"capacitor discharge/refill losses and reactor baseline heat must reach canonical item cells");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "energy-ledger-smoke", CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var worldEntity = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.0");
RequireEqual("1", worldEntity.Props["capacitorCharge"],
"Eve must expose the settled canonical capacitor charge");
RequireEqual("0", worldEntity.Props["reactorDraw"],
"Eve must expose the reset post-settlement reactor ledger");
}
private static void RadiatorPumpsHeatBeforeReactorSettlement()
{
var entity = Entity(0, 0, "player");
entity.HeatsinksEnabled = true;
entity.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "radiator-capacitor", Quality = 1, Durability = 1, Enabled = true } },
new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "radiator", Quality = 1, Durability = 1, Enabled = true } }
];
entity.BehaviorStates =
[
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0, BehaviorIndex = 0, BehaviorKind = "Capacitor", CapacitorCharge = 10
},
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 1, BehaviorIndex = 0, BehaviorKind = "Radiator", RadiatorTemperature = 280
}
];
entity.StatGrids =
[
Grid("hull", 100), Grid("shield", 0), Grid("heat", 400),
Grid("temperature", 400), Grid("thermal-mass", 1), Grid("conductivity", 1)
];
var capacitor = CatalogItem("radiator-capacitor", CapacitorPayload(10, 1));
capacitor.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var radiatorPayload = new AetheriaRuntimeBehaviorPayload(0, "Radiator", 0,
[
new AetheriaRuntimeBehaviorField(1, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(2, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(3, Number(300)),
new AetheriaRuntimeBehaviorField(4, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(5, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(10))
]);
var radiator = CatalogItem("radiator", radiatorPayload);
radiator.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([capacitor, radiator], [], []);
AetheriaRuntimeEnergySimulation.BeginTick(entity, catalog);
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 1);
AetheriaRuntimeEnergySimulation.StepRadiators(entity, catalog, 1);
RequireNear(9, entity.BehaviorStates.Single(value => value.BehaviorKind == "Capacitor").CapacitorCharge,
0.000001, "radiator pump must consume authored energy before reactor settlement");
RequireNear(300.5, GridValue(entity, "temperature", 0), 0.000001,
"radiator must remove pumped heat and return authored waste heat to its item cells");
RequireNear(290, entity.BehaviorStates.Single(value => value.BehaviorKind == "Radiator").RadiatorTemperature,
0.000001, "pumped heat must accumulate in radiator thermal mass before radiation");
}
private static void EquipmentThermalPerformanceOwnsShutdownAndWear()
{
var entity = Entity(0, 0, "player");
entity.ShutdownPerformance = 0.25;
var item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "thermal-wear-item", Quality = 0, Durability = 10, Enabled = true };
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = item }];
entity.StatGrids =
[
Grid("hull", 100), Grid("shield", 0), Grid("heat", 300),
Grid("temperature", 300), Grid("thermal-mass", 1), Grid("conductivity", 1)
];
var wearPayload = new AetheriaRuntimeBehaviorPayload(0, "Wear", 0,
[new AetheriaRuntimeBehaviorField(1, new AetheriaRuntimeBehaviorValue(
"bool", "", 0, true, "", "", [], []))]);
var typed = CatalogItem("thermal-wear-item", wearPayload);
typed.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
typed.Durability = 10;
typed.ThermalResilience = 2;
typed.MinimumTemperature = 200;
typed.MaximumTemperature = 400;
typed.ThermalPerformanceCurveKeys =
[
new AetheriaRuntimeCurveKey(0, 1, 0, 0),
new AetheriaRuntimeCurveKey(1, 0, 0, 0)
];
var catalog = new AetheriaRuntimeCatalogSnapshot([typed], [], []);
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(entity, catalog, 1);
var state = entity.EquipmentStates.Single();
RequireNear(0.5, state.ThermalPerformance, 0.000001,
"item thermal performance must come from mean occupied-cell temperature and authored curve");
Require(state.ThermalOnline && state.Online,
"item above shutdown performance with positive durability must remain online");
AetheriaRuntimeBehaviorSimulation.Step(0, [entity], [], catalog, 1);
RequireNear(10 - state.Wear, item.Durability, 0.000001,
"generic Wear behavior must apply the computed thermal wear potential at its authored cadence");
var temperature = entity.StatGrids.Single(value => value.Name == "temperature");
temperature.Values = [380];
item.OverrideShutdown = false;
entity.OverrideShutdown = true;
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(entity, catalog, 1);
state = entity.EquipmentStates.Single();
Require(!state.ThermalOnline && !state.Online,
"entity override alone must not bypass item thermal shutdown");
item.OverrideShutdown = true;
AetheriaRuntimeThermalSimulation.UpdateEquipmentStates(entity, catalog, 1);
Require(entity.EquipmentStates.Single().ThermalOnline,
"thermal shutdown bypass must require both entity-wide and item-local override");
}
private static void ThermalTopologyComesFromHullAndEquipment()
{
var entity = Entity(0, 0, "player");
entity.HullItemKey = "thermal-hull";
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0, Y = 0, Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "thermal-gear", Quality = 1, Durability = 1, Enabled = true }
}];
entity.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 1, Y = 0, Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "thermal-cargo-bay", Quality = 1, Durability = 1, Enabled = true }
}];
var hull = HullCatalogItem("thermal-hull", 2, 1, 0);
hull.Mass = 10;
hull.SpecificHeat = 2;
hull.Conductivity = 4;
hull.InteriorShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var gear = CatalogItem("thermal-gear");
gear.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
gear.Mass = 4;
gear.SpecificHeat = 5;
gear.Conductivity = 3;
var cargoBay = CatalogItem("thermal-cargo-bay");
cargoBay.Category = AetheriaRuntimeItemCategories.CargoBay;
cargoBay.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
cargoBay.Mass = 6;
cargoBay.SpecificHeat = 2;
cargoBay.Conductivity = 2;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, gear, cargoBay], [], []);
AetheriaRuntimeThermalSimulation.EnsureTopology(entity, catalog);
var mass = entity.StatGrids.Single(value => value.Name == "thermal-mass");
var conductivity = entity.StatGrids.Single(value => value.Name == "conductivity");
RequireNear(30, mass.Values[0], 0.000001,
"occupied cell thermal mass must include its proportional equipment mass");
RequireNear(22, mass.Values[1], 0.000001,
"typed cargo bays must contribute thermal mass at their authored hull cells");
RequireNear(3, conductivity.Values[0], 0.000001,
"occupied cell conductivity must come from installed equipment");
RequireNear(280, entity.StatGrids.Single(value => value.Name == "temperature").Values[1], 0.000001,
"authored hull topology must initialize each occupied cell at the fossil temperature");
}
private static void ThermalMedicalExposureUsesCockpitTemperature()
{
var entity = Entity(0, 0, "player");
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "medical-cockpit", Quality = 1, Durability = 1, Enabled = true }
}];
entity.StatGrids =
[
Grid("hull", 100), Grid("temperature", 340),
Grid("thermal-mass", 1), Grid("conductivity", 1)
];
var cockpit = CatalogItem("medical-cockpit",
new AetheriaRuntimeBehaviorPayload(0, "Cockpit", 0, []));
cockpit.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([cockpit], [], []);
var settings = AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault;
var heat = AetheriaRuntimeThermalMedicalSimulation.Step(entity, catalog, 300, settings);
RequireNear(0.3, entity.Heatstroke, 0.000001,
"cockpit heat must accumulate nonlinear heatstroke at the fossil rate");
Require(heat.HeatstrokeRiskCrossed,
"heatstroke must emit one upward severe-risk crossing");
RequireNear(340, Stat(entity, "cockpit-temperature"), 0.000001,
"cockpit cell temperature must become an authoritative Eve-readable fact");
entity.StatGrids.Single(value => value.Name == "temperature").Values = [280];
AetheriaRuntimeThermalMedicalSimulation.Step(entity, catalog, 1, settings);
RequireNear(0.1, entity.Heatstroke, 0.000001,
"heatstroke must recover linearly below the authored threshold");
entity.StatGrids.Single(value => value.Name == "temperature").Values = [263];
var cold = AetheriaRuntimeThermalMedicalSimulation.Step(entity, catalog, 300, settings);
RequireNear(0.3, entity.Hypothermia, 0.000001,
"cockpit cold must accumulate nonlinear hypothermia at the fossil rate");
Require(cold.HypothermiaRiskCrossed,
"hypothermia must use its own exposure for the intended severe-risk crossing");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "thermal-medical-surface", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var worldEntity = Flatten(surface.Surface.Root).Single(node =>
node.Id == "aetheria.daemon.game.world.entity.0");
RequireEqual("263", worldEntity.Props["cockpitTemperature"],
"Eve must publish canonical cockpit temperature rather than deriving it from effects");
RequireEqual("true", worldEntity.Props["hypothermiaRisk"],
"Eve must publish the current severe cold-risk state");
Require(worldEntity.Props.ContainsKey("heatstrokePostWeight") &&
worldEntity.Props.ContainsKey("severeHeatstrokeWeight"),
"Eve must publish the exact source weights used by the original heatstroke presentation");
RequireEqual("5", worldEntity.Props["heatstrokePhasingFrequency"],
"Eve must publish the provider-authored severe heatstroke pulse frequency");
RequireEqual("1", worldEntity.Props["deathTransitionSeconds"],
"Eve must publish the original thermal death crossfade duration");
}
private static void ThermalMedicalDeathUsesOrdinaryDestructionPath()
{
var entity = Entity(0, 0, "player");
entity.EntityId = "thermal-pilot";
entity.Equipment = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "lethal-cockpit", Quality = 1, Durability = 1, Enabled = true }
}];
entity.StatGrids =
[
Grid("hull", 100), Grid("temperature", 700),
Grid("thermal-mass", 1), Grid("conductivity", 1)
];
var cockpit = CatalogItem("lethal-cockpit",
new AetheriaRuntimeBehaviorPayload(0, "Cockpit", 0, []));
cockpit.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var catalog = new AetheriaRuntimeCatalogSnapshot([cockpit], [], []);
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "thermal-death", CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
};
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics(), catalog, frameId: 17);
RequireEqual("heatstroke", entity.CauseOfDeath,
"lethal cockpit heat must persist the typed cause of death");
Require(!entity.IsActive && !string.IsNullOrWhiteSpace(entity.DestructionId),
"thermal death must enter the ordinary exactly-once destruction path");
var death = run.GameEvents.Single(value => value.Kind == "entity.destroyed");
RequireEqual("heatstroke", death.Reason,
"Eve feedback must carry the thermal cause needed for the original death transition");
Require(run.GameEvents.Any(value => value.Kind == "pilot.heatstroke.risk"),
"a lethal step crossing the severe threshold must publish risk before death");
}
private static void ThermalCellsUseFossilConductionAndRadiation()
{
var entity = Entity(7, 0, "player");
entity.StatGrids =
[
Grid(AetheriaRuntimeThermalSimulation.TemperatureGrid, 2, 1, 300, 280),
Grid(AetheriaRuntimeThermalSimulation.ThermalMassGrid, 2, 1, 1, 1),
Grid(AetheriaRuntimeThermalSimulation.ConductivityGrid, 2, 1, 1, 1)
];
AetheriaRuntimeThermalSimulation.AddHeat(entity, 20);
RequireNear(310, GridValue(entity, AetheriaRuntimeThermalSimulation.TemperatureGrid, 0), 0.000001,
"heat energy must be divided across cells and thermal mass");
RequireNear(290, GridValue(entity, AetheriaRuntimeThermalSimulation.TemperatureGrid, 1), 0.000001,
"heat energy must be divided across cells and thermal mass");
AetheriaRuntimeThermalSimulation.Step(entity, 0.1);
var expectedHot = (310 / 0.01 + 290) / 101;
expectedHot -= Math.Pow(expectedHot, 3) * 0.00000001 * 0.1;
var expectedCool = (290 / 0.01 + 310) / 101;
expectedCool -= Math.Pow(expectedCool, 3) * 0.00000001 * 0.1;
RequireNear(expectedHot, GridValue(entity, AetheriaRuntimeThermalSimulation.TemperatureGrid, 0), 0.000001,
"hot cell must follow fossil conduction and radiation");
RequireNear(expectedCool, GridValue(entity, AetheriaRuntimeThermalSimulation.TemperatureGrid, 1), 0.000001,
"cool cell must follow fossil conduction and radiation");
RequireNear((expectedHot + expectedCool) / 2, Stat(entity, "heat"), 0.000001,
"legacy heat scalar must be derived from cell temperature");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "thermal-projection-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.7",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }]
};
var document = AetheriaRuntimeGameDocuments.CurrentEntity(new AetheriaRuntimeDaemonFrameDocument { Run = run });
RequireNear(Stat(entity, "heat"), document.Hud.MeanTemperature, 0.000001,
"Eve current-entity state must publish mean temperature");
RequireNear(GridValue(entity, AetheriaRuntimeThermalSimulation.TemperatureGrid, 0), document.Hud.MaximumTemperature, 0.000001,
"Eve current-entity state must publish maximum temperature");
Require(document.Hud.ThermalVisibility > 0, "Eve current-entity state must publish thermal visibility");
}
private static void YmirMovesProjectileAndReportsStableContact()
{
var (run, zone, target) = Scenario();
using var physics = NewPhysics();
physics.RetainWorlds(run.RunId, [zone.ZoneIndex]);
physics.Step(run.RunId, 0, 0, zone, zone.Entities, 0.1);
var step = physics.StepPhysicalPayloads(run.RunId, 0, 0, zone, zone.Entities, 0.1);
RequireEqual("ymir.box3d.retained-session.v1", physics.ImplementationId, "zone physics must identify its retained Ymir implementation");
RequireEqual(0, step.PhysicalPayloads.Count, "contacted projectile must not survive");
RequireEqual(1, step.Hits.Count, "Ymir must report one projectile contact");
RequireEqual(target.EntityIndex, step.Hits[0].TargetEntityIndex, "contact must resolve the daemon entity");
RequireEqual("aetheria.physical-payload.smoke-projectile", step.Hits[0].PhysicalPayloadBodyId, "physical payload body id must be stable");
RequireEqual("aetheria.daemon.entity.smoke.entity.2", step.Hits[0].TargetBodyId, "entity body id must be stable");
}
private static void PayloadQueryExcludesItsSource()
{
var source = Entity(1, 0, "player");
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [source],
PhysicalPayloads =
[
new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "source-exclusion",
SourceEntityIndex = source.EntityIndex,
PositionX = 0,
PositionZ = 0,
VelocityX = 10,
Radius = 1,
LifetimeSeconds = 5,
Active = true
}
]
};
using var physics = NewPhysics();
physics.RetainWorlds("source-exclusion-smoke", [zone.ZoneIndex]);
physics.Step("source-exclusion-smoke", 1, 0, zone, zone.Entities, 0.1);
var step = physics.StepPhysicalPayloads("source-exclusion-smoke", 1, 0, zone, zone.Entities, 0.1);
RequireEqual(0, step.Hits.Count, "a payload query must not hit its source body");
RequireEqual(1, step.PhysicalPayloads.Count, "source exclusion must leave the payload alive");
}
private static void PayloadBodiesDoNotCollideWithEachOther()
{
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [],
PhysicalPayloads =
[
new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "payload-left",
SourceEntityIndex = -1,
PositionX = -5,
VelocityX = 100,
Radius = 1,
LifetimeSeconds = 5,
Active = true
},
new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "payload-right",
SourceEntityIndex = -1,
PositionX = 5,
VelocityX = -100,
Radius = 1,
LifetimeSeconds = 5,
Active = true
}
]
};
using var physics = NewPhysics();
physics.RetainWorlds("payload-isolation-smoke", [zone.ZoneIndex]);
physics.Step("payload-isolation-smoke", 2, 0, zone, zone.Entities, 0.1);
var step = physics.StepPhysicalPayloads("payload-isolation-smoke", 2, 0, zone, zone.Entities, 0.1);
RequireEqual(0, step.Hits.Count, "payload bodies must not manufacture world-query hits against each other");
RequireEqual(2, step.PhysicalPayloads.Count, "payload bodies must survive crossing each other");
Require(step.PhysicalPayloads.Single(payload => payload.PayloadId == "payload-left").VelocityX > 0,
"payload-to-payload response must not reverse the left payload");
Require(step.PhysicalPayloads.Single(payload => payload.PayloadId == "payload-right").VelocityX < 0,
"payload-to-payload response must not reverse the right payload");
}
private static void OverlappingMineRemainsQueryableAfterArming()
{
var target = Entity(2, 0, "enemy");
var mine = new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "arming-overlap",
PayloadKind = "mine",
SourceEntityIndex = 1,
PositionX = 0,
PositionZ = 0,
Radius = 1,
TriggerRadius = 25,
ActivationDelaySeconds = 1,
LifetimeSeconds = 10,
Stationary = false,
Active = true
};
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [target],
PhysicalPayloads = [mine]
};
using var physics = NewPhysics();
physics.RetainWorlds("mine-arming-overlap-smoke", [zone.ZoneIndex]);
physics.Step("mine-arming-overlap-smoke", 1, 0, zone, zone.Entities, 0.5);
var unarmed = physics.StepPhysicalPayloads("mine-arming-overlap-smoke", 1, 0, zone, zone.Entities, 0.5);
RequireEqual(1, unarmed.Hits.Count, "an unarmed overlap may be observed without granting Ymir trigger authority");
mine.AgeSeconds = mine.ActivationDelaySeconds;
mine.Radius = mine.TriggerRadius;
zone.PhysicalPayloads = unarmed.PhysicalPayloads;
physics.Step("mine-arming-overlap-smoke", 2, 0, zone, zone.Entities, 0.5);
var armed = physics.StepPhysicalPayloads("mine-arming-overlap-smoke", 2, 0, zone, zone.Entities, 0.5);
RequireEqual(1, armed.Hits.Count,
"a retained mine already overlapping at activation must still yield an eligible Box3D overlap fact");
RequireEqual(target.EntityIndex, armed.Hits[0].TargetEntityIndex,
"the armed overlap fact must preserve the target identity");
}
private static void DaemonSimulationTreatsYmirHitAsPresentationOnly()
{
var (run, _, target) = Scenario();
AetheriaRuntimeDaemonSimulation.Step(
run,
new AetheriaRuntimeDaemonIntentState(),
0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
NewPhysics());
RequireEqual(100.0, Stat(target, "hull"), "presentation projectile contact must not write combat damage");
RequireEqual(0, run.Zones[0].PhysicalPayloads.Count, "spent projectile must leave daemon state");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "physical-payload.contact" && value.SubjectKey == "smoke-projectile"),
"Ymir contact must emit one projectile-identity impact event");
RequireEqual(0, run.GameEvents.Count(value => value.Kind == "entity.damaged" && value.TargetEntityIndex == target.EntityIndex),
"presentation projectile contact must not manufacture a damage event");
var feedback = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 0, Run = run });
Require(Flatten(feedback.Surface.Root).Any(node => node.Kind == "feedback.event" && node.Props["eventKind"] == "physical-payload.contact" && node.Props["subjectKey"] == "smoke-projectile"),
"Eve feedback must project authoritative projectile impact identity");
}
private static void ReactiveGameplayPublishesOnlyNewEventFacts()
{
var player = Entity(0, 0, "player");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "reactive-delta-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [player] }],
GameEvents =
[
new AetheriaRuntimeGameEventCommit { EventId = "old-event", Kind = "old", FrameId = 10, SourceEntityIndex = 0 },
new AetheriaRuntimeGameEventCommit { EventId = "new-event", Kind = "new", FrameId = 12, SourceEntityIndex = 0 }
],
ShotReceipts =
[
new AetheriaRuntimeShotReceiptCommit { ShotId = "old-shot", FrameId = 10, SourceEntityIndex = 0 },
new AetheriaRuntimeShotReceiptCommit { ShotId = "new-shot", FrameId = 12, SourceEntityIndex = 0 }
]
};
run.CurrentEntityKey = run.EntityRecordKey(0, player.EntityIndex);
var delta = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 12, Run = run },
eventsAfterFrame: 10);
var nodes = Flatten(delta.Surface.Root).ToArray();
Require(nodes.Count(node => node.Kind == "feedback.event") == 1 &&
nodes.Single(node => node.Kind == "feedback.event").Props["eventId"] == "new-event",
"reactive gameplay must publish only event facts newer than the previous delivery frame");
Require(nodes.Count(node => node.Kind == "shot.receipt") == 1 &&
nodes.Single(node => node.Kind == "shot.receipt").Props["shotId"] == "new-shot",
"reactive gameplay must not resend retained shot history in every surface update");
var empty = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 13, Run = run },
eventsAfterFrame: 12);
Require(!Flatten(empty.Surface.Root).Any(node =>
node.Kind == "feedback.event" || node.Kind == "shot.receipt"),
"a later reactive update must carry current variables without replaying old event arrays");
}
private static void ProjectileContactCannotKill()
{
var (run, _, target) = Scenario();
target.StatGrids.Single(grid => grid.Name == "hull").Values = [5];
AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault, NewPhysics(), frameId: 9);
Require(target.IsActive && Math.Abs(Stat(target, "hull") - 5) < 0.000001,
"even nominally lethal presentation contact must leave canonical target state untouched");
RequireEqual(0, run.GameEvents.Count(value => value.Kind == "entity.destroyed" && value.TargetEntityIndex == target.EntityIndex),
"presentation contact must not manufacture a destruction event");
}
private static void MissingWorldPhysicsOwnerCannotAdvanceShips()
{
var entity = Entity(0, 0, "player");
entity.VelocityX = 10;
var run = new AetheriaRuntimeRunCheckpointCommit { CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0", Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [entity] }] };
try
{
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-missing-world-physics.cc"), run,
new AetheriaRuntimeDaemonTickOptions { FrameId = 1, FixedDeltaSeconds = 0.1, BuildPublications = false });
}
catch (InvalidOperationException)
{
RequireNear(0, entity.PositionX, 0.000001, "ship must not advance without Ymir world authority");
return;
}
throw new InvalidOperationException("daemon advanced a ship without an authoritative world physics owner");
}
private static void TractorRampsAndPullsThroughYmirWithoutTeleportingCargo()
{
var ship = Entity(0, 0, "player");
ship.DirectionX = 0; ship.DirectionY = 1;
ship.LookDirectionX = 1; ship.LookDirectionY = 0;
var pickup = new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 3, PositionX = 60, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "salvage", Quantity = 1 }, LifetimeSeconds = 30 };
var run = new AetheriaRuntimeRunCheckpointCommit { CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0", Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship], DroppedPickups = [pickup] }] };
var command = AetheriaRuntimeDaemonCommandDocument.Create(AetheriaRuntimeDaemonCommandKinds.SetTractorPower, "pilot", "tractor-smoke", 0, "zone.0.entity.0");
command.CommandId = "tractor-on"; command.ScalarValue = 1;
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-tractor-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 1, FixedDeltaSeconds = 0.25, SimulationTimeSeconds = 0.25, ObservedCommands = [command], BuildPublications = false });
RequireNear(0.5, ship.TractorPower, 0.000001, "tractor power must use the fossil two-per-second ramp");
Require(pickup.VelocityX < 0 && pickup.PositionX < 60,
"Ymir must pull a pickup inside the reticle-aligned tractor volume without rotating the hull");
RequireEqual(1, pickup.Item.Quantity, "tractor force must not consume the pickup item");
RequireEqual(0, CargoQuantity(ship, "salvage"), "scooping must remain a separate capacity-checked transaction");
Require(pickup.AgeSeconds > 0 && pickup.AgeSeconds < pickup.LifetimeSeconds,
"daemon-owned pickup lifetime must advance independently of its SoA presentation");
var release = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.SetTractorPower,
"pilot",
"tractor-smoke",
1,
"zone.0.entity.0");
release.CommandId = "tractor-off";
release.ScalarValue = 0;
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-tractor-release-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = NewPhysics(), FrameId = 2, FixedDeltaSeconds = 0.25, SimulationTimeSeconds = 0.5, ObservedCommands = [release], BuildPublications = false });
RequireNear(0, ship.TractorTargetPower, 0.000001, "released tractor input must set daemon target power to zero");
RequireNear(0, ship.TractorPower, 0.000001, "daemon must ramp released tractor power back to zero at the fossil rate");
var inputCapability = AetheriaRuntimeInputCapabilityDocument
.FromFrame(new AetheriaRuntimeDaemonFrameDocument { FrameId = 2, Run = run })
.ToEveDocument();
var scoopAction = inputCapability.Actions.Single(action => action.ActionId == "pilot.scoop");
Require(scoopAction.InputValue?.Model == "button-hold.v1" &&
scoopAction.InputValue.PayloadKey == "scalarValue" &&
scoopAction.Payload["scalarValue"] == "0",
"tractor input must advertise press/release scalar ownership instead of a one-shot on command");
ship.TractorPower = 0.5;
ship.TractorTargetPower = 1;
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = run },
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("daemon"));
var beam = Flatten(surface.Surface.Root).Single(node => node.Kind == "beam.presentation");
Require(beam.Props["sourceEntityId"] == run.EntityRecordKey(0, ship.EntityIndex) &&
beam.Props["assetRole"] == "effect.beam.tractor" &&
beam.Props["directionMode"] == "source-forward.v1" &&
beam.Props["activationActionId"] == "pilot.scoop" &&
beam.Props["powerStateSemantic"] == AetheriaRuntimeDaemonSoaColumnKinds.BeamPower &&
beam.Props["radius"] == "25" &&
beam.Props["maximumDistance"] == "75",
"Eve beam presentation must project the same daemon tractor power and fossil volume without owning force or contact");
double PulledVelocity(double power, int frameId)
{
var actor = Entity(0, 0, "player");
actor.DirectionX = 0;
actor.DirectionY = 1;
actor.LookDirectionX = 1;
actor.LookDirectionY = 0;
actor.TractorPower = power;
actor.TractorTargetPower = power;
var cargo = new AetheriaRuntimeDroppedPickupCommit
{
PickupIndex = 30 + frameId,
PositionX = 60,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = "salvage", Quantity = 1 },
LifetimeSeconds = 30
};
var scenario = new AetheriaRuntimeRunCheckpointCommit
{
RunId = $"tractor-power-{frameId}",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [actor], DroppedPickups = [cargo] }]
};
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), $"aetheria-tractor-power-{frameId}.cc"),
scenario,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = NewPhysics(), FrameId = frameId, FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = frameId * 0.1, BuildPublications = false
});
return cargo.VelocityX;
}
RequireNear(PulledVelocity(0.02, 20), PulledVelocity(1, 21), 0.000001,
"tractor power above the fossil threshold must gate full traction rather than scale force");
var pickupAsset = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset => asset.Ref.AssetKey == "prefab.entity.pickup");
Require(pickupAsset.Ref.Metadata.TryGetValue("unityAssetPath", out var pickupPath) && pickupPath == "Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab",
"provider asset manifest must advertise the pickup visual used by Eve");
var assets = AetheriaRuntimeAssets.ProjectManifest(null).Assets;
var playerAsset = assets.Single(asset => asset.Ref.AssetKey == "prefab.entity.player");
RequireEqual("player", playerAsset.Ref.Metadata["presentationRole"],
"entity prefab fallback must use a specific presentation role instead of the generic world tag");
var boltEffect = assets.Single(asset => asset.Ref.AssetKey == "prefab.effect.shot.bolt");
RequireEqual("effect.shot.bolt", boltEffect.Ref.Metadata["presentationRole"],
"provider manifest must bind semantic shot feedback to its graduated EveUnity effect");
var tractorEffect = assets.Single(asset => asset.Ref.AssetKey == "prefab.effect.beam.tractor");
RequireEqual("effect.beam.tractor", tractorEffect.Ref.Metadata["presentationRole"],
"provider manifest must bind the stripped fossil tractor prefab to the generic beam role");
var thermalProfiles = assets.Where(asset =>
string.Equals(asset.Ref.Kind, AetheriaRuntimeAssetKinds.VolumeProfile, StringComparison.Ordinal) &&
asset.Ref.Metadata.TryGetValue("presentationRole", out var role) &&
(role.StartsWith("post.thermal.", StringComparison.Ordinal) ||
string.Equals(role, "post.death", StringComparison.Ordinal))).ToArray();
RequireEqual(5, thermalProfiles.Length,
"provider manifest must advertise every original thermal and death volume profile");
Require(thermalProfiles.All(asset =>
asset.Ref.Metadata.ContainsKey("unityAssetPath") &&
asset.Ref.Metadata.ContainsKey("presentationRole")),
"thermal profiles must cross the CDN boundary with exact Unity paths and semantic roles");
}
private static void PickupProximityPrecedesYmirCollisionShell()
{
var hull = CatalogItem("proximity-hull");
hull.HullCapacity = PerformanceStat(1);
var salvage = CatalogItem("proximity-salvage");
salvage.Volume = 1;
var cargoBay = CatalogItem("proximity-cargo-bay");
cargoBay.InteriorOccupiedCells = 1;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
var ship = Entity(0, 0, "player");
ship.HullItemKey = hull.ItemKey;
ship.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}
];
ship.CargoContents = [Cargo()];
var pickup = new AetheriaRuntimeDroppedPickupCommit
{
PickupIndex = 40,
PositionX = AetheriaRuntimeTractorMechanics.CollectionDistance,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 },
LifetimeSeconds = 30
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "pickup-proximity-before-contact",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones =
[
new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [ship],
DroppedPickups = [pickup]
}
]
};
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-pickup-before-contact.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
Catalog = catalog,
WorldPhysics = NewPhysics(),
FrameId = 1,
FixedDeltaSeconds = 0.02,
SimulationTimeSeconds = 0.02,
BuildPublications = false
});
Require(AetheriaRuntimeTractorMechanics.CollectionDistance > 25,
"pickup collection must begin outside the current 20 + 5 Ymir collision shell");
RequireEqual(1, CargoQuantity(ship, salvage.ItemKey),
"daemon XZ proximity must collect before Ymir contact can become the deciding fact");
RequireEqual(0, run.Zones[0].DroppedPickups.Count,
"a proximity-collected pickup must leave authoritative world state exactly once");
}
private static void CatalogItemIndexPreservesSnapshotLookupSemantics()
{
var first = CatalogItem("Indexed-Item");
var duplicate = CatalogItem("indexed-item");
var catalog = new AetheriaRuntimeCatalogSnapshot([first, duplicate], [], []);
Require(ReferenceEquals(first, catalog.FindItem("INDEXED-ITEM")),
"the derived catalog index must preserve first case-insensitive match semantics");
var replacement = CatalogItem("replacement-item");
catalog.Items = [replacement];
Require(catalog.FindItem("indexed-item") == null &&
ReferenceEquals(replacement, catalog.FindItem("REPLACEMENT-ITEM")),
"replacing snapshot items must invalidate the derived lookup index");
}
private static void PickupIsCapacityCheckedExactlyOnceAndExpires()
{
var hull = CatalogItem("pickup-hull"); hull.HullCapacity = PerformanceStat(1);
var salvage = CatalogItem("salvage"); salvage.Volume = 1;
var cargoBay = CatalogItem("pickup-cargo-bay"); cargoBay.InteriorOccupiedCells = 1;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
var ship = Entity(0, 0, "player"); ship.HullItemKey = hull.ItemKey; ship.CargoContents = [Cargo()];
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 } }];
var ally = Entity(1, AetheriaRuntimeTractorMechanics.CollectionDistance, "player"); ally.HullItemKey = hull.ItemKey;
ally.CargoContents = [Cargo()];
ally.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 } }];
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship, ally], DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 7, PositionX = AetheriaRuntimeTractorMechanics.CollectionDistance + 0.001, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 }, LifetimeSeconds = 30 }] };
var run = new AetheriaRuntimeRunCheckpointCommit { CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0", Zones = [zone] };
var forbiddenCommand = AetheriaRuntimeDaemonCommandDocument.Create(AetheriaRuntimeDaemonCommandKinds.PickUpLoot, "pilot", "pickup-smoke", 0, "zone.0.entity.0");
forbiddenCommand.CommandId = "pickup-command-forbidden";
forbiddenCommand.TargetEntityKey = "zone.0.entity.0";
var forbidden = AetheriaRuntimeDaemonOperations.Execute(run, [forbiddenCommand], new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(forbidden.RejectedCommandIds.Contains(forbiddenCommand.CommandId),
"client pickup commands must not own cargo collection");
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-outside-proximity.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = new ScriptedWorldPhysics(), FrameId = 1, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.1, Catalog = catalog, BuildPublications = false });
RequireEqual(1, zone.DroppedPickups.Count, "pickup outside the daemon collection distance must remain in the world");
RequireEqual(0, CargoQuantity(ship, salvage.ItemKey), "outside proximity must not mutate cargo");
zone.DroppedPickups.Single().PositionX = AetheriaRuntimeTractorMechanics.CollectionDistance;
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-proximity-dedup.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = new ScriptedWorldPhysics(), FrameId = 2, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.2, Catalog = catalog, BuildPublications = false });
RequireEqual(0, zone.DroppedPickups.Count, "pickup at the 2D collection boundary must be consumed");
RequireEqual(1, CargoQuantity(ship, salvage.ItemKey), "one pickup identity must commit cargo exactly once");
RequireEqual(0, CargoQuantity(ally, salvage.ItemKey),
"a closer allied simulation ship must not steal pickup ownership from the current controlled ship");
RequireEqual(1, run.GameEvents.Count(value => value.Kind == "pickup.collected" && value.PickupIndex == 7),
"one consumed pickup identity must emit one collection event");
var collectionEvent = run.GameEvents.Single(value => value.Kind == "pickup.collected" && value.PickupIndex == 7);
RequireNear(1, collectionEvent.ScalarValue, 0.000001,
"collection feedback must publish the committed cargo delta");
RequireNear(0, collectionEvent.AuxiliaryValue, 0.000001,
"collection feedback must publish cargo quantity before the transaction");
RequireEqual(0, run.PickupContactReceipts.Count,
"proximity collection must not mint obsolete Ymir-contact receipts");
var collectionFeedback = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 3, Run = run });
Require(Flatten(collectionFeedback.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "pickup.collected" && node.Props["pickupIndex"] == "7" &&
node.Props["itemKey"] == salvage.ItemKey && node.Props["scalarValue"] == "1" &&
node.Props["cargoQuantityBefore"] == "0" && node.Props["cargoQuantityAfter"] == "1"),
"Eve feedback must publish the exact cargo delta committed by daemon proximity");
zone.DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 8, PositionX = 100, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 }, LifetimeSeconds = 30 }];
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-expiry-smoke.cc"), run,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = new ScriptedWorldPhysics(), FrameId = 3, FixedDeltaSeconds = 30, SimulationTimeSeconds = 30, BuildPublications = false });
RequireEqual(0, zone.DroppedPickups.Count, "pickup must expire after the fossil thirty-second lifetime");
Require(run.GameEvents.Any(value => value.Kind == "pickup.expired" && value.PickupIndex == 8),
"daemon lifetime owner must emit authoritative pickup expiry event");
}
private static void TradePurchaseDerivesAcceptanceFromDaemonState()
{
var ore = CatalogItem("trade-ore");
ore.Price = 40;
ore.Volume = 1;
ore.Stackable = true;
ore.MaxStack = 4;
ore.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var cargoBay = CatalogItem("trade-cargo-bay");
cargoBay.InteriorShapeWidth = 3;
cargoBay.InteriorShapeHeight = 1;
cargoBay.InteriorOccupiedCells = 3;
cargoBay.InteriorShapeCells =
[
new AetheriaRuntimeShapeCell(0, 0),
new AetheriaRuntimeShapeCell(1, 0),
new AetheriaRuntimeShapeCell(2, 0)
];
var catalog = new AetheriaRuntimeCatalogSnapshot([ore, cargoBay], [], []);
var station = Entity(0, 0, "station");
station.Kind = "station";
station.CargoContents = [Cargo((ore.ItemKey, 4, 2, 3), (ore.ItemKey, 4, 3, 3))];
station.DockingBayAssignments = [1];
station.ChildEntityIndices = [1];
var ship = Entity(1, 0, "player");
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}];
ship.CargoContents = [Cargo((ore.ItemKey, 3, 0, 0))];
var zone = new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [station, ship] };
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "trade-authority-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.1",
Credits = 5000,
Zones = [zone]
};
var command = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TradePurchase,
"pilot",
"trade-authority-smoke",
1,
run.CurrentEntityKey);
command.TradePurchase.ItemKey = ore.ItemKey;
command.TradePurchase.Quantity = 4;
command.TradePurchase.StationCargoIndex = 0;
command.TradePurchase.TargetCargoIndex = 0;
command.TradePurchase.SourceX = 2;
command.TradePurchase.SourceY = 3;
command.TradePurchase.PurchaseKind = "docked_ship";
command.TradePurchase.UnitPrice = -999;
command.TradePurchase.TotalPrice = -999;
command.TradePurchase.StationEntityKey = "forged-station";
command.TradePurchase.TargetEntityKey = "forged-target";
command.TradePurchase.CreatesDockedShip = true;
Require(AetheriaRuntimeRunCheckpointCommit.TryParseEntityKey(run.CurrentEntityKey, out _, out _),
"trade smoke current entity key must be daemon-resolvable");
RequireEqual(0, AetheriaRuntimeCargoCapacityQueries.UnitsThatFit(ship, catalog, ore.ItemKey, 0),
"the old volume-only capacity proxy must expose why it cannot own spatial stack placement");
var daemonUnitPrice = AetheriaRuntimeDaemonTradeItemQueries.TradeItemValue(
ore,
station.CargoContents[0].Items[0].Item,
catalog.TradeValueSettings).Price;
Require(daemonUnitPrice > 0, "trade smoke daemon catalog price must be positive");
var result = AetheriaRuntimeDaemonOperations.Execute(
run,
[command],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(result.AppliedCommandIds.Contains(command.CommandId),
"daemon must accept a valid commodity purchase regardless of forged compatibility opinions");
RequireEqual(5000 - daemonUnitPrice * 4, run.Credits,
"daemon catalog price must own trade credit mutation");
RequireEqual(7, CargoQuantity(ship, ore.ItemKey), "daemon cargo capacity and placement must own delivery");
Require(ship.CargoContents.Single().Items
.Where(slot => slot.Item.ItemKey == ore.ItemKey)
.Select(slot => (slot.X, slot.Y, slot.Item.Quantity))
.SequenceEqual([(0, 0, 4), (1, 0, 3)]),
"a purchase must fill an existing stack before placing the remainder in the first authored free cell");
RequireEqual(2, zone.Entities.Count, "forged ship-creation opinion must not materialize an entity");
AetheriaRuntimeDaemonCommandDocument Contender(string clientId, string commandId)
{
var contender = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TradePurchase,
clientId,
run.RunId,
2,
run.CurrentEntityKey);
contender.CommandId = commandId;
contender.TradePurchase.ItemKey = ore.ItemKey;
contender.TradePurchase.Quantity = 4;
contender.TradePurchase.StationCargoIndex = 0;
contender.TradePurchase.TargetCargoIndex = 0;
contender.TradePurchase.SourceX = 3;
contender.TradePurchase.SourceY = 3;
return contender;
}
var firstClient = Contender("pilot-a", "trade-contested-a");
var secondClient = Contender("pilot-b", "trade-contested-b");
var creditsBeforeContest = run.Credits;
var contested = AetheriaRuntimeDaemonOperations.Execute(
run,
[firstClient, secondClient],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(contested.AppliedCommandIds.SequenceEqual([firstClient.CommandId]),
"the daemon batch reducer must serialize contested stock to exactly one winner");
Require(contested.RejectedCommandIds.SequenceEqual([secondClient.CommandId]),
"the command observing exhausted stock must be rejected exactly once");
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.TradeStockUnavailable,
contested.RejectedCommandReasons[secondClient.CommandId],
"contested stock must retain the exact daemon-owned rejection reason");
RequireEqual(creditsBeforeContest - daemonUnitPrice * 4, run.Credits,
"contested stock must debit the shared run wallet exactly once");
RequireEqual(11, CargoQuantity(ship, ore.ItemKey),
"contested stock must deliver exactly one winning stack quantity");
RequireEqual(0, CargoQuantity(station, ore.ItemKey),
"the winning purchase must exhaust authoritative station stock");
var frame = AetheriaRuntimeDaemonFrameDocument.Create(
run, "daemon", "trade-contest", 2, 0.04, 0.02);
frame.AppliedCommandIds = contested.AppliedCommandIds;
frame.RejectedCommandIds = contested.RejectedCommandIds;
frame.RejectedCommandReasons = contested.RejectedCommandReasons;
var winnerFact = AetheriaRuntimeCommittedCommandFactDocument.FromAppliedCommand(
frame, firstClient, "aetheria.local");
var loserFact = AetheriaRuntimeCommittedCommandFactDocument.FromRejectedCommand(
frame, secondClient, "aetheria.local");
var winnerReceipt = AetheriaRuntimeDaemonReceiptProjector.Project(
winnerFact, AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId);
var loserReceipt = AetheriaRuntimeDaemonReceiptProjector.Project(
loserFact, AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId);
RequireEqual("reconciled", winnerReceipt.State,
"the winning client must receive an authoritative generic Eve receipt");
Require(loserReceipt.State == "denied" &&
loserReceipt.Message.Contains(
AetheriaRuntimeDaemonRejectionReasons.TradeStockUnavailable,
StringComparison.Ordinal),
"the losing client must receive the exact contested-stock reason through generic Eve");
var root = Path.Combine(Path.GetTempPath(), "aetheria-trade-contest-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(frame).GetAwaiter().GetResult();
node.MutableDocument<AetheriaRuntimeCommittedCommandFactDocument>(
new CultRecordKey(AetheriaRuntimeCommittedCommandFactDocument.CreateRecordKey(winnerFact.FactId)))
.ReplaceAsync(winnerFact).GetAwaiter().GetResult();
node.MutableDocument<AetheriaRuntimeCommittedCommandFactDocument>(
new CultRecordKey(AetheriaRuntimeCommittedCommandFactDocument.CreateRecordKey(loserFact.FactId)))
.ReplaceAsync(loserFact).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
var durableFrame = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable contested-trade frame is missing.");
var durableWinner = node.MutableDocument<AetheriaRuntimeCommittedCommandFactDocument>(
new CultRecordKey(AetheriaRuntimeCommittedCommandFactDocument.CreateRecordKey(winnerFact.FactId)))
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable winning trade fact is missing.");
var durableLoser = node.MutableDocument<AetheriaRuntimeCommittedCommandFactDocument>(
new CultRecordKey(AetheriaRuntimeCommittedCommandFactDocument.CreateRecordKey(loserFact.FactId)))
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable rejected trade fact is missing.");
var durableStation = durableFrame.Run.Zones.Single().Entities.Single(entity => entity.EntityIndex == 0);
var durableShip = durableFrame.Run.Zones.Single().Entities.Single(entity => entity.EntityIndex == 1);
RequireEqual(run.Credits, durableFrame.Run.Credits,
"CultCache reconnect must preserve the single authoritative debit");
RequireEqual(0, CargoQuantity(durableStation, ore.ItemKey),
"CultCache reconnect must not resurrect contested station stock");
RequireEqual(11, CargoQuantity(durableShip, ore.ItemKey),
"CultCache reconnect must preserve the winning cargo delivery exactly once");
RequireEqual("reconciled", AetheriaRuntimeDaemonReceiptProjector.Project(
durableWinner, AetheriaRuntimeDaemonGameSurfaceBuilder.SurfaceId).State,
"the winning generic Eve receipt must survive reconnect");
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.TradeStockUnavailable,
durableLoser.RejectionReason,
"the losing generic Eve receipt reason must survive reconnect");
}
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void TradeSaleIsDaemonPricedSpatialAndAtomic()
{
var ore = CatalogItem("sale-ore");
ore.Price = 40;
ore.Stackable = true;
ore.MaxStack = 10;
ore.ShapeWidth = 1;
ore.ShapeHeight = 1;
ore.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
var cargoBay = CatalogItem("sale-cargo-bay");
cargoBay.InteriorShapeWidth = 2;
cargoBay.InteriorShapeHeight = 1;
cargoBay.InteriorShapeCells =
[
new AetheriaRuntimeShapeCell(0, 0),
new AetheriaRuntimeShapeCell(1, 0)
];
var catalog = new AetheriaRuntimeCatalogSnapshot([ore, cargoBay], [], []);
var station = Entity(0, 0, "station");
station.Kind = "station";
station.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}];
station.CargoContents = [Cargo((ore.ItemKey, 8, 0, 0))];
station.DockingBayAssignments = [1];
var ship = Entity(1, 0, "player");
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}];
ship.CargoContents = [new AetheriaRuntimeCargoBayLoadoutCommit
{
Items = [new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 0,
Y = 0,
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = ore.ItemKey,
Quantity = 5,
Quality = 0.75,
Temperature = 333
}
}]
}];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "trade-sale-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.1",
Credits = 100,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [station, ship] }]
};
var sale = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TradeSale,
"pilot",
"trade-sale-smoke",
1,
run.CurrentEntityKey);
sale.TradeSale.ItemKey = ore.ItemKey;
sale.TradeSale.Quantity = 3;
sale.TradeSale.SourceCargoIndex = 0;
sale.TradeSale.SourceX = 0;
sale.TradeSale.SourceY = 0;
var unitPrice = AetheriaRuntimeDaemonTradeItemQueries.TradeItemValue(
ore,
ship.CargoContents[0].Items[0].Item,
catalog.TradeValueSettings).Price;
var result = AetheriaRuntimeDaemonOperations.Execute(
run,
[sale],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(result.AppliedCommandIds.Contains(sale.CommandId),
"valid docked sale must commit through the daemon economy transaction");
RequireEqual(100 + unitPrice * 3, run.Credits,
"sale payout must be derived from the authoritative source instance and catalog");
RequireEqual(2, CargoQuantity(ship, ore.ItemKey),
"sale must remove exactly the accepted quantity from seller cargo");
RequireEqual(11, CargoQuantity(station, ore.ItemKey),
"station stock must receive the exact sold quantity");
RequireEqual(10, station.CargoContents[0].Items[0].Item.Quantity,
"station stock must fill the first compatible stack before allocating a new cell");
var allocated = station.CargoContents[0].Items.Single(slot => slot.X == 1 && slot.Y == 0);
RequireEqual(1, allocated.Item.Quantity,
"station stock overflow must use the first free authored cargo cell");
RequireNear(0.75, allocated.Item.Quality, 0.000001,
"sale placement must preserve source instance quality");
RequireNear(333, allocated.Item.Temperature, 0.000001,
"sale placement must preserve source instance temperature");
station.CargoContents[0].Items[1].Item.Quantity = 10;
var rejectedSale = AetheriaRuntimeDaemonCommandDocument.Create(
AetheriaRuntimeDaemonCommandKinds.TradeSale,
"pilot",
"trade-sale-smoke",
2,
run.CurrentEntityKey);
rejectedSale.TradeSale.ItemKey = ore.ItemKey;
rejectedSale.TradeSale.Quantity = 1;
rejectedSale.TradeSale.SourceCargoIndex = 0;
rejectedSale.TradeSale.SourceX = 0;
rejectedSale.TradeSale.SourceY = 0;
var creditsBeforeRejection = run.Credits;
result = AetheriaRuntimeDaemonOperations.Execute(
run,
[rejectedSale],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(result.RejectedCommandIds.Contains(rejectedSale.CommandId),
"station stock without spatial capacity must reject the sale");
RequireEqual(AetheriaRuntimeDaemonRejectionReasons.TradeStationNoFit,
result.RejectedCommandReasons[rejectedSale.CommandId],
"full station stock must expose the exact trade rejection reason");
RequireEqual(creditsBeforeRejection, run.Credits,
"rejected sale must not credit the seller");
RequireEqual(2, CargoQuantity(ship, ore.ItemKey),
"rejected sale must not remove seller cargo");
}
private static void StationBodiesCannotConsumePickups()
{
var hull = CatalogItem("station-filter-hull"); hull.HullCapacity = PerformanceStat(1);
var salvage = CatalogItem("station-filter-salvage"); salvage.Volume = 1;
var cargoBay = CatalogItem("station-filter-cargo-bay"); cargoBay.InteriorOccupiedCells = 1;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
var station = Entity(0, 0, "player");
station.Kind = "station";
station.HullItemKey = hull.ItemKey;
station.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}];
station.CargoContents = [Cargo()];
var ship = Entity(1, 200, "player");
var run = new AetheriaRuntimeRunCheckpointCommit
{
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.1",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [station, ship],
DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit
{
PickupIndex = 9,
PositionX = 0,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 },
LifetimeSeconds = 30
}]
}]
};
using var physics = NewPhysics();
AetheriaRuntimeDaemonTickRunner.Tick(
Path.Combine(Path.GetTempPath(), "aetheria-pickup-station-filter.cc"),
run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.1,
Catalog = catalog,
BuildPublications = false
});
RequireEqual(1, run.Zones[0].DroppedPickups.Count,
"station world bodies must not consume ship-scoop pickups");
RequireEqual(0, CargoQuantity(station, salvage.ItemKey),
"station cargo must remain unchanged because only ships collect by proximity");
}
private static void PickupProximityCollectsOrBounces()
{
var hull = CatalogItem("contact-hull"); hull.HullCapacity = PerformanceStat(1);
var salvage = CatalogItem("contact-salvage"); salvage.Volume = 1;
var cargoBay = CatalogItem("contact-cargo-bay"); cargoBay.InteriorOccupiedCells = 1;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
AetheriaRuntimeRunCheckpointCommit Scenario(bool full)
{
var ship = Entity(0, 0, "player"); ship.HullItemKey = hull.ItemKey;
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit { Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 } }];
ship.CargoContents = [full ? Cargo((salvage.ItemKey, 1, 0, 0)) : Cargo()];
return new AetheriaRuntimeRunCheckpointCommit { CurrentZoneIndex = 0, CurrentEntityKey = "zone.0.entity.0", Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship], DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit { PickupIndex = 10, PositionX = 20, Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 }, LifetimeSeconds = 30 }] }] };
}
var open = Scenario(false);
using var openPhysics = NewPhysics();
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-proximity-open.cc"), open,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = openPhysics, FrameId = 1, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.1, Catalog = catalog, BuildPublications = false });
RequireEqual(0, open.Zones[0].DroppedPickups.Count, "2D ship proximity with capacity must collect pickup automatically");
RequireEqual(1, CargoQuantity(open.Zones[0].Entities[0], salvage.ItemKey), "proximity collection must commit cargo once");
RequireEqual(1, open.GameEvents.Count(value => value.Kind == "pickup.collected" && value.PickupIndex == 10),
"proximity collection must emit one stable event");
RequireEqual(0, open.PickupContactReceipts.Count,
"proximity collection must not depend on a Box3D Begin fact");
var full = Scenario(true);
using var fullPhysics = NewPhysics();
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-proximity-full.cc"), full,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = fullPhysics, FrameId = 1, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.1, Catalog = catalog, BuildPublications = false });
RequireEqual(1, full.Zones[0].DroppedPickups.Count, "full hold must leave the nearby pickup alive");
Require(full.Zones[0].DroppedPickups[0].VelocityX > 20, "failed pickup must receive the fossil outward kick");
var rejectedPickup = full.Zones[0].DroppedPickups[0];
var rejectedShip = full.Zones[0].Entities[0];
Require(Math.Sqrt(
Math.Pow(rejectedPickup.PositionX - rejectedShip.PositionX, 2) +
Math.Pow(rejectedPickup.PositionZ - rejectedShip.PositionZ, 2)) >
AetheriaRuntimeTractorMechanics.CollectionDistance,
"capacity rejection must move the pickup outside collection proximity before the next step");
RequireEqual(1, full.GameEvents.Count(value => value.Kind == "pickup.rejected" && value.PickupIndex == 10),
"capacity rejection must emit one stable event");
var rejectionEvent = full.GameEvents.Single(value => value.Kind == "pickup.rejected" && value.PickupIndex == 10);
RequireEqual("cargo-capacity", rejectionEvent.Reason,
"capacity rejection feedback must name the provider-owned reason");
RequireNear(1, rejectionEvent.AuxiliaryValue, 0.000001,
"capacity rejection feedback must preserve the pre-collection cargo quantity");
RequireEqual(0, full.PickupContactReceipts.Count,
"capacity rejection must not mint obsolete contact receipts");
AetheriaRuntimeDaemonTickRunner.Tick(Path.Combine(Path.GetTempPath(), "aetheria-pickup-proximity-full-second.cc"), full,
new AetheriaRuntimeDaemonTickOptions { WorldPhysics = fullPhysics, FrameId = 2, FixedDeltaSeconds = 0.1, SimulationTimeSeconds = 0.2, Catalog = catalog, BuildPublications = false });
RequireEqual(1, full.GameEvents.Count(value => value.Kind == "pickup.rejected" && value.PickupIndex == 10),
"the outward kick must prevent an immediate second rejection event");
var feedback = AetheriaRuntimeDaemonGameSurfaceBuilder.BuildReactiveGameplay(
new AetheriaRuntimeDaemonFrameDocument { FrameId = 1, Run = full });
Require(Flatten(feedback.Surface.Root).Any(node => node.Kind == "feedback.event" &&
node.Props["eventKind"] == "pickup.rejected" && node.Props["pickupIndex"] == "10" &&
node.Props["reason"] == "cargo-capacity" &&
node.Props["cargoQuantityBefore"] == "1" && node.Props["cargoQuantityAfter"] == "1"),
"Eve surface must project authoritative pickup feedback chronology");
}
private static (AetheriaRuntimeRunCheckpointCommit Run, AetheriaRuntimeZoneSnapshotCommit Zone, AetheriaRuntimeEntitySnapshotCommit Target) Scenario()
{
var source = Entity(1, -100, "player");
var target = Entity(2, 30, "enemy");
var zone = new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [source, target],
PhysicalPayloads =
[
new AetheriaRuntimePhysicalPayloadCommit
{
PayloadId = "smoke-projectile",
SourceEntityIndex = 1,
TargetEntityIndex = 2,
PositionX = 0,
PositionZ = 0,
VelocityX = 100,
VelocityY = 0,
Radius = 1,
ContactMagnitude = 12,
LifetimeSeconds = 5,
Active = true
}
]
};
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "ymir-projectile-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.1",
Zones = [zone]
};
return (run, zone, target);
}
private static AetheriaRuntimeEntitySnapshotCommit Entity(int index, double x, string faction) => new()
{
EntityIndex = index,
EntityId = $"smoke.entity.{index}",
Kind = "ship",
FactionKey = faction,
PositionX = x,
PositionZ = 0,
TargetEntityIndex = -1,
IsActive = true,
StatGrids = [Grid("hull", 100), Grid("shield", 0), Grid("heat", 0)]
};
private static AetheriaRuntimeEntityStatGridCommit Grid(string name, double value) => new()
{
Name = name,
Width = 1,
Height = 1,
Values = [value]
};
private static AetheriaRuntimeEntityStatGridCommit Grid(string name, int width, int height, params double[] values) => new()
{
Name = name,
Width = width,
Height = height,
Values = values
};
private static double GridValue(AetheriaRuntimeEntitySnapshotCommit entity, string name, int index) =>
entity.StatGrids.Single(grid => string.Equals(grid.Name, name, StringComparison.OrdinalIgnoreCase)).Values[index];
private static IEnumerable<AetheriaRuntimeSurfaceComponent> Flatten(AetheriaRuntimeSurfaceComponent root)
{
yield return root;
foreach (var child in root.Children)
foreach (var descendant in Flatten(child))
yield return descendant;
}
private static double Stat(AetheriaRuntimeEntitySnapshotCommit entity, string name) =>
entity.StatGrids.Single(grid => string.Equals(grid.Name, name, StringComparison.OrdinalIgnoreCase)).Values[0];
private static void RequireEqual<T>(T expected, T actual, string message)
{
if (!EqualityComparer<T>.Default.Equals(expected, actual))
throw new InvalidOperationException($"{message}. Expected {expected}; actual {actual}.");
}
private static void Require(bool condition, string message)
{
if (!condition)
throw new InvalidOperationException(message);
}
private static void RequireNear(double expected, double actual, double tolerance, string message)
{
if (Math.Abs(expected - actual) > tolerance)
throw new InvalidOperationException($"{message}. Expected {expected}; actual {actual}.");
}
private static void CultCacheRestartPreservesLoadoutAndBehaviorState()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-loadout-restart-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var controller = CatalogItem("reconnect-controller",
new AetheriaRuntimeBehaviorPayload(0, "Thermotoggle", 0,
[
new AetheriaRuntimeBehaviorField(1, Number(300)),
new AetheriaRuntimeBehaviorField(2, BoolValue(false)),
new AetheriaRuntimeBehaviorField(3, BoolValue(true))
]));
controller.Name = "Reconnect Controller";
var hull = HullCatalogItem("reconnect-hull", 9, 7, 100);
var cargoBay = CatalogItem("reconnect-cargo-bay");
cargoBay.InteriorShapeWidth = 6;
cargoBay.InteriorShapeHeight = 4;
var addedModule = CatalogItem("reconnect-added-module");
addedModule.ShapeWidth = 1;
addedModule.ShapeHeight = 1;
addedModule.OccupiedCells = 1;
addedModule.ShapeCells = [new AetheriaRuntimeShapeCell(0, 0)];
addedModule.HardpointType = "";
var catalog = new AetheriaRuntimeCatalogSnapshot(
[hull, cargoBay, controller, addedModule], [], []);
var ship = Entity(0, 0, "player");
ship.HullItemKey = hull.ItemKey;
ship.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
X = 3,
Y = 4,
Rotation = "CounterClockwise",
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = controller.ItemKey,
Quality = 0.73,
Durability = 0.61,
Quantity = 1,
Enabled = true,
OverrideShutdown = true,
Temperature = 417
}
}
];
ship.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = cargoBay.ItemKey,
Quantity = 1,
Quality = 1,
Durability = 1,
Enabled = true
}
}
];
ship.CargoContents = [Cargo(("reconnect-salvage", 7, 2, 1))];
ship.WeaponGroups = [new[] { 0 }];
ship.ActiveWeaponGroups = [true];
ship.EquipmentStates =
[
new AetheriaRuntimeEquipmentStateCommit
{
EquipmentIndex = 0,
Temperature = 417,
PreviousTemperature = 410,
ThermalPerformance = 0.8,
DurabilityPerformance = 0.61,
Wear = 0.12,
ThermalOnline = true,
DurabilityOnline = true,
Online = true
}
];
ship.BehaviorStates =
[
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = AetheriaRuntimeBehaviorStateProjector.EquipmentOwnerKind,
OwnerIndex = 0,
BehaviorIndex = 0,
BehaviorKind = "Thermotoggle",
ThermotoggleTargetTemperature = 412,
ChainReached = true,
ChainSucceeded = true,
ChainCompleted = true
}
];
ship.WeaponStates =
[
new AetheriaRuntimeWeaponStateCommit
{
OwnerKind = "equipment",
OwnerIndex = 0,
BehaviorIndex = 1,
BehaviorKind = AetheriaRuntimeBehaviorKinds.InstantWeapon,
Ammo = 9,
CooldownProgress = 0.37,
LockProgress = 0.82,
LockTargetEntityIndex = 4,
ShotSequence = 23
}
];
ship.ActiveConsumables =
[
new AetheriaRuntimeActiveConsumableCommit
{
EffectId = "effect:reconnect-tonic:1",
ItemKey = "reconnect-tonic",
Quality = 0.67,
Duration = 30,
RemainingDuration = 12,
BehaviorStates =
[
new AetheriaRuntimeConsumableBehaviorStateCommit
{
BehaviorId = "tonic-modifier",
BehaviorIndex = 0,
BehaviorKind = AetheriaRuntimeBehaviorKinds.StatModifier,
ScalarState = 0.42,
StatModifierApplied = true,
StatModifierExecuted = true,
StatModifierTargetStatCount = 3
}
]
}
];
ship.LoadoutGeneration = new AetheriaRuntimeLoadoutGenerationReceiptCommit
{
Seed = 0xA37E,
SourceZoneIndex = 2,
AvailabilityFactionKey = "player",
PriceExponent = 0.25,
Selections =
[
new AetheriaRuntimeLoadoutGenerationSelectionCommit
{
Role = "controller",
ItemKey = controller.ItemKey,
ManufacturerKey = "player",
Price = 1200,
ManufacturerDistance = 1,
Allegiance = 0.9
}
]
};
var station = Entity(1, 0, "player");
station.Kind = "station";
station.DockingBayAssignments = [ship.EntityIndex];
station.CargoBays =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{
ItemKey = cargoBay.ItemKey,
Quantity = 1,
Quality = 1,
Durability = 1,
Enabled = true
}
}
];
station.CargoContents =
[
Cargo((addedModule.ItemKey, 1, 0, 0))
];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "loadout-restart-smoke",
CurrentZoneIndex = 0,
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [ship, station] }]
};
run.CurrentEntityKey = run.EntityRecordKey(0, ship.EntityIndex);
var frame = AetheriaRuntimeDaemonFrameDocument.Create(
run, "smoke", "loadout-restart", 41, 4.1, 0.1);
var shipKey = run.EntityRecordKey(0, ship.EntityIndex);
var stationKey = run.EntityRecordKey(0, station.EntityIndex);
var surface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build(
frame,
new AetheriaRuntimeDaemonHealthDocument(),
AetheriaRuntimeDaemonCommandBoundaryDocument.Create("smoke"),
catalog: catalog);
var equipOperation = AetheriaRuntimeDaemonSurfaceCommandCatalog.CommandName(
AetheriaRuntimeDaemonCommandKinds.EquipItem);
Require(surface.Commands.Any(command => command.Command == equipOperation),
"docked Eve surface must advertise the generic equip operation before reconnect");
var request = new EveSurfaceCommandRequest(
"aetheria",
AetheriaRuntimeDaemonGameSurfaceBuilder.PilotSurfaceId,
CultMesh.OperationInvocation(equipOperation),
CultMesh.OperationPayload(
("entityId", shipKey),
("sourceKind", AetheriaRuntimeRefitSourceKinds.Cargo),
("originEntityKey", stationKey),
("originIndex", "0"),
("destinationEntityKey", shipKey),
("itemKey", addedModule.ItemKey),
("sourceX", "0"),
("sourceY", "0"),
("destinationX", "5"),
("destinationY", "2"),
("hasDestinationPosition", "true")),
DateTimeOffset.UtcNow,
"loadout-restart-smoke");
Require(AetheriaRuntimeDaemonOperationsClient.TryCreateSurfaceCommandDocument(
request,
frame,
".",
"loadout-restart-smoke",
"refit",
out var equipCommand) && equipCommand != null,
"generic Eve equip request must translate into a typed daemon command");
var refit = AetheriaRuntimeDaemonOperations.Execute(
run,
[equipCommand!],
new AetheriaRuntimeDaemonOperationContext { Catalog = catalog });
Require(refit.AppliedCommandIds.Contains(equipCommand!.CommandId, StringComparer.Ordinal),
"generic Eve equip command must commit through the atomic daemon refit transaction: " +
string.Join(",", refit.RejectedCommandReasons.Values));
Require(ship.Equipment.Any(slot => slot.Item.ItemKey == addedModule.ItemKey &&
slot.X == 5 && slot.Y == 2),
"accepted generic refit must place the exact module in daemon world truth");
RequireEqual(0, station.CargoContents.Single().Items.Count,
"accepted generic refit must remove the exact source item atomically");
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(frame).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
var durable = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(
AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable loadout restart frame is missing.");
var restored = durable.Run.Zones.Single().Entities.Single(entity => entity.EntityIndex == ship.EntityIndex);
var restoredStation = durable.Run.Zones.Single().Entities.Single(entity => entity.EntityIndex == station.EntityIndex);
var installed = restored.Equipment.Single(slot => slot.Item.ItemKey == controller.ItemKey);
RequireEqual("reconnect-hull", restored.HullItemKey,
"CultCache restart must preserve daemon-owned hull identity");
Require(installed.X == 3 && installed.Y == 4 && installed.Rotation == "CounterClockwise",
"CultCache restart must preserve exact equipment placement and rotation");
RequireNear(0.73, installed.Item.Quality, 0.000001,
"CultCache restart must preserve installed item quality");
RequireNear(0.61, installed.Item.Durability, 0.000001,
"CultCache restart must preserve installed item durability");
Require(installed.Item.OverrideShutdown && installed.Item.Temperature == 417,
"CultCache restart must preserve per-instance thermal controls");
RequireEqual(7, restored.CargoContents.Single().Items.Single().Item.Quantity,
"CultCache restart must preserve cargo stack quantity");
Require(restored.WeaponGroups.Single().SequenceEqual(new[] { 0 }) &&
restored.ActiveWeaponGroups.SequenceEqual(new[] { true }),
"CultCache restart must preserve weapon membership and held state");
RequireNear(0.12, restored.EquipmentStates.Single().Wear, 0.000001,
"CultCache restart must preserve equipment wear state");
RequireNear(412, restored.BehaviorStates.Single().ThermotoggleTargetTemperature, 0.000001,
"CultCache restart must preserve executable behavior state");
Require(restored.BehaviorStates.Single().ChainCompleted,
"CultCache restart must preserve specialized chain chronology");
Require(restored.WeaponStates.Single().ShotSequence == 23 &&
restored.WeaponStates.Single().Ammo == 9,
"CultCache restart must preserve weapon chronology and ammunition");
var consumableState = restored.ActiveConsumables.Single().BehaviorStates.Single();
Require(consumableState.StatModifierApplied && consumableState.StatModifierExecuted &&
consumableState.StatModifierTargetStatCount == 3,
"CultCache restart must preserve active consumable execution state");
Require(restored.LoadoutGeneration?.Selections.Single().ItemKey == controller.ItemKey,
"CultCache restart must preserve generated-loadout provenance");
Require(restored.Equipment.Any(slot => slot.Item.ItemKey == addedModule.ItemKey &&
slot.X == 5 && slot.Y == 2),
"CultCache restart must preserve the generic Eve refit's accepted placement");
RequireEqual(0, restoredStation.CargoContents.Single().Items.Count,
"CultCache restart must not resurrect the generic refit's consumed source item");
var capability = AetheriaRuntimeInputCapabilityDocument.FromFrame(
durable, catalog: catalog);
RequireEqual("1", capability.Actions.Single(action =>
action.ActionId == "weapon-group.0.fire").Payload["currentValue"],
"generic Eve reconnect must observe the daemon-owned held weapon state");
RequireNear(412, capability.Actions.Single(action =>
action.ActionId == "equipment.0.behavior.0").InputValue?.CurrentValue ?? double.NaN,
0.000001,
"generic Eve reconnect must observe the daemon-owned thermostat target");
}
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void PausedFirstFrameCreatesPersistableYmirWorld()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-ymir-paused-first-frame-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
try
{
var statePath = Path.Combine(root, "world.cc");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "paused-first-frame-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [Entity(0, 0, "player")]
}]
};
var frame = new AetheriaRuntimeDaemonFrameDocument
{
FrameId = 0,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0,
SimulationStepsExecuted = 0,
Run = run
};
using var physics = new AetheriaYmirWorldPhysics();
using var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult();
using var persistence = AetheriaYmirPersistenceCoordinator.OpenAsync(node, physics, null)
.GetAwaiter().GetResult();
var captures = persistence.Capture(frame);
RequireEqual(1, captures.Count,
"a paused first frame must still create one persistable Ymir zone world");
RequireEqual(0L, captures[0].FrameId,
"the paused Ymir persistence capture must belong to the authoritative first frame");
RequireEqual(-1, captures[0].SimulationStepIndex,
"a paused first frame must retain the no-step persistence cursor");
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void PickupRemovalSurvivesRestartWithoutReplay()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-ymir-restart-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var hull = CatalogItem("restart-pickup-hull"); hull.HullCapacity = PerformanceStat(1);
var salvage = CatalogItem("restart-pickup-salvage"); salvage.Volume = 1;
var cargoBay = CatalogItem("restart-pickup-cargo-bay"); cargoBay.InteriorOccupiedCells = 1;
var catalog = new AetheriaRuntimeCatalogSnapshot([hull, salvage, cargoBay], [], []);
var ship = Entity(0, 0, "player"); ship.HullItemKey = hull.ItemKey;
ship.CargoBays = [new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = cargoBay.ItemKey, Quantity = 1 }
}];
ship.CargoContents = [Cargo()];
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "pickup-restart-proximity-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit
{
ZoneIndex = 0,
Entities = [ship],
DroppedPickups = [new AetheriaRuntimeDroppedPickupCommit
{
PickupIndex = 10,
PositionX = 20,
Item = new AetheriaRuntimeLoadoutItemCommit { ItemKey = salvage.ItemKey, Quantity = 1 },
LifetimeSeconds = 30
}]
}]
};
var frame = new AetheriaRuntimeDaemonFrameDocument
{
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.1,
Run = run
};
using (var physics = new AetheriaYmirWorldPhysics())
{
AetheriaRuntimeDaemonTickRunner.Tick(statePath, run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = physics,
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.1,
Catalog = catalog,
BuildPublications = false
});
RequireEqual(0, run.Zones[0].DroppedPickups.Count,
"pre-restart proximity collection must remove the durable pickup row");
RequireEqual(1, CargoQuantity(ship, salvage.ItemKey),
"pre-restart proximity collection must commit cargo once");
RequireEqual(1, run.GameEvents.Count(value =>
value.Kind == "pickup.collected" && value.PickupIndex == 10),
"pre-restart proximity collection must emit one stable event");
RequireEqual(0, run.PickupContactReceipts.Count,
"pre-restart proximity collection must not create contact receipts");
frame.Run = run;
using var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult();
using var persistence = AetheriaYmirPersistenceCoordinator.OpenAsync(node, physics, null)
.GetAwaiter().GetResult();
var capture = persistence.Capture(frame);
persistence.PersistPrivateAsync(capture).GetAwaiter().GetResult();
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(frame).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
persistence.ActivateAsync().GetAwaiter().GetResult();
Require(!AetheriaDocumentRegistry.DocumentTypes.Contains(typeof(AetheriaYmirResumeDocument)),
"daemon-private Ymir resume types must not enter the public Aetheria registry");
Require(!node.Cache.AllEntries.Any(value => value is AetheriaYmirResumeDocument or AetheriaYmirJournalChunkDocument),
"public CultMesh cache must not enumerate daemon-private Ymir restart material");
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
var durable = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable restart smoke frame is missing.");
using var restoredPhysics = new AetheriaYmirWorldPhysics();
using var persistence = AetheriaYmirPersistenceCoordinator.OpenAsync(node, restoredPhysics, durable)
.GetAwaiter().GetResult();
AetheriaRuntimeDaemonTickRunner.Tick(statePath, durable.Run,
new AetheriaRuntimeDaemonTickOptions
{
WorldPhysics = restoredPhysics,
FrameId = 2,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.2,
Catalog = catalog,
BuildPublications = false
});
RequireEqual(0, durable.Run.Zones[0].DroppedPickups.Count,
"restart must not resurrect the consumed pickup identity");
RequireEqual(1, CargoQuantity(durable.Run.Zones[0].Entities[0], salvage.ItemKey),
"restart must not duplicate committed pickup cargo");
RequireEqual(1, durable.Run.GameEvents.Count(value =>
value.Kind == "pickup.collected" && value.PickupIndex == 10),
"restart must not duplicate authoritative pickup collection feedback");
}
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private static void CombatLockSurvivesPublicFrameRestart()
{
var root = Path.Combine(Path.GetTempPath(), "aetheria-combat-restart-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
var statePath = Path.Combine(root, "world.cc");
try
{
var source = Entity(0, 0, "player");
source.DirectionX = 1;
source.LookDirectionX = 1;
source.TargetEntityIndex = 1;
source.Contacts =
[
new AetheriaRuntimeEntityContactCommit
{ TargetEntityIndex = 1, InfoGathered = 1, Hostile = true, Visible = true }
];
source.WeaponGroups = [new[] { 0 }];
source.Equipment =
[
new AetheriaRuntimeLoadoutItemSlotCommit
{
Item = new AetheriaRuntimeLoadoutItemCommit
{ ItemKey = "restart-instant", Quality = 1, Durability = 1, Enabled = true }
}
];
var target = Entity(1, 80, "raider");
var run = new AetheriaRuntimeRunCheckpointCommit
{
RunId = "combat-restart-smoke",
CurrentZoneIndex = 0,
CurrentEntityKey = "zone.0.entity.0",
Zones = [new AetheriaRuntimeZoneSnapshotCommit { ZoneIndex = 0, Entities = [source, target] }]
};
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.InstantWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(20)),
new AetheriaRuntimeBehaviorField(6, PerformanceStat(150)),
new AetheriaRuntimeBehaviorField(15, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(16, PerformanceStat(200)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(0.5)),
new AetheriaRuntimeBehaviorField(23, PerformanceStat(45)),
new AetheriaRuntimeBehaviorField(24, PerformanceStat(0)),
new AetheriaRuntimeBehaviorField(25, PerformanceStat(1))
]);
var catalog = new AetheriaRuntimeCatalogSnapshot([CatalogItem("restart-instant", payload)], [], []);
var fire = new AetheriaRuntimeDaemonIntentState();
fire.WeaponGroups.Add(new AetheriaRuntimeDaemonWeaponGroupIntent
{
ActorEntityKey = "zone.0.entity.0", WeaponGroup = 0, Fire = true, Active = true
});
using (var physics = new AetheriaYmirWorldPhysics())
AetheriaRuntimeDaemonSimulation.Step(run, fire, 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
physics, catalog, 1, 0.1);
var state = source.WeaponStates.Single(value =>
value.BehaviorKind == AetheriaRuntimeBehaviorKinds.InstantWeapon);
Require(state.TriggerPending && state.LockProgress > 0 && state.LockProgress < 0.99 &&
run.ShotReceipts.Count == 0,
"combat restart fixture must stop with one pending trigger and a partial daemon lock");
var partialProgress = state.LockProgress;
var frame = new AetheriaRuntimeDaemonFrameDocument
{
FrameId = 1,
FixedDeltaSeconds = 0.1,
SimulationTimeSeconds = 0.1,
Run = run
};
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReplaceAsync(frame).GetAwaiter().GetResult();
node.FlushAsync(soft: false).GetAwaiter().GetResult();
}
using (var node = AetheriaStateNode.OpenAsync(statePath).GetAwaiter().GetResult())
{
var durable = node.MutableDocument<AetheriaRuntimeDaemonFrameDocument>(AetheriaRuntimeVerseRecordKeys.DaemonFrameLatest)
.ReadAsync().GetAwaiter().GetResult()
?? throw new InvalidOperationException("Durable combat restart frame is missing.");
var restoredSource = durable.Run.Zones[0].Entities[0];
var restoredState = restoredSource.WeaponStates.Single(value =>
value.BehaviorKind == AetheriaRuntimeBehaviorKinds.InstantWeapon);
Require(restoredState.TriggerPending && Math.Abs(restoredState.LockProgress - partialProgress) < 0.000001,
"public frame restart must retain the pending trigger and exact partial lock progress");
using var restoredPhysics = new AetheriaYmirWorldPhysics();
for (var frameId = 2; frameId < 12 && durable.Run.ShotReceipts.Count == 0; frameId++)
AetheriaRuntimeDaemonSimulation.Step(durable.Run, new AetheriaRuntimeDaemonIntentState(), 0.1,
AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
restoredPhysics, catalog, frameId, frameId * 0.1);
RequireEqual(1, durable.Run.ShotReceipts.Count,
"restart must complete one pending instant-weapon shot, not lose or duplicate it");
RequireEqual(1, durable.Run.GameEvents.Count(value => value.Kind == "weapon.lock.started"),
"restart must not replay the pre-restart lock-start transition");
RequireEqual(1, durable.Run.GameEvents.Count(value => value.Kind == "weapon.lock.acquired"),
"restart must publish one completed lock transition");
RequireEqual(1, durable.Run.GameEvents.Count(value => value.Kind == "shot.committed"),
"restart must publish one committed-shot transition");
}
}
finally
{
if (Directory.Exists(root))
Directory.Delete(root, recursive: true);
}
}
private sealed class ScriptedWorldPhysics : IAetheriaRuntimeWorldPhysics
{
public string ImplementationId => "smoke.scripted-world";
public void RetainWorlds(string runId, IReadOnlyList<int> zoneIndices) { }
public AetheriaRuntimeWorldStep Step(
string runId,
long frameId,
int simulationStepIndex,
AetheriaRuntimeZoneSnapshotCommit zone,
IReadOnlyList<AetheriaRuntimeEntitySnapshotCommit> entities,
double deltaSeconds) => new([], []);
public AetheriaRuntimePhysicalPayloadStep StepPhysicalPayloads(
string runId,
long frameId,
int simulationStepIndex,
AetheriaRuntimeZoneSnapshotCommit zone,
IReadOnlyList<AetheriaRuntimeEntitySnapshotCommit> entities,
double deltaSeconds) => new(zone.PhysicalPayloads, []);
}
}