void SPropertyEditorCombo::SendToObjects( const FString& NewValue ) { const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); UProperty* Property = PropertyNode->GetProperty(); FString Value; FString ToolTipValue; if ( bUsesAlternateDisplayValues && !Property->IsA(UStrProperty::StaticClass())) { // currently only enum properties can use alternate display values; this might change, so assert here so that if support is expanded to other property types // without updating this block of code, we'll catch it quickly UEnum* Enum = CastChecked<UByteProperty>(Property)->Enum; check(Enum); int32 Index = INDEX_NONE; for( int32 ItemIndex = 0; ItemIndex < Enum->NumEnums(); ++ItemIndex ) { const FString EnumName = Enum->GetEnumName(ItemIndex); const FString DisplayName = Enum->GetDisplayNameText(ItemIndex ).ToString(); if( DisplayName.Len() > 0 ) { if ( DisplayName == NewValue ) { Index = ItemIndex; break; } } else if (EnumName == NewValue) { Index = ItemIndex; break; } } check( Index != INDEX_NONE ); Value = Enum->GetEnumName(Index); ToolTipValue = Enum->GetMetaData( TEXT("ToolTip"), Index ); FString ToolTipText = Property->GetToolTipText().ToString(); if (ToolTipValue.Len() > 0) { ToolTipText = FString::Printf(TEXT("%s\n\n%s"), *ToolTipText, *ToolTipValue); } SetToolTipText(ToolTipText); } else { Value = NewValue; } const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle(); PropertyHandle->SetValueFromFormattedString( Value ); }
void SPropertyEditorCombo::SendToObjects( const FString& NewValue ) { const TSharedRef< FPropertyNode > PropertyNode = PropertyEditor->GetPropertyNode(); UProperty* Property = PropertyNode->GetProperty(); FString Value; if ( bUsesAlternateDisplayValues && !Property->IsA(UStrProperty::StaticClass())) { // currently only enum properties can use alternate display values; this // might change, so assert here so that if support is expanded to other // property types without updating this block of code, we'll catch it quickly UEnum* Enum = CastChecked<UByteProperty>(Property)->Enum; check(Enum != nullptr); const int32 Index = FindEnumValueIndex(Enum, NewValue); check( Index != INDEX_NONE ); Value = Enum->GetEnumName(Index); FText ToolTipValue = Enum->GetToolTipText(Index); FText ToolTipText = Property->GetToolTipText(); if (!ToolTipValue.IsEmpty()) { ToolTipText = FText::Format(FText::FromString(TEXT("{0}\n\n{1}")), ToolTipText, ToolTipValue); } SetToolTipText(ToolTipText); } else { Value = NewValue; } const TSharedRef< IPropertyHandle > PropertyHandle = PropertyEditor->GetPropertyHandle(); PropertyHandle->SetValueFromFormattedString( Value ); }
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field) { const int32 MetaDataCount = 3; const FString MetaDataKeys[MetaDataCount] = { TEXT("ToolTip"), TEXT("DisplayName"), TEXT("Category") }; const FString AssociatedNamespaces[MetaDataCount] = { TEXT("UObjectToolTips"), TEXT("UObjectDisplayNames"), TEXT("UObjectCategories") }; // Gather for object. { for(int32 i = 0; i < MetaDataCount; ++i) { const FString& MetaDataValue = Field->GetMetaData(*MetaDataKeys[i]); if(!(MetaDataValue.IsEmpty())) { const FString Namespace = AssociatedNamespaces[i]; FLocItem LocItem(MetaDataValue); FContext Context; Context.Key = MetaDataKeys[i] == TEXT("Category") ? MetaDataValue : Field->GetFullGroupName(true) + TEXT(".") + Field->GetName(); Context.SourceLocation = TEXT("Run-time MetaData"); ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context); } } } // For enums, also gather for enum values. { UEnum* Enum = Cast<UEnum>(Field); if(Enum) { const int32 ValueCount = Enum->NumEnums(); for(int32 i = 0; i < ValueCount; ++i) { for(int32 j = 0; j < MetaDataCount; ++j) { const FString& MetaDataValue = Enum->GetMetaData(*MetaDataKeys[j], i); if(!(MetaDataValue.IsEmpty())) { const FString Namespace = AssociatedNamespaces[j]; FLocItem LocItem(MetaDataValue); FContext Context; Context.Key = MetaDataKeys[j] == TEXT("Category") ? MetaDataValue : Enum->GetFullGroupName(true) + TEXT(".") + Enum->GetName() + TEXT(".") + Enum->GetEnumName(i); Context.SourceLocation = TEXT("Run-time MetaData"); ManifestInfo->AddEntry(TEXT("EntryDescription"), Namespace, LocItem, Context); } } } } } }
void UK2Node::PostReconstructNode() { if (!IsTemplate()) { // Make sure we're not dealing with a menu node UEdGraph* OuterGraph = GetGraph(); if( OuterGraph && OuterGraph->Schema ) { const UEdGraphSchema_K2* Schema = Cast<const UEdGraphSchema_K2>(GetSchema()); // fix up any pin data if it needs to for( auto PinIt = Pins.CreateIterator(); PinIt; ++PinIt ) { UEdGraphPin* CurrentPin = *PinIt; const FString& PinCategory = CurrentPin->PinType.PinCategory; // fix up enum names if it exists in enum redirects if (PinCategory == Schema->PC_Byte) { UEnum* EnumPtr = Cast<UEnum>(CurrentPin->PinType.PinSubCategoryObject.Get()); if (EnumPtr) { const FString& PinValue = CurrentPin->DefaultValue; // see if this enum is in EnumRedirects int32 EnumIndex = UEnum::FindEnumRedirects(EnumPtr, *PinValue); if (EnumIndex != INDEX_NONE) { FString EnumName = EnumPtr->GetEnumName(EnumIndex); // if the name does not match with pin value, override pin value if (EnumName != PinValue) { // I'm not marking package as dirty // as I know that's not going to work during serialize or post load CurrentPin->DefaultValue = EnumName; continue; } } } } else if (PinCategory == Schema->PC_Object) { UClass const* PinClass = Cast<UClass const>(CurrentPin->PinType.PinSubCategoryObject.Get()); if ((PinClass != nullptr) && PinClass->IsChildOf(UInterface::StaticClass())) { CurrentPin->PinType.PinCategory = Schema->PC_Interface; } } } } } }
void FRCPassPostProcessVisualizeShadingModels::Process(FRenderingCompositePassContext& Context) { SCOPED_DRAW_EVENT(Context.RHICmdList, PostProcessVisualizeShadingModels); const FPooledRenderTargetDesc* InputDesc = GetInputDesc(ePId_Input0); const FSceneView& View = Context.View; const FViewInfo& ViewInfo = Context.View; const FSceneViewFamily& ViewFamily = *(View.Family); FIntRect SrcRect = View.ViewRect; FIntRect DestRect = View.ViewRect; FIntPoint SrcSize = InputDesc->Extent; const FSceneRenderTargetItem& DestRenderTarget = PassOutputs[0].RequestSurface(Context); // Set the view family's render target/viewport. SetRenderTarget(Context.RHICmdList, DestRenderTarget.TargetableTexture, FTextureRHIRef()); Context.SetViewportAndCallRHI(DestRect); // set the state Context.RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI()); Context.RHICmdList.SetRasterizerState(TStaticRasterizerState<>::GetRHI()); Context.RHICmdList.SetDepthStencilState(TStaticDepthStencilState<false, CF_Always>::GetRHI()); TShaderMapRef<FPostProcessVS> VertexShader(Context.GetShaderMap()); TShaderMapRef<FPostProcessVisualizeShadingModelsPS> PixelShader(Context.GetShaderMap()); static FGlobalBoundShaderState BoundShaderState; SetGlobalBoundShaderState(Context.RHICmdList, Context.GetFeatureLevel(), BoundShaderState, GFilterVertexDeclaration.VertexDeclarationRHI, *VertexShader, *PixelShader); PixelShader->SetPS(Context, ((FViewInfo&)View).ShadingModelMaskInView); // Draw a quad mapping scene color to the view's render target DrawRectangle( Context.RHICmdList, DestRect.Min.X, DestRect.Min.Y, DestRect.Width(), DestRect.Height(), SrcRect.Min.X, SrcRect.Min.Y, SrcRect.Width(), SrcRect.Height(), DestRect.Size(), SrcSize, *VertexShader, EDRF_UseTriangleOptimization); FRenderTargetTemp TempRenderTarget(View, (const FTexture2DRHIRef&)DestRenderTarget.TargetableTexture); FCanvas Canvas(&TempRenderTarget, NULL, 0, 0, 0, Context.GetFeatureLevel()); float X = 30; float Y = 28; const float YStep = 14; const float ColumnWidth = 250; FString Line; Canvas.DrawShadowedString( X, Y += YStep, TEXT("Visualize ShadingModels (mostly to track down bugs)"), GetStatsFont(), FLinearColor(1, 1, 1)); Y = 160 - YStep - 4; uint32 Value = ((FViewInfo&)View).ShadingModelMaskInView; Line = FString::Printf(TEXT("View.ShadingModelMaskInView = 0x%x"), Value); Canvas.DrawShadowedString( X, Y, *Line, GetStatsFont(), FLinearColor(0.5f, 0.5f, 0.5f)); Y += YStep; UEnum* Enum = FindObject<UEnum>(NULL, TEXT("Engine.EMaterialShadingModel")); check(Enum); Y += 5; for(uint32 i = 0; i < MSM_MAX; ++i) { FString Name = Enum->GetEnumName(i); Line = FString::Printf(TEXT("%d. %s"), i, *Name); bool bThere = (Value & (1 << i)) != 0; Canvas.DrawShadowedString(X + 30, Y, *Line, GetStatsFont(), bThere ? FLinearColor(1, 1, 1) : FLinearColor(0, 0, 0) ); Y += 20; } Line = FString::Printf(TEXT("(On CPU, based on what gets rendered)")); Canvas.DrawShadowedString( X, Y, *Line, GetStatsFont(), FLinearColor(0.5f, 0.5f, 0.5f)); Y += YStep; Canvas.Flush_RenderThread(Context.RHICmdList); Context.RHICmdList.CopyToResolveTarget(DestRenderTarget.TargetableTexture, DestRenderTarget.ShaderResourceTexture, false, FResolveParams()); // AdjustGBufferRefCount(1) call is done in constructor FSceneRenderTargets::Get(Context.RHICmdList).AdjustGBufferRefCount(Context.RHICmdList, -1); }
void UCollisionProfile::LoadProfileConfig(bool bForceInit) { /** * This function loads all config data to memory * * 1. First it fixes the meta data for each custom channel name since those meta data is used for #2 * 2. Load Default Profile so that it can be used later * 3. Second it sets up Correct ResponseToChannel for all profiles * 4. It loads profile redirect data **/ // read "EngineTraceChanne" and "GameTraceChanne" and set meta data FConfigSection* Configs = GConfig->GetSectionPrivate( TEXT("/Script/Engine.CollisionProfile"), false, true, GEngineIni ); // before any op, verify if profiles contains invalid name - such as Custom profile name - remove all of them for (auto Iter=Profiles.CreateConstIterator(); Iter; ++Iter) { // make sure it doens't have any if (Iter->Name == CustomCollisionProfileName) { UE_LOG(LogCollisionProfile, Error, TEXT("Profiles contain invalid name : %s is reserved for internal use"), *CustomCollisionProfileName.ToString()); Profiles.RemoveAt(Iter.GetIndex()); } } // 1. First loads all meta data for custom channels // this can be used in #2, so had to fix up first // this is to replace ECollisionChannel's DisplayName to be defined by users FString EngineTraceChannel = TEXT("EngineTraceChannel"); FString GameTraceChannel = TEXT("GameTraceChannel"); // find the enum UEnum* Enum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ECollisionChannel"), true); // we need this Enum check (Enum); UStruct* Struct = FCollisionResponseContainer::StaticStruct(); check (Struct); const FString KeyName = TEXT("DisplayName"); const FString TraceType = TEXT("TraceQuery"); const FString TraceValue = TEXT("1"); const FString HiddenMeta = TEXT("Hidden"); // need to initialize displaynames separate int32 NumEnum = Enum->NumEnums(); ChannelDisplayNames.Empty(NumEnum); ChannelDisplayNames.AddZeroed(NumEnum); TraceTypeMapping.Empty(); ObjectTypeMapping.Empty(); // first go through enum entry, and add suffix to displaynames int32 PrefixLen = FString(TEXT("ECC_")).Len(); // need to have mapping table between ECollisionChannel and EObjectTypeQuery/ETraceTypeQuery UEnum* ObjectTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EObjectTypeQuery"), true); UEnum* TraceTypeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("ETraceTypeQuery"), true); check (ObjectTypeEnum && TraceTypeEnum); for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex ) { FString EnumName = Enum->GetEnumName(EnumIndex); EnumName = EnumName.RightChop(PrefixLen); FName DisplayName = FName(*EnumName); if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) ) { // verify if the Struct name matches // this is to avoid situations where they mismatch and causes random bugs UField* Field = FindField<UField>(Struct, DisplayName); if (!Field) { // error - this is bad. This means somebody changed variable name without changing channel enum UE_LOG(LogCollisionProfile, Error, TEXT("Variable (%s) isn't found for Channel (%s). \nPlease make sure you name matches between ECollisionChannel and FCollisionResponseContainer."), *DisplayName.ToString(), *EnumName); } #if WITH_EDITOR // clear displayname since we're setting it in below Enum->RemoveMetaData(*KeyName, EnumIndex); if (Enum->HasMetaData(*HiddenMeta, EnumIndex) == false) { Enum->SetMetaData(*HiddenMeta, NULL, EnumIndex); } #endif } else { // fix up FCollisionQueryFlag AllObjects flag, if trace type=1 ECollisionChannel CollisionChannel = (ECollisionChannel)EnumIndex; // for any engine level collision profile, we hard coded here // meta data doesn't work in cooked build, so we'll have to manually handle them if ((CollisionChannel == ECC_Visibility) || (CollisionChannel == ECC_Camera)) { // remove from object query flags FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CollisionChannel); TraceTypeMapping.Add(CollisionChannel); } else if ( CollisionChannel < ECC_OverlapAll_Deprecated ) { ObjectTypeMapping.Add(CollisionChannel); } } ChannelDisplayNames[EnumIndex] = DisplayName; } // Now Load Channel setups, and set display names if customized // also initialize DefaultResposneContainer with default response for each channel FCollisionResponseContainer::DefaultResponseContainer.SetAllChannels(ECR_Block); for (auto Iter=DefaultChannelResponses.CreateConstIterator(); Iter; ++Iter) { const FCustomChannelSetup& CustomChannel = *Iter; int32 EnumIndex = CustomChannel.Channel; // make sure it is the range of channels we allow to change if ( IS_VALID_COLLISIONCHANNEL(EnumIndex) ) { if ( CustomChannel.Name != NAME_None ) { // before you set meta data, you'll have to save this value to modify // ECollisionResponseContainer variables meta data FString VariableName = ChannelDisplayNames[EnumIndex].ToString(); FString DisplayValue = CustomChannel.Name.ToString(); // also has to set this for internal use ChannelDisplayNames[EnumIndex] = FName(*DisplayValue); #if WITH_EDITOR // set displayvalue for this enum entry Enum->SetMetaData(*KeyName, *DisplayValue, EnumIndex); // also need to remove "Hidden" Enum->RemoveMetaData(*HiddenMeta, EnumIndex); #endif // now add MetaData type for trace type if it does if (CustomChannel.bTraceType) { #if WITH_EDITOR // add to enum Enum->SetMetaData(*TraceType, *TraceValue, EnumIndex); #endif // remove from all object queries FCollisionQueryFlag::Get().RemoveFromAllObjectsQueryFlag(CustomChannel.Channel); TraceTypeMapping.Add(CustomChannel.Channel); } // if it has display value else { #if WITH_EDITOR // add to enum Enum->RemoveMetaData(*TraceType, EnumIndex); #endif ObjectTypeMapping.Add(CustomChannel.Channel); if (CustomChannel.bStaticObject) { // add to static object FCollisionQueryFlag::Get().AddToAllStaticObjectsQueryFlag(CustomChannel.Channel); } } #if WITH_EDITOR // now enum is fixed, so find member variable for the field UField* Field = FindField<UField>(Struct, FName(*VariableName)); // I verified up in the class, this can't happen check (Field); Field->SetMetaData(*KeyName, *DisplayValue); #endif } else { // missing name UE_LOG(LogCollisionProfile, Warning, TEXT("Name can't be empty for Channel (%d) "), EnumIndex ); } // allow it to set default response FCollisionResponseContainer::DefaultResponseContainer.SetResponse((ECollisionChannel) EnumIndex, CustomChannel.DefaultResponse); } else { // you can't customize those channels UE_LOG(LogCollisionProfile, Warning, TEXT("Default Setup doesn't allow for predefined engine channels (%d) "), EnumIndex); } } #if WITH_EDITOR // now propagate all changes to the channels to EObjectTypeQuery and ETraceTypeQuery for blueprint accessibility // this code is to show in editor more friendly, so it doesn't have to be set if it's not for editor int32 ObjectTypeEnumIndex = 0; int32 TraceTypeEnumIndex = 0; // first go through fill up ObjectType Enum for ( int32 EnumIndex=0; EnumIndex<NumEnum; ++EnumIndex ) { // if not hidden const FString& Hidden = Enum->GetMetaData(*HiddenMeta, EnumIndex); if ( Hidden.IsEmpty() ) { const FString& DisplayName = Enum->GetMetaData(*KeyName, EnumIndex); if ( !DisplayName.IsEmpty() ) { // find out trace type or object type if (Enum->GetMetaData(*TraceType, EnumIndex) == TraceValue) { TraceTypeEnum->RemoveMetaData(*HiddenMeta, TraceTypeEnumIndex); TraceTypeEnum->SetMetaData(*KeyName, *DisplayName, TraceTypeEnumIndex); ++TraceTypeEnumIndex; } else { ObjectTypeEnum->RemoveMetaData(*HiddenMeta, ObjectTypeEnumIndex); ObjectTypeEnum->SetMetaData(*KeyName, *DisplayName, ObjectTypeEnumIndex); ++ObjectTypeEnumIndex; } } } } // make sure TraceTypeEnumIndex matches TraceTypeMapping check (TraceTypeMapping.Num() == TraceTypeEnumIndex); check (ObjectTypeMapping.Num() == ObjectTypeEnumIndex); #endif // collision redirector has to be loaded before profile CollisionChannelRedirectsMap.Empty(); for(auto Iter = CollisionChannelRedirects.CreateConstIterator(); Iter; ++Iter) { FName OldName = Iter->OldName; FName NewName = Iter->NewName; // at least we need to have OldName if(OldName!= NAME_None && NewName != NAME_None) { // add to pair CollisionChannelRedirectsMap.Add(OldName, NewName); } else { // print error UE_LOG(LogCollisionProfile, Warning, TEXT("CollisionChannel Redirects : Name Can't be none (%s: %s)"), *OldName.ToString(), *NewName.ToString()); } } // 2. Second loads all set up back to ResponseToChannels // this does a lot of iteration, but this only happens once loaded, so it's better to be convenient than efficient // fill up Profiles data FillProfileData(Profiles, Enum, KeyName, EditProfiles); // 3. It loads redirect data - now time to load profile redirect ProfileRedirectsMap.Empty(); // handle profile redirect here for(auto Iter = ProfileRedirects.CreateConstIterator(); Iter; ++Iter) { FName OldName = Iter->OldName; FName NewName = Iter->NewName; // at least we need to have OldName if(OldName!= NAME_None && NewName != NAME_None) { FCollisionResponseTemplate Template; // make sure the template exists if(FindProfileData(Profiles, NewName, Template)) { // add to pair ProfileRedirectsMap.Add(OldName, NewName); } else { // print error UE_LOG(LogCollisionProfile, Warning, TEXT("ProfileRedirect (%s : %s) - New Name (\'%s\') isn't found "), *OldName.ToString(), *NewName.ToString(), *NewName.ToString()); } } } #if WITH_EDITOR if (bForceInit) { // go through objects, and see if we can reinitialize profile for(TObjectIterator<UPrimitiveComponent> PrimIt; PrimIt; ++PrimIt) { PrimIt->UpdateCollisionProfile(); } } #endif }
void UGatherTextFromMetaDataCommandlet::GatherTextFromUObject(UField* const Field, const FGatherParameters& Arguments) { // Gather for object. { if( !Field->HasMetaData( TEXT("DisplayName") ) ) { Field->SetMetaData( TEXT("DisplayName"), *FName::NameToDisplayString( Field->GetName(), Field->IsA( UBoolProperty::StaticClass() ) ) ); } for(int32 i = 0; i < Arguments.InputKeys.Num(); ++i) { FFormatNamedArguments PatternArguments; PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Field->GetFullGroupName(false) ) ); if( Field->HasMetaData( *Arguments.InputKeys[i] ) ) { const FString& MetaDataValue = Field->GetMetaData(*Arguments.InputKeys[i]); if( !MetaDataValue.IsEmpty() ) { PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) ); const FString Namespace = Arguments.OutputNamespaces[i]; FLocItem LocItem(MetaDataValue); FManifestContext Context; Context.Key = FText::Format(Arguments.OutputKeys[i], PatternArguments).ToString(); Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of member %s in %s"), *Arguments.InputKeys[i], *Field->GetName(), *Field->GetFullGroupName(true)); GatherManifestHelper->AddSourceText(Namespace, LocItem, Context); } } } } // For enums, also gather for enum values. { UEnum* Enum = Cast<UEnum>(Field); if(Enum) { const int32 ValueCount = Enum->NumEnums(); for(int32 i = 0; i < ValueCount; ++i) { if( !Enum->HasMetaData(TEXT("DisplayName"), i) ) { Enum->SetMetaData(TEXT("DisplayName"), *FName::NameToDisplayString(Enum->GetEnumName(i), false), i); } for(int32 j = 0; j < Arguments.InputKeys.Num(); ++j) { FFormatNamedArguments PatternArguments; PatternArguments.Add( TEXT("FieldPath"), FText::FromString( Enum->GetFullGroupName(false) + TEXT(".") + Enum->GetEnumName(i) ) ); if( Enum->HasMetaData(*Arguments.InputKeys[j], i) ) { const FString& MetaDataValue = Enum->GetMetaData(*Arguments.InputKeys[j], i); if( !MetaDataValue.IsEmpty() ) { PatternArguments.Add( TEXT("MetaDataValue"), FText::FromString(MetaDataValue) ); const FString Namespace = Arguments.OutputNamespaces[j]; FLocItem LocItem(MetaDataValue); FManifestContext Context; Context.Key = FText::Format(Arguments.OutputKeys[j], PatternArguments).ToString(); Context.SourceLocation = FString::Printf(TEXT("From metadata for key %s of enum value %s of enum %s in %s"), *Arguments.InputKeys[j], *Enum->GetEnumName(i), *Enum->GetName(), *Enum->GetFullGroupName(true)); GatherManifestHelper->AddSourceText(Namespace, LocItem, Context); } } } } } } }
TSharedPtr<FEnvQueryInstance> UEnvQueryManager::CreateQueryInstance(const UEnvQuery* Template, EEnvQueryRunMode::Type RunMode) { if (Template == nullptr || Template->Options.Num() == 0) { UE_CLOG(Template != nullptr && Template->Options.Num() == 0, LogEQS, Warning, TEXT("Query [%s] doesn't have any valid options!"), *Template->GetName()); return nullptr; } // try to find entry in cache FEnvQueryInstance* InstanceTemplate = NULL; for (int32 InstanceIndex = 0; InstanceIndex < InstanceCache.Num(); InstanceIndex++) { if (InstanceCache[InstanceIndex].Template->GetFName() == Template->GetFName() && InstanceCache[InstanceIndex].Instance.Mode == RunMode) { InstanceTemplate = &InstanceCache[InstanceIndex].Instance; break; } } // and create one if can't be found if (InstanceTemplate == NULL) { SCOPE_CYCLE_COUNTER(STAT_AI_EQS_LoadTime); // duplicate template in manager's world for BP based nodes UEnvQuery* LocalTemplate = (UEnvQuery*)StaticDuplicateObject(Template, this, *Template->GetName()); { // memory stat tracking: temporary variable will exist only inside this section FEnvQueryInstanceCache NewCacheEntry; NewCacheEntry.Template = LocalTemplate; NewCacheEntry.Instance.QueryName = LocalTemplate->GetName(); NewCacheEntry.Instance.Mode = RunMode; const int32 Idx = InstanceCache.Add(NewCacheEntry); InstanceTemplate = &InstanceCache[Idx].Instance; } // NOTE: We must iterate over this from 0->Num because we are copying the options from the template into the // instance, and order matters! Since we also may need to remove invalid or null options, we must decrement // the iteration pointer when doing so to avoid problems. for (int32 OptionIndex = 0; OptionIndex < LocalTemplate->Options.Num(); ++OptionIndex) { UEnvQueryOption* MyOption = LocalTemplate->Options[OptionIndex]; if (MyOption == nullptr || MyOption->Generator == nullptr || MyOption->Generator->ItemType == nullptr) { UE_LOG(LogEQS, Error, TEXT("Trying to spawn a query with broken Template (generator:%s itemType:%s): %s, option %d"), MyOption ? (MyOption->Generator ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"), (MyOption && MyOption->Generator) ? (MyOption->Generator->ItemType ? TEXT("ok") : TEXT("MISSING")) : TEXT("N/A"), *GetNameSafe(LocalTemplate), OptionIndex); LocalTemplate->Options.RemoveAt(OptionIndex, 1, false); --OptionIndex; // See note at top of for loop. We cannot iterate backwards here. continue; } UEnvQueryOption* LocalOption = (UEnvQueryOption*)StaticDuplicateObject(MyOption, this, TEXT("None")); UEnvQueryGenerator* LocalGenerator = (UEnvQueryGenerator*)StaticDuplicateObject(MyOption->Generator, this, TEXT("None")); LocalTemplate->Options[OptionIndex] = LocalOption; LocalOption->Generator = LocalGenerator; EEnvTestCost::Type HighestCost(EEnvTestCost::Low); TArray<UEnvQueryTest*> SortedTests = MyOption->Tests; TSubclassOf<UEnvQueryItemType> GeneratedType = MyOption->Generator->ItemType; for (int32 TestIndex = SortedTests.Num() - 1; TestIndex >= 0; TestIndex--) { UEnvQueryTest* TestOb = SortedTests[TestIndex]; if (TestOb == NULL || !TestOb->IsSupportedItem(GeneratedType)) { UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't use test [%s] in option %d [%s], removing it"), *GetNameSafe(LocalTemplate), *GetNameSafe(TestOb), OptionIndex, *MyOption->Generator->OptionName); SortedTests.RemoveAt(TestIndex, 1, false); } else if (HighestCost < TestOb->Cost) { HighestCost = TestOb->Cost; } } if (SortedTests.Num() == 0) { UE_LOG(LogEQS, Warning, TEXT("Query [%s] doesn't have any tests in option %d [%s]"), *GetNameSafe(LocalTemplate), OptionIndex, *MyOption->Generator->OptionName); LocalTemplate->Options.RemoveAt(OptionIndex, 1, false); --OptionIndex; // See note at top of for loop. We cannot iterate backwards here. continue; } LocalOption->Tests.Reset(SortedTests.Num()); for (int32 TestIdx = 0; TestIdx < SortedTests.Num(); TestIdx++) { UEnvQueryTest* LocalTest = (UEnvQueryTest*)StaticDuplicateObject(SortedTests[TestIdx], this, TEXT("None")); LocalOption->Tests.Add(LocalTest); } // use locally referenced duplicates SortedTests = LocalOption->Tests; if (SortedTests.Num() && LocalGenerator->bAutoSortTests) { switch (RunMode) { case EEnvQueryRunMode::SingleResult: SortedTests.Sort(EnvQueryTestSort::FSingleResult(HighestCost)); break; case EEnvQueryRunMode::RandomBest5Pct: case EEnvQueryRunMode::RandomBest25Pct: case EEnvQueryRunMode::AllMatching: SortedTests.Sort(EnvQueryTestSort::FAllMatching()); break; default: { UEnum* RunModeEnum = FindObject<UEnum>(ANY_PACKAGE, TEXT("EEnvQueryRunMode")); UE_LOG(LogEQS, Warning, TEXT("Query [%s] can't be sorted for RunMode: %d [%s]"), *GetNameSafe(LocalTemplate), (int32)RunMode, RunModeEnum ? *RunModeEnum->GetEnumName(RunMode) : TEXT("??")); } } } CreateOptionInstance(LocalOption, SortedTests, *InstanceTemplate); } } if (InstanceTemplate->Options.Num() == 0) { return nullptr; } // create new instance TSharedPtr<FEnvQueryInstance> NewInstance(new FEnvQueryInstance(*InstanceTemplate)); return NewInstance; }