Fix NPC shutdown crash (#2971)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com>
This commit is contained in:
@@ -5,38 +5,36 @@ namespace Content.Server.AI.Operators
|
|||||||
{
|
{
|
||||||
public abstract class AiOperator
|
public abstract class AiOperator
|
||||||
{
|
{
|
||||||
public bool HasStartup => _hasStartup;
|
public bool HasStartup { get; private set; }
|
||||||
private bool _hasStartup = false;
|
|
||||||
private bool _hasShutdown = false;
|
public bool HasShutdown { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called once when the AiLogicProcessor starts this action
|
/// Called once when the AiLogicProcessor starts this action
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool TryStartup()
|
/// <returns>true if it hasn't started up previously</returns>
|
||||||
|
public virtual bool Startup()
|
||||||
{
|
{
|
||||||
// If we've already startup then no point continuing
|
// If we've already startup then no point continuing
|
||||||
// This signals to the override that it's already startup
|
// This signals to the override that it's already startup
|
||||||
// Should probably throw but it made some code elsewhere marginally easier
|
// Should probably throw but it made some code elsewhere marginally easier
|
||||||
if (_hasStartup)
|
if (HasStartup)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
HasStartup = true;
|
||||||
_hasStartup = true;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called once when the AiLogicProcessor is done with this action if the outcome is successful or fails.
|
/// Called once when the AiLogicProcessor is done with this action if the outcome is successful or fails.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Shutdown(Outcome outcome)
|
public virtual bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
if (_hasShutdown)
|
if (HasShutdown)
|
||||||
{
|
return false;
|
||||||
throw new InvalidOperationException("AiOperator has already shutdown");
|
|
||||||
}
|
|
||||||
|
|
||||||
_hasShutdown = true;
|
HasShutdown = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -53,4 +51,4 @@ namespace Content.Server.AI.Operators
|
|||||||
Continuing,
|
Continuing,
|
||||||
Failed,
|
Failed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ namespace Content.Server.AI.Operators.Combat.Melee
|
|||||||
_burstTime = burstTime;
|
_burstTime = burstTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryStartup()
|
public override bool Startup()
|
||||||
{
|
{
|
||||||
if (!base.TryStartup())
|
if (!base.Startup())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -42,13 +42,17 @@ namespace Content.Server.AI.Operators.Combat.Melee
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown(Outcome outcome)
|
public override bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
base.Shutdown(outcome);
|
if (!base.Shutdown(outcome))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
||||||
{
|
{
|
||||||
combatModeComponent.IsInCombatMode = false;
|
combatModeComponent.IsInCombatMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Outcome Execute(float frameTime)
|
public override Outcome Execute(float frameTime)
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ namespace Content.Server.AI.Operators.Combat.Melee
|
|||||||
_burstTime = burstTime;
|
_burstTime = burstTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryStartup()
|
public override bool Startup()
|
||||||
{
|
{
|
||||||
if (!base.TryStartup())
|
if (!base.Startup())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -51,13 +51,17 @@ namespace Content.Server.AI.Operators.Combat.Melee
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown(Outcome outcome)
|
public override bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
base.Shutdown(outcome);
|
if (!base.Shutdown(outcome))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
if (_owner.TryGetComponent(out CombatModeComponent combatModeComponent))
|
||||||
{
|
{
|
||||||
combatModeComponent.IsInCombatMode = false;
|
combatModeComponent.IsInCombatMode = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Outcome Execute(float frameTime)
|
public override Outcome Execute(float frameTime)
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ namespace Content.Server.AI.Operators.Inventory
|
|||||||
_owner = owner;
|
_owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryStartup()
|
public override bool Startup()
|
||||||
{
|
{
|
||||||
if (!base.TryStartup())
|
if (!base.Startup())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -40,12 +40,15 @@ namespace Content.Server.AI.Operators.Inventory
|
|||||||
return _target != null;
|
return _target != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown(Outcome outcome)
|
public override bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
base.Shutdown(outcome);
|
if (!base.Shutdown(outcome))
|
||||||
|
return false;
|
||||||
|
|
||||||
var blackboard = UtilityAiHelpers.GetBlackboard(_owner);
|
var blackboard = UtilityAiHelpers.GetBlackboard(_owner);
|
||||||
|
|
||||||
blackboard?.GetState<LastOpenedStorageState>().SetValue(null);
|
blackboard?.GetState<LastOpenedStorageState>().SetValue(null);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Outcome Execute(float frameTime)
|
public override Outcome Execute(float frameTime)
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ namespace Content.Server.AI.Operators.Movement
|
|||||||
_requiresInRangeUnobstructed = requiresInRangeUnobstructed;
|
_requiresInRangeUnobstructed = requiresInRangeUnobstructed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryStartup()
|
public override bool Startup()
|
||||||
{
|
{
|
||||||
if (!base.TryStartup())
|
if (!base.Startup())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -45,11 +45,14 @@ namespace Content.Server.AI.Operators.Movement
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown(Outcome outcome)
|
public override bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
base.Shutdown(outcome);
|
if (!base.Shutdown(outcome))
|
||||||
|
return false;
|
||||||
|
|
||||||
var steering = EntitySystem.Get<AiSteeringSystem>();
|
var steering = EntitySystem.Get<AiSteeringSystem>();
|
||||||
steering.Unregister(_owner);
|
steering.Unregister(_owner);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Outcome Execute(float frameTime)
|
public override Outcome Execute(float frameTime)
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ namespace Content.Server.AI.Operators.Movement
|
|||||||
DesiredRange = desiredRange;
|
DesiredRange = desiredRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool TryStartup()
|
public override bool Startup()
|
||||||
{
|
{
|
||||||
if (!base.TryStartup())
|
if (!base.Startup())
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -34,11 +34,14 @@ namespace Content.Server.AI.Operators.Movement
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Shutdown(Outcome outcome)
|
public override bool Shutdown(Outcome outcome)
|
||||||
{
|
{
|
||||||
base.Shutdown(outcome);
|
if (!base.Shutdown(outcome))
|
||||||
|
return false;
|
||||||
|
|
||||||
var steering = EntitySystem.Get<AiSteeringSystem>();
|
var steering = EntitySystem.Get<AiSteeringSystem>();
|
||||||
steering.Unregister(_owner);
|
steering.Unregister(_owner);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Outcome Execute(float frameTime)
|
public override Outcome Execute(float frameTime)
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ namespace Content.Server.AI.Operators.Sequences
|
|||||||
{
|
{
|
||||||
return Outcome.Success;
|
return Outcome.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
var op = Sequence.Peek();
|
var op = Sequence.Peek();
|
||||||
op.TryStartup();
|
op.Startup();
|
||||||
var outcome = op.Execute(frameTime);
|
var outcome = op.Execute(frameTime);
|
||||||
|
|
||||||
switch (outcome)
|
switch (outcome)
|
||||||
@@ -35,10 +35,10 @@ namespace Content.Server.AI.Operators.Sequences
|
|||||||
op.Shutdown(outcome);
|
op.Shutdown(outcome);
|
||||||
Sequence.Clear();
|
Sequence.Clear();
|
||||||
return Outcome.Failed;
|
return Outcome.Failed;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException();
|
throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace Content.Server.AI.Utility.Actions
|
|||||||
{
|
{
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void Shutdown() {}
|
public virtual void Shutdown() {}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -78,7 +78,7 @@ namespace Content.Server.AI.Utility.Actions
|
|||||||
return Outcome.Success;
|
return Outcome.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
op.TryStartup();
|
op.Startup();
|
||||||
var outcome = op.Execute(frameTime);
|
var outcome = op.Execute(frameTime);
|
||||||
|
|
||||||
switch (outcome)
|
switch (outcome)
|
||||||
@@ -116,7 +116,7 @@ namespace Content.Server.AI.Utility.Actions
|
|||||||
// Overall structure is based on Building a better centaur
|
// Overall structure is based on Building a better centaur
|
||||||
// Ideally we should early-out each action as cheaply as possible if it's not valid, thus
|
// Ideally we should early-out each action as cheaply as possible if it's not valid, thus
|
||||||
// the finalScore can only go down over time.
|
// the finalScore can only go down over time.
|
||||||
|
|
||||||
var finalScore = 1.0f;
|
var finalScore = 1.0f;
|
||||||
var minThreshold = min / Bonus;
|
var minThreshold = min / Bonus;
|
||||||
context.GetState<ConsiderationState>().SetValue(considerations.Count);
|
context.GetState<ConsiderationState>().SetValue(considerations.Count);
|
||||||
|
|||||||
@@ -137,6 +137,8 @@ namespace Content.Server.AI.Utility.AiLogic
|
|||||||
{
|
{
|
||||||
var currentOp = CurrentAction?.ActionOperators.Peek();
|
var currentOp = CurrentAction?.ActionOperators.Peek();
|
||||||
currentOp?.Shutdown(Outcome.Failed);
|
currentOp?.Shutdown(Outcome.Failed);
|
||||||
|
CurrentAction?.Shutdown();
|
||||||
|
CurrentAction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MobStateChanged(MobStateChangedMessage message)
|
public void MobStateChanged(MobStateChangedMessage message)
|
||||||
|
|||||||
@@ -110,17 +110,18 @@ namespace Content.Server.GameObjects.EntitySystems.AI
|
|||||||
|
|
||||||
foreach (var processor in _awakeAi)
|
foreach (var processor in _awakeAi)
|
||||||
{
|
{
|
||||||
if (count >= maxUpdates)
|
if (processor.SelfEntity.Deleted ||
|
||||||
{
|
!processor.SelfEntity.HasComponent<AiControllerComponent>())
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processor.SelfEntity.Deleted)
|
|
||||||
{
|
{
|
||||||
toRemove.Add(processor);
|
toRemove.Add(processor);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (count >= maxUpdates)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
processor.Update(frameTime);
|
processor.Update(frameTime);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user