示例#1
0
void FBuildPatchAppManifest::EnumerateChunkPartInventory(const TArray<FGuid>& ChunksRequired, TMap<FGuid, TArray<FFileChunkPart>>& ChunkPartsAvailable) const
{
	ChunkPartsAvailable.Empty();
	// Use a set to optimize
	TSet<FGuid> ChunksReqSet(ChunksRequired);
	// For each file in the manifest, check what chunks it is made out of, and grab details for the ones in ChunksRequired
	for (auto FileManifestIt = Data->FileManifestList.CreateConstIterator(); FileManifestIt && !FBuildPatchInstallError::HasFatalError(); ++FileManifestIt)
	{
		const FFileManifestData& FileManifest = *FileManifestIt;
		uint64 FileOffset = 0;
		for (auto ChunkPartIt = FileManifest.FileChunkParts.CreateConstIterator(); ChunkPartIt && !FBuildPatchInstallError::HasFatalError(); ++ChunkPartIt)
		{
			const FChunkPartData& ChunkPart = *ChunkPartIt;
			if (ChunksReqSet.Contains(ChunkPart.Guid))
			{
				TArray<FFileChunkPart>& FileChunkParts = ChunkPartsAvailable.FindOrAdd(ChunkPart.Guid);
				FFileChunkPart FileChunkPart;
				FileChunkPart.Filename = FileManifest.Filename;
				FileChunkPart.ChunkPart = ChunkPart;
				FileChunkPart.FileOffset = FileOffset;
				FileChunkParts.Add(FileChunkPart);
			}
			FileOffset += ChunkPart.Size;
		}
	}
}
bool FChunkManifestGenerator::LoadAssetRegistry(const FString& SandboxPath, const TSet<FName>* PackagesToKeep)
{
	UE_LOG(LogChunkManifestGenerator, Display, TEXT("Loading asset registry."));

	// Load generated registry for each platform
	check(Platforms.Num() == 1);

	for (auto Platform : Platforms)
	{
		/*FString PlatformSandboxPath = SandboxPath.Replace(TEXT("[Platform]"), *Platform->PlatformName());
		FArchive* AssetRegistryReader = IFileManager::Get().CreateFileReader(*PlatformSandboxPath);*/

		FString PlatformSandboxPath = SandboxPath.Replace(TEXT("[Platform]"), *Platform->PlatformName());
		FArrayReader FileContents;
		if (FFileHelper::LoadFileToArray(FileContents, *PlatformSandboxPath) == false)
		{
			continue;
		}
		FArchive* AssetRegistryReader = &FileContents;

		TMap<FName, FAssetData*> SavedAssetRegistryData;
		TArray<FDependsNode*> DependencyData;
		if (AssetRegistryReader)
		{
			AssetRegistry.LoadRegistryData(*AssetRegistryReader, SavedAssetRegistryData, DependencyData);
		}
		for (auto& LoadedAssetData : AssetRegistryData)
		{
			if (PackagesToKeep &&
				PackagesToKeep->Contains(LoadedAssetData.PackageName) == false)
			{
				continue;
			}

			FAssetData* FoundAssetData = SavedAssetRegistryData.FindRef(LoadedAssetData.ObjectPath);
			if ( FoundAssetData )
			{
				LoadedAssetData.ChunkIDs.Append(FoundAssetData->ChunkIDs);
				
				SavedAssetRegistryData.Remove(LoadedAssetData.ObjectPath);
				delete FoundAssetData;
			}
		}

		for (const auto& SavedAsset : SavedAssetRegistryData)
		{
			if (PackagesToKeep && PackagesToKeep->Contains(SavedAsset.Value->PackageName))
			{ 
				AssetRegistryData.Add(*SavedAsset.Value);
			}
			
			delete SavedAsset.Value;
		}
		SavedAssetRegistryData.Empty();
	}
	return true;
}
示例#3
0
	void ClearDelegateObjects()
	{
		for (auto obj : DelegateObjects)
		{
			obj->RemoveFromRoot();
		}
		DelegateObjects.Empty();
		functions.Empty();
	}
void EmptyD3DSamplerStateCache()
{
	for (auto Iter = GSamplerStateCache.CreateIterator(); Iter; ++Iter )
	{
		auto* State = Iter.Value();
		// Manually release
		State->Release();
	}

	GSamplerStateCache.Empty();
}
示例#5
0
void EmptyD3DSamplerStateCache()
{
#if LOCK_GSamplerStateCache
	FScopeLock Lock(&GSamplerStateCacheLock);
#endif
	for (auto Iter = GSamplerStateCache.CreateIterator(); Iter; ++Iter )
	{
		auto* State = Iter.Value();
		// Manually release
		State->Release();
	}

	GSamplerStateCache.Empty();
}
void FBlueprintCompileReinstancer::GenerateFieldMappings(TMap<UObject*, UObject*>& FieldMapping)
{
	check(ClassToReinstance);

	FieldMapping.Empty();

	for (auto& Prop : PropertyMap)
	{
		FieldMapping.Add(Prop.Value, FindField<UProperty>(ClassToReinstance, *Prop.Key.ToString()));
	}

	for (auto& Func : FunctionMap)
	{
		UFunction* NewFunction = ClassToReinstance->FindFunctionByName(Func.Key, EIncludeSuperFlag::ExcludeSuper);
		FieldMapping.Add(Func.Value, NewFunction);
	}

	UObject* NewCDO = ClassToReinstance->GetDefaultObject();
	FieldMapping.Add(OriginalCDO, NewCDO);
}
	~FProtoMessagePool()
	{
		for (TMap<TWeakObjectPtr<UFunction>, PoolMapping>::TIterator It(Pool); It; ++It)
		{
			PoolMapping& PM = It.Value();
			// Delete all the non-prototype versions of the message in the pool
			MessagePoolElem* Elem = PM.FreeList;
			while (Elem)
			{
				MessagePoolElem* NextElem = Elem->Next();
				delete (*Elem)->Msg;
				delete Elem;
				Elem = NextElem;
			}
		}

		Pool.Empty();

		// Optional:  Delete all global objects allocated by libprotobuf.
		google::protobuf::ShutdownProtobufLibrary();
	}
	/** 
	 * Process a string command to the logging suppression system 
	 * @param CmdString, string to process
	 * @param FromBoot, if true, this is a boot time command, and is handled differently
	 */
	void ProcessCmdString(const FString& CmdString, bool FromBoot = false)
	{
		// How to use the log command : `log <category> <verbosity>
		// e.g., Turn off all logging : `log global none
		// e.g., Set specific filter  : `log logshaders verbose
		// e.g., Combo command        : `log global none, log logshaders verbose

		static FName NAME_BootGlobal(TEXT("BootGlobal"));
		static FName NAME_Reset(TEXT("Reset"));
		FString Cmds = CmdString;
		Cmds = Cmds.Trim().TrimQuotes();
		Cmds.Trim();
		TArray<FString> SubCmds;
		Cmds.ParseIntoArray(SubCmds, TEXT(","), true);
		for (int32 Index = 0; Index < SubCmds.Num(); Index++)
		{
			static FString LogString(TEXT("Log "));
			FString Command = SubCmds[Index].Trim();
			if (Command.StartsWith(*LogString))
			{
				Command = Command.Right(Command.Len() - LogString.Len());
			}
			TArray<FString> CommandParts;
			Command.ParseIntoArrayWS(CommandParts);
			if (CommandParts.Num() < 1)
			{
				continue;
			}
			FName Category(*CommandParts[0]);
			if (Category == NAME_Global && FromBoot)
			{
				Category = NAME_BootGlobal; // the boot time global is a special one, since we want things like "log global none, log logshaders verbose"
			}
			TArray<FLogCategoryBase*> CategoryVerbosities;
			uint8 Value = 0;
			if (FromBoot)
			{
				// now maybe this was already set at boot, in which case we override what it had
				uint8* Boot = BootAssociations.Find(Category);
				if (Boot)
				{
					Value = *Boot;
				}
				else
				{
					// see if we had a boot global override
					Boot = BootAssociations.Find(NAME_BootGlobal);
					if (Boot)
					{
						Value = *Boot;
					}
				}
			}
			else
			{
				for (TMultiMap<FName, FLogCategoryBase*>::TKeyIterator It(ReverseAssociations, Category); It; ++It)
				{
					checkSlow(!(It.Value()->Verbosity & ELogVerbosity::BreakOnLog)); // this bit is factored out of this variable, always
					Value = It.Value()->Verbosity | (It.Value()->DebugBreakOnLog ? ELogVerbosity::BreakOnLog : 0);
					CategoryVerbosities.Add(It.Value());
				}					
			}
			if (CommandParts.Num() == 1)
			{
				// only possibility is the reset and toggle command which is meaningless at boot
				if (!FromBoot)
				{
					if (Category == NAME_Reset)
					{
						for (TMap<FLogCategoryBase*, FName>::TIterator It(Associations); It; ++It)
						{
							FLogCategoryBase* Verb = It.Key();
							Verb->ResetFromDefault();
							// store off the last non-zero one for toggle
							checkSlow(!(Verb->Verbosity & ELogVerbosity::BreakOnLog)); // this bit is factored out of this variable, always
							if (Verb->Verbosity)
							{
								// currently on, store this in the pending and clear it
								ToggleAssociations.Add(Category, Verb->Verbosity);
							}
						}
					}
					else
					{
						if (Value & ELogVerbosity::VerbosityMask)
						{
							// currently on, toggle it
							Value = Value & ~ELogVerbosity::VerbosityMask;
						}
						else
						{
							// try to get a non-zero value from the toggle backup
							uint8* Toggle = ToggleAssociations.Find(Category);
							if (Toggle && *Toggle)
							{
								Value |= *Toggle;
							}
							else
							{
								Value |= ELogVerbosity::All;
							}
						}
					}
				}
			}
			else
			{

				// now we have the current value, lets change it!
				for (int32 PartIndex = 1; PartIndex < CommandParts.Num(); PartIndex++)
				{
					FName CmdToken = FName(*CommandParts[PartIndex]);
					static FName NAME_Verbose(TEXT("Verbose"));
					static FName NAME_VeryVerbose(TEXT("VeryVerbose"));
					static FName NAME_All(TEXT("All"));
					static FName NAME_Default(TEXT("Default"));
					static FName NAME_On(TEXT("On"));
					static FName NAME_Off(TEXT("Off"));
					static FName NAME_Break(TEXT("Break"));
					static FName NAME_Fatal(TEXT("Fatal"));
					static FName NAME_Log(TEXT("Log"));
					static FName NAME_Display(TEXT("Display"));
					if (CmdToken == NAME_None)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Fatal;
					}
					else if (CmdToken == NAME_Fatal)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Fatal;
					}
					else if (CmdToken == NAME_Error)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Error;
					}
					else if (CmdToken == NAME_Warning)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Warning;
					}
					else if (CmdToken == NAME_Log)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Log;
					}
					else if (CmdToken == NAME_Display)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Display;
					}
					else if (CmdToken == NAME_Verbose)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Verbose;
					}
					else if (CmdToken == NAME_VeryVerbose || CmdToken == NAME_All)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::VeryVerbose;
					}
					else if (CmdToken == NAME_Default)
					{
						if (CategoryVerbosities.Num() && !FromBoot)
						{
							Value = CategoryVerbosities[0]->DefaultVerbosity;
						}
					}
					else if (CmdToken == NAME_Off)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
						Value |= ELogVerbosity::Fatal;
					}
					else if (CmdToken == NAME_On)
					{
						Value &= ~ELogVerbosity::VerbosityMask;
							// try to get a non-zero value from the toggle backup
							uint8* Toggle = ToggleAssociations.Find(Category);
							if (Toggle && *Toggle)
							{
								Value |= *Toggle;
							}
							else
							{
								Value |= ELogVerbosity::All;
							}
					}
					else if (CmdToken == NAME_Break)
					{
						Value ^= ELogVerbosity::BreakOnLog;
					}
				}
			}
			if (Category != NAME_Reset)
			{
				if (FromBoot)
				{
					if (Category == NAME_BootGlobal)
					{
						// changing the global at boot removes everything set up so far
						BootAssociations.Empty();
					}
					BootAssociations.Add(Category, Value);
				}
				else
				{
					for (int32 CategoryIndex = 0; CategoryIndex < CategoryVerbosities.Num(); CategoryIndex++)
					{
						FLogCategoryBase* Verb = CategoryVerbosities[CategoryIndex];
						Verb->SetVerbosity(ELogVerbosity::Type(Value));
					}
					if (Category == NAME_Global)
					{
						// if this was a global change, we need to change them all
						ApplyGlobalChanges();
					}
				}
				// store off the last non-zero one for toggle
				if (Value & ELogVerbosity::VerbosityMask)
				{
					// currently on, store this in the pending and clear it
					ToggleAssociations.Add(Category, Value & ELogVerbosity::VerbosityMask);
				}
			}
		}
	}
