void SFindInBT::MatchTokens(const TArray<FString>& Tokens) { RootSearchResult.Reset(); TWeakPtr<SGraphEditor> FocusedGraphEditor = BehaviorTreeEditorPtr.Pin()->GetFocusedGraphPtr(); UEdGraph* Graph = NULL; if (FocusedGraphEditor.IsValid()) { Graph = FocusedGraphEditor.Pin()->GetCurrentGraph(); } if (Graph == NULL) { return; } RootSearchResult = FSearchResult(new FFindInBTResult(FString("BehaviorTreeRoot"))); for (auto It(Graph->Nodes.CreateConstIterator()); It; ++It) { UEdGraphNode* Node = *It; const FString NodeName = Node->GetNodeTitle(ENodeTitleType::ListView).ToString(); FSearchResult NodeResult(new FFindInBTResult(NodeName, RootSearchResult, Node)); FString NodeSearchString = NodeName + Node->GetClass()->GetName() + Node->NodeComment; NodeSearchString = NodeSearchString.Replace(TEXT(" "), TEXT("")); bool bNodeMatchesSearch = StringMatchesSearchTokens(Tokens, NodeSearchString); UBehaviorTreeGraphNode* BTNode = Cast<UBehaviorTreeGraphNode>(Node); if (BTNode) { // searching through nodes' decorators for (auto DecoratorIt(BTNode->Decorators.CreateConstIterator()); DecoratorIt; ++DecoratorIt) { UBehaviorTreeGraphNode* Decorator = *DecoratorIt; MatchTokensInChild(Tokens, Decorator, NodeResult); } // searching through nodes' services for (auto ServiceIt(BTNode->Services.CreateConstIterator()); ServiceIt; ++ServiceIt) { UBehaviorTreeGraphNode* Service = *ServiceIt; MatchTokensInChild(Tokens, Service, NodeResult); } } if ((NodeResult->Children.Num() > 0) || bNodeMatchesSearch) { NodeResult->SetNodeHighlight(true); ItemsFound.Add(NodeResult); } } }
void SFindInMaterial::MatchTokens(const TArray<FString> &Tokens) { RootSearchResult.Reset(); UEdGraph* Graph = MaterialEditorPtr.Pin()->Material->MaterialGraph; if (Graph == NULL) { return; } RootSearchResult = FSearchResult(new FFindInMaterialResult(FString("BehaviorTreeRoot"))); for (auto It(Graph->Nodes.CreateConstIterator()); It; ++It) { UEdGraphNode* Node = *It; const FString NodeName = Node->GetNodeTitle(ENodeTitleType::ListView).ToString(); FSearchResult NodeResult(new FFindInMaterialResult(NodeName, RootSearchResult, Node)); FString NodeSearchString = NodeName + Node->NodeComment; NodeSearchString = NodeSearchString.Replace(TEXT(" "), TEXT("")); bool bNodeMatchesSearch = StringMatchesSearchTokens(Tokens, NodeSearchString); // Use old Material Expression search functions too if (!bNodeMatchesSearch) { bool bMatchesAllTokens = true; if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node)) { for (int32 Index = 0; Index < Tokens.Num(); ++Index) { if (!MatNode->MaterialExpression->MatchesSearchQuery(*Tokens[Index])) { bMatchesAllTokens = false; break; } } } else if (UMaterialGraphNode_Comment* MatComment = Cast<UMaterialGraphNode_Comment>(Node)) { for (int32 Index = 0; Index < Tokens.Num(); ++Index) { if (!MatComment->MaterialExpressionComment->MatchesSearchQuery(*Tokens[Index])) { bMatchesAllTokens = false; break; } } } else { bMatchesAllTokens = false; } if (bMatchesAllTokens) { bNodeMatchesSearch = true; } } for (TArray<UEdGraphPin*>::TIterator PinIt(Node->Pins); PinIt; ++PinIt) { UEdGraphPin* Pin = *PinIt; if (Pin && Pin->PinFriendlyName.CompareTo(FText::FromString(TEXT(" "))) != 0) { FText PinName = Pin->GetSchema()->GetPinDisplayName(Pin); FString PinSearchString = Pin->PinName + Pin->PinFriendlyName.ToString() + Pin->DefaultValue + Pin->PinType.PinCategory + Pin->PinType.PinSubCategory + (Pin->PinType.PinSubCategoryObject.IsValid() ? Pin->PinType.PinSubCategoryObject.Get()->GetFullName() : TEXT("")); PinSearchString = PinSearchString.Replace(TEXT(" "), TEXT("")); if (StringMatchesSearchTokens(Tokens, PinSearchString)) { FSearchResult PinResult(new FFindInMaterialResult(PinName.ToString(), NodeResult, Pin)); NodeResult->Children.Add(PinResult); } } } if ((NodeResult->Children.Num() > 0) || bNodeMatchesSearch) { ItemsFound.Add(NodeResult); } } }
void FBlueprintSpawnNodeCommands::RegisterCommands() { const FString ConfigSection = TEXT("BlueprintSpawnNodes"); const FString SettingName = TEXT("Node"); TArray< FString > NodeSpawns; GConfig->GetArray(*ConfigSection, *SettingName, NodeSpawns, GEditorPerProjectIni); for(int32 x = 0; x < NodeSpawns.Num(); ++x) { FString ClassName; if(!FParse::Value(*NodeSpawns[x], TEXT("Class="), ClassName)) { // Could not find a class name, cannot continue with this line continue; } FString CommandLabel; UClass* FoundClass = FindObject<UClass>(ANY_PACKAGE, *ClassName, true); TSharedPtr< FNodeSpawnInfo > InfoPtr; if(FoundClass && FoundClass->IsChildOf(UEdGraphNode::StaticClass())) { // The class name matches that of a UEdGraphNode, so setup a spawn info that can generate UEdGraphNode graph actions UEdGraphNode* GraphNode = Cast<UEdGraphNode>(FoundClass->GetDefaultObject()); CommandLabel = GraphNode->GetNodeTitle(ENodeTitleType::ListView).ToString(); if(CommandLabel.Len() == 0) { CommandLabel = FoundClass->GetName(); } InfoPtr = MakeShareable( new FEdGraphNodeSpawnInfo( FoundClass ) ); } else if(UFunction* FoundFunction = FindObject<UFunction>(ANY_PACKAGE, *ClassName, true)) { // The class name matches that of a function, so setup a spawn info that can generate function graph actions InfoPtr = MakeShareable( new FFunctionNodeSpawnInfo((UFunction*)FoundFunction)); CommandLabel = FoundFunction->GetName(); } else { // Check for a macro graph that matches the passed in class name for (TObjectIterator<UBlueprint> BlueprintIt; BlueprintIt; ++BlueprintIt) { UBlueprint* MacroBP = *BlueprintIt; if(MacroBP->BlueprintType == BPTYPE_MacroLibrary) { // getting 'top-level' of the macros for (TArray<UEdGraph*>::TIterator GraphIt(MacroBP->MacroGraphs); GraphIt; ++GraphIt) { UEdGraph* MacroGraph = *GraphIt; // The class name matches that of a macro, so setup a spawn info that can generate macro graph actions if(MacroGraph->GetName() == ClassName) { CommandLabel = MacroGraph->GetName(); InfoPtr = MakeShareable( new FMacroNodeSpawnInfo(MacroGraph)); } } } } } // If spawn info was created, setup a UI Command for keybinding. if(InfoPtr.IsValid()) { TSharedPtr< FUICommandInfo > CommandInfo; FKey Key; bool bShift = false; bool bCtrl = false; bool bAlt = false; bool bCmd = false; // Parse the keybinding information FString KeyString; if( FParse::Value(*NodeSpawns[x], TEXT("Key="), KeyString) ) { Key = *KeyString; } if( Key.IsValid() ) { FParse::Bool(*NodeSpawns[x], TEXT("Shift="), bShift); FParse::Bool(*NodeSpawns[x], TEXT("Alt="), bAlt); FParse::Bool(*NodeSpawns[x], TEXT("Ctrl="), bCtrl); FParse::Bool(*NodeSpawns[x], TEXT("Cmd="), bCmd); } FInputChord Chord(Key, EModifierKey::FromBools(bCtrl, bAlt, bShift, bCmd)); FText CommandLabelText = FText::FromString( CommandLabel ); FText Description = FText::Format( NSLOCTEXT("BlueprintEditor", "NodeSpawnDescription", "Hold down the bound keys and left click in the graph panel to spawn a {0} node."), CommandLabelText ); FUICommandInfo::MakeCommandInfo( this->AsShared(), CommandInfo, FName(*NodeSpawns[x]), CommandLabelText, Description, FSlateIcon(FEditorStyle::GetStyleSetName(), *FString::Printf(TEXT("%s.%s"), *this->GetContextName().ToString(), *NodeSpawns[x])), EUserInterfaceActionType::Button, Chord ); InfoPtr->CommandInfo = CommandInfo; NodeCommands.Add(InfoPtr); } } TSharedPtr<FNodeSpawnInfo> AddActorRefAction = MakeShareable(new FActorRefSpawnInfo); UI_COMMAND(AddActorRefAction->CommandInfo, "Add Selected Actor Reference(s)", "Spawns node(s) which reference the currently selected actor(s) in the level.", EUserInterfaceActionType::Button, FInputChord(EKeys::R)); NodeCommands.Add(AddActorRefAction); }
/** Create a tokenized message record from a message containing @@ indicating where each UObject* in the ArgPtr list goes and place it in the MessageLog. */ void FCompilerResultsLog::InternalLogMessage(const EMessageSeverity::Type& Severity, const TCHAR* Message, va_list ArgPtr) { UEdGraphNode* OwnerNode = nullptr; // Create the tokenized message TSharedRef<FTokenizedMessage> Line = FTokenizedMessage::Create( Severity ); Messages.Add(Line); const TCHAR* DelimiterStr = TEXT("@@"); int32 DelimLength = FCString::Strlen(DelimiterStr); const TCHAR* Start = Message; if (Start && DelimLength) { while (const TCHAR* At = FCString::Strstr(Start, DelimiterStr)) { // Found a delimiter, create a token from the preceding text Line->AddToken( FTextToken::Create( FText::FromString( FString(At - Start, Start) ) ) ); Start += DelimLength + (At - Start); // And read the object and add another token for the object UObject* ObjectArgument = va_arg(ArgPtr, UObject*); FText ObjText; if (ObjectArgument) { // Remap object references to the source nodes ObjectArgument = FindSourceObject(ObjectArgument); if (ObjectArgument) { UEdGraphNode* Node = Cast<UEdGraphNode>(ObjectArgument); const UEdGraphPin* Pin = (Node? nullptr : Cast<UEdGraphPin>(ObjectArgument)); //Get owner node reference, consider the first if (OwnerNode == nullptr) { OwnerNode = (Pin ? Pin->GetOwningNodeUnchecked() : Node); } if (ObjectArgument->GetOutermost() == GetTransientPackage()) { ObjText = LOCTEXT("Transient", "(transient)"); } else if (Node != NULL) { ObjText = Node->GetNodeTitle(ENodeTitleType::ListView); } else if (Pin != NULL) { ObjText = Pin->GetDisplayName(); } else { ObjText = FText::FromString( ObjectArgument->GetName() ); } } else { ObjText = LOCTEXT("None", "(none)"); } } else { ObjText = LOCTEXT("None", "(none)"); } Line->AddToken( FUObjectToken::Create( ObjectArgument, ObjText ) ); } Line->AddToken( FTextToken::Create( FText::FromString( Start ) ) ); } va_end(ArgPtr); // Register node error/warning. AnnotateNode(OwnerNode, Line); if( !bSilentMode && (!bLogInfoOnly || (Severity == EMessageSeverity::Info)) ) { if(Severity == EMessageSeverity::CriticalError || Severity == EMessageSeverity::Error) { UE_LOG(LogBlueprint, Error, TEXT("[compiler] %s"), *Line->ToText().ToString()); } else if(Severity == EMessageSeverity::Warning || Severity == EMessageSeverity::PerformanceWarning) { UE_LOG(LogBlueprint, Warning, TEXT("[compiler] %s"), *Line->ToText().ToString()); } else { UE_LOG(LogBlueprint, Log, TEXT("[compiler] %s"), *Line->ToText().ToString()); } } }
void FKismetConnectionDrawingPolicy::BuildExecutionRoadmap() { LatestTimeDiscovered = 0.0; // Only do highlighting in PIE or SIE if (!CanBuildRoadmap()) { return; } UBlueprint* TargetBP = FBlueprintEditorUtils::FindBlueprintForGraphChecked(GraphObj); UObject* ActiveObject = TargetBP->GetObjectBeingDebugged(); check(ActiveObject); // Due to CanBuildRoadmap // Redirect the target Blueprint when debugging with a macro graph visible if (TargetBP->BlueprintType == BPTYPE_MacroLibrary) { TargetBP = Cast<UBlueprint>(ActiveObject->GetClass()->ClassGeneratedBy); } TArray<UEdGraphNode*> SequentialNodesInGraph; TArray<double> SequentialNodeTimes; TArray<UEdGraphPin*> SequentialExecPinsInGraph; { const TSimpleRingBuffer<FKismetTraceSample>& TraceStack = FKismetDebugUtilities::GetTraceStack(); UBlueprintGeneratedClass* TargetClass = Cast<UBlueprintGeneratedClass>(TargetBP->GeneratedClass); FBlueprintDebugData& DebugData = TargetClass->GetDebugData(); for (int32 i = 0; i < TraceStack.Num(); ++i) { const FKismetTraceSample& Sample = TraceStack(i); if (UObject* TestObject = Sample.Context.Get()) { if (TestObject == ActiveObject) { UEdGraphPin* AssociatedPin = DebugData.FindExecPinFromCodeLocation(Sample.Function.Get(), Sample.Offset); if (UEdGraphNode* Node = DebugData.FindSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset, /*bAllowImpreciseHit=*/ false)) { if (GraphObj == Node->GetGraph()) { SequentialNodesInGraph.Add(Node); SequentialNodeTimes.Add(Sample.ObservationTime); SequentialExecPinsInGraph.Add(AssociatedPin); } else { // If the top-level source node is a macro instance node UK2Node_MacroInstance* MacroInstanceNode = Cast<UK2Node_MacroInstance>(Node); if (MacroInstanceNode) { // Attempt to locate the macro source node through the code mapping UEdGraphNode* MacroSourceNode = DebugData.FindMacroSourceNodeFromCodeLocation(Sample.Function.Get(), Sample.Offset); if (MacroSourceNode) { // If the macro source node is located in the current graph context if (GraphObj == MacroSourceNode->GetGraph()) { // Add it to the sequential node list SequentialNodesInGraph.Add(MacroSourceNode); SequentialNodeTimes.Add(Sample.ObservationTime); SequentialExecPinsInGraph.Add(AssociatedPin); } else { // The macro source node isn't in the current graph context, but we might have a macro instance node that is // in the current graph context, so obtain the set of macro instance nodes that are mapped to the code here. TArray<UEdGraphNode*> MacroInstanceNodes; DebugData.FindMacroInstanceNodesFromCodeLocation(Sample.Function.Get(), Sample.Offset, MacroInstanceNodes); // For each macro instance node in the set for (auto MacroInstanceNodeIt = MacroInstanceNodes.CreateConstIterator(); MacroInstanceNodeIt; ++MacroInstanceNodeIt) { // If the macro instance node is located in the current graph context MacroInstanceNode = Cast<UK2Node_MacroInstance>(*MacroInstanceNodeIt); if (MacroInstanceNode && GraphObj == MacroInstanceNode->GetGraph()) { // Add it to the sequential node list SequentialNodesInGraph.Add(MacroInstanceNode); SequentialNodeTimes.Add(Sample.ObservationTime); SequentialExecPinsInGraph.Add(AssociatedPin); // Exit the loop; we're done break; } } } } } } } } } } } // Run thru and apply bonus time const float InvNumNodes = 1.0f / (float)SequentialNodeTimes.Num(); for (int32 i = 0; i < SequentialNodesInGraph.Num(); ++i) { double& ObservationTime = SequentialNodeTimes[i]; const float PositionRatio = (SequentialNodeTimes.Num() - i) * InvNumNodes; const float PositionBonus = FMath::Pow(PositionRatio, TracePositionExponent) * TracePositionBonusPeriod; ObservationTime += PositionBonus; LatestTimeDiscovered = FMath::Max<double>(LatestTimeDiscovered, ObservationTime); } UEdGraphPin* LastExecPin = NULL; // Record the unique exec-pin to time pairings, keeping only the most recent // times for each pairing... reverse the "SequentialNodes" because right now // it is in stack order (with the last executed node first) for (int32 i = SequentialNodesInGraph.Num() - 1; i >= 1; --i) { UEdGraphNode* CurNode = SequentialNodesInGraph[i]; UEdGraphNode* NextNode = SequentialNodesInGraph[i-1]; // keep track of the last exec-pin executed by CurNode (these tracked // pins coincide with "WireTraceSite" op-codes that have been injected // prior to every "goto" statement... this way we have context for which // pin executed the jump) if (UEdGraphPin* AssociatedPin = SequentialExecPinsInGraph[i]) { LastExecPin = AssociatedPin; } // if this statement is a jump (from one node to another) if (CurNode != NextNode) { // if there was a wire-trace op-code inserted before this jump if (LastExecPin != NULL) { //ensure(LastExecPin->GetOwningNode() == CurNode); double NextNodeTime = SequentialNodeTimes[i-1]; FExecPairingMap& ExecPaths = PredecessorPins.FindOrAdd(NextNode); FTimePair& ExecTiming = ExecPaths.FindOrAdd(LastExecPin); // make sure that if we've already visited this exec-pin (like // in a for-loop or something), that we're replacing it with a // more recent execution time // // @TODO I don't see when this wouldn't be the case if (ExecTiming.ThisExecTime < NextNodeTime) { double CurNodeTime = SequentialNodeTimes[i]; ExecTiming.ThisExecTime = NextNodeTime; ExecTiming.PredExecTime = CurNodeTime; } } // if the nodes aren't graphically connected how could they be // executed back-to-back? well, this could be a pop back to a // sequence node from the end of one thread of execution, etc. else if (AreNodesGraphicallySequential(CurNode, NextNode)) { // only warn when the nodes are directly connected (this is all // for execution flow visualization after all) UE_LOG(LogConnectionDrawingPolicy, Warning, TEXT("Looks like a wire-trace was not injected before the jump from '%s' to '%s'."), *CurNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString(), *NextNode->GetNodeTitle(ENodeTitleType::FullTitle).ToString()); } // clear the exec-pin (we're moving to a new node and want to find // it's executed out pin) LastExecPin = NULL; } // else, we're only collecting this data for tracing node-to-node // executions (so we don't care about this sequence of statements) } // Fade only when free-running (since we're using FApp::GetCurrentTime(), instead of FPlatformTime::Seconds) const double MaxTimeAhead = FMath::Min(FApp::GetCurrentTime() + 2*TracePositionBonusPeriod, LatestTimeDiscovered); //@TODO: Rough clamping; should be exposed as a parameter CurrentTime = FMath::Max(FApp::GetCurrentTime(), MaxTimeAhead); }