Files
UnrealEngineUWP/Engine/Source/Developer/RigVMDeveloper/Private/RigVMModel/RigVMClient.cpp
helge mathee 3acbeeb2d3 RigVM: Store a hash along side each graph identifying the structural state
Avoid rebuilding graphs if we don't need to. hashes contain the structure of each node, the structure of any used data type and the structure of each used template.

#rb sara.schvartzman
#preflight https://horde.devtools.epicgames.com/job/63ee1c6fc9692d7c04423941

[CL 24280391 by helge mathee in ue5-main branch]
2023-02-17 08:46:05 -05:00

1070 lines
30 KiB
C++

// 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<URigVMClientHost>());
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<URigVMGraph*> 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<URigVMCollapseNode>(Model->FindNode(RemainingNodePath)))
{
return CollapseNode->GetContainedGraph();
}
}
}
return nullptr;
}
URigVMGraph* FRigVMClient::GetModel(const UObject* InEditorSideObject) const
{
check(InEditorSideObject);
if (InEditorSideObject->Implements<URigVMEditorSideObject>())
{
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(InEditorSideObject);
return GetModel(UserInterfaceGraph->GetRigVMNodePath());
}
return nullptr;
}
TArray<URigVMGraph*> FRigVMClient::GetAllModels(bool bIncludeFunctionLibrary, bool bRecursive) const
{
TArray<URigVMGraph*> 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<URigVMController>* Controller = Controllers.Find(Key))
{
return Controller->Get();
}
}
return nullptr;
}
URigVMController* FRigVMClient::GetController(const UObject* InEditorSideObject) const
{
check(InEditorSideObject);
if (InEditorSideObject->Implements<URigVMEditorSideObject>())
{
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(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<URigVMEditorSideObject>())
{
const IRigVMEditorSideObject* UserInterfaceGraph = Cast<IRigVMEditorSideObject>(InEditorSideObject);
return GetOrCreateController(UserInterfaceGraph->GetRigVMNodePath());
}
return nullptr;
}
URigVMGraph* FRigVMClient::AddModel(const FName& InName, bool bSetupUndoRedo, const FObjectInitializer* ObjectInitializer, bool bCreateController)
{
#if WITH_EDITOR
TSharedPtr<FScopedTransaction> Transaction;
if(bSetupUndoRedo)
{
Transaction = MakeShared<FScopedTransaction>(NSLOCTEXT("RigVMClient", "AddModel", "Add new root graph"));
GetOuter()->Modify();
}
#endif
const FName SafeGraphName = GetUniqueName(InName);
URigVMGraph* Model = nullptr;
if(ObjectInitializer)
{
Model = ObjectInitializer->CreateDefaultSubobject<URigVMGraph>(GetOuter(), SafeGraphName);
}
else
{
Model = NewObject<URigVMGraph>(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<URigVMFunctionLibrary>())
{
check(FunctionLibrary == nullptr);
FunctionLibrary = Cast<URigVMFunctionLibrary>(InModel);
}
else
{
Models.Add(InModel);
}
InModel->SetExecuteContextStruct(GetExecuteContextStruct());
if(bCreateController)
{
CreateController(InModel);
}
if(InModel->IsA<URigVMFunctionLibrary>())
{
for(URigVMGraph* Model : Models)
{
Model->SetDefaultFunctionLibrary(FunctionLibrary);
}
InModel->SetDefaultFunctionLibrary(FunctionLibrary);
}
else if(FunctionLibrary)
{
InModel->SetDefaultFunctionLibrary(FunctionLibrary);
}
if (GetOuter()->Implements<URigVMClientHost>())
{
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(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<FScopedTransaction> Transaction;
if(bSetupUndoRedo)
{
Transaction = MakeShared<FScopedTransaction>(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<URigVMFunctionLibrary>(GetOuter(), SafeGraphName);
}
else
{
NewFunctionLibrary = NewObject<URigVMFunctionLibrary>(GetOuter(), SafeGraphName);
}
NewFunctionLibrary->GetFunctionHostObjectPathDelegate.BindLambda([this]() -> const FSoftObjectPath
{
if (GetOuter()->Implements<URigVMClientHost>())
{
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
return Cast<UObject>(ClientHost->GetRigVMGraphFunctionHost());
}
return nullptr;
});
AddModel(NewFunctionLibrary, bCreateController);
return NewFunctionLibrary;
}
TArray<FName> FRigVMClient::GetEntryNames() const
{
TArray<FName> 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<FScopedTransaction> Transaction;
if(bSetupUndoRedo)
{
Transaction = MakeShared<FScopedTransaction>(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<URigVMClientHost>())
{
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(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<FScopedTransaction> Transaction;
if(bSetupUndoRedo)
{
Transaction = MakeShared<FScopedTransaction>(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<URigVMClientHost>())
{
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(GetOuter());
ClientHost->HandleRigVMGraphRenamed(this, OldNodePath, NewNodePath);
}
NotifyOuterOfPropertyChange();
return SafeNewName;
}
return NAME_None;
}
void FRigVMClient::PostTransacted(const FTransactionObjectEvent& TransactionEvent)
{
IRigVMClientHost* ClientHost = CastChecked<IRigVMClientHost>(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<URigVMController>* 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<URigVMController>(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<URigVMClientHost>())
{
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(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<URigVMGraph*> 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<URigVMGraph*> ModelsAndFunctionLibrary = GetAllModels(true, true);
for(const URigVMGraph* Model : ModelsAndFunctionLibrary)
{
Hash = HashCombine(Hash, Model->GetSerializedStructureHash());
}
return Hash;
}
FRigVMClientPatchResult FRigVMClient::PatchModelsOnLoad()
{
FRigVMClientPatchResult Result;
TArray<URigVMGraph*> AllModels = GetAllModels(true, true);
TGuardValue<bool> ClientIgnoreModificationsGuard(bIgnoreModelNotifications, true);
for(URigVMGraph* Model : AllModels)
{
URigVMController* Controller = GetOrCreateController(Model);
TGuardValue<bool> GuardSuspendTemplateComputation(Controller->bSuspendTemplateComputation, true);
TGuardValue<bool> 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<URigVMGraph*> 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<URigVMLibraryNode>(Node))
{
UpdateGraphFunctionSerializedGraph(LibraryNode);
}
}
}
void FRigVMClient::HandleGraphModifiedEvent(ERigVMGraphNotifType InNotifType, URigVMGraph* InGraph, UObject* InSubject)
{
if (bIgnoreModelNotifications)
{
return;
}
#if WITH_EDITOR
IRigVMClientHost* ClientHost = Cast<IRigVMClientHost>(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<URigVMFunctionLibrary>())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
{
if (GetOuter()->Implements<URigVMClientHost>())
{
FunctionStore->AddFunction(CollapseNode->GetFunctionHeader(FunctionHost), false);
}
}
}
// A node was added into the contained graph of a function
else if(URigVMLibraryNode* LibraryNode = Cast<URigVMNode>(InSubject)->FindFunctionForNode())
{
DirtyGraphFunctionCompilationData(LibraryNode);
if (URigVMFunctionReferenceNode* FunctionReference = Cast<URigVMFunctionReferenceNode>(InSubject))
{
UpdateDependenciesForFunction(LibraryNode);
UpdateExternalVariablesForFunction(LibraryNode);
}
}
break;
}
case ERigVMGraphNotifType::NodeRemoved: // A node has been removed from the graph (Subject == URigVMNode)
{
if(InSubject->GetOuter()->IsA<URigVMFunctionLibrary>())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
{
if (GetOuter()->Implements<URigVMClientHost>())
{
FunctionStore->RemoveFunction(FRigVMGraphFunctionIdentifier (Cast<UObject>(FunctionHost), CollapseNode));
}
}
}
// A node was added into the contained graph of a function
else if(URigVMLibraryNode* LibraryNode = Cast<URigVMNode>(InSubject)->FindFunctionForNode())
{
DirtyGraphFunctionCompilationData(LibraryNode);
if (URigVMFunctionReferenceNode* FunctionReference = Cast<URigVMFunctionReferenceNode>(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<URigVMNode>(InSubject)->FindFunctionForNode())
{
DirtyGraphFunctionCompilationData(LibraryNode);
UpdateExternalVariablesForFunction(LibraryNode);
}
break;
}
case ERigVMGraphNotifType::NodeRenamed: // A node has been renamed in the graph (Subject == URigVMNode)
{
if(InSubject->GetOuter()->IsA<URigVMFunctionLibrary>())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(InSubject))
{
URigVMBuildData* BuildData = URigVMBuildData::Get();
IRigVMGraphFunctionHost* Host = Cast<IRigVMGraphFunctionHost>(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; i<NewFunctionReferences.Num(); ++i)
{
NewFunctionReferences[i]->ReferencedFunctionHeader = 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<URigVMFunctionLibrary>())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(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<URigVMNode>(InSubject->GetOuter()))
{
if (URigVMLibraryNode* LibraryNode = Node->FindFunctionForNode())
{
DirtyGraphFunctionCompilationData(LibraryNode);
}
if(Node->GetOuter()->IsA<URigVMFunctionLibrary>())
{
if (URigVMCollapseNode* CollapseNode = Cast<URigVMCollapseNode>(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<URigVMLink>(InSubject))
{
if (URigVMNode* OuterNode = Cast<URigVMNode>(Link->GetGraph()->GetOuter()))
{
if (URigVMLibraryNode* LibraryNode = OuterNode->FindFunctionForNode())
{
DirtyGraphFunctionCompilationData(LibraryNode);
}
}
}
break;
}
case ERigVMGraphNotifType::FunctionAccessChanged: // A function was made public/private
{
if (URigVMLibraryNode* LibraryNode = Cast<URigVMLibraryNode>(InSubject))
{
if (URigVMFunctionLibrary* Library = Cast<URigVMFunctionLibrary>(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<IRigVMClientHost>())
{
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; i<FunctionReferenceArray->Num(); ++i)
{
const TSoftObjectPtr<URigVMFunctionReferenceNode>& 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<IRigVMClientHost>();
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<URigVMClientHost>())
{
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<FRigVMGraphFunctionIdentifier, uint32> 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;
}