Bugs in PlayerController ClientStartOnlineSession and ClientEndOnlineSession

After updating to version 4.8, I discovered that logic of starting and ending online sessions has been added to the HandleMatchHasStarted and HandleMatchHasEnded functions in GameSession class. To start and end a session for clients, these functions now call ClientStartOnlineSession and ClientEndOnlineSession in PlayerController class. Both these functions are executed on client. However, these functions appear to be rather buggy and incomplete.

Bug number 1:

To start with, I will explain first problem and how to reproduce it. This bug is in ClientStartOnlineSession function in PlayerController class. This function is always called by server at start of game immediately after loading up a level. function checks PlayerState before proceeding. problem here is that, at this time, PlayerState may be NULL on clients since it takes some time before it is replicated to client. This will almost always cause function to just return without doing anything. In ShooterGame project, this was handled by using a timer that keeps executing function until PlayerState is replicated, like following:

void APlayerController::ClientStartOnlineSession_Implementation()
{
	if (IsPrimaryPlayer() && PlayerState)
	{
		ULocalPlayer* LP = Cast<ULocalPlayer>(Player);
		if (LP)
		{
			UOnlineSession* Session = LP->GetOnlineSession();
			if (Session)
			{
				Session->StartOnlineSession(PlayerState->SessionName);
			}
		}
	}

	if (PlayerState == NULL)
	{
		// Keep retrying until player state is replicated
		GetWorld()->GetTimerManager().SetTimer(TimerHandle_ClientStartOnlineGame, this, 			&APlayerController::ClientStartOnlineSession_Implementation, 0.2f, false);
	}
}

Reproducable steps:

This can be reproduced with following steps. I build engine from source (version 4.8.2) and created a project based on C++ Third Person template and did following modifications:

Add some print code inside ClientStartOnlineSession function in PlayerController to check if PlayerState is NULL, like following:

void APlayerController::ClientStartOnlineSession_Implementation()
{
	GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("APlayerController::ClientStartOnlineSession_Implementation"));

	if (PlayerState == NULL)
	{
		GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("PlayerState == NULL"));
	}

	if (IsPrimaryPlayer() && PlayerState)
	{
		ULocalPlayer* LP = Cast<ULocalPlayer>(Player);
		if (LP)
		{
			UOnlineSession* Session = LP->GetOnlineSession();
			if (Session)
			{
				Session->StartOnlineSession(PlayerState->SessionName);
			}
		}
	}
}

In your custom GameMode class, override PostLogin and add a call to ClientStartOnlineSession, as shown below. This will be called at startup.

void ATestProjectGameMode::PostLogin(APlayerController* NewPlayer)
{
	GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("ABugTestProjectGameMode::PostLogin"));

	Super::PostLogin(NewPlayer);

	if (!NewPlayer->IsLocalController())
	{
		NewPlayer->ClientStartOnlineSession();
	}
}

Now click Play button as “Standalone Game” with two players. I also used Editor Multiplayer Mode with Play as Listen Server and unchecked Use Single Process. You will hopefully see following printed to screen on client:

GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("PlayerState == NULL"));
GEngine->AddOnScreenDebugMessage(-1, 20.0f, Fcolor::Cyan, TEXT("APlayerController::ClientStartOnlineSession_Implementation"));
GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("ABugTestProjectGameMode::PostLogin"));

Bug number 2:

There is also a second bug in the ClientStartOnlineSession function. This bug is also in ClientEndOnlineSession. Since this bug is same for both, I will only explain it for ClientStartOnlineSession. This function calls GetOnlineSession to get online session class. problem here is that returned pointer is only an instance of class OnlineSession and not OnlineSessionClient class. This results in a call to empty function StartOnlineSession inside OnlineSession class (session is therefore not started). I suspect probably wanted to call overriden StartOnlineSession function in OnlineSessionClient class instead, since it actually performs logic of starting a session.

Reproducable steps:

Keep existing reproduced project from first bug, and add following modifications:

Add a print statement in StartOnlineSession function in OnlineSession.h, like following:

virtual void StartOnlineSession(FName SessionName)
{ 
	GEngine->AddOnScreenDebugMessage(-1, 20.0f, Fcolor::Cyan, 	TEXT("UOnlineSession::StartOnlineSession"));
};

Also, add a print statement in StartOnlineSession function in OnlineSessionClient class, like following:

void UOnlineSessionClient::StartOnlineSession(FName SessionName)
{
	GEngine->AddOnScreenDebugMessage(-1, 20.0f, FColor::Cyan, TEXT("UOnlineSessionClient::StartOnlineSession")); 

	IOnlineSessionPtr SessionInterface = Online::GetSessionInterface(GetWorld());
	if (SessionInterface.IsValid())
	{
		FNamedOnlineSession* Session = SessionInterface->GetNamedSession(SessionName);
		if (Session &&
			(Session->SessionState == EOnlineSessionState::Pending || Session->SessionState == EOnlineSessionState::Ended))
		{
			StartSessionCompleteHandle = SessionInterface->AddOnStartSessionCompleteDelegate_Handle(FOnStartSessionCompleteDelegate::CreateUObject(this, &UOnlineSessionClient::OnStartSessionComplete));
			SessionInterface->StartSession(SessionName);
		}
	}
}

Finally, create a function in your custom character class that is called when you click left mouse button (or any other button). Inside this function, call ClientStartOnlineSession, like following:

void ATestProjectCharacter::callStartOnlineSession()
{
	APlayerController* player = Cast<APlayerController>(Controller);
	player->ClientStartOnlineSession();
}

We just use this to check which StartOnlineSession function that is called. Now click Play button as “Standalone Game” with two players. If you click left mouse button, you will see that only StartOnlineSession in OnlineSession class is called.

Hi ,

Thanks for details reproduction steps! I’ve assigned one of our support staff to try reproducing it here, and we’ll let you know if we need any additional information.

Hey Gardennnnnnn-

I tried to copy code you provided into a project but I did not get same output that you mention. Both client and server showed the “ABugTestProjectGameMode::PostLogin” message on screen however other debug lines added did not appear. Could you post a sample project that has this behavior so I can see exactly what you mean?

Cheers

Hello

I created a project now based on third person template that has behavior. I have uploaded this to google drive on following link:

link text

Hey Gardennnnnnn-

Using project you provided I was able to reproduce issue and have submitted a bug reprot (UE-19471) for investigation. I would suggest using workaround you mentioned is included in ShooterGame to retry function until PlayerState is replicated.

Cheers