Couple of questions regarding network failures

  1. Is the problem with the network failure handling a UE4 issue? I’m on UE5 (5.0.3) and my game was already returning to the main menu after a server shutdown.
    Is there some other scenarios that UE’s default behavior doesn’t handle that would require the callback?

  2. When my client tried to join a new server after being disconnected, it wasn’t able to. I tracked down the error to my handling of OnJoinSessionComplete. In my code, I check the Result parameter and only proceeds if it’s Success. But after a disconnect, I get a UnknownError instead. If I ignore that, I am able to join. Is that OK? Or is the proper behavior to call DestroySession on the client before attempting to join, like we do on the servers when hosting?

In answer to your first question, UE5 is based on 4.27 so it is possible that later versions of UE4 behave in the same way - I would have to go back and check. My general opinion of coding is you should always code defensively unless there is certainty of success. You should therefore handle possible failures. Adding in the callback, if even unnecessary, I would consider good practice. This is of course subjective and others may prefer a different approach.

As for the second point, this is not the first time I’ve heard this and checking for success seems to block connecting from working. I can’t actually say if this is an issue with the project or just a general issue. Not checking for success would go against what I said in the first point but from what I’ve seen, the error code seems unreliable. It’s not ideal. Now this could be an issue with the specific OSS and where I saw this was typically the Steam OSS. I’m not sure about the null subsystem and definitely not epic, oculus, ios or android either as they aren’t covered.

I hope this helps. I know it isn’t a definitive answer but it is all I really have on this.

But then it means that potentially two different active paths will try to go to the main menu, which doesn’t seem very feel safe. As defensive coding goes, it feels better to let the Engine do its thing, since it should know what needs to be done, rather than code my own, possibly incomplete, version of the same feature. It’s even worse when I do not know what will happen to UE’s path after my code executes.

That’s why my question was not so much if I should do it or not (although underneath, it was), but if the instructor did it because there are known issues with UE’s path, or if UE’s path is newer than the lecture (or unknown at the time). At the end of the day, I’d rather trust UE unless I have factual reasons not.

This is what I ended up doing, which keeps the result check and seems to work:

void UPuzzleMPGameInstance::Init()
{
	...
	SessionIntf->OnCreateSessionCompleteDelegates.AddUObject(this, &UPuzzleMPGameInstance::OnCreateSessionComplete);
	// SessionIntf->OnDestroySessionCompleteDelegates.AddUObject(this, &UPuzzleMPGameInstance::OnDestroySessionComplete);
	SessionIntf->OnFindSessionsCompleteDelegates.AddUObject(this, &UPuzzleMPGameInstance::OnFindSessionsComplete);
	SessionIntf->OnJoinSessionCompleteDelegates.AddUObject(this, &UPuzzleMPGameInstance::OnJoinSessionComplete);
	...
}

void UPuzzleMPGameInstance::Host(const FString& ServerName)
{
	...
	FNamedOnlineSession* Session = SessionIntf->GetNamedSession(NAME_GameSession);
	if (Session)
	{
		SessionIntf->DestroySession(NAME_GameSession,
			FOnDestroySessionCompleteDelegate::CreateUObject(this, &UPuzzleMPGameInstance::OnDestroyHostSessionComplete));
	}
	else
	{
		CreateSession();
	}
}

void UPuzzleMPGameInstance::OnDestroyHostSessionComplete(FName SessionName, bool bWasSuccessful)
{
	CreateSession();
}

void UPuzzleMPGameInstance::Join(int32 SessionIndex)
{
	...

	// ClientSessionIndex needs to be added as a class variable
	ClientSessionIndex = SessionIndex; 

	FNamedOnlineSession* Session = SessionIntf->GetNamedSession(NAME_GameSession);
	if (Session)
	{
		SessionIntf->DestroySession(NAME_GameSession,
			FOnDestroySessionCompleteDelegate::CreateUObject(this, &UPuzzleMPGameInstance::OnDestroyClientSessionComplete));
	}
	else
	{
		JoinInternal();
	}
}

void UPuzzleMPGameInstance::OnDestroyClientSessionComplete(FName SessionName, bool bWasSuccessful)
{
	JoinInternal();
}

void UPuzzleMPGameInstance::JoinInternal()
{
	if (!SessionIntf)
	{
		return;
	}
	if (!SessionSearch || !SessionSearch->SearchResults.IsValidIndex(ClientSessionIndex))
	{
		return;
	}

	SessionIntf->JoinSession(0, NAME_GameSession, SessionSearch->SearchResults[ClientSessionIndex]);
}

void UPuzzleMPGameInstance::OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type Result)
{
	switch (Result)
	{
		case EOnJoinSessionCompleteResult::Success:
			UE_LOG(LogTemp, Display, TEXT("Joined session %s"), *SessionName.ToString());
			break;
		case EOnJoinSessionCompleteResult::SessionIsFull:
			UE_LOG(LogTemp, Warning, TEXT("Session %s is full"), *SessionName.ToString());
			return;
		case EOnJoinSessionCompleteResult::SessionDoesNotExist:
			UE_LOG(LogTemp, Warning, TEXT("Session %s does not exist anymore"), *SessionName.ToString());
			return;
		case EOnJoinSessionCompleteResult::CouldNotRetrieveAddress:
			UE_LOG(LogTemp, Warning, TEXT("Could not retrieve address of session %s"), *SessionName.ToString());
			return;
		case EOnJoinSessionCompleteResult::AlreadyInSession:
			// Shouldn't really happen since we destroy the session first.
			// Not sure what that means for that matter, since, with the NULL
			// subsystem, I was getting UnknownError when a session already
			// existed and I wasn't destroying it first.
			// Will try to travel to the server anyway and let the server decide
			UE_LOG(LogTemp, Warning, TEXT("Already in session %s"), *SessionName.ToString());
			break;
		case EOnJoinSessionCompleteResult::UnknownError:
			UE_LOG(LogTemp, Warning, TEXT("Unknown error joining session %s"), *SessionName.ToString());
			return;
	}

	// Travel to the server
	...
}

This could well be a change made in later versions of the UE4 engine. There were quite a number of changes in 4.25 and later but they only show up in section 4. Additionally, in UE5, the project from section 4 created in UE4 isn’t even compatible as they removed support for the simple vehicle classes. Don’t worry, there’s a stripped back template containing the necessary assets attached to the course.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.