Getting Asset Number From FAssetData is incorrect

Hello!

Hope whomever reads this is doing well.

I have noticed that when trying to get the Number from FAssetData it adds 1 to the number.

[Image Removed]

My asset is called : test_1001 but what I get back is 1002. Code included in reproduction steps.

I’ve been able to see this with test_100 being 101, test_1 being 2 and test_001 being 0

This seems like a bug to me, but maybe Number is supposed to be stored this way?

Thank you so much!

[Attachment Removed]

Steps to Reproduce
You can add this code to a project and insert your own FSoftObjectPath to reproduce the issue.

IAssetRegistry& AssetRegistry = FModuleManager::GetModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
FAssetData TextureAssetData = AssetRegistry.GetAssetByObjectPath(InSoftObjectPath);
FName AssetPath = TextureAssetData.PackageName;

return 	AssetPath.GetNumber();

[Attachment Removed]

Hi!

That is not an “Asset Number”. You are fetching the PackageName from the FAssetData, which is an FName. You can read more about what FNames are here: https://dev.epicgames.com/documentation/unreal\-engine/fname\-in\-unreal\-engine

The Number part in particular is a memory saving technique because many things in Unreal have incremental Number suffixes, so it pays off to give that number suffix special treatment.

FName use is not limited to FAssetData, so you cannot read anything specifically about assets into the Number part - it’s just an instance counter for how often that particularly string was used.

As for why the number shown in the visualization is one off, there’s a hint if you look into NameTypes.h:

//~ at the top of the file:
 
/** Externally, the instance number to represent no instance number is NAME_NO_NUMBER, 
    but internally, we add 1 to indices, so we use this #define internally for 
	zero'd memory initialization will still make NAME_None as expected */
#define NAME_NO_NUMBER_INTERNAL	0
 
/** Conversion routines between external representations and internal */
#define NAME_INTERNAL_TO_EXTERNAL(x) (x - 1)
#define NAME_EXTERNAL_TO_INTERNAL(x) (x + 1)
 
/** Special value for an FName with no number */
#define NAME_NO_NUMBER NAME_INTERNAL_TO_EXTERNAL(NAME_NO_NUMBER_INTERNAL)
 
//~ further down:
 
	/** Number portion of the string/number pair (stored internally as 1 more than actual, so zero'd memory will be the default, no-instance case) */
	uint32			Number = NAME_NO_NUMBER_INTERNAL;

And if you look into Unreal.natvis you can see why the string visualization shows a different value than the Number member if you expand the raw view:

  <Type Name="FName" Priority="High">
    <DisplayString Condition="this->Number">{DisplayIndex}_{(this->Number - 1),d}</DisplayString>
    <DisplayString>{DisplayIndex}</DisplayString>
    <Expand>
      <ArrayItems>
        <Size>((FNameEntry&amp;)GNameBlocksDebug[DisplayIndex.Value &gt;&gt; FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * (DisplayIndex.Value &amp; FNameDebugVisualizer::OffsetMask)]).Header.Len</Size>
        <ValuePointer>((FNameEntry&amp;)GNameBlocksDebug[DisplayIndex.Value &gt;&gt; FNameDebugVisualizer::OffsetBits][FNameDebugVisualizer::EntryStride * (DisplayIndex.Value &amp; FNameDebugVisualizer::OffsetMask)]).AnsiName</ValuePointer>
      </ArrayItems>
    </Expand>
  </Type>

So that’s the reason behind the one-off :slight_smile:

Ciao, Daniel!

[Attachment Removed]

Hi,

Daniel is correct. The FName class does use 0 to indicate the Number is not in use for a specific name instance. You can see that engine code is relying on NAME_INTERNAL_TO_EXTERNAL when retrieving the Number. FName::ToString is the best example.

Regards,

Martin

[Attachment Removed]

I see! thank you both for replying

[Attachment Removed]