534 lines
18 KiB
C++
534 lines
18 KiB
C++
// © 2024 Long Ly. All rights reserved. Any unauthorized use, reproduction, or distribution of this trademark is strictly prohibited and may result in legal action.
|
|
|
|
|
|
#include "GameModes/OLSGameMode.h"
|
|
|
|
#include "CommonUserSubsystem.h"
|
|
#include "GameMapsSettings.h"
|
|
#include "Characters/OLSCharacter.h"
|
|
#include "DataAssets/OLSExperienceDefinitionDataAsset.h"
|
|
#include "DataAssets/OLSPawnDataAsset.h"
|
|
#include "GameModes/OLSExperienceManagerComponent.h"
|
|
#include "GameModes/OLSGameState.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
#include "Player/OLSPlayerController.h"
|
|
#include "Player/OLSPlayerState.h"
|
|
#include "Systems/OLSAssetManager.h"
|
|
#include "Systems/OLSGameSession.h"
|
|
#include "UI/OLSHUD.h"
|
|
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(OLSGameMode)
|
|
|
|
AOLSGameMode::AOLSGameMode(const FObjectInitializer& objectInitializer) : Super(objectInitializer)
|
|
{
|
|
GameStateClass = AOLSGameState::StaticClass();
|
|
GameSessionClass = AOLSGameSession::StaticClass();
|
|
PlayerControllerClass = AOLSPlayerController::StaticClass();
|
|
// ReplaySpectatorPlayerControllerClass = ALyraReplayPlayerController::StaticClass();
|
|
PlayerStateClass = AOLSPlayerState::StaticClass();
|
|
DefaultPawnClass = AOLSCharacter::StaticClass();
|
|
HUDClass = AOLSHUD::StaticClass();
|
|
}
|
|
|
|
const UOLSPawnDataAsset* AOLSGameMode::GetPawnDataForController(const AController* controller) const
|
|
{
|
|
// See if pawn data is already set on the player state
|
|
if (controller)
|
|
{
|
|
if (const AOLSPlayerState* playerState = controller->GetPlayerState<AOLSPlayerState>())
|
|
{
|
|
if (const UOLSPawnDataAsset* pawnData = playerState->GetPawnData<UOLSPawnDataAsset>())
|
|
{
|
|
return pawnData;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If not, fall back to the the default for the current experience
|
|
check(GameState);
|
|
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
|
check(experienceComponent);
|
|
|
|
if (experienceComponent->IsExperienceLoaded())
|
|
{
|
|
const UOLSExperienceDefinitionDataAsset* experienceDataAsset = experienceComponent->GetCurrentExperienceChecked();
|
|
if (experienceDataAsset->DefaultPawnData)
|
|
{
|
|
return experienceDataAsset->DefaultPawnData;
|
|
}
|
|
|
|
// Experience is loaded and there's still no pawn data, fall back to the default for now
|
|
return UOLSAssetManager::Get().GetDefaultPawnData();
|
|
}
|
|
|
|
// Experience not loaded yet, so there is no pawn data to be had
|
|
return nullptr;
|
|
}
|
|
|
|
void AOLSGameMode::InitGame(const FString& MapName, const FString& Options, FString& ErrorMessage)
|
|
{
|
|
Super::InitGame(MapName, Options, ErrorMessage);
|
|
|
|
// Wait for the next frame to give time to initialize startup settings
|
|
GetWorld()->GetTimerManager().SetTimerForNextTick(this, &ThisClass::HandleMatchAssignmentIfNotExpectingOne);
|
|
}
|
|
|
|
UClass* AOLSGameMode::GetDefaultPawnClassForController_Implementation(AController* controller)
|
|
{
|
|
if (const UOLSPawnDataAsset* pawnData = GetPawnDataForController(controller))
|
|
{
|
|
if (pawnData->PawnClass)
|
|
{
|
|
return pawnData->PawnClass;
|
|
}
|
|
}
|
|
|
|
return Super::GetDefaultPawnClassForController_Implementation(controller);
|
|
}
|
|
|
|
APawn* AOLSGameMode::SpawnDefaultPawnAtTransform_Implementation(
|
|
AController* newPlayer,
|
|
const FTransform& spawnTransform)
|
|
{
|
|
FActorSpawnParameters spawnInfo;
|
|
spawnInfo.Instigator = GetInstigator();
|
|
spawnInfo.ObjectFlags |= RF_Transient; // Never save the default player pawns into a map.
|
|
spawnInfo.bDeferConstruction = true;
|
|
|
|
if (UClass* pawnClass = GetDefaultPawnClassForController(newPlayer))
|
|
{
|
|
if (APawn* spawnedPawn = GetWorld()->SpawnActor<APawn>(pawnClass, spawnTransform, spawnInfo))
|
|
{
|
|
// @TODO: Implement this as well as implement UOLSawnExtensionComponent
|
|
// if (UOLSawnExtensionComponent* PawnExtComp = UOLSawnExtensionComponent::FindPawnExtensionComponent(spawnedPawn))
|
|
// {
|
|
// if (const ULyraPawnData* PawnData = GetPawnDataForController(newPlayer))
|
|
// {
|
|
// PawnExtComp->SetPawnData(PawnData);
|
|
// }
|
|
// else
|
|
// { // @TODO: Replace this with out custom log.
|
|
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to set PawnData on the spawned pawn [%s]."), *GetNameSafe(spawnedPawn }
|
|
// }
|
|
|
|
spawnedPawn->FinishSpawning(spawnTransform);
|
|
|
|
return spawnedPawn;
|
|
}
|
|
else
|
|
{
|
|
// @TODO: Replace this with out custom log.
|
|
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn of class [%s] at [%s]."), *GetNameSafe(pawnClass), *spawnTransform.ToHumanReadableString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// @TODO: Replace this with out custom log.
|
|
// UE_LOG(LogLyra, Error, TEXT("Game mode was unable to spawn Pawn due to NULL pawn class."));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool AOLSGameMode::ShouldSpawnAtStartSpot(AController* Player)
|
|
{
|
|
// We never want to use the start spot, always use the spawn management component.
|
|
return false;
|
|
}
|
|
|
|
void AOLSGameMode::HandleStartingNewPlayer_Implementation(APlayerController* newPlayer)
|
|
{
|
|
// Delay starting new players until the experience has been loaded
|
|
// (players who log in prior to that will be started by OnExperienceLoaded)
|
|
if (IsExperienceLoaded())
|
|
{
|
|
Super::HandleStartingNewPlayer_Implementation(newPlayer);
|
|
}
|
|
}
|
|
|
|
AActor* AOLSGameMode::ChoosePlayerStart_Implementation(AController* player)
|
|
{
|
|
// @TODO: Implement this as well as implement UOLSPlayerSpawningManagerComponent
|
|
// if (UOLSPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<ULyraPlayerSpawningManagerComponent>())
|
|
// {
|
|
// return PlayerSpawningComponent->ChoosePlayerStart(player);
|
|
// }
|
|
|
|
return Super::ChoosePlayerStart_Implementation(player);
|
|
}
|
|
|
|
void AOLSGameMode::FinishRestartPlayer(AController* newPlayer, const FRotator& startRotation)
|
|
{
|
|
// @TODO: Implement this as well as implement UOLSPlayerSpawningManagerComponent
|
|
// if (UOLSPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<UOLSPlayerSpawningManagerComponent>())
|
|
// {
|
|
// PlayerSpawningComponent->FinishRestartPlayer(NewPlayer, StartRotation);
|
|
// }
|
|
|
|
Super::FinishRestartPlayer(newPlayer, startRotation);
|
|
}
|
|
|
|
bool AOLSGameMode::PlayerCanRestart_Implementation(APlayerController* player)
|
|
{
|
|
return ControllerCanRestart(player);
|
|
}
|
|
|
|
void AOLSGameMode::InitGameState()
|
|
{
|
|
Super::InitGameState();
|
|
|
|
// Listen for the experience load to complete
|
|
UOLSExperienceManagerComponent* ExperienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
|
check(ExperienceComponent);
|
|
ExperienceComponent->CallOrRegister_OnExperienceLoaded(FOLSExperienceLoadedNativeDelegate::FDelegate::CreateUObject(this, &ThisClass::OnExperienceLoaded));
|
|
|
|
}
|
|
|
|
bool AOLSGameMode::UpdatePlayerStartSpot(AController* player, const FString& portal, FString& outErrorMessage)
|
|
{
|
|
// Do nothing, we'll wait until PostLogin when we try to spawn the player for real.
|
|
// Doing anything right now is no good, systems like team assignment haven't even occurred yet.
|
|
return true;
|
|
}
|
|
|
|
void AOLSGameMode::GenericPlayerInitialization(AController* newPlayer)
|
|
{
|
|
Super::GenericPlayerInitialization(newPlayer);
|
|
|
|
OnGameModePlayerInitialized.Broadcast(this, newPlayer);
|
|
}
|
|
|
|
void AOLSGameMode::FailedToRestartPlayer(AController* newPlayer)
|
|
{
|
|
Super::FailedToRestartPlayer(newPlayer);
|
|
|
|
// If we tried to spawn a pawn and it failed, lets try again *note* check if there's actually a pawn class
|
|
// before we try this forever.
|
|
if (UClass* PawnClass = GetDefaultPawnClassForController(newPlayer))
|
|
{
|
|
if (APlayerController* newPC = Cast<APlayerController>(newPlayer))
|
|
{
|
|
// If it's a player don't loop forever, maybe something changed and they can no longer restart if so stop trying.
|
|
if (PlayerCanRestart(newPC))
|
|
{
|
|
RequestPlayerRestartNextFrame(newPlayer, false);
|
|
}
|
|
else
|
|
{
|
|
// @TODO: Replace this with our custom log.
|
|
// UE_LOG(LogLyra, Verbose, TEXT("FailedToRestartPlayer(%s) and PlayerCanRestart returned false, so we're not going to try again."), *GetPathNameSafe(newPlayer));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RequestPlayerRestartNextFrame(newPlayer, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// @TODO: Replace this with our custom log.
|
|
// UE_LOG(LogLyra, Verbose, TEXT("FailedToRestartPlayer(%s) but there's no pawn class so giving up."), *GetPathNameSafe(newPlayer));
|
|
}
|
|
}
|
|
|
|
void AOLSGameMode::RequestPlayerRestartNextFrame(AController* controller, bool shouldForceReset)
|
|
{
|
|
if (shouldForceReset && controller)
|
|
{
|
|
controller->Reset();
|
|
}
|
|
|
|
if (APlayerController* playerController = Cast<APlayerController>(controller))
|
|
{
|
|
GetWorldTimerManager().SetTimerForNextTick(playerController, &APlayerController::ServerRestartPlayer_Implementation);
|
|
}
|
|
// else if (AOLSPlayerBotController* BotController = Cast<ALyraPlayerBotController>(controller))
|
|
// {
|
|
// GetWorldTimerManager().SetTimerForNextTick(BotController, &ALyraPlayerBotController::ServerRestartController);
|
|
// }
|
|
}
|
|
|
|
bool AOLSGameMode::ControllerCanRestart(AController* controller)
|
|
{
|
|
if (APlayerController* playerController = Cast<APlayerController>(controller))
|
|
{
|
|
if (!Super::PlayerCanRestart_Implementation(playerController))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bot version of Super::PlayerCanRestart_Implementation
|
|
if ((controller == nullptr) || controller->IsPendingKillPending())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// @TODO: Implement this after having PlayerSpawningManagerComponent
|
|
// if (ULyraPlayerSpawningManagerComponent* PlayerSpawningComponent = GameState->FindComponentByClass<ULyraPlayerSpawningManagerComponent>())
|
|
// {
|
|
// return PlayerSpawningComponent->ControllerCanRestart(controller);
|
|
// }
|
|
|
|
return true;
|
|
}
|
|
|
|
void AOLSGameMode::OnExperienceLoaded(const UOLSExperienceDefinitionDataAsset* currentExperience)
|
|
{
|
|
// Spawn any players that are already attached
|
|
//@TODO: Here we're handling only *player* controllers, but in GetDefaultPawnClassForController_Implementation we skipped all controllers
|
|
// GetDefaultPawnClassForController_Implementation might only be getting called for players anyways
|
|
for (FConstPlayerControllerIterator iterator = GetWorld()->GetPlayerControllerIterator(); iterator; ++iterator)
|
|
{
|
|
APlayerController* playerController = Cast<APlayerController>(*iterator);
|
|
if (playerController && playerController->GetPawn())
|
|
{
|
|
if (PlayerCanRestart(playerController))
|
|
{
|
|
RestartPlayer(playerController);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AOLSGameMode::IsExperienceLoaded() const
|
|
{
|
|
check(GameState);
|
|
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<
|
|
UOLSExperienceManagerComponent>();
|
|
check(experienceComponent);
|
|
|
|
return experienceComponent->IsExperienceLoaded();
|
|
}
|
|
|
|
void AOLSGameMode::OnMatchAssignmentGiven(FPrimaryAssetId experienceId, const FString& experienceIdSource)
|
|
{
|
|
if (experienceId.IsValid())
|
|
{
|
|
// @TODO: Replace this with our custom.
|
|
// UE_LOG(LogLyraExperience, Log, TEXT("Identified experience %s (Source: %s)"), *ExperienceId.ToString(), *ExperienceIdSource);
|
|
|
|
UOLSExperienceManagerComponent* experienceComponent = GameState->FindComponentByClass<UOLSExperienceManagerComponent>();
|
|
check(experienceComponent);
|
|
experienceComponent->SetCurrentExperience(experienceId);
|
|
}
|
|
else
|
|
{
|
|
// @TODO: Replace this with our custom.
|
|
// UE_LOG(LogLyraExperience, Error, TEXT("Failed to identify experience, loading screen will stay up forever"));
|
|
}
|
|
}
|
|
|
|
void AOLSGameMode::HandleMatchAssignmentIfNotExpectingOne()
|
|
{
|
|
FPrimaryAssetId experienceId;
|
|
FString experienceIdSource;
|
|
|
|
// Precedence order (highest wins)
|
|
// - Matchmaking assignment (if present)
|
|
// - URL Options override
|
|
// - Developer Settings (PIE only)
|
|
// - Command Line override
|
|
// - World Settings
|
|
// - Dedicated server
|
|
// - Default experience
|
|
|
|
UWorld* World = GetWorld();
|
|
|
|
if (!experienceId.IsValid() && UGameplayStatics::HasOption(OptionsString, TEXT("Experience")))
|
|
{
|
|
const FString ExperienceFromOptions = UGameplayStatics::ParseOption(OptionsString, TEXT("Experience"));
|
|
experienceId = FPrimaryAssetId(FPrimaryAssetType(UOLSExperienceDefinitionDataAsset::StaticClass()->GetFName()), FName(*ExperienceFromOptions));
|
|
experienceIdSource = TEXT("OptionsString");
|
|
}
|
|
|
|
if (!experienceId.IsValid() && World->IsPlayInEditor())
|
|
{
|
|
// @TODO: Implement this when UOLSDeveloperSettings is implemented.
|
|
// experienceId = GetDefault<UOLSDeveloperSettings>()->ExperienceOverride;
|
|
experienceIdSource = TEXT("DeveloperSettings");
|
|
}
|
|
|
|
// see if the command line wants to set the experience
|
|
if (!experienceId.IsValid())
|
|
{
|
|
FString ExperienceFromCommandLine;
|
|
if (FParse::Value(FCommandLine::Get(), TEXT("Experience="), ExperienceFromCommandLine))
|
|
{
|
|
experienceId = FPrimaryAssetId::ParseTypeAndName(ExperienceFromCommandLine);
|
|
if (!experienceId.PrimaryAssetType.IsValid())
|
|
{
|
|
experienceId = FPrimaryAssetId(FPrimaryAssetType(UOLSExperienceDefinitionDataAsset::StaticClass()->GetFName()), FName(*ExperienceFromCommandLine));
|
|
}
|
|
experienceIdSource = TEXT("CommandLine");
|
|
}
|
|
}
|
|
|
|
// see if the world settings has a default experience
|
|
if (!experienceId.IsValid())
|
|
{
|
|
// @TODO: Implement this when AOLSWorldSettings is implemented.
|
|
// if (AOLSWorldSettings* TypedWorldSettings = Cast<ALyraWorldSettings>(GetWorldSettings()))
|
|
// {
|
|
// experienceId = TypedWorldSettings->GetDefaultGameplayExperience();
|
|
// experienceIdSource = TEXT("WorldSettings");
|
|
// }
|
|
}
|
|
|
|
UOLSAssetManager& assetManager = UOLSAssetManager::Get();
|
|
FAssetData dummy;
|
|
if (experienceId.IsValid() && !assetManager.GetPrimaryAssetData(experienceId, /*out*/ dummy))
|
|
{
|
|
// @TODO: Replace this with our custom.
|
|
// UE_LOG(LogLyraExperience, Error, TEXT("EXPERIENCE: Wanted to use %s but couldn't find it, falling back to the default)"), *experienceIding());
|
|
experienceId = FPrimaryAssetId();
|
|
}
|
|
|
|
// Final fallback to the default experience
|
|
if (!experienceId.IsValid())
|
|
{
|
|
if (TryDedicatedServerLogin())
|
|
{
|
|
// This will start to host as a dedicated server
|
|
return;
|
|
}
|
|
|
|
//@TODO: Pull this from a config setting or something
|
|
experienceId = FPrimaryAssetId(FPrimaryAssetType("LyraExperienceDefinition"), FName("B_LyraDefaultExperience"));
|
|
experienceIdSource = TEXT("Default");
|
|
}
|
|
|
|
OnMatchAssignmentGiven(experienceId, experienceIdSource);
|
|
}
|
|
|
|
bool AOLSGameMode::TryDedicatedServerLogin()
|
|
{
|
|
// Some basic code to register as an active dedicated server, this would be heavily modified by the game
|
|
// FString defaultMap = UGameMapsSettings::GetGameDefaultMap();
|
|
// UWorld* world = GetWorld();
|
|
// UGameInstance* gameInstance = GetGameInstance();
|
|
// if (gameInstance && world && world->GetNetMode() == NM_DedicatedServer && world->URL.Map == defaultMap)
|
|
// {
|
|
// // Only register if this is the default map on a dedicated server
|
|
// UCommonUserSubsystem* userSubsystem = gameInstance->GetSubsystem<UCommonUserSubsystem>();
|
|
//
|
|
// // Dedicated servers may need to do an online login
|
|
// userSubsystem->OnUserInitializeComplete.AddDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer);
|
|
//
|
|
// // There are no local users on dedicated server, but index 0 means the default platform user which is handled by the online login code
|
|
// if (!userSubsystem->TryToLoginForOnlinePlay(0))
|
|
// {
|
|
// OnUserInitializedForDedicatedServer(nullptr, false, FText(), ECommonUserPrivilege::CanPlayOnline, ECommonUserOnlineContext::Default);
|
|
// }
|
|
//
|
|
// return true;
|
|
// }
|
|
|
|
return false;
|
|
}
|
|
|
|
void AOLSGameMode::HostDedicatedServerMatch(ECommonSessionOnlineMode onlineMode)
|
|
{
|
|
// FPrimaryAssetType UserExperienceType = ULyraUserFacingExperienceDefinition::StaticClass()->GetFName();
|
|
//
|
|
// // Figure out what UserFacingExperience to load
|
|
// FPrimaryAssetId UserExperienceId;
|
|
// FString UserExperienceFromCommandLine;
|
|
// if (FParse::Value(FCommandLine::Get(), TEXT("UserExperience="), UserExperienceFromCommandLine) ||
|
|
// FParse::Value(FCommandLine::Get(), TEXT("Playlist="), UserExperienceFromCommandLine))
|
|
// {
|
|
// UserExperienceId = FPrimaryAssetId::ParseTypeAndName(UserExperienceFromCommandLine);
|
|
// if (!UserExperienceId.PrimaryAssetType.IsValid())
|
|
// {
|
|
// UserExperienceId = FPrimaryAssetId(FPrimaryAssetType(UserExperienceType), FName(*UserExperienceFromCommandLine));
|
|
// }
|
|
// }
|
|
//
|
|
// // Search for the matching experience, it's fine to force load them because we're in dedicated server startup
|
|
// ULyraAssetManager& AssetManager = ULyraAssetManager::Get();
|
|
// TSharedPtr<FStreamableHandle> Handle = AssetManager.LoadPrimaryAssetsWithType(UserExperienceType);
|
|
// if (ensure(Handle.IsValid()))
|
|
// {
|
|
// Handle->WaitUntilComplete();
|
|
// }
|
|
//
|
|
// TArray<UObject*> UserExperiences;
|
|
// AssetManager.GetPrimaryAssetObjectList(UserExperienceType, UserExperiences);
|
|
// ULyraUserFacingExperienceDefinition* FoundExperience = nullptr;
|
|
// ULyraUserFacingExperienceDefinition* DefaultExperience = nullptr;
|
|
//
|
|
// for (UObject* Object : UserExperiences)
|
|
// {
|
|
// ULyraUserFacingExperienceDefinition* UserExperience = Cast<ULyraUserFacingExperienceDefinition>(Object);
|
|
// if (ensure(UserExperience))
|
|
// {
|
|
// if (UserExperience->GetPrimaryAssetId() == UserExperienceId)
|
|
// {
|
|
// FoundExperience = UserExperience;
|
|
// break;
|
|
// }
|
|
//
|
|
// if (UserExperience->bIsDefaultExperience && DefaultExperience == nullptr)
|
|
// {
|
|
// DefaultExperience = UserExperience;
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (FoundExperience == nullptr)
|
|
// {
|
|
// FoundExperience = DefaultExperience;
|
|
// }
|
|
//
|
|
// UGameInstance* GameInstance = GetGameInstance();
|
|
// if (ensure(FoundExperience && GameInstance))
|
|
// {
|
|
// // Actually host the game
|
|
// UCommonSession_HostSessionRequest* HostRequest = FoundExperience->CreateHostingRequest(this);
|
|
// if (ensure(HostRequest))
|
|
// {
|
|
// HostRequest->OnlineMode = OnlineMode;
|
|
//
|
|
// // TODO override other parameters?
|
|
//
|
|
// UCommonSessionSubsystem* SessionSubsystem = GameInstance->GetSubsystem<UCommonSessionSubsystem>();
|
|
// SessionSubsystem->HostSession(nullptr, HostRequest);
|
|
//
|
|
// // This will handle the map travel
|
|
// }
|
|
// }
|
|
}
|
|
|
|
void AOLSGameMode::OnUserInitializedForDedicatedServer(
|
|
const UCommonUserInfo* userInfo,
|
|
bool isSuccess,
|
|
FText error,
|
|
ECommonUserPrivilege requestedPrivilege,
|
|
ECommonUserOnlineContext onlineContext)
|
|
{
|
|
UGameInstance* gameInstance = GetGameInstance();
|
|
if (gameInstance)
|
|
{
|
|
// Unbind
|
|
UCommonUserSubsystem* UserSubsystem = gameInstance->GetSubsystem<UCommonUserSubsystem>();
|
|
UserSubsystem->OnUserInitializeComplete.RemoveDynamic(this, &ThisClass::OnUserInitializedForDedicatedServer);
|
|
|
|
// Dedicated servers do not require user login, but some online subsystems may expect it
|
|
if (isSuccess && ensure(userInfo))
|
|
{
|
|
// @TODO: replace this with our custom.
|
|
// UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login succeeded for id %s, starting online server"), *UserInfo->GetNetId().ToString());
|
|
}
|
|
else
|
|
{
|
|
// @TODO: replace this with our custom.
|
|
// UE_LOG(LogLyraExperience, Log, TEXT("Dedicated server user login unsuccessful, starting online server as login is not required"));
|
|
}
|
|
|
|
HostDedicatedServerMatch(ECommonSessionOnlineMode::Online);
|
|
}
|
|
}
|