#Loading an FSoftClassPath results in a crash with child Blueprints.

1 messages · Page 1 of 1 (latest)

molten wren
#

So I have some DataAssets which have a property in them that I'm using as a lookup key for a function to load that DataAsset on demand.

It looks like this:

TSoftClassPtr<UAwardDefinition> UAwardSubsystem::RequestAwardDefinitionByName(FGameplayTag Award)

The function body essentially uses the AssetRegistry to gather all of those DataAssets and loads them to check which of the DataAssets have the Award tag passed into the function as seen below.

#
TSoftClassPtr<UAwardDefinition> Result;
    const TSoftClassPtr<UAwardDefinition>* FoundAward = AwardDefinitions.Find(Award);
    if (FoundAward)
    {
        Result = *FoundAward;
    }
    else
    {
        const FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
        const IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

        FARFilter Filter;
        Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
        Filter.bRecursiveClasses = true;
        Filter.bRecursivePaths = true;
        Filter.PackagePaths.Add(FName("/Game/AwardDefinitions"));

        TArray<FAssetData> AssetDataList;
        AssetRegistry.GetAssets(Filter, AssetDataList);
        for (const FAssetData& AssetData : AssetDataList)
        {
            const UObject* AssetObject = AssetData.ToSoftObjectPath().TryLoad();
            const UBlueprint* AssetBlueprint = Cast<UBlueprint>(AssetObject);
            if (IsValid(AssetBlueprint))
            {
                const UAwardDefinition* AwardDefinition = Cast<UAwardDefinition>(AssetBlueprint->GeneratedClass.GetDefaultObject());
                if (IsValid(AwardDefinition))
                {
                    AwardDefinitions.Add(Award, AwardDefinition->GetClass());
                    if (AwardDefinition->GetNameTag() == Award)
                    {
                        Result = AwardDefinition->GetClass();
                        break;
                    }
                }
            }
        }
    }
    
    return Result;
#

However, there is a crash thats occurring when the TryLoad function is called on the SoftObjectPath of the AssetData.

const UObject* AssetObject = AssetData.ToSoftObjectPath().TryLoad();

[File:E:/ProjectGenesisSourceEngine/Engine/Source/Runtime/CoreUObject/Private/Serialization/AsyncLoading.cpp] [Line: 3159] Could not find SuperStruct BP_AwardDefinition_C to create AD_VaultKey_C

What I noticed is that the AsyncLoader is unable to find/load(?) the parent Blueprint of the DataAsset its currently trying to load.

The class hierarchy of AD_VaultKey is as follows:
UPrimaryDataAsset->UAwardDefinition->UPGAwardDefinition->BP_AwardDefinition->AD_VaultKey

This issue only occurs in packaged builds.

Ive made sure that the Directory of all of these DataAssets exists in, is always included in the cooking process.

I made a hard reference to BP_AwardDefinition elsewhere in the project and doing so no longer results in a crash for DataAssets with it as a direct parent, other DataAssets that are further children basically crash on their immediate parent with the same error.

Should I not be loading Assets this way? What code path would result in an Asset with Blueprint Parents also allowing those to be loaded, avoiding the crash?

Any insite would be helpful.

true sail
#

Could it possibly be that your code is trying to reference a module that has yet to be loaded?

#

Probably not, but thought it would be worth asking

molten wren
#

I made a hard reference to BP_AwardDefinition elsewhere in the project and doing so no longer results in a crash for DataAssets with it as a direct parent, other DataAssets that are further children basically crash on their immediate parent with the same error.

true sail
#

I remember seeing something about someone running into something similar about only having cooked builds crash on a super class not being available and it was due to somehow them running their module at the wrong stage

molten wren
#

It works as expected if I make hard references to the assets elsewhere.

leaden cave
#

that forces it to load, which can mess up module load order

molten wren
#

Ideally I dont want to have to hard reference all of the Assets, cause that defeats the purpose lol.

west shuttle
#

so its crashing cause BP_AwardDefinition is not valid right?

#

ie not loaded

molten wren
#

Yes

#

It appears thats the case

#

Its trying to load the child first

#

Then crashing cause the parent isnt loaded

west shuttle
#

im confused with this UPrimaryDataAsset->UAwardDefinition->UPGAwardDefinition->BP_AwardDefinition->AD_VaultKey

#

so AD_VaultKey is based on BP_AwardDef

molten wren
#

Yes

west shuttle
#

BP_AwaredDef is an asset or a blueprint?

molten wren
#

Sorry, Its a blueprint, they are all blueprints

#

I mispoke

west shuttle
#

