Async loading screen multiplayer issues

Hey guys! I’ve had some issues with my async loading screen for my multiplayer game. It works great when I test it in the main menu and lobby but once I introduce server travel / seamless travel it seems to act differently.

With this setup I get it to appear for a brief second (Where I expect 5 sec here) and then the screen just goes black and the throbber disappears. When I then click on the screen it goes to the next level instantly. I’m aware that this approach wants me to manually stop the loading screen at the moment but one step at a time :smiley:

Game Instance:

void UBaseGameInstance::Init()
{
	Super::Init();

FWorldDelegates::OnSeamlessTravelStart.AddUObject(this, &UBaseGameInstance::OnSeamlessTravelStart);
	
}

void UBaseGameInstance::OnSeamlessTravelStart(UWorld* PersistentWorld, const FString& LevelName)
{
	StartInGameLoadingScreen(true, 5);
}

void UBaseGameInstance::StartInGameLoadingScreen(bool bPlayUntilStopped, float PlayTime)
{
	FLoadingScreenAttributes LoadingScreen;
	LoadingScreen.bAutoCompleteWhenLoadingCompletes = !bPlayUntilStopped;
	LoadingScreen.bWaitForManualStop = bPlayUntilStopped;
	LoadingScreen.bAllowEngineTick = true;
	LoadingScreen.MinimumLoadingScreenDisplayTime = PlayTime;
	LoadingScreen.WidgetLoadingScreen = FLoadingScreenAttributes::NewTestLoadingScreenWidget();
	
	GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
	GetMoviePlayer()->PlayMovie();
}

Game Mode:

void AGM_LobbyHost::BeginPlay()
{
	Super::BeginPlay();

	bUseSeamlessTravel = true;
}

void AGM_LobbyHost::StartTheMatch()
{
ProcessServerTravel(MapName);
}

There’s two ways to fix this problem. Since we get two calls from the engine to change the loading screen attributes after we’ve reached the server as a client (for some strange reason?!). We need to make sure it do not update the setting if it’s still valid.

Alt 1: Modify the Engine

DefaultMoviePlayer.cpp

Modify the SetupLoadingScreen function the like this:

void FDefaultGameMoviePlayer::SetupLoadingScreen(const FLoadingScreenAttributes& InLoadingScreenAttributes)
{
	if (!CanPlayMovie())
	{
		LoadingScreenAttributes = FLoadingScreenAttributes();
		UE_LOG(LogMoviePlayer, Warning, TEXT("Initial loading screen disabled from BaseDeviceProfiles.ini: r.AndroidDisableThreadedRenderingFirstLoad=1"));
	}
	else
	{
		if (!LoadingScreenAttributes.IsValid())
			LoadingScreenAttributes = InLoadingScreenAttributes;
    }
}

Alt 2: Modify the AsyncLoadingScreen plugin

AsyncLoadingScreen.h

Add:

#include "MoviePlayer.h"

Add under private

void MoviePlaybackFinished();
FLoadingScreenAttributes LoadingScreen;
FLoadingScreenAttributes CurrentLoadingScreen;

AsyncLoadingScreen.cpp

Remove:

#include "MoviePlayer.h"

Add a Callback from OnMoviePlaybackFinished in the StartupModule:

void FAsyncLoadingScreenModule::StartupModule()
{
	// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
	if (!IsRunningDedicatedServer() && FSlateApplication::IsInitialized())
	{
		const ULoadingScreenSettings* Settings = GetDefault<ULoadingScreenSettings>();
				
		if (IsMoviePlayerEnabled())
		{
			GetMoviePlayer()->OnPrepareLoadingScreen().AddRaw(this, &FAsyncLoadingScreenModule::PreSetupLoadingScreen);
			GetMoviePlayer()->OnMoviePlaybackFinished().AddRaw(this, &FAsyncLoadingScreenModule::MoviePlaybackFinished);
		}		
		
		// If PreloadBackgroundImages option is check, load all background images into memory
		if (Settings->bPreloadBackgroundImages)
		{
			LoadBackgroundImages();
		}

		// Prepare the startup screen, the PreSetupLoadingScreen callback won't be called
		// if we've already explicitly setup the loading screen
		bIsStartupLoadingScreen = true;
		SetupLoadingScreen(Settings->StartupLoadingScreen);
	}	
}

