214 lines
7.6 KiB
C++
214 lines
7.6 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "LyraFrontendStateComponent.h"
|
|
#include "Engine/LocalPlayer.h"
|
|
#include "GameFramework/PlayerController.h"
|
|
#include "GameFramework/GameModeBase.h"
|
|
#include "GameFeaturesSubsystem.h"
|
|
#include "System/LyraAssetManager.h"
|
|
#include "GameFeatureAction.h"
|
|
#include "GameFeaturesSubsystemSettings.h"
|
|
#include "GameModes/LyraExperienceManagerComponent.h"
|
|
#include "GameModes/LyraExperienceDefinition.h"
|
|
#include "TimerManager.h"
|
|
#include "NativeGameplayTags.h"
|
|
#include "ControlFlowManager.h"
|
|
#include "CommonUIExtensions.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "PrimaryGameLayout.h"
|
|
#include "ICommonUIModule.h"
|
|
#include "CommonUISettings.h"
|
|
#include "CommonUserSubsystem.h"
|
|
#include "CommonSessionSubsystem.h"
|
|
#include "Engine/GameInstance.h"
|
|
|
|
namespace FrontendTags
|
|
{
|
|
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_PLATFORM_TRAIT_SINGLEONLINEUSER, "Platform.Trait.SingleOnlineUser");
|
|
UE_DEFINE_GAMEPLAY_TAG_STATIC(TAG_UI_LAYER_MENU, "UI.Layer.Menu");
|
|
}
|
|
|
|
ULyraFrontendStateComponent::ULyraFrontendStateComponent(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::BeginPlay()
|
|
{
|
|
Super::BeginPlay();
|
|
|
|
// Listen for the experience load to complete
|
|
AGameStateBase* GameState = GetGameStateChecked<AGameStateBase>();
|
|
ULyraExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<ULyraExperienceManagerComponent>();
|
|
check(ExperienceComponent);
|
|
|
|
// This delegate is on a component with the same lifetime as this one, so no need to unhook it in
|
|
ExperienceComponent->CallOrRegister_OnExperienceLoaded_HighPriority(FOnLyraExperienceLoaded::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
|
|
{
|
|
Super::EndPlay(EndPlayReason);
|
|
}
|
|
|
|
bool ULyraFrontendStateComponent::ShouldShowLoadingScreen(FString& OutReason) const
|
|
{
|
|
if (bShouldShowLoadingScreen)
|
|
{
|
|
OutReason = TEXT("Frontend Flow Pending...");
|
|
|
|
if (FrontEndFlow.IsValid())
|
|
{
|
|
const TOptional<FString> StepDebugName = FrontEndFlow->GetCurrentStepDebugName();
|
|
if (StepDebugName.IsSet())
|
|
{
|
|
OutReason = StepDebugName.GetValue();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::OnExperienceLoaded(const ULyraExperienceDefinition* Experience)
|
|
{
|
|
FControlFlow& Flow = FControlFlowStatics::Create(this, TEXT("FrontendFlow"))
|
|
.QueueStep(TEXT("Wait For User Initialization"), this, &ThisClass::FlowStep_WaitForUserInitialization)
|
|
.QueueStep(TEXT("Try Show Press Start Screen"), this, &ThisClass::FlowStep_TryShowPressStartScreen)
|
|
.QueueStep(TEXT("Try Show Main Screen"), this, &ThisClass::FlowStep_TryShowMainScreen);
|
|
|
|
Flow.ExecuteFlow();
|
|
|
|
FrontEndFlow = Flow.AsShared();
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::FlowStep_WaitForUserInitialization(FControlFlowNodeRef SubFlow)
|
|
{
|
|
// If this was a hard disconnect, explicitly destroy all user and session state
|
|
// TODO: Refactor the engine disconnect flow so it is more explicit about why it happened
|
|
bool bWasHardDisconnect = false;
|
|
AGameModeBase* GameMode = GetWorld()->GetAuthGameMode<AGameModeBase>();
|
|
UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);
|
|
|
|
if (ensure(GameMode) && UGameplayStatics::HasOption(GameMode->OptionsString, TEXT("closed")))
|
|
{
|
|
bWasHardDisconnect = true;
|
|
}
|
|
|
|
// Only reset users on hard disconnect
|
|
UCommonUserSubsystem* UserSubsystem = GameInstance->GetSubsystem<UCommonUserSubsystem>();
|
|
if (ensure(UserSubsystem) && bWasHardDisconnect)
|
|
{
|
|
UserSubsystem->ResetUserState();
|
|
}
|
|
|
|
// Always reset sessions
|
|
UCommonSessionSubsystem* SessionSubsystem = GameInstance->GetSubsystem<UCommonSessionSubsystem>();
|
|
if (ensure(SessionSubsystem))
|
|
{
|
|
SessionSubsystem->CleanUpSessions();
|
|
}
|
|
|
|
SubFlow->ContinueFlow();
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::FlowStep_TryShowPressStartScreen(FControlFlowNodeRef SubFlow)
|
|
{
|
|
const UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);
|
|
UCommonUserSubsystem* UserSubsystem = GameInstance->GetSubsystem<UCommonUserSubsystem>();
|
|
|
|
// Check to see if the first player is already logged in, if they are, we can skip the press start screen.
|
|
if (const UCommonUserInfo* FirstUser = UserSubsystem->GetUserInfoForLocalPlayerIndex(0))
|
|
{
|
|
if (FirstUser->InitializationState == ECommonUserInitializationState::LoggedInLocalOnly ||
|
|
FirstUser->InitializationState == ECommonUserInitializationState::LoggedInOnline)
|
|
{
|
|
SubFlow->ContinueFlow();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Check to see if the platform actually requires a 'Press Start' screen. This is only
|
|
// required on platforms where there can be multiple online users where depending on what player's
|
|
// controller presses 'Start' establishes the player to actually login to the game with.
|
|
if (!UserSubsystem->ShouldWaitForStartInput())
|
|
{
|
|
// Start the auto login process, this should finish quickly
|
|
InProgressPressStartScreen = SubFlow;
|
|
UserSubsystem->OnUserInitializeComplete.AddDynamic(this, &ULyraFrontendStateComponent::OnUserInitialized);
|
|
UserSubsystem->TryToInitializeForLocalPlay(0, 0, false);
|
|
|
|
return;
|
|
}
|
|
|
|
// Add the Press Start screen, move to the next flow when it deactivates.
|
|
if (UPrimaryGameLayout* RootLayout = UPrimaryGameLayout::GetPrimaryGameLayoutForPrimaryPlayer(this))
|
|
{
|
|
constexpr bool bSuspendInputUntilComplete = true;
|
|
RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(FrontendTags::TAG_UI_LAYER_MENU, bSuspendInputUntilComplete, PressStartScreenClass,
|
|
[this, SubFlow](EAsyncWidgetLayerState State, UCommonActivatableWidget* Screen) {
|
|
switch (State)
|
|
{
|
|
case EAsyncWidgetLayerState::AfterPush:
|
|
bShouldShowLoadingScreen = false;
|
|
Screen->OnDeactivated().AddWeakLambda(this, [this, SubFlow]() {
|
|
SubFlow->ContinueFlow();
|
|
});
|
|
break;
|
|
case EAsyncWidgetLayerState::Canceled:
|
|
bShouldShowLoadingScreen = false;
|
|
SubFlow->ContinueFlow();
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::OnUserInitialized(const UCommonUserInfo* UserInfo, bool bSuccess, FText Error, ECommonUserPrivilege RequestedPrivilege, ECommonUserOnlineContext OnlineContext)
|
|
{
|
|
FControlFlowNodePtr FlowToContinue = InProgressPressStartScreen;
|
|
UGameInstance* GameInstance = UGameplayStatics::GetGameInstance(this);
|
|
UCommonUserSubsystem* UserSubsystem = GameInstance->GetSubsystem<UCommonUserSubsystem>();
|
|
|
|
if (ensure(FlowToContinue.IsValid() && UserSubsystem))
|
|
{
|
|
UserSubsystem->OnUserInitializeComplete.RemoveDynamic(this, &ULyraFrontendStateComponent::OnUserInitialized);
|
|
InProgressPressStartScreen.Reset();
|
|
|
|
if (bSuccess)
|
|
{
|
|
// On success continue flow normally
|
|
FlowToContinue->ContinueFlow();
|
|
}
|
|
else
|
|
{
|
|
// TODO: Just continue for now, could go to some sort of error screen
|
|
FlowToContinue->ContinueFlow();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ULyraFrontendStateComponent::FlowStep_TryShowMainScreen(FControlFlowNodeRef SubFlow)
|
|
{
|
|
if (UPrimaryGameLayout* RootLayout = UPrimaryGameLayout::GetPrimaryGameLayoutForPrimaryPlayer(this))
|
|
{
|
|
constexpr bool bSuspendInputUntilComplete = true;
|
|
RootLayout->PushWidgetToLayerStackAsync<UCommonActivatableWidget>(FrontendTags::TAG_UI_LAYER_MENU, bSuspendInputUntilComplete, MainScreenClass,
|
|
[this, SubFlow](EAsyncWidgetLayerState State, UCommonActivatableWidget* Screen) {
|
|
switch (State)
|
|
{
|
|
case EAsyncWidgetLayerState::AfterPush:
|
|
bShouldShowLoadingScreen = false;
|
|
SubFlow->ContinueFlow();
|
|
return;
|
|
case EAsyncWidgetLayerState::Canceled:
|
|
bShouldShowLoadingScreen = false;
|
|
SubFlow->ContinueFlow();
|
|
return;
|
|
}
|
|
});
|
|
}
|
|
}
|