Core - Keep the last good outputs of a node that fails to evaluate (#917)

A node that failed to evaluate reset its outputs to their default values, so a node breaking for a few frames made
everything after it jump to defaults. It now keeps the outputs of its last successful run, like before #911, and still
marks them as evaluated so it isn't evaluated again for every pin reading from it.
This commit is contained in:
RobertBeekman authored and GitHub committed 2026-10-27 21:07:32 +01:00
1 parent 5ddd50b4a8
commit 8d2927a054
4 files changed
+8 -25

No files matched your search

@@ -218,9 +218,9 @@ public class NodeTests : ScriptTestBase
} }
[Fact] [Fact]
public void FailingNode_InAScript_SetsItsOutputsToTheirDefaultValues() public void FailingNode_InAScript_KeepsTheOutputsOfItsLastSuccessfulRun()
{ {
// Rather than letting the value from the last run that worked flow on // So a node that fails for a few frames doesn't make the script jump to default values
NodeScript<Numeric> script = CreateScript<Numeric>(); NodeScript<Numeric> script = CreateScript<Numeric>();
TestThrowingNode node = ScriptNodes.AddNode<TestThrowingNode>(script); TestThrowingNode node = ScriptNodes.AddNode<TestThrowingNode>(script);
script.GetExitNode().Input.ConnectTo(node.Output); script.GetExitNode().Input.ConnectTo(node.Output);
@@ -231,7 +231,7 @@ public class NodeTests : ScriptTestBase
node.FailsOn = TestThrowingNode.FailureMode.Evaluate; node.FailsOn = TestThrowingNode.FailureMode.Evaluate;
script.Run(); script.Run();
script.Result.ShouldBe(new Numeric(0)); script.Result.ShouldBe(new Numeric(5));
node.BrokenState.ShouldBe("Failed to evaluate"); node.BrokenState.ShouldBe("Failed to evaluate");
script.GetExitNode().BrokenState.ShouldBeNull(); script.GetExitNode().BrokenState.ShouldBeNull();
} }
@@ -239,7 +239,7 @@ public class NodeTests : ScriptTestBase
[Fact] [Fact]
public void FailingNode_InAScript_IsEvaluatedOncePerRun() public void FailingNode_InAScript_IsEvaluatedOncePerRun()
{ {
// A failing node sets its outputs to their default values, so the other pins reading from it don't try again // A failing node still marks its outputs as evaluated, so the other pins reading from it don't try again
NodeScript<Numeric> script = CreateScript<Numeric>(); NodeScript<Numeric> script = CreateScript<Numeric>();
TestNode failing = new() {OnEvaluate = _ => throw new InvalidOperationException()}; TestNode failing = new() {OnEvaluate = _ => throw new InvalidOperationException()};
OutputPin<Numeric> output = failing.CreateOutputPin<Numeric>(); OutputPin<Numeric> output = failing.CreateOutputPin<Numeric>();
@@ -388,10 +388,10 @@ public abstract class Node : BreakableModel, INode
if (TryOrBreak(Evaluate, "Failed to evaluate")) if (TryOrBreak(Evaluate, "Failed to evaluate"))
return; return;
// Don't let the values of the last successful run flow on, and don't try again for every pin that reads from this // Keep the values of the last successful run so a brief failure goes unnoticed, and don't try again for every pin
// node. Setting a value marks the pin as evaluated until the next run // that reads from this node
foreach (Pin pin in Pins.Concat(PinCollections.SelectMany(c => c)).OfType<Pin>().Where(p => p.Direction == PinDirection.Output)) foreach (IPin pin in Pins.Concat(PinCollections.SelectMany(c => c)).Where(p => p.Direction == PinDirection.Output))
pin.SetDefaultValue(); pin.IsEvaluated = true;
} }
/// <summary> /// <summary>
@@ -29,11 +29,6 @@ public sealed class OutputPin<T> : Pin
/// <inheritdoc /> /// <inheritdoc />
public override PinDirection Direction => PinDirection.Output; public override PinDirection Direction => PinDirection.Output;
internal override void SetDefaultValue()
{
Value = default;
}
private T? _value; private T? _value;
/// <summary> /// <summary>
@@ -105,11 +100,6 @@ public sealed class OutputPin : Pin
/// <inheritdoc /> /// <inheritdoc />
public override PinDirection Direction => PinDirection.Output; public override PinDirection Direction => PinDirection.Output;
internal override void SetDefaultValue()
{
Value = Type.GetDefault();
}
private object? _value; private object? _value;
private Type _type; private Type _type;
@@ -85,13 +85,6 @@ public abstract class Pin : CorePropertyChanged, IPin
IsEvaluated = false; IsEvaluated = false;
} }
/// <summary>
/// Sets the value of an output pin to the default value of its type, used when its node fails to evaluate
/// </summary>
internal virtual void SetDefaultValue()
{
}
/// <inheritdoc /> /// <inheritdoc />
public void ConnectTo(IPin pin) public void ConnectTo(IPin pin)
{ {