using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Unity.VisualScripting { public abstract class GraphElement : 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(value); graph = (TGraph)value; } } [DoNotSerialize] IGraph IGraphItem.graph => graph; #endregion #region Poutine public virtual IEnumerable deserializationDependencies => Enumerable.Empty(); public virtual IEnumerable GetAotStubs(HashSet visited) { return Enumerable.Empty(); } public virtual void Prewarm() { } protected void CopyFrom(GraphElement 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 public virtual AnalyticsIdentifier GetAnalyticsIdentifier() { throw new NotImplementedException(); } } }