I have a gameplay effect which is not replicating its attribute changes

The attribute changes from the effect seem to apply on the server but not on the client. We rely pretty heavily on dynamically creating gameplay effects and we don’t need the effects to replicate but we absolutely need attribute changes and tag changes to replicate.

Honestly even some info on debugging what could be going wrong here would be useful

[Attachment Removed]

Steps to Reproduce

// setup a second infinite effect that we can use to manage the value
UGameplayEffect* ManagedEffect = NewObject<UGameplayEffect>();
ManagedEffect->DurationPolicy = GESpec.Def->DurationPolicy == EGameplayEffectDurationType::Instant?
                         EGameplayEffectDurationType::Instant : EGameplayEffectDurationType::Infinite;
 
FGameplayModifierInfo SetByCaller;
SetByCaller.Attribute = Attribute;
FSetByCallerFloat SetByCallerFloat;
SetByCallerFloat.DataTag = ModifierEffectComponent::Modifier_Magnitude;
SetByCaller.ModifierMagnitude = FGameplayEffectModifierMagnitude(SetByCallerFloat);
SetByCaller.ModifierOp = Operation;
SetByCaller.EvaluationChannelSettings.SetEvaluationChannel(EvaluationChannel);
SetByCaller.SourceTags = SourceTags;
SetByCaller.TargetTags = TargetTags;
ManagedEffect->Modifiers.Add(SetByCaller);
FGameplayEffectContextHandle Context = SourceASC->MakeEffectContext();
Context.AddSourceObject(this);
FActiveGameplayEffectHandle AppliedEffect = SourceASC->ApplyGameplayEffectToTarget(ManagedEffect, TargetASC, 1.f, Context);
 
FActiveGameplayEffectsContainer* ActiveEffects = TargetASC->GetActiveGameplayEffectsContainer();
ActiveEffects->UpdateActiveGameplayEffectSetByCallerMagnitude(AppliedEffect, ModifierEffectComponentTags::Modifier_Magnitude, AttributeAmount);
ActiveEffects->UpdateActiveGameplayEffectSetByCallerMagnitude(AppliedEffect, ModifierEffectComponentTags::Modifier_MagnitudeInitial, AttributeAmount);

I’m running this code within OnActiveGameplayEffectApplied from an effect component on another effect

[Attachment Removed]

Hello. We support attribute value changes from dynamically constructed gameplay effects. Whether an attribute replicates or not, is defined in the UAttributeSet subclass, just like any other replicated property. It’s compatible with all ASC replication modes. GAS is compatible with push-model replication. For an attribute, whether it uses push model or not will be defined by its attribute set’s GetLifetimeReplicatedProps implementation.

First some sanity checks:

  • Are the attributes you’re modifying marked as replicated?
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated)
	FGameplayAttributeData ExampleAttribute;
	ATTRIBUTE_ACCESSORS_BASIC(UMyAttributeSet, ExampleAttribute)
  • What is that attribute set’s implementation of GetLifetimeReplicatedProps?
void UMyAttributeSet::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const
{
	Super::GetLifetimeReplicatedProps(OutLifetimeProps);
 
	FDoRepLifetimeParams Params;
	Params.bIsPushBased = true;
	DOREPLIFETIME_WITH_PARAMS_FAST(UMyAttributeSet, ExampleAttribute, Params);
}
  • "Narrowed this down a bit. It’s related to having the replication mode set to full rather than minimal. " Are you seeing the correct result in full replication mode only, or on minimal only? And are we talking about autonomous proxies or simulated proxies?

With the above type of setup, any server-side change to “ExampleAttribute” in this case should replicate down to clients: both autonomous proxies and simulated proxies. However, with push model you must also ensure that the property is marked dirty on any change. In that SourceASC->ApplyGameplayEffectToTarget call, any modifiers applied by the ManagedEffect itself will mark the attribute dirty for push model networking. FGameplayAttribute::SetNumericValueChecked is the function that does that by calling MARK_PROPERTY_DIRTY.

You might want to double check whether the MARK_PROPERTY_DIRTY call in SetNumericValueChecked actually enters all the way into MarkPropertyDirty() in PushModel.cpp, like this callstack:

>	UnrealEditor-NetCore.dll!UEPushModelPrivate::MarkPropertyDirty(const UObject * Object, const UEPushModelPrivate::FNetPushObjectId ObjectId, const int RepIndex) Line 469	C++
 	UnrealEditor-GameplayAbilities.dll!FGameplayAttribute::SetNumericValueChecked(float & NewValue, UAttributeSet * Dest) Line 100	C++
 	UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::InternalUpdateNumericalAttribute(FGameplayAttribute Attribute, float NewValue, const FGameplayEffectModCallbackData * ModData, bool bFromRecursiveCall) Line 3897	C++
 	UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::SetAttributeBaseValue(FGameplayAttribute Attribute, float NewBaseValue) Line 3985	C++
 	UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::ApplyModToAttribute(const FGameplayAttribute & Attribute, TEnumAsByte<enum EGameplayModOp::Type> ModifierOp, float ModifierMagnitude, const FGameplayEffectModCallbackData * ModData) Line 4109	C++
 	UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::InternalExecuteMod(FGameplayEffectSpec & Spec, FGameplayModifierEvaluatedData & ModEvalData) Line 4063	C++
 	UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::ExecuteActiveEffectsFrom(FGameplayEffectSpec & Spec, FPredictionKey PredictionKey) Line 3205	C++

I’m not sure what you’re doing during the lifetime of that active GE. Are you modifying attributes and are those modifications not reflected on clients? Whenever an attribute changes value server-side, see if that actually triggers a call to FGameplayAttribute::SetNumericValueChecked.

Note: the value stored on the AttributeSet may be different, from the value calculated on the fly with FAggregators. Depending on how you’re using gameplay attributes:

  • (a) The value on the attribute set that’s updated on applying a GE, or
  • (b) Calculating attribute values on the fly via AttemptCalculateAttributeMagnitude

The (b) value calculated server-side vs. client-side might differ if any of its dependencies aren’t replicated over. Is that the type of issue you’re seeing?

Hopefully this provides some help debugging already.

By the way, if you’re not using Gameplay Debugger in PIE yet, please use it so that you can see whether the server and client at least agree on the replicated attribute value stored on the attribute set.

[Attachment Removed]

Hello, I believe I understand the issue now. This was a good hint:

“The base value of the attribute seems fine, it just seems as though that current value is not replicating.”

I was a bit too focused on the fact that attribute values should replicate regardless of how Active GE info is replicated. This is true, but I suspect your client is overwriting the attribute Current value.

I should correct my previous statement: attribute changes from server-side dynamically constructed Instant gameplay effects are supported, but…

  • Tag replication via dynamic GEs is not supported. The Spec.Def indeed won’t exist client-side because the NewObject you created isn’t a replicated object. You also shouldn’t make it a replicated object because of the overhead of having many. You should add the intended tags server-side via ASC->UpdateTag(Tag, Count, EGameplayTagReplicationState::CountToOwner). See EGameplayTagReplicationState for other options. This used to be ReplicatedLooseTags.
  • Infinite and Duration effects are not, because they write to the Current value of an attribute. The Current value is replicated from server->client, but the client will overwrite the value whenever another GE is applied that modifies the same attribute. Basically, an attribute’s Current is frequently recalculated client-side but the client won’t have the dynamic GE info.
    • In other words: the attribute’s Base + Current are replicated over currently.
    • But then Current gets stomped whenever the client locally adds/removes a GE that operates on the ability. It recalculates current based on all locally known modifiers.
    • In Minimal mode, by design neither autonomous nor sim proxies will have ActiveGameplayEffect info, so will not attempt to recalculate the Current value. It leaves the server-replicated value alone, so keeps it in sync.
    • In Mixed mode, by the above applies for sim proxies. But I expect the autonomous proxy to still have a desynced attribute value, either immediately or the first time you add another GE that modifies the same attribute.

My recommendations:

  • For tag replication of runtime decided tags, server-side call ASC->UpdateTag with a value for EGameplayTagReplicationState so that it replicates to clients. Pre UE 5.6 the recommendation was to add it to ReplicatedLooseTags.
  • For instant dynamic constructed GEs, modifying attributes server-side should work as intended because the server will modify the Base value, which clients won’t overwrite.
  • For infinite dynamic constructed GEs, perhaps you can use the server-side to modify replicated attributes that the client will never modify predictively and the client will never modify via a non-dynamic GE.
    • But in my opinion it’s better to look into refactoring things into either SetByCaller values on the original non-dynamic GE in your setup, that the server may update over the original active GE’s lifetime. UpdateActiveGameplayEffectSetByCallerMagnitude will work, but only for non-dynamic GEs.
    • Or let the server remove an old GE + apply a new one when a modifier value needs to change at runtime. It depends on how frequent this will be necessary.

Either way some refactoring will be necessary because dynamic constructed GEs don’t have the feature set that I believe you’re expecting. I’m happy to assist with brainstorming if you share more context about your setup + designer needs.

[Attachment Removed]

We are using push model replication for attributes if thats relevant

[Attachment Removed]

Narrowed this down a bit. It’s related to having the replication mode set to full rather than minimal. Does this cause attributes not to be replicated somehow?

[Attachment Removed]

Hi. We’re seeing the appropriate values in minimal/mixed only. Our GetLifetimeReplicatedProps looks as you showed it.

It seems like there’s some issues on the client with full as it can’t resolve the definition from the spec.

FGameplayAttribute::SetNumericValueChecked - this does get called.

The base value of the attribute seems fine, it just seems as though that current value is not replicating.

[Attachment Removed]