// Copyright Epic Games, Inc. All Rights Reserved. #include "RigVMModel/RigVMClient.h" #include "Misc/TransactionObjectEvent.h" #include "Exporters/Exporter.h" #include "UnrealExporter.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(RigVMClient) #if WITH_EDITOR #include "ScopedTransaction.h" #endif void FRigVMClient::SetOuterClientHost(UObject* InOuterClientHost, const FName& InOuterClientHostPropertyName) { OuterClientHost = InOuterClientHost; OuterClientPropertyName = InOuterClientHostPropertyName; check(OuterClientHost->Implements()); check(GetOuterClientProperty() != nullptr); } 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::Reset() { for(URigVMGraph* Model : Models) { DestroyObject(Model); } for(auto Pair : Controllers) { DestroyObject(Pair.Value); } DestroyObject(FunctionLibrary); Models.Reset(); Controllers.Reset(); FunctionLibrary = nullptr; } URigVMGraph* FRigVMClient::GetDefaultModel() const { if(Models.IsEmpty()) { return nullptr; } return GetModel(0); } 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; } TArray FRigVMClient::GetAllModels(bool bIncludeFunctionLibrary, bool bRecursive) const { TArray AllModels = Models; if(bRecursive) { for(URigVMGraph* Model : Models) { AllModels.Append(Model->GetContainedGraphs(true /* recursive */)); } } if(bIncludeFunctionLibrary && FunctionLibrary) { AllModels.Add(FunctionLibrary); if(bRecursive) { AllModels.Append(FunctionLibrary->GetContainedGraphs(true /* recursive */)); } } return AllModels; } 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)) { 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)) { 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; } URigVMGraph* FRigVMClient::AddModel(const FName& InName, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController) { #if WITH_EDITOR TSharedPtr Transaction; if(bSetupUndoRedo) { Transaction = MakeShared(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph")); GetOuter()->Modify(); } #endif const FName SafeGraphName = GetUniqueName(InName); URigVMGraph* Model = nullptr; if(ObjectInitializer) { Model = ObjectInitializer->CreateDefaultSubobject(GetOuter(), SafeGraphName); } else { Model = NewObject(GetOuter(), SafeGraphName); } AddModel(Model, bCreateController); if(bSetupUndoRedo) { GetOuter()->Modify(); UndoRedoIndex++; FRigVMClientAction Action; Action.Type = ERigVMClientAction::ERigVMClientAction_AddModel; Action.NodePath = Model->GetNodePath(); UndoStack.Push(Action); RedoStack.Reset(); } return Model; } void FRigVMClient::AddModel(URigVMGraph* InModel, bool bCreateController) { check(InModel); if(InModel->IsA()) { check(FunctionLibrary == nullptr); FunctionLibrary = Cast(InModel); } else { Models.Add(InModel); } InModel->SetExecuteContextStruct(GetExecuteContextStruct()); if(bCreateController) { CreateController(InModel); } if(InModel->IsA()) { for(URigVMGraph* Model : Models) { 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) { if(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->GetFunctionHostObjectPathDelegate.BindLambda([this]() -> const FSoftObjectPath { if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); return Cast(ClientHost->GetRigVMGraphFunctionHost()); } return nullptr; }); AddModel(NewFunctionLibrary, bCreateController); return NewFunctionLibrary; } TArray FRigVMClient::GetEntryNames() const { TArray EntryNames; for(const URigVMGraph* Model : Models) { for(const URigVMNode* Node : Model->GetNodes()) { const FName EntryName = Node->GetEventName(); if(!EntryName.IsNone()) { EntryNames.Add(EntryName); } } } return EntryNames; } bool FRigVMClient::RemoveModel(const FString& InNodePathOrName, bool bSetupUndoRedo) { if(URigVMGraph* Model = GetModel(InNodePathOrName)) { if(Model == GetDefaultModel()) { #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")); GetOuter()->Modify(); } #endif // remove the controller if(URigVMController* Controller = GetController(Model)) { verify(Controller == Controllers.FindAndRemoveChecked(Model)); } if (GetOuter()->Implements()) { IRigVMClientHost* ClientHost = Cast(GetOuter()); ClientHost->HandleRigVMGraphRemoved(this, InNodePathOrName); } if(bSetupUndoRedo) { GetOuter()->Modify(); UndoRedoIndex++; FRigVMClientAction Action; Action.Type = ERigVMClientAction_RemoveModel; Action.NodePath = Model->GetNodePath(); UndoStack.Push(Action); RedoStack.Reset(); } // clean up the model Models.Remove(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 const FString OldNodePath = Model->GetNodePath(); const FName SafeNewName = GetUniqueName(InNewName); Model->Rename(*SafeNewName.ToString(), nullptr, REN_ForceNoResetLoaders | REN_DontCreateRedirectors); const FString NewNodePath = Model->GetNodePath(); 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::OnSubGraphRenamed(const FSoftObjectPath& OldObjectPath, const FSoftObjectPath& NewObjectPath) { if(OldObjectPath != NewObjectPath) { if (TObjectPtr* Controller = Controllers.Find(OldObjectPath)) { Controllers.Add(NewObjectPath, *Controller); Controllers.Remove(OldObjectPath); } } } URigVMNode* FRigVMClient::FindNode(const FString& InNodePathOrName) const { for(const URigVMGraph* Model : Models) { 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 : Models) { 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 FName SafeControllerName = GetUniqueName(*FString::Printf(TEXT("%s_Controller"), *InModel->GetName())); URigVMController* Controller = NewObject(GetOuter(), SafeControllerName); Controllers.Add(InModel, Controller); 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); } Controller->RemoveStaleNodes(); return Controller; } FName FRigVMClient::GetUniqueName(const FName& InDesiredName) const { return GetUniqueName(GetOuter(), InDesiredName); } FName FRigVMClient::GetUniqueName(UObject* InOuter, const FName& InDesiredName) { return URigVMController::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_ForceNoResetLoaders | REN_DontCreateRedirectors); 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 = GetAllModels(true, true); TGuardValue ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true); for(URigVMGraph* Model : AllModels) { URigVMController* Controller = GetOrCreateController(Model); TGuardValue GuardSuspendTemplateComputation(Controller->bSuspendTemplateComputation, true); TGuardValue GuardIsTransacting(Controller->bIsTransacting, true); Result.Merge(Controller->PatchUnitNodesOnLoad()); Result.Merge(Controller->PatchDispatchNodesOnLoad()); Result.Merge(Controller->PatchBranchNodesOnLoad()); Result.Merge(Controller->PatchIfSelectNodesOnLoad()); Result.Merge(Controller->PatchArrayNodesOnLoad()); Result.Merge(Controller->PatchInvalidLinksOnWildcards()); } return Result; } void FRigVMClient::PostDuplicateHost(const FString& InOldPathName, const FString& InNewPathName) { TArray AllModels = GetAllModels(true, true); for(URigVMGraph* Model : AllModels) { URigVMController* Controller = GetOrCreateController(Model); Controller->PostDuplicateHost(InOldPathName, InNewPathName); } } void FRigVMClient::PreSave() { for (URigVMNode* Node : FunctionLibrary->GetNodes()) { if (URigVMLibraryNode* LibraryNode = Cast(Node)) { UpdateGraphFunctionSerializedGraph(LibraryNode); } } } void FRigVMClient::HandleGraphModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject) { if (bIgnoreModelNotifications) { return; } #if WITH_EDITOR IRigVMClientHost* ClientHost = Cast(GetOuter()); IRigVMGraphFunctionHost* FunctionHost = ClientHost->GetRigVMGraphFunctionHost(); FRigVMGraphFunctionStore* 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()) { 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()) { FunctionStore->RemoveFunction(FRigVMGraphFunctionIdentifier (Cast(FunctionHost), CollapseNode)); } } } // 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::NodeRenamed: // A node has been renamed in the graph (Subject == URigVMNode) { if(InSubject->GetOuter()->IsA()) { if (URigVMCollapseNode* CollapseNode = Cast(InSubject)) { URigVMBuildData* BuildData = URigVMBuildData::Get(); IRigVMGraphFunctionHost* Host = Cast(CollapseNode->GetFunctionIdentifier().HostObject.ResolveObject()); FRigVMGraphFunctionData* Data = Host->GetRigVMGraphFunctionStore()->FindFunctionByName(CollapseNode->GetPreviousFName()); const FRigVMGraphFunctionIdentifier PreviousFunctionId = Data->Header.LibraryPointer; Data->Header = CollapseNode->GetFunctionHeader(); 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; } } 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) { 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) { if (URigVMNode* Node = Cast(InSubject->GetOuter())) { if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode()) { DirtyGraphFunctionCompilationData(LibraryNode); } if(Node->GetOuter()->IsA()) { if (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); } } } 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]; // Load reference package if (!Reference.IsValid()) { Reference.LoadSynchronous(); } if (Reference.IsValid()) { URigVMFunctionReferenceNode* Node = Reference.Get(); Node->Modify(); Node->ReferencedFunctionHeader = Header; 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 (GetOuter()->Implements()) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { if (FRigVMGraphFunctionData* Data = Store->UpdateFunctionInterface(InLibraryNode->GetFunctionHeader())) { 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)) { FRigVMGraphFunctionIdentifier Identifier = InLibraryNode->GetFunctionIdentifier(); if (FRigVMGraphFunctionData* Data = Store->FindFunction(Identifier)) { FStringOutputDevice Archive; const FExportObjectInnerContext Context; UExporter::ExportToOutputDevice(&Context, InLibraryNode, NULL, Archive, TEXT("copy"), 0, PPF_ExportsNotFullyQualified | PPF_Copy | PPF_Delimited, false, nullptr); Data->SerializedCollapsedNode = Archive; return true; } } return false; } bool FRigVMClient::IsFunctionPublic(URigVMLibraryNode* InLibraryNode) { if (FRigVMGraphFunctionStore* Store = FindFunctionStore(InLibraryNode)) { return Store->IsFunctionPublic(InLibraryNode->GetFunctionIdentifier()); } return false; }