Hello! In this post we’ll learn together how to Lazy Load the Ability System Component in our multiplayer games to reduce memory usage!
Introduction
The Ability System Component (ASC) can be a bit resource-intensive, especially when dealing with World Actors that require it, like Damageables. If our world is populated with such Actors, their memory footprint can become noticeable.
Taking a cue from Epic and the insights shared by Tranek in the excellent GASDocumentation, Fortnite adopts a smart approach. They lazily load their ASCs in their World Actors just when they’re needed, dodging the memory hit until it’s absolutely necessary. This memory optimization is a significant boost for scalability; since ASC World Actors won’t carry that footprint unless explicitly interacted with.
Think of it like this: buildings, trees, and damageable props on standby with a lower cost, only racking up the memory bill when they step into the gameplay spotlight.
But before we start, I would love to thank my dear friend KaosSpectrum for his significant research and collaboration on the topic; without him, this wouldn’t have been possible.
The implementation
So… let’s take a look on how we can implement this. Bear with me, as Epic hasn’t posted this anywhere online, so if you spot any error, feel free to report it!
The first step is to create an Actor type that will hold this behaviour, in my case I called it AMyGameplayActor. It is critical that your actor implements the IAbilitySystemInterface interface, as we’ll require to override the GetAbilitySystemComponent function.
In this Actor, I’ve defined an Enum EAbilitySystemCreationPolicy that determines the ASC loading behaviour:
I’ve also defined other relevant properties for the implementation, such as the Ability System Component class, the Attribute mutation buffer, or the runtime ASC transient property:
Gameplay Actors need to replicate by default, so we setup their constructor appropriately:
Then, we create a the relevant functions to create the ASC and to Initialize it:
Following next, we override and implement the functions below to accomodate to the ASC creation policies we defined:
As we can see, if the policy is Lazy, the first time we call GetAbilitySystemComponent() through any of the ASC API functions, the ASC will get created and initialized. The PreReplication overriden function and our proxy ReplicatedAbilitySystemComponent variable will make sure AbilitySystemComponent will get to the client and Server properly.
Pending Attribute Replication
When the Ability System Component gets created in the server, it takes latency time before it reaches the client, and during that time, Attributes might need to replicate (accounting for Net Priority and Frequency on the owning Actors).
During this time, we need to hold the attribute changes somewhere until the ASC is available in the Client, for that, we use the PendingAttributeReplications Array defined in our Gameplay Actor.
To buffer the Attribute mutations, we need to route Attribute replication through our Gameplay Actor. In our base Attribute class we have in our game define the following macro:
Then, in our child Attribute Sets, we have to implement our OnRep as follows:
As we can see, the macro we created calls SetPendingAttributeFromReplication we defined in our AMyGameplayActor from the Attribute Set OnRep functions, which fill our buffer Array PendingAttributeReplications which gets consumed in ApplyPendingAttributesFromReplication, when the ASC reaches the client in AMyGameplayActor.
ApplyPendingAttributesFromReplication calls DeferredSetBaseAttributeValueFromReplication which will ensure we have the most up to date attribute values on the client:
Conclusion
Thanks for reading!
Now you’ll have a way to save some memory from your ASC Gameplay Actors! I hope you found this article helpful!
I’m going to leave a remaining challenge to the curious reader: Deferred delegates! These deferred delegates should happen when the ASC reaches the client in our Gameplay Actor, and it would give us the possibility to react to the buffered changes. If you get to implement this, feel free to share it!
In this post we’ll learn a very cool trick to enhance your multiplayer logs. It consists on accessing the server instance from the client in the editor. But ...