Code inside of UOnlineSessionClient is causing game crashes for us when a disconnect is handled by the above class.
The following occurs:
When DestroyExistingSession_Impl is called, the following code is run:
Result = SessionInt->AddOnDestroySessionCompleteDelegate_Handle(Delegate);
SessionInt->DestroySession(SessionName);
Notice here the resulting handle from the delegate addition is assigned to a local variable, and does not have a chance to be stored in the variable OnDestroyForMainMenuCompleteDelegateHandle before the call to DestroySession immediately after. This is because OnDestroyForMainMenuCompleteDelegateHandle is assigned via DestroyExistingSession_Impl’s return.
The problem occurs when the DestroySessionCompleteDelegate is called synchronously during the call to DestroySession which happens right after the delegate’s addition, but before it is saved to OnDestroyForMainMenuCompleteDelegateHandle.
The Delegate has OnDestroyForMainMenuComplete assigned to it, and in that call to OnDestroyForMainMenuComplete which occurs synchronously during the DestroySession call,
the class tries to clear the delegate like so:
SessionInt->ClearOnDestroySessionCompleteDelegate_Handle(OnDestroyForMainMenuCompleteDelegateHandle);
Because it passes an unassigned DelegateHandle, the delegate assigned previously is not actually removed.
This does not crash itself, but the problem occurs if a player reconnects to a server again and disconnects again. The uncleaned handle causes a crash when the class tries to add the OnDestroyForMainMenuCompleteDelegateHandle again during the call to DestroyExistingSession_Impl. As the previous delegate was not removed, the call to AddDelegateInstance in DelegateSignatureImpl_Variadics.inl thows an error on the following check:
check(!DelegateInstanceInterface->IsSameFunction(*InDelegateInstance));
MOST LIKELY SOLUTION:
It looks like the issue would not occur if the handle addition above assigned the handle variable via an output variable argument to DestroyExistingSession_Impl instead of via the result of the call.
Namely instead of
FDelegateHandle UOnlineSessionClient::DestroyExistingSession_Impl(FName SessionName, FOnDestroySessionCompleteDelegate& Delegate)
{
...
Result = SessionInt->AddOnDestroySessionCompleteDelegate_Handle(Delegate);
SessionInt->DestroySession(SessionName);
...
return Result;
}
doing
void UOnlineSessionClient::DestroyExistingSession_Impl(FName SessionName, FOnDestroySessionCompleteDelegate& Delegate, FDelegateHandle* DelegateHandleOut)
{
...
FDelegateHandle Result = SessionInt->AddOnDestroySessionCompleteDelegate_Handle(Delegate);
if (DelegateHandleOut) *DelegateHandleOut = Result;
SessionInt->DestroySession(SessionName);
...
}
Hope that helps!