// Copyright Epic Games, Inc. All Rights Reserved. #include "RigVMModel/RigVMClient.h" #include "Misc/TransactionObjectEvent.h" #include "Exporters/Exporter.h" #include "UObject/ObjectSaveContext.h" #include "RigVMModel/RigVMControllerActions.h" #include "RigVMModel/Nodes/RigVMDispatchNode.h" #include "RigVMCore/RigVMObjectArchive.h" #include "EdGraph/RigVMEdGraph.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMClient) #if WITH_EDITOR #include "ScopedTransaction.h" #endif UObject* IRigVMClientHost::ResolveUserDefinedTypeById(const FString& InTypeName) const { return nullptr; } void FRigVMClient::SetDefaultSchemaClass(TSubclassOf InSchemaClass) { check(InSchemaClass); if(InSchemaClass == DefaultSchemaClass) { return; } DefaultSchemaClass = InSchemaClass; for(TPair>& ObjectControllerPair : Controllers) { ObjectControllerPair.Value->SetSchemaClass(InSchemaClass); } } void FRigVMClient::SetControllerClass(TSubclassOf InControllerClass) { check(InControllerClass); if (InControllerClass == ControllerClass) { return; } for (URigVMGraph* Model : GetModels()) { RemoveController(Model); } ControllerClass = InControllerClass; } void FRigVMClient::SetOuterClientHost(UObject* InOuterClientHost, const FName& InOuterClientHostPropertyName) { OuterClientHost = InOuterClientHost; OuterClientPropertyName = InOuterClientHostPropertyName; check(OuterClientHost->Implements()); check(GetOuterClientProperty() != nullptr); // create the null graph / default controller. // we need this to react to notifs without a valid graph // such as interaction brackets static const URigVMGraph* NullGraph = nullptr; if(!Controllers.Contains(NullGraph)) { CreateController(NullGraph); } } void FRigVMClient::SetFromDeprecatedData(URigVMGraph* InDefaultGraph, URigVMFunctionLibrary* InFunctionLibrary) { if(GetDefaultModel() != InDefaultGraph || GetFunctionLibrary() != InFunctionLibrary) { if(GetDefaultModel() == InDefaultGraph) { Models.Reset(); } if(InFunctionLibrary == nullptr) { Swap(FunctionLibrary, InFunctionLibrary); } Reset(); if(InDefaultGraph) { AddModel(InDefaultGraph, false); } if(InFunctionLibrary) { AddModel(InFunctionLibrary, false); } } } void FRigVMClient::SetExternalModelHost(IRigVMClientExternalModelHost* InExternalModelHost) { // We dont allow this to be set dynamically, assume it is set once at creation time check(ExternalModelHost == nullptr); ExternalModelHost = InExternalModelHost; } void FRigVMClient::Reset() { for(URigVMGraph* Model : GetModels()) { DestroyObject(Model); } for(auto Pair : Controllers) { DestroyObject(Pair.Value); } DestroyObject(FunctionLibrary); Models.Reset(); Controllers.Reset(); FunctionLibrary = nullptr; ResetActionStack(); } URigVMSchema* FRigVMClient::GetDefaultSchema() const { check(DefaultSchemaClass); return DefaultSchemaClass->GetDefaultObject(); } URigVMGraph* FRigVMClient::GetDefaultModel() const { if(GetModels().IsEmpty()) { return nullptr; } return GetModel(0); } URigVMGraph* FRigVMClient::GetModel(int32 InIndex) const { const TArray>& LocalModels = GetModels(); if(LocalModels.IsValidIndex(InIndex)) { return LocalModels[InIndex]; } return nullptr; } URigVMGraph* FRigVMClient::GetModel(const UEdGraph* InEdGraph) const { if (InEdGraph == nullptr) { return GetDefaultModel(); } //#if WITH_EDITORONLY_DATA // if (InEdGraph == FunctionLibraryEdGraph) // { // return RigVMClient.GetFunctionLibrary(); // } //#endif const URigVMEdGraph* RigGraph = Cast< URigVMEdGraph>(InEdGraph); check(RigGraph); return GetModel(RigGraph->ModelNodePath); } URigVMGraph* FRigVMClient::GetModel(const FString& InNodePathOrName) const { if(InNodePathOrName.IsEmpty()) { return GetDefaultModel(); } TArray ModelsAndFunctionLibrary = GetAllModels(true, false); for(URigVMGraph* Model : ModelsAndFunctionLibrary) { if(Model->GetNodePath() == InNodePathOrName || Model->GetName() == InNodePathOrName) { return Model; } static constexpr TCHAR NodePathPrefixFormat[] = TEXT("%s|"); const FString NodePathPrefix = FString::Printf(NodePathPrefixFormat, *Model->GetNodePath()); if(InNodePathOrName.StartsWith(NodePathPrefix)) { const FString RemainingNodePath = InNodePathOrName.Mid(NodePathPrefix.Len()); if(const URigVMCollapseNode* CollapseNode = Cast(Model->FindNode(RemainingNodePath))) { return CollapseNode->GetContainedGraph(); } } } return nullptr; } URigVMGraph* FRigVMClient::GetModel(const UObject* InEditorSideObject) const { check(InEditorSideObject); if (InEditorSideObject->Implements()) { const IRigVMEditorSideObject* UserInterfaceGraph = Cast(InEditorSideObject); return GetModel(UserInterfaceGraph->GetRigVMNodePath()); } return nullptr; } void FRigVMClient::RefreshAllModels(ERigVMLoadType InLoadType, bool bEnablePostLoadHashing, bool& bIsCompiling) { const bool bIsPostLoad = InLoadType == ERigVMLoadType::PostLoad; // avoid any compute if the current structure hashes match with the serialized ones if (bEnablePostLoadHashing && GetStructureHash() == GetSerializedStructureHash()) { if (bIsPostLoad) { TArray ModelGraphs = GetAllModels(true, true); Algo::Reverse(ModelGraphs); for (URigVMGraph* ModelGraph : ModelGraphs) { URigVMController* Controller = GetOrCreateController(ModelGraph); URigVMController::FRestoreLinkedPathSettings Settings; Settings.bFollowCoreRedirectors = true; Settings.bRelayToOrphanPins = true; Controller->ProcessDetachedLinks(Settings); } } return; } TGuardValue IsCompilingGuard(bIsCompiling, true); TGuardValue ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true); TArray AllModelsLeavesFirst = GetAllModelsLeavesFirst(true); TMap> LinkedPaths; if (ensure(IsInGameThread())) { TArray RepopulatePinsNodesData; constexpr int32 REPOPULATE_NODES_NUM_RESERVED = 800; RepopulatePinsNodesData.Reserve(REPOPULATE_NODES_NUM_RESERVED); for (URigVMGraph* Graph : AllModelsLeavesFirst) { URigVMController* Controller = GetOrCreateController(Graph); // temporarily disable default value validation during load time, serialized values should always be accepted TGuardValue PerGraphDisablePinDefaultValueValidation(Controller->bValidatePinDefaults, false); TGuardValue GuardEditGraph(Graph->bEditable, true); FRigVMControllerNotifGuard NotifGuard(Controller, true); LinkedPaths.Add(Graph, Controller->GetLinkedPaths()); const TArray Nodes = Graph->GetNodes(); if (Nodes.Num() > 0) { RepopulatePinsNodesData.Reset(); for (URigVMNode* Node : Nodes) { Controller->GenerateRepopulatePinsNodeData(RepopulatePinsNodesData, Node, true, true); } #if UE_RIGVMCONTROLLER_VERBOSE_REPOPULATE UE_LOG(LogRigVMDeveloper, Display, TEXT("--- Graph: [%s/%s] - NumNodes : [%d]"), *Graph->GetOuter()->GetName(), *Graph->GetName(), RepopulatePinsNodesData.Num()); #endif Controller->OrphanPins(RepopulatePinsNodesData); Controller->FastBreakLinkedPaths(LinkedPaths.FindChecked(Graph)); Controller->RepopulatePins(RepopulatePinsNodesData); } } if (IRigVMClientHost* ClientHost = Cast(GetOuter())) { ClientHost->SetupPinRedirectorsForBackwardsCompatibility(); } } for (URigVMGraph* Graph : AllModelsLeavesFirst) { URigVMController* Controller = GetOrCreateController(Graph); TGuardValue GuardEditGraph(Graph->bEditable, true); FRigVMControllerNotifGuard NotifGuard(Controller, true); { URigVMController::FRestoreLinkedPathSettings Settings; Settings.bFollowCoreRedirectors = true; Settings.bRelayToOrphanPins = true; Controller->RestoreLinkedPaths(LinkedPaths.FindChecked(Graph), Settings); } for (URigVMNode* ModelNode : Graph->GetNodes()) { Controller->RemoveUnusedOrphanedPins(ModelNode); } if (bIsPostLoad) { for (URigVMNode* ModelNode : Graph->GetNodes()) { if (URigVMTemplateNode* TemplateNode = Cast(ModelNode)) { TemplateNode->InvalidateCache(); TemplateNode->PostLoad(); } } } #if WITH_EDITOR if (bIsPostLoad) { for (URigVMNode* ModelNode : Graph->GetNodes()) { if (URigVMUnitNode* UnitNode = Cast(ModelNode)) { if (!UnitNode->HasWildCardPin()) { UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct(); if (ScriptStruct == nullptr) { Controller->FullyResolveTemplateNode(UnitNode, INDEX_NONE, false); } // Try to find a deprecated template if (UnitNode->GetScriptStruct() == nullptr && !UnitNode->TemplateNotation.IsNone()) { const FRigVMTemplate* Template = FRigVMRegistry::Get().FindTemplate(UnitNode->TemplateNotation, true); FRigVMTemplate::FTypeMap TypeMap = UnitNode->GetTemplatePinTypeMap(); int32 Permutation; if (Template->FullyResolve(TypeMap, Permutation)) { const FRigVMFunction* Function = Template->GetPermutation(Permutation); UnitNode->ResolvedFunctionName = Function->GetName(); } } if (UnitNode->GetScriptStruct() == nullptr) { static constexpr TCHAR UnresolvedUnitNodeMessage[] = TEXT("Node %s could not be resolved."); Controller->ReportErrorf(UnresolvedUnitNodeMessage, *ModelNode->GetNodePath(true)); } } } if (URigVMDispatchNode* DispatchNode = Cast(ModelNode)) { if (DispatchNode->GetFactory() == nullptr) { static constexpr TCHAR UnresolvedDispatchNodeMessage[] = TEXT("Dispatch node %s has no factory.."); Controller->ReportErrorf(UnresolvedDispatchNodeMessage, *ModelNode->GetNodePath(true)); } else if (!DispatchNode->HasWildCardPin()) { if (DispatchNode->GetResolvedFunction() == nullptr) { Controller->FullyResolveTemplateNode(DispatchNode, INDEX_NONE, false); } if (DispatchNode->GetResolvedFunction() == nullptr) { static constexpr TCHAR UnresolvedDispatchNodeMessage[] = TEXT("Node %s could not be resolved."); Controller->ReportErrorf(UnresolvedDispatchNodeMessage, *ModelNode->GetNodePath(true)); } } } } } #endif } } const TArray>& FRigVMClient::GetModels() const { return ExternalModelHost != nullptr ? ExternalModelHost->GetExternalModels() : Models; } TArray FRigVMClient::GetAllModels(bool bIncludeFunctionLibrary, bool bRecursive) const { TArray AllModels = GetModels(); if(bRecursive) { for(URigVMGraph* Model : GetModels()) { AllModels.Append(Model->GetContainedGraphs(true /* recursive */)); } } if(bIncludeFunctionLibrary && FunctionLibrary) { AllModels.Add(FunctionLibrary); if(bRecursive) { AllModels.Append(FunctionLibrary->GetContainedGraphs(true /* recursive */)); } } return AllModels; } TArray FRigVMClient::GetAllModelsLeavesFirst(bool bIncludeFunctionLibrary) const { TArray SortedModels = GetAllModels(bIncludeFunctionLibrary, true); URigVMController::SortGraphElementsByGraphDepth(SortedModels, true); return SortedModels; } URigVMController* FRigVMClient::GetController(int32 InIndex) const { return GetController(GetModel(InIndex)); } URigVMController* FRigVMClient::GetController(const FString& InNodePathOrName) const { return GetController(GetModel(InNodePathOrName)); } URigVMController* FRigVMClient::GetController(const URigVMGraph* InModel) const { if(InModel == nullptr) { InModel = GetDefaultModel(); } if(InModel) { const FSoftObjectPath Key(InModel); if(const TObjectPtr* Controller = Controllers.Find(Key)) { checkf(Controller->Get()->GetGraph() == InModel, TEXT("Controller %s contains unexpected graph."), *Controller->GetPathName()); return Controller->Get(); } } return nullptr; } URigVMController* FRigVMClient::GetController(const UObject* InEditorSideObject) const { check(InEditorSideObject); if (InEditorSideObject->Implements()) { const IRigVMEditorSideObject* UserInterfaceGraph = Cast(InEditorSideObject); return GetController(UserInterfaceGraph->GetRigVMNodePath()); } return nullptr; } URigVMController* FRigVMClient::GetOrCreateController(int32 InIndex) { return GetOrCreateController(GetModel(InIndex)); } URigVMController* FRigVMClient::GetOrCreateController(const FString& InNodePathOrName) { return GetOrCreateController(GetModel(InNodePathOrName)); } URigVMController* FRigVMClient::GetOrCreateController(const URigVMGraph* InModel) { if(InModel == nullptr) { InModel = GetDefaultModel(); } if(InModel) { if(URigVMController* Controller = GetController(InModel)) { // We associate controllers to graphs via soft path, so they can match newly created graphs // If this happens make sure the graph is correctly bound to the controller if (!InModel->ModifiedEvent.IsBound()) { const_cast(InModel)->OnModified().AddUObject(Controller, &URigVMController::HandleModifiedEvent); } return Controller; } return CreateController(InModel); } return nullptr; } URigVMController* FRigVMClient::GetOrCreateController(const UObject* InEditorSideObject) { check(InEditorSideObject); if (InEditorSideObject->Implements()) { const IRigVMEditorSideObject* UserInterfaceGraph = Cast(InEditorSideObject); return GetOrCreateController(UserInterfaceGraph->GetRigVMNodePath()); } return nullptr; } URigVMController* FRigVMClient::GetControllerByName(const FString InGraphName) const { if (InGraphName.IsEmpty()) { if (const URigVMGraph* DefaultModel = GetDefaultModel()) { return GetController(DefaultModel); } } for (const URigVMGraph* Graph : GetAllModels(true, true)) { if (Graph->GetName() == InGraphName || Graph->GetGraphName() == InGraphName) { return GetController(Graph); } } return nullptr; } bool FRigVMClient::RemoveController(const URigVMGraph* InModel) { const FSoftObjectPath Key(InModel); URigVMController* Controller = GetController(InModel); const bool bSuccess = Controllers.Remove(Key) > 0; if(Controller) { Controller->SetActionStack(nullptr); Controller->Rename(nullptr, GetTransientPackage(), REN_DoNotDirty | REN_DontCreateRedirectors | REN_NonTransactional); Controller->RemoveFromRoot(); Controller->MarkAsGarbage(); } return bSuccess; } URigVMGraph* FRigVMClient::AddModel(const FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand) { const FString DesiredName = FString::Printf(TEXT("%s %s"), FRigVMClient::RigVMModelPrefix, *InName); return AddModel(*DesiredName, bSetupUndoRedo); } URigVMGraph* FRigVMClient::AddModel(const FName& InName, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController) { check(DefaultSchemaClass); URigVMGraph* NewModel = CreateModel(InName, DefaultSchemaClass, bSetupUndoRedo, GetOuter(), ObjectInitializer, bCreateController); AddModel(NewModel, bCreateController); return NewModel; } URigVMGraph* FRigVMClient::AddModel(const FName& InName, TSubclassOf InSchemaClass, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController) { URigVMGraph* NewModel = CreateModel(InName, InSchemaClass, bSetupUndoRedo, GetOuter(), ObjectInitializer, bCreateController); AddModel(NewModel, bCreateController); return NewModel; } URigVMGraph* FRigVMClient::CreateModel(const FName& InName, TSubclassOf InSchemaClass, bool bSetupUndoRedo, UObject* InOuter, const FObjectInitializer* ObjectInitializer, bool bCreateController) { check(InSchemaClass); #if WITH_EDITOR TSharedPtr Transaction; if(bSetupUndoRedo) { Transaction = MakeShared(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph")); InOuter->Modify(); } #endif const FName SafeGraphName = GetUniqueName(InName); URigVMGraph* Model = nullptr; if(ObjectInitializer) { Model = ObjectInitializer->CreateDefaultSubobject(InOuter, SafeGraphName); } else { Model = NewObject(InOuter, SafeGraphName); } Model->SetSchemaClass(InSchemaClass); if(bSetupUndoRedo) { InOuter->Modify(); UndoRedoIndex++; FRigVMClientAction Action; Action.Type = ERigVMClientAction::ERigVMClientAction_AddModel; Action.NodePath = Model->GetNodePath(); UndoStack.Push(Action); RedoStack.Reset(); } return Model; } TObjectPtr FRigVMClient::CreateContainedGraphModel(URigVMCollapseNode* CollapseNode, const FName& Name) { TObjectPtr Model = nullptr; check(CollapseNode); if (ExternalModelHost != nullptr) { Model = ExternalModelHost->CreateContainedGraphModel(CollapseNode, Name); } else { Model = NewObject(CollapseNode, Name); // keep schema from collapse node graph, if exists if (CollapseNode->GetGraph() != nullptr && CollapseNode->GetGraph()->GetSchema() != nullptr) { Model->SetSchemaClass(CollapseNode->GetGraph()->GetSchema()->GetClass()); } else { Model->SetSchemaClass(GetDefaultSchemaClass()); } } return Model; } void FRigVMClient::AddModel(URigVMGraph* InModel, bool bCreateController) { check(InModel); if(InModel->IsA()) { check(FunctionLibrary == nullptr); FunctionLibrary = Cast(InModel); } else { if(ExternalModelHost == nullptr) { Models.Add(InModel); } } if(InModel->GetSchemaClass() == nullptr) { InModel->SetSchemaClass(GetDefaultSchemaClass()); } InModel->SetExecuteContextStruct(InModel->GetSchema()->GetExecuteContextStruct()); if(bCreateController) { CreateController(InModel); } if(InModel->IsA()) { for(URigVMGraph* Model : GetModels()) { Model->SetDefaultFunctionLibrary(FunctionLibrary); } InModel->SetDefaultFunctionLibrary(FunctionLibrary); } else if(FunctionLibrary) { InModel->SetDefaultFunctionLibrary(FunctionLibrary); } if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); ClientHost->HandleRigVMGraphAdded(this, InModel->GetNodePath()); } NotifyOuterOfPropertyChange(); } URigVMFunctionLibrary* FRigVMClient::GetOrCreateFunctionLibrary(bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController) { check(DefaultSchemaClass); return GetOrCreateFunctionLibrary(DefaultSchemaClass, bSetupUndoRedo, ObjectInitializer, bCreateController); } static void SetGetFunctionHostObjectPathDelegate(FRigVMClient* RigVMClient, URigVMFunctionLibrary* FunctionLibrary) { if (RigVMClient->GetOuter()->Implements()) { if (IRigVMClientHost* ClientHost = Cast(RigVMClient->GetOuter())) { TWeakObjectPtr WeakClientHost = Cast(ClientHost); FunctionLibrary->GetFunctionHostObjectPathDelegate.BindLambda([WeakClientHost]() -> const FSoftObjectPath { if (WeakClientHost.IsValid()) { if (IRigVMClientHost* ClientHost = Cast(WeakClientHost.Get())) { return Cast(ClientHost->GetRigVMGraphFunctionHost()); } } return nullptr; }); } } }; URigVMFunctionLibrary* FRigVMClient::GetOrCreateFunctionLibrary(TSubclassOf InSchemaClass, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController) { if(FunctionLibrary) { if (!FunctionLibrary->GetFunctionHostObjectPathDelegate.IsBound()) { SetGetFunctionHostObjectPathDelegate(this, FunctionLibrary); } return FunctionLibrary; } #if WITH_EDITOR TSharedPtr Transaction; if(bSetupUndoRedo) { Transaction = MakeShared(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph")); GetOuter()->Modify(); } #endif static constexpr TCHAR FunctionLibraryName[] = TEXT("RigVMFunctionLibrary"); const FName SafeGraphName = GetUniqueName(FunctionLibraryName); URigVMFunctionLibrary* NewFunctionLibrary = nullptr; if(ObjectInitializer) { NewFunctionLibrary = ObjectInitializer->CreateDefaultSubobject(GetOuter(), SafeGraphName); } else { NewFunctionLibrary = NewObject(GetOuter(), SafeGraphName); } NewFunctionLibrary->SetSchemaClass(InSchemaClass); if (!NewFunctionLibrary->GetFunctionHostObjectPathDelegate.IsBound()) { SetGetFunctionHostObjectPathDelegate(this, NewFunctionLibrary); } AddModel(NewFunctionLibrary, bCreateController); return NewFunctionLibrary; } TArray FRigVMClient::GetEntryNames(UScriptStruct* InUnitScriptStructFilter) const { TArray EntryNames; for(const URigVMGraph* Model : GetModels()) { for(const URigVMNode* Node : Model->GetNodes()) { // Filter out unit nodes that are not of the specified type if(InUnitScriptStructFilter != nullptr) { if(const URigVMUnitNode* UnitNode = Cast(Node)) { UScriptStruct* ScriptStruct = UnitNode->GetScriptStruct(); if(ScriptStruct == nullptr || !ScriptStruct->IsChildOf(InUnitScriptStructFilter)) { continue; } } } const FName EntryName = Node->GetEventName(); if(!EntryName.IsNone()) { EntryNames.Add(EntryName); } } } return EntryNames; } UScriptStruct* FRigVMClient::GetDefaultExecuteContextStruct() const { check(GetDefaultSchema()); return GetDefaultSchema()->GetExecuteContextStruct(); } void FRigVMClient::SetDefaultExecuteContextStruct(UScriptStruct* InExecuteContextStruct) { GetDefaultSchema()->SetExecuteContextStruct(InExecuteContextStruct); } URigVMGraph* FRigVMClient::GetFocusedModel() const { #if WITH_EDITOR if (OnGetFocusedGraph().IsBound()) { return OnGetFocusedGraph().Execute(); } #endif return GetDefaultModel(); } bool FRigVMClient::RemoveModel(FString InName, bool bSetupUndoRedo, bool bPrintPythonCommand) { return RemoveModel(InName, bSetupUndoRedo); } bool FRigVMClient::RemoveModel(const FString& InNodePathOrName, bool bSetupUndoRedo) { if(URigVMGraph* Model = GetModel(InNodePathOrName)) { UObject* ModelOuter = Model->GetOuter(); check(ModelOuter); if(Model == GetDefaultModel() && !bDefaultModelCanBeRemoved) { #if WITH_EDITOR static constexpr TCHAR Message[] = TEXT("Cannot remove the default model."); FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString()); #endif return false; } if(Model == FunctionLibrary) { #if WITH_EDITOR static constexpr TCHAR Message[] = TEXT("Cannot remove the function library."); FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString()); #endif return false; } #if WITH_EDITOR TSharedPtr Transaction; if(bSetupUndoRedo) { Transaction = MakeShared(NSLOCTEXT("RigVMClient", "RemoveModel", "Remove a root graph")); ModelOuter->Modify(); } #endif if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); ClientHost->HandleRigVMGraphRemoved(this, InNodePathOrName); } if(bSetupUndoRedo) { ModelOuter->Modify(); UndoRedoIndex++; FRigVMClientAction Action; Action.Type = ERigVMClientAction_RemoveModel; Action.NodePath = Model->GetNodePath(); UndoStack.Push(Action); RedoStack.Reset(); } // clean up the model if(ExternalModelHost == nullptr) { verify(Models.Remove(Model)); } else { // Should have already been removed from external models verify(!ExternalModelHost->GetExternalModels().Contains(Model)); } NotifyOuterOfPropertyChange(); return true; } return false; } FName FRigVMClient::RenameModel(const FString& InNodePathOrName, const FName& InNewName, bool bSetupUndoRedo) { if(URigVMGraph* Model = GetModel(InNodePathOrName)) { if(Model == FunctionLibrary) { #if WITH_EDITOR static constexpr TCHAR Message[] = TEXT("Cannot rename the function library."); FScriptExceptionHandler::Get().HandleException(ELogVerbosity::Error, Message, *FString()); #endif return NAME_None; } if(Model->GetFName() == InNewName) { return InNewName; } #if WITH_EDITOR TSharedPtr Transaction; if(bSetupUndoRedo) { Transaction = MakeShared(NSLOCTEXT("RigVMClient", "RenameModel", "Rename a root graph")); } #endif TObjectPtr* Controller = Controllers.Find(Model); const FString OldNodePath = Model->GetNodePath(); const FName SafeNewName = GetUniqueName(InNewName); Model->Rename(*SafeNewName.ToString(), nullptr, REN_DontCreateRedirectors); const FString NewNodePath = Model->GetNodePath(); if (Controller) { Controllers.Add(Model, *Controller); } if(bSetupUndoRedo) { GetOuter()->Modify(); UndoRedoIndex++; FRigVMClientAction Action; Action.Type = ERigVMClientAction_RenameModel; Action.NodePath = OldNodePath; Action.OtherNodePath = NewNodePath; UndoStack.Push(Action); RedoStack.Reset(); } if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); ClientHost->HandleRigVMGraphRenamed(this, OldNodePath, NewNodePath); } NotifyOuterOfPropertyChange(); return SafeNewName; } return NAME_None; } void FRigVMClient::PostTransacted(const FTransactionObjectEvent& TransactionEvent) { IRigVMClientHost* ClientHost = CastChecked(GetOuter()); if (TransactionEvent.GetEventType() == ETransactionObjectEventType::UndoRedo) { auto PerformAction = [this, ClientHost](const FRigVMClientAction& InAction, bool bUndo) { switch(InAction.Type) { case ERigVMClientAction_AddModel: case ERigVMClientAction_RemoveModel: { if(InAction.Type == ERigVMClientAction_RemoveModel) { bUndo = !bUndo; } if(URigVMGraph* Model = GetModel(InAction.NodePath)) { if(bUndo) { ClientHost->HandleRigVMGraphRemoved(this, InAction.NodePath); } else { URigVMController* Controller = GetOrCreateController(Model); ClientHost->HandleRigVMGraphAdded(this, InAction.NodePath); if(Controller) { Controller->ResendAllNotifications(); } } } break; } case ERigVMClientAction_RenameModel: { const FString NodePathA = bUndo ? InAction.OtherNodePath : InAction.NodePath; const FString NodePathB = bUndo ? InAction.NodePath : InAction.OtherNodePath; FString NodeNameB = NodePathB; NodeNameB.Split(TEXT("|"), nullptr, &NodeNameB, ESearchCase::IgnoreCase, ESearchDir::FromEnd); NodeNameB.RemoveFromEnd(TEXT("::")); RenameModel(NodePathA, *NodeNameB, false); break; } } }; while(UndoStack.Num() != UndoRedoIndex) { // do we need to undo - or redo? if(UndoStack.Num() > UndoRedoIndex) { FRigVMClientAction Action = UndoStack.Pop(); PerformAction(Action, true); RedoStack.Push(Action); } else { FRigVMClientAction Action = RedoStack.Pop(); PerformAction(Action, false); UndoStack.Push(Action); } } } } void FRigVMClient::OnCollapseNodeRenamed(const URigVMCollapseNode* InCollapseNode) { TMap> OldControllers = Controllers; for(const TPair>& Pair : OldControllers) { if(URigVMController* Controller = Pair.Value) { if(const URigVMGraph* Graph = Controller->GetGraph()) { if(Graph->IsInOuter(InCollapseNode)) { const FSoftObjectPath& OldObjectPath = Pair.Key; const FSoftObjectPath NewObjectPath(Graph); if(OldObjectPath != NewObjectPath) { Controllers.Add(NewObjectPath, Controller); Controllers.Remove(OldObjectPath); } } } } } } void FRigVMClient::OnCollapseNodeRemoved(const URigVMCollapseNode* InCollapseNode) { TMap> OldControllers = Controllers; for(const TPair>& Pair : OldControllers) { if(URigVMController* Controller = Pair.Value) { if(const URigVMGraph* Graph = Controller->GetGraph()) { if(Graph->IsInOuter(InCollapseNode)) { RemoveController(Graph); } } } } } URigVMNode* FRigVMClient::FindNode(const FString& InNodePathOrName) const { for(const URigVMGraph* Model : GetModels()) { if(URigVMNode* Node = Model->FindNode(InNodePathOrName)) { return Node; } } if(FunctionLibrary) { return FunctionLibrary->FindNode(InNodePathOrName); } return nullptr; } URigVMPin* FRigVMClient::FindPin(const FString& InPinPath) const { for(const URigVMGraph* Model : GetModels()) { if(URigVMPin* Pin = Model->FindPin(InPinPath)) { return Pin; } } if(FunctionLibrary) { return FunctionLibrary->FindPin(InPinPath); } return nullptr; } UObject* FRigVMClient::GetOuter() const { UObject* Outer = OuterClientHost.Get(); check(Outer); return Outer; } FProperty* FRigVMClient::GetOuterClientProperty() const { return GetOuter()->GetClass()->FindPropertyByName(OuterClientPropertyName); } void FRigVMClient::NotifyOuterOfPropertyChange(EPropertyChangeType::Type ChangeType) const { if(bSuspendNotifications) { return; } FProperty* Property = GetOuterClientProperty(); FPropertyChangedEvent PropertyChangedEvent(Property, ChangeType); GetOuter()->PostEditChangeProperty(PropertyChangedEvent); } URigVMController* FRigVMClient::CreateController(const URigVMGraph* InModel) { const FString ModelName = InModel ? InModel->GetName() : TEXT("__NullGraph"); const FName SafeControllerName = GetUniqueName(*FString::Printf(TEXT("%s_Controller"), *ModelName)); URigVMController* Controller = NewObject(GetOuter(), ControllerClass, SafeControllerName); Controllers.Add(InModel, Controller); if(InModel && InModel->GetSchemaClass() != nullptr) { Controller->SetSchemaClass(InModel->GetSchemaClass()); } else { check(DefaultSchemaClass); Controller->SetSchemaClass(DefaultSchemaClass); } Controller->SetActionStack(GetOrCreateActionStack()); if(InModel) { Controller->SetGraph((URigVMGraph*)InModel); } Controller->OnModified().AddLambda([this](ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject) { HandleGraphModifiedEvent(InNotifType, InGraph, InSubject); }); if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); ClientHost->HandleConfigureRigVMController(this, Controller); } if(InModel) { Controller->RemoveStaleNodes(); } return Controller; } URigVMActionStack* FRigVMClient::GetOrCreateActionStack() { if(UObject* Outer = GetOuter()) { if (ActionStack && ActionStack->GetOuter() != Outer) { ResetActionStack(); } if(ActionStack == nullptr) { ActionStack = NewObject(Outer, TEXT("ActionStack")); } } return ActionStack; } void FRigVMClient::ResetActionStack() { DestroyObject(ActionStack); ActionStack = nullptr; } FName FRigVMClient::GetUniqueName(const FName& InDesiredName) const { return GetUniqueName(GetOuter(), InDesiredName); } FName FRigVMClient::GetUniqueName(UObject* InOuter, const FName& InDesiredName) { return URigVMSchema::GetUniqueName(*InDesiredName.ToString(), [InOuter](const FName& InName) -> bool { return FindObjectWithOuter(InOuter, nullptr, InName) == nullptr; }, false, true); } void FRigVMClient::DestroyObject(UObject* InObject) { if(InObject) { static int32 ObjectIndexToBeDestroyed = 0; static constexpr TCHAR ObjectNameFormat[] = TEXT("RigVMClient_ObjectToBeDestroyed_%d"); const FString NewObjectName = FString::Printf(ObjectNameFormat, ObjectIndexToBeDestroyed++); InObject->Rename(*NewObjectName, GetTransientPackage(), REN_DontCreateRedirectors); if(!InObject->IsRooted()) { InObject->MarkAsGarbage(); } } } uint32 FRigVMClient::GetStructureHash() const { uint32 Hash = 0; const TArray ModelsAndFunctionLibrary = GetAllModels(true, true); for(const URigVMGraph* Model : ModelsAndFunctionLibrary) { Hash = HashCombine(Hash, Model->GetStructureHash()); } return Hash; } uint32 FRigVMClient::GetSerializedStructureHash() const { uint32 Hash = 0; const TArray ModelsAndFunctionLibrary = GetAllModels(true, true); for(const URigVMGraph* Model : ModelsAndFunctionLibrary) { Hash = HashCombine(Hash, Model->GetSerializedStructureHash()); } return Hash; } FRigVMClientPatchResult FRigVMClient::PatchModelsOnLoad() { FRigVMClientPatchResult Result; TArray AllModels = GetAllModelsLeavesFirst(true); TGuardValue ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true); for(URigVMGraph* Model : AllModels) { Model->PostLoad(); Model->SetSchemaClass(GetDefaultSchemaClass()); URigVMController* Controller = GetOrCreateController(Model); TGuardValue GuardSuspendTemplateComputation(Controller->bSuspendTemplateComputation, true); TGuardValue GuardIsTransacting(Controller->bIsTransacting, true); { FRigVMDefaultValueTypeGuard DefaultValueTypeGuard(Controller, ERigVMPinDefaultValueType::KeepValueType); Result.Merge(Controller->PatchLocalVariableTypes()); Result.Merge(Controller->PatchRerouteNodesOnLoad()); Result.Merge(Controller->PatchUnitNodesOnLoad()); Result.Merge(Controller->PatchDispatchNodesOnLoad()); Result.Merge(Controller->PatchBranchNodesOnLoad()); Result.Merge(Controller->PatchIfSelectNodesOnLoad()); Result.Merge(Controller->PatchArrayNodesOnLoad()); Result.Merge(Controller->PatchReduceArrayFloatDoubleConvertsionsOnLoad()); Result.Merge(Controller->PatchInvalidLinksOnWildcards()); Result.Merge(Controller->PatchExecutePins()); Result.Merge(Controller->PatchLazyPins()); Result.Merge(Controller->PatchUserDefinedStructPinNames()); if (URigVMCollapseNode* CollapseNode = Model->GetTypedOuter()) { Result.Merge(Controller->PatchFunctionsWithInvalidReturnPaths()); } } Result.Merge(Controller->PatchPinDefaultValues()); } return Result; } #if WITH_EDITOR void FRigVMClient::PatchFunctionReferencesOnLoad() { // If the asset was copied from one project to another, the function referenced might have a different // path, even if the function is internal to the contorl rig. In that case, let's try to find the function // in the local function library. for (URigVMGraph* Model : *this) { TArray Nodes = Model->GetNodes(); for (URigVMLibraryNode* Library : GetFunctionLibrary()->GetFunctions()) { Nodes.Append(Library->GetContainedNodes()); } for (int32 i = 0; i < Nodes.Num(); ++i) { URigVMNode* Node = Nodes[i]; if (URigVMFunctionReferenceNode* FunctionReferenceNode = Cast(Node)) { if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsValid()) { (void)FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.LoadSynchronous(); } if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED) { if (FunctionLibrary) { FString FunctionPath = FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.ToSoftObjectPath().GetSubPathString(); FString Left, Right; if (FunctionPath.Split(TEXT("."), &Left, &Right)) { FString LibraryNodePath = FunctionLibrary->GetNodePath(); if (Left == FunctionLibrary->GetName()) { if (URigVMLibraryNode* LibraryNode = Cast(FunctionLibrary->FindNode(Right))) { FunctionReferenceNode->ReferencedNodePtr_DEPRECATED = LibraryNode; } } } } } if (FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsValid()) { FunctionReferenceNode->ReferencedFunctionHeader = FunctionReferenceNode->ReferencedNodePtr_DEPRECATED->GetFunctionHeader(); } else if (!FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.IsNull()) { // At least lets make sure we store the path in the header FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.SetLibraryNodePath(FunctionReferenceNode->ReferencedNodePtr_DEPRECATED.ToSoftObjectPath().ToString()); } if (FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.LibraryNode_DEPRECATED.IsValid()) { FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.SetLibraryNodePath(FunctionReferenceNode->ReferencedFunctionHeader.LibraryPointer.LibraryNode_DEPRECATED.ToString()); } for (URigVMPin* Pin : FunctionReferenceNode->GetPins()) { if (Pin->IsArray()) { Pin->bIsDynamicArray = true; } } } if (URigVMCollapseNode* CollapseNode = Cast(Node)) { Nodes.Append(CollapseNode->GetContainedNodes()); } } } } #endif // WITH_EDITOR void FRigVMClient::PatchFunctionsOnLoad(IRigVMGraphFunctionHost* FunctionHost, TArray& BackwardsCompatiblePublicFunctions, TMap& OldHeaders) { if (FRigVMGraphFunctionStore* Store = FunctionHost->GetRigVMGraphFunctionStore()) { // Lets rebuild the FunctionStore from the model if (FunctionLibrary) { Store->PublicFunctions.Reset(); Store->PrivateFunctions.Reset(); for (URigVMLibraryNode* LibraryNode : FunctionLibrary->GetFunctions()) { // Fix array pins to be marked as dynamic array for (URigVMPin* Pin : LibraryNode->GetPins()) { if (Pin->IsArray()) { Pin->bIsDynamicArray = true; } } if (URigVMFunctionEntryNode* EntryNode = LibraryNode->GetEntryNode()) { for (URigVMPin* Pin : EntryNode->GetPins()) { if (Pin->IsArray()) { Pin->bIsDynamicArray = true; } } } if (URigVMFunctionReturnNode* ReturnNode = LibraryNode->GetReturnNode()) { for (URigVMPin* Pin : ReturnNode->GetPins()) { if (Pin->IsArray()) { Pin->bIsDynamicArray = true; } } } bool bIsPublic = FunctionLibrary->IsFunctionPublic(LibraryNode->GetFName()); if (!bIsPublic) { bIsPublic = BackwardsCompatiblePublicFunctions.Contains(LibraryNode->GetFName()); if (bIsPublic) { FunctionLibrary->PublicFunctionNames.Add(LibraryNode->GetFName()); } } FRigVMGraphFunctionHeader Header = LibraryNode->GetFunctionHeader(FunctionHost); if (FRigVMGraphFunctionHeader* OldHeader = OldHeaders.Find(LibraryNode)) { Header.ExternalVariables = OldHeader->ExternalVariables; Header.Dependencies = OldHeader->Dependencies; Header.Layout = OldHeader->Layout; } const FRigVMVariant* Variant = FunctionLibrary->GetFunctionVariant(LibraryNode->GetFName()); if (!Variant) { Header.Variant.Guid = FRigVMVariant::GenerateGUID(Header.LibraryPointer.GetLibraryNodePath()); FunctionLibrary->FunctionToVariant.FindOrAdd(Header.Name) = Header.Variant; } else { Header.Variant = *Variant; } FRigVMGraphFunctionData* FunctionData = Store->AddFunction(Header, bIsPublic); if (bIsPublic) { UpdateGraphFunctionSerializedGraph(LibraryNode); } else { FunctionData->SerializedCollapsedNode_DEPRECATED.Empty(); FunctionData->CollapseNodeArchive.Empty(); } } // Update dependencies and external variables if needed for (URigVMLibraryNode* LibraryNode : FunctionLibrary->GetFunctions()) { UpdateExternalVariablesForFunction(LibraryNode); UpdateDependenciesForFunction(LibraryNode); } } } } FRigVMClientPatchResult FRigVMClient::PatchPinDefaultValues() { FRigVMClientPatchResult Result; TArray AllModels = GetAllModelsLeavesFirst(true); TGuardValue ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true); for(URigVMGraph* Model : AllModels) { URigVMController* Controller = GetOrCreateController(Model); TGuardValue GuardIsTransacting(Controller->bIsTransacting, true); Result.Merge(Controller->PatchPinDefaultValues()); } return Result; } void FRigVMClient::ProcessDetachedLinks() { TArray AllModels = GetAllModels(true, true); for(URigVMGraph* Model : AllModels) { URigVMController* Controller = GetOrCreateController(Model); Controller->ProcessDetachedLinks(); } } void FRigVMClient::PreSave(FObjectPreSaveContext ObjectSaveContext) { if (!ObjectSaveContext.IsCooking()) { if (IRigVMClientHost* ClientHost = Cast(GetOuter())) { if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost()) { if (FRigVMGraphFunctionStore* Store = FunctionHost->GetRigVMGraphFunctionStore()) { for (URigVMNode* Node : FunctionLibrary->GetNodes()) { if (URigVMLibraryNode* LibraryNode = Cast(Node)) { FRigVMGraphFunctionIdentifier Identifier = LibraryNode->GetFunctionIdentifier(); if (Store->IsFunctionPublic(Identifier)) { UpdateGraphFunctionSerializedGraph(LibraryNode); } } } } } } } } void FRigVMClient::HandleGraphModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject) { if (bIgnoreModelNotifications) { return; } #if WITH_EDITOR IRigVMClientHost* ClientHost = Cast(GetOuter()); IRigVMGraphFunctionHost* FunctionHost = nullptr; FRigVMGraphFunctionStore* FunctionStore = nullptr; if (ClientHost) { FunctionHost = ClientHost->GetRigVMGraphFunctionHost(); if (FunctionHost) { FunctionStore = FunctionHost->GetRigVMGraphFunctionStore(); } } switch (InNotifType) { case ERigVMGraphNotifType::NodeAdded: // A node has been added to the graph (Subject == URigVMNode) { // A node was added directly into the function library if(InGraph->IsA()) { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { if (GetOuter()->Implements()) { if (FunctionStore && FunctionHost) { FunctionStore->AddFunction(CollapseNode->GetFunctionHeader(FunctionHost), false); } } } } // A node was added into the contained graph of a function else if(URigVMLibraryNode* LibraryNode = Cast(InSubject)->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); if (URigVMFunctionReferenceNode* FunctionReference = Cast(InSubject)) { UpdateDependenciesForFunction(LibraryNode); UpdateExternalVariablesForFunction(LibraryNode); } } break; } case ERigVMGraphNotifType::NodeRemoved: // A node has been removed from the graph (Subject == URigVMNode) { if(InSubject->GetOuter()->IsA()) { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { if (GetOuter()->Implements()) { if (FunctionStore && FunctionHost) { FunctionStore->RemoveFunction(FRigVMGraphFunctionIdentifier (Cast(FunctionHost), CollapseNode->GetPathName())); } } } } // A node was added into the contained graph of a function else if(URigVMLibraryNode* LibraryNode = Cast(InSubject)->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); if (URigVMFunctionReferenceNode* FunctionReference = Cast(InSubject)) { UpdateDependenciesForFunction(LibraryNode); UpdateExternalVariablesForFunction(LibraryNode); } } break; } case ERigVMGraphNotifType::VariableAdded: // A variable has been added (Subject == URigVMVariableNode) case ERigVMGraphNotifType::VariableRemoved: // A variable has been removed (Subject == URigVMVariableNode) case ERigVMGraphNotifType::VariableRenamed: // A variable has been renamed (Subject == URigVMVariableNode) case ERigVMGraphNotifType::VariableRemappingChanged: // A function reference node's remapping has changed (Subject == URigVMFunctionReferenceNode) { if(URigVMLibraryNode* LibraryNode = Cast(InSubject)->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); UpdateExternalVariablesForFunction(LibraryNode); } break; } case ERigVMGraphNotifType::FunctionRenamed: // A node has been renamed in the graph (Subject == URigVMNode) { if(InSubject->GetOuter()->IsA()) { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { URigVMBuildData* BuildData = URigVMBuildData::Get(); if (IRigVMGraphFunctionHost* Host = Cast(CollapseNode->GetFunctionIdentifier().HostObject.ResolveObject())) { if (FRigVMGraphFunctionData* Data = Host->GetRigVMGraphFunctionStore()->FindFunctionByName(CollapseNode->GetPreviousFName())) { const FRigVMGraphFunctionIdentifier PreviousFunctionId = Data->Header.LibraryPointer; const FRigVMVariant Variant = Data->Header.Variant; Data->Header = CollapseNode->GetFunctionHeader(); Data->Header.Variant = Variant; if (const FRigVMFunctionReferenceArray* FunctionReferencesPtr = BuildData->GraphFunctionReferences.Find(PreviousFunctionId)) { const FRigVMFunctionReferenceArray& FunctionReferences = *FunctionReferencesPtr; BuildData->Modify(); FRigVMFunctionReferenceArray& NewFunctionReferences = BuildData->GraphFunctionReferences.Add(Data->Header.LibraryPointer, FunctionReferences); BuildData->GraphFunctionReferences.Remove(PreviousFunctionId); BuildData->MarkPackageDirty(); for (int32 i=0; iReferencedFunctionHeader = Data->Header; NewFunctionReferences[i]->MarkPackageDirty(); } } } UpdateFunctionReferences(Data->Header, true, false); UpdateGraphFunctionData(CollapseNode); } } } } break; } case ERigVMGraphNotifType::NodeColorChanged: // A node's color has changed (Subject == URigVMNode) case ERigVMGraphNotifType::NodeCategoryChanged: // A node's category has changed (Subject == URigVMNode) case ERigVMGraphNotifType::NodeKeywordsChanged: // A node's keywords have changed (Subject == URigVMNode) case ERigVMGraphNotifType::NodeDescriptionChanged: // A node's description has changed (Subject == URigVMNode) case ERigVMGraphNotifType::NodeTitleChanged: // A node's title has changed (Subject == URigVMNode) case ERigVMGraphNotifType::VariantTagsChanged: // The tags in the header of this function variant have changed (Subject == URigVMLibraryNode) { if(InSubject->GetOuter()->IsA()) { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { UpdateGraphFunctionData(CollapseNode); } } break; } case ERigVMGraphNotifType::PinAdded: // A pin has been added to a given node (Subject == URigVMPin) case ERigVMGraphNotifType::PinRemoved: // A pin has been removed from a given node (Subject == URigVMPin) case ERigVMGraphNotifType::PinRenamed: // A pin has been renamed (Subject == URigVMPin) case ERigVMGraphNotifType::PinArraySizeChanged: // An array pin's size has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinDefaultValueChanged: // A pin's default value has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinDirectionChanged: // A pin's direction has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinTypeChanged: // A pin's data type has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinIndexChanged: // A pin's index has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinWatchedChanged: // A pin's watch state has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinDisplayNameChanged: // A pin's display name / UI label has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinCategoryChanged: // A pin's category has changed (Subject == URigVMPin) case ERigVMGraphNotifType::PinCategoryExpansionChanged: // A pin category has been collapsed / expanded on the function node (Subject == URigVMNode) case ERigVMGraphNotifType::FunctionVariantGuidChanged: // A function has changed its guid (Subject == URigVMLibraryNode) { URigVMNode* Node = Cast(InSubject); if (const URigVMPin* Pin = Cast(InSubject)) { Node = Cast(Pin->GetNode()); } if(Node) { if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); } if(Node->GetOuter()->IsA()) { if (const URigVMCollapseNode* CollapseNode = Cast(Node)) { UpdateGraphFunctionData(CollapseNode); } } } break; } case ERigVMGraphNotifType::PinCategoriesChanged: // A node's category list has changed (Subject == URigVMNode) { if (URigVMNode* Node = Cast(InSubject)) { if(Node->GetOuter()->IsA()) { if (const URigVMCollapseNode* CollapseNode = Cast(Node)) { UpdateGraphFunctionData(CollapseNode); } } } break; } case ERigVMGraphNotifType::LinkAdded: // A link has been added (Subject == URigVMLink) case ERigVMGraphNotifType::LinkRemoved: // A link has been removed (Subject == URigVMLink) { if (URigVMLink* Link = Cast(InSubject)) { if (URigVMNode* OuterNode = Cast(Link->GetGraph()->GetOuter())) { if (URigVMLibraryNode* LibraryNode = OuterNode->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); } } } break; } case ERigVMGraphNotifType::FunctionAccessChanged: // A function was made public/private { if (URigVMLibraryNode* LibraryNode = Cast(InSubject)) { if (URigVMFunctionLibrary* Library = Cast(InGraph)) { bool bIsPublic = Library->IsFunctionPublic(LibraryNode->GetFName()); if (FRigVMGraphFunctionStore* Store = FindFunctionStore(LibraryNode)) { Store->MarkFunctionAsPublic(LibraryNode->GetFunctionIdentifier(), bIsPublic); if(bIsPublic) { UpdateGraphFunctionSerializedGraph(LibraryNode); } } } } break; } default: { break; } } #endif } FRigVMGraphFunctionStore* FRigVMClient::FindFunctionStore(const URigVMLibraryNode* InLibraryNode) { if (IRigVMClientHost* ClientHost = InLibraryNode->GetImplementingOuter()) { if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost()) { return FunctionHost->GetRigVMGraphFunctionStore(); } } return nullptr; } bool FRigVMClient::UpdateFunctionReferences(const FRigVMGraphFunctionHeader& Header, bool bUpdateDependencies, bool bUpdateExternalVariables) { URigVMBuildData* BuildData = URigVMBuildData::Get(); if (const FRigVMFunctionReferenceArray* FunctionReferenceArray = BuildData->FindFunctionReferences(Header.LibraryPointer)) { for (int32 i=0; iNum(); ++i) { const TSoftObjectPtr& Reference = FunctionReferenceArray->FunctionReferences[i]; // Only update references that are loaded // Other references will be updated when they are loaded if (Reference.IsValid()) { URigVMFunctionReferenceNode* Node = Reference.Get(); Node->Modify(); Node->ReferencedFunctionHeader = Header; Node->InvalidateCache(); if (bUpdateDependencies || bUpdateExternalVariables) { if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode()) { IRigVMClientHost* OtherClientHost = LibraryNode->GetImplementingOuter(); if (bUpdateDependencies) { OtherClientHost->GetRigVMClient()->UpdateDependenciesForFunction(LibraryNode); } if (bUpdateExternalVariables) { OtherClientHost->GetRigVMClient()->UpdateExternalVariablesForFunction(LibraryNode); } } } Node->MarkPackageDirty(); } } } return true; } bool FRigVMClient::UpdateGraphFunctionData(const URigVMLibraryNode* InLibraryNode) { if (IRigVMClientHost* ClientHost = InLibraryNode->GetImplementingOuter()) { if (IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost()) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { if (FRigVMGraphFunctionData* Data = Store->UpdateFunctionInterface(InLibraryNode->GetFunctionHeader(FunctionHost))) { UpdateFunctionReferences(Data->Header, false, false); return true; } } } } return false; } bool FRigVMClient::UpdateExternalVariablesForFunction(const URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier(); if (Store->UpdateExternalVariables(Identifier, InLibraryNode->GetExternalVariables())) { const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier); UpdateFunctionReferences(Data->Header, false, true); return true; } } return false; } bool FRigVMClient::UpdateDependenciesForFunction(const URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { TMap Dependencies = InLibraryNode->GetDependencies(); FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier(); if (Store->UpdateDependencies(Identifier, Dependencies)) { const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier); UpdateFunctionReferences(Data->Header, true, false); return true; } } return false; } bool FRigVMClient::DirtyGraphFunctionCompilationData(URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier(); if (const FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier)) { Store->RemoveFunctionCompilationData(Identifier); // References to this function will check if the compilation hash matches, and will recompile if they // see a different compilation hash. No need to dirty their compilation data. return true; } } return false; } bool FRigVMClient::UpdateGraphFunctionSerializedGraph(URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { const FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier(); if (FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier)) { Data->SerializedCollapsedNode_DEPRECATED.Empty(); URigVMController* Controller = GetOrCreateController(InLibraryNode->GetGraph()); check(Controller); Data->CollapseNodeArchive.Reset(); (void)Controller->ExportFunctionToArchive(Identifier.GetFunctionFName(), Data->CollapseNodeArchive); return true; } } return false; } bool FRigVMClient::IsFunctionPublic(URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { return Store->IsFunctionPublic(InLibraryNode->GetFunctionIdentifier()); } return false; }