void FGameplayCueTranslationManager::BuildTagTranslationTable_Forward() { #if WITH_EDITOR SCOPE_LOG_TIME_IN_SECONDS(*FString::Printf(TEXT("FGameplayCueTranslatorManager::BuildTagTranslationTable_Forward")), nullptr) #endif // Build the normal TranslationLUT first. This is only done to make sure that UsedTranslators are filled in, giving "real" tags higher priority. // Example: // 1) GC.Rampage.Enraged // 2) GC.Rampage.Elemental.Enraged // // 2 is am override for 1, but comes first alphabetically. In the _Forward method, 2 would be handled first and expanded again to GC.Rampage.Elemental.Elemental.Enraged. // rule recursion wouldn't have been hit yet because 2 actually exists and would be encountered before 1. // // Since BuildTagTranslationTable_Forward is only called by the editor and BuildTagTranslationTable is already fast, this is the simplest way to avoid the above example. // _Forward() could be made more complicated to test for this itself, but doesn't seem like a good trade off for how it would complicate the function. BuildTagTranslationTable(); TArray<FName> SplitNames; SplitNames.Reserve(10); FGameplayTagContainer AllGameplayCueTags = TagManager->RequestGameplayTagChildren(UGameplayCueSet::BaseGameplayCueTag()); // Each GameplayCueTag for (const FGameplayTag& Tag : AllGameplayCueTags) { SplitNames.Reset(); TagManager->SplitGameplayTagFName(Tag, SplitNames); BuildTagTranslationTable_Forward_r(Tag.GetTagName(), SplitNames); } }
void FGameplayCueTranslationManager::BuildTagTranslationTable() { #if WITH_EDITOR SCOPE_LOG_TIME_IN_SECONDS(*FString::Printf(TEXT("FGameplayCueTranslatorManager::BuildTagTranslationTables")), nullptr) #endif TagManager = &IGameplayTagsModule::Get().GetGameplayTagsManager(); check(TagManager); FGameplayTagContainer AllGameplayCueTags = TagManager->RequestGameplayTagChildren(UGameplayCueSet::BaseGameplayCueTag()); ResetTranslationLUT(); RefreshNameSwaps(); // ---------------------------------------------------------------------------------------------- // Find what tags may be derived from swap rules. Note how we work backwards. // If we worked forward, by expanding out all possible tags and then seeing if they exist, // this would take much much longer! TArray<FName> SplitNames; SplitNames.Reserve(10); // All gameplaycue tags for (const FGameplayTag& Tag : AllGameplayCueTags) { SplitNames.Reset(); TagManager->SplitGameplayTagFName(Tag, SplitNames); BuildTagTranslationTable_r(Tag.GetTagName(), SplitNames); } // ---------------------------------------------------------------------------------------------- }
void UGameplayCueManager::InitObjectLibraries(TArray<FString> Paths, UObjectLibrary* ActorObjectLibrary, UObjectLibrary* StaticObjectLibrary, FOnGameplayCueNotifySetLoaded OnLoadDelegate, FShouldLoadGCNotifyDelegate ShouldLoadDelegate) { DECLARE_SCOPE_CYCLE_COUNTER(TEXT("Loading Library"), STAT_ObjectLibrary, STATGROUP_LoadTime); #if WITH_EDITOR bAccelerationMapOutdated = false; FFormatNamedArguments Args; FScopedSlowTask SlowTask(0, FText::Format(NSLOCTEXT("AbilitySystemEditor", "BeginLoadingGameplayCueNotify", "Loading GameplayCue Library"), Args)); SlowTask.MakeDialog(); #endif FScopeCycleCounterUObject PreloadScopeActor(ActorObjectLibrary); ActorObjectLibrary->LoadBlueprintAssetDataFromPaths(Paths); StaticObjectLibrary->LoadBlueprintAssetDataFromPaths(Paths); // --------------------------------------------------------- // Determine loading scheme. // Sync at startup in commandlets like cook. // Async at startup in all other cases // --------------------------------------------------------- const bool bSyncFullyLoad = IsRunningCommandlet(); const bool bAsyncLoadAtStartup = !bSyncFullyLoad && ShouldAsyncLoadAtStartup(); if (bSyncFullyLoad) { #if STATS FString PerfMessage = FString::Printf(TEXT("Fully Loaded GameplayCueNotify object library")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif ActorObjectLibrary->LoadAssetsFromAssetData(); StaticObjectLibrary->LoadAssetsFromAssetData(); } // --------------------------------------------------------- // Look for GameplayCueNotifies that handle events // --------------------------------------------------------- TArray<FAssetData> ActorAssetDatas; ActorObjectLibrary->GetAssetDataList(ActorAssetDatas); TArray<FAssetData> StaticAssetDatas; StaticObjectLibrary->GetAssetDataList(StaticAssetDatas); TArray<FGameplayCueReferencePair> CuesToAdd; BuildCuesToAddToGlobalSet(ActorAssetDatas, GET_MEMBER_NAME_CHECKED(AGameplayCueNotify_Actor, GameplayCueName), bAsyncLoadAtStartup, CuesToAdd, OnLoadDelegate, ShouldLoadDelegate); BuildCuesToAddToGlobalSet(StaticAssetDatas, GET_MEMBER_NAME_CHECKED(UGameplayCueNotify_Static, GameplayCueName), bAsyncLoadAtStartup, CuesToAdd, OnLoadDelegate, ShouldLoadDelegate); check(GlobalCueSet); GlobalCueSet->AddCues(CuesToAdd); }
void UGameplayTagsManager::ConstructGameplayTagTree() { if (!GameplayRootTag.IsValid()) { GameplayRootTag = MakeShareable(new FGameplayTagNode()); { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: Construct from data asset")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif for (auto It(GameplayTagTables.CreateIterator()); It; It++) { if (*It) { PopulateTreeFromDataTable(*It); } } } if (ShouldImportTagsFromINI()) { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: ImportINI")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif // Update path: Check for old tags in DefaultEngine.ini (we'll push them to the UGameplayTagSettings class). TArray<FString> EngineConfigTags; GConfig->GetArray(TEXT("/Script/GameplayTags.GameplayTagsSettings"), TEXT("GameplayTags"), EngineConfigTags, GEngineIni); if (EngineConfigTags.Num() > 0) { UGameplayTagsSettings* MutableDefault = GetMutableDefault<UGameplayTagsSettings>(); if (MutableDefault->GameplayTags.Num() == 0) { MutableDefault->GameplayTags.Append(EngineConfigTags); } } // Load any GameplayTagSettings from config (their default object) for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* Class = *ClassIt; if (!Class->IsChildOf<UGameplayTagsSettings>() || Class->HasAnyClassFlags(CLASS_Abstract)) { continue; } #if WITH_EDITOR Class->GetDefaultObject<UGameplayTagsSettings>()->SortTags(); #endif for (FString TagStr : Class->GetDefaultObject<UGameplayTagsSettings>()->GameplayTags) { FGameplayTagTableRow TableRow; TableRow.Tag = TagStr; AddTagTableRow(TableRow); } } GameplayRootTag->GetChildTagNodes().Sort(FCompareFGameplayTagNodeByTag()); } if (ShouldUseFastReplication()) { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: Reconstruct NetIndex")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif ConstructNetIndex(); } { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: GameplayTagTreeChangedEvent.Broadcast")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif GameplayTagTreeChangedEvent.Broadcast(); } // Update the TagRedirects map TagRedirects.Empty(); FConfigSection* PackageRedirects = GConfig->GetSectionPrivate(TEXT("/Script/Engine.Engine"), false, true, GEngineIni); for (FConfigSection::TIterator It(*PackageRedirects); It; ++It) { if (It.Key() == TEXT("GameplayTagRedirects")) { FName OldTagName = NAME_None; FName NewTagName; if (FParse::Value(*It.Value(), TEXT("OldTagName="), OldTagName)) { if (FParse::Value(*It.Value(), TEXT("NewTagName="), NewTagName)) { if (ensureMsgf(!TagRedirects.Contains(OldTagName), TEXT("Old tag %s is being redirected to more than one tag. Please remove all the redirections except for one."), *OldTagName.ToString())) { FGameplayTag OldTag = RequestGameplayTag(OldTagName, false); //< This only succeeds if OldTag is in the Table! if (OldTag.IsValid()) { UE_LOG(LogGameplayTags, Warning, TEXT("Old tag (%s) which is being redirected still exists in the table! Generally you should " TEXT("remove the old tags from the table when you are redirecting to new tags, or else users will ") TEXT("still be able to add the old tags to containers.")), *OldTagName.ToString() ); } FGameplayTag NewTag = (NewTagName != NAME_None) ? RequestGameplayTag(NewTagName, false) : FGameplayTag(); if (!NewTag.IsValid() && NewTagName != NAME_None) { UE_LOG(LogGameplayTags, Warning, TEXT("Invalid new tag %s! Cannot replace old tag %s."), *NewTagName.ToString(), *OldTagName.ToString()); } else { // Populate the map TagRedirects.Add(OldTagName, NewTag); } } } } } } }
bool ICrashDebugHelper::SyncModules() { // Check source control if( !ISourceControlModule::Get().IsEnabled() ) { return false; } if( !FPDBCache::Get().UsePDBCache() ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "The PDB Cache is disabled, cannot proceed, %s" ), *CrashInfo.EngineVersion ); return false; } // @TODO yrx 2015-02-23 Obsolete, remove after 4.8 const TCHAR* UESymbols = TEXT( "Rocket/Symbols/" ); const bool bHasExecutable = !CrashInfo.ExecutablesPath.IsEmpty(); const bool bHasSymbols = !CrashInfo.SymbolsPath.IsEmpty(); TArray< TSharedRef<ISourceControlLabel> > Labels = ISourceControlModule::Get().GetProvider().GetLabels( CrashInfo.LabelName ); const bool bContainsProductVersion = FPDBCache::Get().ContainsPDBCacheEntry( CrashInfo.EngineVersion ); if( bHasExecutable && bHasSymbols ) { if( bContainsProductVersion ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "Using cached storage: %s" ), *CrashInfo.EngineVersion ); CrashInfo.PDBCacheEntry = FPDBCache::Get().FindAndTouchPDBCacheEntry( CrashInfo.EngineVersion ); } else { SCOPE_LOG_TIME_IN_SECONDS( TEXT( "SyncExecutableAndSymbolsFromNetwork" ), nullptr ); // Find all executables. TArray<FString> NetworkExecutables; IFileManager::Get().FindFilesRecursive( NetworkExecutables, *CrashInfo.ExecutablesPath, TEXT( "*.dll" ), true, false, false ); IFileManager::Get().FindFilesRecursive( NetworkExecutables, *CrashInfo.ExecutablesPath, TEXT( "*.exe" ), true, false, false ); // Find all symbols. TArray<FString> NetworkSymbols; IFileManager::Get().FindFilesRecursive( NetworkSymbols, *CrashInfo.SymbolsPath, TEXT( "*.pdb" ), true, false, false ); // From=Full pathname // To=Relative pathname TMap<FString, FString> FilesToBeCached; for( const auto& ExecutablePath : NetworkExecutables ) { const FString NetworkRelativePath = ExecutablePath.Replace( *CrashInfo.ExecutablesPath, TEXT( "" ) ); FilesToBeCached.Add( ExecutablePath, NetworkRelativePath ); } for( const auto& SymbolPath : NetworkSymbols ) { const FString SymbolRelativePath = SymbolPath.Replace( *CrashInfo.SymbolsPath, TEXT( "" ) ); FilesToBeCached.Add( SymbolPath, SymbolRelativePath ); } // Initialize and add a new PDB Cache entry to the database. CrashInfo.PDBCacheEntry = FPDBCache::Get().CreateAndAddPDBCacheEntryMixed( CrashInfo.EngineVersion, FilesToBeCached ); } } // Get all labels associated with the crash info's label. else if( Labels.Num() >= 1 ) { TSharedRef<ISourceControlLabel> Label = Labels[0]; TSet<FString> FilesToSync; // Use product version instead of label name to make a distinguish between chosen methods. const bool bContainsLabelName = FPDBCache::Get().ContainsPDBCacheEntry( CrashInfo.LabelName ); if( bContainsProductVersion ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "Using cached storage: %s" ), *CrashInfo.EngineVersion ); CrashInfo.PDBCacheEntry = FPDBCache::Get().FindAndTouchPDBCacheEntry( CrashInfo.EngineVersion ); } else if( bContainsLabelName ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "Using cached storage: %s" ), *CrashInfo.LabelName ); CrashInfo.PDBCacheEntry = FPDBCache::Get().FindAndTouchPDBCacheEntry( CrashInfo.LabelName ); } else if( bHasExecutable ) { SCOPE_LOG_TIME_IN_SECONDS( TEXT( "SyncModulesAndNetwork" ), nullptr ); // Grab information about symbols. TArray< TSharedRef<class ISourceControlRevision, ESPMode::ThreadSafe> > PDBSourceControlRevisions; const FString PDBsPath = FString::Printf( TEXT( "%s/%s....pdb" ), *CrashInfo.DepotName, UESymbols ); Label->GetFileRevisions( PDBsPath, PDBSourceControlRevisions ); TSet<FString> PDBPaths; for( const auto& PDBSrc : PDBSourceControlRevisions ) { PDBPaths.Add( PDBSrc->GetFilename() ); } // Now, sync symbols. for( const auto& PDBPath : PDBPaths ) { if( Label->Sync( PDBPath ) ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "Synced PDB: %s" ), *PDBPath ); } } // Find all the executables in the product network path. TArray<FString> NetworkExecutables; IFileManager::Get().FindFilesRecursive( NetworkExecutables, *CrashInfo.ExecutablesPath, TEXT( "*.dll" ), true, false, false ); IFileManager::Get().FindFilesRecursive( NetworkExecutables, *CrashInfo.ExecutablesPath, TEXT( "*.exe" ), true, false, false ); // From=Full pathname // To=Relative pathname TMap<FString, FString> FilesToBeCached; // If a symbol matches an executable, add the pair to the list of files that should be cached. for( const auto& NetworkExecutableFullpath : NetworkExecutables ) { for( const auto& PDBPath : PDBPaths ) { const FString PDBRelativePath = PDBPath.Replace( *CrashInfo.DepotName, TEXT( "" ) ).Replace( UESymbols, TEXT( "" ) ); const FString PDBFullpath = FPDBCache::Get().GetDepotRoot() / PDBPath.Replace( P4_DEPOT_PREFIX, TEXT( "" ) ); const FString PDBMatch = PDBRelativePath.Replace( TEXT( "pdb" ), TEXT( "" ) ); const FString NetworkRelativePath = NetworkExecutableFullpath.Replace( *CrashInfo.ExecutablesPath, TEXT( "" ) ); const bool bMatch = NetworkExecutableFullpath.Contains( PDBMatch ); if( bMatch ) { // From -> Where FilesToBeCached.Add( NetworkExecutableFullpath, NetworkRelativePath ); FilesToBeCached.Add( PDBFullpath, PDBRelativePath ); break; } } } // Initialize and add a new PDB Cache entry to the database. CrashInfo.PDBCacheEntry = FPDBCache::Get().CreateAndAddPDBCacheEntryMixed( CrashInfo.EngineVersion, FilesToBeCached ); } else { TArray<FString> FilesToBeCached; //@TODO: MAC: Excluding labels for Mac since we are only syncing windows binaries here... if( Label->GetName().Contains( TEXT( "Mac" ) ) ) { UE_LOG( LogCrashDebugHelper, Log, TEXT( "Skipping Mac label: %s" ), *Label->GetName() ); } else { // Sync all the dll, exes, and related symbol files UE_LOG( LogCrashDebugHelper, Log, TEXT( "Syncing modules with label: %s" ), *Label->GetName() ); SCOPE_LOG_TIME_IN_SECONDS( TEXT( "SyncModules" ), nullptr ); // Grab all dll and pdb files for the specified label. TArray< TSharedRef<class ISourceControlRevision, ESPMode::ThreadSafe> > DLLSourceControlRevisions; const FString DLLsPath = FString::Printf( TEXT( "%s/....dll" ), *CrashInfo.DepotName ); Label->GetFileRevisions( DLLsPath, DLLSourceControlRevisions ); TArray< TSharedRef<class ISourceControlRevision, ESPMode::ThreadSafe> > EXESourceControlRevisions; const FString EXEsPath = FString::Printf( TEXT( "%s/....exe" ), *CrashInfo.DepotName ); Label->GetFileRevisions( EXEsPath, EXESourceControlRevisions ); TArray< TSharedRef<class ISourceControlRevision, ESPMode::ThreadSafe> > PDBSourceControlRevisions; const FString PDBsPath = FString::Printf( TEXT( "%s/....pdb" ), *CrashInfo.DepotName ); Label->GetFileRevisions( PDBsPath, PDBSourceControlRevisions ); TSet<FString> ModulesPaths; for( const auto& DLLSrc : DLLSourceControlRevisions ) { ModulesPaths.Add( DLLSrc->GetFilename().Replace( *CrashInfo.DepotName, TEXT( "" ) ) ); } for( const auto& EXESrc : EXESourceControlRevisions ) { ModulesPaths.Add( EXESrc->GetFilename().Replace( *CrashInfo.DepotName, TEXT( "" ) ) ); } TSet<FString> PDBPaths; for( const auto& PDBSrc : PDBSourceControlRevisions ) { PDBPaths.Add( PDBSrc->GetFilename().Replace( *CrashInfo.DepotName, TEXT( "" ) ) ); } // Iterate through all module and see if we have dll and pdb associated with the module, if so add it to the files to sync. for( const auto& ModuleName : CrashInfo.ModuleNames ) { const FString ModuleNamePDB = ModuleName.Replace( TEXT( ".dll" ), TEXT( ".pdb" ) ).Replace( TEXT( ".exe" ), TEXT( ".pdb" ) ); for( const auto& ModulePath : ModulesPaths ) { const bool bContainsModule = ModulePath.Contains( ModuleName ); if( bContainsModule ) { FilesToSync.Add( ModulePath ); } } for( const auto& PDBPath : PDBPaths ) { const bool bContainsPDB = PDBPath.Contains( ModuleNamePDB ); if( bContainsPDB ) { FilesToSync.Add( PDBPath ); } } } // Now, sync all files. for( const auto& Filename : FilesToSync ) { const FString DepotPath = CrashInfo.DepotName + Filename; if( Label->Sync( DepotPath ) ) { UE_LOG( LogCrashDebugHelper, Warning, TEXT( "Synced binary: %s" ), *DepotPath ); } FilesToBeCached.Add( DepotPath ); } } // Initialize and add a new PDB Cache entry to the database. CrashInfo.PDBCacheEntry = FPDBCache::Get().CreateAndAddPDBCacheEntry( CrashInfo.LabelName, CrashInfo.DepotName, FilesToBeCached ); } } else { UE_LOG( LogCrashDebugHelper, Error, TEXT( "Could not find label: %s"), *CrashInfo.LabelName ); return false; } return true; }
static void FindInvalidScalableFloats(const TArray<FString>& Args, bool ShowCoeffecients) { GCurrentBadScalableFloatList.Empty(); TArray<UClass*> ClassesWithScalableFloats; for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* ThisClass = *ClassIt; if (FindClassesWithScalableFloat_r(Args, ThisClass, ThisClass)) { ClassesWithScalableFloats.Add(ThisClass); ABILITY_LOG(Warning, TEXT("Class has scalable float: %s"), *ThisClass->GetName()); } } for (UClass* ThisClass : ClassesWithScalableFloats) { UObjectLibrary* ObjLibrary = nullptr; TArray<FAssetData> AssetDataList; TArray<FString> Paths; Paths.Add(TEXT("/Game/")); { FString PerfMessage = FString::Printf(TEXT("Loading %s via ObjectLibrary"), *ThisClass->GetName() ); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) ObjLibrary = UObjectLibrary::CreateLibrary(ThisClass, true, true); ObjLibrary->LoadBlueprintAssetDataFromPaths(Paths, true); ObjLibrary->LoadAssetsFromAssetData(); ObjLibrary->GetAssetDataList(AssetDataList); ABILITY_LOG( Warning, TEXT("Found: %d %s assets."), AssetDataList.Num(), *ThisClass->GetName()); } for (FAssetData Data: AssetDataList) { UPackage* ThisPackage = Data.GetPackage(); UBlueprint* ThisBlueprint = CastChecked<UBlueprint>(Data.GetAsset()); UClass* AssetClass = ThisBlueprint->GeneratedClass; UObject* ThisCDO = AssetClass->GetDefaultObject(); FString PathName = ThisCDO->GetName(); PathName.RemoveFromStart(TEXT("Default__")); GCurrentBadScalableFloat.Asset = ThisCDO; //ABILITY_LOG( Warning, TEXT("Asset: %s "), *PathName ); CheckForBadScalableFloats_r(ThisCDO, AssetClass, AssetClass); } } ABILITY_LOG( Error, TEXT("")); ABILITY_LOG( Error, TEXT("")); if (ShowCoeffecients == false) { for ( FBadScalableFloat& BadFoo : GCurrentBadScalableFloatList) { ABILITY_LOG( Error, TEXT(", %s, %s, %s,"), *BadFoo.Asset->GetFullName(), *BadFoo.Property->GetFullName(), *BadFoo.String ); } ABILITY_LOG( Error, TEXT("")); ABILITY_LOG( Error, TEXT("%d Errors total"), GCurrentBadScalableFloatList.Num() ); } else { ABILITY_LOG( Error, TEXT("Non 1 coefficients: ")); for ( FBadScalableFloat& BadFoo : GCurrentNaughtyScalableFloatList) { ABILITY_LOG( Error, TEXT(", %s, %s, %s"), *BadFoo.Asset->GetFullName(), *BadFoo.Property->GetFullName(), *BadFoo.String ); } } }
void UGameplayTagsManager::ConstructGameplayTagTree() { if (!GameplayRootTag.IsValid()) { GameplayRootTag = MakeShareable(new FGameplayTagNode()); { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: Construct from data asset")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif for (auto It(GameplayTagTables.CreateIterator()); It; It++) { if (*It) { PopulateTreeFromDataTable(*It); } } } if (ShouldImportTagsFromINI()) { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: ImportINI")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif // Update path: Check for old tags in DefaultEngine.ini (we'll push them to the UGameplayTagSettings class). TArray<FString> EngineConfigTags; GConfig->GetArray(TEXT("/Script/GameplayTags.GameplayTagsSettings"), TEXT("GameplayTags"), EngineConfigTags, GEngineIni); if (EngineConfigTags.Num() > 0) { UGameplayTagsSettings* MutableDefault = GetMutableDefault<UGameplayTagsSettings>(); if (MutableDefault->GameplayTags.Num() == 0) { MutableDefault->GameplayTags.Append(EngineConfigTags); } } // Load any GameplayTagSettings from config (their default object) for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt) { UClass* Class = *ClassIt; if (!Class->IsChildOf<UGameplayTagsSettings>() || Class->HasAnyClassFlags(CLASS_Abstract)) { continue; } #if WITH_EDITOR Class->GetDefaultObject<UGameplayTagsSettings>()->SortTags(); #endif for (FString TagStr : Class->GetDefaultObject<UGameplayTagsSettings>()->GameplayTags) { FGameplayTagTableRow TableRow; TableRow.Tag = TagStr; AddTagTableRow(TableRow); } } GameplayRootTag->GetChildTagNodes().Sort(FCompareFGameplayTagNodeByTag()); } if (ShouldUseFastReplication()) { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: Reconstruct NetIndex")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif ConstructNetIndex(); } { #if STATS FString PerfMessage = FString::Printf(TEXT("UGameplayTagsManager::ConstructGameplayTagTree: GameplayTagTreeChangedEvent.Broadcast")); SCOPE_LOG_TIME_IN_SECONDS(*PerfMessage, nullptr) #endif GameplayTagTreeChangedEvent.Broadcast(); } }