Attribute is changed multiple times on Instant Gameplay Gameplay Effect prediction

An attribute is changed an unnecessary number of times.

First, a gameplay effect is applied on a client, and it changes the attribute.

Then, the effect is removed on a client, and it removes modifiers from AttributeModeAggregator.

Then, an update attribute set is replicated from the server, and the “on dirty” event is broadcast for the aggregator, which causes the effect recalculation without modifiers already.

Then, the attribute value is updated with server’s value.

Here’s what I get in logs after following STR:

LogTemp: Frame: 41338; Role: ROLE_AutonomousProxy; Attribute changed: 0.00 -> 1.00

LogTemp: Frame: 41339; Role: ROLE_Authority; Attribute changed: 0.00 -> 1.00

LogTemp: Frame: 41340; Role: ROLE_AutonomousProxy; Attribute changed: 1.00 -> 0.00

LogTemp: Frame: 41340; Role: ROLE_AutonomousProxy; Attribute changed: 0.00 -> 1.00

We did a workaround in our project and committed an ability only on authority, while manually faking prediction on the locally controlled client. Because we need to play a gameplay cue on the client with a new attribute value, and we need to do it on ability activation so a gameplay cue has a prediction key and can actually be executed.

So it has quite a few layers to it. Maybe you can suggest a better solution for this? And is it even an intended behavior of the gameplay effects?

Steps to Reproduce

  1. Set up a project with GAS (f.e., from a third-person template and add an ASC on a character in C++)
  2. Add a custom attribute set with an attribute and grant it to ASC.
  3. Subscribe to attribute changes (f.e., from Character) and add logging there.
  4. Create a gameplay effect that changes the attribute.
  5. For convenience, I added a gameplay ability that uses that effect as a cost effect. I think this is not necessary, but I haven’t tried it another way.
  6. Bind the ability activation to a debug key.
  7. Start PIE in Client-Server mode and activate the ability.

Changed/added files in the provided repro project: C++: MyAttributeSet, InstantGECharacter; Blueprints: BP_ThirdPersonCharacter, GE_ChangeMyAttribute, GA_Test

Hi [mention removed]​,

Apologies for the delayed response and thanks for the repro project and detailed steps.

This behavior is intended in GAS as Instant Gameplay Effects applied under prediction will trigger attribute changes multiple times on the autonomous client as part of the prediction and reconciliation cycle. This is described in GameplayPrediction.h:

“We treat predictive instant gameplay effects as infinite-duration gameplay effects… When the prediction key is caught up, the predictive GameplayEffect is removed and we will return to the server-given value.”Client first applies the effect predictively, then removes the temporary modifier when the prediction key is acknowledged, and finally updates the attribute once more when the server’s authoritative value replicates.

If your goal is to react once when the ability activates, you could use Gameplay Cues Notifies instead of binding directly to OnAttributeChange.

Using your repro project as reference, you can do the following:

  • In GE_ChangeMyAttribute, add a Gameplay Cue Tag such as GameplayCue.Change.
  • Create a GameplayCueNotify_Burst Blueprint (e.g: GCN_MyAttrChange) and assign it the same tag.
  • In the Notify’s OnBurst event, add a Print String to verify when it fires.

Once configured, the cue will fire predictively once when the ability is commited. The log output will look like this:

LogTemp: Frame: 1415576; Role: ROLE_AutonomousProxy; Attribute changed: 0.00 -> 1.00
LogBlueprintUserMessages: [GCN_MyAttrChange_C] Client 1: Tag GameplayCue.Change triggered with value 1 by instigator BP_ThirdPersonCharacter0
LogTemp: Frame: 1415577; Role: ROLE_Authority; Attribute changed: 0.00 -> 1.00
LogTemp: Frame: 1415577; Role: ROLE_AutonomousProxy; Attribute changed: 1.00 -> 0.00
LogTemp: Frame: 1415577; Role: ROLE_AutonomousProxy; Attribute changed: 0.00 -> 1.00

The cue executes once at activation (predictively), while the attribute reconciliation still occurs internally as expected.

Please let me know if this information helps.

Best,

Francisco

Hi Francisco, thanks for the response. Yeah, it seems this should do the trick

Hi Nazarii,

Glad to hear it helped! Closing this case now.

Best,

Francisco