WIP: Boostrapping NetSlime
- Just a old name for a set of changes to make the game framework hardened for multiplayer as well as some ease of use functionality.
This commit is contained in:
		| @@ -1,5 +1,84 @@ | ||||
| #include "GasaGameInstance.h" | ||||
|  | ||||
| #include "Engine/NetDriver.h" | ||||
| #include "Engine/World.h" | ||||
| using namespace Gasa; | ||||
|  | ||||
| #pragma region GameFramework | ||||
| // TODO(Ed): Make a NetLog | ||||
|  | ||||
| void UGasaGameInstance::NotifyGameFrameworkClassReady(EGameFrameworkClassFlag ClassReady) | ||||
| { | ||||
| 	switch (ClassReady) | ||||
| 	{ | ||||
| 		case EGameFrameworkClassFlag::GameMode: | ||||
| 			GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::GameMode; | ||||
| 			NetLog("Gameplay Framework class ready: Game State", ELogV::Log, LogGasaNet ); | ||||
| 		break; | ||||
| 		case EGameFrameworkClassFlag::GameState: | ||||
| 			GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::GameState; | ||||
| 			NetLog("Gameplay Framework class ready: Game State", ELogV::Log, LogGasaNet ); | ||||
| 		break; | ||||
| 		case EGameFrameworkClassFlag::PlayerController: | ||||
| 			GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::PlayerController; | ||||
| 			NetLog("Gameplay Framework class ready: Player Controller", ELogV::Log, LogGasaNet); | ||||
| 		break; | ||||
| 		case EGameFrameworkClassFlag::PlayerState: | ||||
| 			GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::PlayerState; | ||||
| 			NetLog("Gameplay Framework class ready: Player State", ELogV::Log, LogGasaNet); | ||||
| 		break; | ||||
| 		case EGameFrameworkClassFlag::Levels: | ||||
| 			GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::Levels; | ||||
| 			NetLog("Gameplay Framework class ready: Levels", ELogV::Log, LogGasaNet); | ||||
| 		break; | ||||
| 	} | ||||
| 	ProcessGameFrameworkState(); | ||||
| } | ||||
|  | ||||
| void UGasaGameInstance::ProcessGameFrameworkState() | ||||
| { | ||||
| 	switch (GameFrameworkState) | ||||
| 	{ | ||||
| 		case EGameFrameworkState::Uninitialized: | ||||
| 		{ | ||||
| 			uint32 InitializedFlags = | ||||
| 				(uint32)EGameFrameworkClassFlag::GameState        | | ||||
| 				(uint32)EGameFrameworkClassFlag::PlayerController | | ||||
| 				(uint32)EGameFrameworkClassFlag::PlayerState      | | ||||
| 				(uint32)EGameFrameworkClassFlag::Levels | ||||
| 			; | ||||
|  | ||||
| 			if (GetWorld()->NetDriver == nullptr || GetWorld()->NetDriver->IsServer()) | ||||
| 			{ | ||||
| 				InitializedFlags |= (uint32)EGameFrameworkClassFlag::GameMode; | ||||
| 			} | ||||
|  | ||||
| 			FString MsgGM = "GameMode        : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::GameMode )) ); | ||||
| 			FString MsgGS = "GameState       : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::GameState )) ); | ||||
| 			FString MsgPC = "PlayerController: " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::PlayerController ) )); | ||||
| 			FString MsgPS = "PlayerState     : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::PlayerState ) )); | ||||
| 			FString MsgL  = "Levels          : " + FString::FromInt( Bitfield_IsSet( GameFrameworkClassesState, scast(uint32, EGameFrameworkClassFlag::Levels ) )); | ||||
|  | ||||
| 			NetLog(MsgGM, ELogV::Log, LogGasaNet); | ||||
| 			NetLog(MsgGS, ELogV::Log, LogGasaNet); | ||||
| 			NetLog(MsgPC, ELogV::Log, LogGasaNet); | ||||
| 			NetLog(MsgPS, ELogV::Log, LogGasaNet); | ||||
| 			NetLog(MsgL,  ELogV::Log, LogGasaNet); | ||||
| 			 | ||||
| 			if (GameFrameworkClassesState == InitializedFlags) | ||||
| 			{ | ||||
| 				GameFrameworkState = EGameFrameworkState::Initialized; | ||||
| 				NetLog("Gameplay Framework initialized"); | ||||
| 				 | ||||
| 				Event_OnGameFrameworkInitialized.Broadcast(); | ||||
| 			} | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| #pragma endregion GameFramework | ||||
|  | ||||
| #pragma region GameInstance | ||||
| void UGasaGameInstance::Init() | ||||
| { | ||||
| 	Super::Init(); | ||||
| @@ -7,5 +86,6 @@ void UGasaGameInstance::Init() | ||||
| 	DevOptionsCache.CachedDevOptions(); | ||||
|  | ||||
| 	using namespace Gasa; | ||||
| 	Log(FString::Printf(TEXT("UObject Size:  %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize )); | ||||
| 	NetLog(FString::Printf(TEXT("UObject Size:  %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize )); | ||||
| } | ||||
| #pragma region GameInstance | ||||
|   | ||||
| @@ -1,10 +1,32 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Networking/GasaNetLibrary.h" | ||||
| #include "GasaDevOptionsCache.h" | ||||
| #include "Engine/Engine.h" | ||||
|  | ||||
| #include "Engine/GameInstance.h" | ||||
| #include "GasaGameInstance.generated.h" | ||||
|  | ||||
| UENUM(BlueprintType) | ||||
| enum class EGameFrameworkClassFlag : uint8 | ||||
| { | ||||
| 	None             = 0 UMETA(Hidden), | ||||
| 	GameMode         = 1 << 0, | ||||
| 	GameState        = 1 << 1, | ||||
| 	PlayerController = 1 << 2, | ||||
| 	PlayerState      = 1 << 3, | ||||
| 	Levels           = 1 << 4 | ||||
| }; | ||||
|  | ||||
| UENUM(BlueprintType) | ||||
| enum class EGameFrameworkState : uint8 | ||||
| { | ||||
| 	Initialized, | ||||
| 	Uninitialized | ||||
| }; | ||||
|  | ||||
| DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameFrameworkInitializedSig); | ||||
|  | ||||
| UCLASS(Blueprintable) | ||||
| class GASA_API UGasaGameInstance : public UGameInstance | ||||
| { | ||||
| @@ -14,6 +36,55 @@ public: | ||||
| 	UPROPERTY(VisibleAnywhere, Category="Dev Cache") | ||||
| 	FGasaDevOptionsCache DevOptionsCache; | ||||
|  | ||||
| #pragma region GameFramework | ||||
| 	UPROPERTY(BlueprintAssignable, Category = "GameFramework") | ||||
| 	FOnGameFrameworkInitializedSig Event_OnGameFrameworkInitialized; | ||||
| 	 | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameFramework") | ||||
| 	EGameFrameworkState GameFrameworkState; | ||||
|  | ||||
| 	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "GameFramework", meta=(Bitmask, BitmaskEnum = EGameFrameworkClassFlag)) | ||||
| 	int32 GameFrameworkClassesState; | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, Category="GameFramework") | ||||
| 	void ClearGameplayFrameworkState() 	{ | ||||
| 		Gasa::Log("Clearing game framework state", EGasaVerbosity::Log, LogGasaNet ); // TODO(Ed): Make a default NetLog | ||||
| 		GameFrameworkClassesState = scast(int32, EGameFrameworkClassFlag::None); | ||||
| 		GameFrameworkState        = EGameFrameworkState::Uninitialized; | ||||
| 	} | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, BlueprintPure, Category = "GameFramework") | ||||
| 	FORCEINLINE bool IsGameFrameworkInitialized() { return GameFrameworkState == EGameFrameworkState::Initialized; } | ||||
| 	 | ||||
| 	UFUNCTION(BlueprintCallable, Category="GameFramework") | ||||
| 	void NotifyGameFrameworkClassReady(EGameFrameworkClassFlag ClassReady); | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, Category = "GameFramework", meta=(BlueprintProtected)) | ||||
| 	void ProcessGameFrameworkState(); | ||||
| #pragma endregion GameFramework | ||||
|  | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
|  | ||||
| #pragma region GameInstance | ||||
| 	void Init() override; | ||||
| #pragma endregion GameInstance | ||||
|   | ||||
| @@ -1,2 +1,454 @@ | ||||
| #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<UGasaGameInstance>()->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<APlayerController>(C); | ||||
| 		if (PC != nullptr) | ||||
| 		{ | ||||
| 			// Moved to: void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn) | ||||
| 			// InitializeHUDForPlayer(Cast<APlayerController>(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<APlayerController> 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<APlayerController>(Controller); | ||||
| 	UClass*            PCClassToSpawn = GetPlayerControllerClassToSpawnForSeamlessTravel(PC); | ||||
| 	// FUniqueNetIdRepl StoredNativePlatformUniqueNetId = C->GetPlayerState<AGasaPlayerState>()->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<AGasaGameState>()) | ||||
| 	{ | ||||
| #if 0 | ||||
| 		UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>(); | ||||
| 		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<ASlipgatePlayerState>(); | ||||
| 				PS->SgID = GS->OnlinePlayers.Find( PS ); | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| 	// Controller->GetPlayerState<AGasaPlayerState>()->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<APlayerController>(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<APlayerController>(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<AGasaPlayerController>(Exiting)); | ||||
| 	NetLog("User Logged out: " + Exiting->GetName()); | ||||
|  | ||||
| 	if (AGasaGameState* GS = Cast<AGasaGameState>(GetWorld()->GetGameState())) | ||||
| 	{ | ||||
| #if 0 | ||||
| 		int32 Index = GS->OnlinePlayers.Find(Exiting->GetPlayerState<AGasaGameState>()); | ||||
| 		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<AGasaGameState>()) | ||||
| 	{ | ||||
| 		UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>(); | ||||
| 		 | ||||
| 		// 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<AGasaPlayerState>(); | ||||
| 				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<AGasaPlayerController>(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<APlayerController>(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<UGasaGameInstance>(); | ||||
| 	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<APlayerController> InPlayerControllerClass) | ||||
| { | ||||
| 	NetLog("SpawnPlayerControllerCommon"); | ||||
| 	return Super::SpawnPlayerControllerCommon(InRemoteRole, SpawnLocation, SpawnRotation, InPlayerControllerClass); | ||||
| } | ||||
| #pragma endregion GameModeBase | ||||
|   | ||||
| @@ -1,28 +1,102 @@ | ||||
| #pragma once | ||||
| #pragma once | ||||
| #include "GameFramework/GameMode.h" | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Networking/GasaNetLibrary_Inlines.h" | ||||
| #include "Engine/Engine.h" | ||||
|  | ||||
| #include "GasaGameMode.generated.h" | ||||
|  | ||||
| UCLASS(Blueprintable) | ||||
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnLogoutSig, AGasaPlayerController*, PC); | ||||
|  | ||||
| UCLASS( Blueprintable ) | ||||
| class GASA_API AGasaGameMode : public AGameMode | ||||
| { | ||||
| 	GENERATED_BODY() | ||||
| public: | ||||
| 	UPROPERTY(EditAnywhere, BlueprintAssignable) | ||||
| 	FOnLogoutSig Event_OnLogout; | ||||
| 	 | ||||
| #pragma region GameFramework | ||||
| 	UFUNCTION() | ||||
| 	void OnGameFrameworkInitialized(); | ||||
| 	 | ||||
| 	UFUNCTION() | ||||
| 	void OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC); | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, meta=(DisplayName = "On Game Framework Initialized")) | ||||
| 	void BP_OnGameFrameworkInitialized(); | ||||
| #pragma endregion GameFramework | ||||
| 	 | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
|  | ||||
| #pragma region GameMode | ||||
| 	void HandleMatchAborted() override; | ||||
| 	void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) override; | ||||
| 	 | ||||
| 	bool ReadyToStartMatch_Implementation() override; | ||||
| #pragma endregion GameMode | ||||
|  | ||||
| #pragma region GameModeBase | ||||
| 	void EndPlay(const EEndPlayReason::Type EndPlayReason) override; | ||||
|  | ||||
| 	void GenericPlayerInitialization(AController* C) override; | ||||
|  | ||||
| 	TSubclassOf<APlayerController> GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController) override; | ||||
|  | ||||
| 	void HandleSeamlessTravelPlayer(AController*& C) override; | ||||
|  | ||||
| 	void InitializeHUDForPlayer_Implementation(APlayerController* NewPlayer) override; | ||||
| 	void InitSeamlessTravelPlayer(AController* NewController) override; | ||||
| 	 | ||||
| 	void Logout(AController* Exiting) override; | ||||
| 	 | ||||
| 	void PreLogin(const FString& Options, const FString& Address, const FUniqueNetIdRepl& UniqueId, FString& ErrorMessage) override; | ||||
| 	void PostLogin(APlayerController* NewPlayer) override; | ||||
| 	void PostSeamlessTravel() override; | ||||
|  | ||||
| 	void SetPlayerDefaults(APawn* PlayerPawn) override; | ||||
| 	void SetSeamlessTravelViewTarget(APlayerController* PC) override; | ||||
| 	 | ||||
| 	void StartPlay() override; | ||||
| 	void StartToLeaveMap() override; | ||||
|  | ||||
| 	void SwapPlayerControllers(APlayerController* OldPC, APlayerController* NewPC) override; | ||||
|  | ||||
| 	APlayerController* SpawnPlayerControllerCommon(ENetRole InRemoteRole, FVector const& SpawnLocation, FRotator const& SpawnRotation, TSubclassOf<APlayerController> InPlayerControllerClass) override; | ||||
| #pragma endregion GameModeBase | ||||
| }; | ||||
|  | ||||
| namespace Gasa | ||||
| { | ||||
| 	inline | ||||
| 	AGasaGameMode* GetGameMode(UObject* Context) | ||||
| 	inline AGasaGameMode* GetGameMode( UObject* Context ) | ||||
| 	{ | ||||
| 		UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); | ||||
| 		if (World == nullptr) | ||||
| 		UWorld* World = GEngine->GetWorldFromContextObject( Context, EGetWorldErrorMode::LogAndReturnNull ); | ||||
| 		if ( World == nullptr ) | ||||
| 		{ | ||||
| 			Log("World is null... are you running in a proper context?", ELogV::Error); | ||||
| 			Log( "World is null... are you running in a proper context?", ELogV::Error ); | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 		return Cast<AGasaGameMode>(World->GetAuthGameMode()); | ||||
| 		return Cast<AGasaGameMode>( World->GetAuthGameMode() ); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,10 @@ | ||||
|  | ||||
| #include "CogAll.h" | ||||
| #include "CogWindowManager.h" | ||||
| #include "GasaPlayerState.h" | ||||
| #include "GasaGameInstance.h" | ||||
| #include "Net/UnrealNetwork.h" | ||||
| using namespace Gasa; | ||||
|  | ||||
| AGasaGameState::AGasaGameState() | ||||
| { | ||||
| @@ -9,11 +13,92 @@ AGasaGameState::AGasaGameState() | ||||
|     PrimaryActorTick.bCanEverTick = true; | ||||
|     PrimaryActorTick.SetTickFunctionEnable(true); | ||||
|     PrimaryActorTick.bStartWithTickEnabled = true; | ||||
|  | ||||
| 	// Replication | ||||
| 	 | ||||
| 	bReplicates            = true; | ||||
| 	bNetLoadOnClient       = false; | ||||
| 	NetDormancy            = DORM_Awake; | ||||
| 	NetCullDistanceSquared = NetCullDist_Default; | ||||
| 	NetUpdateFrequency     = 10.0f; | ||||
| 	MinNetUpdateFrequency  = 1.0f; | ||||
| 	NetPriority            = 5.0f; | ||||
| } | ||||
|  | ||||
| #pragma region GameState | ||||
| #pragma region GameFramework | ||||
| void AGasaGameState::OnGameFrameworkInitialized() | ||||
| { | ||||
| 	NetLog("Received gameplay framework initialization."); | ||||
|  | ||||
| 	if (IsServer()) | ||||
| 	{ | ||||
| 		if (PlayerArray.Num() > 0) | ||||
| 		{ | ||||
| 			ListenServerHost = Cast<AGasaPlayerState>(PlayerArray[0]); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			NetLog("Was not able to assign HostingPlayer!", ELogV::Error); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	BP_OnGameFrameworkInitialized(); | ||||
| } | ||||
| #pragma endregion GameFramework | ||||
|  | ||||
| #pragma region Networking | ||||
| void AGasaGameState::Client_OnRep_OnlinePlayers() | ||||
| { | ||||
| } | ||||
| #pragma endregion Networking | ||||
|  | ||||
| #pragma region Seamless Travel | ||||
| void AGasaGameState::Multicast_R_NotifySeamlessTravelEnd_Implementation() | ||||
| { | ||||
| 	NetLog("Multicast_R_NotifySeamlessTravelEnd_Implementation"); | ||||
| 	BP_Event_OnSeamlessTravelEnd.Broadcast(); | ||||
| 	Event_OnSeamlessTravelEnd.Broadcast(); | ||||
| } | ||||
| #pragma endregion Seamless Travel | ||||
|  | ||||
| #pragma region GameStateBase | ||||
| void AGasaGameState::HandleBeginPlay() | ||||
| { | ||||
| 	Super::HandleBeginPlay(); | ||||
| 	NetLog("HandleBeginPlay: Directly called from GM"); | ||||
| } | ||||
|  | ||||
| void AGasaGameState::SeamlessTravelTransitionCheckpoint(bool bToTransitionMap) | ||||
| { | ||||
| 	Super::SeamlessTravelTransitionCheckpoint(bToTransitionMap); | ||||
|  | ||||
| 	NetLog("SeamlessTravelTransitionCheckpoint"); | ||||
| 	NetLog(FString("ToTransitionMap: ") + FString(bToTransitionMap ? "true" : "false")); | ||||
|  | ||||
| 	if (bToTransitionMap) | ||||
| 	{ | ||||
| 		Event_OnSeamlessTravelStart.Broadcast(); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		Multicast_R_NotifySeamlessTravelEnd(); | ||||
| 	} | ||||
| } | ||||
| #pragma endregion GameStateBase | ||||
|  | ||||
| #pragma region Actor | ||||
| void AGasaGameState::BeginPlay() | ||||
| { | ||||
| 	Super::BeginPlay(); | ||||
|  | ||||
| 	NetLog("BeginPlay"); | ||||
|  | ||||
| 	// Notified as initialized here as any possible components should also be initialized by this point. | ||||
| 	UGasaGameInstance* | ||||
| 	GI = GetGameInstance<UGasaGameInstance>(); | ||||
| 	GI->Event_OnGameFrameworkInitialized.AddDynamic(this, & ThisClass::OnGameFrameworkInitialized); | ||||
| 	GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::GameState); | ||||
| 	 | ||||
| #if ENABLE_COG | ||||
|     CogWindowManager = NewObject<UCogWindowManager>(this); | ||||
|     CogWindowManagerRef = CogWindowManager; | ||||
| @@ -23,6 +108,28 @@ void AGasaGameState::BeginPlay() | ||||
| #endif //ENABLE_COG | ||||
| } | ||||
|  | ||||
| void AGasaGameState::PostInitializeComponents() | ||||
| { | ||||
| 	NetLog("PostInitializeComponents"); | ||||
|  | ||||
| 	Super::PostInitializeComponents(); | ||||
|  | ||||
| 	if ( ! GetWorld()->IsEditorWorld() && IsServer()) | ||||
| 	{ | ||||
| 		OnlinePlayers.Empty(); | ||||
| #if 0 | ||||
| 		const auto GI = Cast<UGasaGameInstance>(GetGameInstance()); | ||||
| 		if (GI != nullptr) | ||||
| 		{ | ||||
| 			int32 NumConnections = GI->SessionSettings.bPublicGame | ||||
| 				? GI->SessionSettings.PublicConnections | ||||
| 				: GI->SessionSettings.PrivateConnections; | ||||
| 			OnlinePlayers.Init(nullptr, NumConnections); | ||||
| 		} | ||||
| #endif | ||||
| 	}	 | ||||
| } | ||||
|  | ||||
| void AGasaGameState::Tick(float DeltaSeconds) | ||||
| { | ||||
| 	Super::Tick(DeltaSeconds); | ||||
| @@ -31,4 +138,14 @@ void AGasaGameState::Tick(float DeltaSeconds) | ||||
|     CogWindowManager->Tick(DeltaSeconds); | ||||
| #endif //ENABLE_COG | ||||
| } | ||||
| #pragma endregion GameState | ||||
| #pragma endregion Actor | ||||
|  | ||||
| #pragma region UObject | ||||
| void AGasaGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const | ||||
| { | ||||
| 	Super::GetLifetimeReplicatedProps(OutLifetimeProps); | ||||
|  | ||||
| 	DOREPLIFETIME(AGasaGameState, ListenServerHost); | ||||
| 	DOREPLIFETIME(AGasaGameState, OnlinePlayers); | ||||
| } | ||||
| #pragma endregion UObject | ||||
|   | ||||
| @@ -1,46 +1,116 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "GameFramework/GameState.h" | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Engine/Engine.h" | ||||
| #include "Networking/GasaNetLibrary.h" | ||||
|  | ||||
| #include "GasaGameState.generated.h" | ||||
|  | ||||
| UCLASS(Blueprintable) | ||||
| DECLARE_MULTICAST_DELEGATE( FOnTravelDelegate ); | ||||
| DECLARE_DYNAMIC_MULTICAST_DELEGATE( FOnTravelSig ); | ||||
| DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerCharacterReadySig, APlayerCharacter*, Character); | ||||
|  | ||||
| UCLASS( Blueprintable ) | ||||
| class GASA_API AGasaGameState : public AGameState | ||||
| { | ||||
| 	GENERATED_BODY() | ||||
| public: | ||||
| #pragma region Cog | ||||
|     // To make sure it doesn't get garbage collected. | ||||
|     UPROPERTY() | ||||
|     TObjectPtr<UObject> CogWindowManagerRef; | ||||
| 	 | ||||
| 	// To make sure it doesn't get garbage collected. | ||||
| 	UPROPERTY() | ||||
| 	TObjectPtr<UObject> CogWindowManagerRef; | ||||
|  | ||||
| #if ENABLE_COG | ||||
| 	TObjectPtr<UCogWindowManager> CogWindowManager; | ||||
| #endif // ENABLE_COG | ||||
| #endif | ||||
| // ENABLE_COG | ||||
| #pragma endregion Cog | ||||
|  | ||||
| 	AGasaGameState(); | ||||
|  | ||||
| #pragma region GameFramework | ||||
| 	UFUNCTION() | ||||
| 	void OnGameFrameworkInitialized(); | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "Game Framework Initialized")) | ||||
| 	void BP_OnGameFrameworkInitialized(); | ||||
| #pragma endregion GameFramework | ||||
|  | ||||
| #pragma region Networking | ||||
| 	UPROPERTY(Replicated, BlueprintReadOnly) | ||||
| 	AGasaPlayerState* ListenServerHost; | ||||
|  | ||||
| 	UPROPERTY(ReplicatedUsing = "Client_OnRep_OnlinePlayers", BlueprintReadOnly) | ||||
| 	TArray<AGasaPlayerState> OnlinePlayers; | ||||
|  | ||||
| 	UFUNCTION() | ||||
| 	void Client_OnRep_OnlinePlayers(); | ||||
| #pragma endregion Networking | ||||
|  | ||||
| #pragma region Seamless Travel | ||||
| 	UPROPERTY(BlueprintAssignable) | ||||
| 	FOnTravelSig Event_OnSeamlessTravelStart; | ||||
|  | ||||
| 	UPROPERTY(BlueprintAssignable, meta=(DisplayName="Event: On Seamless Travel End")) | ||||
| 	FOnTravelSig BP_Event_OnSeamlessTravelEnd; | ||||
| 	 | ||||
| 	FOnTravelDelegate Event_OnSeamlessTravelEnd; | ||||
|  | ||||
| 	UFUNCTION(NetMulticast, Reliable) | ||||
| 	void Multicast_R_NotifySeamlessTravelEnd(); | ||||
| #pragma endregion Seamless Travel | ||||
| 	 | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
| 	 | ||||
| #pragma region GameState | ||||
| 	void BeginPlay() override; | ||||
| 	void HandleBeginPlay() override; | ||||
| #pragma endregion GameState | ||||
|  | ||||
| 	void Tick(float DeltaSeconds) override; | ||||
| #pragma endregion GameState	 | ||||
| #pragma region GameStateBase | ||||
| 	void SeamlessTravelTransitionCheckpoint(bool bToTransitionMap) override; | ||||
| #pragma endregion GameStateBase | ||||
|  | ||||
| #pragma region Actor | ||||
| 	void BeginPlay() override; | ||||
| 	void PostInitializeComponents() override; | ||||
| 	void Tick( float DeltaSeconds ) override; | ||||
| #pragma endregion Actor | ||||
|  | ||||
| #pragma region UObject | ||||
| 	void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override; | ||||
| #pragma endregion UObject | ||||
| }; | ||||
|  | ||||
| namespace Gasa | ||||
| { | ||||
| 	inline | ||||
| 	AGasaGameState* GetGameState(UObject* Context) | ||||
| 	inline AGasaGameState* GetGameState( UObject* Context ) | ||||
| 	{ | ||||
| 		UWorld* World = GEngine->GetWorldFromContextObject(Context, EGetWorldErrorMode::LogAndReturnNull); | ||||
| 		if (World == nullptr) | ||||
| 		UWorld* World = GEngine->GetWorldFromContextObject( Context, EGetWorldErrorMode::LogAndReturnNull ); | ||||
| 		if ( World == nullptr ) | ||||
| 		{ | ||||
| 			Log("World is null... are you running in a proper context?", ELogV::Error); | ||||
| 			Log( "World is null... are you running in a proper context?", ELogV::Error ); | ||||
| 			return nullptr; | ||||
| 		} | ||||
| 		return Cast<AGasaGameState>(World->GetGameState()); | ||||
| 		return Cast<AGasaGameState>( World->GetGameState() ); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,11 +1,39 @@ | ||||
| #include "GasaLevelScriptActor.h" | ||||
|  | ||||
| #include "GasaDevOptions.h" | ||||
| #include "GasaGameInstance.h" | ||||
| #include "Engine/PostProcessVolume.h" | ||||
| #include "Kismet/GameplayStatics.h" | ||||
| #include "Materials/MaterialInstance.h" | ||||
| #include "Materials/MaterialInstanceDynamic.h" | ||||
| using namespace Gasa; | ||||
|  | ||||
| #pragma region Game Framework | ||||
| AGasaLevelScriptActor::AGasaLevelScriptActor() | ||||
| { | ||||
| 	// Replication | ||||
| 	 | ||||
| 	bReplicates            = true; | ||||
| 	bNetLoadOnClient       = true; | ||||
| 	NetDormancy            = DORM_Awake; | ||||
| 	NetCullDistanceSquared = NetCullDist_Default; | ||||
| 	NetUpdateFrequency     = 10.0f; | ||||
| 	MinNetUpdateFrequency  = 1.0f; | ||||
| 	NetPriority            = 1.0f; | ||||
| } | ||||
|  | ||||
| void AGasaLevelScriptActor::OnGameFrameworkInitialized() | ||||
| { | ||||
| 	NetLog("Received game framework initialization."); | ||||
| 	BP_OnGameFrameworkInitialized(); | ||||
| } | ||||
| #pragma endregion Game Framework | ||||
|  | ||||
| #pragma region Actor | ||||
| void AGasaLevelScriptActor::BeginPlay() | ||||
| { | ||||
| 	Super::BeginPlay(); | ||||
| 	NetLog("BeginPlay"); | ||||
|  | ||||
| 	using namespace Gasa; | ||||
|  | ||||
| @@ -21,4 +49,12 @@ void AGasaLevelScriptActor::BeginPlay() | ||||
| 		PPV->Settings.WeightedBlendables.Array[0].Object = MID; | ||||
| 		break; | ||||
| 	} | ||||
| 	 | ||||
| 	UGasaGameInstance* GI = GetGameInstance<UGasaGameInstance>(); | ||||
| 	if(GI) | ||||
| 		GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & ThisClass::OnGameFrameworkInitialized); | ||||
|  | ||||
| 	if (!bOverrideGameplayFrameworkReady) | ||||
| 		GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::Levels); | ||||
| } | ||||
| #pragma endregion Actor | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| #pragma once | ||||
| #include "GasaCommon.h" | ||||
| #include "Engine/Engine.h" | ||||
|  | ||||
| #include "Engine/LevelScriptActor.h" | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Networking/GasaNetLibrary.h" | ||||
|  | ||||
| #include "GasaLevelScriptActor.generated.h" | ||||
|  | ||||
| @@ -14,6 +15,41 @@ public: | ||||
|     UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Post Process") | ||||
|     TObjectPtr<APostProcessVolume> GlobalPPV; | ||||
|  | ||||
| 	AGasaLevelScriptActor(); | ||||
|  | ||||
| #pragma region GameFramework | ||||
| 	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) | ||||
| 	bool bOverrideGameplayFrameworkReady = false; | ||||
|  | ||||
| 	UFUNCTION() | ||||
| 	void OnGameFrameworkInitialized(); | ||||
|  | ||||
| 	UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName="On Game Framework Initialized")) | ||||
| 	void BP_OnGameFrameworkInitialized();	 | ||||
| #pragma endregion GameFramework | ||||
|  | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
|  | ||||
| #pragma region Actor | ||||
|     void BeginPlay() override; | ||||
| #pragma region endActor | ||||
|   | ||||
| @@ -155,19 +155,22 @@ void AGasaPlayerController::BeginPlay() | ||||
| { | ||||
| 	Super::BeginPlay(); | ||||
|  | ||||
| 	check(IMC); | ||||
| 	 | ||||
| 	UEnhancedInputLocalPlayerSubsystem* | ||||
| 	EILP_Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()); | ||||
| 	check(EILP_Subsystem); | ||||
| 	EILP_Subsystem->AddMappingContext(IMC, 0); | ||||
| 	if (IsLocalController()) | ||||
| 	{ | ||||
| 		bShowMouseCursor   = true; | ||||
| 		DefaultMouseCursor = EMouseCursor::Default; | ||||
| 		FInputModeGameAndUI MouseMode; | ||||
| 		MouseMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); | ||||
| 		MouseMode.SetHideCursorDuringCapture(false); | ||||
| 		SetInputMode(MouseMode); | ||||
| 		check(IMC); | ||||
| 		 | ||||
| 		UEnhancedInputLocalPlayerSubsystem* | ||||
| 		EILP_Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(GetLocalPlayer()); | ||||
| 		check(EILP_Subsystem); | ||||
| 		EILP_Subsystem->AddMappingContext(IMC, 0); | ||||
| 		{ | ||||
| 			bShowMouseCursor   = true; | ||||
| 			DefaultMouseCursor = EMouseCursor::Default; | ||||
| 			FInputModeGameAndUI MouseMode; | ||||
| 			MouseMode.SetLockMouseToViewportBehavior(EMouseLockMode::DoNotLock); | ||||
| 			MouseMode.SetHideCursorDuringCapture(false); | ||||
| 			SetInputMode(MouseMode); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,9 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Engine/Engine.h" | ||||
| #include "GameFramework/PlayerController.h" | ||||
| #include "Networking/GasaNetLibrary.h" | ||||
|  | ||||
| #include "GasaPlayerController.generated.h" | ||||
|  | ||||
| @@ -51,12 +53,33 @@ public: | ||||
| 	AGasaPlayerController(); | ||||
|  | ||||
| 	inline AGasaPlayerState* GetPlayerState(); | ||||
|  | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
| 	 | ||||
| #pragma region PlayerController | ||||
| 	void SpawnDefaultHUD() override; | ||||
| 	 | ||||
| 	void OnPossess(APawn* InPawn) override; | ||||
|  | ||||
| 	void OnUnPossess() override; | ||||
| 	 | ||||
| 	void PlayerTick(float DeltaTime) override; | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
|  | ||||
| AGasaPlayerState::AGasaPlayerState() | ||||
| { | ||||
| 	bAutoAbilitySystem = true; | ||||
| 	 | ||||
| 	AbilitySystem = CreateDefaultSubobject<UGasaAbilitySystemComp>("Ability System"); | ||||
| 	AbilitySystem->SetIsReplicated(true); | ||||
| 	AbilitySystem->SetReplicationMode(EGameplayEffectReplicationMode::Mixed); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #include "GameFramework/PlayerState.h" | ||||
|  | ||||
| #include "GasaCommon.h" | ||||
| #include "Networking/GasaNetLibrary.h" | ||||
|  | ||||
| #include "GasaPlayerState.generated.h" | ||||
|  | ||||
| @@ -16,7 +17,7 @@ class GASA_API AGasaPlayerState : public APlayerState | ||||
| public: | ||||
| #pragma region Ability System | ||||
| 	UPROPERTY(EditAnywhere, Category="Ability System") | ||||
| 	bool bAutoAbilitySystem = true; | ||||
| 	bool bAutoAbilitySystem; | ||||
| 	 | ||||
| 	UPROPERTY(EditAnywhere, Category="Ability System") | ||||
| 	TObjectPtr<UAbilitySystemComponent> AbilitySystem; | ||||
| @@ -27,6 +28,28 @@ public: | ||||
| 	 | ||||
| 	AGasaPlayerState(); | ||||
|  | ||||
| #pragma region NetSlime | ||||
| 	// NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp | ||||
| 	FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } | ||||
| 	FORCEINLINE bool IsClient()               const { return Gasa::IsClient( this ); } | ||||
| 	FORCEINLINE bool IsListenServer()         const { return Gasa::IsListenServer( this ); } | ||||
| 	FORCEINLINE bool IsNetOwner()             const { return Gasa::IsNetOwner( this ); } | ||||
| 	FORCEINLINE bool IsServer()               const { return Gasa::IsServer( this ); } | ||||
| 	FORCEINLINE bool IsSimulatedProxy()       const { return Gasa::IsSimulatedProxy( this ); } | ||||
| 	FORCEINLINE void NetLog( | ||||
| 		FString           Message, | ||||
| 		EGasaVerbosity    Verbosity = EGasaVerbosity::Log, | ||||
| 		FLogCategoryBase& Category  = LogGasaNet, | ||||
| 		bool              DumpStack = false, | ||||
| 		int32             Line      = __builtin_LINE(), | ||||
| 		ANSICHAR const*   File      = __builtin_FILE(), | ||||
| 		ANSICHAR const*   Func      = __builtin_FUNCTION() | ||||
| 	) | ||||
| 	{ | ||||
| 		Gasa::NetLog( this, Message, Verbosity, Category, DumpStack, Line, File, Func ); | ||||
| 	} | ||||
| #pragma endregion NetSlime | ||||
|  | ||||
| #pragma region IAbilitySystem | ||||
| 	FORCEINLINE UAttributeSet*           GetAttributes()                            { return Attributes; } | ||||
| 	FORCEINLINE UAbilitySystemComponent* GetAbilitySystemComponent() const override { return AbilitySystem; } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user