From 086c2f155ddc2d3a332fdfc54032852998e18df8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 13 Mar 2023 15:39:36 +0100 Subject: [PATCH] Fix error when using nested Visject Surface context during State Machines editing in Anim Graph --- .../Archetypes/Animation.StateMachine.cs | 9 +++ Source/Editor/Surface/ISurfaceContext.cs | 5 ++ .../Editor/Surface/Undo/ConnectBoxesAction.cs | 8 +++ Source/Editor/Surface/Undo/ContextHandle.cs | 70 ++++++++++++++++++- .../Editor/Surface/VisjectSurface.Context.cs | 17 +++-- Source/Editor/Surface/VisjectSurfaceWindow.cs | 3 + .../Viewport/Previews/MaterialPreview.cs | 3 + .../Windows/AssetReferencesGraphWindow.cs | 3 + .../Assets/VisjectFunctionSurfaceWindow.cs | 3 + .../Windows/Assets/VisualScriptWindow.cs | 3 + .../Windows/Search/ContentSearchWindow.cs | 3 + 11 files changed, 120 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index d1d8cbea1..598adc22b 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -250,6 +250,9 @@ namespace FlaxEditor.Surface.Archetypes set => Values[1] = value; } + /// + public VisjectSurfaceContext ParentContext => Context; + /// public void OnContextCreated(VisjectSurfaceContext context) { @@ -1311,6 +1314,9 @@ namespace FlaxEditor.Surface.Archetypes set => Values[1] = value; } + /// + public VisjectSurfaceContext ParentContext => Context; + /// public void OnContextCreated(VisjectSurfaceContext context) { @@ -1682,6 +1688,9 @@ namespace FlaxEditor.Surface.Archetypes set => RuleGraph = value; } + /// + public VisjectSurfaceContext ParentContext => SourceState.Context; + /// public void OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Surface/ISurfaceContext.cs b/Source/Editor/Surface/ISurfaceContext.cs index cade0b6f0..8e5f330cb 100644 --- a/Source/Editor/Surface/ISurfaceContext.cs +++ b/Source/Editor/Surface/ISurfaceContext.cs @@ -25,6 +25,11 @@ namespace FlaxEditor.Surface /// byte[] SurfaceData { get; set; } + /// + /// Gets the context which owns this surface context (null for root). + /// + VisjectSurfaceContext ParentContext { get; } + /// /// Called when Visject Surface context gets created for this surface data source. Can be used to link for some events. /// diff --git a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs index 7fa2476cb..72b9242a4 100644 --- a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs +++ b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs @@ -33,6 +33,14 @@ namespace FlaxEditor.Surface.Undo CaptureConnections(iB, out _inputBefore); CaptureConnections(oB, out _outputBefore); + +#if BUILD_DEBUG + // Validate handles + if (_context.Get(_surface) != iB.ParentNode.Context) + throw new System.Exception("Invalid ContextHandle"); + if (_input.Get(iB.ParentNode.Context) != iB || _output.Get(oB.ParentNode.Context) != oB) + throw new System.Exception("Invalid BoxHandle"); +#endif } public void End() diff --git a/Source/Editor/Surface/Undo/ContextHandle.cs b/Source/Editor/Surface/Undo/ContextHandle.cs index 5206c7f16..c816511cc 100644 --- a/Source/Editor/Surface/Undo/ContextHandle.cs +++ b/Source/Editor/Surface/Undo/ContextHandle.cs @@ -1,7 +1,9 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. using System; +using System.Text; using FlaxEngine; +using FlaxEngine.Json; namespace FlaxEditor.Surface.Undo { @@ -9,7 +11,7 @@ namespace FlaxEditor.Surface.Undo /// The helper structure for Surface context handle. /// [HideInEditor] - public struct ContextHandle + public struct ContextHandle : IEquatable { private readonly string[] _path; @@ -42,6 +44,31 @@ namespace FlaxEditor.Surface.Undo } } + /// + /// Initializes a new instance of the struct. + /// + /// The child context provider. + public ContextHandle(ISurfaceContext child) + { + if (child == null) + throw new ArgumentNullException(); + VisjectSurfaceContext parent = child.ParentContext; + VisjectSurfaceContext context = parent; + int count = 1; + while (parent != null) + { + count++; + parent = parent.Parent; + } + _path = new string[count]; + _path[0] = child.SurfaceName; + for (int i = 1; i < count; i++) + { + _path[i] = context.Context.SurfaceName; + context = context.Parent; + } + } + /// /// Gets the context. /// @@ -69,5 +96,46 @@ namespace FlaxEditor.Surface.Undo return context; } + + /// + public bool Equals(ContextHandle other) + { + return JsonSerializer.ValueEquals(_path, other._path); + } + + /// + public override bool Equals(object obj) + { + return obj is ContextHandle other && Equals(other); + } + + /// + public override int GetHashCode() + { + int hash = 17; + if (_path != null) + { + unchecked + { + for (var i = 0; i < _path.Length; i++) + { + var item = _path[i]; + hash = hash * 23 + (item != null ? item.GetHashCode() : 0); + } + } + } + return hash; + } + + /// + public override string ToString() + { + var sb = new StringBuilder(); + if (_path == null) + return string.Empty; + for (int i = _path.Length - 1; i >= 0; i--) + sb.Append(_path[i]).Append('/'); + return sb.ToString(); + } } } diff --git a/Source/Editor/Surface/VisjectSurface.Context.cs b/Source/Editor/Surface/VisjectSurface.Context.cs index e3547fa52..1d8b97729 100644 --- a/Source/Editor/Surface/VisjectSurface.Context.cs +++ b/Source/Editor/Surface/VisjectSurface.Context.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +using FlaxEditor.Surface.Undo; +using FlaxEngine; namespace FlaxEditor.Surface { @@ -9,7 +11,7 @@ namespace FlaxEditor.Surface { private VisjectSurfaceContext _root; private VisjectSurfaceContext _context; - private readonly Dictionary _contextCache = new Dictionary(); + private readonly Dictionary _contextCache = new Dictionary(); /// /// The surface context stack. @@ -54,11 +56,12 @@ namespace FlaxEditor.Surface return; // Get or create context - if (!_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext)) + var contextHandle = new ContextHandle(context); + if (!_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext)) { surfaceContext = CreateContext(_context, context); _context?.Children.Add(surfaceContext); - _contextCache.Add(context, surfaceContext); + _contextCache.Add(contextHandle, surfaceContext); context.OnContextCreated(surfaceContext); @@ -118,7 +121,8 @@ namespace FlaxEditor.Surface } // Check if has context in cache - if (_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext)) + var contextHandle = new ContextHandle(context); + if (_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext)) { // Remove from navigation path while (ContextStack.Contains(surfaceContext)) @@ -126,7 +130,7 @@ namespace FlaxEditor.Surface // Dispose surfaceContext.Clear(); - _contextCache.Remove(context); + _contextCache.Remove(contextHandle); } } @@ -147,7 +151,8 @@ namespace FlaxEditor.Surface return; // Check if already in a path - if (_contextCache.TryGetValue(context, out VisjectSurfaceContext surfaceContext) && ContextStack.Contains(surfaceContext)) + var contextHandle = new ContextHandle(context); + if (_contextCache.TryGetValue(contextHandle, out VisjectSurfaceContext surfaceContext) && ContextStack.Contains(surfaceContext)) { // Change stack do diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 93b7a685d..0b3bc8130 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -899,6 +899,9 @@ namespace FlaxEditor.Surface /// public abstract byte[] SurfaceData { get; set; } + /// + public VisjectSurfaceContext ParentContext => null; + /// public void OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Viewport/Previews/MaterialPreview.cs b/Source/Editor/Viewport/Previews/MaterialPreview.cs index 1cfd13743..632ec199e 100644 --- a/Source/Editor/Viewport/Previews/MaterialPreview.cs +++ b/Source/Editor/Viewport/Previews/MaterialPreview.cs @@ -366,6 +366,9 @@ namespace FlaxEditor.Viewport.Previews } } + /// + public VisjectSurfaceContext ParentContext => null; + /// void ISurfaceContext.OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Windows/AssetReferencesGraphWindow.cs b/Source/Editor/Windows/AssetReferencesGraphWindow.cs index 6e7d3d0ef..de5b86f96 100644 --- a/Source/Editor/Windows/AssetReferencesGraphWindow.cs +++ b/Source/Editor/Windows/AssetReferencesGraphWindow.cs @@ -408,6 +408,9 @@ namespace FlaxEditor.Windows /// public byte[] SurfaceData { get; set; } + /// + public VisjectSurfaceContext ParentContext => null; + /// public void OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs b/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs index 2a75a76bd..7ae6ae8be 100644 --- a/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs +++ b/Source/Editor/Windows/Assets/VisjectFunctionSurfaceWindow.cs @@ -179,6 +179,9 @@ namespace FlaxEditor.Windows.Assets /// public abstract byte[] SurfaceData { get; set; } + /// + public VisjectSurfaceContext ParentContext => null; + /// public void OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs index 72a043eaa..6c2ec72b0 100644 --- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs +++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs @@ -1147,6 +1147,9 @@ namespace FlaxEditor.Windows.Assets } } + /// + public VisjectSurfaceContext ParentContext => null; + /// public void OnContextCreated(VisjectSurfaceContext context) { diff --git a/Source/Editor/Windows/Search/ContentSearchWindow.cs b/Source/Editor/Windows/Search/ContentSearchWindow.cs index 1545fd80b..5ee3ff11c 100644 --- a/Source/Editor/Windows/Search/ContentSearchWindow.cs +++ b/Source/Editor/Windows/Search/ContentSearchWindow.cs @@ -105,6 +105,9 @@ namespace FlaxEngine.Windows.Search /// public byte[] SurfaceData { get; set; } + /// + public VisjectSurfaceContext ParentContext => null; + /// public void OnContextCreated(VisjectSurfaceContext context) {