GatherTextFromAssetsCommandlet doesn't work correctly for complex IncludePathFilters

After upgrading to UE 5.6 from UE 5.5, we found that gathering loc from assets no longer works.

Context about our project setup:

  1. We define our localization text in `.uasset` files, and we use the `GatherTextFromAssets` commandlet to gather them, and then pass them to our translators.
  2. We configure our localization target through the Localization dashboard, and our asset gather step is configured with `IncludePathFilters=%LOCPROJECTROOT%Plugins/GameFeatures/*`
  3. The GameFeatures directory contains a number of Game Feature plugins, whose assets we want to scan. The number of plugins is large and we don’t want to list them 1 by 1.

With UE 5.5 this worked (Gather would find our text), with 5.6 it doesn’t work (Gather finds 0 text).

Culprit:

This change updated asset filters, and more specifically the FirstPassFilter, which I think is an optimization to load less assets when a plugin path is used (all the way to a Plugin’s `Content` directory), to set `InOutFilter.WithoutPackageFlags = PKG_Cooked;`

This FistPassFilter is later checked with `IsEmpty()` to determine if we should scan all assets, or just the assets pertaining to the filter, however, `IsEmpty()` takes into account the `WithoutPackageFlags` that was already set to non 0, so the `IsEmpty()` check never evaluates to `true`. See:

https://github.com/EpicGames/UnrealEngine/blob/6978b63c8951e57d97048d8424a0bebd637dde1d/Engine/Source/Runtime/CoreUObject/Public/AssetRegistry/ARFilter.h\#L108

How to fix

I would like to discuss possible ways to fix this, since I don’t have context on all the possible scenarios of using the commandlet.

Option A:

In `UGatherTextFromAssetsCommandlet::ApplyFirstPassFilter`, replace the `if (InFilter.IsEmpty())`

with `if ((InFilter.PackageNames.Num() + InFilter.PackagePaths.Num() + InFilter.SoftObjectPaths.Num() + InFilter.ClassPaths.Num() + InFilter.TagsAndValues.Num()) == 0)

which more correctly represents the concept of “are there any assets in the first pass filter?”

https://github.com/EpicGames/UnrealEngine/blob/6978b63c8951e57d97048d8424a0bebd637dde1d/Engine/Source/Editor/UnrealEd/Private/Commandlets/GatherTextFromAssetsCommandlet.cpp\#L939

Option B:

Move the setting of the `WithoutPackageFlags`, so it happens after we evaluate `IsEmpty()`.

void UGatherTextFromAssetsCommandlet::ApplyFirstPassFilter(/*removed const*/ FARFilter& InFilter, TArray<FAssetData>& InOutAssetDataArray) const
{
    // Apply filter if valid to do so, get all assets otherwise.
    if (InFilter.IsEmpty())
    {
       // @TODOLocalization: Logging that the first path filter is empty resulting in all assets being gathered can confuse users who generally rely on the second pass.
       // Figure out a good way to still convey the information in a log or clog.
       const double GetAllAssetsStartTime = FPlatformTime::Seconds();
       IAssetRegistry::GetChecked().GetAllAssets(InOutAssetDataArray);
       UE_LOG(LogGatherTextFromAssetsCommandlet, Display, TEXT("Loading all assets from asset registry took %.2f seconds."), FPlatformTime::Seconds() - GetAllAssetsStartTime);
    }
    else
    {
       const double GetAllAssetsWithFirstPassFilterStartTime = FPlatformTime::Seconds();

// NEW CODE HERE  - Note we can also rename InFilter to InOutFilter since it's changed
       InFilter.bIncludeOnlyOnDiskAssets = true;
       InFilter.WithoutPackageFlags = PKG_Cooked;
// ---- 

       IAssetRegistry::GetChecked().GetAssets(InFilter, InOutAssetDataArray);
       UE_LOG(LogGatherTextFromAssetsCommandlet, Display, TEXT("Getting all assets with first pass filter from asset registry took %.2f seconds."), FPlatformTime::Seconds() - GetAllAssetsWithFirstPassFilterStartTime);
    }
}

I think that Option B is more maintainable, but I’m curious about what you think, and possibly another option.

Steps to Reproduce

  1. Create an empty game project
  2. Enable Gameplay Feature Plugins
  3. Create 2 plugins
  4. Add a blueprint in each plugin that contains localizable text. E.g. I added a text variable to each BP. Add some content to the strings.
  5. Open the Localization Dashboard, click on “Gather from packages”, and as the path use the root of your plugins folder.
  6. Run Gather Text.

Expected:

You should get 2 strings (Gather will report the number of words).

Observed:

Gather finds 0 strings.

Hey, I think you’re describing the same symptoms that this change fixed. Can you try merging it and let me know.

https://github.com/EpicGames/UnrealEngine/commit/3c93afd213c6b7ff0b5f7a85dd063304af27401a

I can confirm this change did fix the issue. Thank you!