#include "GasaGameMode.h" #include "GasaGameInstance.h" #include "GasaGameState.h" #include "GasaPlayerController.h" #include "GasaPlayerState.h" #include "Engine/Player.h" #include "GameFramework/GameSession.h" #include "GameFramework/GameState.h" #include "GameFramework/PlayerState.h" #include "OnlineSubsystem.h" using namespace Gasa; #pragma region Game Framework void AGasaGameMode::OnGameFrameworkInitialized() { NetLog("OnGameFrameworkInitialized"); BP_OnGameFrameworkInitialized(); if (MatchState == MatchState::WaitingToStart && ReadyToStartMatch()) StartMatch(); } void AGasaGameMode::OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC) { NetLog("OwningClient_OnGameFrameworkInitialized"); HandleStartingNewPlayer(PC); } #pragma endregion Game Framework #pragma region GameMode void AGasaGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) { Super::HandleStartingNewPlayer_Implementation(NewPlayer); if (NewPlayer) NewPlayer->bBlockInput = false; } bool AGasaGameMode::ReadyToStartMatch_Implementation() { if ( ! GetGameInstance()->IsGameFrameworkInitialized()) { return false; } // Super::ReadyToStartMatch(); { // If bDelayed Start is set, wait for a manual match start if (bDelayedStart) { return false; } // By default start when we have > 0 players if (GetMatchState() == MatchState::WaitingToStart) { if (NumPlayers + NumBots > 0) { return true; } } return false; } } #pragma endregion GameMode #pragma region GameModeBase void AGasaGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); NetLog("EndPlay"); } void AGasaGameMode::GenericPlayerInitialization(AController* C) { NetLog("GenericPlayerInitialization: " + C->GetName()); // AGameMode::GenericPlayerInitialization(C); { APlayerController* PC = Cast(C); if (PC != nullptr) { // Moved to: void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn) // InitializeHUDForPlayer(Cast(C)); // Notify the game that we can now be muted and mute others UpdateGameplayMuteList(PC); if (GameSession != nullptr) { // Tell the player to enable voice by default or use the push to talk method PC->ClientEnableNetworkVoice(!GameSession->RequiresPushToTalk()); } ReplicateStreamingStatus(PC); bool HidePlayer = false, HideHUD = false, DisableMovement = false, DisableTurning = false; // Check to see if we should start in cinematic mode (matinee movie capture) if (ShouldStartInCinematicMode(PC, HidePlayer, HideHUD, DisableMovement, DisableTurning)) { PC->SetCinematicMode(true, HidePlayer, HideHUD, DisableMovement, DisableTurning); } } } } TSubclassOf AGasaGameMode::GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController) { NetLog("GetPlayerControllerClassToSpawnForSeamlessTravel: " + PreviousPlayerController->GetName()); return Super::GetPlayerControllerClassToSpawnForSeamlessTravel(PreviousPlayerController); } void AGasaGameMode::HandleSeamlessTravelPlayer(AController*& Controller) { NetLog("HandleSeamlessTravelPlayer: " + Controller->GetName()); // Super::HandleSeamlessTravelPlayer( C ); UE_LOG(LogGameMode, Log, TEXT(">> GameMode::HandleSeamlessTravelPlayer: %s "), * Controller->GetName()); APlayerController* PC = Cast(Controller); UClass* PCClassToSpawn = GetPlayerControllerClassToSpawnForSeamlessTravel(PC); // FUniqueNetIdRepl StoredNativePlatformUniqueNetId = C->GetPlayerState()->NativePlatformUniqueNetId; if (PC && PC->GetClass() != PCClassToSpawn) { if (PC->Player != nullptr) { // We need to spawn a new PlayerController to replace the old one APlayerController* const NewPC = SpawnPlayerControllerCommon(PC->IsLocalPlayerController() ? ROLE_SimulatedProxy : ROLE_AutonomousProxy, PC->GetFocalLocation(), PC->GetControlRotation(), PCClassToSpawn); if (NewPC == nullptr) { NetLog(FString::Printf( TEXT("Failed to spawn new PlayerController for %s (old class %s)"), *PC->GetHumanReadableName(), *PC->GetClass()->GetName()) , ELogV::Warning ); PC->Destroy(); return; } else { PC->SeamlessTravelTo(NewPC); NewPC->SeamlessTravelFrom(PC); SwapPlayerControllers(PC, NewPC); PC = NewPC; Controller = NewPC; } } else { PC->Destroy(); } } else { // clear out data that was only for the previous game Controller->PlayerState->Reset(); // create a new PlayerState and copy over info; this is necessary because the old GameMode may have used a different PlayerState class APlayerState* OldPlayerState = Controller->PlayerState; Controller->InitPlayerState(); OldPlayerState->SeamlessTravelTo( Controller->PlayerState); // we don"t need the old PlayerState anymore //@fixme: need a way to replace PlayerStates that doesn't cause incorrect "player left the game"/"player entered the game" messages OldPlayerState->Destroy(); } InitSeamlessTravelPlayer(Controller); // Initialize hud and other player details, shared with PostLogin GenericPlayerInitialization(Controller); if (AGasaGameState* GS = GetGameState()) { #if 0 UGasaGameInstance* GI = GetGameInstance(); int32 NumConnections = GI->SessionSettings.bPublicGame ? GI->SessionSettings.PublicConnections : GI->SessionSettings.PrivateConnections; if (GS->OnlinePlayers.Num() < NumConnections) GS->OnlinePlayers.Init( nullptr, NumConnections ); for (AGasaPlayerState* & PS : GS->OnlinePlayers) { if (PS == nullptr) { PS = C->GetPlayerState(); PS->SgID = GS->OnlinePlayers.Find( PS ); break; } } #endif } // Controller->GetPlayerState()->NativePlatformUniqueNetId = StoredNativePlatformUniqueNetId; NetLog(FString::Printf(TEXT("HandleSeamlessTravelPlayer: %s"), * Controller->GetName()) ); #if 0 if (PC) PC->Event_NetOwner_OnGameplayFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized); #endif } void AGasaGameMode::InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer) { // Super::InitializeHUDForPlayer_Implementation(NewPlayer); NewPlayer->ClientSetHUD(HUDClass); } void AGasaGameMode::InitSeamlessTravelPlayer(AController* NewController) { if (NewController) NewController->bBlockInput = true; // GameMode::InitSeamlessTravelPlayer(NewController); { // AGameModeBase::InitSeamlessTravelPlayer(NewController); { APlayerController* NewPC = Cast(NewController); FString ErrorMessage; if (!UpdatePlayerStartSpot(NewController, TEXT(""), ErrorMessage)) NetLog(FString::Printf( TEXT("InitSeamlessTravelPlayer: %s"), *ErrorMessage), ELogV::Warning); if (NewPC != nullptr) { NewPC->PostSeamlessTravel(); if (MustSpectate(NewPC)) { NewPC->StartSpectatingOnly(); } else { NewPC->bPlayerIsWaiting = true; NewPC->ChangeState(NAME_Spectating); NewPC->ClientGotoState(NAME_Spectating); } } } APlayerController* NewPC = Cast(NewController); if (NewPC != nullptr) { SetSeamlessTravelViewTarget(NewPC); if (!MustSpectate(NewPC)) { NumPlayers++; NumTravellingPlayers--; } } else { NumBots++; } } NetLog("InitSeamlessTravelPlayer: " + NewController->GetName()); } void AGasaGameMode::HandleMatchAborted() { Super::HandleMatchAborted(); NetLog("HandleMatchAborted"); } void AGasaGameMode::Logout(AController* Exiting) { Super::Logout(Exiting); Event_OnLogout.Broadcast(Cast(Exiting)); NetLog("User Logged out: " + Exiting->GetName()); if (AGasaGameState* GS = Cast(GetWorld()->GetGameState())) { #if 0 int32 Index = GS->OnlinePlayers.Find(Exiting->GetPlayerState()); if (Index == INDEX_NONE) { NetLog("Could not find exiting player state in online players!", ELogV::Error); return; } GS->OnlinePlayers[Index] = nullptr; #endif #if 0 IOnlineSessionPtr const SessionInterface = Online::GetSessionInterface(GetWorld()); if ( ! SessionInterface.IsValid()) { NetLog("DestoryCurrentSession: Could not get the session interface.", ELogV::Warning); return; } #endif } } void AGasaGameMode::PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) { Super::PreLogin(Options, Address, UniqueId, ErrorMessage); // TODO(Ed): Refuse players if the server is full. } void AGasaGameMode::PostLogin(APlayerController* NewPlayer) { // AGameMode::PostLogin(NewPlayer); { UWorld* World = GetWorld(); // Update player count if (MustSpectate(NewPlayer)) NumSpectators++; else if (World->IsInSeamlessTravel() || NewPlayer->HasClientLoadedCurrentWorld()) NumPlayers++; else NumTravellingPlayers++; // Save network address for re-associating with reconnecting player, after stripping out port number FString Address = NewPlayer->GetPlayerNetworkAddress(); int32 Pos = Address.Find(TEXT(":"), ESearchCase::CaseSensitive); NewPlayer->PlayerState->SavedNetworkAddress = (Pos > 0) ? Address.Left(Pos) : Address; // Check if this player is reconnecting and already has PlayerState FindInactivePlayer(NewPlayer); } // AGameModeBase::PostLogin(NewPlayer) { // Runs shared initialization that can happen during seamless travel as well GenericPlayerInitialization(NewPlayer); // Perform initialization that only happens on initially joining a server NewPlayer->ClientCapBandwidth(NewPlayer->Player->CurrentNetSpeed); if (MustSpectate(NewPlayer)) { NewPlayer->ClientGotoState(NAME_Spectating); } else { // If NewPlayer is not only a spectator and has a valid ID, add him as a user to the replay. FUniqueNetIdRepl const& NewPlayerStateUniqueId = NewPlayer->PlayerState->GetUniqueId(); if (NewPlayerStateUniqueId.IsValid()) { GetGameInstance()->AddUserToReplay(NewPlayerStateUniqueId.ToString()); } } if (GameSession) { GameSession->PostLogin(NewPlayer); } } if (AGasaGameState* GS = GetGameState()) { UGasaGameInstance* GI = GetGameInstance(); // int32 numconnections = gi->sessionsettings.bpublicgame // ? GI->SessionSettings.PublicConnections : GI->SessionSettings.PrivateConnections; #if 0 if (GS->OnlinePlayers.Num() < NumConnections) { GS->OnlinePlayers.Init( nullptr, NumConnections ); } for (AGasaPlayerState* & PS : GS->OnlinePlayers) { if (PS == nullptr) { PS = NewPlayer->GetPlayerState(); PS->GasaID = GS->OnlinePlayers.Find( PS ); break; } } #endif } // cont. AGameModeBase::PostLogin(NewPlayer) { // Notify Blueprints that a new player has logged in. Calling it here, because this is the first time that the PlayerController can take RPCs DispatchPostLogin(NewPlayer); } AGasaPlayerController* PC = Cast(NewPlayer); // if (PC) // PC->Event_OnNetOwner_GameplayFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized); } void AGasaGameMode::PostSeamlessTravel() { Super::PostSeamlessTravel(); NetLog("PostSeamlessTravel"); } void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn) { InitializeHUDForPlayer(Cast(PlayerPawn->GetController())); // Super::SetPlayerDefaults(PlayerPawn); { PlayerPawn->SetPlayerDefaults(); } } void AGasaGameMode::SetSeamlessTravelViewTarget(APlayerController* PC) { Super::SetSeamlessTravelViewTarget(PC); NetLog("SetSeamlessTravelViewTarget"); } void AGasaGameMode::StartPlay() { if (MatchState == MatchState::EnteringMap) SetMatchState(MatchState::WaitingToStart); // Start match is deferred until the framework is considered initialized. NetLog("StartPlay"); UGasaGameInstance* GI = GetGameInstance(); GI->Event_OnGameFrameworkInitialized.AddDynamic(this, &ThisClass::OnGameFrameworkInitialized); GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::GameMode); // Not called yet, will wait for initialization of the framework. //Super::StartPlay(); } void AGasaGameMode::StartToLeaveMap() { NetLog("StartToLeaveMap"); Super::StartToLeaveMap(); } void AGasaGameMode::SwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC) { NetLog("SwapPlayerControllers"); NetLog("Old: " + OldPC->GetName()); NetLog("New: " + NewPC->GetName()); Super::SwapPlayerControllers(OldPC, NewPC); } APlayerController* AGasaGameMode::SpawnPlayerControllerCommon(ENetRole InRemoteRole, FVector const& SpawnLocation, FRotator const& SpawnRotation, TSubclassOf InPlayerControllerClass) { NetLog("SpawnPlayerControllerCommon"); return Super::SpawnPlayerControllerCommon(InRemoteRole, SpawnLocation, SpawnRotation, InPlayerControllerClass); } #pragma endregion GameModeBase