but UPGAwardDefinition is a PrimaryDataAsset

leaden cave
#

and you do that the second your module loads?

west shuttle
#

then you made a blueprint of that, just so im clear

molten wren
#

Correct

leaden cave
#

i'm still assuming that it's a loading order issue

west shuttle
#

then you made a Asset based on that BP?

#

or is AD still a blueprint?

leaden cave
#

hard referencing your parent just forces a different load order

#

likely that you could hard ref anything from the content directory, and it would "fix" the crash

west shuttle
#

does it show up properly in the asset auditor (thats the first place i always check)

molten wren
#

Hold on Im just loading the Editor, Ill show you how they are setup

leaden cave
#

and if this is the case, you are in UB territory already, since you are probably querying the asset registry while it's being populated

molten wren
#

@west shuttle

west shuttle
#

ah so they are all blueprints

#

not true assets

molten wren
#

Yeah, originally they would have been pure DataAssets, but as the design moved on, I needed them to also run code.

#

So I just left them as PrimaryDataAssets and just made them as Blueprints instead.

west shuttle
#

yeah, we had the same issue, in the end we put an instanced object inside them

#

which ran logic 😛

#

and kept them as assets

molten wren
#

Ehhh

#

But then I would need ANOTHER asset lol

west shuttle
#

yeah 😛

#

its how we did it

#

worked fine tbh, but 🤷

#

anyway thats no solving your issue

#

do you have any debug info on the crash

#

other than whats above

#

ie have you crashed with debugger attached? or its not possible?

molten wren
#

Thats the best crash log I have of it.

#

You can see in this one its not the same asset.

#

Since at that point i had a hard ref to the base Blueprint

west shuttle
#

one thing i would probably do, cause ofc its not loading the parent class, thats your issue, we have our asset manager actually preload a bunch of stuff on init

molten wren
#

I havent had a chance to attach a debugger yet.

leaden cave
#

ok, my assumption was wrong based on that crash

west shuttle
#

the thing thats happening is

#

its trying to load a primary asset

#

AD_Blah

#

which is derived from a BP base

#

can you show me your asset manager setup

#

for that paticular thing?

#

for example this?

molten wren
west shuttle
#

yeah you need to tick Has Blueprint Classes

#

else it will go boom in packaged

molten wren
#

It is ticked

west shuttle
#

oh

#

i was looking at mine HAH

#

damn im tired today

molten wren
#

Should I also have an entry for BP_AwardDefintion?

west shuttle
#

the base class

#

should be the BP

molten wren
#

The base blueprint class

west shuttle
#

not award definition

molten wren
#

Ok

#

The C++ class is the one in the entry

west shuttle
#

yeah

molten wren
#

Will that fix it though?

#

You reckon this will help?

west shuttle
#

i mean i have never seen that error, im just looking what Asset Base Class really means

#

in the context of loading

#

uno momento

#

yes that should cause

#

TObjectPtr<UClass> AssetBaseClassLoaded;

#

it stored the loaded base class

#

which should load and store that bp

molten wren
#

But what about children of children of that Blueprint?

west shuttle
#

it wont load them

#

you still ahve the logic above to load them

#

but the base bp class will be loaded

#

cause its the base for the asset

molten wren
#

Yes but what Im saying is that BP_AwardDefinition->AD_Kill might be fine to load because BP_AwardDefinition is the immediate parent, however BP_AwardDefintiion->AD_Kill->AD_Kill_Bot will still crash?

#

If AD_Kill is not loaded?

west shuttle
#

that i am not sure off, hmm

#

i would really need to have some way to test it

molten wren
#

I feel like it will still be a problem.

#

Another thought I had, was if there was a way to sort the AssetDataList before we start loading such that the parents are loaded first in all cases.

west shuttle
#

what is actually in the assetdatalist

#

when you call that function

molten wren
#

Basically all of those Blueprints

#

In the order they were found by the registry

#

TArray<FAssetData> AssetDataList;
AssetRegistry.GetAssets(Filter, AssetDataList);

#

Probably alphabetically

west shuttle
#

including the parent ?

molten wren
#

Yes

#
FARFilter Filter;
        Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName());
        Filter.bRecursiveClasses = true;
        Filter.bRecursivePaths = true;
        Filter.PackagePaths.Add(FName("/Game/AwardDefinitions"));
#

Grab all Blueprints in that Directory.

#

Thats what its doing.

west shuttle
#

sec

#

one problem i see is your also using TryLoad

molten wren
#

Well thats one part I was unsure about anyway.

#

Is TryLoad the right way to manage this?

#

Or should I be using a different code path?

west shuttle
#

