Fix NPC prototype reloads (#13981)

This commit is contained in:
metalgearsloth
2023-02-08 08:27:34 +11:00
committed by GitHub
parent bfedfd0abb
commit 6b6c6b1c9f
4 changed files with 39 additions and 15 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.NPC.HTN; using Content.Server.NPC.HTN;
using NUnit.Framework; using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -19,6 +20,7 @@ public sealed class NPCTest
await server.WaitIdleAsync(); await server.WaitIdleAsync();
var htnSystem = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<HTNSystem>();
var protoManager = server.ResolveDependency<IPrototypeManager>(); var protoManager = server.ResolveDependency<IPrototypeManager>();
await server.WaitAssertion(() => await server.WaitAssertion(() =>
@@ -27,7 +29,7 @@ public sealed class NPCTest
foreach (var compound in protoManager.EnumeratePrototypes<HTNCompoundTask>()) foreach (var compound in protoManager.EnumeratePrototypes<HTNCompoundTask>())
{ {
Count(compound, counts); Count(compound, counts, htnSystem);
counts.Clear(); counts.Clear();
} }
}); });
@@ -35,11 +37,13 @@ public sealed class NPCTest
await pool.CleanReturnAsync(); await pool.CleanReturnAsync();
} }
private static void Count(HTNCompoundTask compound, Dictionary<string, int> counts) private static void Count(HTNCompoundTask compound, Dictionary<string, int> counts, HTNSystem htnSystem)
{ {
foreach (var branch in compound.Branches) var compoundBranches = htnSystem.CompoundBranches[compound];
for (var i = 0; i < compound.Branches.Count; i++)
{ {
foreach (var task in branch.Tasks) foreach (var task in compoundBranches[i])
{ {
if (task is HTNCompoundTask compoundTask) if (task is HTNCompoundTask compoundTask)
{ {
@@ -48,7 +52,7 @@ public sealed class NPCTest
Assert.That(count, Is.LessThan(50)); Assert.That(count, Is.LessThan(50));
counts[compound.ID] = count; counts[compound.ID] = count;
Count(compoundTask, counts); Count(compoundTask, counts, htnSystem);
} }
} }
} }

View File

@@ -12,8 +12,6 @@ public sealed class HTNBranch
[DataField("preconditions")] [DataField("preconditions")]
public List<HTNPrecondition> Preconditions = new(); public List<HTNPrecondition> Preconditions = new();
[ViewVariables] public List<HTNTask> Tasks = new();
/// <summary> /// <summary>
/// Due to how serv3 works we need to defer getting the actual tasks until after they have all been serialized. /// Due to how serv3 works we need to defer getting the actual tasks until after they have all been serialized.
/// </summary> /// </summary>

View File

@@ -11,6 +11,7 @@ namespace Content.Server.NPC.HTN;
/// </summary> /// </summary>
public sealed class HTNPlanJob : Job<HTNPlan> public sealed class HTNPlanJob : Job<HTNPlan>
{ {
private readonly HTNSystem _htn;
private readonly HTNCompoundTask _rootTask; private readonly HTNCompoundTask _rootTask;
private NPCBlackboard _blackboard; private NPCBlackboard _blackboard;
@@ -21,11 +22,13 @@ public sealed class HTNPlanJob : Job<HTNPlan>
public HTNPlanJob( public HTNPlanJob(
double maxTime, double maxTime,
HTNSystem htn,
HTNCompoundTask rootTask, HTNCompoundTask rootTask,
NPCBlackboard blackboard, NPCBlackboard blackboard,
List<int>? branchTraversal, List<int>? branchTraversal,
CancellationToken cancellationToken = default) : base(maxTime, cancellationToken) CancellationToken cancellationToken = default) : base(maxTime, cancellationToken)
{ {
_htn = htn;
_rootTask = rootTask; _rootTask = rootTask;
_blackboard = blackboard; _blackboard = blackboard;
_branchTraversal = branchTraversal; _branchTraversal = branchTraversal;
@@ -156,6 +159,8 @@ public sealed class HTNPlanJob : Job<HTNPlan>
/// </summary> /// </summary>
private bool TryFindSatisfiedMethod(HTNCompoundTask compound, Queue<HTNTask> tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex) private bool TryFindSatisfiedMethod(HTNCompoundTask compound, Queue<HTNTask> tasksToProcess, NPCBlackboard blackboard, ref int mtrIndex)
{ {
var compBranches = _htn.CompoundBranches[compound];
for (var i = mtrIndex; i < compound.Branches.Count; i++) for (var i = mtrIndex; i < compound.Branches.Count; i++)
{ {
var branch = compound.Branches[i]; var branch = compound.Branches[i];
@@ -173,7 +178,9 @@ public sealed class HTNPlanJob : Job<HTNPlan>
if (!isValid) if (!isValid)
continue; continue;
foreach (var task in branch.Tasks) var branchTasks = compBranches[i];
foreach (var task in branchTasks)
{ {
tasksToProcess.Enqueue(task); tasksToProcess.Enqueue(task);
} }

View File

@@ -27,6 +27,10 @@ public sealed class HTNSystem : EntitySystem
private readonly HashSet<ICommonSession> _subscribers = new(); private readonly HashSet<ICommonSession> _subscribers = new();
// hngngghghgh
public IReadOnlyDictionary<HTNCompoundTask, List<HTNTask>[]> CompoundBranches => _compoundBranches;
private Dictionary<HTNCompoundTask, List<HTNTask>[]> _compoundBranches = new();
// Hierarchical Task Network // Hierarchical Task Network
public override void Initialize() public override void Initialize()
{ {
@@ -75,6 +79,8 @@ public sealed class HTNSystem : EntitySystem
} }
} }
_compoundBranches.Clear();
// Add dependencies for all operators. // Add dependencies for all operators.
// We put code on operators as I couldn't think of a clean way to put it on systems. // We put code on operators as I couldn't think of a clean way to put it on systems.
foreach (var compound in _prototypeManager.EnumeratePrototypes<HTNCompoundTask>()) foreach (var compound in _prototypeManager.EnumeratePrototypes<HTNCompoundTask>())
@@ -111,21 +117,25 @@ public sealed class HTNSystem : EntitySystem
private void UpdateCompound(HTNCompoundTask compound) private void UpdateCompound(HTNCompoundTask compound)
{ {
foreach (var branch in compound.Branches) var branchies = new List<HTNTask>[compound.Branches.Count];
_compoundBranches.Add(compound, branchies);
for (var i = 0; i < compound.Branches.Count; i++)
{ {
branch.Tasks.Clear(); var branch = compound.Branches[i];
branch.Tasks.EnsureCapacity(branch.TaskPrototypes.Count); var brancho = new List<HTNTask>(branch.TaskPrototypes.Count);
branchies[i] = brancho;
// Didn't do this in a typeserializer because we can't recursively grab our own prototype during it, woohoo! // Didn't do this in a typeserializer because we can't recursively grab our own prototype during it, woohoo!
foreach (var proto in branch.TaskPrototypes) foreach (var proto in branch.TaskPrototypes)
{ {
if (_prototypeManager.TryIndex<HTNCompoundTask>(proto, out var compTask)) if (_prototypeManager.TryIndex<HTNCompoundTask>(proto, out var compTask))
{ {
branch.Tasks.Add(compTask); brancho.Add(compTask);
} }
else if (_prototypeManager.TryIndex<HTNPrimitiveTask>(proto, out var primTask)) else if (_prototypeManager.TryIndex<HTNPrimitiveTask>(proto, out var primTask))
{ {
branch.Tasks.Add(primTask); brancho.Add(primTask);
} }
else else
{ {
@@ -330,6 +340,7 @@ public sealed class HTNSystem : EntitySystem
var job = new HTNPlanJob( var job = new HTNPlanJob(
0.02, 0.02,
this,
_prototypeManager.Index<HTNCompoundTask>(component.RootTask), _prototypeManager.Index<HTNCompoundTask>(component.RootTask),
component.Blackboard.ShallowClone(), branchTraversal, cancelToken.Token); component.Blackboard.ShallowClone(), branchTraversal, cancelToken.Token);
@@ -360,13 +371,17 @@ public sealed class HTNSystem : EntitySystem
else if (task is HTNCompoundTask compound) else if (task is HTNCompoundTask compound)
{ {
builder.AppendLine(buffer + $"Compound: {task.ID}"); builder.AppendLine(buffer + $"Compound: {task.ID}");
var compoundBranches = CompoundBranches[compound];
foreach (var branch in compound.Branches) for (var i = 0; i < compound.Branches.Count; i++)
{ {
var branch = compound.Branches[i];
builder.AppendLine(buffer + " branch:"); builder.AppendLine(buffer + " branch:");
indent++; indent++;
var branchTasks = compoundBranches[i];
foreach (var branchTask in branch.Tasks) foreach (var branchTask in branchTasks)
{ {
AppendDomain(builder, branchTask, ref indent); AppendDomain(builder, branchTask, ref indent);
} }