Tag Stacks & Replication
A tag stack container is a replicated map of GameplayTag -> count, used everywhere a system needs cheap, networked counters keyed by tag: ammo, charges, status stacks, currencies, elemental buildup. There are two: FCrimsonTagStackContainer (int32) and FCrimsonFloatTagStackContainer (float).
Why a FastArray
Both containers derive from FFastArraySerializer, so only the items that changed replicate each net update - not the whole array. A naive replicated TArray resends everything on any change; a FastArray sends a delta. That matters when counts change often (every shot, every stack tick).
| Aspect | How it works |
|---|---|
| Storage | A private TArray<FCrimsonTagStack> plus an accelerated TMap<FGameplayTag,int32> for O(1) reads |
| Replication | NetDeltaSerialize via FastArrayDeltaSerialize - delta-only; the type traits opt in with WithNetDeltaSerializer |
| Reads | GetStackCount / ContainsTag hit the accelerated map; safe on any machine |
| Writes | AddStack / RemoveStack / ClearStacks - server authority only |
Dirtying is handled for you
The container's own mutators call MarkItemDirty / MarkArrayDirty internally, so as a consumer you just call AddStack/RemoveStack and mark the UPROPERTY Replicated. You only deal with dirtying directly if you write a new container type - not when you use these.
HasAuthority() - a client write is overwritten by the next server delta (silent desync). (2) Add the property to GetLifetimeReplicatedProps with DOREPLIFETIME - forgetting it compiles fine and silently never replicates.Blueprint access
Because the container is a USTRUCT, its mutators aren't directly Blueprint-callable - use the UCrimsonTagStackStatics function library instead. Its functions take the container by ref, so they wire straight into your component's container variable: Add Tag Stack, Remove Tag Stack, Get Tag Stack Count, and the Float equivalents (which add Set Float Tag Stack).