RealtimeStyleTransferRuntime/Source/LyraGame/Character/LyraPawnExtensionComponent.cpp

329 lines
10 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "LyraPawnExtensionComponent.h"
#include "Abilities/GameplayAbilityTypes.h"
#include "AbilitySystem/LyraAbilitySystemComponent.h"
#include "Components/GameFrameworkComponentManager.h"
#include "Containers/Array.h"
#include "Containers/Set.h"
#include "Containers/UnrealString.h"
#include "Engine/EngineBaseTypes.h"
#include "GameFramework/Controller.h"
#include "GameFramework/Pawn.h"
#include "GameplayTagContainer.h"
#include "HAL/Platform.h"
#include "Logging/LogCategory.h"
#include "Logging/LogMacros.h"
#include "LyraGameplayTags.h"
#include "LyraLogChannels.h"
#include "LyraPawnData.h"
#include "Misc/AssertionMacros.h"
#include "Net/UnrealNetwork.h"
#include "Templates/SharedPointer.h"
#include "Trace/Detail/Channel.h"
#include "UObject/UObjectBaseUtility.h"
#include "UObject/UnrealNames.h"
#include "UObject/WeakObjectPtr.h"
#include "UObject/WeakObjectPtrTemplates.h"
class FLifetimeProperty;
class UActorComponent;
const FName ULyraPawnExtensionComponent::NAME_ActorFeatureName("PawnExtension");
ULyraPawnExtensionComponent::ULyraPawnExtensionComponent(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PrimaryComponentTick.bStartWithTickEnabled = false;
PrimaryComponentTick.bCanEverTick = false;
SetIsReplicatedByDefault(true);
PawnData = nullptr;
AbilitySystemComponent = nullptr;
}
void ULyraPawnExtensionComponent::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ULyraPawnExtensionComponent, PawnData);
}
void ULyraPawnExtensionComponent::OnRegister()
{
Super::OnRegister();
const APawn* Pawn = GetPawn<APawn>();
ensureAlwaysMsgf((Pawn != nullptr), TEXT("LyraPawnExtensionComponent on [%s] can only be added to Pawn actors."), *GetNameSafe(GetOwner()));
TArray<UActorComponent*> PawnExtensionComponents;
Pawn->GetComponents(ULyraPawnExtensionComponent::StaticClass(), PawnExtensionComponents);
ensureAlwaysMsgf((PawnExtensionComponents.Num() == 1), TEXT("Only one LyraPawnExtensionComponent should exist on [%s]."), *GetNameSafe(GetOwner()));
// Register with the init state system early, this will only work if this is a game world
RegisterInitStateFeature();
}
void ULyraPawnExtensionComponent::BeginPlay()
{
Super::BeginPlay();
// Listen for changes to all features
BindOnActorInitStateChanged(NAME_None, FGameplayTag(), false);
// Notifies state manager that we have spawned, then try rest of default initialization
ensure(TryToChangeInitState(FLyraGameplayTags::Get().InitState_Spawned));
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::EndPlay(const EEndPlayReason::Type EndPlayReason)
{
UninitializeAbilitySystem();
UnregisterInitStateFeature();
Super::EndPlay(EndPlayReason);
}
void ULyraPawnExtensionComponent::SetPawnData(const ULyraPawnData* InPawnData)
{
check(InPawnData);
APawn* Pawn = GetPawnChecked<APawn>();
if (Pawn->GetLocalRole() != ROLE_Authority)
{
return;
}
if (PawnData)
{
UE_LOG(LogLyra, Error, TEXT("Trying to set PawnData [%s] on pawn [%s] that already has valid PawnData [%s]."), *GetNameSafe(InPawnData), *GetNameSafe(Pawn), *GetNameSafe(PawnData));
return;
}
PawnData = InPawnData;
Pawn->ForceNetUpdate();
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::OnRep_PawnData()
{
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::InitializeAbilitySystem(ULyraAbilitySystemComponent* InASC, AActor* InOwnerActor)
{
check(InASC);
check(InOwnerActor);
if (AbilitySystemComponent == InASC)
{
// The ability system component hasn't changed.
return;
}
if (AbilitySystemComponent)
{
// Clean up the old ability system component.
UninitializeAbilitySystem();
}
APawn* Pawn = GetPawnChecked<APawn>();
AActor* ExistingAvatar = InASC->GetAvatarActor();
UE_LOG(LogLyra, Verbose, TEXT("Setting up ASC [%s] on pawn [%s] owner [%s], existing [%s] "), *GetNameSafe(InASC), *GetNameSafe(Pawn), *GetNameSafe(InOwnerActor), *GetNameSafe(ExistingAvatar));
if ((ExistingAvatar != nullptr) && (ExistingAvatar != Pawn))
{
UE_LOG(LogLyra, Log, TEXT("Existing avatar (authority=%d)"), ExistingAvatar->HasAuthority() ? 1 : 0);
// There is already a pawn acting as the ASC's avatar, so we need to kick it out
// This can happen on clients if they're lagged: their new pawn is spawned + possessed before the dead one is removed
ensure(!ExistingAvatar->HasAuthority());
if (ULyraPawnExtensionComponent* OtherExtensionComponent = FindPawnExtensionComponent(ExistingAvatar))
{
OtherExtensionComponent->UninitializeAbilitySystem();
}
}
AbilitySystemComponent = InASC;
AbilitySystemComponent->InitAbilityActorInfo(InOwnerActor, Pawn);
if (ensure(PawnData))
{
InASC->SetTagRelationshipMapping(PawnData->TagRelationshipMapping);
}
OnAbilitySystemInitialized.Broadcast();
}
void ULyraPawnExtensionComponent::UninitializeAbilitySystem()
{
if (!AbilitySystemComponent)
{
return;
}
// Uninitialize the ASC if we're still the avatar actor (otherwise another pawn already did it when they became the avatar actor)
if (AbilitySystemComponent->GetAvatarActor() == GetOwner())
{
FGameplayTagContainer AbilityTypesToIgnore;
AbilityTypesToIgnore.AddTag(FLyraGameplayTags::Get().Ability_Behavior_SurvivesDeath);
AbilitySystemComponent->CancelAbilities(nullptr, &AbilityTypesToIgnore);
AbilitySystemComponent->ClearAbilityInput();
AbilitySystemComponent->RemoveAllGameplayCues();
if (AbilitySystemComponent->GetOwnerActor() != nullptr)
{
AbilitySystemComponent->SetAvatarActor(nullptr);
}
else
{
// If the ASC doesn't have a valid owner, we need to clear *all* actor info, not just the avatar pairing
AbilitySystemComponent->ClearActorInfo();
}
OnAbilitySystemUninitialized.Broadcast();
}
AbilitySystemComponent = nullptr;
}
void ULyraPawnExtensionComponent::HandleControllerChanged()
{
if (AbilitySystemComponent && (AbilitySystemComponent->GetAvatarActor() == GetPawnChecked<APawn>()))
{
ensure(AbilitySystemComponent->AbilityActorInfo->OwnerActor == AbilitySystemComponent->GetOwnerActor());
if (AbilitySystemComponent->GetOwnerActor() == nullptr)
{
UninitializeAbilitySystem();
}
else
{
AbilitySystemComponent->RefreshAbilityActorInfo();
}
}
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::HandlePlayerStateReplicated()
{
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::SetupPlayerInputComponent()
{
CheckDefaultInitialization();
}
void ULyraPawnExtensionComponent::CheckDefaultInitialization()
{
// Before checking our progress, try progressing any other features we might depend on
CheckDefaultInitializationForImplementers();
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
static const TArray<FGameplayTag> StateChain = { InitTags.InitState_Spawned, InitTags.InitState_DataAvailable, InitTags.InitState_DataInitialized, InitTags.InitState_GameplayReady };
// This will try to progress from spawned (which is only set in BeginPlay) through the data initialization stages until it gets to gameplay ready
ContinueInitStateChain(StateChain);
}
bool ULyraPawnExtensionComponent::CanChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState) const
{
check(Manager);
APawn* Pawn = GetPawn<APawn>();
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
if (!CurrentState.IsValid() && DesiredState == InitTags.InitState_Spawned)
{
// As long as we are on a valid pawn, we count as spawned
if (Pawn)
{
return true;
}
}
if (CurrentState == InitTags.InitState_Spawned && DesiredState == InitTags.InitState_DataAvailable)
{
// Pawn data is required.
if (!PawnData)
{
return false;
}
const bool bHasAuthority = Pawn->HasAuthority();
const bool bIsLocallyControlled = Pawn->IsLocallyControlled();
if (bHasAuthority || bIsLocallyControlled)
{
// Check for being possessed by a controller.
if (!GetController<AController>())
{
return false;
}
}
return true;
}
else if (CurrentState == InitTags.InitState_DataAvailable && DesiredState == InitTags.InitState_DataInitialized)
{
// Transition to initialize if all features have their data available
return Manager->HaveAllFeaturesReachedInitState(Pawn, InitTags.InitState_DataAvailable);
}
else if (CurrentState == InitTags.InitState_DataInitialized && DesiredState == InitTags.InitState_GameplayReady)
{
return true;
}
return false;
}
void ULyraPawnExtensionComponent::HandleChangeInitState(UGameFrameworkComponentManager* Manager, FGameplayTag CurrentState, FGameplayTag DesiredState)
{
if (DesiredState == FLyraGameplayTags::Get().InitState_DataInitialized)
{
// This is currently all handled by other components listening to this state change
}
}
void ULyraPawnExtensionComponent::OnActorInitStateChanged(const FActorInitStateChangedParams& Params)
{
// If another feature is now in DataAvailable, see if we should transition to DataInitialized
if (Params.FeatureName != NAME_ActorFeatureName)
{
const FLyraGameplayTags& InitTags = FLyraGameplayTags::Get();
if (Params.FeatureState == InitTags.InitState_DataAvailable)
{
CheckDefaultInitialization();
}
}
}
void ULyraPawnExtensionComponent::OnAbilitySystemInitialized_RegisterAndCall(FSimpleMulticastDelegate::FDelegate Delegate)
{
if (!OnAbilitySystemInitialized.IsBoundToObject(Delegate.GetUObject()))
{
OnAbilitySystemInitialized.Add(Delegate);
}
if (AbilitySystemComponent)
{
Delegate.Execute();
}
}
void ULyraPawnExtensionComponent::OnAbilitySystemUninitialized_Register(FSimpleMulticastDelegate::FDelegate Delegate)
{
if (!OnAbilitySystemUninitialized.IsBoundToObject(Delegate.GetUObject()))
{
OnAbilitySystemUninitialized.Add(Delegate);
}
}