示例#9
0
// TODO: Refactor this (and some other stuff) into a preprocessing step for use by any compiler?
bool FNiagaraCompiler::MergeInFunctionNodes()
{
	struct FReconnectionInfo
	{
	public:
		UEdGraphPin* From;
		TArray<UEdGraphPin*> To;

		//Fallback default value if an input connection is not connected.
		FString FallbackDefault;

		FReconnectionInfo()
			: From(NULL)
		{}
	};
	TMap<FName, FReconnectionInfo> InputConnections;
	TMap<FName, FReconnectionInfo> OutputConnections;

	TArray<class UEdGraphPin*> FuncCallInputPins;
	TArray<class UEdGraphPin*> FuncCallOutputPins;

	//Copies the function graph into the main graph.
	//Removes the Function call in the main graph and the input and output nodes in the function graph, reconnecting their pins appropriately.
	auto MergeFunctionIntoMainGraph = [&](UNiagaraNodeFunctionCall* InFunc, UNiagaraGraph* FuncGraph)
	{
		InputConnections.Empty();
		OutputConnections.Empty();
		FuncCallInputPins.Empty();
		FuncCallOutputPins.Empty();

		check(InFunc && FuncGraph);
		if (InFunc->FunctionScript)
		{
			//Get all the pins that are connected to the inputs of the function call node in the main graph.
			InFunc->GetInputPins(FuncCallInputPins);
			for (UEdGraphPin* FuncCallInputPin : FuncCallInputPins)
			{
				FName InputName(*FuncCallInputPin->PinName);
				FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputName);
				if (FuncCallInputPin->LinkedTo.Num() > 0)
				{
					check(FuncCallInputPin->LinkedTo.Num() == 1);
					UEdGraphPin* LinkFrom = FuncCallInputPin->LinkedTo[0];
					check(LinkFrom->Direction == EGPD_Output);
					InputConnection.From = LinkFrom;
				}
				else
				{
					//This input has no link so we need the default value from the pin.
					InputConnection.FallbackDefault = FuncCallInputPin->GetDefaultAsString();
				}
			}
			//Get all the pins that are connected to the outputs of the function call node in the main graph.
			InFunc->GetOutputPins(FuncCallOutputPins);
			for (UEdGraphPin* FuncCallOutputPin : FuncCallOutputPins)
			{
				FName OutputName(*FuncCallOutputPin->PinName);
				for (UEdGraphPin* LinkTo : FuncCallOutputPin->LinkedTo)
				{
					check(LinkTo->Direction == EGPD_Input);
					FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName);
					OutputConnection.To.Add(LinkTo);
				}
			}

			//Remove the function call node from the graph now that we have everything we need from it.
			SourceGraph->RemoveNode(InFunc);

			//Keep a list of the Input and Output nodes we see in the function graph so that we can remove (most of) them later.
			TArray<UEdGraphNode*, TInlineAllocator<64>> ToRemove;

			//Search the nodes in the function graph, finding any connections to input or output nodes.
			for (UEdGraphNode* FuncGraphNode : FuncGraph->Nodes)
			{
				if (UNiagaraNodeInput* InputNode = Cast<UNiagaraNodeInput>(FuncGraphNode))
				{
					check(InputNode->Pins.Num() == 1);
					//Get an array of "To" pins from one or more input nodes referencing each named input.
					FReconnectionInfo& InputConnection = InputConnections.FindOrAdd(InputNode->Input.Name);
					if (InputConnection.From)
					{
						//We have a connection from the function call so remove the input node and connect to that.
						ToRemove.Add(InputNode);
					}
					else
					{
						//This input has no connection from the function call so what do we do here? 
						//For now we just leave the input node and connect back to it. 
						//This will mean unconnected pins from the function call will look for constants or attributes. 
						//In some cases we may want to just take the default value from the function call pin instead?
						//Maybe have some properties on the function call defining that.
						InputConnection.From = InputNode->Pins[0];
					}

					TArray<UEdGraphPin*>& LinkToPins = InputNode->Pins[0]->LinkedTo;
					for (UEdGraphPin* ToPin : LinkToPins)
					{
						check(ToPin->Direction == EGPD_Input);
						InputConnection.To.Add(ToPin);
					}
				}
				else if (UNiagaraNodeOutput* OutputNode = Cast<UNiagaraNodeOutput>(FuncGraphNode))
				{
					//Unlike the input nodes, we don't have the option of keeping these if there is no "From" pin. The default values from the node pins should be used.
					ToRemove.Add(OutputNode);

					//For each output, get the "From" pin to be reconnected later.
					for (int32 OutputIdx = 0; OutputIdx < OutputNode->Outputs.Num(); ++OutputIdx)
					{
						FName OutputName = OutputNode->Outputs[OutputIdx].Name;

						UEdGraphPin* OutputNodePin = OutputNode->Pins[OutputIdx];
						check(OutputNodePin->LinkedTo.Num() <= 1);
						FReconnectionInfo& OutputConnection = OutputConnections.FindOrAdd(OutputName);
						UEdGraphPin* LinkFromPin = OutputNodePin->LinkedTo.Num() == 1 ? OutputNodePin->LinkedTo[0] : NULL;
						if (LinkFromPin)
						{
							check(LinkFromPin->Direction == EGPD_Output);
							OutputConnection.From = LinkFromPin;
						}
						else
						{
							//This output is not connected so links to it in the main graph must use it's default value.
							OutputConnection.FallbackDefault = OutputNodePin->GetDefaultAsString();
						}
					}
				}
			}

			//Remove all the In and Out nodes from the function graph.
			for (UEdGraphNode* Remove : ToRemove)
			{
				FuncGraph->RemoveNode(Remove);
			}

			//Copy the nodes from the function graph over into the main graph.
			FuncGraph->MoveNodesToAnotherGraph(SourceGraph, false);

			//Finally, do all the reconnection.
			auto MakeConnection = [&](FReconnectionInfo& Info)
			{
				for (UEdGraphPin* LinkTo : Info.To)
				{
					if (Info.From)
					{
						Info.From->MakeLinkTo(LinkTo);
					}
					else
					{
						LinkTo->DefaultValue = Info.FallbackDefault;
					}
				}
			};
			for (TPair<FName, FReconnectionInfo>& ReconnectInfo : InputConnections){ MakeConnection(ReconnectInfo.Value); }
			for (TPair<FName, FReconnectionInfo>& ReconnectInfo : OutputConnections){ MakeConnection(ReconnectInfo.Value); }
		}
	};

	//Helper struct for traversing nested function calls.
	struct FFunctionContext
	{
		//True if this context's function has been merged into the main graph.
		bool bProcessed;
		//The index of this context into the ContextPool. 
		int32 PoolIdx;
		//Pointer back to the parent context for traversal.
		FFunctionContext* Parent;
		//The function call node for this function in the source/parent graph.
		UNiagaraNodeFunctionCall* Function;
		//The graph for this function that we are going to merge into the main graph.
		UNiagaraGraph* FunctionGraph;
		//The script from which the graph is copied. Used for re entrance check.
		UNiagaraScript* Script;

		//Contexts for function calls in this function graph.
		TArray<FFunctionContext*, TInlineAllocator<64>> SubFunctionCalls;

		FFunctionContext()
			: bProcessed(false)
			, PoolIdx(INDEX_NONE)
			, Parent(NULL)
			, Function(NULL)
			, FunctionGraph(NULL)
			, Script(NULL)
		{
		}

		/**
		We don't allow re-entrant functions as this would cause an infinite loop of merging in graphs.
		Maybe in the future if we allow branching in the VM we can allow this.
		*/
		bool CheckForReentrance()const
		{
			UNiagaraNodeFunctionCall* Func = Function;
			FFunctionContext* Curr = Parent;
			while (Curr)
			{
				if (Curr->Script == Script)
					return true;

				Curr = Curr->Parent;
			}
			return false;
		}

		FString GetCallstack()const
		{
			FString Ret;
			const FFunctionContext* Curr = this;
			while (Curr)
			{
				if (Curr->Script)
				{
					Ret.Append(*(Curr->Script->GetPathName()));
				}
				else
				{
					Ret.Append(TEXT("Unknown"));
				}

				Ret.Append(TEXT("\n"));

				Curr = Curr->Parent;
			}
			return Ret;
		}
	};

	//A pool of contexts on the stack to avoid loads of needless, small heap allocations.
	TArray<FFunctionContext, TInlineAllocator<512>> ContextPool;
	ContextPool.Reserve(512);

	FFunctionContext RootContext;
	FFunctionContext* CurrentContext = &RootContext;
	CurrentContext->FunctionGraph = SourceGraph;
	CurrentContext->Script = Script;

	//Depth first traversal of all function calls.
	while (CurrentContext)
	{
		//Find any sub functions and process this function call.
		if (!CurrentContext->bProcessed)
		{
			CurrentContext->bProcessed = true;

			//Find any sub functions and check for re-entrance.
			if (CurrentContext->FunctionGraph)
			{
				for (UEdGraphNode* Node : CurrentContext->FunctionGraph->Nodes)
				{
					UNiagaraNodeFunctionCall* FuncNode = Cast<UNiagaraNodeFunctionCall>(Node);
					if (FuncNode)
					{
						int32 NewIdx = ContextPool.AddZeroed();
						FFunctionContext* SubFuncContext = &ContextPool[NewIdx];
						CurrentContext->SubFunctionCalls.Push(SubFuncContext);
						SubFuncContext->Parent = CurrentContext;
						SubFuncContext->Function = FuncNode;
						SubFuncContext->PoolIdx = NewIdx;
						SubFuncContext->Script = FuncNode->FunctionScript;

						if (SubFuncContext->CheckForReentrance())
						{
							FString Callstack = SubFuncContext->GetCallstack();
							MessageLog.Error(TEXT("Reentrant function call!\n%s"), *Callstack);
							return false;
						}

						//Copy the function graph as we'll be modifying it as we merge in with the main graph.
						UNiagaraScriptSource* FuncSource = CastChecked<UNiagaraScriptSource>(FuncNode->FunctionScript->Source);
						check(FuncSource);
						SubFuncContext->FunctionGraph = CastChecked<UNiagaraGraph>(FEdGraphUtilities::CloneGraph(FuncSource->NodeGraph, NULL, &MessageLog));
					}
				}
			}

			//Merge this function into the main graph now.
			if (CurrentContext->Function && CurrentContext->FunctionGraph)
			{
				MergeFunctionIntoMainGraph(CurrentContext->Function, CurrentContext->FunctionGraph);
			}
		}

		if (CurrentContext->SubFunctionCalls.Num() > 0)
		{
			//Move to the next sub function.
			CurrentContext = CurrentContext->SubFunctionCalls.Pop();
		}
		else
		{
			//Done processing this function so remove it and move back to the parent.
			if (CurrentContext->PoolIdx != INDEX_NONE)
			{
				CurrentContext->FunctionGraph->MarkPendingKill();

				ContextPool.RemoveAtSwap(CurrentContext->PoolIdx);
			}
			CurrentContext = CurrentContext->Parent;
		}
	}

	return true;
}
void IGameplayCueInterface::ClearTagToFunctionMap()
{
	PerClassGameplayTagToFunctionMap.Empty();
}
示例#11
0
FReply SGraphPin::OnPinMouseDown( const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent )
{
	bIsMovingLinks = false;

	if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
	{
		if (!GraphPinObj->bNotConnectable && IsEditable.Get())
		{
			if (MouseEvent.IsAltDown())
			{
				// Alt-Left clicking will break all existing connections to a pin
				const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
				Schema->BreakPinLinks(*GraphPinObj, true);
				return FReply::Handled();
			}

			auto OwnerNodePinned = OwnerNodePtr.Pin();
			if (MouseEvent.IsControlDown() && (GraphPinObj->LinkedTo.Num() > 0))
			{
				// Get a reference to the owning panel widget
				check(OwnerNodePinned.IsValid());
				TSharedPtr<SGraphPanel> OwnerPanelPtr = OwnerNodePinned->GetOwnerPanel();
				check(OwnerPanelPtr.IsValid());

				// Obtain the set of all pins within the panel
				TSet<TSharedRef<SWidget> > AllPins;
				OwnerPanelPtr->GetAllPins(AllPins);

				// Construct a UEdGraphPin->SGraphPin mapping for the full pin set
				TMap< UEdGraphPin*, TSharedRef<SGraphPin> > PinToPinWidgetMap;
				for( TSet< TSharedRef<SWidget> >::TIterator ConnectorIt(AllPins); ConnectorIt; ++ConnectorIt )
				{
					const TSharedRef<SWidget>& SomePinWidget = *ConnectorIt;
					const SGraphPin& PinWidget = static_cast<const SGraphPin&>(SomePinWidget.Get());

					PinToPinWidgetMap.Add(PinWidget.GetPinObj(), StaticCastSharedRef<SGraphPin>(SomePinWidget));
				}

				// Define a local struct to temporarily store lookup information for pins that we are currently linked to
				struct LinkedToPinInfo
				{
					// Pin name string
					FString PinName;

					// A weak reference to the node object that owns the pin
					TWeakObjectPtr<UEdGraphNode> OwnerNodePtr;
				};

				// Build a lookup table containing information about the set of pins that we're currently linked to
				TArray<LinkedToPinInfo> LinkedToPinInfoArray;
				for( TArray<UEdGraphPin*>::TIterator LinkArrayIter(GetPinObj()->LinkedTo); LinkArrayIter; ++LinkArrayIter )
				{
					if (auto PinWidget = PinToPinWidgetMap.Find(*LinkArrayIter))
					{
						check((*PinWidget)->OwnerNodePtr.IsValid());

						LinkedToPinInfo PinInfo;
						PinInfo.PinName = (*PinWidget)->GetPinObj()->PinName;
						PinInfo.OwnerNodePtr = (*PinWidget)->OwnerNodePtr.Pin()->GetNodeObj();
						LinkedToPinInfoArray.Add(PinInfo);
					}
				}

				// Control-Left clicking will break all existing connections to a pin
				// Note that for some nodes, this can cause reconstruction. In that case, pins we had previously linked to may now be destroyed.
				const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
				Schema->BreakPinLinks(*GraphPinObj, true);

				// Check to see if the panel has been invalidated by a graph change notification
				if (!OwnerPanelPtr->Contains(OwnerNodePinned->GetNodeObj()))
				{
					// Force the panel to update. This will cause node & pin widgets to be reinstanced to match any reconstructed node/pin object references.
					OwnerPanelPtr->Update();

					// Obtain the full set of pins again after the update
					AllPins.Empty(AllPins.Num());
					OwnerPanelPtr->GetAllPins(AllPins);

					// Rebuild the UEdGraphPin->SGraphPin mapping for the full pin set
					PinToPinWidgetMap.Empty(PinToPinWidgetMap.Num());
					for( TSet< TSharedRef<SWidget> >::TIterator ConnectorIt(AllPins); ConnectorIt; ++ConnectorIt )
					{
						const TSharedRef<SWidget>& SomePinWidget = *ConnectorIt;
						const SGraphPin& PinWidget = static_cast<const SGraphPin&>(SomePinWidget.Get());

						PinToPinWidgetMap.Add(PinWidget.GetPinObj(), StaticCastSharedRef<SGraphPin>(SomePinWidget));
					}
				}
				
				// Now iterate over our lookup table to find the instances of pin widgets that we had previously linked to
				TArray<TSharedRef<SGraphPin>> PinArray;
				for(auto LinkedToPinInfoIter = LinkedToPinInfoArray.CreateConstIterator(); LinkedToPinInfoIter; ++LinkedToPinInfoIter)
				{
					LinkedToPinInfo PinInfo = *LinkedToPinInfoIter;
					UEdGraphNode* OwnerNodeObj = PinInfo.OwnerNodePtr.Get();
					if(OwnerNodeObj != NULL)
					{
						for(auto PinIter = PinInfo.OwnerNodePtr.Get()->Pins.CreateConstIterator(); PinIter; ++PinIter)
						{
							UEdGraphPin* Pin = *PinIter;
							if(Pin->PinName == PinInfo.PinName)
							{
								if (auto pWidget = PinToPinWidgetMap.Find(Pin))
								{
									PinArray.Add(*pWidget);
								}
							}
						}
					}
				}

				if(PinArray.Num() > 0)
				{
					bIsMovingLinks = true;

					return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(OwnerPanelPtr.ToSharedRef(), PinArray, /*bIsShiftOperation=*/ false));
				}
				else
				{
					// Shouldn't get here, but just in case we lose our previous links somehow after breaking them, we'll just skip the drag.
					return FReply::Handled();
				}
			}
			
			// Start a drag-drop on the pin
			if (ensure(OwnerNodePinned.IsValid()))
			{
				TArray<TSharedRef<SGraphPin>> PinArray;
				PinArray.Add(SharedThis(this));

				return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(OwnerNodePinned->GetOwnerPanel().ToSharedRef(), PinArray, MouseEvent.IsShiftDown()));
			}
			else
			{
				return FReply::Unhandled();
			}
		}
		else
		{
			// It's not connectable, but we don't want anything above us to process this left click.
			return FReply::Handled();
		}
	}
	else
	{
		return FReply::Unhandled();
	}
}
示例#12
0
void FChunkWriter::FQueuedChunkWriter::GetChunkFilesizes(TMap<FGuid, int64>& OutChunkFileSizes)
{
	FScopeLock ScopeLock(&ChunkFileSizesCS);
	OutChunkFileSizes.Empty(ChunkFileSizes.Num());
	OutChunkFileSizes.Append(ChunkFileSizes);
}
void FVulkanVertexDeclaration::EmptyCache()
{
	GVertexDeclarationCache.Empty(0);
}