Navigation
API > API/Runtime > API/Runtime/IrisCore
This is a simplification of the process of building novel NetToken types and NetTokenDataStores for those types.
The "General Idea" here is that you can define a USTRUCT that can be easily serialized as a NetToken instead of serializing the entirety of the data structure every time. This is typically useful for large data structures where the data changes infrequently
or
where the data is commonly one of a small-ish set of permutations of that data. There are probably other useful cases as well, but these uses are readily apparent.
Each NetToken refers to single, immutable, instance of the Struct you pass in/out. Every instance of the Struct that returns the same GetUniqueKey has the same NetToken. Conceptually this means that you can't store pointers or references in them to sub pieces of data that change.
You can't store references to other NetToken types, or other reference types... i.e. no properties that also are exported as NetTokens or ObjectReferences (NetGuids/NetRefHandles) You should generally consider that you can only store primitive types in the NetToken struct.
This means no: FNames, FGameplayTags or anything with a NetGuid.
Additionally, this means that the data permutations that you are going to iterate are finite, or effectively finite. This isn't magic and it is not a compression algorithm. We also aren't doing anything "smart" here about storing off the ShadowCopies of the input Structs. We very naively keep an entire copy of the struct in memory and there is no age out mechanism. If you try to put "big" pieces of data in here we will just be making copies of that data. It also means complicated hashing mechanics for GetUniqueKey will eat up a lot of time.
Again, its a simplification, not magic. You still want to be judicious in the data you are choosing to replicate.
Most/All of the "Hard Bits" are implemented for you in a 'reasonable' default. You will need to implement custom versions if you need more concrete control.
- Replay Compatibility
- Default Serialization/Quantization
- Bookkeeping
- Maintaining Shadow Copies of the required data
Example: Pretend that StringTokenStore didn't exist and I have a RPC that I want to call with a couple of FStrings that come from a set of known Strings.
TArray
When asked to set this up we have the naive implementation: USTRUCT() struct FTwoStrings {
FString Left;
FString Right; };
... UFUCNTION(Server,Reliable) void SendSelectionToServer(FTwoStrings Data);
... void OnTick(double DeltaTime) { ... SendSelectionToServer({Left,Right}); }
Given this naive implementation we can (hopefully immediately) see the multitude of problems. In this simplistic example we could probably come up with several immediate fixes. Let's pretend we have to actually send this data on every tick.
We can spend a good deal of time coming up with custom serializer that would use significantly less data or some other representation for the data... For example: USTRUCT() struct FTwoStringIdx {
int32 Left;
int32 Right; };
But this suffers from a fundamental problem that isn't immediately obvious. KnownStrings is mutable and might be changing... And now we have a synchronization problem, where we are trying to maintain the state of KnownStrings, the index and the versioning of both. This rapidly grows in complexity beyond a simple "how do we make this use less data". We have to figure out a strategy on determining agreement on state and ordering and more. Ex: Which updates first? Do we need to maintain a history of known states in the event of delayed packets,etc,etc...
Even though the list of possible strings is known/finite, the mutable nature of the KnownStrings array in conjunction with the indeterminate ordering of network communication makes this a much more difficult problem.
This problem can be solved like this.
TwoStrings.h
USTRUCT() struct FTwoStrings : FNetTokenStructBase {
FString Left;
FString Right; uint64 GetUniqueKey() { FString Combo = Left+Right; return CityHash64((const char*)Combo, Combo.Num()*sizeof(TCHAR)); } };UE_NET_DECLARE_NAMED_NETTOKEN_STRUCT_SERIALIZERS(TwoStrings, YOUR_MODULES_API)TwoStrings.cpp
UE_NET_IMPLEMENT_NAMED_NETTOKEN_STRUCT_SERIALIZERS(TwoStrings)
DefaultEngine.ini
+ReservedTypeIds=(StoreTypeName="TwoStrings", TypeID=4) # Don't forget to use an unused TypeID.
It is also possible to explicitly declare the DataStore, and use it in a custom NetSerialize. class FTwoStringDataStore : public UE::Net::TStructNetTokenDataStore
It is possible to use these in custom serializers like this: FTwoStrings Data{Left,Right}; FTwoStringDataStore::NetSerializeAndExportToken(Ar,Map,Data);
| Name | TStructNetTokenDataStoreHelper |
| Type | class |
| Header File | /Engine/Source/Runtime/Net/Iris/Public/Iris/ReplicationSystem/StructNetTokenDataStore.h |
| Include Path | #include "Iris/ReplicationSystem/StructNetTokenDataStore.h" |
Syntax
template<typename T>
class TStructNetTokenDataStoreHelper