// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Map.h" #include "Interfaces/MetasoundFrontendInterfaceRegistry.h" #include "MetasoundFrontendDocumentCacheInterface.h" #include "MetasoundDocumentInterface.h" #include "MetasoundFrontendDocument.h" #include "MetasoundFrontendDocumentModifyDelegates.h" #include "MetasoundFrontendRegistries.h" #include "MetasoundFrontendTransform.h" #include "MetasoundVertex.h" #include "Templates/Function.h" #include "UObject/ScriptInterface.h" #include "MetasoundFrontendDocumentBuilder.generated.h" // Forward Declarations class FMetasoundAssetBase; namespace Metasound::Frontend { // Forward Declarations class INodeTemplate; using FFinalizeNodeFunctionRef = TFunctionRef; enum class EInvalidEdgeReason : uint8 { None = 0, MismatchedAccessType, MismatchedDataType, MissingInput, MissingOutput, COUNT }; METASOUNDFRONTEND_API FString LexToString(const EInvalidEdgeReason& InReason); struct METASOUNDFRONTEND_API FNamedEdge { const FGuid OutputNodeID; const FName OutputName; const FGuid InputNodeID; const FName InputName; friend bool operator==(const FNamedEdge& InLHS, const FNamedEdge& InRHS) { return InLHS.OutputNodeID == InRHS.OutputNodeID && InLHS.OutputName == InRHS.OutputName && InLHS.InputNodeID == InRHS.InputNodeID && InLHS.InputName == InRHS.InputName; } friend bool operator!=(const FNamedEdge& InLHS, const FNamedEdge& InRHS) { return !(InLHS == InRHS); } friend FORCEINLINE uint32 GetTypeHash(const FNamedEdge& InBinding) { const int32 NameHash = HashCombineFast(GetTypeHash(InBinding.OutputName), GetTypeHash(InBinding.InputName)); const int32 GuidHash = HashCombineFast(GetTypeHash(InBinding.OutputNodeID), GetTypeHash(InBinding.InputNodeID)); return HashCombineFast(NameHash, GuidHash); } }; struct METASOUNDFRONTEND_API FModifyInterfaceOptions { FModifyInterfaceOptions(const TArray& InInterfacesToRemove, const TArray& InInterfacesToAdd); FModifyInterfaceOptions(TArray&& InInterfacesToRemove, TArray&& InInterfacesToAdd); FModifyInterfaceOptions(const TArray& InInterfaceVersionsToRemove, const TArray& InInterfaceVersionsToAdd); TArray InterfacesToRemove; TArray InterfacesToAdd; // Function used to determine if an old of a removed interface // and new member of an added interface are considered equal and // to be swapped, retaining preexisting connections (and locations // if in editor and 'SetDefaultNodeLocations' option is set) TFunction NamePairingFunction; #if WITH_EDITORONLY_DATA bool bSetDefaultNodeLocations = true; #endif // WITH_EDITORONLY_DATA }; } // namespace Metasound::Frontend // Builder Document UObject, which is only used for registration purposes when attempting // async registration whereby the original document is serialized and must not be mutated. UCLASS() class METASOUNDFRONTEND_API UMetaSoundBuilderDocument : public UObject, public IMetaSoundDocumentInterface { GENERATED_BODY() public: UE_DEPRECATED(5.5, "Use overload supplying MetaSound to copy (builder documents no longer supported for cases outside of cloned document registration.") static UMetaSoundBuilderDocument& Create(const UClass& InBuilderClass); // Create and return a valid builder document which copies the provided interface's document & class static UMetaSoundBuilderDocument& Create(const IMetaSoundDocumentInterface& InDocToCopy); virtual bool ConformObjectToDocument() override; // Returns the document virtual const FMetasoundFrontendDocument& GetConstDocument() const override; // Returns temp path of builder document virtual FTopLevelAssetPath GetAssetPathChecked() const override; // Returns the base class registered with the MetaSound UObject registry. virtual const UClass& GetBaseMetaSoundUClass() const final override; // Returns the builder class used to modify the given document. virtual const UClass& GetBuilderUClass() const final override; // Returns if the document is being actively built (always true as builder documents are always being actively built) virtual bool IsActivelyBuilding() const final override; private: virtual FMetasoundFrontendDocument& GetDocument() override; virtual void OnBeginActiveBuilder() override; virtual void OnFinishActiveBuilder() override; UPROPERTY(Transient) FMetasoundFrontendDocument Document; UPROPERTY(Transient) TObjectPtr MetaSoundUClass = nullptr; UPROPERTY(Transient) TObjectPtr BuilderUClass = nullptr; }; // Builder used to support dynamically generating MetaSound documents at runtime. Builder contains caches that speed up // common search and modification operations on a given document, which may result in slower performance on construction, // but faster manipulation of its managed document. The builder's managed copy of a document is expected to not be modified // by any external system to avoid cache becoming stale. USTRUCT() struct METASOUNDFRONTEND_API FMetaSoundFrontendDocumentBuilder { GENERATED_BODY() public: // Default ctor should typically never be used directly as builder interface (and optionally delegates) should be specified on construction (Default exists only to make UObject reflection happy). FMetaSoundFrontendDocumentBuilder(TScriptInterface InDocumentInterface = { }, TSharedPtr InDocumentDelegates = { }, bool bPrimeCache = false); virtual ~FMetaSoundFrontendDocumentBuilder(); // Call when the builder will no longer modify the IMetaSoundDocumentInterface void FinishBuilding(); const FMetasoundFrontendClass* AddDependency(const FMetasoundFrontendClass& InClass); void AddEdge(FMetasoundFrontendEdge&& InNewEdge, const FGuid* InPageID = nullptr); bool AddNamedEdges(const TSet& ConnectionsToMake, TArray* OutEdgesCreated = nullptr, bool bReplaceExistingConnections = true, const FGuid* InPageID = nullptr); bool AddEdgesByNodeClassInterfaceBindings(const FGuid& InFromNodeID, const FGuid& InToNodeID, bool bReplaceExistingConnections = true, const FGuid* InPageID = nullptr); bool AddEdgesFromMatchingInterfaceNodeOutputsToGraphOutputs(const FGuid& InNodeID, TArray& OutEdgesCreated, bool bReplaceExistingConnections = true, const FGuid* InPageID = nullptr); bool AddEdgesFromMatchingInterfaceNodeInputsToGraphInputs(const FGuid& InNodeID, TArray& OutEdgesCreated, bool bReplaceExistingConnections = true, const FGuid* InPageID = nullptr); const FMetasoundFrontendNode* AddGraphInput(const FMetasoundFrontendClassInput& InClassInput, const FGuid* InPageID = nullptr); const FMetasoundFrontendNode* AddGraphNode(const FMetasoundFrontendGraphClass& InClass, FGuid InNodeID = FGuid::NewGuid(), const FGuid* InPageID = nullptr); const FMetasoundFrontendNode* AddGraphOutput(const FMetasoundFrontendClassOutput& InClassOutput, const FGuid* InPageID = nullptr); bool AddInterface(FName InterfaceName); const FMetasoundFrontendNode* AddNodeByClassName(const FMetasoundFrontendClassName& InClassName, int32 InMajorVersion = 1, FGuid InNodeID = FGuid::NewGuid(), const FGuid* InPageID = nullptr); const FMetasoundFrontendNode* AddNodeByTemplate(const Metasound::Frontend::INodeTemplate& InTemplate, Metasound::Frontend::FNodeTemplateGenerateInterfaceParams Params, FGuid InNodeID = FGuid::NewGuid(), const FGuid* InPageID = nullptr); // Adds a graph page to the given builder's document const FMetasoundFrontendGraph& AddGraphPage(const FGuid& InPageID, bool bDuplicateLastGraph = true, bool bSetAsBuildGraph = true); // Returns whether or not the given edge can be added, which requires that its input // is not already connected and the edge is valid (see function 'IsValidEdge'). bool CanAddEdge(const FMetasoundFrontendEdge& InEdge, const FGuid* InPageID = nullptr) const; // Clears document completely of all graph page data (nodes, edges, & member metadata), dependencies, // interfaces, member metadata, preset state, etc. Leaves ClassMetadata intact. Reloads the builder state, // so external delegates must be relinked if desired. void ClearDocument(TSharedRef ModifyDelegates = { }); UE_DEPRECATED(5.5, "Use ClearDocument instead") void ClearGraph() { } #if WITH_EDITORONLY_DATA bool ClearMemberMetadata(const FGuid& InMemberID); #endif // WITH_EDITORONLY_DATA bool ContainsDependencyOfType(EMetasoundFrontendClassType ClassType) const; bool ContainsEdge(const FMetasoundFrontendEdge& InEdge, const FGuid* InPageID = nullptr) const; bool ContainsNode(const FGuid& InNodeID, const FGuid* InPageID = nullptr) const; bool ConvertFromPreset(); bool ConvertToPreset(const FMetasoundFrontendDocument& InReferencedDocument, TSharedRef ModifyDelegates = { }); const FMetasoundFrontendNode* DuplicateGraphInput(const FMetasoundFrontendClassInput& InClassInput, FMetasoundFrontendLiteral DefaultValue, const FName InName, const FGuid* InPageID = nullptr); const FMetasoundFrontendNode* DuplicateGraphOutput(const FMetasoundFrontendClassOutput& InClassOutput, const FName InName, const FGuid* InPageID = nullptr); #if WITH_EDITORONLY_DATA const FMetaSoundFrontendGraphComment* FindGraphComment(const FGuid& InCommentID) const; FMetaSoundFrontendGraphComment* FindGraphComment(const FGuid& InCommentID); FMetaSoundFrontendGraphComment& FindOrAddGraphComment(const FGuid& InCommentID); UMetaSoundFrontendMemberMetadata* FindMemberMetadata(const FGuid& InMemberID); #endif // WITH_EDITORONLY_DATA static bool FindDeclaredInterfaces(const FMetasoundFrontendDocument& InDocument, TArray& OutInterfaces); bool FindDeclaredInterfaces(TArray& OutInterfaces) const; const FMetasoundFrontendClass* FindDependency(const FGuid& InClassID) const; const FMetasoundFrontendClass* FindDependency(const FMetasoundFrontendClassMetadata& InMetadata) const; TArray FindEdges(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendClassInput* FindGraphInput(FName InputName) const; const FMetasoundFrontendNode* FindGraphInputNode(FName InputName, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendClassOutput* FindGraphOutput(FName OutputName) const; const FMetasoundFrontendNode* FindGraphOutputNode(FName OutputName, const FGuid* InPageID = nullptr) const; bool FindInterfaceInputNodes(FName InterfaceName, TArray& OutInputs, const FGuid* InPageID = nullptr) const; bool FindInterfaceOutputNodes(FName InterfaceName, TArray& OutOutputs, const FGuid* InPageID = nullptr) const; FMetasoundFrontendGraph& FindBuildGraphChecked(); const FMetasoundFrontendGraph& FindConstBuildGraphChecked() const; const FMetasoundFrontendNode* FindNode(const FGuid& InNodeID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendVertex* FindNodeInput(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendVertex* FindNodeInput(const FGuid& InNodeID, FName InVertexName, const FGuid* InPageID = nullptr) const; TArray FindNodeInputs(const FGuid& InNodeID, FName TypeName = FName(), const FGuid* InPageID = nullptr) const; TArray FindNodeInputsConnectedToNodeOutput(const FGuid& InOutputNodeID, const FGuid& InOutputVertexID, TArray* ConnectedInputNodes = nullptr, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendVertex* FindNodeOutput(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendVertex* FindNodeOutput(const FGuid& InNodeID, FName InVertexName, const FGuid* InPageID = nullptr) const; TArray FindNodeOutputs(const FGuid& InNodeID, FName TypeName = FName(), const FGuid* InPageID = nullptr) const; const FMetasoundFrontendVertex* FindNodeOutputConnectedToNodeInput(const FGuid& InInputNodeID, const FGuid& InInputVertexID, const FMetasoundFrontendNode** ConnectedOutputNode = nullptr, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendDocument& GetConstDocumentChecked() const; const IMetaSoundDocumentInterface& GetConstDocumentInterfaceChecked() const; const FString GetDebugName() const; UE_DEPRECATED(5.5, "Use GetConstDocumentChecked() instead") const FMetasoundFrontendDocument& GetDocument() const; // The graph ID used when requests are made to mutate specific paged graph topology (ex. adding or removing nodes or edges) const FGuid& GetBuildPageID() const; template TObjectType& CastDocumentObjectChecked() const { UObject* Owner = DocumentInterface.GetObject(); return *CastChecked(Owner); } // Generates and returns new class name for the given builder's document. Should ONLY be called on new assets // using transient frontend builder, as using a persistent builder registered with the DocumentBuilderRegistry // may result in corrupt records keyed off of undefined class name being potentially generated over. In addition, // this can potentially leave existing node references in an abandoned state to this class causing MetaSound generator // build errors. FMetasoundFrontendClassName GenerateNewClassName(); Metasound::Frontend::FDocumentModifyDelegates& GetDocumentDelegates(); UE_DEPRECATED(5.5, "Use GetConstDocumentInterfaceChecked instead") const IMetaSoundDocumentInterface& GetDocumentInterface() const; FMetasoundAssetBase& GetMetasoundAsset() const; int32 GetTransactionCount() const; TArray GetGraphInputTemplateNodes(FName InInputName, const FGuid* InPageID = nullptr); EMetasoundFrontendVertexAccessType GetNodeInputAccessType(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendLiteral* GetNodeInputClassDefault(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; const FMetasoundFrontendLiteral* GetNodeInputDefault(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; EMetasoundFrontendVertexAccessType GetNodeOutputAccessType(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; // Initializes the builder's document, using the (optional) provided document template, (optional) class name, and (optionally) whether or not to reset the existing class version. void InitDocument(const FMetasoundFrontendDocument* InDocumentTemplate = nullptr, const FMetasoundFrontendClassName* InNewClassName = nullptr, bool bResetVersion = true); // Initializes GraphClass Metadata, optionally resetting the version back to 1.0 and/or creating a unique class name if a name is not provided. static void InitGraphClassMetadata(FMetasoundFrontendClassMetadata& InOutMetadata, bool bResetVersion = false, const FMetasoundFrontendClassName* NewClassName = nullptr); void InitGraphClassMetadata(bool bResetVersion, const FMetasoundFrontendClassName* NewClassName); void InitNodeLocations(); UE_DEPRECATED(5.5, "Use invalidate overload that is provided new version of modify delegates") void InvalidateCache() { } bool IsDependencyReferenced(const FGuid& InClassID) const; bool IsNodeInputConnected(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; bool IsNodeOutputConnected(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr) const; bool IsInterfaceDeclared(FName InInterfaceName) const; bool IsInterfaceDeclared(const FMetasoundFrontendVersion& InInterfaceVersion) const; bool IsPreset() const; // Returns whether or not builder is attached to a DocumentInterface and is valid to build or act on a document. bool IsValid() const; // Returns whether or not the given edge is valid (i.e. represents an input and output that equate in data and access types) or malformed. // Note that this does not return whether or not the given edge exists, but rather if it could be legally applied to the given edge vertices. Metasound::Frontend::EInvalidEdgeReason IsValidEdge(const FMetasoundFrontendEdge& InEdge, const FGuid* InPageID = nullptr) const; bool ModifyInterfaces(Metasound::Frontend::FModifyInterfaceOptions&& InOptions); void RemoveAllGraphPages(); UE_DEPRECATED(5.5, "Cache invalidation may require new copy of delegates. In addition, re-priming is discouraged. " "To enforce this, new recommended pattern is to construct a new builder instead") void ReloadCache(); bool RemoveDependency(const FGuid& InClassID); bool RemoveDependency(EMetasoundFrontendClassType ClassType, const FMetasoundFrontendClassName& InClassName, const FMetasoundFrontendVersionNumber& InClassVersionNumber); bool RemoveEdge(const FMetasoundFrontendEdge& EdgeToRemove, const FGuid* InPageID = nullptr); // Removes all edges connected to an input or output vertex associated with the node of the given ID. bool RemoveEdges(const FGuid& InNodeID, const FGuid* InPageID = nullptr); bool RemoveEdgesByNodeClassInterfaceBindings(const FGuid& InOutputNodeID, const FGuid& InInputNodeID, const FGuid* InPageID = nullptr); bool RemoveEdgesFromNodeOutput(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr); bool RemoveEdgeToNodeInput(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr); #if WITH_EDITOR bool RemoveGraphComment(const FGuid& InCommentID, const FGuid* InPageID = nullptr); #endif // WITH_EDITOR bool RemoveGraphInput(FName InInputName); bool RemoveGraphOutput(FName InOutputName); bool RemoveGraphPage(const FGuid& InPageID); bool RemoveInterface(FName InName); bool RemoveNamedEdges(const TSet& InNamedEdgesToRemove, TArray* OutRemovedEdges = nullptr, const FGuid* InPageID = nullptr); bool RemoveNode(const FGuid& InNodeID, const FGuid* InPageID = nullptr); #if WITH_EDITOR int32 RemoveNodeLocation(const FGuid& InNodeID, const FGuid* InLocationGuid = nullptr, const FGuid* InPageID = nullptr); #endif // WITH_EDITOR void Reload(TSharedPtr Delegates = {}, bool bPrimeCache = false); bool RemoveNodeInputDefault(const FGuid& InNodeID, const FGuid& InVertexID, const FGuid* InPageID = nullptr); bool RemoveUnusedDependencies(); UE_DEPRECATED(5.5, "Use GenerateNewClassName instead") bool RenameRootGraphClass(const FMetasoundFrontendClassName& InName); #if WITH_EDITOR void SetAuthor(const FString& InAuthor); #endif // WITH_EDITOR // Sets the builder's targeted paged graph ID to the given ID if it exists. // Returns true if the builder is already targeting the given ID or if it successfully // found a page implementation with the given ID and was able to switch to it, false if not. // Swapping the targeted build graph ID clears the local cache, so swapping frequently can // induce cash thrashing. bool SetBuildPageID(const FGuid& InBuildPageID); // Sets the given graph input's access type. If connected to other nodes and access type is not compatible, // associated edges/connections are removed. Returns true if either DataType was successfully set to new // value or if AccessType is already the given AccessType. bool SetGraphInputAccessType(FName InputName, EMetasoundFrontendVertexAccessType AccessType); // Sets the given graph input's data type. If connected to other nodes, associated edges/connections // are removed. Returns true if either DataType was successfully set to new value or if DataType is // already the given DataType. bool SetGraphInputDataType(FName InputName, FName DataType); bool SetGraphInputDefault(FName InputName, const FMetasoundFrontendLiteral& InDefaultLiteral); // Sets the given graph output's access type. If connected to other nodes and access type is not compatible, // associated edges/connections are removed. Returns true if either DataType was successfully set to new // value or if AccessType is already the given AccessType. bool SetGraphOutputAccessType(FName OutputName, EMetasoundFrontendVertexAccessType AccessType); // Sets the given graph output's data type. If connected to other nodes, associated edges/connections // are removed. Returns true if either DataType was successfully set to new value or if DataType is // already the given DataType. bool SetGraphOutputDataType(FName OutputName, FName DataType); #if WITH_EDITOR void SetDisplayName(const FText& InDisplayName); void SetMemberMetadata(UMetaSoundFrontendMemberMetadata& NewMetadata); // Sets the editor-only comment to the provided value. // Returns true if the node was found and the comment was updated, false if not. bool SetNodeComment(const FGuid& InNodeID, FString&& InNewComment, const FGuid* InPageID = nullptr); // Sets the editor-only comment visibility. // Returns true if the node was found and the visibility was set, false if not. bool SetNodeCommentVisible(const FGuid& InNodeID, bool bIsVisible, const FGuid* InPageID = nullptr); // Sets the editor-only node location of a node with the given ID to the provided location. // Returns true if the node was found and the location was updated, false if not. bool SetNodeLocation(const FGuid& InNodeID, const FVector2D& InLocation, const FGuid* InLocationGuid = nullptr, const FGuid* InPageID = nullptr); // Sets the editor-only Unconnected Pins Hidden for a node with the given ID. bool SetNodeUnconnectedPinsHidden(const FGuid& InNodeID, const bool bUnconnectedPinsHidden); // Gets the editor-only style of a node with the given ID. const FMetasoundFrontendNodeStyle* GetNodeStyle(const FGuid& InNodeID); #endif // WITH_EDITOR bool SetNodeInputDefault(const FGuid& InNodeID, const FGuid& InVertexID, const FMetasoundFrontendLiteral& InLiteral, const FGuid* InPageID = nullptr); // Sets the document's version number. Should only be called by document versioning. void SetVersionNumber(const FMetasoundFrontendVersionNumber& InDocumentVersionNumber); bool SwapGraphInput(const FMetasoundFrontendClassVertex& InExistingInputVertex, const FMetasoundFrontendClassVertex& NewInputVertex); bool SwapGraphOutput(const FMetasoundFrontendClassVertex& InExistingOutputVertex, const FMetasoundFrontendClassVertex& NewOutputVertex); bool UpdateDependencyClassNames(const TMap& OldToNewReferencedClassNames); #if WITH_EDITORONLY_DATA // Transforms template nodes within the given builder's document, which can include swapping associated edges and/or // replacing nodes with other, registry-defined concrete node class instances. Returns true if any template nodes were processed. bool TransformTemplateNodes(); // Versions legacy document members that contained interface information bool VersionInterfaces(); #endif // WITH_EDITORONLY_DATA private: using FFinalizeNodeFunctionRef = TFunctionRef; FMetasoundFrontendNode* AddNodeInternal(const FMetasoundFrontendClassMetadata& InClassMetadata, Metasound::Frontend::FFinalizeNodeFunctionRef FinalizeNode, const FGuid& InPageID, FGuid InNodeID = FGuid::NewGuid(), int32* NewNodeIndex = nullptr); void BeginBuilding(TSharedPtr Delegates = {}, bool bPrimeCache = false); // Conforms GraphOutput node's ClassID, Access & Data Type with the GraphOutput. // creating and removing dependencies as necessary within the document dependency array. Does *NOT* // modify edge data (i.e. if the DataType is changed on the given node and it has corresponding // edges, edges may then be invalid due to access type/DataType incompatibility). bool ConformGraphInputNodeToClass(const FMetasoundFrontendClassInput& GraphInput); // Conforms GraphOutput node's ClassID, Access & Data Type with the GraphOutput. // Creates and removes dependencies as necessary within the document dependency array. Does *NOT* // modify edge data (i.e. if the DataType is changed on the given node and it has corresponding // edges, edges may then be invalid due to access type/DataType incompatibility). bool ConformGraphOutputNodeToClass(const FMetasoundFrontendClassOutput& GraphOutput); bool FindNodeClassInterfaces(const FGuid& InNodeID, TSet& OutInterfaces, const FGuid& InPageID) const; FMetasoundFrontendNode* FindNodeInternal(const FGuid& InNodeID, const FGuid* InPageID = nullptr); void IterateNodesConnectedWithVertex(const FMetasoundFrontendVertexHandle& Vertex, TFunctionRef NodeIndexIterFunc, const FGuid& InPageID); const FTopLevelAssetPath GetBuilderClassPath() const; FMetasoundFrontendDocument& GetDocumentChecked() const; IMetaSoundDocumentInterface& GetDocumentInterfaceChecked() const; bool SetGraphInputInheritsDefault(FName InName, bool bInputInheritsDefault); bool SpliceVariableNodeFromStack(const FGuid& InNodeID, const FGuid& InPageID); bool UnlinkVariableNode(const FGuid& InNodeID, const FGuid& InPageID); UPROPERTY(Transient) TScriptInterface DocumentInterface; // Default page ID to apply build transactions to if no page ID is optionally set directly. UPROPERTY(Transient) FGuid BuildPageID; TSharedPtr DocumentCache; TSharedPtr DocumentDelegates; };