void FBlueprintStatsModule::DumpBlueprintStats() { TArray<FBlueprintStatRecord> Records; for (TObjectIterator<UBlueprint> BlueprintIt; BlueprintIt; ++BlueprintIt) { UBlueprint* Blueprint = *BlueprintIt; new (Records) FBlueprintStatRecord(Blueprint); } // Now merge them FBlueprintStatRecord Aggregate(NULL); for (const FBlueprintStatRecord& SourceRecord : Records) { Aggregate.MergeAnotherRecordIn(SourceRecord); } // Sort the lists Aggregate.NodeCount.ValueSort(TGreater<int32>()); Aggregate.FunctionCount.ValueSort(TGreater<int32>()); Aggregate.FunctionOwnerCount.ValueSort(TGreater<int32>()); Aggregate.RemoteMacroCount.ValueSort(TGreater<int32>()); // Print out the merged record UE_LOG(LogBlueprintStats, Log, TEXT("Blueprint stats for %d blueprints in %s"), Records.Num(), GGameName); UE_LOG(LogBlueprintStats, Log, TEXT("%s"), *Aggregate.ToString(true)); UE_LOG(LogBlueprintStats, Log, TEXT("%s"), *Aggregate.ToString(false)); UE_LOG(LogBlueprintStats, Log, TEXT("\n")); // Print out the node list UE_LOG(LogBlueprintStats, Log, TEXT("NodeClass,NumInstances")); for (const auto& NodePair : Aggregate.NodeCount) { UE_LOG(LogBlueprintStats, Log, TEXT("%s,%d"), *(NodePair.Key->GetName()), NodePair.Value); } UE_LOG(LogBlueprintStats, Log, TEXT("\n")); // Print out the function list UE_LOG(LogBlueprintStats, Log, TEXT("FunctionPath,ClassName,FunctionName,NumInstances")); for (const auto& FunctionPair : Aggregate.FunctionCount) { UFunction* Function = FunctionPair.Key; UE_LOG(LogBlueprintStats, Log, TEXT("%s,%s,%s,%d"), *(Function->GetPathName()), *(Function->GetOuterUClass()->GetName()), *(Function->GetName()), FunctionPair.Value); } UE_LOG(LogBlueprintStats, Log, TEXT("\n")); // Print out the macro list UE_LOG(LogBlueprintStats, Log, TEXT("MacroPath,MacroName,NumInstances")); for (const auto& MacroPair : Aggregate.RemoteMacroCount) { UEdGraph* MacroGraph = MacroPair.Key; UE_LOG(LogBlueprintStats, Log, TEXT("%s,%s,%d"), *(MacroGraph->GetPathName()), *(MacroGraph->GetName()), MacroPair.Value); } UE_LOG(LogBlueprintStats, Log, TEXT("\n")); }
static void SetNodeFunc(UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr<UFunction> FunctionPtr) { UK2Node_LatentGameplayTaskCall* AsyncTaskNode = CastChecked<UK2Node_LatentGameplayTaskCall>(NewNode); if (FunctionPtr.IsValid()) { UFunction* Func = FunctionPtr.Get(); UObjectProperty* ReturnProp = CastChecked<UObjectProperty>(Func->GetReturnProperty()); AsyncTaskNode->ProxyFactoryFunctionName = Func->GetFName(); AsyncTaskNode->ProxyFactoryClass = Func->GetOuterUClass(); AsyncTaskNode->ProxyClass = ReturnProp->PropertyClass; } }
void UK2Node_Switch::CreateFunctionPin() { // Set properties on the function pin const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>(); UEdGraphPin* FunctionPin = CreatePin(EGPD_Input, K2Schema->PC_Object, TEXT(""), FunctionClass, false, false, FunctionName.ToString()); FunctionPin->bDefaultValueIsReadOnly = true; FunctionPin->bNotConnectable = true; FunctionPin->bHidden = true; UFunction* Function = FindField<UFunction>(FunctionClass, FunctionName); const bool bIsStaticFunc = Function->HasAllFunctionFlags(FUNC_Static); if (bIsStaticFunc) { // Wire up the self to the CDO of the class if it's not us if (UBlueprint* BP = GetBlueprint()) { UClass* FunctionOwnerClass = Function->GetOuterUClass(); if (!BP->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass)) { FunctionPin->DefaultObject = FunctionOwnerClass->GetDefaultObject(); } } } }
void UK2Node_LatentAbilityCall::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { // these nested loops are combing over the same classes/functions the // FBlueprintActionDatabase does; ideally we save on perf and fold this in // with FBlueprintActionDatabase, but we want to keep the modules separate for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* Class = *ClassIt; if (!Class->IsChildOf<UAbilityTask>() || Class->HasAnyClassFlags(CLASS_Abstract)) { continue; } for (TFieldIterator<UFunction> FuncIt(Class, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt) { UFunction* Function = *FuncIt; if (!Function->HasAnyFunctionFlags(FUNC_Static)) { continue; } // to keep from needlessly instantiating a UBlueprintNodeSpawner, first // check to make sure that the registrar is looking for actions of this type // (could be regenerating actions for a specific asset, and therefore the // registrar would only accept actions corresponding to that asset) if (!ActionRegistrar.IsOpenForRegistration(Function)) { continue; } UObjectProperty* ReturnProperty = Cast<UObjectProperty>(Function->GetReturnProperty()); // see if the function is a static factory method for online proxies bool const bIsProxyFactoryMethod = (ReturnProperty != nullptr) && ReturnProperty->PropertyClass->IsChildOf<UAbilityTask>(); if (bIsProxyFactoryMethod) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintFunctionNodeSpawner::Create(Function); check(NodeSpawner != nullptr); NodeSpawner->NodeClass = GetClass(); auto CustomizeAcyncNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr<UFunction> FunctionPtr) { UK2Node_LatentAbilityCall* AsyncTaskNode = CastChecked<UK2Node_LatentAbilityCall>(NewNode); if (FunctionPtr.IsValid()) { UFunction* Func = FunctionPtr.Get(); UObjectProperty* ReturnProp = CastChecked<UObjectProperty>(Func->GetReturnProperty()); AsyncTaskNode->ProxyFactoryFunctionName = Func->GetFName(); AsyncTaskNode->ProxyFactoryClass = Func->GetOuterUClass(); AsyncTaskNode->ProxyClass = ReturnProp->PropertyClass; } }; TWeakObjectPtr<UFunction> FunctionPtr = Function; NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeAcyncNodeLambda, FunctionPtr); // @TODO: since this can't be folded into FBlueprintActionDatabase, we // need a way to associate these spawners with a certain class ActionRegistrar.AddBlueprintAction(Function, NodeSpawner); } } } }
void UK2Node_Message::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UEdGraphPin* ExecPin = Schema->FindExecutionPin(*this, EGPD_Input); const bool bExecPinConnected = ExecPin && (ExecPin->LinkedTo.Num() > 0); UEdGraphPin* ThenPin = Schema->FindExecutionPin(*this, EGPD_Output); const bool bThenPinConnected = ThenPin && (ThenPin->LinkedTo.Num() > 0); // Skip ourselves if our exec isn't wired up if (bExecPinConnected) { // Make sure our interface is valid if (FunctionReference.GetMemberParentClass(this) == NULL) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalid_Error", "Message node @@ has an invalid interface.").ToString()), this); return; } UFunction* MessageNodeFunction = GetTargetFunction(); if (MessageNodeFunction == NULL) { //@TODO: Why do this here in teh compiler, it's already done on AllocateDefaultPins() during on-load node reconstruction MessageNodeFunction = Cast<UFunction>(UK2Node::FindRemappedField(FunctionReference.GetMemberParentClass(this), FunctionReference.GetMemberName())); } if (MessageNodeFunction == NULL) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeInvalidFunction_Error", "Unable to find function with name %s for Message node @@.").ToString(), *(FunctionReference.GetMemberName().ToString())), this); return; } // Check to make sure we have a target UEdGraphPin* MessageSelfPin = Schema->FindSelfPin(*this, EGPD_Input); if( !MessageSelfPin || MessageSelfPin->LinkedTo.Num() == 0 ) { CompilerContext.MessageLog.Error(*FString::Printf(*LOCTEXT("MessageNodeSelfPin_Error", "Message node @@ must have a valid target or reference to self.").ToString()), this); return; } // First, create an internal cast-to-interface node UK2Node_DynamicCast* CastToInterfaceNode = CompilerContext.SpawnIntermediateNode<UK2Node_DynamicCast>(this, SourceGraph); CastToInterfaceNode->TargetType = MessageNodeFunction->GetOuterUClass(); CastToInterfaceNode->SetPurity(false); CastToInterfaceNode->AllocateDefaultPins(); UEdGraphPin* CastToInterfaceResultPin = CastToInterfaceNode->GetCastResultPin(); if( !CastToInterfaceResultPin ) { CompilerContext.MessageLog.Error(*LOCTEXT("InvalidInterfaceClass_Error", "Node @@ has an invalid target interface class").ToString(), this); return; } CastToInterfaceResultPin->PinType.PinSubCategoryObject = *CastToInterfaceNode->TargetType; if (ExecPin != nullptr) { UEdGraphPin* CastExecInput = CastToInterfaceNode->GetExecPin(); check(CastExecInput != nullptr); // Wire up the connections CompilerContext.MovePinLinksToIntermediate(*ExecPin, *CastExecInput); } UEdGraphPin* CastToInterfaceSourceObjectPin = CastToInterfaceNode->GetCastSourcePin(); CompilerContext.MovePinLinksToIntermediate(*MessageSelfPin, *CastToInterfaceSourceObjectPin); // Next, create the function call node UK2Node_CallFunction* FunctionCallNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph); FunctionCallNode->bIsInterfaceCall = true; FunctionCallNode->FunctionReference = FunctionReference; FunctionCallNode->AllocateDefaultPins(); UEdGraphPin* CastToInterfaceValidPin = CastToInterfaceNode->GetValidCastPin(); check(CastToInterfaceValidPin != nullptr); UEdGraphPin* LastOutCastFaildPin = CastToInterfaceNode->GetInvalidCastPin(); check(LastOutCastFaildPin != nullptr); UEdGraphPin* LastOutCastSuccessPin = CastToInterfaceValidPin; // Wire up the connections if (UEdGraphPin* CallFunctionExecPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Input)) { CastToInterfaceValidPin->MakeLinkTo(CallFunctionExecPin); LastOutCastSuccessPin = Schema->FindExecutionPin(*FunctionCallNode, EGPD_Output); } // Self pin UEdGraphPin* FunctionCallSelfPin = Schema->FindSelfPin(*FunctionCallNode, EGPD_Input); CastToInterfaceResultPin->MakeLinkTo(FunctionCallSelfPin); UFunction* ArrayClearFunction = UKismetArrayLibrary::StaticClass()->FindFunctionByName(FName(TEXT("Array_Clear"))); check(ArrayClearFunction); bool const bIsPureFunc = Super::IsNodePure(); // Variable pins - Try to associate variable inputs to the message node with the variable inputs and outputs to the call function node for( int32 i = 0; i < Pins.Num(); i++ ) { UEdGraphPin* CurrentPin = Pins[i]; if( CurrentPin && (CurrentPin->PinType.PinCategory != Schema->PC_Exec) && (CurrentPin->PinName != Schema->PN_Self) ) { // Try to find a match for the pin on the function call node UEdGraphPin* FunctionCallPin = FunctionCallNode->FindPin(CurrentPin->PinName); if( FunctionCallPin ) { // Move pin links if the pin is connected... CompilerContext.MovePinLinksToIntermediate(*CurrentPin, *FunctionCallPin); // when cast fails all return values must be cleared. if (EEdGraphPinDirection::EGPD_Output == CurrentPin->Direction) { UEdGraphPin* VarOutPin = FunctionCallPin; if (bIsPureFunc) { // since we cannot directly use the output from the // function call node (since it is pure, and invoking // it with a null target would cause an error), we // have to use a temporary variable in it's place... UK2Node_TemporaryVariable* TempVar = CompilerContext.SpawnIntermediateNode<UK2Node_TemporaryVariable>(this, SourceGraph); TempVar->VariableType = CurrentPin->PinType; TempVar->AllocateDefaultPins(); VarOutPin = TempVar->GetVariablePin(); // nodes using the function's outputs directly, now // use this TempVar node instead CompilerContext.MovePinLinksToIntermediate(*FunctionCallPin, *VarOutPin); // on a successful cast, the temp var is filled with // the function's value, on a failed cast, the var // is filled with a default value (DefaultValueNode, // below)... this is the node for the success case: UK2Node_AssignmentStatement* AssignTempVar = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); AssignTempVar->AllocateDefaultPins(); // assign the output from the pure function node to // the TempVar (either way, this message node is // returning the TempVar's value, so on a successful // cast we want it to have the function's result) UEdGraphPin* ValueInPin = AssignTempVar->GetValuePin(); Schema->TryCreateConnection(FunctionCallPin, ValueInPin); AssignTempVar->PinConnectionListChanged(ValueInPin); UEdGraphPin* VarInPin = AssignTempVar->GetVariablePin(); Schema->TryCreateConnection(VarOutPin, VarInPin); AssignTempVar->PinConnectionListChanged(VarInPin); // fold this AssignTempVar node into the cast's // success execution chain Schema->TryCreateConnection(AssignTempVar->GetExecPin(), LastOutCastSuccessPin); LastOutCastSuccessPin = AssignTempVar->GetThenPin(); } UK2Node* DefaultValueNode = NULL; UEdGraphPin* DefaultValueThenPin = NULL; if (CurrentPin->PinType.bIsArray) { UK2Node_CallArrayFunction* ClearArray = CompilerContext.SpawnIntermediateNode<UK2Node_CallArrayFunction>(this, SourceGraph); DefaultValueNode = ClearArray; ClearArray->SetFromFunction(ArrayClearFunction); ClearArray->AllocateDefaultPins(); UEdGraphPin* ArrayPin = ClearArray->GetTargetArrayPin(); check(ArrayPin); Schema->TryCreateConnection(ArrayPin, VarOutPin); ClearArray->PinConnectionListChanged(ArrayPin); DefaultValueThenPin = ClearArray->GetThenPin(); } else { UK2Node_AssignmentStatement* AssignDefaultValue = CompilerContext.SpawnIntermediateNode<UK2Node_AssignmentStatement>(this, SourceGraph); DefaultValueNode = AssignDefaultValue; AssignDefaultValue->AllocateDefaultPins(); Schema->TryCreateConnection(AssignDefaultValue->GetVariablePin(), VarOutPin); AssignDefaultValue->PinConnectionListChanged(AssignDefaultValue->GetVariablePin()); Schema->SetPinDefaultValueBasedOnType(AssignDefaultValue->GetValuePin()); DefaultValueThenPin = AssignDefaultValue->GetThenPin(); } UEdGraphPin* DefaultValueExecPin = DefaultValueNode->GetExecPin(); check(DefaultValueExecPin); Schema->TryCreateConnection(DefaultValueExecPin, LastOutCastFaildPin); LastOutCastFaildPin = DefaultValueThenPin; check(LastOutCastFaildPin); } } else { UE_LOG(LogK2Compiler, Log, TEXT("%s"), *LOCTEXT("NoPinConnectionFound_Error", "Unable to find connection for pin! Check AllocateDefaultPins() for consistency!").ToString()); } } } if( bThenPinConnected ) { check(LastOutCastFaildPin != nullptr); // Failure case for the cast runs straight through to the exit CompilerContext.CopyPinLinksToIntermediate(*ThenPin, *LastOutCastFaildPin); check(LastOutCastSuccessPin != nullptr); // Copy all links from the invalid cast case above to the call function node CompilerContext.MovePinLinksToIntermediate(*ThenPin, *LastOutCastSuccessPin); } } // Break all connections to the original node, so it will be pruned BreakAllNodeLinks(); }