Regarding this:
“On SimulatedProxy the effect is applied predictively as Inifiniteand it only changes the Current value of the HP attribute: Health = Base:100 Current:85”
This is intentional to make the supported autonomous proxy use case work. When you predictively apply a GE to yourself as autonomous proxy, we will apply the GE and its modifiers as Infinite, so that it can be removed later as soon as the server has applied the GE as well. So we apply the instant GE as infinite, just to remember that it exists and to remove it later, removing the mods and tags. That’s also why the Health’s Current value is modified instead of the Base value, because we treat the predicted GE as a duration-based, undoable GE instead of a permanent one. Ultimately whether the Base (permanent) or Current (temporary) value is modified doesn’t matter, as long as the predicted GE is removed so the same modifier hasn’t applied to both.
With the usual autonomous proxy predicted GE (for example: a local predicted ability calls ApplyGameplayEffectToOwner), the predicted GE sets up cleanup upon server confirmation in these functions. Preparing the delegate to remove the predicted GE on server confirmation:
> UnrealEditor-GameplayAbilities.dll!FPredictionKey::NewRejectOrCaughtUpDelegate(TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy> Event) Line 247 C++
UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::ApplyGameplayEffectSpec(const FGameplayEffectSpec & Spec, FPredictionKey & InPredictionKey, bool & bFoundExistingStackableGE) Line 4486 C++
UnrealEditor-GameplayAbilities.dll!UAbilitySystemComponent::ApplyGameplayEffectSpecToSelf(const FGameplayEffectSpec & Spec, FPredictionKey PredictionKey) Line 1054 C++
UnrealEditor-GameplayAbilities.dll!UGameplayAbility::ApplyGameplayEffectSpecToOwner(const FGameplayAbilitySpecHandle AbilityHandle, const FGameplayAbilityActorInfo * ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEffectSpecHandle SpecHandle) Line 2080 C++
UnrealEditor-GameplayAbilities.dll!UGameplayAbility::ApplyGameplayEffectToOwner(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo * ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const UGameplayEffect * GameplayEffect, float GameplayEffectLevel, int Stacks) Line 2056 C++
UnrealEditor-GameplayAbilities.dll!UGameplayAbility::BP_ApplyGameplayEffectToOwner(TSubclassOf<UGameplayEffect> GameplayEffectClass, int GameplayEffectLevel, int Stacks) Line 2041 C++
UnrealEditor-GameplayAbilities.dll!UGameplayAbility::execBP_ApplyGameplayEffectToOwner(UObject * Context, FFrame & Stack, void * const Z_Param__Result) Line 129 C++
This is the callstack for the client removing the predicted GE on an autonomous proxy:
> UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::RemoveActiveGameplayEffectGrantedTagsAndModifiers(const FActiveGameplayEffect & Effect, bool bInvokePredictedEffects, bool bPredictionRejected) Line 4893 C++
UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::InternalOnActiveGameplayEffectRemoved(FActiveGameplayEffect & Effect, bool bInvokePredictedEffects, const FGameplayEffectRemovalInfo & GameplayEffectRemovalInfo) Line 4878 C++
UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::InternalRemoveActiveGameplayEffect(int Idx, int StacksToRemove, bool bPrematureRemoval, bool bPredictionRejected) Line 4812 C++
UnrealEditor-GameplayAbilities.dll!FActiveGameplayEffectsContainer::RemoveActiveGameplayEffect(FActiveGameplayEffectHandle Handle, int StacksToRemove, bool bPredictionRejected) Line 4695 C++
UnrealEditor-GameplayAbilities.dll!UAbilitySystemComponent::RemoveActiveGameplayEffect_AllowClientRemoval(FActiveGameplayEffectHandle Handle, int StacksToRemove) Line 1248 C++
[External Code]
[Inline Frame] UnrealEditor-GameplayAbilities.dll!TDelegate<void __cdecl(void),FDefaultDelegateUserPolicy>::ExecuteIfBound() Line 643 C++
UnrealEditor-GameplayAbilities.dll!FPredictionKeyDelegates::CatchUpTo(short Key) Line 333 C++
UnrealEditor-GameplayAbilities.dll!FReplicatedPredictionKeyItem::OnRep(const FReplicatedPredictionKeyMap & InArray) Line 602 C++
[Inline Frame] UnrealEditor-GameplayAbilities.dll!FReplicatedPredictionKeyItem::PostReplicatedChange(const FReplicatedPredictionKeyMap &) Line 585 C++
UnrealEditor-GameplayAbilities.dll!FFastArraySerializer::TFastArraySerializeHelper<FReplicatedPredictionKeyItem,FReplicatedPredictionKeyMap>::PostReceiveCleanup<TMap<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0>>,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0>>,0>>>(FFastArraySerializer::FFastArraySerializerHeader & Header, TArray<int,TSizedInlineAllocator<8,32,TSizedDefaultAllocator<32>>> & ChangedIndices, TArray<int,TSizedInlineAllocator<8,32,TSizedDefaultAllocator<32>>> & AddedIndices, TMap<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0>>,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0>>,0>> & GuidMap) Line 1172 C++
UnrealEditor-GameplayAbilities.dll!FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs<FReplicatedPredictionKeyItem,FReplicatedPredictionKeyMap>(TArray<FReplicatedPredictionKeyItem,TSizedDefaultAllocator<32>> & Items, FNetDeltaSerializeInfo & Parms, FReplicatedPredictionKeyMap & ArraySerializer) Line 1858 C++
UnrealEditor-GameplayAbilities.dll!FFastArraySerializer::FastArrayDeltaSerialize<FReplicatedPredictionKeyItem,FReplicatedPredictionKeyMap>(TArray<FReplicatedPredictionKeyItem,TSizedDefaultAllocator<32>> & Items, FNetDeltaSerializeInfo & Parms, FReplicatedPredictionKeyMap & ArraySerializer) Line 1215 C++
UnrealEditor-Engine.dll!UScriptStruct::ICppStructOps::NetDeltaSerialize(FNetDeltaSerializeInfo & DeltaParms, void * Data) Line 1923 C++
In that last callstack, the property being replicated that triggers the cleanup is ReplicatedPredictionKeyMap in AbilitySystemComponent. The process is explained in GameplayPrediction.h, see the comment starting from “If ServerTryActivateAbility succeeds”.
If you want to get predicted GEs on simulated proxies to work, you need to set up something similar, so:
- You apply a GE predictively on a target, in whatever way you can where the ASC doesn’t ignore the application. Instant GEs should be applied as Infinite, for undo purposes.
- You apply the same GE on the server.
- The client finds out ASAP when the server has applied the same GE (or rejected the application) and removes the predicted Infinite GE.
You can try to make the above work with the ASC api with engine modifications, or the FPredictionKey api, or manually. Since this isn’t something we support, the rest is up to you. But I hope this information is helpful!
[Attachment Removed]