NetIndexFirstBitSegment
NetIndexFirstBitSegment
#Overview
name: NetIndexFirstBitSegment
The value of this variable can be defined or overridden in .ini config files. 1
.ini config file referencing this setting variable.
It is referenced in 9
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of NetIndexFirstBitSegment is to optimize the network serialization of gameplay tags in Unreal Engine 5. It is used to define the length in bits of the first segment when net serializing tags, allowing for more efficient replication of frequently used tags.
This setting variable is primarily used by the Gameplay Tags system, which is part of Unreal Engine’s core gameplay framework. It is referenced in the GameplayTags module, specifically in the GameplayTagsManager and GameplayTagContainer classes.
The value of this variable is set in multiple places:
- In the UGameplayTagsManager constructor, where it’s initialized to 16.
- In the UGameplayTagsSettings constructor, where it’s also set to 16.
- It can be configured in the project settings through the UGameplayTagsSettings class.
- It’s updated in the UGameplayTagsManager::ConstructGameplayTagTree() function based on the value from UGameplayTagsSettings.
NetIndexFirstBitSegment interacts with NetIndexTrueBitNum, which represents the total number of bits used for tag serialization. The NetIndexFirstBitSegment should never be larger than NetIndexTrueBitNum.
Developers should be aware that:
- This variable affects network performance and should be tuned based on the project’s specific tag usage patterns.
- It’s used in a two-segment serialization scheme, where the first segment (NetIndexFirstBitSegment bits) is always sent, and a “more” bit indicates if a second segment is needed.
- The value should be set to accommodate the most frequently replicated tags within the first segment for optimal performance.
Best practices when using this variable include:
- Analyzing tag replication patterns in your game using the “GameplayTags.PrintReport” console command or “GameplayTags.PrintReportOnShutdown” cvar.
- Adjusting the NetIndexFirstBitSegment value based on the analysis to ensure frequently used tags fit within the first segment.
- Keeping the value reasonable (e.g., 16 bits) to balance between optimization and flexibility.
- Regularly reviewing and updating this value as your game’s tag usage evolves during development.
#Setting Variables
#References In INI files
Location: <Workspace>/Projects/Lyra/Config/DefaultGameplayTags.ini:10, section: [/Script/GameplayTags.GameplayTagsSettings]
- INI Section:
/Script/GameplayTags.GameplayTagsSettings
- Raw value:
16
- Is Array:
False
#References in C++ code
#Callsites
This variable is referenced in the following C++ source code:
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Classes/GameplayTagsManager.h:600
Scope: file
Source code excerpt:
int32 GetNetIndexTrueBitNum() const { VerifyNetworkIndex(); return NetIndexTrueBitNum; }
/** The length in bits of the first segment when net serializing tags. We will serialize NetIndexFirstBitSegment + 1 bit to indicatore "more" (more = second segment that is NetIndexTrueBitNum - NetIndexFirstBitSegment) */
int32 GetNetIndexFirstBitSegment() const { VerifyNetworkIndex(); return NetIndexFirstBitSegment; }
/** This is the actual value for an invalid tag "None". This is computed at runtime as (Total number of tags) + 1 */
FGameplayTagNetIndex GetInvalidTagNetIndex() const { VerifyNetworkIndex(); return InvalidTagNetIndex; }
const TArray<TSharedPtr<FGameplayTagNode>>& GetNetworkGameplayTagNodeIndex() const { VerifyNetworkIndex(); return NetworkGameplayTagNodeIndex; }
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Classes/GameplayTagsManager.h:621
Scope: file
Source code excerpt:
int32 NetIndexTrueBitNum;
/** The length in bits of the first segment when net serializing tags. We will serialize NetIndexFirstBitSegment + 1 bit to indicatore "more" (more = second segment that is NetIndexTrueBitNum - NetIndexFirstBitSegment) */
int32 NetIndexFirstBitSegment;
/** This is the actual value for an invalid tag "None". This is computed at runtime as (Total number of tags) + 1 */
FGameplayTagNetIndex InvalidTagNetIndex;
public:
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Classes/GameplayTagsSettings.h:148
Scope (from outer to inner):
file
class class UGameplayTagsSettings : public UGameplayTagsList
Source code excerpt:
int32 NumBitsForContainerSize;
/** The length in bits of the first segment when net serializing tags. We will serialize NetIndexFirstBitSegment + 1 bit to indicate "more", which is slower to replicate */
UPROPERTY(config, EditAnywhere, Category= "Advanced Replication")
int32 NetIndexFirstBitSegment;
/** A list of .ini files used to store restricted gameplay tags. */
UPROPERTY(config, EditAnywhere, AdvancedDisplay, Category = "Advanced Gameplay Tags")
TArray<FRestrictedConfigInfo> RestrictedConfigFiles;
#if WITH_EDITORONLY_DATA
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagContainer.cpp:39
Scope: file
Source code excerpt:
/**
* Replicates a tag in a packed format:
* -A segment of NetIndexFirstBitSegment bits are always replicated.
* -Another bit is replicated to indicate "more"
* -If "more", then another segment of (MaxBits - NetIndexFirstBitSegment) length is replicated.
*
* This format is basically the same as SerializeIntPacked, except that there are only 2 segments and they are not the same size.
* The gameplay tag system is able to exploit knoweledge in what tags are frequently replicated to ensure they appear in the first segment.
* Making frequently replicated tags as cheap as possible.
*
*
* Setting up your project to take advantage of the packed format.
* -Run a normal networked game on non shipping build.
* -After some time, run console command "GameplayTags.PrintReport" or set "GameplayTags.PrintReportOnShutdown 1" cvar.
* -This will generate information on the server log about what tags replicate most frequently.
* -Take this list and put it in DefaultGameplayTags.ini.
* -CommonlyReplicatedTags is the ordered list of tags.
* -NetIndexFirstBitSegment is the number of bits (not including the "more" bit) for the first segment.
*
*/
void SerializeTagNetIndexPacked(FArchive& Ar, FGameplayTagNetIndex& Value, const int32 NetIndexFirstBitSegment, const int32 MaxBits)
{
// Case where we have no segment or the segment is larger than max bits
if (NetIndexFirstBitSegment <= 0 || NetIndexFirstBitSegment >= MaxBits)
{
if (Ar.IsLoading())
{
Value = 0;
}
Ar.SerializeBits(&Value, MaxBits);
return;
}
const uint32 BitMasks[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff, 0xffff};
const uint32 MoreBits[] = {0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000};
const int32 FirstSegment = NetIndexFirstBitSegment;
const int32 SecondSegment = MaxBits - NetIndexFirstBitSegment;
if (Ar.IsSaving())
{
uint32 Mask = BitMasks[FirstSegment];
if (Value > Mask)
{
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagContainer.cpp:92
Scope (from outer to inner):
file
function void SerializeTagNetIndexPacked
Source code excerpt:
{
uint32 SerializedValue = Value;
Ar.SerializeBits(&SerializedValue, NetIndexFirstBitSegment + 1);
}
}
else
{
uint32 FirstData = 0;
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsManager.cpp:182
Scope (from outer to inner):
file
function UGameplayTagsManager::UGameplayTagsManager
Source code excerpt:
bDoneAddingNativeTags = false;
bShouldAllowUnloadingTags = false;
NetIndexFirstBitSegment = 16;
NetIndexTrueBitNum = 16;
NumBitsForContainerSize = 6;
NetworkGameplayTagNodeIndexHash = 0;
}
// Enable to turn on detailed startup logging
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsManager.cpp:561
Scope (from outer to inner):
file
function void UGameplayTagsManager::ConstructGameplayTagTree
Source code excerpt:
bShouldClearInvalidTags = MutableDefault->ClearInvalidTags;
NumBitsForContainerSize = MutableDefault->NumBitsForContainerSize;
NetIndexFirstBitSegment = MutableDefault->NetIndexFirstBitSegment;
#if WITH_EDITOR
if (GIsEditor)
{
bShouldAllowUnloadingTags = MutableDefault->AllowEditorTagUnloading;
}
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsManager.cpp:630
Scope (from outer to inner):
file
function void UGameplayTagsManager::ConstructNetIndex
Source code excerpt:
// This should never be smaller than NetIndexTrueBitNum
NetIndexFirstBitSegment = FMath::Min<int32>(GetDefault<UGameplayTagsSettings>()->NetIndexFirstBitSegment, NetIndexTrueBitNum);
// This is now sorted and it should be the same on both client and server
if (NetworkGameplayTagNodeIndex.Num() >= INVALID_TAGNETINDEX)
{
ensureMsgf(false, TEXT("Too many tags in dictionary for networking! Remove tags or increase tag net index size"));
#Loc: <Workspace>/Engine/Source/Runtime/GameplayTags/Private/GameplayTagsSettings.cpp:69
Scope (from outer to inner):
file
function UGameplayTagsSettings::UGameplayTagsSettings
Source code excerpt:
InvalidTagCharacters = ("\"',");
NumBitsForContainerSize = 6;
NetIndexFirstBitSegment = 16;
}
#if WITH_EDITOR
void UGameplayTagsSettings::PreEditChange(FProperty* PropertyThatWillChange)
{
Super::PreEditChange(PropertyThatWillChange);