Recover abandoned agent work
This commit is contained in:
parent
57bda320fa
commit
a88fa37c63
2 changed files with 93 additions and 5 deletions
|
|
@ -22,6 +22,8 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
MultipleActorsUseTheSameMovementLever();
|
||||
AgentClaimsAndCompletesExploreTaskThroughCommands();
|
||||
SchedulerAssignsHighestPriorityCompatibleTask();
|
||||
SchedulerRequeuesTaskFromDeadAgent();
|
||||
SchedulerCollapsesDuplicateAssignmentMarkers();
|
||||
SchedulerAssignsShortestGalaxyRoute();
|
||||
AgentTraversesGalaxyRouteBeforeExecutingTask();
|
||||
AttackAgentControlsOptimumRangeThroughMovementLever();
|
||||
|
|
@ -604,6 +606,68 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
|
|||
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";
|
||||
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");
|
||||
}
|
||||
|
||||
private static AetheriaRuntimeAgentTaskCommit AgentTask(string id, int priority) => new()
|
||||
{
|
||||
TaskId = id,
|
||||
|
|
|
|||
|
|
@ -745,17 +745,41 @@ namespace GameCult.Aetheria.State.Verse
|
|||
|
||||
private static void ReleaseInvalidAssignments(AetheriaRuntimeRunCheckpointCommit run)
|
||||
{
|
||||
var activeTaskIds = (run.AgentTasks ?? Array.Empty<AetheriaRuntimeAgentTaskCommit>())
|
||||
var tasks = (run.AgentTasks ?? Array.Empty<AetheriaRuntimeAgentTaskCommit>())
|
||||
.Where(task => task != null)
|
||||
.ToArray();
|
||||
var entities = (run.Zones ?? Array.Empty<AetheriaRuntimeZoneSnapshotCommit>())
|
||||
.Where(zone => zone != null)
|
||||
.SelectMany(zone => zone.Entities ?? Array.Empty<AetheriaRuntimeEntitySnapshotCommit>())
|
||||
.Where(entity => entity != null)
|
||||
.ToArray();
|
||||
var activeTaskIds = tasks
|
||||
.Where(task => task != null && string.Equals(task.Status, AetheriaRuntimeAgentTaskStatuses.Assigned, StringComparison.Ordinal))
|
||||
.Select(task => task.TaskId)
|
||||
.ToHashSet(StringComparer.Ordinal);
|
||||
foreach (var entity in (run.Zones ?? Array.Empty<AetheriaRuntimeZoneSnapshotCommit>())
|
||||
.Where(zone => zone != null)
|
||||
.SelectMany(zone => zone.Entities ?? Array.Empty<AetheriaRuntimeEntitySnapshotCommit>()))
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(entity.AssignedAgentTaskId) && !activeTaskIds.Contains(entity.AssignedAgentTaskId))
|
||||
if (!string.IsNullOrWhiteSpace(entity.AssignedAgentTaskId) &&
|
||||
(!entity.IsActive || !activeTaskIds.Contains(entity.AssignedAgentTaskId)))
|
||||
entity.AssignedAgentTaskId = "";
|
||||
}
|
||||
foreach (var task in tasks.Where(task => string.Equals(task.Status, AetheriaRuntimeAgentTaskStatuses.Assigned, StringComparison.Ordinal)))
|
||||
{
|
||||
var carriers = entities
|
||||
.Where(entity => entity.IsActive && string.Equals(entity.AssignedAgentTaskId, task.TaskId, StringComparison.Ordinal))
|
||||
.OrderBy(entity => entity.EntityIndex)
|
||||
.ToArray();
|
||||
if (carriers.Length == 0)
|
||||
{
|
||||
task.Status = AetheriaRuntimeAgentTaskStatuses.Queued;
|
||||
task.AssignedEntityIndex = -1;
|
||||
task.AssignedFrameId = -1;
|
||||
continue;
|
||||
}
|
||||
task.AssignedEntityIndex = carriers[0].EntityIndex;
|
||||
foreach (var duplicate in carriers.Skip(1))
|
||||
duplicate.AssignedAgentTaskId = "";
|
||||
}
|
||||
}
|
||||
|
||||
private static string EntityKey(AetheriaRuntimeRunCheckpointCommit run, int zoneIndex, int entityIndex) =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue