diff --git a/src/Artemis.Core/Models/BreakableModel.cs b/src/Artemis.Core/Models/BreakableModel.cs
index 7276b6adb..a752bf38b 100644
--- a/src/Artemis.Core/Models/BreakableModel.cs
+++ b/src/Artemis.Core/Models/BreakableModel.cs
@@ -69,11 +69,16 @@ public abstract class BreakableModel : CorePropertyChanged, IBreakableModel
///
public void ClearBrokenState(string state)
{
- if (state == null) throw new ArgumentNullException(nameof(state));
+ if (state == null)
+ throw new ArgumentNullException(nameof(state));
+
+ // If there was no broken state to begin with, done!
if (BrokenState == null)
return;
-
- if (BrokenState != state) return;
+ // Only clear similar broken states
+ if (BrokenState != state)
+ return;
+
BrokenState = null;
BrokenStateException = null;
OnBrokenStateChanged();
diff --git a/src/Artemis.Core/Services/NodeService.cs b/src/Artemis.Core/Services/NodeService.cs
index 2a9d7de6d..867fe8cb3 100644
--- a/src/Artemis.Core/Services/NodeService.cs
+++ b/src/Artemis.Core/Services/NodeService.cs
@@ -115,7 +115,7 @@ internal class NodeService : INodeService
}
}
- node.Initialize(script);
+ node.TryInitialize(script);
return node;
}
diff --git a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
index b5e16a50b..eb3fc4177 100644
--- a/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
+++ b/src/Artemis.Core/VisualScripting/Interfaces/INode.cs
@@ -8,7 +8,7 @@ namespace Artemis.Core;
///
/// Represents a kind of node inside a
///
-public interface INode : INotifyPropertyChanged
+public interface INode : INotifyPropertyChanged, IBreakableModel
{
///
/// Gets or sets the ID of the node.
@@ -81,15 +81,15 @@ public interface INode : INotifyPropertyChanged
event EventHandler> PinCollectionRemoved;
///
- /// Called when the node was loaded from storage or newly created
+ /// Attempts to initialize the node.
///
/// The script the node is contained in
- void Initialize(INodeScript script);
+ void TryInitialize(INodeScript script);
///
- /// Evaluates the value of the output pins of this node
+ /// Attempts to evaluate the value of the output pins of this node
///
- void Evaluate();
+ void TryEvaluate();
///
/// Resets the node causing all pins to re-evaluate the next time is called
diff --git a/src/Artemis.Core/VisualScripting/Node.cs b/src/Artemis.Core/VisualScripting/Node.cs
index 5c1ccb03f..70b313fd3 100644
--- a/src/Artemis.Core/VisualScripting/Node.cs
+++ b/src/Artemis.Core/VisualScripting/Node.cs
@@ -12,7 +12,7 @@ namespace Artemis.Core;
///
/// Represents a kind of node inside a
///
-public abstract class Node : CorePropertyChanged, INode
+public abstract class Node : BreakableModel, INode
{
///
public event EventHandler? Resetting;
@@ -95,6 +95,9 @@ public abstract class Node : CorePropertyChanged, INode
///
public IReadOnlyCollection PinCollections => new ReadOnlyCollection(_pinCollections);
+ ///
+ public override string BrokenDisplayName => Name;
+
#endregion
#region Construtors
@@ -335,12 +338,17 @@ public abstract class Node : CorePropertyChanged, INode
return isRemoved;
}
- ///
+ ///
+ /// Called when the node was loaded from storage or newly created
+ ///
+ /// The script the node is contained in
public virtual void Initialize(INodeScript script)
{
}
- ///
+ ///
+ /// Evaluates the value of the output pins of this node
+ ///
public abstract void Evaluate();
///
@@ -354,6 +362,18 @@ public abstract class Node : CorePropertyChanged, INode
Resetting?.Invoke(this, EventArgs.Empty);
}
+ ///
+ public void TryInitialize(INodeScript script)
+ {
+ TryOrBreak(() => Initialize(script), "Failed to initialize");
+ }
+
+ ///
+ public void TryEvaluate()
+ {
+ TryOrBreak(Evaluate, "Failed to evaluate");
+ }
+
///
/// Called whenever the node must show it's custom view model, if , no custom view model is used
///
diff --git a/src/Artemis.Core/VisualScripting/NodeScript.cs b/src/Artemis.Core/VisualScripting/NodeScript.cs
index 8644c06b0..3034187f0 100644
--- a/src/Artemis.Core/VisualScripting/NodeScript.cs
+++ b/src/Artemis.Core/VisualScripting/NodeScript.cs
@@ -101,7 +101,7 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript
node.Reset();
}
- ExitNode.Evaluate();
+ ExitNode.TryEvaluate();
}
///
diff --git a/src/Artemis.Core/VisualScripting/OutputPin.cs b/src/Artemis.Core/VisualScripting/OutputPin.cs
index 2e135eb13..b9fa3332d 100644
--- a/src/Artemis.Core/VisualScripting/OutputPin.cs
+++ b/src/Artemis.Core/VisualScripting/OutputPin.cs
@@ -43,7 +43,7 @@ public sealed class OutputPin : Pin
get
{
if (!IsEvaluated)
- Node?.Evaluate();
+ Node?.TryEvaluate();
return _value;
}
@@ -115,7 +115,7 @@ public sealed class OutputPin : Pin
get
{
if (!IsEvaluated)
- Node?.Evaluate();
+ Node?.TryEvaluate();
return _value;
}
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
index aebe108cf..40849f3d2 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeView.axaml
@@ -37,35 +37,38 @@
ClipToBounds="True"
Background="{DynamicResource ContentDialogBackground}">
-
-
-
-
-
-
diff --git a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
index bb3a1670d..e203356b7 100644
--- a/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
+++ b/src/Artemis.UI/Screens/VisualScripting/NodeViewModel.cs
@@ -8,6 +8,7 @@ using Artemis.Core.Events;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.VisualScripting.Pins;
using Artemis.UI.Shared;
+using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.NodeEditor;
using Artemis.UI.Shared.Services.NodeEditor.Commands;
using Avalonia;
@@ -21,9 +22,9 @@ namespace Artemis.UI.Screens.VisualScripting;
public class NodeViewModel : ActivatableViewModelBase
{
private readonly INodeEditorService _nodeEditorService;
+ private readonly IWindowService _windowService;
private ICustomNodeViewModel? _customNodeViewModel;
- private ReactiveCommand? _deleteNode;
private double _dragOffsetX;
private double _dragOffsetY;
private ObservableAsPropertyHelper? _hasInputPins;
@@ -34,14 +35,13 @@ public class NodeViewModel : ActivatableViewModelBase
private double _startX;
private double _startY;
- public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService)
+ public NodeViewModel(NodeScriptViewModel nodeScriptViewModel, INode node, INodeVmFactory nodeVmFactory, INodeEditorService nodeEditorService, IWindowService windowService)
{
_nodeEditorService = nodeEditorService;
+ _windowService = windowService;
NodeScriptViewModel = nodeScriptViewModel;
Node = node;
- DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
-
SourceList nodePins = new();
SourceList nodePinCollections = new();
@@ -61,7 +61,10 @@ public class NodeViewModel : ActivatableViewModelBase
nodePins.Connect().Merge(nodePinCollections.Connect().TransformMany(c => c.PinViewModels)).Bind(out ReadOnlyObservableCollection pins).Subscribe();
PinViewModels = pins;
-
+
+ DeleteNode = ReactiveCommand.Create(ExecuteDeleteNode, this.WhenAnyValue(vm => vm.IsStaticNode).Select(v => !v));
+ ShowBrokenState = ReactiveCommand.Create(ExecuteShowBrokenState);
+
this.WhenActivated(d =>
{
_isStaticNode = Node.WhenAnyValue(n => n.IsDefaultNode, n => n.IsExitNode)
@@ -152,17 +155,14 @@ public class NodeViewModel : ActivatableViewModelBase
set => RaiseAndSetIfChanged(ref _customNodeViewModel, value);
}
- public ReactiveCommand? DeleteNode
- {
- get => _deleteNode;
- set => RaiseAndSetIfChanged(ref _deleteNode, value);
- }
-
public bool IsSelected
{
get => _isSelected;
set => RaiseAndSetIfChanged(ref _isSelected, value);
}
+
+ public ReactiveCommand ShowBrokenState { get; }
+ public ReactiveCommand DeleteNode { get; }
public void StartDrag(Point mouseStartPosition)
{
@@ -195,4 +195,10 @@ public class NodeViewModel : ActivatableViewModelBase
{
_nodeEditorService.ExecuteCommand(NodeScriptViewModel.NodeScript, new DeleteNode(NodeScriptViewModel.NodeScript, Node));
}
+
+ private void ExecuteShowBrokenState()
+ {
+ if (Node.BrokenState != null && Node.BrokenStateException != null)
+ _windowService.ShowExceptionDialog(Node.BrokenState, Node.BrokenStateException);
+ }
}
\ No newline at end of file
diff --git a/src/Artemis.VisualScripting/Nodes/Text/StringRegexMatchNode.cs b/src/Artemis.VisualScripting/Nodes/Text/StringRegexMatchNode.cs
index c3c1d1a8b..810080877 100644
--- a/src/Artemis.VisualScripting/Nodes/Text/StringRegexMatchNode.cs
+++ b/src/Artemis.VisualScripting/Nodes/Text/StringRegexMatchNode.cs
@@ -8,7 +8,7 @@ public class StringRegexMatchNode : Node
{
private string? _lastPattern;
private Regex? _regex;
- private bool _broken;
+ private Exception? _exception;
public StringRegexMatchNode() : base("Regex Match", "Checks provided regex pattern matches the input.")
{
@@ -25,20 +25,27 @@ public class StringRegexMatchNode : Node
{
if (Input.Value == null || Pattern.Value == null)
return;
- if (_broken && _lastPattern == Pattern.Value)
- return;
+ // If the regex was invalid output false and rethrow the exception
+ if (_lastPattern == Pattern.Value && _exception != null)
+ {
+ Result.Value = false;
+ throw _exception;
+ }
+
+ // If there is no regex yet or the regex changed, recompile
if (_regex == null || _lastPattern != Pattern.Value)
{
try
{
_regex = new Regex(Pattern.Value, RegexOptions.Compiled);
- _broken = false;
+ _exception = null;
}
- catch (Exception)
+ catch (Exception e)
{
- _broken = true;
- return;
+ // If there is an exception, save it to keep rethrowing until the regex is fixed
+ _exception = e;
+ throw;
}
finally
{