Files
UnrealEngine/Engine/Source/Runtime/GameplayDebugger/Private/GameplayDebuggerPlayerManager.cpp
2025-05-18 13:04:45 +08:00

384 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "GameplayDebuggerPlayerManager.h"
#include "Engine/World.h"
#include "Components/InputComponent.h"
#include "GameplayDebuggerCategoryReplicator.h"
#include "GameplayDebuggerLocalController.h"
#include "Engine/DebugCameraController.h"
#include "Engine/InputDelegateBinding.h"
#include "GameplayDebuggerConfig.h"
#include "UnrealEngine.h"
#include "Engine/LocalPlayer.h"
#include "Net/UnrealNetwork.h"
#include "GameFramework/GameModeBase.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(GameplayDebuggerPlayerManager)
AGameplayDebuggerPlayerManager::AGameplayDebuggerPlayerManager(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
PrimaryActorTick.bCanEverTick = true;
PrimaryActorTick.bAllowTickOnDedicatedServer = true;
PrimaryActorTick.bTickEvenWhenPaused = true;
PrimaryActorTick.bStartWithTickEnabled = false;
PrimaryActorTick.TickInterval = 0.5f;
#if WITH_EDITOR
SetIsTemporarilyHiddenInEditor(true);
#endif
#if WITH_EDITORONLY_DATA
bHiddenEdLevel = true;
bHiddenEdLayer = true;
bHiddenEd = true;
bEditable = false;
#endif
bIsLocal = false;
bInitialized = false;
bEditorTimeTick = false;
}
void AGameplayDebuggerPlayerManager::PostInitProperties()
{
Super::PostInitProperties();
if (HasAnyFlags(RF_ClassDefaultObject) == false)
{
if (InputComponent == nullptr)
{
// create an InputComponent object so that the level script actor can bind key events
InputComponent = NewObject<UInputComponent>(this, TEXT("GameplayDebug_Input"));
InputComponent->RegisterComponent();
}
PRAGMA_DISABLE_DEPRECATION_WARNINGS
if (UInputDelegateBinding::SupportsInputDelegate(GetClass()))
PRAGMA_ENABLE_DEPRECATION_WARNINGS
{
UInputDelegateBinding::BindInputDelegates(GetClass(), InputComponent);
}
#if WITH_EDITORONLY_DATA
const UWorld* World = GetWorld();
bEditorTimeTick = (World != nullptr) && (World->IsEditorWorld() == true) && (World->IsGameWorld() == false);
#endif // WITH_EDITORONLY_DATA
}
}
void AGameplayDebuggerPlayerManager::BeginPlay()
{
Super::BeginPlay();
UWorld* World = GetWorld();
check(World);
const ENetMode NetMode = World->GetNetMode();
bHasAuthority = FGameplayDebuggerUtils::IsAuthority(World);
bIsLocal = (NetMode != NM_DedicatedServer);
bInitialized = true;
if (bHasAuthority)
{
UpdateAuthReplicators();
SetActorTickEnabled(true);
}
for (int32 Idx = 0; Idx < PendingRegistrations.Num(); Idx++)
{
RegisterReplicator(*PendingRegistrations[Idx]);
}
PendingRegistrations.Empty();
FNetworkReplayDelegates::OnScrubTeardown.AddUObject(this, &ThisClass::OnReplayScrubTeardown);
FGameModeEvents::GameModeLogoutEvent.AddUObject(this, &ThisClass::OnGameModeLogout);
}
void AGameplayDebuggerPlayerManager::BeginDestroy()
{
Super::BeginDestroy();
// Make sure the tick is completely disabled
SetTickableTickType(ETickableTickType::Never);
}
void AGameplayDebuggerPlayerManager::EndPlay(const EEndPlayReason::Type Reason)
{
Super::EndPlay(Reason);
for (int32 Idx = 0; Idx < PlayerData.Num(); Idx++)
{
FGameplayDebuggerPlayerData& TestData = PlayerData[Idx];
if (IsValid(TestData.Controller))
{
TestData.Controller->Cleanup();
TestData.Controller = nullptr;
}
}
FNetworkReplayDelegates::OnScrubTeardown.RemoveAll(this);
FGameModeEvents::GameModeLogoutEvent.RemoveAll(this);
}
void AGameplayDebuggerPlayerManager::Init()
{
#if WITH_EDITOR
UWorld* World = GetWorld();
if (World != nullptr && World->WorldType == EWorldType::Editor && (GetDefault<UGameplayDebuggerUserSettings>()->bEnableGameplayDebuggerInEditor))
{
bHasAuthority = true;
bIsLocal = true;
bInitialized = true;
AGameplayDebuggerCategoryReplicator* Replicator = World->SpawnActorDeferred<AGameplayDebuggerCategoryReplicator>(AGameplayDebuggerCategoryReplicator::StaticClass(), FTransform::Identity);
Replicator->SetReplicatorOwner(nullptr);
Replicator->FinishSpawning(FTransform::Identity, true);
SetActorTickEnabled(true);
EditorWorldData.Replicator = Replicator;
Replicator->InitForEditor();
EditorWorldData.Controller = NewObject<UGameplayDebuggerLocalController>(this, TEXT("GameplayDebug_Controller_Editor"));
EditorWorldData.Controller->Initialize(*Replicator, *this);
}
#endif // WITH_EDITOR
}
void AGameplayDebuggerPlayerManager::TickActor(float DeltaTime, enum ELevelTick TickType, FActorTickFunction& ThisTickFunction)
{
Super::TickActor(DeltaTime, TickType, ThisTickFunction);
UpdateAuthReplicators();
};
void AGameplayDebuggerPlayerManager::UpdateAuthReplicators()
{
UWorld* World = GetWorld();
for (int32 Idx = PlayerData.Num() - 1; Idx >= 0; Idx--)
{
FGameplayDebuggerPlayerData& TestData = PlayerData[Idx];
if (IsValid(TestData.Replicator) && TestData.Replicator->GetReplicationOwner().IsRemote())
{
continue;
}
if (!IsValid(TestData.Replicator) || !IsValid(TestData.Replicator->GetReplicationOwner()))
{
if (IsValid(TestData.Replicator))
{
World->DestroyActor(TestData.Replicator);
}
if (IsValid(TestData.Controller))
{
TestData.Controller->Cleanup();
}
PlayerData.RemoveAt(Idx, EAllowShrinking::No);
}
}
#if WITH_GAMEPLAY_DEBUGGER
for (FConstPlayerControllerIterator It = World->GetPlayerControllerIterator(); It; It++)
{
APlayerController* TestPC = It->Get();
if (TestPC && !TestPC->IsA<ADebugCameraController>())
{
const bool bNeedsReplicator = (GetReplicator(*TestPC) == nullptr);
if (bNeedsReplicator)
{
AGameplayDebuggerCategoryReplicator* Replicator = World->SpawnActorDeferred<AGameplayDebuggerCategoryReplicator>(AGameplayDebuggerCategoryReplicator::StaticClass(), FTransform::Identity);
Replicator->SetReplicatorOwner(TestPC);
Replicator->FinishSpawning(FTransform::Identity, true);
}
}
}
#endif // WITH_GAMEPLAY_DEBUGGER
PrimaryActorTick.TickInterval = PlayerData.Num() ? 5.0f : 0.5f;
}
void AGameplayDebuggerPlayerManager::RegisterReplicator(AGameplayDebuggerCategoryReplicator& Replicator)
{
if (!bInitialized)
{
PendingRegistrations.Add(&Replicator);
return;
}
// keep all player related objects together for easy access and GC
FGameplayDebuggerPlayerData NewData;
NewData.Replicator = &Replicator;
#if WITH_GAMEPLAY_DEBUGGER_MENU
if (bIsLocal)
{
APlayerController* OwnerPC = Replicator.GetReplicationOwner();
NewData.InputComponent = OwnerPC ? NewObject<UInputComponent>(OwnerPC, TEXT("GameplayDebug_Input")) : ToRawPtr(InputComponent);
check(NewData.InputComponent);
NewData.InputComponent->Priority = -1;
NewData.Controller = NewObject<UGameplayDebuggerLocalController>(OwnerPC ? OwnerPC : (AActor*)this, TEXT("GameplayDebug_Controller"));
NewData.Controller->Initialize(Replicator, *this);
NewData.Controller->BindInput(*NewData.InputComponent);
if (OwnerPC)
{
OwnerPC->PushInputComponent(NewData.InputComponent);
}
}
else
{
NewData.Controller = nullptr;
NewData.InputComponent = nullptr;
}
#endif // WITH_GAMEPLAY_DEBUGGER_MENU
PlayerData.Add(NewData);
}
void AGameplayDebuggerPlayerManager::RefreshInputBindings(AGameplayDebuggerCategoryReplicator& Replicator)
{
#if WITH_GAMEPLAY_DEBUGGER_MENU
for (int32 Idx = 0; Idx < PlayerData.Num(); Idx++)
{
FGameplayDebuggerPlayerData& TestData = PlayerData[Idx];
if (TestData.Replicator == &Replicator)
{
TestData.InputComponent->ClearActionBindings();
TestData.InputComponent->ClearBindingValues();
TestData.InputComponent->KeyBindings.Empty();
TestData.Controller->BindInput(*TestData.InputComponent);
}
}
#endif // WITH_GAMEPLAY_DEBUGGER_MENU
}
AGameplayDebuggerCategoryReplicator* AGameplayDebuggerPlayerManager::GetReplicator(const APlayerController& OwnerPC) const
{
const FGameplayDebuggerPlayerData* DataPtr = GetPlayerData(OwnerPC);
return DataPtr ? DataPtr->Replicator : nullptr;
}
UInputComponent* AGameplayDebuggerPlayerManager::GetInputComponent(const APlayerController& OwnerPC) const
{
const FGameplayDebuggerPlayerData* DataPtr = GetPlayerData(OwnerPC);
return DataPtr ? DataPtr->InputComponent : nullptr;
}
UGameplayDebuggerLocalController* AGameplayDebuggerPlayerManager::GetLocalController(const APlayerController& OwnerPC) const
{
const FGameplayDebuggerPlayerData* DataPtr = GetPlayerData(OwnerPC);
return DataPtr ? DataPtr->Controller : nullptr;
}
const FGameplayDebuggerPlayerData* AGameplayDebuggerPlayerManager::GetPlayerData(const APlayerController& OwnerPC) const
{
for (int32 Idx = 0; Idx < PlayerData.Num(); Idx++)
{
const FGameplayDebuggerPlayerData& TestData = PlayerData[Idx];
if (TestData.Replicator && TestData.Replicator->GetReplicationOwner() == &OwnerPC)
{
return &TestData;
}
}
return nullptr;
}
void AGameplayDebuggerPlayerManager::GetViewPoint(const APlayerController& OwnerPC, FVector& OutViewLocation, FVector& OutViewDirection)
{
UWorld* World = OwnerPC.GetWorld();
FVector CameraLocation = FVector::ZeroVector;
FRotator CameraRotation = FRotator::ZeroRotator;
if (OwnerPC.Player)
{
// normal game
OwnerPC.GetPlayerViewPoint(CameraLocation, CameraRotation);
}
else
{
// spectator mode
for (FLocalPlayerIterator It(GEngine, World); It; ++It)
{
ADebugCameraController* SpectatorPC = Cast<ADebugCameraController>(It->PlayerController);
if (SpectatorPC)
{
SpectatorPC->GetPlayerViewPoint(CameraLocation, CameraRotation);
break;
}
}
}
OutViewLocation = CameraLocation;
OutViewDirection = CameraRotation.Vector();
}
// FTickableGameObject begin
void AGameplayDebuggerPlayerManager::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
#if WITH_EDITORONLY_DATA
if (EditorWorldData.Replicator)
{
FActorTickFunction DummyTickFunction;
EditorWorldData.Replicator->TickActor(DeltaTime, ELevelTick::LEVELTICK_All, DummyTickFunction);
}
#endif // WITH_EDITORONLY_DATA
}
ETickableTickType AGameplayDebuggerPlayerManager::GetTickableTickType() const
{
return
#if WITH_EDITOR
IsTickable() ? ETickableTickType::Conditional :
#endif // WITH_EDITOR
ETickableTickType::Never;
}
TStatId AGameplayDebuggerPlayerManager::GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(AGameplayDebuggerPlayerManager, STATGROUP_Tickables);
}
// FTickableGameObject end
void AGameplayDebuggerPlayerManager::OnGameModeLogout(AGameModeBase* GameMode, AController* Exiting)
{
if (GameMode && GameMode->GetWorld() == GetWorld())
{
UWorld* World = GetWorld();
for (int32 Idx = PlayerData.Num() - 1; Idx >= 0; Idx--)
{
FGameplayDebuggerPlayerData& TestData = PlayerData[Idx];
if (IsValid(TestData.Replicator) && (TestData.Replicator->GetReplicationOwner() == Exiting))
{
if (IsValid(TestData.Replicator))
{
World->DestroyActor(TestData.Replicator);
}
if (IsValid(TestData.Controller))
{
TestData.Controller->Cleanup();
}
PlayerData.RemoveAt(Idx, EAllowShrinking::No);
break;
}
}
}
}
void AGameplayDebuggerPlayerManager::OnReplayScrubTeardown(UWorld* InWorld)
{
if (GetWorld() == InWorld)
{
UpdateAuthReplicators();
}
}