What is intended approach to wipe all tags from a character?
As well as adding loose gameplay tags, we add minimal replicated tags ( AbilitySystem->AddMinimalReplicationGameplayTags( GameplayTags ) ) in places where we want them to be predicted.
For the most part these tags are managed on an individual basis, but there are some states the player can get into where a tag is not correctly removed when they die. Instead of requiring individual watertight logic to clean all of these up on death, it makes sense that there would be a way to wipe all tags and start fresh (to minimize any room for human error / need for boilerplate logic).
Simply wiping all loose tags, leaves the minimal replicated ones, causing them to be re-added for remote players. Access to the list of these minimal replicated tags is protected, so the solution I’ve come up with is the following in a child of the ability system component:
Is this a valid approach? What is the intended method of cleaning up minimal replicated tags? Is it expected that tags will always be added and cleaned up on an individual basis, and there should never be a need to wipe them all?
Hey there, the comment that describes the purpose of MinimalReplicationTags is a bit oddly placed, but that tag map is primarily intended for internal use.
/**
* Minimally replicated tags are replicated tags that come from GEs when in bMinimalReplication mode.
* (The GEs do not replicate, but the tags they grant do replicate via these functions)
*/
inline void AddMinimalReplicationGameplayTag(const FGameplayTag& GameplayTag)
In order words: server-side applied GEs populate minimal replication tags, so to clean them up you would let the server clean up those GEs.
If the tag is not associated with a GE, then only use LooseTags and ReplicatedLooseTags for that:
The server adds to LooseTags (to apply locally) and ReplicatedLooseTags (to replicate to clients)
The client adds to LooseTags to apply locally, predictively
So if I understand correctly, you’re currently using minimal replication tags for something that I recommend handling exclusively with loose tags.
Thanks, this makes sense - the original intention for our usage was for prediction so that we can have a tag added to remote players only, and allow the local player to add their own tag predictively. This means that we can avoid the latency of having to add the loose tag on the server and wait for it to come back down to our local player.
An example would be with traversal where we want to predictively add the tag on the local player so we can animate straight away, then when the server gets to that point in the code, it adds a replicating tag that will only replicate then to remote players.
Same goes for predictive tag removal. It basically just gives us local autonomy over our own tags while remote players remain simulated from the server based on replication.
We can have a go at replacing these calls with the regular loose ones but that may introduce double deteles/adds if we run it on both client and server. Having it only run on the server though will be very latent.
We could use gameplay effects instead, but we’re then adding a load of bloat just to wrap the minimal replication tags that we’re using anyway. What would you suggest for these kind of use cases? Thanks
Hey again, I know that in Fortnite autonomous proxies will predictively call ASC->AddLooseGameplayTag for predictive effects. I’m going to talk to some colleagues next week and get back to you with more specifics. I also remember that since FMinimalReplicationTagCountMap directly writes 0 or 1 tag count into the ASC’s GameplayTagCountContainer, if you’re not careful then counts can get stomped when you interact with that container from multiple sources. I’ll also ask whether Fortnite has ever ran into that or has a way to avoid it.
Hello. I’ve gotten some info about common issues, upcoming changes to address those issues, and Fortnite’s way of doing things.
I want to correct my earlier suggestion: you may want to keep using MinimalReplicationTags for the time being.
In Fortnite, for player pawns specifically:
We call ASC->AddLooseGameplayTags() to predictively apply loose tags (and ASC->AddReplicatedLooseGameplayTags() server-side) but in Fortnite we also have changed that field’s behavior so that both MinimalReplicationTags and ReplicatedLooseTags SkipOwner for replication, unlike default UE where ReplicatedLooseTags replicates back to owner and as you mentioned, can cause fighting between prediction and down-replication.
This means we don’t (can’t) use AddReplicatedLooseTags for server-only initiated tags, but only for tags we know the owner will predict or has predicted.
Fortnite’s CharacterMovementComponent has an extendable movement mode state machine, where autonomous proxies can predict movement mode transitions and the server can send a correction containing the active movement modes. We typically let that state machine apply loose tags locally: this is the mechanism through which the server can affect the autonomous proxy’s loose tags. For networked movement, that also means that when receiving a movement correction from the server it will always have the server-side movement-affecting tags in time.
For other cases we’d create a wrapping GameplayEffect. Predicted GE application in local predicted ability activation and predicted GE cleanup on ability rejection is more robust than ReplicatedLooseTags. Edit: by robust I mean doesn’t suffer from local prediction fighting with replication since FActiveGameplayEffectsContainer handles consolidation of GEs.
I would say your current way of using MinimalReplicationTags matches Fortnite’s way better than usingn ReplicatedLooseTags, since that’s a SkipOwner property. For server->autonomous proxy loose tags, you can still use ReplicatedLooseTags where Fortnite uses an engineering heavy approach to avoid replication timing issues on server correction. (For more on replication timing issues, see my tutorial here).
Be aware: In UE5.7 both MinimalReplicationTags and ReplicatedLooseTags as properties will be deprecated. They will continue to function for the time being but ultimately dropped for new functions and new method of replication, which is that the ASC’s GameplayTagCountContainer will handles its own replication. See CL 45164677 on //UE5/Main. The new API gives granular control over which tag is replicated with how much detail: i.e. tag only or counts as well, whereas before all tags would receive the same behavior. The new method doesn’t have a SkipOwner replication option currently, we are considering that though and would like your thoughts if you have any on this matter. Currently our recommendation would be to use wrapping GEs if you want to predictively apply tags that the server will also replicate to everyone.
To answer your original question: In Fortnite we do let each source of loose tags clean up after itself - so the movement modes upon exiting clean up their tags. That pattern is a bit easier to maintain when everything is already set up as a state machine.
But if you want to wipe all tags at once, adding your own function for it makes sense! I’ll ask the GAS system owner to consider this too.