whichjj is a sync load

#

not async load

#

i assume you want them instant?

molten wren
#

At this point its fine

#

I just want it working lol

#

I thought maybe doing GetAsset on the FAssetData first might be a better approach?

#

I figured that might load the parents as well, prior to TryLoad on the SoftObjectPath

west shuttle
#

have you tried

#

FStreamableManagers SyncLoad ?

molten wren
#

No I havent.

west shuttle
#
AssetManager.GetStreamableManager().LoadSynchronous(Path, false);```
#

iirc

#

its smarther

#

smarter*

#

than TryLoad

molten wren
#

Ok ill have a look

west shuttle
#

its how we load stuff

#

through streamblemanager

#
UObject* UIronwardAssetManager::SynchronousLoadAsset(const FSoftObjectPath& AssetPath)
{
    if (AssetPath.IsValid())
    {
        if (ShouldLogAssetLoads())
        {
            TUniquePtr<FScopeLogTime> LogTimePtr = MakeUnique<FScopeLogTime>(*FString::Printf(TEXT("Synchronously loaded asset [%s]"), *AssetPath.ToString()), nullptr, FScopeLogTime::ScopeLog_Seconds);
        }

        if (UAssetManager::IsInitialized())
        {
            return GetStreamableManager().LoadSynchronous(AssetPath, false);
        }

        // Use LoadObject if asset manager isn't ready yet.
        return AssetPath.TryLoad();
    }

    return nullptr;
}
#

but it might still fail cause the base classes are not loaded

#

again i need to make a test to see

molten wren
#
            const UObject* AssetObject = AssetManager.GetStreamableManager().LoadSynchronous(AssetData.ToSoftObjectPath(), false);
            //const UObject* AssetObject = AssetData.ToSoftObjectPath().TryLoad();
#

So thats your suggestion?

west shuttle
#

well i know for a fact adding the BP base class to assetmanager will cause it to load it

#

void FPrimaryAssetTypeInfo::FillRuntimeData(bool& bIsValid, bool& bBaseClassWasLoaded)

#

as its done in here

molten wren
#

Yeah, however that just concerns me for children of children.

west shuttle
#

yeah that is what im not sure off

#

cause we never have such a heirachy

#

of Blueprints from primary data assets

molten wren
#

Ill try with StreamableManager and see what results come out of it

#

Well I can probably not make the PDAs

#

Anymore

#

If that changes anything

#

I can make them regular UObjects

#

But I dont really see how it does.

#

Since a PDA is still a UObject

west shuttle
#

yeah but when you mark something primary

#

it is no longer "automagically loaded" including its CDO

#

iirc

#

ie, the engine has no idea about it

molten wren
#

Yeah fair point.

west shuttle
#

and thats why you got that crash

#

cause that BP class was not found at all, engine didnt have a clue about it

molten wren
#

Yep

west shuttle
#

are they too big to always have loaded in the game?

molten wren
#

There are a lot of them, with UI stuff like textures and sounds etc.

west shuttle
#

yeah

#

then if you dont need them you dont want to load them

molten wren
#

160+ something like that

#

They are basically Accolades.

west shuttle
#

trying to understand what HasBlueprintClasses

#

actually does

molten wren
#

They produce pretty stuff on screen for doing things

#

XP payouts etc

west shuttle
#

you could try

#

but not sure if that would really help

molten wren
#

I dont think using the StreamingManager is going to help.

#

It ultimately leads to the same call into FlushAsyncLoading that TryLoad does.

#

So Im thinking it will just have the same result.

west shuttle
#

:/

#

is it not a 100% crash then?

molten wren
#

It is, as soon as you do something ingame that requires RequestAwardDefinitionByName to be called.

#

Like picking up a resource or something

west shuttle
#

if you could crash or breakpoint with more info would be good, would like to see that heirachy, it shouldnt no longer crash on direct children, but further children may pose the same issue like you said. Thing is, you cant determine the class heirachy at all

#

cause that bp is not loaded and you cant load it

molten wren
#

Yeah that was my initial impression.

#

Thing is, there is only like a couple of them that have children of children.

west shuttle
#

yeah

molten wren
#

Of the roughly 160, maybe 5 have children.

west shuttle
#

this is uncharted teritory for me really its not something i have ever done

molten wren
#

I wonder if I could serialize them at Editor time and load those at runtime.

#

When the game starts

west shuttle
#

i mean the only thing you really have

#

is the registry

#

you can tag specific property or class

#

and read that without loading the asset

molten wren
#

What do you mean?

west shuttle
#

well all PDA's can have tags

#

which you can query without loading the asset

molten wren
#

How does that work?

west shuttle
#

well you can mark the property AssetRegistrySearchable or you can manually add tags

molten wren
#

Oh

west shuttle
#

iirc

#

Data Assets have

#

NativeClass

#

one sec

molten wren
#

Does that still work even if they are BPs and not actual DAs?

west shuttle
#
    UPROPERTY(AssetRegistrySearchable)
    TSubclassOf<UDataAsset> NativeClass;```
#

inside DataAsset

molten wren
#
    /* The name of the Award. */
    UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, meta = (AllowPrivateAccess = true, DisplayPriority = 0, Categories = "Awards"), Category = "Settings")
    FGameplayTag NameTag = FGameplayTag::EmptyTag;
#

This is in UAwardDefinition this is what that function is looking for.

#

Your saying if I mark that RegistrySearchable?

#

It might mean I dont need ot load it at all?

#

Cause theoretically, I could use the fact its a tag to assume inheritance.

#

Loading the parents first.

#

Via the tags

#

Award.Kill for AD_Kill
Award.Kill.Bot for AD_Kill_Bot

#

Since AD_Kill_Bot is a child of AD_Kill

west shuttle
#

yeah you can find assets

#

based on the tag

molten wren
#

Ok well I guess I need to research that then.

west shuttle
#
    TMultiMap<FName, FString> TagsAndValues;
    TagsAndValues.Add(TEXT("AtlasGroupGUID"), Atlas->AtlasGUID.ToString(EGuidFormats::Digits));
    AssetRegistryModule.Get().GetAssetsByTagValues(TagsAndValues, AssetList);```
molten wren
#

Ive never used AssetRegistrySearchable before

west shuttle
#

basically its like data you can read

#

without loading the asset

#

so you can query assets without needing to load it

#

its like stored meta data

molten wren
#

Ill give it a go

west shuttle
#

you can use FARFilter

#

also to filter assets with specific tags

#

you could grab all the FAssetData

#

for the stuff you grabbed

#

sort it then load the correct way

molten wren
#

This approach might end up being better anyway

#

Becuase if it works, then its only going to grab the exact assets I need.

west shuttle
#

yeah]

#

it works with gameplay tags

molten wren
#

Sick

#

Ill see how it goes.

west shuttle
#

cause they are just FName

#

we use it quite a lot

#

to grab specific item definitions

molten wren
#

Yeah and with how I have it setup they are basically mirroring the inheritance of the assets anyway

west shuttle
#

based on tag container

#

like for our UI, we have weapons, but we don't want to load all weapons, if your only looking at say "shotguns"

#

but all weapons are based on RangedWeaponItemDefinition

molten wren
#

For sure.

#

This sounds like what I should have done to begin with.

west shuttle
#

we also use bundles

unreal epoch
#

Ah I was gonna mention AssetRegistrySearchable

west shuttle
#

so we only load what we need for main menu compared to gameplay

molten wren
#

Yeah I wasnt sure if Bundles would have resulted in the same issue

unreal epoch
#

But that won’t address the unreachable parent class

molten wren
#

Award.Kill.Bot

#

Award.Kill is the parent asset

unreal epoch
#

Oh that’s true

west shuttle
#

its a hack

molten wren
#

It is

west shuttle
#

but should work

molten wren
#

lol

#

I can setup some editor stuff to verify assets adhere to this as a requirement.

unreal epoch
#

Could probably even do that in a single pass with zero unnecessary loading

molten wren
#

Oh, I think I know what you mean.

#

UAwardSubsystem::RequestAwardDefinitionByName(FGameplayTag Award)

#

Award would be the AssetRegistrySearchable

#

Award.Kill.Bot

#

I can search for just the assets that have Award.Kill and Award.Kill.Bot

#

Load them in that order.

#

No need to load any other assets.

#

Is that what you mean?

#
        TMultiMap<FName, FString> TagsAndValues;
        const FGameplayTagContainer AwardParents = Award.GetGameplayTagParents();
        for (const FGameplayTag Tag : AwardParents)
        {
            TagsAndValues.Add(TEXT("NameTag"), Tag.ToString());
        }

        // Dont forget the actual requested Award, since it wont be in the Parent container?
        TagsAndValues.Add(TEXT("NameTag"), Award.ToString());
        
        TArray<FAssetData> AssetDataList;
        AssetRegistry.GetAssetsByTagValues(TagsAndValues, AssetDataList);
#

@west shuttle Thats what you are suggesting right?

#

NameTag is marked as AssetRegistrySearchable in the UAwardDefinition class.

#

Well its wrong anyway, I have to iterate the container backwards.

molten wren
#
        const IAssetRegistry& AssetRegistry = AssetRegistryModule.Get();

        // We assume here that the class hierarchy of the AwardDefinition that we are looking for has the same structure as the Award tag.
        // If we shove all of the parent tags into an array, we can use them to search the registry for the AwardDefinitions in the order they need to be loaded.
        TMultiMap<FName, FString> TagsAndValues;
        for (const FGameplayTag& Tag : Award.GetGameplayTagParents())
        {
            TagsAndValues.Add(TEXT("NameTag"), Tag.ToString());
        }
        
        TArray<FAssetData> AssetDataList;
        AssetRegistry.GetAssetsByTagValues(TagsAndValues, AssetDataList);

        // Sort them by their NameTag, since they most likely will be out of order.
        // We can safely assume that the NameTag length is a good indicator of order, the longer the closer it is to the requested one.
        AssetDataList.Sort([](const FAssetData& A, const FAssetData& B)
        {
            FString ANameTag;
            A.GetTagValue<FString>(TEXT("NameTag"), ANameTag);

            FString BNameTag;
            B.GetTagValue<FString>(TEXT("NameTag"), BNameTag);

            return ANameTag < BNameTag;
        });

        // Now that we have the AwardDefinitions parents in order, lets load them in that order and return the requested definition.
        for (const FAssetData& AssetData : AssetDataList)
        {
          ...
#

This seems to work.

#

At least in Editor.

molten wren
#

Thanks for showing me that AssetRegistrySearchable thing. Thats like a game changer lol

unreal epoch
#

I’m glad it worked out, but yeah something like that

molten wren
#

Well I just got around to testing it in a packaged build.

#

On the plus side, its not crashing anymore.

#

On the down side, its just simply not loading them at all, as if they dont exist.

west shuttle
#

hmm

#

when your around if you havent solved it

#

i could possibly try a test in a local isolated project

west shuttle
#

this spits out the correct values in packages also

#
//Extract parent tags
    TArray<FGameplayTag> OutTags;
    UGameplayTagsManager::Get().ExtractParentTags(InTag, OutTags);

    //Help to return a FString for the Asset Registry tag value checking
    auto SerializedString = [](FGameplayTag InToSerialize) -> FString
    {
        FString SerializedTag;

        FGameplayTag::StaticStruct()->ExportText(
            /*out*/ SerializedTag,
            &InToSerialize,
            nullptr,
            nullptr,
            PPF_None,
            nullptr
        );

        return SerializedTag;
    };

    //Get all assets with the same tag value
    UAssetManager& AssetManager = UAssetManager::Get();
    TMultiMap<FName, FString> Map;
    Map.Add("MyTag", SerializedString(InTag));
    for (const FGameplayTag& Tag : OutTags)
    {
        Map.Add("MyTag", SerializedString(Tag));
    }
    TArray<FAssetData> OutAssets;
    AssetManager.GetAssetRegistry().GetAssetsByTagValues(Map, OutAssets);
    
    //Sort by the most parent tag first
    OutAssets.Sort([&](const FAssetData& A, const FAssetData& B)
    {
        FString ExportedTagA, ExportedTagB;
        A.GetTagValue("GameplayTag", ExportedTagA);
        B.GetTagValue("GameplayTag", ExportedTagB);

        FGameplayTag TagA, TagB;
        TagA.FromExportString(ExportedTagA);
        TagB.FromExportString(ExportedTagB);

        int32 MatchA = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagA);
        int32 MatchB = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagB);

        return MatchA > MatchB;
    });

    TArray<UTestPrimaryDA*> LoadedObjects;

    for (const FAssetData& Asset : OutAssets)
    {
        FSoftObjectPath AssetPath = Asset.ToSoftObjectPath();

        UBlueprint* BP = Cast<UBlueprint>(AssetManager.GetStreamableManager().LoadSynchronous(AssetPath, true));
        // Get the class
        UClass* BPClass = BP ? BP->GeneratedClass : nullptr;

        // Use the class or CDO
        if (BPClass->IsChildOf(UTestPrimaryDA::StaticClass()))
        {
            UTestPrimaryDA* CDO = Cast<UTestPrimaryDA>(BPClass->GetDefaultObject());
            LoadedObjects.Add(CDO);
        }
    }

    //Return the last loaded in the list
    return LoadedObjects.Num() > 0 ? LoadedObjects[LoadedObjects.Num() - 1] : nullptr;```
