Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -0,0 +1,162 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class Graph : IGraph
|
||||
{
|
||||
protected Graph()
|
||||
{
|
||||
elements = new MergedGraphElementCollection();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return StringUtility.FallbackWhitespace(title, base.ToString());
|
||||
}
|
||||
|
||||
public abstract IGraphData CreateData();
|
||||
|
||||
public virtual IGraphDebugData CreateDebugData()
|
||||
{
|
||||
return new GraphDebugData(this);
|
||||
}
|
||||
|
||||
public virtual void Instantiate(GraphReference instance)
|
||||
{
|
||||
// Debug.Log($"Instantiating graph {instance}");
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
element.Instantiate(instance);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Uninstantiate(GraphReference instance)
|
||||
{
|
||||
// Debug.Log($"Uninstantiating graph {instance}");
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
element.Uninstantiate(instance);
|
||||
}
|
||||
}
|
||||
|
||||
#region Elements
|
||||
|
||||
[SerializeAs(nameof(elements))]
|
||||
private List<IGraphElement> _elements = new List<IGraphElement>();
|
||||
|
||||
[DoNotSerialize]
|
||||
public MergedGraphElementCollection elements { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Metadata
|
||||
|
||||
[Serialize]
|
||||
public string title { get; set; }
|
||||
|
||||
[Serialize]
|
||||
[InspectorTextArea(minLines = 1, maxLines = 10)]
|
||||
public string summary { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Canvas
|
||||
|
||||
[Serialize]
|
||||
public Vector2 pan { get; set; }
|
||||
|
||||
[Serialize]
|
||||
public float zoom { get; set; } = 1;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Serialization
|
||||
|
||||
public IEnumerable<ISerializationDependency> deserializationDependencies => _elements.SelectMany(e => e.deserializationDependencies);
|
||||
|
||||
public virtual void OnBeforeSerialize()
|
||||
{
|
||||
_elements.Clear();
|
||||
_elements.AddRange(elements);
|
||||
}
|
||||
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
Serialization.AwaitDependencies(this);
|
||||
}
|
||||
|
||||
public virtual void OnAfterDependenciesDeserialized()
|
||||
{
|
||||
elements.Clear();
|
||||
|
||||
// _elements.OrderBy(e => e.dependencyOrder)
|
||||
var sortedElements = ListPool<IGraphElement>.New();
|
||||
foreach (var element in _elements)
|
||||
{
|
||||
sortedElements.Add(element);
|
||||
}
|
||||
sortedElements.Sort((a, b) => a.dependencyOrder.CompareTo(b.dependencyOrder));
|
||||
|
||||
foreach (var element in sortedElements)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!element.HandleDependencies())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
elements.Add(element);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Failed to add element to graph during deserialization: {element}\n{ex}");
|
||||
}
|
||||
}
|
||||
|
||||
ListPool<IGraphElement>.Free(sortedElements);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Poutine
|
||||
|
||||
public IEnumerable<object> aotStubs => elements.SelectMany(element => element.aotStubs);
|
||||
|
||||
private bool prewarmed;
|
||||
|
||||
public void Prewarm()
|
||||
{
|
||||
if (prewarmed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var element in elements)
|
||||
{
|
||||
element.Prewarm();
|
||||
}
|
||||
|
||||
prewarmed = true;
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
foreach (var element in elements)
|
||||
{
|
||||
element.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3af658b760ac04b8ea3f040f301bbc1d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,117 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class GraphData<TGraph> : IGraphData
|
||||
where TGraph : class, IGraph
|
||||
{
|
||||
public GraphData(TGraph definition)
|
||||
{
|
||||
this.definition = definition;
|
||||
}
|
||||
|
||||
protected TGraph definition { get; }
|
||||
|
||||
protected Dictionary<IGraphElementWithData, IGraphElementData> elementsData { get; } = new Dictionary<IGraphElementWithData, IGraphElementData>();
|
||||
|
||||
protected Dictionary<IGraphParentElement, IGraphData> childrenGraphsData { get; } = new Dictionary<IGraphParentElement, IGraphData>();
|
||||
|
||||
protected Dictionary<Guid, IGraphElementData> phantomElementsData { get; } = new Dictionary<Guid, IGraphElementData>();
|
||||
|
||||
protected Dictionary<Guid, IGraphData> phantomChildrenGraphsData { get; } = new Dictionary<Guid, IGraphData>();
|
||||
|
||||
public bool TryGetElementData(IGraphElementWithData element, out IGraphElementData data)
|
||||
{
|
||||
return elementsData.TryGetValue(element, out data);
|
||||
}
|
||||
|
||||
public bool TryGetChildGraphData(IGraphParentElement element, out IGraphData data)
|
||||
{
|
||||
return childrenGraphsData.TryGetValue(element, out data);
|
||||
}
|
||||
|
||||
public IGraphElementData CreateElementData(IGraphElementWithData element)
|
||||
{
|
||||
// Debug.Log($"Creating element data for {element}");
|
||||
|
||||
if (elementsData.ContainsKey(element))
|
||||
{
|
||||
throw new InvalidOperationException($"Graph data already contains element data for {element}.");
|
||||
}
|
||||
|
||||
IGraphElementData elementData;
|
||||
|
||||
if (phantomElementsData.TryGetValue(element.guid, out elementData))
|
||||
{
|
||||
// Debug.Log($"Restoring phantom element data for {element}.");
|
||||
phantomElementsData.Remove(element.guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
elementData = element.CreateData();
|
||||
}
|
||||
|
||||
elementsData.Add(element, elementData);
|
||||
|
||||
return elementData;
|
||||
}
|
||||
|
||||
public void FreeElementData(IGraphElementWithData element)
|
||||
{
|
||||
// Debug.Log($"Freeing element data for {element}");
|
||||
|
||||
if (elementsData.TryGetValue(element, out var elementData))
|
||||
{
|
||||
elementsData.Remove(element);
|
||||
phantomElementsData.Add(element.guid, elementData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Graph data does not contain element data to free for {element}.");
|
||||
}
|
||||
}
|
||||
|
||||
public IGraphData CreateChildGraphData(IGraphParentElement element)
|
||||
{
|
||||
// Debug.Log($"Creating child graph data for {element}");
|
||||
|
||||
if (childrenGraphsData.ContainsKey(element))
|
||||
{
|
||||
throw new InvalidOperationException($"Graph data already contains child graph data for {element}.");
|
||||
}
|
||||
|
||||
IGraphData childGraphData;
|
||||
|
||||
if (phantomChildrenGraphsData.TryGetValue(element.guid, out childGraphData))
|
||||
{
|
||||
// Debug.Log($"Restoring phantom child graph data for {element}.");
|
||||
phantomChildrenGraphsData.Remove(element.guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
childGraphData = element.childGraph.CreateData();
|
||||
}
|
||||
|
||||
childrenGraphsData.Add(element, childGraphData);
|
||||
|
||||
return childGraphData;
|
||||
}
|
||||
|
||||
public void FreeChildGraphData(IGraphParentElement element)
|
||||
{
|
||||
// Debug.Log($"Freeing child graph data for {element}");
|
||||
|
||||
if (childrenGraphsData.TryGetValue(element, out var childGraphData))
|
||||
{
|
||||
childrenGraphsData.Remove(element);
|
||||
phantomChildrenGraphsData.Add(element.guid, childGraphData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Graph data does not contain child graph data to free for {element}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e82ba0da11a744d4aa13044f972bf8f0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class GraphDebugData : IGraphDebugData
|
||||
{
|
||||
protected Dictionary<IGraphElementWithDebugData, IGraphElementDebugData> elementsData { get; } = new Dictionary<IGraphElementWithDebugData, IGraphElementDebugData>();
|
||||
|
||||
protected Dictionary<IGraphParentElement, IGraphDebugData> childrenGraphsData { get; } = new Dictionary<IGraphParentElement, IGraphDebugData>();
|
||||
|
||||
IEnumerable<IGraphElementDebugData> IGraphDebugData.elementsData => elementsData.Values;
|
||||
|
||||
public GraphDebugData(IGraph definition) { }
|
||||
|
||||
public IGraphElementDebugData GetOrCreateElementData(IGraphElementWithDebugData element)
|
||||
{
|
||||
if (!elementsData.TryGetValue(element, out var elementDebugData))
|
||||
{
|
||||
elementDebugData = element.CreateDebugData();
|
||||
elementsData.Add(element, elementDebugData);
|
||||
}
|
||||
|
||||
return elementDebugData;
|
||||
}
|
||||
|
||||
public IGraphDebugData GetOrCreateChildGraphData(IGraphParentElement element)
|
||||
{
|
||||
if (!childrenGraphsData.TryGetValue(element, out var data))
|
||||
{
|
||||
data = new GraphDebugData(element.childGraph);
|
||||
childrenGraphsData.Add(element, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4ac0e89f600ed4b6da74436eb5fc67f7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,177 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class GraphElement<TGraph> : IGraphElement where TGraph : class, IGraph
|
||||
{
|
||||
[Serialize]
|
||||
public Guid guid { get; set; } = Guid.NewGuid();
|
||||
|
||||
// To minimize the amount of implementation needed, and simplify inversion of control,
|
||||
// we provide instantiation routines that fit most types of elements in the right order and
|
||||
// we provide implemented defaults for interfaces that the element could implement.
|
||||
// Normally, an element shouldn't have to override instantiate or uninstantiate directly.
|
||||
|
||||
public virtual void Instantiate(GraphReference instance)
|
||||
{
|
||||
// Create the data for this graph, non-recursively, that is
|
||||
// required for the nest instantiation, so we do it first.
|
||||
if (this is IGraphElementWithData withData)
|
||||
{
|
||||
instance.data.CreateElementData(withData);
|
||||
}
|
||||
|
||||
// Nest instantiation is a recursive operation that will
|
||||
// call Instantiate on descendant graphs and elements.
|
||||
// Because event listening will descend graph data recursively,
|
||||
// we need to create it before.
|
||||
if (this is IGraphNesterElement nester && nester.nest.graph != null)
|
||||
{
|
||||
GraphInstances.Instantiate(instance.ChildReference(nester, true));
|
||||
}
|
||||
|
||||
// StartListening is a recursive operation that will
|
||||
// descend graphs recursively. It must be called after
|
||||
// instantiation of all child graphs because it will require
|
||||
// their data. The drawback with this approach is that it will be
|
||||
// called at each step bubbling up, whereas it will only
|
||||
// effectively trigger once we reach the top level.
|
||||
// Traversal is currently O(n!) where n is the number of descendants,
|
||||
// ideally it would be O(n) if only triggered from the root.
|
||||
|
||||
// => Listening has to be implemented by Bolt classes, because Graphs isn't aware of the event system
|
||||
}
|
||||
|
||||
public virtual void Uninstantiate(GraphReference instance)
|
||||
{
|
||||
// See above comments, in reverse order.
|
||||
|
||||
if (this is IGraphNesterElement nester && nester.nest.graph != null)
|
||||
{
|
||||
GraphInstances.Uninstantiate(instance.ChildReference(nester, true));
|
||||
}
|
||||
|
||||
if (this is IGraphElementWithData withData)
|
||||
{
|
||||
instance.data.FreeElementData(withData);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void BeforeAdd() { }
|
||||
|
||||
public virtual void AfterAdd()
|
||||
{
|
||||
var instances = GraphInstances.OfPooled(graph);
|
||||
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
Instantiate(instance);
|
||||
}
|
||||
|
||||
instances.Free();
|
||||
}
|
||||
|
||||
public virtual void BeforeRemove()
|
||||
{
|
||||
var instances = GraphInstances.OfPooled(graph);
|
||||
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
Uninstantiate(instance);
|
||||
}
|
||||
|
||||
instances.Free();
|
||||
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public virtual void AfterRemove() { }
|
||||
|
||||
public virtual void Dispose() { }
|
||||
|
||||
protected void InstantiateNest()
|
||||
{
|
||||
var nester = (IGraphNesterElement)this;
|
||||
|
||||
if (graph == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var instances = GraphInstances.OfPooled(graph);
|
||||
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
GraphInstances.Instantiate(instance.ChildReference(nester, true));
|
||||
}
|
||||
|
||||
instances.Free();
|
||||
}
|
||||
|
||||
protected void UninstantiateNest()
|
||||
{
|
||||
var nester = (IGraphNesterElement)this;
|
||||
|
||||
var instances = GraphInstances.ChildrenOfPooled(nester);
|
||||
|
||||
foreach (var instance in instances)
|
||||
{
|
||||
GraphInstances.Uninstantiate(instance);
|
||||
}
|
||||
|
||||
instances.Free();
|
||||
}
|
||||
|
||||
#region Graph
|
||||
|
||||
[DoNotSerialize]
|
||||
public virtual int dependencyOrder => 0;
|
||||
|
||||
public virtual bool HandleDependencies() => true;
|
||||
|
||||
[DoNotSerialize]
|
||||
public TGraph graph { get; set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
IGraph IGraphElement.graph
|
||||
{
|
||||
get => graph;
|
||||
set
|
||||
{
|
||||
Ensure.That(nameof(value)).IsOfType<TGraph>(value);
|
||||
graph = (TGraph)value;
|
||||
}
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
IGraph IGraphItem.graph => graph;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Poutine
|
||||
|
||||
public virtual IEnumerable<ISerializationDependency> deserializationDependencies => Enumerable.Empty<ISerializationDependency>();
|
||||
|
||||
public virtual IEnumerable<object> aotStubs => Enumerable.Empty<object>();
|
||||
|
||||
public virtual void Prewarm() { }
|
||||
|
||||
protected void CopyFrom(GraphElement<TGraph> source) { }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append(GetType().Name);
|
||||
sb.Append("#");
|
||||
sb.Append(guid.ToString().Substring(0, 5));
|
||||
sb.Append("...");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: df291d15317bf4d94820d46ed5deea4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,130 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class GraphElementCollection<TElement> : GuidCollection<TElement>, IGraphElementCollection<TElement>, IProxyableNotifyCollectionChanged<TElement>
|
||||
where TElement : IGraphElement
|
||||
{
|
||||
public GraphElementCollection(IGraph graph) : base()
|
||||
{
|
||||
Ensure.That(nameof(graph)).IsNotNull(graph);
|
||||
|
||||
this.graph = graph;
|
||||
}
|
||||
|
||||
public IGraph graph { get; }
|
||||
|
||||
public event Action<TElement> ItemAdded;
|
||||
|
||||
public event Action<TElement> ItemRemoved;
|
||||
|
||||
public event Action CollectionChanged;
|
||||
|
||||
public bool ProxyCollectionChange { get; set; }
|
||||
|
||||
public void BeforeAdd(TElement element)
|
||||
{
|
||||
if (element.graph != null)
|
||||
{
|
||||
if (element.graph == graph)
|
||||
{
|
||||
throw new InvalidOperationException("Graph elements cannot be added multiple time into the same graph.");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Graph elements cannot be shared across graphs.");
|
||||
}
|
||||
}
|
||||
|
||||
element.graph = graph;
|
||||
element.BeforeAdd();
|
||||
}
|
||||
|
||||
public void AfterAdd(TElement element)
|
||||
{
|
||||
element.AfterAdd();
|
||||
ItemAdded?.Invoke(element);
|
||||
CollectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void BeforeRemove(TElement element)
|
||||
{
|
||||
element.BeforeRemove();
|
||||
}
|
||||
|
||||
public void AfterRemove(TElement element)
|
||||
{
|
||||
element.graph = null;
|
||||
element.AfterRemove();
|
||||
ItemRemoved?.Invoke(element);
|
||||
CollectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
protected override void InsertItem(int index, TElement element)
|
||||
{
|
||||
Ensure.That(nameof(element)).IsNotNull(element);
|
||||
|
||||
if (!ProxyCollectionChange)
|
||||
{
|
||||
BeforeAdd(element);
|
||||
}
|
||||
|
||||
base.InsertItem(index, element);
|
||||
|
||||
if (!ProxyCollectionChange)
|
||||
{
|
||||
AfterAdd(element);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveItem(int index)
|
||||
{
|
||||
var element = this[index];
|
||||
|
||||
if (!Contains(element))
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(element));
|
||||
}
|
||||
|
||||
if (!ProxyCollectionChange)
|
||||
{
|
||||
BeforeRemove(element);
|
||||
}
|
||||
|
||||
base.RemoveItem(index);
|
||||
|
||||
if (!ProxyCollectionChange)
|
||||
{
|
||||
AfterRemove(element);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ClearItems()
|
||||
{
|
||||
// this.OrderByDescending(e => e.dependencyOrder).ToList()
|
||||
var toRemove = ListPool<TElement>.New();
|
||||
foreach (var element in this)
|
||||
{
|
||||
toRemove.Add(element);
|
||||
}
|
||||
toRemove.Sort((a, b) => b.dependencyOrder.CompareTo(a.dependencyOrder));
|
||||
|
||||
foreach (var element in toRemove)
|
||||
{
|
||||
Remove(element);
|
||||
}
|
||||
|
||||
ListPool<TElement>.Free(toRemove);
|
||||
}
|
||||
|
||||
protected override void SetItem(int index, TElement item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public new NoAllocEnumerator<TElement> GetEnumerator()
|
||||
{
|
||||
return new NoAllocEnumerator<TElement>(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dd19e32f1cc4b4aa0be94290188834ef
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,152 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public static class GraphInstances
|
||||
{
|
||||
private static readonly object @lock = new object();
|
||||
|
||||
private static readonly Dictionary<IGraph, HashSet<GraphReference>> byGraph = new Dictionary<IGraph, HashSet<GraphReference>>();
|
||||
|
||||
private static readonly Dictionary<IGraphParent, HashSet<GraphReference>> byParent = new Dictionary<IGraphParent, HashSet<GraphReference>>();
|
||||
|
||||
public static void Instantiate(GraphReference instance)
|
||||
{
|
||||
lock (@lock)
|
||||
{
|
||||
Ensure.That(nameof(instance)).IsNotNull(instance);
|
||||
|
||||
instance.CreateGraphData();
|
||||
|
||||
instance.graph.Instantiate(instance);
|
||||
|
||||
if (!byGraph.TryGetValue(instance.graph, out var instancesWithGraph))
|
||||
{
|
||||
instancesWithGraph = new HashSet<GraphReference>();
|
||||
byGraph.Add(instance.graph, instancesWithGraph);
|
||||
}
|
||||
|
||||
if (instancesWithGraph.Add(instance))
|
||||
{
|
||||
// Debug.Log($"Added graph instance mapping:\n{instance.graph} => {instance}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Attempting to add duplicate graph instance mapping:\n{instance.graph} => {instance}");
|
||||
}
|
||||
|
||||
if (!byParent.TryGetValue(instance.parent, out var instancesWithParent))
|
||||
{
|
||||
instancesWithParent = new HashSet<GraphReference>();
|
||||
byParent.Add(instance.parent, instancesWithParent);
|
||||
}
|
||||
|
||||
if (instancesWithParent.Add(instance))
|
||||
{
|
||||
// Debug.Log($"Added parent instance mapping:\n{instance.parent.ToSafeString()} => {instance}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Attempting to add duplicate parent instance mapping:\n{instance.parent.ToSafeString()} => {instance}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Uninstantiate(GraphReference instance)
|
||||
{
|
||||
lock (@lock)
|
||||
{
|
||||
instance.graph.Uninstantiate(instance);
|
||||
|
||||
if (!byGraph.TryGetValue(instance.graph, out var instancesWithGraph))
|
||||
{
|
||||
throw new InvalidOperationException("Graph instance not found via graph.");
|
||||
}
|
||||
|
||||
if (instancesWithGraph.Remove(instance))
|
||||
{
|
||||
// Debug.Log($"Removed graph instance mapping:\n{instance.graph} => {instance}");
|
||||
|
||||
// Free the key references for GC collection
|
||||
if (instancesWithGraph.Count == 0)
|
||||
{
|
||||
byGraph.Remove(instance.graph);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Could not find graph instance mapping to remove:\n{instance.graph} => {instance}");
|
||||
}
|
||||
|
||||
if (!byParent.TryGetValue(instance.parent, out var instancesWithParent))
|
||||
{
|
||||
throw new InvalidOperationException("Graph instance not found via parent.");
|
||||
}
|
||||
|
||||
if (instancesWithParent.Remove(instance))
|
||||
{
|
||||
// Debug.Log($"Removed parent instance mapping:\n{instance.parent.ToSafeString()} => {instance}");
|
||||
|
||||
// Free the key references for GC collection
|
||||
if (instancesWithParent.Count == 0)
|
||||
{
|
||||
byParent.Remove(instance.parent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"Could not find parent instance mapping to remove:\n{instance.parent.ToSafeString()} => {instance}");
|
||||
}
|
||||
|
||||
// It's important to only free the graph data after
|
||||
// dissociating the instance mapping, because the data
|
||||
// is used as part of the equality comparison for pointers
|
||||
instance.FreeGraphData();
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<GraphReference> OfPooled(IGraph graph)
|
||||
{
|
||||
Ensure.That(nameof(graph)).IsNotNull(graph);
|
||||
|
||||
lock (@lock)
|
||||
{
|
||||
if (byGraph.TryGetValue(graph, out var instances))
|
||||
{
|
||||
// Debug.Log($"Found {instances.Count} instances of {graph}\n{instances.ToLineSeparatedString()}");
|
||||
|
||||
return instances.ToHashSetPooled();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.Log($"Found no instances of {graph}.\n");
|
||||
|
||||
return HashSetPool<GraphReference>.New();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<GraphReference> ChildrenOfPooled(IGraphParent parent)
|
||||
{
|
||||
Ensure.That(nameof(parent)).IsNotNull(parent);
|
||||
|
||||
lock (@lock)
|
||||
{
|
||||
if (byParent.TryGetValue(parent, out var instances))
|
||||
{
|
||||
// Debug.Log($"Found {instances.Count} instances of {parent.ToSafeString()}\n{instances.ToLineSeparatedString()}");
|
||||
|
||||
return instances.ToHashSetPooled();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Debug.Log($"Found no instances of {parent.ToSafeString()}.\n");
|
||||
|
||||
return HashSetPool<GraphReference>.New();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2779fdac2027441508654540bc9b17dc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,199 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class GraphNest<TGraph, TMacro> : IGraphNest
|
||||
where TGraph : class, IGraph, new()
|
||||
where TMacro : Macro<TGraph>
|
||||
{
|
||||
[DoNotSerialize]
|
||||
public IGraphNester nester { get; set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
private GraphSource _source = GraphSource.Macro;
|
||||
|
||||
[DoNotSerialize]
|
||||
private TMacro _macro;
|
||||
|
||||
[DoNotSerialize]
|
||||
private TGraph _embed;
|
||||
|
||||
[Serialize]
|
||||
public GraphSource source
|
||||
{
|
||||
get => _source;
|
||||
set
|
||||
{
|
||||
if (value == source)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeGraphChange();
|
||||
|
||||
_source = value;
|
||||
|
||||
AfterGraphChange();
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
public TMacro macro
|
||||
{
|
||||
get => _macro;
|
||||
set
|
||||
{
|
||||
if (value == macro)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeGraphChange();
|
||||
|
||||
_macro = value;
|
||||
|
||||
AfterGraphChange();
|
||||
}
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
public TGraph embed
|
||||
{
|
||||
get => _embed;
|
||||
set
|
||||
{
|
||||
if (value == embed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeGraphChange();
|
||||
|
||||
_embed = value;
|
||||
|
||||
AfterGraphChange();
|
||||
}
|
||||
}
|
||||
|
||||
[DoNotSerialize]
|
||||
public TGraph graph
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case GraphSource.Embed:
|
||||
return embed;
|
||||
|
||||
case GraphSource.Macro:
|
||||
return macro?.graph;
|
||||
|
||||
default:
|
||||
throw new UnexpectedEnumValueException<GraphSource>(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMacro IGraphNest.macro
|
||||
{
|
||||
get => macro;
|
||||
set => macro = (TMacro)value;
|
||||
}
|
||||
|
||||
IGraph IGraphNest.embed
|
||||
{
|
||||
get => embed;
|
||||
set => embed = (TGraph)value;
|
||||
}
|
||||
|
||||
IGraph IGraphNest.graph => graph;
|
||||
|
||||
Type IGraphNest.graphType => typeof(TGraph);
|
||||
|
||||
Type IGraphNest.macroType => typeof(TMacro);
|
||||
|
||||
// TODO: Use these in the editor when appropriate to minimize change events
|
||||
public void SwitchToEmbed(TGraph embed)
|
||||
{
|
||||
if (source == GraphSource.Embed && this.embed == embed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeGraphChange();
|
||||
|
||||
_source = GraphSource.Embed;
|
||||
_embed = embed;
|
||||
_macro = null;
|
||||
|
||||
AfterGraphChange();
|
||||
}
|
||||
|
||||
public void SwitchToMacro(TMacro macro)
|
||||
{
|
||||
if (source == GraphSource.Macro && this.macro == macro)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BeforeGraphChange();
|
||||
|
||||
_source = GraphSource.Macro;
|
||||
_embed = null;
|
||||
_macro = macro;
|
||||
|
||||
AfterGraphChange();
|
||||
}
|
||||
|
||||
public event Action beforeGraphChange;
|
||||
|
||||
public event Action afterGraphChange;
|
||||
|
||||
private void BeforeGraphChange()
|
||||
{
|
||||
if (graph != null)
|
||||
{
|
||||
nester.UninstantiateNest();
|
||||
}
|
||||
|
||||
beforeGraphChange?.Invoke();
|
||||
}
|
||||
|
||||
private void AfterGraphChange()
|
||||
{
|
||||
afterGraphChange?.Invoke();
|
||||
|
||||
if (graph != null)
|
||||
{
|
||||
nester.InstantiateNest();
|
||||
}
|
||||
}
|
||||
|
||||
#region Serialization
|
||||
|
||||
public IEnumerable<ISerializationDependency> deserializationDependencies
|
||||
{
|
||||
get
|
||||
{
|
||||
if (macro != null)
|
||||
{
|
||||
yield return macro;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Poutine
|
||||
|
||||
[DoNotSerialize]
|
||||
public IEnumerable<object> aotStubs => LinqUtility.Concat<object>(graph?.aotStubs);
|
||||
|
||||
[DoNotSerialize]
|
||||
public bool hasBackgroundEmbed => source == GraphSource.Macro && embed != null;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a4dcc9606f76f4e0ca4401e047130678
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,717 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public abstract class GraphPointer
|
||||
{
|
||||
#region Lifecycle
|
||||
|
||||
protected static bool IsValidRoot(IGraphRoot root)
|
||||
{
|
||||
return root?.childGraph != null && root as UnityObject != null;
|
||||
}
|
||||
|
||||
protected static bool IsValidRoot(UnityObject rootObject)
|
||||
{
|
||||
return rootObject != null && (rootObject as IGraphRoot)?.childGraph != null;
|
||||
}
|
||||
|
||||
internal GraphPointer() { }
|
||||
|
||||
protected void Initialize(IGraphRoot root)
|
||||
{
|
||||
if (!IsValidRoot(root))
|
||||
{
|
||||
throw new ArgumentException("Graph pointer root must be a valid Unity object with a non-null child graph.", nameof(root));
|
||||
}
|
||||
|
||||
if (!(root is IMachine && root is MonoBehaviour || root is IMacro && root is ScriptableObject))
|
||||
{
|
||||
throw new ArgumentException("Graph pointer root must be either a machine or a macro.", nameof(root));
|
||||
}
|
||||
|
||||
this.root = root;
|
||||
|
||||
parentStack.Add(root);
|
||||
|
||||
graphStack.Add(root.childGraph);
|
||||
|
||||
dataStack.Add(machine?.graphData);
|
||||
|
||||
debugDataStack.Add(fetchRootDebugDataBinding?.Invoke(root));
|
||||
|
||||
if (machine != null)
|
||||
{
|
||||
// Annoyingly, getting the gameObject property is an API call
|
||||
// First, we'll try using our IMachine safe reference that is assigned in play mode on Awake
|
||||
// If that fails, we'll try fetching it dynamically
|
||||
|
||||
if (machine.threadSafeGameObject != null)
|
||||
{
|
||||
gameObject = machine.threadSafeGameObject;
|
||||
}
|
||||
else if (UnityThread.allowsAPI)
|
||||
{
|
||||
gameObject = component.gameObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new GraphPointerException("Could not fetch graph pointer root game object.", this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gameObject = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Initialize(IGraphRoot root, IEnumerable<IGraphParentElement> parentElements, bool ensureValid)
|
||||
{
|
||||
Initialize(root);
|
||||
|
||||
Ensure.That(nameof(parentElements)).IsNotNull(parentElements);
|
||||
|
||||
foreach (var parentElement in parentElements)
|
||||
{
|
||||
if (!TryEnterParentElement(parentElement, out var error))
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void Initialize(UnityObject rootObject, IEnumerable<Guid> parentElementGuids, bool ensureValid)
|
||||
{
|
||||
Initialize(rootObject as IGraphRoot);
|
||||
|
||||
Ensure.That(nameof(parentElementGuids)).IsNotNull(parentElementGuids);
|
||||
|
||||
foreach (var parentElementGuid in parentElementGuids)
|
||||
{
|
||||
if (!TryEnterParentElement(parentElementGuid, out var error))
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Conversion
|
||||
|
||||
public abstract GraphReference AsReference();
|
||||
|
||||
public virtual void CopyFrom(GraphPointer other)
|
||||
{
|
||||
root = other.root;
|
||||
gameObject = other.gameObject;
|
||||
|
||||
parentStack.Clear();
|
||||
parentElementStack.Clear();
|
||||
graphStack.Clear();
|
||||
dataStack.Clear();
|
||||
debugDataStack.Clear();
|
||||
|
||||
foreach (var parent in other.parentStack)
|
||||
{
|
||||
parentStack.Add(parent);
|
||||
}
|
||||
|
||||
foreach (var parentElement in other.parentElementStack)
|
||||
{
|
||||
parentElementStack.Add(parentElement);
|
||||
}
|
||||
|
||||
foreach (var graph in other.graphStack)
|
||||
{
|
||||
graphStack.Add(graph);
|
||||
}
|
||||
|
||||
foreach (var data in other.dataStack)
|
||||
{
|
||||
dataStack.Add(data);
|
||||
}
|
||||
|
||||
foreach (var debugData in other.debugDataStack)
|
||||
{
|
||||
debugDataStack.Add(debugData);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Stack
|
||||
|
||||
public IGraphRoot root { get; protected set; }
|
||||
|
||||
public UnityObject rootObject => root as UnityObject;
|
||||
|
||||
public IMachine machine => root as IMachine;
|
||||
|
||||
public IMacro macro => root as IMacro;
|
||||
|
||||
public MonoBehaviour component => root as MonoBehaviour;
|
||||
|
||||
public GameObject gameObject { get; private set; }
|
||||
|
||||
public GameObject self => gameObject;
|
||||
|
||||
public ScriptableObject scriptableObject => root as ScriptableObject;
|
||||
|
||||
public Scene? scene
|
||||
{
|
||||
get
|
||||
{
|
||||
if (gameObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var scene = gameObject.scene;
|
||||
|
||||
// We must allow to return unloaded scenes, because
|
||||
// On Enable might try fetching scene variables for example
|
||||
// See: https://support.ludiq.io/communities/5/topics/1864-/
|
||||
|
||||
if (!scene.IsValid() /* || !scene.isLoaded */)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
}
|
||||
|
||||
public UnityObject serializedObject
|
||||
{
|
||||
get
|
||||
{
|
||||
var depth = this.depth;
|
||||
|
||||
while (depth > 0)
|
||||
{
|
||||
var parent = parentStack[depth - 1];
|
||||
|
||||
if (parent.isSerializationRoot)
|
||||
{
|
||||
return parent.serializedObject;
|
||||
}
|
||||
|
||||
depth--;
|
||||
}
|
||||
|
||||
throw new GraphPointerException("Could not find serialized object.", this);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly List<IGraphParent> parentStack = new List<IGraphParent>();
|
||||
|
||||
protected readonly List<IGraphParentElement> parentElementStack = new List<IGraphParentElement>();
|
||||
|
||||
protected readonly List<IGraph> graphStack = new List<IGraph>();
|
||||
|
||||
protected readonly List<IGraphData> dataStack = new List<IGraphData>();
|
||||
|
||||
protected readonly List<IGraphDebugData> debugDataStack = new List<IGraphDebugData>();
|
||||
|
||||
public IEnumerable<Guid> parentElementGuids => parentElementStack.Select(parentElement => parentElement.guid);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Utility
|
||||
|
||||
public int depth => parentStack.Count;
|
||||
|
||||
public bool isRoot => depth == 1;
|
||||
|
||||
public bool isChild => depth > 1;
|
||||
|
||||
public void EnsureDepthValid(int depth)
|
||||
{
|
||||
Ensure.That(nameof(depth)).IsGte(depth, 1);
|
||||
|
||||
if (depth > this.depth)
|
||||
{
|
||||
throw new GraphPointerException($"Trying to fetch a graph pointer level above depth: {depth} > {this.depth}", this);
|
||||
}
|
||||
}
|
||||
|
||||
public void EnsureChild()
|
||||
{
|
||||
if (!isChild)
|
||||
{
|
||||
throw new GraphPointerException("Graph pointer does not point to a child graph.", this);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsWithin<T>() where T : IGraphParent
|
||||
{
|
||||
return parent is T;
|
||||
}
|
||||
|
||||
public void EnsureWithin<T>() where T : IGraphParent
|
||||
{
|
||||
if (!IsWithin<T>())
|
||||
{
|
||||
throw new GraphPointerException($"Graph pointer must be within a {typeof(T)} for this operation.", this);
|
||||
}
|
||||
}
|
||||
|
||||
public IGraphParent parent => parentStack[parentStack.Count - 1];
|
||||
|
||||
public T GetParent<T>() where T : IGraphParent
|
||||
{
|
||||
EnsureWithin<T>();
|
||||
|
||||
return (T)parent;
|
||||
}
|
||||
|
||||
public IGraphParentElement parentElement
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureChild();
|
||||
|
||||
return parentElementStack[parentElementStack.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public IGraph rootGraph => graphStack[0];
|
||||
|
||||
public IGraph graph => graphStack[graphStack.Count - 1];
|
||||
|
||||
protected IGraphData _data
|
||||
{
|
||||
get => dataStack[dataStack.Count - 1];
|
||||
set => dataStack[dataStack.Count - 1] = value;
|
||||
}
|
||||
|
||||
public IGraphData data
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureDataAvailable();
|
||||
return _data;
|
||||
}
|
||||
}
|
||||
|
||||
protected IGraphData _parentData => dataStack[dataStack.Count - 2];
|
||||
|
||||
public bool hasData => _data != null;
|
||||
|
||||
public void EnsureDataAvailable()
|
||||
{
|
||||
if (!hasData)
|
||||
{
|
||||
throw new GraphPointerException($"Graph data is not available.", this);
|
||||
}
|
||||
}
|
||||
|
||||
public T GetGraphData<T>() where T : IGraphData
|
||||
{
|
||||
var data = this.data;
|
||||
|
||||
if (data is T)
|
||||
{
|
||||
return (T)data;
|
||||
}
|
||||
|
||||
throw new GraphPointerException($"Graph data type mismatch. Found {data.GetType()}, expected {typeof(T)}.", this);
|
||||
}
|
||||
|
||||
public T GetElementData<T>(IGraphElementWithData element) where T : IGraphElementData
|
||||
{
|
||||
if (_data.TryGetElementData(element, out var elementData))
|
||||
{
|
||||
if (elementData is T)
|
||||
{
|
||||
return (T)elementData;
|
||||
}
|
||||
|
||||
throw new GraphPointerException($"Graph element data type mismatch. Found {elementData.GetType()}, expected {typeof(T)}.", this);
|
||||
}
|
||||
|
||||
throw new GraphPointerException($"Missing graph element data for {element}.", this);
|
||||
}
|
||||
|
||||
public static Func<IGraphRoot, IGraphDebugData> fetchRootDebugDataBinding { get; set; }
|
||||
|
||||
public bool hasDebugData => _debugData != null;
|
||||
|
||||
public void EnsureDebugDataAvailable()
|
||||
{
|
||||
if (!hasDebugData)
|
||||
{
|
||||
throw new GraphPointerException($"Graph debug data is not available.", this);
|
||||
}
|
||||
}
|
||||
|
||||
protected IGraphDebugData _debugData
|
||||
{
|
||||
get => debugDataStack[debugDataStack.Count - 1];
|
||||
set => debugDataStack[debugDataStack.Count - 1] = value;
|
||||
}
|
||||
|
||||
public IGraphDebugData debugData
|
||||
{
|
||||
get
|
||||
{
|
||||
EnsureDebugDataAvailable();
|
||||
return _debugData;
|
||||
}
|
||||
}
|
||||
|
||||
public T GetGraphDebugData<T>() where T : IGraphDebugData
|
||||
{
|
||||
var debugData = this.debugData;
|
||||
|
||||
if (debugData is T)
|
||||
{
|
||||
return (T)debugData;
|
||||
}
|
||||
|
||||
throw new GraphPointerException($"Graph debug data type mismatch. Found {debugData.GetType()}, expected {typeof(T)}.", this);
|
||||
}
|
||||
|
||||
public T GetElementDebugData<T>(IGraphElementWithDebugData element)
|
||||
{
|
||||
var elementDebugData = debugData.GetOrCreateElementData(element);
|
||||
|
||||
if (elementDebugData is T)
|
||||
{
|
||||
return (T)elementDebugData;
|
||||
}
|
||||
|
||||
throw new GraphPointerException($"Graph element runtime debug data type mismatch. Found {elementDebugData.GetType()}, expected {typeof(T)}.", this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Traversal
|
||||
|
||||
protected bool TryEnterParentElement(Guid parentElementGuid, out string error, int? maxRecursionDepth = null)
|
||||
{
|
||||
if (!graph.elements.TryGetValue(parentElementGuid, out var element))
|
||||
{
|
||||
error = "Trying to enter a graph parent element with a GUID that is not within the current graph.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(element is IGraphParentElement))
|
||||
{
|
||||
error = "Provided element GUID does not point to a graph parent element.";
|
||||
return false;
|
||||
}
|
||||
|
||||
var parentElement = (IGraphParentElement)element;
|
||||
|
||||
return TryEnterParentElement(parentElement, out error, maxRecursionDepth);
|
||||
}
|
||||
|
||||
protected bool TryEnterParentElement(IGraphParentElement parentElement, out string error, int? maxRecursionDepth = null, bool skipContainsCheck = false)
|
||||
{
|
||||
// The contains check is expensive because variant+merged collection checks
|
||||
// If we already know for sure this error cannot happen, skipping it provides a significant optim
|
||||
if (!skipContainsCheck && !graph.elements.Contains(parentElement))
|
||||
{
|
||||
error = "Trying to enter a graph parent element that is not within the current graph.";
|
||||
return false;
|
||||
}
|
||||
|
||||
var childGraph = parentElement.childGraph;
|
||||
|
||||
if (childGraph == null)
|
||||
{
|
||||
error = "Trying to enter a graph parent element without a child graph.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Recursion.safeMode)
|
||||
{
|
||||
var recursionDepth = 0;
|
||||
var _maxRecursionDepth = maxRecursionDepth ?? Recursion.defaultMaxDepth;
|
||||
|
||||
foreach (var parentGraph in graphStack)
|
||||
{
|
||||
if (parentGraph == childGraph)
|
||||
{
|
||||
recursionDepth++;
|
||||
}
|
||||
}
|
||||
|
||||
if (recursionDepth > _maxRecursionDepth)
|
||||
{
|
||||
error = $"Max recursion depth of {_maxRecursionDepth} has been exceeded. Are you nesting a graph within itself?\nIf not, consider increasing '{nameof(Recursion)}.{nameof(Recursion.defaultMaxDepth)}'.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
EnterValidParentElement(parentElement);
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void EnterParentElement(IGraphParentElement parentElement)
|
||||
{
|
||||
if (!TryEnterParentElement(parentElement, out var error))
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void EnterParentElement(Guid parentElementGuid)
|
||||
{
|
||||
if (!TryEnterParentElement(parentElementGuid, out var error))
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void EnterValidParentElement(IGraphParentElement parentElement)
|
||||
{
|
||||
var childGraph = parentElement.childGraph;
|
||||
|
||||
parentStack.Add(parentElement);
|
||||
parentElementStack.Add(parentElement);
|
||||
graphStack.Add(childGraph);
|
||||
|
||||
IGraphData childGraphData = null;
|
||||
_data?.TryGetChildGraphData(parentElement, out childGraphData);
|
||||
dataStack.Add(childGraphData);
|
||||
|
||||
var childGraphDebugData = _debugData?.GetOrCreateChildGraphData(parentElement);
|
||||
debugDataStack.Add(childGraphDebugData);
|
||||
}
|
||||
|
||||
protected void ExitParentElement()
|
||||
{
|
||||
if (!isChild)
|
||||
{
|
||||
throw new GraphPointerException("Trying to exit the root graph.", this);
|
||||
}
|
||||
|
||||
parentStack.RemoveAt(parentStack.Count - 1);
|
||||
parentElementStack.RemoveAt(parentElementStack.Count - 1);
|
||||
graphStack.RemoveAt(graphStack.Count - 1);
|
||||
dataStack.RemoveAt(dataStack.Count - 1);
|
||||
debugDataStack.RemoveAt(debugDataStack.Count - 1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Validation
|
||||
|
||||
public bool isValid
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
if (rootObject == null)
|
||||
{
|
||||
// Root object has been destroyed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rootGraph != root.childGraph)
|
||||
{
|
||||
// Root graph has changed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (serializedObject == null)
|
||||
{
|
||||
// Serialized object has been destroyed
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var depth = 1; depth < this.depth; depth++)
|
||||
{
|
||||
var parentElement = parentElementStack[depth - 1];
|
||||
var parentGraph = graphStack[depth - 1];
|
||||
var childGraph = graphStack[depth];
|
||||
|
||||
// Important to check by object and not by GUID here,
|
||||
// because object stack integrity has to be guaranteed
|
||||
// (GUID integrity is implied because they're immutable)
|
||||
if (!parentGraph.elements.Contains(parentElement))
|
||||
{
|
||||
// Parent graph no longer contains the parent element
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parentElement.childGraph != childGraph)
|
||||
{
|
||||
// Child graph has changed
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning("Failed to check graph pointer validity: \n" + ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void EnsureValid()
|
||||
{
|
||||
if (!isValid)
|
||||
{
|
||||
throw new GraphPointerException("Graph pointer is invalid.", this);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Equality
|
||||
|
||||
public bool InstanceEquals(GraphPointer other)
|
||||
{
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!UnityObjectUtility.TrulyEqual(rootObject, other.rootObject))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DefinitionEquals(other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var depth = this.depth; // Micro optimization
|
||||
|
||||
for (int d = 0; d < depth; d++)
|
||||
{
|
||||
var data = dataStack[d];
|
||||
var otherData = other.dataStack[d];
|
||||
|
||||
if (data != otherData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DefinitionEquals(GraphPointer other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rootGraph != other.rootGraph)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var depth = this.depth; // Micro optimization
|
||||
|
||||
if (depth != other.depth)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int d = 1; d < depth; d++)
|
||||
{
|
||||
var parentElement = parentElementStack[d - 1];
|
||||
var otherParentElement = other.parentElementStack[d - 1];
|
||||
|
||||
if (parentElement != otherParentElement)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int ComputeHashCode()
|
||||
{
|
||||
var hashCode = 17;
|
||||
|
||||
hashCode = hashCode * 23 + (rootObject.AsUnityNull()?.GetHashCode() ?? 0);
|
||||
|
||||
hashCode = hashCode * 23 + (rootGraph?.GetHashCode() ?? 0);
|
||||
|
||||
var depth = this.depth; // Micro optimization
|
||||
|
||||
for (int d = 1; d < depth; d++)
|
||||
{
|
||||
var parentElementGuid = parentElementStack[d - 1].guid;
|
||||
|
||||
hashCode = hashCode * 23 + parentElementGuid.GetHashCode();
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Breadcrumbs
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.Append("[ ");
|
||||
|
||||
sb.Append(rootObject.ToSafeString());
|
||||
|
||||
for (var depth = 1; depth < this.depth; depth++)
|
||||
{
|
||||
sb.Append(" > ");
|
||||
|
||||
var parentElementIndex = depth - 1;
|
||||
|
||||
if (parentElementIndex >= parentElementStack.Count)
|
||||
{
|
||||
sb.Append("?");
|
||||
break;
|
||||
}
|
||||
|
||||
var parentElement = parentElementStack[parentElementIndex];
|
||||
|
||||
sb.Append(parentElement);
|
||||
}
|
||||
|
||||
sb.Append(" ]");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fb46e167769fd4b5e8f7c2ca3e0d431e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class GraphPointerException : Exception
|
||||
{
|
||||
public GraphPointer pointer { get; }
|
||||
|
||||
public GraphPointerException(string message, GraphPointer pointer) : base(message + "\n" + pointer)
|
||||
{
|
||||
this.pointer = pointer;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b217cb060732b439ab2575423ca7eb5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,373 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class GraphReference : GraphPointer
|
||||
{
|
||||
static GraphReference()
|
||||
{
|
||||
ReferenceCollector.onSceneUnloaded += FreeInvalidInterns;
|
||||
}
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
private GraphReference() { }
|
||||
|
||||
public static GraphReference New(IGraphRoot root, bool ensureValid)
|
||||
{
|
||||
if (!ensureValid && !IsValidRoot(root))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var reference = new GraphReference();
|
||||
reference.Initialize(root);
|
||||
reference.Hash();
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static GraphReference New(IGraphRoot root, IEnumerable<IGraphParentElement> parentElements, bool ensureValid)
|
||||
{
|
||||
if (!ensureValid && !IsValidRoot(root))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var reference = new GraphReference();
|
||||
reference.Initialize(root, parentElements, ensureValid);
|
||||
reference.Hash();
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static GraphReference New(UnityObject rootObject, IEnumerable<Guid> parentElementGuids, bool ensureValid)
|
||||
{
|
||||
if (!ensureValid && !IsValidRoot(rootObject))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var reference = new GraphReference();
|
||||
reference.Initialize(rootObject, parentElementGuids, ensureValid);
|
||||
reference.Hash();
|
||||
return reference;
|
||||
}
|
||||
|
||||
private static GraphReference New(GraphPointer model)
|
||||
{
|
||||
var reference = new GraphReference();
|
||||
reference.CopyFrom(model);
|
||||
return reference;
|
||||
}
|
||||
|
||||
public override void CopyFrom(GraphPointer other)
|
||||
{
|
||||
base.CopyFrom(other);
|
||||
|
||||
if (other is GraphReference reference)
|
||||
{
|
||||
hashCode = reference.hashCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
Hash();
|
||||
}
|
||||
}
|
||||
|
||||
public GraphReference Clone()
|
||||
{
|
||||
return New(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Conversion
|
||||
|
||||
public override GraphReference AsReference()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public GraphStack ToStackPooled()
|
||||
{
|
||||
return GraphStack.New(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Instantiation
|
||||
|
||||
public void CreateGraphData()
|
||||
{
|
||||
if (_data != null)
|
||||
{
|
||||
throw new GraphPointerException("Graph data already exists.", this);
|
||||
}
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
if (machine != null)
|
||||
{
|
||||
// Debug.Log($"Creating root graph data for {this}");
|
||||
|
||||
_data = machine.graphData = graph.CreateData();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new GraphPointerException("Root graph data can only be created on machines.", this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_parentData == null)
|
||||
{
|
||||
throw new GraphPointerException("Child graph data can only be created from parent graph data.", this);
|
||||
}
|
||||
|
||||
_data = _parentData.CreateChildGraphData(parentElement);
|
||||
}
|
||||
}
|
||||
|
||||
public void FreeGraphData()
|
||||
{
|
||||
if (_data == null)
|
||||
{
|
||||
throw new GraphPointerException("Graph data does not exist.", this);
|
||||
}
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
if (machine != null)
|
||||
{
|
||||
// Debug.Log($"Freeing root graph data for {this}");
|
||||
|
||||
_data = machine.graphData = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new GraphPointerException("Root graph data can only be freed on machines.", this);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_parentData == null)
|
||||
{
|
||||
throw new GraphPointerException("Child graph data can only be freed from parent graph data.", this);
|
||||
}
|
||||
|
||||
_parentData.FreeChildGraphData(parentElement);
|
||||
_data = null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Equality
|
||||
|
||||
[DoNotSerialize]
|
||||
private int hashCode;
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (!(obj is GraphReference other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return InstanceEquals(other);
|
||||
}
|
||||
|
||||
private void Hash()
|
||||
{
|
||||
hashCode = ComputeHashCode();
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public static bool operator ==(GraphReference x, GraphReference y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return x.Equals(y);
|
||||
}
|
||||
|
||||
public static bool operator !=(GraphReference x, GraphReference y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Traversal
|
||||
|
||||
public GraphReference ParentReference(bool ensureValid)
|
||||
{
|
||||
if (isRoot)
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw new GraphPointerException("Trying to get parent graph reference of a root.", this);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var pointer = Clone();
|
||||
pointer.ExitParentElement();
|
||||
pointer.Hash();
|
||||
return pointer;
|
||||
}
|
||||
|
||||
public GraphReference ChildReference(IGraphParentElement parentElement, bool ensureValid, int? maxRecursionDepth = null)
|
||||
{
|
||||
var pointer = Clone();
|
||||
|
||||
if (!pointer.TryEnterParentElement(parentElement, out var error, maxRecursionDepth))
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
pointer.Hash();
|
||||
return pointer;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Validation
|
||||
|
||||
public GraphReference Revalidate(bool ensureValid)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Important to recreate by GUIDs to avoid serialization ghosting
|
||||
return New(rootObject, parentElementGuids, ensureValid);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
Debug.LogWarning("Failed to revalidate graph pointer: \n" + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Navigation
|
||||
|
||||
public IEnumerable<GraphReference> GetBreadcrumbs()
|
||||
{
|
||||
for (int depth = 0; depth < this.depth; depth++)
|
||||
{
|
||||
yield return New(root, parentElementStack.Take(depth), true);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Interning
|
||||
|
||||
private static readonly Dictionary<int, List<GraphReference>> internPool = new Dictionary<int, List<GraphReference>>();
|
||||
|
||||
public static GraphReference Intern(GraphPointer pointer)
|
||||
{
|
||||
var hash = pointer.ComputeHashCode();
|
||||
|
||||
if (internPool.TryGetValue(hash, out var interns))
|
||||
{
|
||||
foreach (var intern in interns)
|
||||
{
|
||||
if (intern.InstanceEquals(pointer))
|
||||
{
|
||||
return intern;
|
||||
}
|
||||
}
|
||||
|
||||
var reference = New(pointer);
|
||||
interns.Add(reference);
|
||||
return reference;
|
||||
}
|
||||
else
|
||||
{
|
||||
var reference = New(pointer);
|
||||
internPool.Add(reference.hashCode, new List<GraphReference>() { reference });
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
|
||||
public static void FreeInvalidInterns()
|
||||
{
|
||||
var invalidHashes = ListPool<int>.New();
|
||||
|
||||
foreach (var internsByHash in internPool)
|
||||
{
|
||||
var hash = internsByHash.Key;
|
||||
var interns = internsByHash.Value;
|
||||
|
||||
var invalidInterns = ListPool<GraphReference>.New();
|
||||
|
||||
foreach (var intern in interns)
|
||||
{
|
||||
if (!intern.isValid)
|
||||
{
|
||||
invalidInterns.Add(intern);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var intern in invalidInterns)
|
||||
{
|
||||
interns.Remove(intern);
|
||||
}
|
||||
|
||||
if (interns.Count == 0)
|
||||
{
|
||||
invalidHashes.Add(hash);
|
||||
}
|
||||
|
||||
invalidInterns.Free();
|
||||
}
|
||||
|
||||
foreach (var hash in invalidHashes)
|
||||
{
|
||||
internPool.Remove(hash);
|
||||
}
|
||||
|
||||
invalidHashes.Free();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7f68b088e619c460d8aa786bc78bc3e9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,11 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public enum GraphSource
|
||||
{
|
||||
Embed,
|
||||
[InspectorName("Graph")]
|
||||
Macro
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 73d6e074b5faa4ac1b86898dec2ca1c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,112 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class GraphStack : GraphPointer, IPoolable, IDisposable
|
||||
{
|
||||
#region Lifecycle
|
||||
|
||||
private GraphStack() { }
|
||||
|
||||
private void InitializeNoAlloc(IGraphRoot root, List<IGraphParentElement> parentElements, bool ensureValid)
|
||||
{
|
||||
Initialize(root);
|
||||
|
||||
Ensure.That(nameof(parentElements)).IsNotNull(parentElements);
|
||||
|
||||
foreach (var parentElement in parentElements)
|
||||
{
|
||||
if (!TryEnterParentElement(parentElement, out var error))
|
||||
{
|
||||
if (ensureValid)
|
||||
{
|
||||
throw new GraphPointerException(error, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static GraphStack New(IGraphRoot root, List<IGraphParentElement> parentElements)
|
||||
{
|
||||
var stack = GenericPool<GraphStack>.New(() => new GraphStack());
|
||||
stack.InitializeNoAlloc(root, parentElements, true);
|
||||
return stack;
|
||||
}
|
||||
|
||||
internal static GraphStack New(GraphPointer model)
|
||||
{
|
||||
var stack = GenericPool<GraphStack>.New(() => new GraphStack());
|
||||
stack.CopyFrom(model);
|
||||
return stack;
|
||||
}
|
||||
|
||||
public GraphStack Clone()
|
||||
{
|
||||
return New(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GenericPool<GraphStack>.Free(this);
|
||||
}
|
||||
|
||||
void IPoolable.New()
|
||||
{
|
||||
}
|
||||
|
||||
void IPoolable.Free()
|
||||
{
|
||||
root = null;
|
||||
parentStack.Clear();
|
||||
parentElementStack.Clear();
|
||||
graphStack.Clear();
|
||||
dataStack.Clear();
|
||||
debugDataStack.Clear();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Conversion
|
||||
|
||||
public override GraphReference AsReference()
|
||||
{
|
||||
return ToReference();
|
||||
}
|
||||
|
||||
public GraphReference ToReference()
|
||||
{
|
||||
return GraphReference.Intern(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Traversal
|
||||
|
||||
public new void EnterParentElement(IGraphParentElement parentElement)
|
||||
{
|
||||
base.EnterParentElement(parentElement);
|
||||
}
|
||||
|
||||
public bool TryEnterParentElement(IGraphParentElement parentElement)
|
||||
{
|
||||
return TryEnterParentElement(parentElement, out var error);
|
||||
}
|
||||
|
||||
public bool TryEnterParentElementUnsafe(IGraphParentElement parentElement)
|
||||
{
|
||||
return TryEnterParentElement(parentElement, out var error, null, true);
|
||||
}
|
||||
|
||||
public new void ExitParentElement()
|
||||
{
|
||||
base.ExitParentElement();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 33947f447eea3434e9c61e7975f2c9b5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,93 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public static class GraphsExceptionUtility
|
||||
{
|
||||
// Note: Checking hasDebugData here instead of enableDebug,
|
||||
// because we always want exceptions to register, even if
|
||||
// background debug is disabled.
|
||||
|
||||
private const string handledKey = "Bolt.Core.Handled";
|
||||
|
||||
public static Exception GetException(this IGraphElementWithDebugData element, GraphPointer pointer)
|
||||
{
|
||||
if (!pointer.hasDebugData)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var debugData = pointer.GetElementDebugData<IGraphElementDebugData>(element);
|
||||
|
||||
return debugData.runtimeException;
|
||||
}
|
||||
|
||||
public static void SetException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex)
|
||||
{
|
||||
if (!pointer.hasDebugData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var debugData = pointer.GetElementDebugData<IGraphElementDebugData>(element);
|
||||
|
||||
debugData.runtimeException = ex;
|
||||
}
|
||||
|
||||
public static void HandleException(this IGraphElementWithDebugData element, GraphPointer pointer, Exception ex)
|
||||
{
|
||||
Ensure.That(nameof(ex)).IsNotNull(ex);
|
||||
|
||||
if (pointer == null)
|
||||
{
|
||||
Debug.LogError("Caught exception with null graph pointer (flow was likely disposed):\n" + ex);
|
||||
return;
|
||||
}
|
||||
|
||||
var reference = pointer.AsReference();
|
||||
|
||||
if (!ex.HandledIn(reference))
|
||||
{
|
||||
element.SetException(pointer, ex);
|
||||
}
|
||||
|
||||
while (reference.isChild)
|
||||
{
|
||||
var parentElement = reference.parentElement;
|
||||
reference = reference.ParentReference(true);
|
||||
|
||||
if (parentElement is IGraphElementWithDebugData debuggableParentElement)
|
||||
{
|
||||
if (!ex.HandledIn(reference))
|
||||
{
|
||||
debuggableParentElement.SetException(reference, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool HandledIn(this Exception ex, GraphReference reference)
|
||||
{
|
||||
Ensure.That(nameof(ex)).IsNotNull(ex);
|
||||
|
||||
if (!ex.Data.Contains(handledKey))
|
||||
{
|
||||
ex.Data.Add(handledKey, new HashSet<GraphReference>());
|
||||
}
|
||||
|
||||
var handled = (HashSet<GraphReference>)ex.Data[handledKey];
|
||||
|
||||
if (handled.Contains(reference))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
handled.Add(reference);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ba39e771494a542fb9f11fafe2479f54
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraph : IDisposable, IPrewarmable, IAotStubbable, ISerializationDepender
|
||||
{
|
||||
Vector2 pan { get; set; }
|
||||
|
||||
float zoom { get; set; }
|
||||
|
||||
MergedGraphElementCollection elements { get; }
|
||||
|
||||
string title { get; }
|
||||
|
||||
string summary { get; }
|
||||
|
||||
IGraphData CreateData();
|
||||
|
||||
IGraphDebugData CreateDebugData();
|
||||
|
||||
void Instantiate(GraphReference instance);
|
||||
|
||||
void Uninstantiate(GraphReference instance);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e3ac3da4f4ae1479a860034ee83b9463
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphData
|
||||
{
|
||||
bool TryGetElementData(IGraphElementWithData element, out IGraphElementData data);
|
||||
|
||||
bool TryGetChildGraphData(IGraphParentElement element, out IGraphData data);
|
||||
|
||||
IGraphElementData CreateElementData(IGraphElementWithData element);
|
||||
|
||||
void FreeElementData(IGraphElementWithData element);
|
||||
|
||||
IGraphData CreateChildGraphData(IGraphParentElement element);
|
||||
|
||||
void FreeChildGraphData(IGraphParentElement element);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8e1b7860a93164eeb84e96c8d6544df5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphDebugData
|
||||
{
|
||||
IGraphElementDebugData GetOrCreateElementData(IGraphElementWithDebugData element);
|
||||
|
||||
IGraphDebugData GetOrCreateChildGraphData(IGraphParentElement element);
|
||||
|
||||
IEnumerable<IGraphElementDebugData> elementsData { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: dee62c9ee6b8b4c4fb371dd99b538497
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElement : IGraphItem, INotifiedCollectionItem, IDisposable, IPrewarmable, IAotStubbable, IIdentifiable
|
||||
{
|
||||
new IGraph graph { get; set; }
|
||||
|
||||
bool HandleDependencies();
|
||||
|
||||
int dependencyOrder { get; }
|
||||
|
||||
new Guid guid { get; set; }
|
||||
|
||||
void Instantiate(GraphReference instance);
|
||||
|
||||
void Uninstantiate(GraphReference instance);
|
||||
|
||||
IEnumerable<ISerializationDependency> deserializationDependencies { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cfe804b1243f445bdb36ab6c3360de35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementCollection<T> : IKeyedCollection<Guid, T>, INotifyCollectionChanged<T> where T : IGraphElement
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37e7718ffdc7c4fdcbd2776305671c47
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,4 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementData { }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aac53a58d9e78493d916ada4ed9e309e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementDebugData
|
||||
{
|
||||
// Being lazy with the interfaces here to simplify things
|
||||
Exception runtimeException { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4dff94c93e9204acda46c024902f8949
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementWithData : IGraphElement
|
||||
{
|
||||
IGraphElementData CreateData();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e8107ffb6540d4965a7e91dc321d9c3c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphElementWithDebugData : IGraphElement
|
||||
{
|
||||
IGraphElementDebugData CreateDebugData();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 918e58f37ca08498c8d16db046eaf343
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphItem
|
||||
{
|
||||
IGraph graph { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4da0cf276f0824701b16235437b01c34
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphNest : IAotStubbable
|
||||
{
|
||||
IGraphNester nester { get; set; }
|
||||
|
||||
GraphSource source { get; set; }
|
||||
IGraph embed { get; set; }
|
||||
IMacro macro { get; set; }
|
||||
IGraph graph { get; }
|
||||
|
||||
Type graphType { get; }
|
||||
Type macroType { get; }
|
||||
|
||||
bool hasBackgroundEmbed { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 49fa2e46fd0be40b7939430e5ff3bf98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,10 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphNester : IGraphParent
|
||||
{
|
||||
IGraphNest nest { get; }
|
||||
|
||||
void InstantiateNest();
|
||||
void UninstantiateNest();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9cb6febeac1a471bac43cd4c509a764
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,4 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphNesterElement : IGraphParentElement, IGraphNester { }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 870079fb53f614f1e97e352a303047d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using UnityObject = UnityEngine.Object;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphParent
|
||||
{
|
||||
IGraph childGraph { get; }
|
||||
|
||||
bool isSerializationRoot { get; }
|
||||
|
||||
UnityObject serializedObject { get; }
|
||||
|
||||
IGraph DefaultGraph();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: ad05adb7ed2c342ac84cf9a4aafce6fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,4 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphParentElement : IGraphElement, IGraphParent { }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0c840ddd1fdb040759e04bfab84c6cab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,7 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IGraphRoot : IGraphParent
|
||||
{
|
||||
GraphPointer GetReference();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 55b94feb6833e4cb4a7f7ed332598d2d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class MergedGraphElementCollection : MergedKeyedCollection<Guid, IGraphElement>, INotifyCollectionChanged<IGraphElement>
|
||||
{
|
||||
public event Action<IGraphElement> ItemAdded;
|
||||
|
||||
public event Action<IGraphElement> ItemRemoved;
|
||||
|
||||
public event Action CollectionChanged;
|
||||
|
||||
public override void Include<TSubItem>(IKeyedCollection<Guid, TSubItem> collection)
|
||||
{
|
||||
base.Include(collection);
|
||||
|
||||
var graphElementCollection = collection as IGraphElementCollection<TSubItem>;
|
||||
|
||||
if (graphElementCollection != null)
|
||||
{
|
||||
graphElementCollection.ItemAdded += (element) => ItemAdded?.Invoke(element);
|
||||
graphElementCollection.ItemRemoved += (element) => ItemRemoved?.Invoke(element);
|
||||
graphElementCollection.CollectionChanged += () => CollectionChanged?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 12ce731cce6f947049d4c6d825a29c3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue