Gameplay Ability System
Standalone:
Removing an attribute set and then removing an active gameplay effect that has a modifier for an attribute from that set results in a crash. Currently, one must remove the gameplay effect before removing an attribute set.
Related server-client problem:
Removing a gameplay effect and then removing an attribute set at the same time on the server does not guarantee the same order of operations on the client, since the replication order of SpawnedAttributes and ActiveGameplayEffects is not guaranteed. When SpawnedAttributes replicates to a client before ActiveGameplayEffects this results in an attribute set being removed before the gameplay effect - this results in the same crash as in the standalone case.
Replication order is never guaranteed so this poses a problem for server-side code that calls RemoveSpawnedAttribute and RemoveActiveGameplayEffect at roughly the same time.
Repro project instructions
In the repro project I've done what's described below and the following hotkeys are bound:
1 2 3 4 works as intended. 1 2 4 3 causes the crash.
Standalone scenario
Start PIE as standalone. Try the following order of hotkeys:
Observe that everything works as intended. Then try:
Observe that a crash happens at step 4, as the gameplay effect removal logic assumes that the attribute set exists.
Second scenario
A bit harder to repro, but this is the source of the user reported crash. When server-code removes a gameplay effect and attribute set in the same tick and in the correct order (first GE, then attribute set) - this can happen out of order on clients since replication order is not guaranteed. To repro this, make all hotkeys execute their logic on server only by introducing server RPCs. In the repro project this is set up.
The two replicated attributes of AbilitySystemComponent that are relevant are ActiveGameplayEffects and SpawnedAttributes.
UAbilitySystemComponent::SetNumericAttribute_Internal(const FGameplayAttribute &,float &) AbilitySystemComponent.cpp:342
FActiveGameplayEffectsContainer::InternalUpdateNumericalAttribute(FGameplayAttribute,float,const FGameplayEffectModCallbackData *,bool) GameplayEffect.cpp:2814
FActiveGameplayEffectsContainer::OnAttributeAggregatorDirty(FAggregator *,FGameplayAttribute,bool) GameplayEffect.cpp:2405
UAbilitySystemComponent::OnAttributeAggregatorDirty(FAggregator *,FGameplayAttribute,bool) AbilitySystemComponent.cpp:1785
[Inlined] Invoke(void (UAbilitySystemComponent::*)(FAggregator *, FGameplayAttribute, bool),UAbilitySystemComponent *&,FAggregator *&,const FGameplayAttribute &,const bool &) Invoke.h:66
[Inlined] UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int,0,1>,FGameplayAttribute,bool>::ApplyAfter(void (UAbilitySystemComponent::*&)(FAggregator *, FGameplayAttribute, bool),UAbilitySystemComponent *&,FAggregator *&) Tuple.h:321
TBaseUObjectMethodDelegateInstance<0,UAbilitySystemComponent,void __cdecl(FAggregator *),FDefaultDelegateUserPolicy,FGameplayAttribute,bool>::ExecuteIfSafe(FAggregator *) DelegateInstancesImpl.h:550
[Inlined] TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(FAggregator *) MulticastDelegateBase.h:204
TMulticastDelegate<void __cdecl(FAggregator *),FDefaultDelegateUserPolicy>::Broadcast(FAggregator *) DelegateSignatureImpl.inl:988
FAggregator::BroadcastOnDirty() GameplayEffectAggregator.cpp:605
[Inlined] FAggregator::RemoveAggregatorMod(FActiveGameplayEffectHandle) GameplayEffectAggregator.cpp:476
FActiveGameplayEffectsContainer::RemoveActiveGameplayEffectGrantedTagsAndModifiers(const FActiveGameplayEffect &,bool) GameplayEffect.cpp:3730
FActiveGameplayEffectsContainer::InternalOnActiveGameplayEffectRemoved(FActiveGameplayEffect &,bool,const FGameplayEffectRemovalInfo &) GameplayEffect.cpp:3703
FActiveGameplayEffect::PreReplicatedRemove(const FActiveGameplayEffectsContainer &) GameplayEffect.cpp:1835
FFastArraySerializer::TFastArraySerializeHelper<FActiveGameplayEffect,FActiveGameplayEffectsContainer>::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 &,TArray<int,TSizedInlineAllocator<8,32,TSizedDefaultAllocator<32> > > &,TArray<int,TSizedInlineAllocator<8,32,TSizedDefaultAllocator<32> > > &,TMap<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0> >,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,TMap<int,FGuidReferences,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<int,FGuidReferences,0> >,0> > &) FastArraySerializer.h:1132
FFastArraySerializer::FastArrayDeltaSerialize_DeltaSerializeStructs<FActiveGameplayEffect,FActiveGameplayEffectsContainer>(TArray<FActiveGameplayEffect,TSizedDefaultAllocator<32> > &,FNetDeltaSerializeInfo &,FActiveGameplayEffectsContainer &) FastArraySerializer.h:1814
FFastArraySerializer::FastArrayDeltaSerialize<FActiveGameplayEffect,FActiveGameplayEffectsContainer>(TArray<FActiveGameplayEffect,TSizedDefaultAllocator<32> > &,FNetDeltaSerializeInfo &,FActiveGameplayEffectsContainer &) FastArraySerializer.h:1191
FActiveGameplayEffectsContainer::NetDeltaSerialize(FNetDeltaSerializeInfo &) GameplayEffect.cpp:4134
FRepLayout::ReceiveCustomDeltaProperty(FReceivingRepState *__restrict,FNetDeltaSerializeInfo &,FStructProperty *) RepLayout.cpp:4612
[Inlined] FNetSerializeCB::ReceiveCustomDeltaProperty(const FRepLayout &,FReceivingRepState *,FNetDeltaSerializeInfo &,FStructProperty *) DataReplication.cpp:262
FObjectReplicator::ReceivedBunch(FNetBitReader &,const FReplicationFlags &,const bool,bool &) DataReplication.cpp:1136
UActorChannel::ProcessBunch(FInBunch &) DataChannel.cpp:3125
UActorChannel::ReceivedBunch(FInBunch &) DataChannel.cpp:2965
UChannel::ReceivedSequencedBunch(FInBunch &) DataChannel.cpp:558
UChannel::ReceivedNextBunch(FInBunch &,bool &) DataChannel.cpp:1022
UChannel::ReceivedRawBunch(FInBunch &,bool &) DataChannel.cpp:670
UNetConnection::ReceivedPacket(FBitReader &,bool) NetConnection.cpp:3530
UNetConnection::ReceivedRawPacket(void *,int) NetConnection.cpp:1881
UIpNetDriver::TickDispatch(float) IpNetDriver.cpp:1294
UNetDriver::InternalTickDispatch(float) NetDriver.cpp:1539
[Inlined] Invoke(void (UNetDriver::*)(float),UNetDriver *&,float &) Invoke.h:66
[Inlined] UE::Core::Private::Tuple::TTupleBase<TIntegerSequence<unsigned int> >::ApplyAfter(void (UNetDriver::*&)(float),UNetDriver *&,float &) Tuple.h:321
TBaseUObjectMethodDelegateInstance<0,UNetDriver,void __cdecl(float),FDefaultDelegateUserPolicy>::ExecuteIfSafe(float) DelegateInstancesImpl.h:550
[Inlined] TMulticastDelegateBase<FDefaultDelegateUserPolicy>::Broadcast(float) MulticastDelegateBase.h:204
TMulticastDelegate<void __cdecl(float),FDefaultDelegateUserPolicy>::Broadcast(float) DelegateSignatureImpl.inl:988
UWorld::Tick(ELevelTick,float) LevelTick.cpp:1352
UEditorEngine::Tick(float,bool) EditorEngine.cpp:1903
UUnrealEdEngine::Tick(float,bool) UnrealEdEngine.cpp:516
FEngineLoop::Tick() LaunchEngineLoop.cpp:5806
[Inlined] EngineTick() Launch.cpp:61
GuardedMain(const wchar_t *) Launch.cpp:195
LaunchWindowsStartup(HINSTANCE__ *,HINSTANCE__ *,char *,int,const wchar_t *) LaunchWindows.cpp:233
WinMain(HINSTANCE__ *,HINSTANCE__ *,char *,int) LaunchWindows.cpp:284
[Inlined] invoke_main() 0x00007ff67dedd9a6
__scrt_common_main_seh() 0x00007ff67dedd985
<unknown> 0x00007ff845c47614
<unknown> 0x00007ff8479626b1
There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-193689 in the post.
0 |
Component | UE - Gameplay - Gameplay Ability System |
---|---|
Affects Versions | 5.2, 5.3 |
Target Fix | 5.4 |
Fix Commit | 28130133 |
---|---|
Main Commit | 28130157 |
Created | Aug 23, 2023 |
---|---|
Resolved | Sep 22, 2023 |
Updated | Oct 2, 2023 |