@@ -45,6 +45,10 @@ public sealed class NPCTest
|
||||
var count = counts.GetOrNew(compound.ID);
|
||||
count++;
|
||||
|
||||
// Compound tasks marked with AllowRecursion are only evaluated once
|
||||
if (counts.ContainsKey(compound.ID) && compound.AllowRecursion)
|
||||
continue;
|
||||
|
||||
Assert.That(count, Is.LessThan(50));
|
||||
counts[compound.ID] = count;
|
||||
Count(protoManager.Index<HTNCompoundPrototype>(compoundTask.Task), counts, htnSystem, protoManager);
|
||||
|
||||
@@ -12,4 +12,10 @@ public sealed partial class HTNCompoundPrototype : IPrototype
|
||||
|
||||
[DataField("branches", required: true)]
|
||||
public List<HTNBranch> Branches = new();
|
||||
|
||||
/// <summary>
|
||||
/// Exclude this compound task from the CompoundRecursion integration test.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool AllowRecursion = false;
|
||||
}
|
||||
|
||||
@@ -63,8 +63,13 @@ public sealed class HTNPlanJob : Job<HTNPlan>
|
||||
// How many primitive tasks we've added since last record.
|
||||
var primitiveCount = 0;
|
||||
|
||||
int tasksProcessed = 0;
|
||||
|
||||
while (tasksToProcess.TryDequeue(out var currentTask))
|
||||
{
|
||||
if (tasksProcessed++ > _rootTask.MaximumTasks)
|
||||
throw new Exception("HTN Planner exceeded maximum tasks");
|
||||
|
||||
switch (currentTask)
|
||||
{
|
||||
case HTNCompoundTask compound:
|
||||
|
||||
@@ -3,4 +3,10 @@ namespace Content.Server.NPC.HTN;
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class HTNTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Limit the amount of tasks the planner considers. Exceeding this value sleeps the NPC and throws an exception.
|
||||
/// The expected way to hit this limit is with badly written recursive tasks.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaximumTasks = 1000;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user