From 82a33a0f0f6f127382e36b773dd5708f14ed183b Mon Sep 17 00:00:00 2001 From: Ed_ Date: Tue, 23 Apr 2024 18:54:17 -0400 Subject: [PATCH] 'NetSlime' Initial port finished and working with 2 players (at least) --- Project/Binaries/Win64/UnrealEditor-Gasa.dll | 3 - .../Win64/UnrealEditor-GasaEditor.dll | 2 +- .../Source/Gasa/Characters/EnemyCharacter.cpp | 1 + .../Source/Gasa/Characters/GasaCharacter.cpp | 119 +++++++- .../Source/Gasa/Characters/GasaCharacter.h | 30 +- .../Gasa/Characters/PlayerCharacter.cpp | 35 +-- Project/Source/Gasa/Game/GasaGameInstance.cpp | 22 +- Project/Source/Gasa/Game/GasaGameInstance.h | 6 +- Project/Source/Gasa/Game/GasaGameMode.cpp | 45 ++- Project/Source/Gasa/Game/GasaGameMode.h | 4 +- Project/Source/Gasa/Game/GasaGameState.cpp | 2 +- Project/Source/Gasa/Game/GasaGameState.h | 17 +- .../Source/Gasa/Game/GasaLevelScriptActor.cpp | 3 +- .../Source/Gasa/Game/GasaLevelScriptActor.h | 2 +- .../Source/Gasa/Game/GasaPlayerController.cpp | 284 ++++++++++++++++-- .../Source/Gasa/Game/GasaPlayerController.h | 91 +++++- Project/Source/Gasa/Game/GasaPlayerState.cpp | 74 +++++ Project/Source/Gasa/Game/GasaPlayerState.h | 45 ++- Project/Source/Gasa/Gasa.Build.cs | 1 + Project/Source/Gasa/GasaColdHeadersPCH.h | 4 +- Project/Source/Gasa/GasaCommon.h | 16 + Project/Source/Gasa/GasaEngineMinimal.h | 139 +-------- .../Source/Gasa/Networking/GasaNetLibrary.cpp | 137 +++++---- .../Source/Gasa/Networking/GasaNetLibrary.h | 33 +- .../Gasa/Networking/GasaNetLibrary_Inlines.h | 85 +----- 25 files changed, 799 insertions(+), 401 deletions(-) delete mode 100644 Project/Binaries/Win64/UnrealEditor-Gasa.dll diff --git a/Project/Binaries/Win64/UnrealEditor-Gasa.dll b/Project/Binaries/Win64/UnrealEditor-Gasa.dll deleted file mode 100644 index 8bbd303..0000000 --- a/Project/Binaries/Win64/UnrealEditor-Gasa.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:457c5440bf798c3fe0ac5681a4d6e59166b427c08fddba63e479c5249ee92114 -size 1010176 diff --git a/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll b/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll index 28e4fff..9e6db4c 100644 --- a/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll +++ b/Project/Binaries/Win64/UnrealEditor-GasaEditor.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:36682d8d415863139146fca4b596b67bb232b60f306c82f2b2692780b044f200 +oid sha256:cb9da4955b87ca673e2193c41b05cdc57340e70334ddc52b5cfdbad0792583f1 size 79360 diff --git a/Project/Source/Gasa/Characters/EnemyCharacter.cpp b/Project/Source/Gasa/Characters/EnemyCharacter.cpp index fad9c7a..a50b33d 100644 --- a/Project/Source/Gasa/Characters/EnemyCharacter.cpp +++ b/Project/Source/Gasa/Characters/EnemyCharacter.cpp @@ -1,4 +1,5 @@ #include "EnemyCharacter.h" +#include "Networking/GasaNetLibrary_Inlines.h" AEnemyCharacter::AEnemyCharacter() { diff --git a/Project/Source/Gasa/Characters/GasaCharacter.cpp b/Project/Source/Gasa/Characters/GasaCharacter.cpp index d562697..7eee235 100644 --- a/Project/Source/Gasa/Characters/GasaCharacter.cpp +++ b/Project/Source/Gasa/Characters/GasaCharacter.cpp @@ -11,13 +11,12 @@ #include "AbilitySystem/GasaAttributeSet.h" #include "Components/SkeletalMeshComponent.h" #include "Engine/PostProcessVolume.h" +#include "Game/GasaGameInstance.h" #include "Game/GasaLevelScriptActor.h" +#include "Game/GasaPlayerController.h" #include "Materials/MaterialInstanceDynamic.h" - -void AGasaCharacter::SetHighlight(EHighlight Desired) -{ - HighlightState = Desired; -} +#include "Networking/GasaNetLibrary_Inlines.h" +using namespace Gasa; AGasaCharacter::AGasaCharacter() { @@ -51,13 +50,112 @@ AGasaCharacter::AGasaCharacter() Attributes = CreateDefaultSubobject("Attributes"); } + + // Replication + + bReplicates = false; + bNetLoadOnClient = true; + NetDormancy = DORM_Awake; + NetCullDistanceSquared = NetCullDist_Medium; + NetUpdateFrequency = 30.0f; + MinNetUpdateFrequency = 5.0f; + NetPriority = 2.0f; + + ACharacter::SetReplicateMovement(true); } +#pragma region GameFramework +void AGasaCharacter::Controller_OnPawnPossessed() +{ + NetLog("Controller confirmed possession."); + + // Do stuff here that you needed to wait for the player controller be aware of you for. + BP_Controller_OnPawnPossessed(); + + if (Event_OnPawnReady.IsBound()) + Event_OnPawnReady.Broadcast(); +} + +void AGasaCharacter::ServerRPC_R_NotifyClientPawnReady_Implementation() +{ + Event_OnPawnReady.Broadcast(); +} +#pragma endregion GameFramework + +#pragma region Highlight +void AGasaCharacter::SetHighlight(EHighlight Desired) +{ + HighlightState = Desired; +} +#pragma endregion Highlight + #pragma region Pawn +void AGasaCharacter::OnRep_PlayerState() +{ + Super::OnRep_PlayerState(); +} + void AGasaCharacter::PossessedBy(AController* NewController) { - Super::PossessedBy(NewController); + NetLog("Pawn possessed."); + AController* OldController; + + // APawn::PossessedBy + { + SetOwner(NewController); + OldController = Controller; + Controller = NewController; + + ForceNetUpdate(); + + #if UE_WITH_IRIS + // The owning connection depends on the Controller having the new value. + UpdateOwningNetConnection(); + #endif + + if (Controller->PlayerState != nullptr) + SetPlayerState(Controller->PlayerState); + + if (APlayerController* PlayerController = Cast(Controller)) + { + if (GetNetMode() != NM_Standalone) + { + SetReplicates(true); + SetAutonomousProxy(true); + } + } + else + CopyRemoteRoleFrom(GetDefault()); + } + + if (AGasaPlayerController* PC = Cast(NewController)) + { + PC->Event_OnPawnPossessed.AddUniqueDynamic(this, & ThisClass::Controller_OnPawnPossessed); + } + else + { + NetLog("Controller assigned to GasaCharacter is not derived from GasaPlayerController.", ELogV::Warning); + NetLog("Controller: Name: " + NewController->GetName() + " Class: " + NewController->GetClass()->GetName(), ELogV::Warning); + } + + // cont. APawn::PossessedBy + { + // Dispatch Blueprint event if necessary + if (OldController != NewController) + { + ReceivePossessed(Controller); + NotifyControllerChanged(); + } + } + + // ACharacter::PossessedBy + { + // If we are controlled remotely, set animation timing to be driven by client's network updates. So timing and events remain in sync. + if (GetMesh() && IsReplicatingMovement() && (GetRemoteRole() == ROLE_AutonomousProxy && GetNetConnection() != nullptr)) + GetMesh()->bOnlyAllowAutonomousTickPose = true; + } + if (bAutoAbilitySystem) { // TODO(Ed): Do we need to do this for enemies? @@ -65,9 +163,14 @@ void AGasaCharacter::PossessedBy(AController* NewController) } } -void AGasaCharacter::OnRep_PlayerState() +void AGasaCharacter::SetPlayerDefaults() { - Super::OnRep_PlayerState(); + Super::SetPlayerDefaults(); +} + +void AGasaCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) +{ + Super::SetupPlayerInputComponent(PlayerInputComponent); } #pragma endregion Pawn diff --git a/Project/Source/Gasa/Characters/GasaCharacter.h b/Project/Source/Gasa/Characters/GasaCharacter.h index 4e2bd73..72f7d0a 100644 --- a/Project/Source/Gasa/Characters/GasaCharacter.h +++ b/Project/Source/Gasa/Characters/GasaCharacter.h @@ -4,7 +4,7 @@ #include "GameFramework/Character.h" #include "GasaCommon.h" -#include "Game/GasaPlayerState.h" +#include "Game/GasaGameState.h" #include "Networking/GasaNetLibrary.h" #include "GasaCharacter.generated.h" @@ -22,6 +22,9 @@ class GASA_API AGasaCharacter : public ACharacter { GENERATED_BODY() public: + + AGasaCharacter(); + #pragma region Ability System UPROPERTY(EditAnywhere, Category="Ability System") bool bAutoAbilitySystem = true; @@ -38,6 +41,20 @@ public: TObjectPtr Weapon; #pragma endregion Combat +#pragma region GameFramework + UPROPERTY(BlueprintAssignable) + FOnPawnSig Event_OnPawnReady; + + UFUNCTION() + void Controller_OnPawnPossessed(); + + UFUNCTION(BlueprintImplementableEvent) + void BP_Controller_OnPawnPossessed(); + + UFUNCTION(Server, Reliable) + void ServerRPC_R_NotifyClientPawnReady(); +#pragma endregion GameFramework + #pragma region Highlighting static constexpr float HighlightStencilDepth = 256.0; @@ -57,10 +74,6 @@ public: FORCEINLINE void Dehighlight() { SetHighlight(EHighlight::Disabled); }; #pragma endregion Highlighting - AGasaCharacter(); - - FORCEINLINE AGasaPlayerState* GetGasaPlayerState() { return GetPlayerState(); } - #pragma region NetSlime // NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } @@ -89,14 +102,14 @@ public: #pragma endregion IAbilitySystem #pragma region Pawn - void PossessedBy(AController* NewController) override; - void OnRep_PlayerState() override; + void PossessedBy(AController* NewController) override; + void SetPlayerDefaults() override; + void SetupPlayerInputComponent(UInputComponent* PlayerInputComponent) override; #pragma endregion Pawn #pragma region Actor void BeginPlay() override; - void Tick(float DeltaSeconds) override; #pragma endregion Actor }; @@ -108,3 +121,4 @@ namespace Gasa // // } } + diff --git a/Project/Source/Gasa/Characters/PlayerCharacter.cpp b/Project/Source/Gasa/Characters/PlayerCharacter.cpp index 1ec3939..12896f9 100644 --- a/Project/Source/Gasa/Characters/PlayerCharacter.cpp +++ b/Project/Source/Gasa/Characters/PlayerCharacter.cpp @@ -1,5 +1,6 @@ #include "PlayerCharacter.h" +#include "Networking/GasaNetLibrary_Inlines.h" #include "AbilitySystemComponent.h" #include "Game/GasaPlayerController.h" #include "UI/GasaHUD.h" @@ -12,46 +13,12 @@ APlayerCharacter::APlayerCharacter() bAutoAbilitySystem = false; } -// TODO(Ed): We need to setup Net Slime... void APlayerCharacter::PossessedBy(AController* NewController) { Super::PossessedBy(NewController); - - AGasaPlayerState* PS = GetGasaPlayerState(); - // Server setup ability system (character side) - { - AbilitySystem = PS->AbilitySystem; - Attributes = PS->Attributes; - AbilitySystem->InitAbilityActorInfo(PS, this); - } - - if (IsLocallyControlled()) - { - AGasaPlayerController* PC = GetController(); - AGasaHUD* HUD = PC->GetHUD(); - FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes }; - HUD->InitHostWidget(& Data); - } } -// TODO(Ed): We need to setup Net Slime... void APlayerCharacter::OnRep_PlayerState() { Super::OnRep_PlayerState(); - - AGasaPlayerState* PS = GetGasaPlayerState(); - // Client setup ability system - { - AbilitySystem = PS->AbilitySystem; - Attributes = PS->Attributes; - AbilitySystem->InitAbilityActorInfo(PS, this); - } - - if (IsLocallyControlled()) - { - AGasaPlayerController* PC = GetController(); - AGasaHUD* HUD = PC->GetHUD(); - FWidgetControllerData Data = { PC, PS, AbilitySystem, Attributes }; - HUD->InitHostWidget(& Data); - } } diff --git a/Project/Source/Gasa/Game/GasaGameInstance.cpp b/Project/Source/Gasa/Game/GasaGameInstance.cpp index 3424c08..5c189c7 100644 --- a/Project/Source/Gasa/Game/GasaGameInstance.cpp +++ b/Project/Source/Gasa/Game/GasaGameInstance.cpp @@ -7,29 +7,34 @@ using namespace Gasa; #pragma region GameFramework // TODO(Ed): Make a NetLog +UGasaGameInstance::UGasaGameInstance() +{ + GameFrameworkState = EGameFrameworkState::Uninitialized; +} + void UGasaGameInstance::NotifyGameFrameworkClassReady(EGameFrameworkClassFlag ClassReady) { switch (ClassReady) { case EGameFrameworkClassFlag::GameMode: GameFrameworkClassesState |= (uint32)EGameFrameworkClassFlag::GameMode; - NetLog("Gameplay Framework class ready: Game State", ELogV::Log, LogGasaNet ); + NetLog("Game 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 ); + NetLog("Game 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); + NetLog("Game 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); + NetLog("Game 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); + NetLog("Game Framework class ready: Levels", ELogV::Log, LogGasaNet); break; } ProcessGameFrameworkState(); @@ -68,7 +73,7 @@ void UGasaGameInstance::ProcessGameFrameworkState() if (GameFrameworkClassesState == InitializedFlags) { GameFrameworkState = EGameFrameworkState::Initialized; - NetLog("Gameplay Framework initialized"); + NetLog("Game Framework initialized"); Event_OnGameFrameworkInitialized.Broadcast(); } @@ -84,8 +89,5 @@ void UGasaGameInstance::Init() Super::Init(); DevOptionsCache.CachedDevOptions(); - - using namespace Gasa; - NetLog(FString::Printf(TEXT("UObject Size: %d RT: %d"), sizeof(UObject), UObject::StaticClass()->PropertiesSize )); } -#pragma region GameInstance +#pragma endregion GameInstance diff --git a/Project/Source/Gasa/Game/GasaGameInstance.h b/Project/Source/Gasa/Game/GasaGameInstance.h index fb23c69..0c401c0 100644 --- a/Project/Source/Gasa/Game/GasaGameInstance.h +++ b/Project/Source/Gasa/Game/GasaGameInstance.h @@ -21,8 +21,8 @@ enum class EGameFrameworkClassFlag : uint8 UENUM(BlueprintType) enum class EGameFrameworkState : uint8 { - Initialized, - Uninitialized + Uninitialized, + Initialized }; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnGameFrameworkInitializedSig); @@ -33,6 +33,8 @@ class GASA_API UGasaGameInstance : public UGameInstance GENERATED_BODY() public: + UGasaGameInstance(); + UPROPERTY(VisibleAnywhere, Category="Dev Cache") FGasaDevOptionsCache DevOptionsCache; diff --git a/Project/Source/Gasa/Game/GasaGameMode.cpp b/Project/Source/Gasa/Game/GasaGameMode.cpp index 2861ca5..26f5c75 100644 --- a/Project/Source/Gasa/Game/GasaGameMode.cpp +++ b/Project/Source/Gasa/Game/GasaGameMode.cpp @@ -1,9 +1,9 @@ #include "GasaGameMode.h" +#include "Online/CoreOnline.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" @@ -71,6 +71,33 @@ void AGasaGameMode::EndPlay(const EEndPlayReason::Type EndPlayReason) NetLog("EndPlay"); } +void AGasaGameMode::FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation) +{ + // Super::FinishRestartPlayer(NewPlayer, StartRotation); + { + NewPlayer->Possess(NewPlayer->GetPawn()); + + // If the Pawn is destroyed as part of possession we have to abort + if (!IsValid(NewPlayer->GetPawn())) + { + FailedToRestartPlayer(NewPlayer); + } + else + { + // Set initial control rotation to starting rotation rotation + NewPlayer->ClientSetRotation(NewPlayer->GetPawn()->GetActorRotation(), true); + + FRotator NewControllerRot = StartRotation; + NewControllerRot.Roll = 0.f; + NewPlayer->SetControlRotation(NewControllerRot); + + SetPlayerDefaults(NewPlayer->GetPawn()); + + K2_OnRestartPlayer(NewPlayer); + } + } +} + void AGasaGameMode::GenericPlayerInitialization(AController* C) { NetLog("GenericPlayerInitialization: " + C->GetName()); @@ -360,10 +387,10 @@ void AGasaGameMode::PostLogin(APlayerController* NewPlayer) { UGasaGameInstance* GI = GetGameInstance(); - // int32 numconnections = gi->sessionsettings.bpublicgame - // ? GI->SessionSettings.PublicConnections : GI->SessionSettings.PrivateConnections; - #if 0 + int32 numconnections = GI->sessionsettings.bPublicGame + ? GI->SessionSettings.PublicConnections : GI->SessionSettings.PrivateConnections; + if (GS->OnlinePlayers.Num() < NumConnections) { GS->OnlinePlayers.Init( nullptr, NumConnections ); @@ -388,8 +415,8 @@ void AGasaGameMode::PostLogin(APlayerController* NewPlayer) } AGasaPlayerController* PC = Cast(NewPlayer); - // if (PC) - // PC->Event_OnNetOwner_GameplayFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized); + if (PC) + PC->Event_NetOwner_OnGameFrameworkInitialized.AddDynamic(this, &ThisClass::OwningClient_OnGameFrameworkInitialized); } void AGasaGameMode::PostSeamlessTravel() @@ -401,11 +428,7 @@ void AGasaGameMode::PostSeamlessTravel() void AGasaGameMode::SetPlayerDefaults(APawn* PlayerPawn) { InitializeHUDForPlayer(Cast(PlayerPawn->GetController())); - - // Super::SetPlayerDefaults(PlayerPawn); - { - PlayerPawn->SetPlayerDefaults(); - } + Super::SetPlayerDefaults(PlayerPawn); } void AGasaGameMode::SetSeamlessTravelViewTarget(APlayerController* PC) diff --git a/Project/Source/Gasa/Game/GasaGameMode.h b/Project/Source/Gasa/Game/GasaGameMode.h index 0be8d82..e1d2d6a 100644 --- a/Project/Source/Gasa/Game/GasaGameMode.h +++ b/Project/Source/Gasa/Game/GasaGameMode.h @@ -24,7 +24,7 @@ public: UFUNCTION() void OwningClient_OnGameFrameworkInitialized(AGasaPlayerController* PC); - UFUNCTION(BlueprintCallable, meta=(DisplayName = "On Game Framework Initialized")) + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "On Game Framework Initialized")) void BP_OnGameFrameworkInitialized(); #pragma endregion GameFramework @@ -60,6 +60,8 @@ public: #pragma region GameModeBase void EndPlay(const EEndPlayReason::Type EndPlayReason) override; + void FinishRestartPlayer(AController* NewPlayer, const FRotator& StartRotation) override; + void GenericPlayerInitialization(AController* C) override; TSubclassOf GetPlayerControllerClassToSpawnForSeamlessTravel(APlayerController* PreviousPlayerController) override; diff --git a/Project/Source/Gasa/Game/GasaGameState.cpp b/Project/Source/Gasa/Game/GasaGameState.cpp index 0a35308..19b03b4 100644 --- a/Project/Source/Gasa/Game/GasaGameState.cpp +++ b/Project/Source/Gasa/Game/GasaGameState.cpp @@ -5,6 +5,7 @@ #include "GasaPlayerState.h" #include "GasaGameInstance.h" #include "Net/UnrealNetwork.h" +#include "Networking/GasaNetLibrary_Inlines.h" using namespace Gasa; AGasaGameState::AGasaGameState() @@ -15,7 +16,6 @@ AGasaGameState::AGasaGameState() PrimaryActorTick.bStartWithTickEnabled = true; // Replication - bReplicates = true; bNetLoadOnClient = false; NetDormancy = DORM_Awake; diff --git a/Project/Source/Gasa/Game/GasaGameState.h b/Project/Source/Gasa/Game/GasaGameState.h index cf9fa6b..aaeb365 100644 --- a/Project/Source/Gasa/Game/GasaGameState.h +++ b/Project/Source/Gasa/Game/GasaGameState.h @@ -1,3 +1,4 @@ +#pragma once #include "GameFramework/GameState.h" #include "GasaCommon.h" @@ -6,10 +7,6 @@ #include "GasaGameState.generated.h" -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 { @@ -29,11 +26,21 @@ public: AGasaGameState(); #pragma region GameFramework + UPROPERTY(BlueprintAssignable) + FOnPawnReadySig Event_OnPlayerPawnReady; + UFUNCTION() void OnGameFrameworkInitialized(); UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "Game Framework Initialized")) void BP_OnGameFrameworkInitialized(); + + UFUNCTION() + void NotifyPlayerPawnReady(APawn* Pawn) + { + if (Event_OnPlayerPawnReady.IsBound()) + Event_OnPlayerPawnReady.Broadcast(Pawn); + } #pragma endregion GameFramework #pragma region Networking @@ -41,7 +48,7 @@ public: AGasaPlayerState* ListenServerHost; UPROPERTY(ReplicatedUsing = "Client_OnRep_OnlinePlayers", BlueprintReadOnly) - TArray OnlinePlayers; + TArray OnlinePlayers; UFUNCTION() void Client_OnRep_OnlinePlayers(); diff --git a/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp index 904a899..4243665 100644 --- a/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp +++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.cpp @@ -6,6 +6,7 @@ #include "Kismet/GameplayStatics.h" #include "Materials/MaterialInstance.h" #include "Materials/MaterialInstanceDynamic.h" +#include "Networking/GasaNetLibrary_Inlines.h" using namespace Gasa; #pragma region Game Framework @@ -54,7 +55,7 @@ void AGasaLevelScriptActor::BeginPlay() if(GI) GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & ThisClass::OnGameFrameworkInitialized); - if (!bOverrideGameplayFrameworkReady) + if (!bOverrideGameFrameworkReady) GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::Levels); } #pragma endregion Actor diff --git a/Project/Source/Gasa/Game/GasaLevelScriptActor.h b/Project/Source/Gasa/Game/GasaLevelScriptActor.h index 0463081..0272d12 100644 --- a/Project/Source/Gasa/Game/GasaLevelScriptActor.h +++ b/Project/Source/Gasa/Game/GasaLevelScriptActor.h @@ -19,7 +19,7 @@ public: #pragma region GameFramework UPROPERTY(EditDefaultsOnly, BlueprintReadOnly) - bool bOverrideGameplayFrameworkReady = false; + bool bOverrideGameFrameworkReady = false; UFUNCTION() void OnGameFrameworkInitialized(); diff --git a/Project/Source/Gasa/Game/GasaPlayerController.cpp b/Project/Source/Gasa/Game/GasaPlayerController.cpp index bb713e6..6a91152 100644 --- a/Project/Source/Gasa/Game/GasaPlayerController.cpp +++ b/Project/Source/Gasa/Game/GasaPlayerController.cpp @@ -1,18 +1,26 @@ #include "GasaPlayerController.h" #include "GasaPlayerController_Inlines.h" +#include "Networking/GasaNetLibrary_Inlines.h" #include "AbilitySystemComponent.h" #include "Engine/LocalPlayer.h" #include "EnhancedInputComponent.h" #include "EnhancedInputSubsystems.h" +#include "Characters/GasaCharacter.h" +#include "Characters/PlayerCharacter.h" +#include "Components/CapsuleComponent.h" +#include "Interfaces/NetworkPredictionInterface.h" +#include "Kismet/KismetSystemLibrary.h" +#include "Net/UnrealNetwork.h" +#include "GameFramework/PawnMovementComponent.h" + #include "GasaDevOptions.h" +#include "GasaGameInstance.h" +#include "GasaGameState.h" #include "GasaPlayerState.h" #include "Actors/CameraMount.h" -#include "Camera/CameraComponent.h" -#include "Characters/GasaCharacter.h" -#include "Components/CapsuleComponent.h" -#include "GameFramework/SpringArmComponent.h" -#include "Kismet/KismetSystemLibrary.h" +#include "UI/GasaHUD.h" +#include "UI/WidgetController.h" using namespace Gasa; AGasaPlayerController::AGasaPlayerController() @@ -24,7 +32,143 @@ AGasaPlayerController::AGasaPlayerController() bReplicates = true; } +void AGasaPlayerController::OnSeamlessTravelStart() +{ +} +#pragma region GameFramework +void AGasaPlayerController::Client_CheckIfOwnerReady() +{ + if (IsServer()) + return; + + UGasaGameInstance* GI = GetGameInstance(); + if ( ! GI->IsGameFrameworkInitialized() || PlayerState == NULL || ! IsValid(GetPawn())) + return; + + NetOwner_OnReady(); +} + +void AGasaPlayerController::NetOwner_OnReady() +{ + NetLog("Net Owner of controller is ready to play."); + if ( ! IsNetOwner() || bNetOwnerReady) + return; + + BP_NetOwner_OnReady(); + Event_NetOwner_OnReady.Broadcast(this); + bNetOwnerReady = true; + + AGasaGameState* GS = Cast(GetWorld()->GetGameState()); + if (GS) + GS->NotifyPlayerPawnReady(GetPawn()); + + if (IsClient()) + ServerRPC_R_NotifyOwningClientReady(); + + AGasaPlayerState* PS = GetPlayerState(); + APlayerCharacter* PlayerChar = GetPawn(); + { + PlayerChar->AbilitySystem = PS->AbilitySystem; + PlayerChar->Attributes = PS->Attributes; + PlayerChar->AbilitySystem->InitAbilityActorInfo(PS, this); + Cam->AttachToActor(PlayerChar, FAttachmentTransformRules::KeepRelativeTransform); + } +} + +void AGasaPlayerController::OnGameFrameworkInitialized() +{ + NetLog("Received game framework initialization."); + if (IsNetOwner()) + { + Server_SetNetOwner_GameFrameworkInitialized(); + Client_CheckIfOwnerReady(); + } + + AGasaGameState* GS = GetGameState(this); + NullGuard_DEV(GS, Log, "OnGameFrameworkInitialized: GS is null"); + GS->Event_OnSeamlessTravelStart.AddDynamic( this, & ThisClass::OnSeamlessTravelStart ); + + BP_OnGameFrameworkInitialized(); +} + +void AGasaPlayerController::OnPawnReady() +{ + NetLog("Player is ready."); + + // Originally: Super::OnPossess(PawnToPossess); + { + ChangeState(NAME_Playing); + if (bAutoManageActiveCameraTarget) + { + AutoManageActiveCameraTarget(GetPawn()); + ResetCameraMode(); + } + } + + // Override this and add your own conditions... + BP_OnPawnReady(); + + if (IsServer() && IsNetOwner()) + { + // The server host doesn't have to wait for the player state to replicate. + NetOwner_OnReady(); + } +} + +void AGasaPlayerController::Server_SetupOnPawnReadyBinds(APawn* PawnToBindTo) +{ + if (IsClient()) + return; +#if 0 + if (PawnToBindTo->IsA(AGasaPawn::StaticClass())) + { + Cast(PawnToBindTo)->Event_OnPawnReady.AddUniqueDynamic(this, & ThisClass::OnPawnReady); + } + else +#endif + if (PawnToBindTo->IsA(AGasaCharacter::StaticClass())) + { + Cast(PawnToBindTo)->Event_OnPawnReady.AddUniqueDynamic(this, & ThisClass::OnPawnReady); + } +} + +void AGasaPlayerController::Server_SetNetOwner_GameFrameworkInitialized() +{ + if (IsClient()) + { + ServerRPC_R_SetNetOwner_GameFrameworkInitialized(); + return; + } + + bNetOwner_GameFrameworkInitialized = true; + if (Event_NetOwner_OnGameFrameworkInitialized.IsBound()) + { + Event_NetOwner_OnGameFrameworkInitialized.Broadcast(this); + Event_NetOwner_OnGameFrameworkInitialized.Clear(); + } +} + +void AGasaPlayerController::ServerRPC_R_NotifyOwningClientReady_Implementation() +{ + NetLog("Net Owner Ready: Notified via RPC."); + + BP_NetOwner_OnReady(); + bNetOwnerReady = true; + Event_NetOwner_OnReady.Broadcast(this); + Event_NetOwner_OnReady.Clear(); + + AGasaGameState* GS = GetGameState(this); + + if (GS) + GS->NotifyPlayerPawnReady(GetPawn()); +} + +void AGasaPlayerController::ServerRPC_R_SetNetOwner_GameFrameworkInitialized_Implementation() +{ + Server_SetNetOwner_GameFrameworkInitialized(); +} +#pragma endregion GameFramework #pragma region Input void AGasaPlayerController::Move(FInputActionValue const& ActionValue) @@ -72,29 +216,109 @@ void AGasaPlayerController::Move(FInputActionValue const& ActionValue) #pragma endregion Input #pragma region PlayerController -void AGasaPlayerController::SpawnDefaultHUD() +bool AGasaPlayerController::CanRestartPlayer() { - Super::SpawnDefaultHUD(); + bool BaseCheck = + PlayerState && + !PlayerState->IsOnlyASpectator() && + HasClientLoadedCurrentWorld() && + PendingSwapConnection == NULL + ; + + return BaseCheck && bNetOwner_GameFrameworkInitialized; +} + +void AGasaPlayerController::ClientSetHUD_Implementation(TSubclassOf NewHUDClass) +{ + Super::ClientSetHUD_Implementation(NewHUDClass); + AGasaPlayerState* PS = GetPlayerState(); + AGasaHUD* HUD = GetHUD(); + FWidgetControllerData Data = { this, PS, PS->AbilitySystem, PS->Attributes }; + HUD->InitHostWidget(& Data); +} + +void AGasaPlayerController::ClientUpdateLevelStreamingStatus_Implementation(FName PackageName, bool bNewShouldBeLoaded, bool bNewShouldBeVisible, + bool bNewShouldBlockOnLoad, int32 LODIndex, FNetLevelVisibilityTransactionId TransactionId, bool bNewShouldBlockOnUnload) +{ + Super::ClientUpdateLevelStreamingStatus_Implementation(PackageName, bNewShouldBeLoaded, bNewShouldBeVisible, bNewShouldBlockOnLoad, LODIndex, + TransactionId, bNewShouldBlockOnUnload); + + NetLog("ClientUpdateLevelStreamingStatus"); + NetLog(FString("PackageName : ") + PackageName.ToString()); + NetLog(FString("NewShouldBeLoaded : ") + FString(bNewShouldBeLoaded ? "true" : "false")); + NetLog(FString("NewShouldBeVisible : ") + FString(bNewShouldBeVisible ? "true" : "false")); + NetLog(FString("bNewShouldBlockOnLoad : ") + FString(bNewShouldBlockOnLoad ? "true" : "false")); + NetLog(FString("bNewShouldBlockOnUnload: ") + FString(bNewShouldBlockOnUnload ? "true" : "false")); + NetLog(FString("LODIndex : ") + FString::FromInt( LODIndex )); } // TODO(Ed): We need to setup Net Slime... -void AGasaPlayerController::OnPossess(APawn* InPawn) +void AGasaPlayerController::OnPossess(APawn* PawnPossesed) { - Super::OnPossess(InPawn); - - Cam->AttachToActor(InPawn, FAttachmentTransformRules::KeepRelativeTransform); - - AGasaPlayerState* PS = GetPlayerState(); - AGasaCharacter* character = Cast(InPawn); - // Net Owner setup ability system - if (0) + // Super::OnPossess(PawnPossesed); { - character->AbilitySystem = PS->AbilitySystem; - character->Attributes = PS->Attributes; - character->AbilitySystem->InitAbilityActorInfo(PS, character); + if (PawnPossesed && (PlayerState == NULL || !PlayerState->IsOnlyASpectator()) ) + { + // ====================================================================Originally: Super::OnPossess(PawnToPossess); + const bool bNewPawn = (GetPawn() != PawnPossesed); + if (GetPawn() && bNewPawn) + UnPossess(); + + if (PawnPossesed->Controller != NULL) + PawnPossesed->Controller->UnPossess(); + + PawnPossesed->PossessedBy(this); + + // update rotation to match possessed pawn's rotation + SetControlRotation( PawnPossesed->GetActorRotation() ); + SetPawn(PawnPossesed); + + check(GetPawn() != NULL); + if (GetPawn() && GetPawn()->PrimaryActorTick.bStartWithTickEnabled) + GetPawn()->SetActorTickEnabled(true); + + INetworkPredictionInterface* NetworkPredictionInterface = GetPawn() + ? Cast(GetPawn()->GetMovementComponent()) + : nullptr; + if (NetworkPredictionInterface) + NetworkPredictionInterface->ResetPredictionData_Server(); + + AcknowledgedPawn = NULL; + + // Local PCs will have the Restart() triggered right away in ClientRestart (via PawnClientRestart()), but the server should call Restart() locally for remote PCs. + // We're really just trying to avoid calling Restart() multiple times. + if (!IsLocalPlayerController()) + GetPawn()->Restart(); + + ClientRestart(GetPawn()); + + + // Moved to: void AGasaPlayerController::OnPawnReady + #if 0 + ChangeState( NAME_Playing ); + if (bAutoManageActiveCameraTarget) + { + AutoManageActiveCameraTarget(GetPawn()); + ResetCameraMode(); + } + #endif + //==========================================================End of=================== Originally: Super::OnPossess(PawnToPossess); + + NetLog("OnPossess"); + Server_SetupOnPawnReadyBinds(PawnPossesed); + Event_OnPawnPossessed.Broadcast(); + } } } +void AGasaPlayerController::OnRep_Pawn() +{ + Super::OnRep_Pawn(); + + NetLog("OnRep_Pawn"); + Client_CheckIfOwnerReady(); +} + void AGasaPlayerController::OnUnPossess() { Super::OnUnPossess(); @@ -138,6 +362,11 @@ void AGasaPlayerController::PlayerTick(float DeltaTime) } } +void AGasaPlayerController::PostSeamlessTravel() +{ + Super::PostSeamlessTravel(); +} + void AGasaPlayerController::SetupInputComponent() { Super::SetupInputComponent(); @@ -148,12 +377,22 @@ void AGasaPlayerController::SetupInputComponent() EIC->BindAction(IA_Move, ETriggerEvent::Triggered, this, &ThisClass::Move); } } + +void AGasaPlayerController::SpawnDefaultHUD() +{ + Super::SpawnDefaultHUD(); +} #pragma endregion PlayerController #pragma region Actor void AGasaPlayerController::BeginPlay() { Super::BeginPlay(); + NetLog("BeginPlay"); + + UGasaGameInstance* GI = GetGameInstance(); + GI->Event_OnGameFrameworkInitialized.AddUniqueDynamic(this, & AGasaPlayerController::OnGameFrameworkInitialized); + GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::PlayerController); if (IsLocalController()) { @@ -209,4 +448,11 @@ void AGasaPlayerController::Tick(float DeltaSeconds) } #endif } + +void AGasaPlayerController::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + DOREPLIFETIME(AGasaPlayerController, bNetOwner_GameFrameworkInitialized); +} #pragma endregion Actor diff --git a/Project/Source/Gasa/Game/GasaPlayerController.h b/Project/Source/Gasa/Game/GasaPlayerController.h index 7fcf542..78c38a6 100644 --- a/Project/Source/Gasa/Game/GasaPlayerController.h +++ b/Project/Source/Gasa/Game/GasaPlayerController.h @@ -1,17 +1,26 @@ #pragma once #include "GasaCommon.h" +#include "GasaPlayerState.h" +#include "Networking/GasaNetLibrary.h" #include "Engine/Engine.h" #include "GameFramework/PlayerController.h" -#include "Networking/GasaNetLibrary.h" #include "GasaPlayerController.generated.h" +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnGasaPlayerControllerSig, AGasaPlayerController*, PC); + UCLASS(Blueprintable) class GASA_API AGasaPlayerController : public APlayerController { GENERATED_BODY() +protected: + friend void AGasaPlayerState::ClientInitialize(AController* NewOwner); public: + + UFUNCTION() + void OnSeamlessTravelStart(); + #pragma region Camera UPROPERTY(EditAnywhere, BlueprintReadWrite) TObjectPtr Cam; @@ -33,6 +42,55 @@ public: FORCEINLINE void Dehighlight() { SetHighlight(EHighlight::Disabled); }; #pragma endregion Highlighting #endif + +#pragma region GameFramework + UPROPERTY(BlueprintAssignable) + FOnGasaPlayerControllerSig Event_NetOwner_OnGameFrameworkInitialized; + + UPROPERTY(BlueprintAssignable) + FOnGasaPlayerControllerSig Event_NetOwner_OnReady; + + UPROPERTY(BlueprintAssignable) + FOnPawnSig Event_OnPawnPossessed; + + UPROPERTY(Replicated, VisibleAnywhere) + bool bNetOwner_GameFrameworkInitialized = false; + + UPROPERTY(VisibleAnywhere, Category = "Game Framework") + bool bNetOwnerReady = false; + + void Client_CheckIfOwnerReady(); + + UFUNCTION() + void NetOwner_OnReady(); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta = (DisplayName = "NetOwner: On Ready")) + void BP_NetOwner_OnReady(); + + UFUNCTION() + void OnGameFrameworkInitialized(); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "On Game Framework Initialized")) + void BP_OnGameFrameworkInitialized(); + + UFUNCTION() + void OnPawnReady(); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta=(DisplayName = "On Pawn Ready")) + void BP_OnPawnReady(); + + UFUNCTION() + void Server_SetupOnPawnReadyBinds(APawn* PawnToBindTo); + + UFUNCTION() + void Server_SetNetOwner_GameFrameworkInitialized(); + + UFUNCTION(Server, Reliable) + void ServerRPC_R_NotifyOwningClientReady(); + + UFUNCTION(Server, Reliable) + void ServerRPC_R_SetNetOwner_GameFrameworkInitialized(); +#pragma endregion GameFramework #pragma region Input UPROPERTY(VisibleAnywhere, BlueprintReadOnly) @@ -50,10 +108,6 @@ public: void Move(FInputActionValue const& ActionValue); #pragma endregion Input - AGasaPlayerController(); - - inline AGasaPlayerState* GetPlayerState(); - #pragma region NetSlime // NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } @@ -76,24 +130,35 @@ public: } #pragma endregion NetSlime + AGasaPlayerController(); + + inline AGasaPlayerState* GetPlayerState(); + #pragma region PlayerController - void SpawnDefaultHUD() override; - - void OnPossess(APawn* InPawn) override; - void OnUnPossess() override; - + bool CanRestartPlayer() override; + void ClientSetHUD_Implementation(TSubclassOf NewHUDClass) override; + void ClientUpdateLevelStreamingStatus_Implementation(FName PackageName, bool bNewShouldBeLoaded, bool bNewShouldBeVisible, bool bNewShouldBlockOnLoad, int32 LODIndex, FNetLevelVisibilityTransactionId TransactionId, bool bNewShouldBlockOnUnload) override; void PlayerTick(float DeltaTime) override; - + void PostSeamlessTravel() override; void SetupInputComponent() override; + void SpawnDefaultHUD() override; #pragma endregion PlayerController +#pragma region Controller + void OnPossess(APawn* InPawn) override; + void OnRep_Pawn() override; + void OnUnPossess() override; +#pragma endregion Controller + #pragma region Actor void BeginPlay() override; - void PostInitializeComponents() override; - void Tick(float DeltaSeconds) override; #pragma endregion Actor + +#pragma region UObject + void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; +#pragma endregion UObject }; namespace Gasa diff --git a/Project/Source/Gasa/Game/GasaPlayerState.cpp b/Project/Source/Gasa/Game/GasaPlayerState.cpp index c5ba382..c39b0e5 100644 --- a/Project/Source/Gasa/Game/GasaPlayerState.cpp +++ b/Project/Source/Gasa/Game/GasaPlayerState.cpp @@ -1,5 +1,8 @@ #include "GasaPlayerState.h" +#include "Networking/GasaNetLibrary_Inlines.h" +#include "GasaGameInstance.h" +#include "GasaPlayerController.h" #include "AbilitySystem/GasaAbilitySystemComponent.h" #include "AbilitySystem/GasaAttributeSet.h" @@ -16,3 +19,74 @@ AGasaPlayerState::AGasaPlayerState() // Replication NetUpdateFrequency = 100.f; } + +#pragma region GameFramework +void AGasaPlayerState::OnGameFrameworkInitialized() +{ + NetLog("Received game framework initialization."); + BP_OnGameFrameworkInitialized(); +} + +void AGasaPlayerState::OnNetOwnerReady(AGasaPlayerController* PC) +{ + BP_OnNetOwnerReady(); +} + +void AGasaPlayerState::Reset() +{ + Super::Reset(); + NetLog("Reset"); +} + +#pragma endregion GameFramework + +#pragma region PlayerState +void AGasaPlayerState::ClientInitialize(AController* NewOwner) +{ + Super::ClientInitialize(NewOwner); + NetLog("Client Initialization: This is the OnRep for player state."); + + AGasaPlayerController* GasaPC = Cast(NewOwner); + if (GasaPC) + GasaPC->Client_CheckIfOwnerReady(); +} +#pragma endregion PlayerState + +#pragma region Actor +void AGasaPlayerState::BeginPlay() +{ + NetLog("Begin Play"); + Super::BeginPlay(); + + UGasaGameInstance* GI = GetGameInstance(); + GI->Event_OnGameFrameworkInitialized.AddDynamic(this, & ThisClass::OnGameFrameworkInitialized); + GI->NotifyGameFrameworkClassReady(EGameFrameworkClassFlag::PlayerState); +} + +void AGasaPlayerState::PostInitializeComponents() +{ + Super::PostInitializeComponents(); + NetLog("Post Initialization"); +} + +void AGasaPlayerState::RegisterPlayerWithSession(bool bWasFromInvite) +{ + Super::RegisterPlayerWithSession(bWasFromInvite); + NetLog("RegisterPlayerWithSession"); + + if (IsServer() && GetInstigatorController()) + { + AGasaPlayerController* PC = Cast(GetInstigatorController()); + PC->Event_NetOwner_OnReady.AddDynamic(this, & ThisClass::OnNetOwnerReady); + } +} +#pragma endregion Actor + +#pragma region UObject +void AGasaPlayerState::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const +{ + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + + +} +#pragma endregion UObject diff --git a/Project/Source/Gasa/Game/GasaPlayerState.h b/Project/Source/Gasa/Game/GasaPlayerState.h index 2c498e8..b08363f 100644 --- a/Project/Source/Gasa/Game/GasaPlayerState.h +++ b/Project/Source/Gasa/Game/GasaPlayerState.h @@ -15,6 +15,8 @@ class GASA_API AGasaPlayerState : public APlayerState { GENERATED_BODY() public: + AGasaPlayerState(); + #pragma region Ability System UPROPERTY(EditAnywhere, Category="Ability System") bool bAutoAbilitySystem; @@ -25,9 +27,31 @@ public: UPROPERTY(EditAnywhere, Category="Ability System") TObjectPtr Attributes; #pragma endregion Ability System - - AGasaPlayerState(); +#pragma region GameFramework + UFUNCTION() + void OnGameFrameworkInitialized(); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta = (DisplayName = "On Game Framework Initialized")) + void BP_OnGameFrameworkInitialized(); + + UFUNCTION() + void OnNetOwnerReady(AGasaPlayerController* PC); + + UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, meta = (DisplayName = "On Net Owner Ready")) + void BP_OnNetOwnerReady(); +#pragma endregion GameFramework + +#pragma region Networking +#if 0 + UPROPERTY(ReplicatedUsing = Client_OnRep_GasaID) + int32 GasaID = INDEX_NONE; + + UFUNCTION() + void Client_OnRep_GasaID; +#endif +#pragma endregion Networking + #pragma region NetSlime // NetSlime interface is generated by GasaGen/GasaGen_NetSlime.cpp FORCEINLINE ENetworkMode GetNetworkMode() const { return Gasa::GetNetworkMode( this ); } @@ -55,7 +79,18 @@ public: FORCEINLINE UAbilitySystemComponent* GetAbilitySystemComponent() const override { return AbilitySystem; } #pragma endregion IAbilitySystem -// #pragma region -// -// #pragma endregion +#pragma region PlayerState + void ClientInitialize(AController* C) override; +#pragma endregion PlayerState + +#pragma region Actor + void BeginPlay() override; + void PostInitializeComponents() override; + void RegisterPlayerWithSession(bool bWasFromInvite) override; + void Reset() override; +#pragma endregion Actor + +#pragma region UObject + void GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const override; +#pragma endregion UObject }; diff --git a/Project/Source/Gasa/Gasa.Build.cs b/Project/Source/Gasa/Gasa.Build.cs index 2de60ae..fb8db38 100644 --- a/Project/Source/Gasa/Gasa.Build.cs +++ b/Project/Source/Gasa/Gasa.Build.cs @@ -48,6 +48,7 @@ public class Gasa : ModuleRules "Core", "AIModule", + "CoreOnline", "CoreUObject", "DeveloperSettings", "Engine", diff --git a/Project/Source/Gasa/GasaColdHeadersPCH.h b/Project/Source/Gasa/GasaColdHeadersPCH.h index 3e37bb5..e0a978f 100644 --- a/Project/Source/Gasa/GasaColdHeadersPCH.h +++ b/Project/Source/Gasa/GasaColdHeadersPCH.h @@ -35,8 +35,8 @@ #include "Game/GasaViewport.h" // Networking -// #include "Networking/GasaNetLibrary.h" -// #include "Networking/GasaNetLibrary_Inlines.h" +#include "Networking/GasaNetLibrary.h" +#include "Networking/GasaNetLibrary_Inlines.h" // UI // #include "UI/GasaCanvas.h" diff --git a/Project/Source/Gasa/GasaCommon.h b/Project/Source/Gasa/GasaCommon.h index 3662cc1..3070e7c 100644 --- a/Project/Source/Gasa/GasaCommon.h +++ b/Project/Source/Gasa/GasaCommon.h @@ -2,6 +2,8 @@ #include "GasaEngineMinimal.h" +#include "GasaCommon.generated.h" + #define global #define internal static #define local_persist static @@ -18,14 +20,19 @@ #pragma region Engine Forwards struct FInputActionValue; struct FOnAttributeChangeData; +struct FReplicationFlags; class AActor; +class APawn; class APostProcessVolume; +class FOutBunch; + class IAbilitySystemInterface; class UAbilitySystemComponent; class UAbilitySystemInterface; +class UActorChannel; class UAttributeSet; class UCameraComponent; class UGameplayEffect; @@ -189,3 +196,12 @@ namespace Gasa constexpr float _480Hz = .002f; } #pragma endregion Timing + +#pragma region Delegates +DECLARE_MULTICAST_DELEGATE(FOnTravelDelegate); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnTravelSig); +DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnPawnSig); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPlayerCharacterReadySig, APlayerCharacter*, Character); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnPawnReadySig, APawn*, Pawn); +#pragma endregion Delegates + diff --git a/Project/Source/Gasa/GasaEngineMinimal.h b/Project/Source/Gasa/GasaEngineMinimal.h index 85194d7..f66c423 100644 --- a/Project/Source/Gasa/GasaEngineMinimal.h +++ b/Project/Source/Gasa/GasaEngineMinimal.h @@ -21,140 +21,5 @@ #include "Misc/VarArgs.h" #include "Logging/LogVerbosity.h" #include "UObject/ObjectMacros.h" - -// #include "Misc/OutputDevice.h" -// #include "HAL/PlatformCrt.h" -// #include "HAL/PlatformMisc.h" -// #include "Misc/AssertionMacros.h" -// #include "Templates/IsPointer.h" -// #include "HAL/PlatformMemory.h" -// #include "HAL/PlatformAtomics.h" -// #include "Misc/Exec.h" -// #include "HAL/MemoryBase.h" -// #include "HAL/UnrealMemory.h" -// #include "Templates/IsArithmetic.h" -// #include "Templates/AndOrNot.h" -// #include "Templates/IsPODType.h" -// #include "Templates/IsUECoreType.h" -// #include "Templates/IsTriviallyCopyConstructible.h" -// #include "Templates/UnrealTypeTraits.h" -// #include "Templates/EnableIf.h" -// #include "Templates/RemoveReference.h" -// #include "Templates/IntegralConstant.h" -// #include "Templates/IsClass.h" -// #include "Templates/TypeCompatibleBytes.h" -// #include "Traits/IsContiguousContainer.h" -// #include "Templates/UnrealTemplate.h" -// #include "Math/NumericLimits.h" -// #include "HAL/PlatformMath.h" -// #include "Templates/IsTriviallyCopyAssignable.h" -// #include "Templates/IsTriviallyDestructible.h" -// #include "Templates/MemoryOps.h" -// #include "Containers/ContainerAllocationPolicies.h" -// #include "Templates/IsEnumClass.h" -// #include "HAL/PlatformProperties.h" -// #include "Misc/EngineVersionBase.h" -// #include "Internationalization/TextNamespaceFwd.h" -// #include "Serialization/Archive.h" -// #include "Templates/Less.h" -// #include "Templates/Sorting.h" -// #include "Misc/Char.h" -// #include "GenericPlatform/GenericPlatformStricmp.h" -// #include "GenericPlatform/GenericPlatformString.h" -// #include "HAL/PlatformString.h" -// #include "Misc/CString.h" -// #include "Misc/Crc.h" -// #include "Math/UnrealMathUtility.h" -// #include "Containers/UnrealString.h" -// #include "Containers/Array.h" -// #include "Misc/FrameNumber.h" -// #include "Misc/Timespan.h" -// #include "Containers/StringConv.h" -// #include "UObject/UnrealNames.h" -// #include "UObject/NameTypes.h" -// #include "Misc/Parse.h" -// #include "Templates/AlignmentTemplates.h" -// #include "Misc/StructBuilder.h" -// #include "Templates/Decay.h" -// #include "Templates/PointerIsConvertibleFromTo.h" -// #include "Templates/Invoke.h" -// #include "Templates/Function.h" -// #include "Templates/TypeHash.h" - -// #include "Containers/ScriptArray.h" -// #include "Containers/BitArray.h" -// #include "Containers/SparseArray.h" -// #include "Containers/Set.h" - -// #include "Algo/Reverse.h" -// #include "Containers/Map.h" -// #include "Math/IntPoint.h" -// #include "Math/IntVector.h" - -// #include "Logging/LogCategory.h" -// #include "Logging/LogMacros.h" - -// #include "Math/Vector2D.h" -// #include "Math/IntRect.h" -// #include "Misc/ByteSwap.h" -// #include "Containers/EnumAsByte.h" -// #include "HAL/PlatformTLS.h" -// #include "CoreGlobals.h" - -// #include "Templates/SharedPointer.h" -// #include "Internationalization/CulturePointer.h" -// #include "UObject/WeakObjectPtrTemplates.h" -// #include "Delegates/DelegateSettings.h" -// #include "Delegates/IDelegateInstance.h" -// #include "Delegates/DelegateBase.h" -// #include "Delegates/MulticastDelegateBase.h" -// #include "Delegates/IntegerSequence.h" -// #include "Templates/Tuple.h" -// #include "UObject/ScriptDelegates.h" -// #include "Delegates/Delegate.h" -// #include "Internationalization/TextLocalizationManager.h" -// #include "Misc/Optional.h" -// #include "Templates/IsArray.h" -// #include "Templates/RemoveExtent.h" -// #include "Templates/UniquePtr.h" -// #include "Internationalization/Text.h" -// #include "Templates/UniqueObj.h" -// #include "Internationalization/Internationalization.h" -// #include "Math/Vector.h" -// #include "Math/Vector4.h" -// #include "Math/VectorRegister.h" -// #include "Math/TwoVectors.h" -// #include "Math/Edge.h" -// #include "UObject/ObjectVersion.h" -// #include "Math/CapsuleShape.h" -// #include "Math/Rotator.h" -// #include "Misc/DateTime.h" -// #include "Math/RangeBound.h" -// #include "Misc/AutomationEvent.h" -// #include "Math/Range.h" -// #include "Math/RangeSet.h" -// #include "Math/Interval.h" -// #include "Math/Box.h" -// #include "Math/Box2D.h" -// #include "Math/BoxSphereBounds.h" -// #include "Math/OrientedBox.h" -// #include "Math/Axis.h" -// #include "Math/Matrix.h" -// #include "Math/RotationTranslationMatrix.h" -// #include "Math/RotationAboutPointMatrix.h" -// #include "Math/ScaleRotationTranslationMatrix.h" -// #include "Math/RotationMatrix.h" -// #include "Math/Quat.h" -// #include "Math/PerspectiveMatrix.h" -// #include "Math/OrthoMatrix.h" -// #include "Math/TranslationMatrix.h" -// #include "Math/QuatRotationTranslationMatrix.h" -// #include "Math/InverseRotationMatrix.h" -// #include "Math/ScaleMatrix.h" -// #include "Math/MirrorMatrix.h" -// #include "Math/ClipProjectionMatrix.h" -// #include "Math/Float32.h" -// #include "Math/Float16.h" -// #include "Math/Transform.h" -// #include "Math/ConvexHull2d.h" -// #include "Math/UnrealMath.h" \ No newline at end of file +#include "Delegates/Delegate.h" +#include "Delegates/DelegateCombinations.h" diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary.cpp b/Project/Source/Gasa/Networking/GasaNetLibrary.cpp index 3741f56..ddb1cc2 100644 --- a/Project/Source/Gasa/Networking/GasaNetLibrary.cpp +++ b/Project/Source/Gasa/Networking/GasaNetLibrary.cpp @@ -1,67 +1,94 @@ #include "GasaNetLibrary.h" #include "GasaNetLibrary_Inlines.h" +#include "GasaObject.h" + +#include "Kismet/KismetMathLibrary.h" +#include "Kismet/KismetSystemLibrary.h" DEFINE_LOG_CATEGORY(LogGasaNet); -void NetLog( UObject const* Context, FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log - , FLogCategoryBase& Category = LogGasaNet - , bool DumpStack = false - , int32 Line = __builtin_LINE() - , const ANSICHAR* File = __builtin_FILE() - , const ANSICHAR* Func = __builtin_FUNCTION() ) +namespace Gasa { -#if !UE_BUILD_SHIPPING && !NO_LOGGING - ELogVerbosity::Type EngineVerbosity = (ELogVerbosity::Type) Verbosity; - if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) - return; - if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > Category.GetVerbosity()) - return; - if ( Category.IsSuppressed(EngineVerbosity)) - return; - - AActor const* Actor = nullptr; - FString ActorLevel; + void DrawNetCullingSphere(const UObject* Context, float Duration, float Thickness) { - if (Context != nullptr) - { - if (Context->GetClass()->IsChildOf(AActor::StaticClass())) - Actor = Cast(Context); - else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass())) - Actor = Cast(Context)->GetOwner(); - // Its assumed that all GasaObjects have an outer actor - else if (Context->IsA(UGasaObject::StaticClass())) - Actor = Cast(Context->GetOuter()); - } - if (Actor) - { - if (Actor->HasLocalNetOwner()) - ActorLevel = TEXT("Net Owner"); - - else if (Actor->HasAuthority()) - ActorLevel = TEXT("Server Authorized"); - - else - ActorLevel = TEXT("No Authority"); - } - else - ActorLevel = TEXT("Local"); + const AActor* actor = nullptr; + + if (Context->IsA(UGasaObject::StaticClass())) + actor = Cast(Context->GetOuter()); + + else if (Context->IsA(AActor::StaticClass())) + actor = Cast(Context); + + if (actor) + UKismetSystemLibrary::DrawDebugSphere(actor + , actor->GetActorLocation() + , UKismetMathLibrary::Sqrt(actor->NetCullDistanceSquared) * 2 + , 12 + , FLinearColor(FColor::Emerald) + , Duration + , Thickness); } + + void NetLog( UObject const* Context, FString Message, EGasaVerbosity Verbosity + , FLogCategoryBase& Category + , bool DumpStack + , int32 Line + , const ANSICHAR* File + , const ANSICHAR* Func ) + { +#if !UE_BUILD_SHIPPING && !NO_LOGGING + ELogVerbosity::Type EngineVerbosity = (ELogVerbosity::Type) Verbosity; + if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > ELogVerbosity::COMPILED_IN_MINIMUM_VERBOSITY) + return; + if ((EngineVerbosity & ELogVerbosity::VerbosityMask) > Category.GetVerbosity()) + return; + if ( Category.IsSuppressed(EngineVerbosity)) + return; - FString NetMode = FString::Printf(TEXT("%-16s"), * GetNetworkModeStr(Context)); - ActorLevel = FString::Printf(TEXT("%-18s"), * ActorLevel); - FString Name = FString::Printf(TEXT("%-40s"), * Context->GetName()); - FString FullMsg = NetMode + " " + ActorLevel + " " + Name + " : " + Message; - - static UE::Logging::Private::FStaticBasicLogDynamicData LOG_Dynamic; - static UE::Logging::Private::FStaticBasicLogRecord - LOG_Static(TEXT("%s -- %hs %hs(%d)"), File, Line, EngineVerbosity, LOG_Dynamic); + AActor const* Actor = nullptr; + FString ActorLevel; + { + if (Context != nullptr) + { + if (Context->GetClass()->IsChildOf(AActor::StaticClass())) + Actor = Cast(Context); + else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass())) + Actor = Cast(Context)->GetOwner(); + // Its assumed that all GasaObjects have an outer actor + else if (Context->IsA(UGasaObject::StaticClass())) + Actor = Cast(Context->GetOuter()); + } + if (Actor) + { + if (Actor->HasLocalNetOwner()) + ActorLevel = TEXT("Net Owner"); + + else if (Actor->HasAuthority()) + ActorLevel = TEXT("Server Authorized"); + + else + ActorLevel = TEXT("No Authority"); + } + else + ActorLevel = TEXT("Local"); + } - SET_WARN_COLOR(COLOR_PURPLE) + FString NetMode = FString::Printf(TEXT("%-16s"), * GetNetworkModeStr(Context)); + ActorLevel = FString::Printf(TEXT("%-18s"), * ActorLevel); + FString Name = FString::Printf(TEXT("%-40s"), * Context->GetName()); + FString FullMsg = NetMode + " " + ActorLevel + " " + Name + " : " + Message; - if (DumpStack) - FDebug::DumpStackTraceToLog(EngineVerbosity); - BasicLog(Category, &LOG_Static, * FullMsg, File, Func, Line); + static UE::Logging::Private::FStaticBasicLogDynamicData LOG_Dynamic; + static UE::Logging::Private::FStaticBasicLogRecord + LOG_Static(TEXT("%s -- %hs %hs(%d)"), File, Line, EngineVerbosity, LOG_Dynamic); + + // SET_WARN_COLOR(COLOR_PURPLE) - CLEAR_WARN_COLOR() -#endif -} \ No newline at end of file + if (DumpStack) + FDebug::DumpStackTraceToLog(EngineVerbosity); + BasicLog(Category, &LOG_Static, * FullMsg, File, Func, Line); + + // CLEAR_WARN_COLOR() + #endif + } +} diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary.h b/Project/Source/Gasa/Networking/GasaNetLibrary.h index 98a9a33..3a356c0 100644 --- a/Project/Source/Gasa/Networking/GasaNetLibrary.h +++ b/Project/Source/Gasa/Networking/GasaNetLibrary.h @@ -8,6 +8,34 @@ DECLARE_LOG_CATEGORY_EXTERN(LogGasaNet, Log, All); +#define NullGuard( Object_, Logger_, Message_ ) \ +do { \ + if ( ! IsValid(Object_) ) \ + { \ + Logger_( (Message_) , ELogV::Error ); \ + ensure( IsValid(Object_) ); \ + return; \ + } \ +} while (0) + +#define NetGuard( Condition_, Logger_, Message_ ) \ +do { \ + if ( Condition_ ) \ + { \ + Logger_( (Message_) , ELogV::Error ); \ + ensure( Condition_ ); \ + return; \ + } \ +} while (0) + +#if UE_BUILD_DEVELOPMENT +# define NullGuard_DEV NullGuard +# define NetGuard_DEV NetGuard +#else +# define NullGuard_DEV( Object_, Logger_, Message_) +# define NetGuard_DEV( Object_, Logger_, Message_) +#endif + UENUM(BlueprintType) enum class ENetworkMode : uint8 { @@ -39,13 +67,11 @@ namespace Gasa bool IsListenServer(UObject const* Context); bool IsNetOwner(UObject const* Context); - bool IsNetOwner(UGasaObject const* Context); bool IsNetOwner(AActor const* Context); bool IsServer(UObject const* Context); bool IsSimulatedProxy(UObject const* Context); - bool IsSimulatedProxy(UGasaObject const* Context); bool IsSimulatedProxy(AActor const* Context); void NetLog( UObject const* Context, FString Message, EGasaVerbosity Verbosity = EGasaVerbosity::Log @@ -56,6 +82,7 @@ namespace Gasa , const ANSICHAR* Func = __builtin_FUNCTION() ); bool ServerAuthorized(UObject const* Context); - bool ServerAuthorized(UGasaObject const* Context); bool ServerAuthorized(AActor const* Context); } + +#include "GasaNetLibrary_Inlines.h" diff --git a/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h b/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h index c2a621d..84c3aa7 100644 --- a/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h +++ b/Project/Source/Gasa/Networking/GasaNetLibrary_Inlines.h @@ -1,35 +1,12 @@ -#include "GasaNetLibrary.h" -#include "GasaObject.h" +#pragma once +#include "GasaNetLibrary.h" #include "Engine/NetDriver.h" -#include "Game/GasaGameMode.h" -#include "Kismet/KismetMathLibrary.h" -#include "Kismet/KismetSystemLibrary.h" +#include "Engine/World.h" namespace Gasa { // TODO(Ed): Profile these... - inline - void DrawNetCullingSphere(const UObject* Context, float Duration, float Thickness) - { - const AActor* actor = nullptr; - - if (Context->IsA(UGasaObject::StaticClass())) - actor = Cast(Context->GetOuter()); - - else if (Context->IsA(AActor::StaticClass())) - actor = Cast(Context); - - if (actor) - UKismetSystemLibrary::DrawDebugSphere(actor - , actor->GetActorLocation() - , UKismetMathLibrary::Sqrt(actor->NetCullDistanceSquared) * 2 - , 12 - , FLinearColor(FColor::Emerald) - , Duration - , Thickness); - } - inline ENetworkMode GetNetworkMode(UObject const* Context) { @@ -110,8 +87,6 @@ namespace Gasa else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass())) Actor = Cast(Context)->GetOwner(); // Its assumed that all GasaObjects have an outer actor - else if (Context->IsA(UGasaObject::StaticClass())) - Actor = Cast(Context->GetOuter()); else { UObject const* Outermost = Context->GetOutermostObject(); @@ -127,23 +102,7 @@ namespace Gasa bool Result = Actor->HasLocalNetOwner(); return Result; } - - inline - bool IsNetOwner(UGasaObject const* Context) - { - if (Context == nullptr || Context->GetWorld() == nullptr) - return false; - - AActor const* Actor = Cast(Context->GetOuter()); - if (Actor == nullptr) - { - Log("Could not get actor reference", ELogV::Warning, LogGasaNet); - return false; - } - bool Result = Actor->HasLocalNetOwner(); - return Result; - } - + inline bool IsNetOwner(AActor const* Actor) { @@ -177,8 +136,6 @@ namespace Gasa else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass())) Actor = Cast(Context)->GetOwner(); // Its assumed that all GasaObjects have an outer actor - else if (Context->IsA(UGasaObject::StaticClass())) - Actor = Cast(Context->GetOuter()); else { UObject const* Outermost = Context->GetOutermostObject(); @@ -194,22 +151,6 @@ namespace Gasa bool Result = Actor->GetLocalRole() == ENetRole::ROLE_SimulatedProxy; return Result; } - - inline - bool IsSimulatedProxy(UGasaObject const* Context) - { - if (Context == nullptr || Context->GetWorld() == nullptr) - return false; - - AActor const* Actor = Cast(Context->GetOuter()); - if (Actor == nullptr) - { - Log("Could not get actor reference", ELogV::Warning, LogGasaNet); - return false; - } - bool Result = Actor->GetLocalRole() == ENetRole::ROLE_SimulatedProxy; - return Result; - } inline bool IsSimulatedProxy(AActor const* Actor) @@ -233,8 +174,6 @@ namespace Gasa else if (Context->GetClass()->IsChildOf(UActorComponent::StaticClass())) Actor = Cast(Context)->GetOwner(); // Its assumed that all GasaObjects have an outer actor - else if (Context->IsA(UGasaObject::StaticClass())) - Actor = Cast(Context->GetOuter()); else { UObject const* Outermost = Context->GetOutermostObject(); @@ -251,22 +190,6 @@ namespace Gasa return Result; } - inline - bool ServerAuthorized(UGasaObject const* Context) - { - if (Context == nullptr || Context->GetWorld() == nullptr) - return false; - - AActor const* Actor = Cast(Context->GetOuter()); - if (Actor == nullptr) - { - Log("Could not get actor reference", ELogV::Warning, LogGasaNet); - return false; - } - bool Result = Actor->HasAuthority(); - return Result; - } - inline bool ServerAuthorized(AActor const* Actor) {