Add the function MoviePlaybackFinished:

void FAsyncLoadingScreenModule::MoviePlaybackFinished()
{
	if (LoadingScreen.IsValid() && CurrentLoadingScreen.WidgetLoadingScreen == LoadingScreen.WidgetLoadingScreen)
	{
		UE_LOG(LogLoadingScreen, Display, TEXT("Resetting Loading Screen Settings"));
		LoadingScreen = FLoadingScreenAttributes();
	}
}

Modify SetupLoadingScreen function like this:

void FAsyncLoadingScreenModule::SetupLoadingScreen(const FALoadingScreenSettings& LoadingScreenSettings)
{
	if (LoadingScreen.IsValid() && CurrentLoadingScreen.WidgetLoadingScreen == LoadingScreen.WidgetLoadingScreen)
		return;

	TArray<FString> MoviesList = LoadingScreenSettings.MoviePaths;

	// Shuffle the movies list
	if (LoadingScreenSettings.bShuffle == true)
	{
		ShuffleMovies(MoviesList);
	}
		
	if (LoadingScreenSettings.bSetDisplayMovieIndexManually == true)
	{
		MoviesList.Empty();

		// Show specific movie if valid otherwise show original movies list
		if (LoadingScreenSettings.MoviePaths.IsValidIndex(UAsyncLoadingScreenLibrary::GetDisplayMovieIndex()))
		{
			MoviesList.Add(LoadingScreenSettings.MoviePaths[UAsyncLoadingScreenLibrary::GetDisplayMovieIndex()]);
		}
		else
		{
			MoviesList = LoadingScreenSettings.MoviePaths;
		}
	}

	LoadingScreen.MinimumLoadingScreenDisplayTime = LoadingScreenSettings.MinimumLoadingScreenDisplayTime;
	LoadingScreen.bAutoCompleteWhenLoadingCompletes = LoadingScreenSettings.bAutoCompleteWhenLoadingCompletes;
	LoadingScreen.bMoviesAreSkippable = LoadingScreenSettings.bMoviesAreSkippable;
	LoadingScreen.bWaitForManualStop = LoadingScreenSettings.bWaitForManualStop;
	LoadingScreen.bAllowInEarlyStartup = LoadingScreenSettings.bAllowInEarlyStartup;
	LoadingScreen.bAllowEngineTick = LoadingScreenSettings.bAllowEngineTick;
	LoadingScreen.MoviePaths = MoviesList;
	LoadingScreen.PlaybackType = LoadingScreenSettings.PlaybackType;

	if (LoadingScreenSettings.bShowWidgetOverlay)
	{
		const ULoadingScreenSettings* Settings = GetDefault<ULoadingScreenSettings>();

		switch (LoadingScreenSettings.Layout)
		{
		case EAsyncLoadingScreenLayout::ALSL_Classic:
			LoadingScreen.WidgetLoadingScreen = SNew(SClassicLayout, LoadingScreenSettings, Settings->Classic);
			break;
		case EAsyncLoadingScreenLayout::ALSL_Center:
			LoadingScreen.WidgetLoadingScreen = SNew(SCenterLayout, LoadingScreenSettings, Settings->Center);
			break;
		case EAsyncLoadingScreenLayout::ALSL_Letterbox:
			LoadingScreen.WidgetLoadingScreen = SNew(SLetterboxLayout, LoadingScreenSettings, Settings->Letterbox);
			break;
		case EAsyncLoadingScreenLayout::ALSL_Sidebar:
			LoadingScreen.WidgetLoadingScreen = SNew(SSidebarLayout, LoadingScreenSettings, Settings->Sidebar);
			break;
		case EAsyncLoadingScreenLayout::ALSL_DualSidebar:
			LoadingScreen.WidgetLoadingScreen = SNew(SDualSidebarLayout, LoadingScreenSettings, Settings->DualSidebar);
			break;
		}
		
	}

	CurrentLoadingScreen = LoadingScreen;
	GetMoviePlayer()->SetupLoadingScreen(LoadingScreen);
}

I hope I didn’t miss anything :slight_smile: