Skip to content

Commit dee28e0

Browse files
committed
Update to version 1.4.2. Refactored the method for searching nested data assets. Structures and arrays of structures are now checked during recursive data asset loading.
1 parent e3a4ba9 commit dee28e0

4 files changed

Lines changed: 71 additions & 38 deletions

File tree

Plugins/AsyncDataAssetManager/AsyncDataAssetManager.uplugin

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"FileVersion": 3,
33
"Version": 1,
4-
"VersionName": "1.4.1",
4+
"VersionName": "1.4.2",
55
"FriendlyName": "Async Data Asset Manager",
66
"Description": "Asynchronous management of data assets.",
77
"Category": "Async Technologies",

Plugins/AsyncDataAssetManager/Source/AsyncDataAssetManager/Private/ADAMS_Getters.cpp

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -88,67 +88,94 @@ int32 UAsyncDataAssetManagerSubsystem::GetIndexDataADAM(TSoftObjectPtr<UPrimaryD
8888
TArray<TSoftObjectPtr<UPrimaryDataAsset>> UAsyncDataAssetManagerSubsystem::FindNestedAssets(UPrimaryDataAsset* DataAsset)
8989
{
9090
TArray<TSoftObjectPtr<UPrimaryDataAsset>> NestedAssets;
91-
9291
if (!DataAsset)
9392
{
9493
UE_LOG(LogTemp, Warning, TEXT("ADAM (Recursive Load): Received a null value."));
9594

9695
return NestedAssets;
9796
}
9897

99-
// Storage of unique asset names for duplicate control.
100-
TArray<FString> UniqueAssetNames;
98+
// Use a set for faster unique checks
99+
TSet<FString> UniqueAssetNames;
100+
101+
// Start recursion from the DataAsset itself
102+
FindNestedAssetsRecursive(DataAsset, DataAsset->GetClass(), NestedAssets, UniqueAssetNames);
101103

102-
// Use reflection to search for nested Data Assets
103-
for (TFieldIterator<FProperty> PropIterator(DataAsset->GetClass()); PropIterator; ++PropIterator)
104+
if (EnableLog && NestedAssets.Num() == 0)
104105
{
105-
const FProperty* Prop = *PropIterator;
106+
UE_LOG(LogTemp, Display, TEXT("ADAM (Recursive data): Iteration is complete. All nested data is loaded!"));
107+
}
108+
109+
return NestedAssets;
110+
}
111+
112+
void UAsyncDataAssetManagerSubsystem::FindNestedAssetsRecursive(void* Container, UStruct* Struct, TArray<TSoftObjectPtr<UPrimaryDataAsset>>& OutNestedAssets, TSet<FString>& UniqueAssetNames)
113+
{
114+
if (!Container || !Struct)
115+
return;
106116

107-
// Look for properties of type TSoftObjectPtr<UPrimaryDataAsset>
117+
// Iterate over all properties in the struct/class
118+
for (TFieldIterator<FProperty> PropIterator(Struct); PropIterator; ++PropIterator)
119+
{
120+
const FProperty* Prop = *PropIterator;
121+
// Handle soft object properties (TSoftObjectPtr<UPrimaryDataAsset>)
108122
if (const FSoftObjectProperty* SoftObjectProperty = CastField<FSoftObjectProperty>(Prop))
109123
{
110124
if (SoftObjectProperty->PropertyClass->IsChildOf(UPrimaryDataAsset::StaticClass()))
111125
{
112-
TSoftObjectPtr<UPrimaryDataAsset> ChildAsset = *SoftObjectProperty->ContainerPtrToValuePtr<TSoftObjectPtr<UPrimaryDataAsset>>(DataAsset);
126+
TSoftObjectPtr<UPrimaryDataAsset> ChildAsset = *SoftObjectProperty->ContainerPtrToValuePtr<TSoftObjectPtr<UPrimaryDataAsset>>(Container);
113127
FString ChildAssetName = ChildAsset.GetAssetName();
114-
// Filtering and adding data
115128
if (!ChildAssetName.IsEmpty() && !UniqueAssetNames.Contains(ChildAssetName))
116129
{
117-
UniqueAssetNames.AddUnique(ChildAssetName);
118-
NestedAssets.Add(ChildAsset);
130+
UniqueAssetNames.Add(ChildAssetName);
131+
OutNestedAssets.Add(ChildAsset);
119132
}
120133
}
121134
}
122-
123-
// If the array of soft references
124-
if (const FArrayProperty* ArrayProp = CastField<FArrayProperty>(Prop))
135+
// Handle struct properties (recurse into the struct)
136+
else if (const FStructProperty* StructProperty = CastField<FStructProperty>(Prop))
137+
{
138+
void* StructData = StructProperty->ContainerPtrToValuePtr<void>(Container);
139+
if (StructData)
140+
{
141+
FindNestedAssetsRecursive(StructData, StructProperty->Struct, OutNestedAssets, UniqueAssetNames);
142+
}
143+
}
144+
// Handle array properties
145+
else if (const FArrayProperty* ArrayProp = CastField<FArrayProperty>(Prop))
125146
{
126-
if (const FSoftObjectProperty* InnerSoftObjectProperty = CastField<FSoftObjectProperty>(ArrayProp->Inner))
147+
FScriptArrayHelper ArrayHelper(ArrayProp, ArrayProp->ContainerPtrToValuePtr<void>(Container));
148+
// Check inner property type
149+
const FProperty* InnerProp = ArrayProp->Inner;
150+
// If inner is soft object ptr
151+
if (const FSoftObjectProperty* InnerSoftObjectProperty = CastField<FSoftObjectProperty>(InnerProp))
127152
{
128153
if (InnerSoftObjectProperty->PropertyClass->IsChildOf(UPrimaryDataAsset::StaticClass()))
129154
{
130-
FScriptArrayHelper ArrayHelper(ArrayProp, ArrayProp->ContainerPtrToValuePtr<void>(DataAsset));
131-
132155
for (int32 i = 0; i < ArrayHelper.Num(); ++i)
133156
{
134157
TSoftObjectPtr<UPrimaryDataAsset> ChildAsset = *reinterpret_cast<TSoftObjectPtr<UPrimaryDataAsset>*>(ArrayHelper.GetRawPtr(i));
135158
FString ChildAssetName = ChildAsset.GetAssetName();
136-
// Filtering and adding data
137159
if (!ChildAssetName.IsEmpty() && !UniqueAssetNames.Contains(ChildAssetName))
138160
{
139-
UniqueAssetNames.AddUnique(ChildAssetName);
140-
NestedAssets.Add(ChildAsset);
161+
UniqueAssetNames.Add(ChildAssetName);
162+
OutNestedAssets.Add(ChildAsset);
141163
}
142164
}
143165
}
144166
}
167+
// If inner is struct, recurse on each array element
168+
else if (const FStructProperty* InnerStructProperty = CastField<FStructProperty>(InnerProp))
169+
{
170+
for (int32 i = 0; i < ArrayHelper.Num(); ++i)
171+
{
172+
void* ElementData = ArrayHelper.GetRawPtr(i);
173+
if (ElementData)
174+
{
175+
FindNestedAssetsRecursive(ElementData, InnerStructProperty->Struct, OutNestedAssets, UniqueAssetNames);
176+
}
177+
}
178+
}
145179
}
146180
}
147-
148-
if (EnableLog && NestedAssets.Num() == 0)
149-
{
150-
UE_LOG(LogTemp, Display, TEXT("ADAM (Recursive data): Iteration is complete. All nested data is loaded!"));
151-
}
152-
153-
return NestedAssets;
154181
}

Plugins/AsyncDataAssetManager/Source/AsyncDataAssetManager/Public/AsyncDataAssetManagerSubsystem.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,20 @@ class ASYNCDATAASSETMANAGER_API UAsyncDataAssetManagerSubsystem : public UGameIn
253253
UPROPERTY()
254254
TMap<FName, int32> QueueCounterADAM;
255255

256-
// Function for searching nested data assets
256+
// Searching nested data assets
257257
UFUNCTION()
258258
TArray<TSoftObjectPtr<UPrimaryDataAsset>> FindNestedAssets(UPrimaryDataAsset* DataAsset);
259259

260+
/**
261+
* Recursive function for searching nested data assets.
262+
*
263+
* @param Container Pointer to the memory of the struct or object instance being inspected.
264+
* @param Struct Reflection metadata describing the type of the container (UStruct or UClass).
265+
* @param OutNestedAssets Array to collect discovered nested PrimaryDataAssets.
266+
* @param UniqueAssetNames Set of asset names used to prevent duplicates when collecting assets.
267+
*/
268+
void FindNestedAssetsRecursive(void* Container, UStruct* Struct, TArray<TSoftObjectPtr<UPrimaryDataAsset>>& OutNestedAssets, TSet<FString>& UniqueAssetNames);
269+
260270
/**
261271
* Add data to the main DataADAM array
262272
* @param PrimaryDataAsset Soft link to data asset.

README.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@ ADAM is a plugin for Unreal Engine 5 that adds a subsystem for asynchronous load
99
> The plugin has been pre-packaged only for Win64 and Android.
1010
1111
## Latest Updates
12-
`Version 1.4.1`
13-
- Build version for Unreal Engine 5.6.0
14-
- Code refactoring and decomposition.
15-
- Improving code security.
16-
- Cleaning up the chaos in the DemoFiles folder after the latest patch and putting things in order.
17-
- Redesign of the data asset loading tag system. Tags in the ADAM subsystem functions are now universal — this means that for each data asset package, you can choose which approach to use: a regular `FName` for complex tags (e.g., level names) or fixed tags like `FGameplayTag` for centralized management (so you don’t have to keep everything written down). Both approaches can be used at the same time, since the main storage supports both tag types as `FName`. However, only one tag type can be used per data asset package.
18-
- Redesign of the data asset unloading tag system in the `UnloadAllTagsADAM` function. For convenience, the tag selection has been expanded to three. You can fill in and specify all the tag types you need to unload at once.
19-
- Correction for the `UnloadAllTagsADAM` function — now it is also possible to bulk unload from memory only data assets with the tag "None".
12+
`Version 1.4.2`
13+
- Build version for Unreal Engine 5.6.0+
14+
- Refactored the method for searching nested data assets.
15+
- Structures and arrays of structures are now checked during recursive data asset loading.
2016

2117
## What it's for
2218
- Load and unload Data Assets asynchronously using simple functions.
@@ -28,7 +24,7 @@ ADAM is a plugin for Unreal Engine 5 that adds a subsystem for asynchronous load
2824
- Additional duplicate checking ensures that there are no additional references to resources in memory and that they are retained by the standard system.
2925
- Supports bulk asynchronous loading of unique Data Assets.
3026
- This subsystem enables recursive data loading. If you load a single DataAsset that includes multiple nested Data Assets, all of them will be loaded and filtered to avoid duplicates in memory.
31-
- Group your uploaded DataAssets using tags so that they can be unloaded at the right moment <i>(for example, this can be useful if you are uploading DataAssets in parts and want to unload them without affecting other necessary data still stored in memory)</i>.
27+
- Group your uploaded DataAssets using tags so that they can be unloaded at the right moment <i>(for example, this can be useful if you are uploading DataAssets in parts and want to unload them without affecting other necessary data still stored in memory)</i>. You can also choose which approach to use: a regular `FName` for complex tags (e.g., level names) or fixed tags like `FGameplayTag` for centralized management (so you don’t have to keep everything written down).
3228
- Supports asynchronous loading without memory retention <i>(e.g., if you need to immediately access data and then free up memory)</i>.
3329
- Single notification for bulk data load. The `OnAllLoadedADAM` delegate notifies when all Data Assets have been loaded simultaneously. It only functions if the `NotifyAfterFullLoaded` option is enabled, which is supported exclusively by the `LoadArrayADAM` method.
3430
- Disableable debug logs allow you to monitor the entire asynchronous data management process. Plugin settings are located in `Project Settings > Plugins > Async Technologies - ADAM`.

0 commit comments

Comments
 (0)