#

@unreal epoch see any issues there?

#

not sure if he wants the class or the CDO object

#

but i left both options on the table here

#

going to test this in packaged quickly

molten wren
# west shuttle

These appear in a packaged build for me as well. It just fails to find them, almost like they didn't get cooked.

#

I don't understand though, I've set the packager settings to include the directory they exist in.

#

Im away from my PC for the night so can't really explain much further.

#

Ill checkin again tomorrow.

west shuttle
#

ah

#

did you mark that directory

#

as always cook?

west shuttle
#

i think i have a fully working soloution

#

just doing final tests

#

so @molten wren

#
//Extract parent tags
    TArray<FGameplayTag> OutTags;
    UGameplayTagsManager::Get().ExtractParentTags(InTag, OutTags);

    //Help to return a FString for the Asset Registry tag value checking
    auto SerializedString = [](FGameplayTag InToSerialize) -> FString
    {
        FString SerializedTag;

        FGameplayTag::StaticStruct()->ExportText(
            /*out*/ SerializedTag,
            &InToSerialize,
            nullptr,
            nullptr,
            PPF_None,
            nullptr
        );

        return SerializedTag;
    };

    //Get all assets with the same tag value
    UAssetManager& AssetManager = UAssetManager::Get();
    TMultiMap<FName, FString> Map;
    Map.Add("MyTag", SerializedString(InTag));
    for (const FGameplayTag& Tag : OutTags)
    {
        Map.Add("MyTag", SerializedString(Tag));
    }
    TArray<FAssetData> OutAssets;
    AssetManager.GetAssetRegistry().GetAssetsByTagValues(Map, OutAssets);
    
    //Sort by the most parent tag first
    OutAssets.Sort([&](const FAssetData& A, const FAssetData& B)
    {
        FString ExportedTagA, ExportedTagB;
        A.GetTagValue("GameplayTag", ExportedTagA);
        B.GetTagValue("GameplayTag", ExportedTagB);

        FGameplayTag TagA, TagB;
        TagA.FromExportString(ExportedTagA);
        TagB.FromExportString(ExportedTagB);

        int32 MatchA = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagA);
        int32 MatchB = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagB);

        return MatchA > MatchB;
    });

    TArray<UTestPrimaryDA*> LoadedObjects;

    for (const FAssetData& Asset : OutAssets)
    {
        FString GeneratedClassPath;
        Asset.GetTagValue(TEXT("GeneratedClass"), GeneratedClassPath);
        UClass* LoadedClass = LoadObject<UClass>(nullptr, *GeneratedClassPath);
        if (LoadedClass && LoadedClass->IsChildOf(UTestPrimaryDA::StaticClass()))
        {
            UTestPrimaryDA* CDO = Cast<UTestPrimaryDA>(LoadedClass->GetDefaultObject());
            if (CDO)
            {
                LoadedObjects.Add(CDO);
            }
        }
    }

    //Return the last loaded in the list
    return LoadedObjects.Num() > 0 ? LoadedObjects[LoadedObjects.Num() - 1] : nullptr;```
#

this works

#

even in cooked, ```+PrimaryAssetTypesToScan=(PrimaryAssetType="TestPrimaryDA",AssetBaseClass="/Game/Test/BP_TestDAParent.BP_TestDAParent_C",bHasBlueprintClasses=True,bIsEditorOnly=False,Directories=((Path="/Game/Test")),SpecificAssets=,Rules=(Priority=-1,ChunkId=-1,bApplyRecursively=True,CookRule=AlwaysCook))

