Just to preface this - I’m no expert on Gameplay Abilities. I’ve only recently started looking into the system in depth to see how it works to see if it’s suitable for something I’m working on, or whether I should build my own system. So far however I’ve noticed something that seems a bit backwards in it’s implementation.
I’m looking specifically at FGameplayEffectContext, and how it is typically passed around the code as a pointer-type (wrapped with FGameplayEffectContextHandle) - and can be replicated through psuedo-polymorphism, essentially by allocating it’s data members at runtime when needed. The thing is, the framework only allows you to subclass FGameplayEffectContext on a per-project level (by overriding UAbilitySystemGlobals::AllocGameplayEffectContext())
What this means is that the base type becomes a bloated monolithic type, and must contain all functionality and members needed for any gameplay effect. This seems wasteful to me and counters the polymorphic system that has been put in place.
A better approach IMO, would be to allow subclasses of UGameplayEffect to specify their own FGameplayEffectContext-derived type - therefore specifying all the data that is relevant to that effect context. The base FGameplayEffectContext would then only contain the bare essentials required for the ability system to function.
As far as I can tell, every location in code that calls AllocGameplayEffectContext() already has the UGameplayEffect available to it. For FGameplayEffectContextHandle::NetSerialize, the FGameplayEffectContext base-type can contain the UGameplayEffect class (which will replicate as an int ID not an FName after the first replication) in order to construct the appropriate type.
This would essentially allow you to create subclasses of UGameplayEffect, and bind only the relevant data for their execution to them. In theory this might even present some opportunities for reduced bandwidth consumption (a few bits at least per executed effect) and maybe make the data types more cache-coherent?