#

didnt crash once and loaded the assets

#

ah but once its loaded its no longer searchable in registry for some reason with that method

#

so it works only the once, so you probably want to either cache it or do something else

#
//Extract parent tags
    TArray<FGameplayTag> OutTags;
    UGameplayTagsManager::Get().ExtractParentTags(InTag, OutTags);

    //Help to return a FString for the Asset Registry tag value checking
    auto SerializedString = [](FGameplayTag InToSerialize) -> FString
    {
        FString SerializedTag;

        FGameplayTag::StaticStruct()->ExportText(
            /*out*/ SerializedTag,
            &InToSerialize,
            nullptr,
            nullptr,
            PPF_None,
            nullptr
        );

        return SerializedTag;
    };

    //Get all assets with the same tag value
    UAssetManager& AssetManager = UAssetManager::Get();
    TMultiMap<FName, FString> Map;
    Map.Add("MyTag", SerializedString(InTag));
    for (const FGameplayTag& Tag : OutTags)
    {
        Map.Add("MyTag", SerializedString(Tag));
    }
    FARFilter Filter;
    Filter.ClassPaths.Add(FTopLevelAssetPath("/Script/Engine.Blueprint"));
    Filter.PackagePaths.Add("/Game/Test");
    Filter.bRecursivePaths = true;
    Filter.bRecursiveClasses = true;
    
    TArray<FAssetData> Results;
    AssetManager.GetAssetRegistry().GetAssets(Filter, Results);
    
    //Sort by the most parent tag first
    Results.Sort([&](const FAssetData& A, const FAssetData& B)
    {
        FString ExportedTagA, ExportedTagB;
        A.GetTagValue("GameplayTag", ExportedTagA);
        B.GetTagValue("GameplayTag", ExportedTagB);

        FGameplayTag TagA, TagB;
        TagA.FromExportString(ExportedTagA);
        TagB.FromExportString(ExportedTagB);

        int32 MatchA = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagA);
        int32 MatchB = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagB);

        return MatchA > MatchB;
    });

    TArray<UTestPrimaryDA*> LoadedObjects;

    for (const FAssetData& Asset : Results)
    {
        FString GeneratedClassPath;
        Asset.GetTagValue(TEXT("GeneratedClass"), GeneratedClassPath);
        // Use the streamable manager to load synchronously
        UClass* LoadedClass = Cast<UClass>(
            UAssetManager::GetStreamableManager().LoadSynchronous(GeneratedClassPath, true)
        );
        if (LoadedClass && LoadedClass->IsChildOf(UTestPrimaryDA::StaticClass()))
        {
            UTestPrimaryDA* CDO = Cast<UTestPrimaryDA>(LoadedClass->GetDefaultObject());
            if (CDO)
            {
                LoadedObjects.Add(CDO);
            }
        }
    }

    //Return the last loaded in the list
    return LoadedObjects.Num() > 0 ? LoadedObjects[LoadedObjects.Num() - 1] : nullptr;``` if you dont want to hold on to already loaded ones. Just means you will run this all the time, maybe better to have somekinda subsystem to hold the loaded ones with corresponding GameplayTag but the one here will load them regardless.
west shuttle
#

hopefully this works for you lol

#

i did test it

molten wren
#

Ill try your code and see if it makes a difference, I only really see subtle differences in yours vs what Ive got.

#

The AssetManager also has the type set to AlwaysCook.

#

So im kind of scratching my head.

#

I did get a report from one of the QA guys that they saw one of the Awards working ingame.

#

But none of the others do

#

So its a strange one.

#

Maybe the StreamableManager is a more reliable approach to loading.

west shuttle
#

let me know

#

interested if it works

#

in my testing it worked fine

molten wren
#

Your solution above didn't work, I noticed that it was retrieving all Assets, instead of just the ones that met the Tag requirement.

#

What I did find though

#

Was that a missing piece of the puzzle might have been the following.

#

AssetRegistry.ScanPathsSynchronous(Directories);

#

I cobbled together a slightly different approach. Which does work.

#

However the only major difference that I can comprehend being important is that call to ScanPathsSynchronous

#

Prior to trying to retreive assets.

#

But even then, it was finding the Assets before, so Im still not sure whats changed that has helped.

#

Just not able to load them...

#

Im still digging into it.

west shuttle
#

🤔

#

only thing i can think of is you need to manually reesaave all bps

#

for it to make the tag

#

you could override that function that returns the tag and not use assetregistrysearchable

#

you can override COREUOBJECT_API virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const;

#
virtual void GetAssetRegistryTags(TArray<FAssetRegistryTag>& OutTags) const
{
  Super::GetAssetRegistryTags(OutTags);
  OutTags.Add(FAssetRegistryTag("MyTag", MyTag.ToString(), ETagType::TT_Alphabetical));
}``` if on 5.3 or less
#

then you can remove AssetRegistrySearchable

#

and ```cpp
//Extract parent tags
TArray<FGameplayTag> OutTags;
UGameplayTagsManager::Get().ExtractParentTags(InTag, OutTags);

//Get all assets with the same tag value
UAssetManager& AssetManager = UAssetManager::Get();
TMultiMap<FName, FString> Map;
Map.Add("MyTag", InTag.ToString());
for (const FGameplayTag& Tag : OutTags)
{
    Map.Add("MyTag", Tag.ToString());
}
FARFilter Filter;
Filter.ClassPaths.Add(FTopLevelAssetPath("/Script/Engine.Blueprint"));
Filter.PackagePaths.Add("/Game/Test");
Filter.bRecursivePaths = true;
Filter.bRecursiveClasses = true;

TArray<FAssetData> Results;
AssetManager.GetAssetRegistry().GetAssets(Filter, Results);

//Sort by the most parent tag first
Results.Sort([&](const FAssetData& A, const FAssetData& B)
{
    FString ExportedTagA, ExportedTagB;
    A.GetTagValue("GameplayTag", ExportedTagA);
    B.GetTagValue("GameplayTag", ExportedTagB);

    FGameplayTag TagA, TagB;
    FGameplayTag TagA = FGameplayTag::RequestGameplayTag(ExportedTagA);
    FGameplayTag TagB = FGameplayTag::RequestGameplayTag(ExportedTagB);

    int32 MatchA = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagA);
    int32 MatchB = UGameplayTagsManager::Get().GameplayTagsMatchDepth(InTag, TagB);

    return MatchA > MatchB;
});

TArray<UTestPrimaryDA*> LoadedObjects;

for (const FAssetData& Asset : Results)
{
    FString GeneratedClassPath;
    Asset.GetTagValue(TEXT("GeneratedClass"), GeneratedClassPath);
    // Use the streamable manager to load synchronously
    UClass* LoadedClass = Cast<UClass>(
        UAssetManager::GetStreamableManager().LoadSynchronous(GeneratedClassPath, true)
    );
    if (LoadedClass && LoadedClass->IsChildOf(UTestPrimaryDA::StaticClass()))
    {
        UTestPrimaryDA* CDO = Cast<UTestPrimaryDA>(LoadedClass->GetDefaultObject());
        if (CDO)
        {
            LoadedObjects.Add(CDO);
        }
    }
}

//Return the last loaded in the list
return LoadedObjects.Num() > 0 ? LoadedObjects[LoadedObjects.Num() - 1] : nullptr;```
west shuttle
#

@molten wren ```
UMyTestPDA* UMyTestPDA::GetAssetWithTag(FGameplayTag InTag)
{
//Extract parent tags
TArray<FGameplayTag> OutTags;
UGameplayTagsManager::Get().ExtractParentTags(InTag, OutTags);

//Get all assets with the same tag value
UAssetManager& AssetManager = UAssetManager::Get();
FARFilter Filter;

TMultiMap<FName, FString> Map;
Filter.TagsAndValues.Add("MyTag", InTag.ToString());
for (const FGameplayTag& Tag : OutTags)
{
    Filter.TagsAndValues.Add("MyTag", Tag.ToString());
}
Filter.ClassPaths.Add(FTopLevelAssetPath("/Script/Engine.Blueprint"));


TArray<FAssetData> Results;
AssetManager.GetAssetRegistry().GetAssets(Filter, Results);

//Sort by the most parent tag first
Results.Sort([&](const FAssetData& A, const FAssetData& B)
    {
        FString ExportedTagA, ExportedTagB;
        A.GetTagValue("MyTag", ExportedTagA);
        B.GetTagValue("MyTag", ExportedTagB);

        return ExportedTagA < ExportedTagB;
    });

TArray<UMyTestPDA*> LoadedObjects;

for (const FAssetData& Asset : Results)
{
    FString GeneratedClassPath;
    Asset.GetTagValue(TEXT("GeneratedClass"), GeneratedClassPath);
    // Use the streamable manager to load synchronously
    UClass* LoadedClass = Cast<UClass>(
        UAssetManager::GetStreamableManager().LoadSynchronous(GeneratedClassPath, true)
    );
    if (LoadedClass && LoadedClass->IsChildOf(UMyTestPDA::StaticClass()))
    {
        UMyTestPDA* CDO = Cast<UMyTestPDA>(LoadedClass->GetDefaultObject());
        if (CDO)
        {
            LoadedObjects.Add(CDO);
        }
    }
}

//Return the last loaded in the list
return LoadedObjects.Num() > 0 ? LoadedObjects[LoadedObjects.Num() - 1] : nullptr;

}``` i tested this and it only returns the 3 assets to load in the heirachy and not any other asset

#
    
public:
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Test")
    FGameplayTag MyTag;

#if WITH_EDITOR

    virtual void GetAssetRegistryTags(FAssetRegistryTagsContext Context) const override
    {
        Super::GetAssetRegistryTags(Context);
        // Add custom tags here
        Context.AddTag(FAssetRegistryTag("MyTag", MyTag.ToString(), FAssetRegistryTag::TT_Alphabetical));
    }
#endif```
#

i did this so we can avoid needing to use the exported tag name

#

this just makes it a bit cleaner

molten wren
#

Yeah i actually tried that approach myself and it still resulted in the same outcome. Im on 4.27 so not sure if maybe there is some quirks there thats affecting it.

#

Ill give you an update on Monday when I return to work.

west shuttle
#

i know its working in 5.5

#

cant say in 4.27