diff --git a/ActionsExtension.uplugin b/ActionsExtension.uplugin index 6ec0ec7..5c49112 100644 --- a/ActionsExtension.uplugin +++ b/ActionsExtension.uplugin @@ -18,13 +18,23 @@ "Type": "Runtime", "LoadingPhase": "PreDefault", "WhitelistPlatforms": [ - "Win64", - "Win32", - "Linux", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Win64", + "Mac", + "IOS", + "Android", + "Linux" + ] + }, + { + "Name": "ActionsGraph", + "Type": "UncookedOnly", + "LoadingPhase": "Default", + "WhitelistPlatforms": [ + "Win64", + "Mac", + "IOS", + "Android", + "Linux" ] }, { @@ -32,13 +42,11 @@ "Type": "Editor", "LoadingPhase": "PostEngineInit", "WhitelistPlatforms": [ - "Win64", - "Win32", - "Linux", - "PS5", - "XboxOne", - "Switch", - "Mac" + "Win64", + "Mac", + "IOS", + "Android", + "Linux" ] }, { @@ -47,7 +55,6 @@ "LoadingPhase" : "PreDefault", "WhitelistPlatforms": [ "Win64", - "Win32", "Linux", "Mac" ] diff --git a/Source/Actions/Actions.Build.cs b/Source/Actions/Actions.Build.cs index 364eb15..be58f79 100644 --- a/Source/Actions/Actions.Build.cs +++ b/Source/Actions/Actions.Build.cs @@ -12,20 +12,10 @@ public Actions(ReadOnlyTargetRules TargetRules) : base(TargetRules) "Core", "CoreUObject", "Engine", - "GameplayTasks", "AIModule" }); - PrivateDependencyModuleNames.AddRange(new string[]{}); - - - if (TargetRules.bBuildEditor == true) - { - PrivateDependencyModuleNames.AddRange( new string[] { - "SlateCore", - "Slate" - }); - } + PrivateDependencyModuleNames.AddRange(new string[] { }); if (TargetRules.bBuildDeveloperTools || (Target.Configuration != UnrealTargetConfiguration.Shipping && Target.Configuration != UnrealTargetConfiguration.Test)) { diff --git a/Source/Actions/Private/Action.cpp b/Source/Actions/Private/Action.cpp index 3e9711e..08db245 100644 --- a/Source/Actions/Private/Action.cpp +++ b/Source/Actions/Private/Action.cpp @@ -1,31 +1,74 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "Action.h" -#include "Engine/EngineTypes.h" #include "TimerManager.h" -#include "Engine/World.h" -#include "GameplayTaskOwnerInterface.h" +#include +#include +#include +#include + + #if WITH_GAMEPLAY_DEBUGGER -#include "GameplayDebugger_Actions.h" -#endif // WITH_GAMEPLAY_DEBUGGER +# include "GameplayDebugger_Actions.h" +#endif // WITH_GAMEPLAY_DEBUGGER DEFINE_LOG_CATEGORY(ActionLog); +UAction* CreateAction(UObject* Owner, const TSubclassOf Type, bool bAutoActivate /*= false*/) +{ + if (!IsValid(Owner) || !Type.Get() || Type == UAction::StaticClass()) + { + return nullptr; + } + + UAction* Action = NewObject(Owner, Type); + if (bAutoActivate) + { + Action->Activate(); + } + return Action; +} + +UAction* CreateAction(UObject* Owner, UAction* Template, bool bAutoActivate /*= false*/) +{ + if (!IsValid(Owner) || !Template) + { + return nullptr; + } + + UClass* const Type = Template->GetClass(); + check(Type); + + if (Type == UAction::StaticClass()) + { + return nullptr; + } + + UAction* Action = NewObject(Owner, Type, NAME_None, RF_NoFlags, Template); + if (bAutoActivate) + { + Action->Activate(); + } + return Action; +} + + void UAction::Activate() { UActionsSubsystem* Subsystem = GetSubsystem(); if (!IsValid(this) || !IsValid(GetOuter()) || State != EActionState::Preparing) { - UE_LOG(ActionLog, Warning, TEXT("Action '%s' is already running or pending destruction."), *GetName()); + UE_LOG( + ActionLog, Warning, TEXT("Action '%s' is already running or pending destruction."), *GetName()); Destroy(); return; } - if(!CanActivate()) + if (!CanActivate()) { UE_LOG(ActionLog, Log, TEXT("Could not activate. CanActivate() Failed.")); Destroy(); @@ -42,7 +85,10 @@ void UAction::Activate() Subsystem->AddRootAction(this); } - Subsystem->AddActionToTickGroup(this); + if (bWantsToTick) + { + Subsystem->AddActionToTickGroup(this); + } State = EActionState::Running; OnActivation(); @@ -66,7 +112,7 @@ void UAction::Cancel() void UAction::OnFinish(const EActionState Reason) { // Stop any timers or latent actions for the action - if (UWorld * World = GetWorld()) + if (UWorld* World = GetWorld()) { World->GetLatentActionManager().RemoveActionsForObject(this); World->GetTimerManager().ClearAllTimersForObject(this); @@ -82,14 +128,15 @@ void UAction::OnFinish(const EActionState Reason) } } -void UAction::Finish(bool bSuccess) { +void UAction::Finish(bool bSuccess) +{ if (!IsRunning() || !IsValid(this)) return; State = bSuccess ? EActionState::Success : EActionState::Failure; OnFinish(State); - //Remove from parent action + // Remove from parent action if (auto* ParentAction = GetParentAction()) { ParentAction->RemoveChildren(this); @@ -103,16 +150,16 @@ void UAction::Destroy() if (!IsValid(this)) return; - //Cancel and destroy all children tasks + // Cancel and destroy all children tasks for (auto* Children : ChildrenActions) { - if (Children) { + if (Children) + { Children->Cancel(); } } ChildrenActions.Reset(); - //Mark for destruction MarkAsGarbage(); } @@ -123,7 +170,7 @@ void UAction::AddChildren(UAction* Child) void UAction::RemoveChildren(UAction* Child) { - ChildrenActions.Remove(Child); + ChildrenActions.RemoveSwap(Child, false); } bool UAction::ReceiveCanActivate_Implementation() @@ -131,28 +178,83 @@ bool UAction::ReceiveCanActivate_Implementation() return true; } +bool UAction::GetWantsToTick() const +{ + return bWantsToTick; +} + +bool UAction::IsRunning() const +{ + return State == EActionState::Running; +} + +bool UAction::Succeeded() const +{ + return State == EActionState::Success; +} + +bool UAction::Failed() const +{ + return State == EActionState::Failure; +} + +EActionState UAction::GetState() const +{ + return State; +} + +UObject* const UAction::GetParent() const +{ + return GetOuter(); +} + +UAction* UAction::GetParentAction() const +{ + return Cast(GetOuter()); +} + +float UAction::GetTickRate() const +{ + // Reduce TickRate Precision to 0.1ms + return FMath::FloorToFloat(TickRate * 10000.f) * 0.0001f; +} + UObject* UAction::GetOwner() const { - UObject* Outer = nullptr; - const UAction* Current = this; + return Owner.Get(); +} - // #TODO: Ensure this works - while (Current) +AActor* UAction::GetOwnerActor() const +{ + UObject* Owner = GetOwner(); + // With this function we can predict the owner is more likely to be an actor so we check it first + if (AActor* Actor = Cast(Owner)) { - Outer = Current->GetOuter(); - Current = Cast(Outer); + return Actor; } - return Outer; + else if (auto* Component = Cast(Owner)) + { + return Component->GetOwner(); + } + return nullptr; } -AActor* UAction::GetOwnerActor() const +UActorComponent* UAction::GetOwnerComponent() const +{ + return Cast(GetOwner()); +} + +UWorld* UAction::GetWorld() const { - UObject* const Owner = GetOwner(); - if (auto * Component = Cast(Owner)) + // If we are a CDO, we must return nullptr to fool UObject::ImplementsGetWorld + if (HasAllFlags(RF_ClassDefaultObject)) + return nullptr; + + if (const UObject* InOwner = GetOwner()) { - return Component->GetOwner(); + InOwner->GetWorld(); } - return Cast(Owner); + return nullptr; } #if WITH_GAMEPLAY_DEBUGGER @@ -161,14 +263,14 @@ void UAction::DescribeSelfToGameplayDebugger(FGameplayDebugger_Actions& Debugger FString ColorText = TEXT(""); switch (State) { - case EActionState::Running: - ColorText = TEXT("{cyan}"); - break; - case EActionState::Success: - ColorText = TEXT("{green}"); - break; - default: - ColorText = TEXT("{red}"); + case EActionState::Running: + ColorText = TEXT("{cyan}"); + break; + case EActionState::Success: + ColorText = TEXT("{green}"); + break; + default: + ColorText = TEXT("{red}"); } FString IndentString = ""; @@ -181,7 +283,8 @@ void UAction::DescribeSelfToGameplayDebugger(FGameplayDebugger_Actions& Debugger if (IsRunning()) { - Debugger.AddTextLine(FString::Printf(TEXT("%s%s>%s %s"), *IndentString, *ColorText, *GetName(), *CanceledSuffix)); + Debugger.AddTextLine( + FString::Printf(TEXT("%s%s>%s %s"), *IndentString, *ColorText, *GetName(), *CanceledSuffix)); for (const auto* ChildAction : ChildrenActions) { @@ -196,42 +299,43 @@ void UAction::DescribeSelfToGameplayDebugger(FGameplayDebugger_Actions& Debugger } } } -#endif // WITH_GAMEPLAY_DEBUGGER +#endif // WITH_GAMEPLAY_DEBUGGER -UAction* UAction::Create(UObject* Owner, const TSubclassOf Type, bool bAutoActivate /*= false*/) +void UAction::SetWantsToTick(bool bValue) { - if (!IsValid(Owner) || !Type.Get() || Type == UAction::StaticClass()) + if (bValue != bWantsToTick) { - return nullptr; - } - - UAction* Action = NewObject(Owner, Type); - if (bAutoActivate) - { - Action->Activate(); + bWantsToTick = bValue; + UActionsSubsystem* Subsystem = GetSubsystem(); + if (bValue) + { + Subsystem->AddActionToTickGroup(this); + } + else + { + Subsystem->RemoveActionFromTickGroup(this); + } } - return Action; } -UAction* UAction::Create(UObject* Owner, UAction* Template, bool bAutoActivate /*= false*/) +void UAction::PostInitProperties() { - if (!IsValid(Owner) || !Template) - { - return nullptr; - } + Super::PostInitProperties(); - UClass* const Type = Template->GetClass(); - check(Type); - - if (Type == UAction::StaticClass()) + UObject* Outer = GetOuter(); + if (UAction* Parent = Cast(Outer)) { - return nullptr; + Owner = Parent->GetOwner(); } - - UAction* Action = NewObject(Owner, Type, NAME_None, RF_NoFlags, Template); - if (bAutoActivate) + else { - Action->Activate(); + Owner = Outer; } - return Action; +} + +UActionsSubsystem* UAction::GetSubsystem() const +{ + const UWorld* World = GetWorld(); + const UGameInstance* GI = World ? World->GetGameInstance() : nullptr; + return UGameInstance::GetSubsystem(GI); } diff --git a/Source/Actions/Private/ActionLibrary.cpp b/Source/Actions/Private/ActionLibrary.cpp index d6b550a..dd45708 100644 --- a/Source/Actions/Private/ActionLibrary.cpp +++ b/Source/Actions/Private/ActionLibrary.cpp @@ -1,3 +1,3 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionLibrary.h" diff --git a/Source/Actions/Private/ActionsModule.cpp b/Source/Actions/Private/ActionsModule.cpp index 13f746c..15362e0 100644 --- a/Source/Actions/Private/ActionsModule.cpp +++ b/Source/Actions/Private/ActionsModule.cpp @@ -1,11 +1,11 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionsModule.h" #if WITH_GAMEPLAY_DEBUGGER -#include "GameplayDebugger.h" -#include "GameplayDebugger_Actions.h" -#endif // WITH_GAMEPLAY_DEBUGGER +# include "GameplayDebugger.h" +# include "GameplayDebugger_Actions.h" +#endif // WITH_GAMEPLAY_DEBUGGER DEFINE_LOG_CATEGORY(LogActions) @@ -19,7 +19,9 @@ void FActionsModule::StartupModule() // Register Gameplay debugger #if WITH_GAMEPLAY_DEBUGGER IGameplayDebugger& GameplayDebuggerModule = IGameplayDebugger::Get(); - GameplayDebuggerModule.RegisterCategory("Actions", IGameplayDebugger::FOnGetCategory::CreateStatic(&FGameplayDebugger_Actions::MakeInstance), EGameplayDebuggerCategoryState::EnabledInGameAndSimulate); + GameplayDebuggerModule.RegisterCategory("Actions", + IGameplayDebugger::FOnGetCategory::CreateStatic(&FGameplayDebugger_Actions::MakeInstance), + EGameplayDebuggerCategoryState::EnabledInGameAndSimulate); GameplayDebuggerModule.NotifyCategoriesChanged(); #endif } diff --git a/Source/Actions/Private/ActionsSubsystem.cpp b/Source/Actions/Private/ActionsSubsystem.cpp index 7f3d063..8050f05 100644 --- a/Source/Actions/Private/ActionsSubsystem.cpp +++ b/Source/Actions/Private/ActionsSubsystem.cpp @@ -1,13 +1,15 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionsSubsystem.h" -#include #include "Action.h" +#include + + #if WITH_GAMEPLAY_DEBUGGER -#include "GameplayDebugger_Actions.h" -#endif // WITH_GAMEPLAY_DEBUGGER +# include "GameplayDebugger_Actions.h" +#endif // WITH_GAMEPLAY_DEBUGGER void FActionsTickGroup::Tick(float DeltaTime) @@ -32,7 +34,7 @@ void FActionsTickGroup::Tick(float DeltaTime) } else { - //Normal Tick + // Normal Tick DelayedTick(DeltaTime); } } @@ -41,9 +43,8 @@ void FActionsTickGroup::DelayedTick(float DeltaTime) { for (int32 i = 0; i < Actions.Num(); ++i) { - auto* Action = Actions[i]; - - if (!IsValid(Action)) + auto* const Action = Actions[i]; + if (!Action) { Actions.RemoveAtSwap(i, 1, false); --i; @@ -55,9 +56,9 @@ void FActionsTickGroup::DelayedTick(float DeltaTime) } } -void FRootAction::CancelAll(bool bShouldShrink) +void FActionOwner::CancelAll(bool bShouldShrink) { - for (auto* Action : Actions) + for (auto& Action : Actions) { if (Action) { @@ -71,15 +72,14 @@ void FRootAction::CancelAll(bool bShouldShrink) Actions.Reset(); } -void FRootAction::CancelByPredicate(const TFunctionRef& Predicate, bool bShouldShrink) +void FActionOwner::CancelByPredicate(const TFunctionRef& Predicate, bool bShouldShrink) { for (int32 i = 0; i < Actions.Num(); ++i) { - auto* Action = Actions[i]; - + auto* Action = Actions[i].Get(); if (Action && Predicate(Action)) { - //Cancel action + // Cancel action Action->Cancel(); // Remove action @@ -108,8 +108,8 @@ void UActionsSubsystem::Deinitialize() void UActionsSubsystem::Tick(float DeltaTime) { - // Cancel destroyed object actions - for (auto RootIt = RootActions.CreateIterator(); RootIt; ++RootIt) + // Cancel destroyed object actions or of which the outer is invalid + for (auto RootIt = ActionOwners.CreateIterator(); RootIt; ++RootIt) { if (!RootIt->Owner.IsValid()) { @@ -119,9 +119,11 @@ void UActionsSubsystem::Tick(float DeltaTime) else { // Remove garbage collected actions - RootIt->Actions.RemoveAllSwap([](const UAction* Action) { - return !Action; - }, false); + RootIt->Actions.RemoveAllSwap( + [](const UAction* Action) { + return !IsValid(Action); + }, + false); if (RootIt->Actions.Num() <= 0) { @@ -138,7 +140,7 @@ void UActionsSubsystem::Tick(float DeltaTime) TickGroup.Tick(DeltaTime * TimeDilation); - if(TickGroup.Actions.Num() <= 0) + if (TickGroup.Actions.Num() <= 0) { // Tick group is empty so we remove and ignore it TickGroups.RemoveAtSwap(i, 1, false); @@ -150,26 +152,26 @@ void UActionsSubsystem::Tick(float DeltaTime) void UActionsSubsystem::CancelAll() { - for (auto& RootAction : RootActions) + for (auto& RootAction : ActionOwners) { RootAction.CancelAll(false); } - RootActions.Reset(); + ActionOwners.Reset(); } void UActionsSubsystem::CancelAllByOwner(UObject* Object) { - const FSetElementId RootId = RootActions.FindId(Object); - if (RootId.IsValidId()) + const FSetElementId OwnerId = ActionOwners.FindId(Object); + if (OwnerId.IsValidId()) { - RootActions[RootId].CancelAll(false); - RootActions.Remove(RootId); + ActionOwners[OwnerId].CancelAll(false); + ActionOwners.Remove(OwnerId); } } void UActionsSubsystem::CancelByPredicate(TFunctionRef Predicate) { - for (auto& RootAction : RootActions) + for (auto& RootAction : ActionOwners) { RootAction.CancelByPredicate(Predicate); } @@ -177,9 +179,9 @@ void UActionsSubsystem::CancelByPredicate(TFunctionRef Pre void UActionsSubsystem::CancelByOwnerPredicate(UObject* Object, TFunctionRef Predicate) { - if(FRootAction* const RootAction = RootActions.Find(Object)) + if (FActionOwner* const Owner = ActionOwners.Find(Object)) { - RootAction->CancelByPredicate(Predicate); + Owner->CancelByPredicate(Predicate); } } @@ -190,13 +192,12 @@ void UActionsSubsystem::AddRootAction(UAction* Child) // Registry for GC Canceling UObject* Owner = Child->GetOuter(); - FSetElementId RootId = RootActions.FindId(Owner); - if (!RootId.IsValidId()) + FSetElementId OwnerId = ActionOwners.FindId(Owner); + if (!OwnerId.IsValidId()) { - RootId = RootActions.Add({ Owner }); + OwnerId = ActionOwners.Add({Owner}); } - - RootActions[RootId].Actions.Add(Child); + ActionOwners[OwnerId].Actions.Add(Child); } void UActionsSubsystem::AddActionToTickGroup(UAction* Child) @@ -207,23 +208,43 @@ void UActionsSubsystem::AddActionToTickGroup(UAction* Child) FActionsTickGroup* Group = TickGroups.FindByKey(TickRate); if (!Group) { - TickGroups.Add({ TickRate }); + TickGroups.Add({TickRate}); Group = &TickGroups.Last(); } Group->Actions.Add(Child); } +void UActionsSubsystem::RemoveActionFromTickGroup(UAction* Child) +{ + const float TickRate = Child->GetTickRate(); + int32 Index = TickGroups.Find(TickRate); + if (Index != INDEX_NONE) + { + auto& Group = TickGroups[Index]; + if (Group.Actions.Num() > 1) + { + Group.Actions.RemoveSwap(Child, false); + } + else + { + TickGroups.RemoveAtSwap(Index); + } + } +} + #if WITH_GAMEPLAY_DEBUGGER -void UActionsSubsystem::DescribeOwnerToGameplayDebugger(UObject* Owner, const FName& BaseName, FGameplayDebugger_Actions& Debugger) const +void UActionsSubsystem::DescribeOwnerToGameplayDebugger( + UObject* Owner, const FName& BaseName, FGameplayDebugger_Actions& Debugger) const { static const FString StateColorText = TEXT("{green}"); - Debugger.AddTextLine(FString::Printf(TEXT("%s%s: %s"), *StateColorText, *BaseName.ToString(), *Owner->GetName())); + Debugger.AddTextLine( + FString::Printf(TEXT("%s%s: %s"), *StateColorText, *BaseName.ToString(), *Owner->GetName())); - if (const FRootAction* const RootAction = RootActions.Find(Owner)) + if (const FActionOwner* const RootAction = ActionOwners.Find(Owner)) { - for (const auto* Action : RootAction->Actions) + for (const UAction* Action : RootAction->Actions) { if (IsValid(Action)) { @@ -234,4 +255,4 @@ void UActionsSubsystem::DescribeOwnerToGameplayDebugger(UObject* Owner, const FN Debugger.AddTextLine(TEXT("")); } -#endif // WITH_GAMEPLAY_DEBUGGER +#endif // WITH_GAMEPLAY_DEBUGGER diff --git a/Source/Actions/Private/BTT_RunAction.cpp b/Source/Actions/Private/BTT_RunAction.cpp index 12d8f0f..87e9213 100644 --- a/Source/Actions/Private/BTT_RunAction.cpp +++ b/Source/Actions/Private/BTT_RunAction.cpp @@ -1,37 +1,39 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "BTT_RunAction.h" EBTNodeResult::Type UBTT_RunAction::ExecuteTask(UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory) { - if(!ActionType) { + if (!ActionType) + { return EBTNodeResult::Failed; } AActor* OwnerActor = InOwnerComp.GetTypedOuter(); check(OwnerActor); - Action = UAction::Create(OwnerActor, ActionType, false); + Action = CreateAction(OwnerActor, ActionType, false); check(Action); Action->OnFinishedDelegate.AddDynamic(this, &UBTT_RunAction::OnRunActionFinished); Action->Activate(); OwnerComp = &InOwnerComp; - return Action? EBTNodeResult::InProgress : EBTNodeResult::Failed; + return Action ? EBTNodeResult::InProgress : EBTNodeResult::Failed; } EBTNodeResult::Type UBTT_RunAction::AbortTask(UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory) { - if(IsValid(Action)) + if (IsValid(Action)) { Action->Cancel(); } return EBTNodeResult::Aborted; } -void UBTT_RunAction::DescribeRuntimeValues(const UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const +void UBTT_RunAction::DescribeRuntimeValues(const UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory, + EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const { Super::DescribeRuntimeValues(InOwnerComp, NodeMemory, Verbosity, Values); @@ -44,7 +46,7 @@ void UBTT_RunAction::DescribeRuntimeValues(const UBehaviorTreeComponent& InOwner FString UBTT_RunAction::GetStaticDescription() const { - FString ActionName = (ActionType)? ActionType->GetClass()->GetName() : "None"; + FString ActionName = (ActionType) ? ActionType->GetClass()->GetName() : "None"; ActionName.RemoveFromEnd("_C", ESearchCase::CaseSensitive); return FString::Printf(TEXT("Action: %s"), *ActionName); } @@ -55,17 +57,17 @@ void UBTT_RunAction::OnRunActionFinished(const EActionState Reason) { switch (Reason) { - case EActionState::Success: - FinishLatentTask(*OwnerComp, EBTNodeResult::Succeeded); - break; - case EActionState::Failure: - FinishLatentTask(*OwnerComp, EBTNodeResult::Failed); - break; - case EActionState::Cancelled: //Do Nothing - break; - default: - FinishLatentTask(*OwnerComp, EBTNodeResult::Aborted); - break; + case EActionState::Success: + FinishLatentTask(*OwnerComp, EBTNodeResult::Succeeded); + break; + case EActionState::Failure: + FinishLatentTask(*OwnerComp, EBTNodeResult::Failed); + break; + case EActionState::Cancelled: // Do Nothing + break; + default: + FinishLatentTask(*OwnerComp, EBTNodeResult::Aborted); + break; } } } \ No newline at end of file diff --git a/Source/Actions/Private/GameplayDebugger_Actions.cpp b/Source/Actions/Private/GameplayDebugger_Actions.cpp index e6d3de4..9b66ca5 100644 --- a/Source/Actions/Private/GameplayDebugger_Actions.cpp +++ b/Source/Actions/Private/GameplayDebugger_Actions.cpp @@ -1,19 +1,19 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "GameplayDebugger_Actions.h" +#include "ActionsSubsystem.h" + #include #include #include #include -#include "ActionsSubsystem.h" #if WITH_GAMEPLAY_DEBUGGER -FGameplayDebugger_Actions::FGameplayDebugger_Actions() -{} +FGameplayDebugger_Actions::FGameplayDebugger_Actions() {} TSharedRef FGameplayDebugger_Actions::MakeInstance() { @@ -24,7 +24,7 @@ void FGameplayDebugger_Actions::CollectData(APlayerController* OwnerPC, AActor* { UWorld* World = OwnerPC->GetWorld(); - UGameInstance* GI = World? World->GetGameInstance() : nullptr; + UGameInstance* GI = World ? World->GetGameInstance() : nullptr; if (!GI) { return; @@ -49,4 +49,4 @@ void FGameplayDebugger_Actions::CollectData(APlayerController* OwnerPC, AActor* } } } -#endif // ENABLE_GAMEPLAY_DEBUGGER +#endif // ENABLE_GAMEPLAY_DEBUGGER diff --git a/Source/Actions/Private/GameplayDebugger_Actions.h b/Source/Actions/Private/GameplayDebugger_Actions.h index af3513d..4073a53 100644 --- a/Source/Actions/Private/GameplayDebugger_Actions.h +++ b/Source/Actions/Private/GameplayDebugger_Actions.h @@ -1,10 +1,10 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once #include "CoreMinimal.h" #if WITH_GAMEPLAY_DEBUGGER -#include "GameplayDebuggerCategory.h" +# include "GameplayDebuggerCategory.h" #endif #if WITH_GAMEPLAY_DEBUGGER @@ -19,4 +19,4 @@ class ACTIONS_API FGameplayDebugger_Actions : public FGameplayDebuggerCategory virtual void CollectData(APlayerController* OwnerPC, AActor* DebugActor) override; }; -#endif // WITH_GAMEPLAY_DEBUGGER +#endif // WITH_GAMEPLAY_DEBUGGER diff --git a/Source/Actions/Public/Action.h b/Source/Actions/Public/Action.h index eddbd58..7bfbee6 100644 --- a/Source/Actions/Public/Action.h +++ b/Source/Actions/Public/Action.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once @@ -18,6 +18,44 @@ DECLARE_LOG_CATEGORY_EXTERN(ActionLog, Log, All); +class AActor; +class UActorComponent; +class UAction; + + +/** + * Creates a new action. Templated version + * @param ActionType + * @param Owner of the action. If destroyed, the action will follow. + * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. + */ +template +ActionType* CreateAction(UObject* Owner, bool bAutoActivate = false) +{ + static_assert( + !std::is_same_v, "Instantiating UAction is not allowed. Use a child class."); + static_assert(TIsDerivedFrom::IsDerived, "Provided class must inherit UAction."); + return Cast(CreateAction(Owner, ActionType::StaticClass(), bAutoActivate)); +} + +/** + * Creates a new action + * @param Owner of the action. If destroyed, the action will follow. + * @param Type of the action to create + * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. + */ +ACTIONS_API UAction* CreateAction( + UObject* Owner, const TSubclassOf Type, bool bAutoActivate = false); + +/** + * Creates a new action + * @param Owner of the action. If destroyed, the action will follow. + * @param Template whose properties and class are used to create the action. + * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. + */ +ACTIONS_API UAction* CreateAction(UObject* Owner, UAction* Template, bool bAutoActivate = false); + + /** * Result of a node execution */ @@ -31,7 +69,7 @@ enum class EActionState : uint8 Cancelled }; -FORCEINLINE FString ToString(EActionState Value) +inline FString ToString(EActionState Value) { const UEnum* EnumPtr = FindObject(nullptr, TEXT("/Script/Actions.EActionState"), true); return EnumPtr ? EnumPtr->GetNameByValue((int64) Value).ToString() : TEXT("Invalid"); @@ -52,21 +90,26 @@ class ACTIONS_API UAction : public UObject /************************************************************************/ /* PROPERTIES */ /************************************************************************/ -public: - UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category = Action) +private: + UPROPERTY() + TWeakObjectPtr Owner; + + UPROPERTY() + EActionState State = EActionState::Preparing; + + UPROPERTY(SaveGame) + TArray ChildrenActions; + + + /** If true the action will tick. Tick can be enabled or disabled while running. */ + UPROPERTY(EditAnywhere, Category = Action) bool bWantsToTick = false; protected: // Tick length in seconds. 0 is default tick rate - UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = Action, BlueprintGetter = "GetTickRate") + UPROPERTY(EditDefaultsOnly, Category = Action) float TickRate = 0.15f; -private: - UPROPERTY(Transient) - EActionState State = EActionState::Preparing; - - UPROPERTY(SaveGame) - TSet ChildrenActions; public: /** Delegates */ @@ -85,7 +128,7 @@ class ACTIONS_API UAction : public UObject /************************************************************************/ /** Called to active an action if not already. */ - UFUNCTION(BlueprintCallable, Category = "Action", BlueprintInternalUseOnly) + UFUNCTION(BlueprintCallable, Category = Action, BlueprintInternalUseOnly) void Activate(); /** Internal Use Only. Called when the action is stopped from running by its owner */ @@ -157,55 +200,38 @@ class ACTIONS_API UAction : public UObject void ReceiveFinished(const EActionState Reason); - /** Helpers */ public: - FORCEINLINE bool CanTick() const + bool CanTick() const { - return bWantsToTick && IsRunning() && IsValid(GetOuter()); + return bWantsToTick && IsRunning(); } + UFUNCTION(BlueprintCallable, Category = Action) + void SetWantsToTick(bool bValue); + UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE bool IsRunning() const - { - return IsValid(this) && State == EActionState::Running; - } + bool GetWantsToTick() const; UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE bool Succeeded() const - { - return IsValid(this) && State == EActionState::Success; - } + float GetTickRate() const; UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE bool Failed() const - { - return IsValid(this) && State == EActionState::Failure; - } + bool IsRunning() const; UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE EActionState GetState() const - { - return State; - } + bool Succeeded() const; UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE UObject* const GetParent() const - { - return GetOuter(); - } + bool Failed() const; UFUNCTION(BlueprintPure, Category = Action) - FORCEINLINE UAction* GetParentAction() const - { - return Cast(GetOuter()); - } + EActionState GetState() const; - UFUNCTION(BlueprintGetter) - FORCEINLINE float GetTickRate() const - { - // Reduce TickRate Precision to 0.1ms - return FMath::FloorToFloat(TickRate * 10000.f) * 0.0001f; - } + UFUNCTION(BlueprintPure, Category = Action) + UObject* const GetParent() const; + + UFUNCTION(BlueprintPure, Category = Action) + UAction* GetParentAction() const; /** @return the object that executes the root action */ UFUNCTION(BlueprintPure, Category = Action) @@ -217,64 +243,18 @@ class ACTIONS_API UAction : public UObject /** @return the component if any that executes the root action */ UFUNCTION(BlueprintPure, Category = Action) - UActorComponent* GetOwnerComponent() const - { - return Cast(GetOwner()); - } + UActorComponent* GetOwnerComponent() const; - virtual UWorld* GetWorld() const override - { - // If we are a CDO, we must return nullptr to fool UObject::ImplementsGetWorld. - if (HasAllFlags(RF_ClassDefaultObject)) - return nullptr; - - const UObject* const InOwner = GetOwner(); - return InOwner ? InOwner->GetWorld() : nullptr; - } + UWorld* GetWorld() const override; #if WITH_GAMEPLAY_DEBUGGER void DescribeSelfToGameplayDebugger(class FGameplayDebugger_Actions& Debugger, int8 Indent) const; #endif // WITH_GAMEPLAY_DEBUGGER -private: - UActionsSubsystem* GetSubsystem() const - { - const UWorld* World = GetWorld(); - const UGameInstance* GI = World ? World->GetGameInstance() : nullptr; - return UGameInstance::GetSubsystem(GI); - } - -public: - /** STATIC */ - - /** - * Creates a new action. Templated version - * @param ActionType - * @param Owner of the action. If destroyed, the action will follow. - * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. - */ - template - static ActionType* Create(UObject* Owner, bool bAutoActivate = false) - { - static_assert( - !std::is_same_v, "Instantiating UAction is not allowed. Use a child class."); - static_assert(TIsDerivedFrom::IsDerived, "Provided class must inherit UAction."); - return Cast(Create(Owner, ActionType::StaticClass(), bAutoActivate)); - } + //~ Begin UObject Interface + void PostInitProperties() override; + //~ End UObject Interface - /** - * Creates a new action - * @param Owner of the action. If destroyed, the action will follow. - * @param Type of the action to create - * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. - */ - static UAction* Create(UObject* Owner, const TSubclassOf Type, bool bAutoActivate = false); - - /** - * Creates a new action - * @param Owner of the action. If destroyed, the action will follow. - * @param Template whose properties and class are used to create the action. - * @param bAutoActivate if true activates the action. If false, Action->Activate() can be called later. - */ - static UAction* Create(UObject* Owner, class UAction* Template, bool bAutoActivate = false); +protected: + UActionsSubsystem* GetSubsystem() const; }; diff --git a/Source/Actions/Public/ActionLibrary.h b/Source/Actions/Public/ActionLibrary.h index 3be8328..e114eec 100644 --- a/Source/Actions/Public/ActionLibrary.h +++ b/Source/Actions/Public/ActionLibrary.h @@ -1,11 +1,12 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once +#include "Action.h" + #include #include -#include "Action.h" #include "ActionLibrary.generated.h" @@ -15,10 +16,10 @@ class ACTIONS_API UActionLibrary : public UBlueprintFunctionLibrary GENERATED_BODY() public: - - UFUNCTION(BlueprintCallable, Category = Action, meta = (DisplayName = "Create Action", BlueprintInternalUseOnly = "true", DefaultToSelf = "Owner", WorldContext = "Owner")) + UFUNCTION(BlueprintCallable, Category = Action, + meta = (BlueprintInternalUseOnly = "true", DefaultToSelf = "Owner", WorldContext = "Owner")) static UAction* CreateAction(UObject* Owner, const TSubclassOf Type, bool bAutoActivate = false) { - return UAction::Create(Owner, Type.Get(), bAutoActivate); + return ::CreateAction(Owner, Type.Get(), bAutoActivate); } }; diff --git a/Source/Actions/Public/ActionsModule.h b/Source/Actions/Public/ActionsModule.h index ccd2dff..e695bbf 100644 --- a/Source/Actions/Public/ActionsModule.h +++ b/Source/Actions/Public/ActionsModule.h @@ -1,13 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once #include #include #if WITH_EDITOR -#include "Developer/AssetTools/Public/IAssetTools.h" -#include "Developer/AssetTools/Public/AssetToolsModule.h" -#endif //WITH_EDITOR +# include "Developer/AssetTools/Public/AssetToolsModule.h" +# include "Developer/AssetTools/Public/IAssetTools.h" + +#endif // WITH_EDITOR DECLARE_LOG_CATEGORY_EXTERN(LogActions, All, All); @@ -15,12 +16,14 @@ DECLARE_LOG_CATEGORY_EXTERN(LogActions, All, All); class FActionsModule : public IModuleInterface { public: - /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; - virtual bool SupportsDynamicReloading() override { return true; } + virtual bool SupportsDynamicReloading() override + { + return true; + } #if WITH_EDITOR EAssetTypeCategories::Type GetAssetCategoryBit() const diff --git a/Source/Actions/Public/ActionsSubsystem.h b/Source/Actions/Public/ActionsSubsystem.h index 1e91b01..07c5fb3 100644 --- a/Source/Actions/Public/ActionsSubsystem.h +++ b/Source/Actions/Public/ActionsSubsystem.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once @@ -36,15 +36,22 @@ struct FActionsTickGroup inline void Tick(float DeltaTime); private: - inline void DelayedTick(float DeltaTime); public: + bool operator==(const FActionsTickGroup& Other) const + { + return FMath::IsNearlyEqual(TickRate, Other.TickRate); + } + bool operator!=(const FActionsTickGroup& Other) const + { + return !(*this == Other); + } - FORCEINLINE bool operator==(const FActionsTickGroup& Other) const { return FMath::IsNearlyEqual(TickRate, Other.TickRate); } - FORCEINLINE bool operator!=(const FActionsTickGroup& Other) const { return !(*this == Other); } - - friend const uint32 GetTypeHash(const FActionsTickGroup& InGroup) { return GetTypeHash(InGroup.TickRate); } + friend const uint32 GetTypeHash(const FActionsTickGroup& InGroup) + { + return GetTypeHash(InGroup.TickRate); + } }; /** @@ -52,7 +59,7 @@ struct FActionsTickGroup * Used to cancel actions whose owner is destroyed */ USTRUCT() -struct FRootAction +struct FActionOwner { GENERATED_BODY() @@ -60,10 +67,10 @@ struct FRootAction TWeakObjectPtr Owner; UPROPERTY() - TArray Actions; + TArray> Actions; - FRootAction(UObject* Owner = nullptr) : Owner(Owner) {} + FActionOwner(UObject* Owner = nullptr) : Owner(Owner) {} void CancelAll(bool bShouldShrink = true); void CancelByPredicate(const TFunctionRef& Predicate, bool bShouldShrink = true); @@ -71,10 +78,18 @@ struct FRootAction /** * Operator overloading & Hashes */ - FORCEINLINE bool operator==(const FRootAction& Other) const { return Owner == Other.Owner; } - FORCEINLINE bool operator!=(const FRootAction& Other) const { return !(*this == Other); } - - friend uint32 GetTypeHash(const FRootAction& InAction) { return GetTypeHash(InAction.Owner); } + bool operator==(const FActionOwner& Other) const + { + return Owner == Other.Owner; + } + bool operator!=(const FActionOwner& Other) const + { + return !(*this == Other); + } + friend uint32 GetTypeHash(const FActionOwner& InAction) + { + return GetTypeHash(InAction.Owner); + } }; @@ -91,35 +106,34 @@ class ACTIONS_API UActionsSubsystem : public UGameInstanceSubsystem, public FTic friend UAction; private: - UPROPERTY(SaveGame) - TSet RootActions; + TSet ActionOwners; UPROPERTY(Transient) TArray TickGroups; protected: - virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; public: - //~ Begin Tickable GameObject Interface virtual void Tick(float DeltaTime) override; - virtual bool IsTickable() const override { - return RootActions.Num() > 0 || TickGroups.Num() > 0; + virtual bool IsTickable() const override + { + return ActionOwners.Num() > 0 || TickGroups.Num() > 0; } - virtual TStatId GetStatId() const override { + virtual TStatId GetStatId() const override + { RETURN_QUICK_DECLARE_CYCLE_STAT(UActionsSubsystem, STATGROUP_Tickables); } //~ End Tickable GameObject Interface - /** Cancel ALL current actions of the game. Use with care! */ + /** Cancel all current actions of the game. Use with care! */ void CancelAll(); /** Cancel all actions executing inside an object @@ -135,15 +149,15 @@ class ACTIONS_API UActionsSubsystem : public UGameInstanceSubsystem, public FTic void CancelByOwnerPredicate(UObject* Object, TFunctionRef Predicate); private: - void AddRootAction(UAction* Child); void AddActionToTickGroup(UAction* Child); + void RemoveActionFromTickGroup(UAction* Child); public: - #if WITH_GAMEPLAY_DEBUGGER - void DescribeOwnerToGameplayDebugger(UObject* Owner, const FName& BaseName, class FGameplayDebugger_Actions& Debugger) const; -#endif // WITH_GAMEPLAY_DEBUGGER + void DescribeOwnerToGameplayDebugger( + UObject* Owner, const FName& BaseName, class FGameplayDebugger_Actions& Debugger) const; +#endif // WITH_GAMEPLAY_DEBUGGER FORCEINLINE static UActionsSubsystem* Get(UWorld* World) diff --git a/Source/Actions/Public/BTT_RunAction.h b/Source/Actions/Public/BTT_RunAction.h index 89c5322..12f1c99 100644 --- a/Source/Actions/Public/BTT_RunAction.h +++ b/Source/Actions/Public/BTT_RunAction.h @@ -1,15 +1,15 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once -#include "CoreMinimal.h" -#include "BehaviorTree/BTTaskNode.h" - #include "Action.h" +#include "BehaviorTree/BTTaskNode.h" +#include "CoreMinimal.h" #include "BTT_RunAction.generated.h" + /** * */ @@ -19,7 +19,6 @@ class ACTIONS_API UBTT_RunAction : public UBTTaskNode GENERATED_BODY() public: - UPROPERTY(Instanced, EditAnywhere, BlueprintReadWrite, Category = "Node", meta = (DisplayName = "Action")) UAction* ActionType; @@ -32,7 +31,8 @@ class ACTIONS_API UBTT_RunAction : public UBTTaskNode virtual EBTNodeResult::Type ExecuteTask(UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory) override; virtual EBTNodeResult::Type AbortTask(UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory) override; - virtual void DescribeRuntimeValues(const UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory, EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const override; + virtual void DescribeRuntimeValues(const UBehaviorTreeComponent& InOwnerComp, uint8* NodeMemory, + EBTDescriptionVerbosity::Type Verbosity, TArray& Values) const override; virtual FString GetStaticDescription() const override; UFUNCTION() diff --git a/Source/Editor/ActionsEditor.Build.cs b/Source/Editor/ActionsEditor.Build.cs index 69bb111..d36d3a0 100644 --- a/Source/Editor/ActionsEditor.Build.cs +++ b/Source/Editor/ActionsEditor.Build.cs @@ -9,31 +9,11 @@ public ActionsEditor(ReadOnlyTargetRules TargetRules) : base(TargetRules) PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; PublicDependencyModuleNames.AddRange(new string[] { - "Core", - "InputCore" }); - PrivateDependencyModuleNames.AddRange( new string[] { + PrivateDependencyModuleNames.AddRange(new string[] { "Core", - "CoreUObject", - "Engine", "UnrealEd", - "Blutility", - "Slate", - "SlateCore", - "AssetTools", - "EditorStyle", - "KismetWidgets", - "KismetCompiler", - "BlueprintGraph", - "GraphEditor", - "Kismet", - "PropertyEditor", - "DetailCustomizations", - "ContentBrowser", - "Settings", - "AIModule", - "ToolMenus", "Actions" }); } diff --git a/Source/Editor/Private/ActionsEditor.cpp b/Source/Editor/Private/ActionsEditor.cpp index 4f3864a..9fe5b4b 100644 --- a/Source/Editor/Private/ActionsEditor.cpp +++ b/Source/Editor/Private/ActionsEditor.cpp @@ -1,12 +1,12 @@ // Copyright 2015-2019 Piperift. All Rights Reserved. #include "ActionsEditor.h" -#include -#include "Action.h" +#include +#include -#define LOCTEXT_NAMESPACE "UMapComponent" +#define LOCTEXT_NAMESPACE "FActionsEditorModule" DEFINE_LOG_CATEGORY(LogActionsEd) @@ -29,25 +29,25 @@ void FActionsEditorModule::ShutdownModule() } -void FActionsEditorModule::RegisterPropertyTypeCustomizations() -{ -} +void FActionsEditorModule::RegisterPropertyTypeCustomizations() {} void FActionsEditorModule::PrepareAutoGeneratedDefaultEvents() { - //Task events + // Task events RegisterDefaultEvent(UAction, ReceiveActivate); RegisterDefaultEvent(UAction, ReceiveTick); RegisterDefaultEvent(UAction, ReceiveFinished); } -void FActionsEditorModule::RegisterCustomPropertyTypeLayout(FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate) +void FActionsEditorModule::RegisterCustomPropertyTypeLayout( + FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate) { check(PropertyTypeName != NAME_None); static FName PropertyEditor("PropertyEditor"); - FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked(PropertyEditor); + FPropertyEditorModule& PropertyModule = + FModuleManager::GetModuleChecked(PropertyEditor); PropertyModule.RegisterCustomPropertyTypeLayout(PropertyTypeName, PropertyTypeLayoutDelegate); } diff --git a/Source/Editor/Private/Nodes/ActionNodeHelpers.h b/Source/Editor/Private/Nodes/ActionNodeHelpers.h deleted file mode 100644 index 2393ea6..0000000 --- a/Source/Editor/Private/Nodes/ActionNodeHelpers.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "UObject/ObjectMacros.h" -#include "BlueprintNodeSpawner.h" -#include "BlueprintFunctionNodeSpawner.h" -#include "BlueprintActionDatabaseRegistrar.h" -//#include "Kismet2/BlueprintEditorUtils.h" - -#include "Action.h" - -class UK2Node_Action; - -struct FActionNodeHelpers { - static void RegisterActionClassActions(FBlueprintActionDatabaseRegistrar& InActionRegister, UClass* NodeClass); - - static void SetNodeFunc(UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr ClassPtr); - - - static int32 RegistryActionClassAction(FBlueprintActionDatabaseRegistrar& InActionRegistar, UClass* NodeClass, UClass* Class); - - template < typename TBase > - static void GetAllBlueprintSubclasses(TSet>& OutSubclasses, bool bAllowAbstract, FString const& Path); -}; \ No newline at end of file diff --git a/Source/Editor/Private/Nodes/ActionReflection.h b/Source/Editor/Private/Nodes/ActionReflection.h deleted file mode 100644 index ac0c70a..0000000 --- a/Source/Editor/Private/Nodes/ActionReflection.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. - -#pragma once -#include -#include - -#include "ActionReflection.generated.h" - - -USTRUCT() -struct FBaseActionProperty -{ - GENERATED_BODY() - -protected: - - UPROPERTY() - TFieldPath Property; - - UPROPERTY() - FName Name; - - UPROPERTY() - FEdGraphPinType Type; - -public: - - FBaseActionProperty() {} - - FName GetFName() const { return Name; } - const FEdGraphPinType& GetType() const { return Type; } - FProperty* GetProperty() const { return Property.Get(); } - - bool IsValid() const { return Property != nullptr; } - - bool operator==(const FBaseActionProperty& Other) const - { - return Name == Other.Name && Type == Other.Type; - } - bool operator!=(const FBaseActionProperty& Other) const { return !operator==(Other); } - - friend int32 GetTypeHash(const FBaseActionProperty& Item) - { - return GetTypeHash(Item.GetFName()); - } - -protected: - - FBaseActionProperty(FProperty* Property) - : Property(Property) - { - RefreshProperty(); - } - -private: - - void RefreshProperty(); -}; - - -USTRUCT() -struct FDelegateActionProperty : public FBaseActionProperty -{ - GENERATED_BODY() - - FDelegateActionProperty(FMulticastDelegateProperty* Property = nullptr) - : FBaseActionProperty(Property) - {} - - FMulticastDelegateProperty* GetDelegate() const - { - return CastFieldChecked(Property.Get()); - } - - UFunction* GetFunction() const - { - return GetDelegate()->SignatureFunction; - } - - bool HasParams() const - { - auto* Function = GetFunction(); - return Function? Function->NumParms > 0 : false; - } -}; - - -USTRUCT() -struct FVariableActionProperty : public FBaseActionProperty -{ - GENERATED_BODY() - - FVariableActionProperty(FProperty* Property = nullptr) - : FBaseActionProperty(Property) - {} -}; - - -USTRUCT() -struct FActionProperties -{ - GENERATED_BODY() - - UPROPERTY() - TSet Variables; - - UPROPERTY() - TSet SimpleDelegates; - - UPROPERTY() - TSet ComplexDelegates; - - - bool operator==(const FActionProperties& Other) const; - bool operator!=(const FActionProperties& Other) const { return !operator==(Other); } - -private: - - template - bool Equals(const TSet& One, const TSet& Other) const - { - for(const T& Value : One) - { - const T* const OtherValue = Other.Find(Value); - if (!OtherValue || *OtherValue != Value) - { - // Element not found or not equal - return false; - } - } - return true; - } -}; - - -namespace ActionReflection -{ - bool GetVisibleProperties(UClass* Class, FActionProperties& OutProperties); -}; diff --git a/Source/Editor/Public/ActionsEditor.h b/Source/Editor/Public/ActionsEditor.h index 9ec1627..d25dff4 100644 --- a/Source/Editor/Public/ActionsEditor.h +++ b/Source/Editor/Public/ActionsEditor.h @@ -1,12 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once -#include "PropertyEditorModule.h" #include "IAssetTools.h" +#include "PropertyEditorModule.h" + DECLARE_LOG_CATEGORY_EXTERN(LogActionsEd, All, All) -class FActionsEditorModule: public IModuleInterface +class FActionsEditorModule : public IModuleInterface { public: virtual void StartupModule() override; @@ -18,12 +19,13 @@ class FActionsEditorModule: public IModuleInterface void PrepareAutoGeneratedDefaultEvents(); /** - * Registers a custom struct - * - * @param StructName The name of the struct to register for property customization - * @param StructLayoutDelegate The delegate to call to get the custom detail layout instance - */ - void RegisterCustomPropertyTypeLayout(FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate); + * Registers a custom struct + * + * @param StructName The name of the struct to register for property customization + * @param StructLayoutDelegate The delegate to call to get the custom detail layout instance + */ + void RegisterCustomPropertyTypeLayout( + FName PropertyTypeName, FOnGetPropertyTypeCustomizationInstance PropertyTypeLayoutDelegate); void RegisterAssetTypeAction(IAssetTools& AssetTools, TSharedRef Action) { @@ -31,14 +33,16 @@ class FActionsEditorModule: public IModuleInterface CreatedAssetTypeActions.Add(Action); } - //Simplify Registering generated default events -#define RegisterDefaultEventChecked(Class, FuncName) \ - (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent(this, Class::StaticClass(), GET_FUNCTION_NAME_CHECKED(Class, FuncName))) + // Simplify Registering generated default events +#define RegisterDefaultEventChecked(Class, FuncName) \ + (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent( \ + this, Class::StaticClass(), GET_FUNCTION_NAME_CHECKED(Class, FuncName))) -#define RegisterDefaultEvent(Class, FuncName) \ - (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent(this, Class::StaticClass(), FName(TEXT(#FuncName)))) +#define RegisterDefaultEvent(Class, FuncName) \ + (FKismetEditorUtilities::RegisterAutoGeneratedDefaultEvent( \ + this, Class::StaticClass(), FName(TEXT(#FuncName)))) /** All created asset type actions. Cached here so that we can unregister them during shutdown. */ - TArray< TSharedPtr > CreatedAssetTypeActions; + TArray > CreatedAssetTypeActions; }; \ No newline at end of file diff --git a/Source/Graph/ActionsGraph.Build.cs b/Source/Graph/ActionsGraph.Build.cs new file mode 100644 index 0000000..dbbb72f --- /dev/null +++ b/Source/Graph/ActionsGraph.Build.cs @@ -0,0 +1,27 @@ +// Copyright 2015-2020 Piperift. All Rights Reserved. + +using UnrealBuildTool; + +public class ActionsGraph : ModuleRules +{ + public ActionsGraph(ReadOnlyTargetRules TargetRules) : base(TargetRules) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { + }); + + PrivateDependencyModuleNames.AddRange(new string[] { + "Actions", + "Core", + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "ToolMenus", + "UnrealEd", + "KismetCompiler", + "BlueprintGraph" + }); + } +} diff --git a/Source/Editor/Private/Nodes/ActionNodeHelpers.cpp b/Source/Graph/Private/ActionNodeHelpers.cpp similarity index 65% rename from Source/Editor/Private/Nodes/ActionNodeHelpers.cpp rename to Source/Graph/Private/ActionNodeHelpers.cpp index a612c50..af72e93 100644 --- a/Source/Editor/Private/Nodes/ActionNodeHelpers.cpp +++ b/Source/Graph/Private/ActionNodeHelpers.cpp @@ -1,13 +1,16 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionNodeHelpers.h" -#include "K2Node_Action.h" #include "AssetRegistry/ARFilter.h" #include "AssetRegistry/AssetRegistryModule.h" +#include "K2Node_Action.h" + +#include -void FActionNodeHelpers::RegisterActionClassActions(FBlueprintActionDatabaseRegistrar& InActionRegister, UClass* NodeClass) +void FActionNodeHelpers::RegisterActionClassActions( + FBlueprintActionDatabaseRegistrar& InActionRegister, UClass* NodeClass) { UClass* TaskType = UAction::StaticClass(); @@ -22,11 +25,12 @@ void FActionNodeHelpers::RegisterActionClassActions(FBlueprintActionDatabaseRegi check(NewAction != nullptr); TWeakObjectPtr TargetClassPtr = const_cast(TargetClass); - NewAction->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(SetNodeFunc, TargetClassPtr); + NewAction->CustomizeNodeDelegate = + UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(SetNodeFunc, TargetClassPtr); if (NewAction) { - RegisteredCount += (int32)InActionRegister.AddBlueprintAction(TargetClass, NewAction); + RegisteredCount += (int32) InActionRegister.AddBlueprintAction(TargetClass, NewAction); } } } @@ -44,7 +48,7 @@ void FActionNodeHelpers::RegisterActionClassActions(FBlueprintActionDatabaseRegi RegisteredCount += RegistryActionClassAction(InActionRegister, NodeClass, Class); } - //Registry blueprint classes + // Registry blueprint classes /*TSet> BPClasses; GetAllBlueprintSubclasses(BPClasses, false, ""); for (auto& BPClass : BPClasses) @@ -62,7 +66,8 @@ void FActionNodeHelpers::RegisterActionClassActions(FBlueprintActionDatabaseRegi return; } -void FActionNodeHelpers::SetNodeFunc(UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr ClassPtr) +void FActionNodeHelpers::SetNodeFunc( + UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr ClassPtr) { UK2Node_Action* ActionNode = CastChecked(NewNode); if (ClassPtr.IsValid()) @@ -72,8 +77,9 @@ void FActionNodeHelpers::SetNodeFunc(UEdGraphNode* NewNode, bool /*bIsTemplateNo } } -template -void FActionNodeHelpers::GetAllBlueprintSubclasses(TSet>& Subclasses, bool bAllowAbstract, FString const& Path) +template +void FActionNodeHelpers::GetAllBlueprintSubclasses( + TSet>& Subclasses, bool bAllowAbstract, FString const& Path) { static const FName GeneratedClassTag = TEXT("GeneratedClass"); static const FName ClassFlagsTag = TEXT("ClassFlags"); @@ -82,24 +88,25 @@ void FActionNodeHelpers::GetAllBlueprintSubclasses(TSet>& S check(Base); // Load the asset registry module - FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked< FAssetRegistryModule >(FName("AssetRegistry")); + FAssetRegistryModule& AssetRegistryModule = + FModuleManager::LoadModuleChecked(FName("AssetRegistry")); IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); FName BaseClassName = Base->GetFName(); // Use the asset registry to get the set of all class names deriving from Base - TSet< FName > DerivedNames; + TSet DerivedNames; { - TArray< FName > BaseNames; + TArray BaseNames; BaseNames.Add(BaseClassName); - TSet< FName > Excluded; + TSet Excluded; AssetRegistry.GetDerivedClassNames(BaseNames, Excluded, DerivedNames); } - // Set up a filter and then pull asset data for all blueprints in the specified path from the asset registry. - // Note that this works in packaged builds too. Even though the blueprint itself cannot be loaded, its asset data - // still exists and is tied to the UBlueprint type. + // Set up a filter and then pull asset data for all blueprints in the specified path from the asset + // registry. Note that this works in packaged builds too. Even though the blueprint itself cannot be + // loaded, its asset data still exists and is tied to the UBlueprint type. FARFilter Filter; Filter.ClassNames.Add(UBlueprint::StaticClass()->GetFName()); Filter.bRecursiveClasses = true; @@ -109,7 +116,7 @@ void FActionNodeHelpers::GetAllBlueprintSubclasses(TSet>& S } Filter.bRecursivePaths = true; - TArray< FAssetData > AssetList; + TArray AssetList; AssetRegistry.GetAssets(Filter, AssetList); // Iterate over retrieved blueprint assets @@ -120,7 +127,8 @@ void FActionNodeHelpers::GetAllBlueprintSubclasses(TSet>& S if (GeneratedClassPath.IsSet()) { // Convert path to just the name part - const FString ClassObjectPath = FPackageName::ExportTextPathToObjectPath(GeneratedClassPath.GetValue()); + const FString ClassObjectPath = + FPackageName::ExportTextPathToObjectPath(GeneratedClassPath.GetValue()); const FString ClassName = FPackageName::ObjectPathToObjectName(ClassObjectPath); // Check if this class is in the derived set @@ -130,21 +138,23 @@ void FActionNodeHelpers::GetAllBlueprintSubclasses(TSet>& S } // Store using the path to the generated class - Subclasses.Add(TAssetSubclassOf{ FStringAssetReference(ClassObjectPath) }); + Subclasses.Add(TAssetSubclassOf{FStringAssetReference(ClassObjectPath)}); } } } -int32 FActionNodeHelpers::RegistryActionClassAction(FBlueprintActionDatabaseRegistrar& InActionRegistar, UClass* NodeClass, UClass* Class) +int32 FActionNodeHelpers::RegistryActionClassAction( + FBlueprintActionDatabaseRegistrar& InActionRegistar, UClass* NodeClass, UClass* Class) { UBlueprintNodeSpawner* NewAction = UBlueprintNodeSpawner::Create(NodeClass); check(NewAction != nullptr); - NewAction->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(SetNodeFunc, TWeakObjectPtr{ Class }); + NewAction->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic( + SetNodeFunc, TWeakObjectPtr{Class}); if (NewAction) { - return (int32)InActionRegistar.AddBlueprintAction(Class, NewAction); + return (int32) InActionRegistar.AddBlueprintAction(Class, NewAction); } return 0; diff --git a/Source/Editor/Private/Nodes/ActionReflection.cpp b/Source/Graph/Private/ActionReflection.cpp similarity index 57% rename from Source/Editor/Private/Nodes/ActionReflection.cpp rename to Source/Graph/Private/ActionReflection.cpp index a1b3222..49f27ed 100644 --- a/Source/Editor/Private/Nodes/ActionReflection.cpp +++ b/Source/Graph/Private/ActionReflection.cpp @@ -1,13 +1,13 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionReflection.h" -#include "EdGraphSchema_K2.h" +#include void FBaseActionProperty::RefreshProperty() { - if(Property.Get()) + if (Property.Get()) { Name = Property->GetFName(); const auto* K2Schema = GetDefault(); @@ -18,23 +18,24 @@ void FBaseActionProperty::RefreshProperty() bool ActionReflection::GetVisibleProperties(UClass* Class, FActionProperties& OutProperties) { - if(!Class) + if (!Class) { return false; } OutProperties = {}; - for (TFieldIterator PropertyIt(Class, EFieldIteratorFlags::IncludeSuper); PropertyIt; ++PropertyIt) + for (TFieldIterator PropertyIt(Class, EFieldIteratorFlags::IncludeSuper); PropertyIt; + ++PropertyIt) { FProperty* Property = *PropertyIt; // Delegate properties - if(auto* DelegateProperty = CastField(Property)) + if (auto* DelegateProperty = CastField(Property)) { if (DelegateProperty && Property->HasAllPropertyFlags(CPF_BlueprintAssignable)) { - FDelegateActionProperty ActionProperty{ DelegateProperty }; - if(ActionProperty.HasParams()) + FDelegateActionProperty ActionProperty{DelegateProperty}; + if (ActionProperty.HasParams()) { OutProperties.ComplexDelegates.Add(ActionProperty); } @@ -46,11 +47,11 @@ bool ActionReflection::GetVisibleProperties(UClass* Class, FActionProperties& Ou } // Variable properties else if (UEdGraphSchema_K2::IsPropertyExposedOnSpawn(Property) && - !Property->HasAnyPropertyFlags(CPF_Parm) && - !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance) && - Property->HasAllPropertyFlags(CPF_BlueprintVisible)) + !Property->HasAnyPropertyFlags(CPF_Parm) && + !Property->HasAnyPropertyFlags(CPF_DisableEditOnInstance) && + Property->HasAllPropertyFlags(CPF_BlueprintVisible)) { - OutProperties.Variables.Add({ Property }); + OutProperties.Variables.Add({Property}); } } return true; @@ -58,14 +59,13 @@ bool ActionReflection::GetVisibleProperties(UClass* Class, FActionProperties& Ou bool FActionProperties::operator==(const FActionProperties& Other) const { - if (Variables.Num() != Other.Variables.Num() || - SimpleDelegates.Num() != Other.SimpleDelegates.Num() || + if (Variables.Num() != Other.Variables.Num() || SimpleDelegates.Num() != Other.SimpleDelegates.Num() || ComplexDelegates.Num() != Other.ComplexDelegates.Num()) { return false; } - return Equals(Variables, Other.Variables) - && Equals(SimpleDelegates, Other.SimpleDelegates) - && Equals(ComplexDelegates, Other.ComplexDelegates); + return Equals(Variables, Other.Variables) && + Equals(SimpleDelegates, Other.SimpleDelegates) && + Equals(ComplexDelegates, Other.ComplexDelegates); } diff --git a/Source/Graph/Private/ActionsGraphModule.cpp b/Source/Graph/Private/ActionsGraphModule.cpp new file mode 100644 index 0000000..6b33e2e --- /dev/null +++ b/Source/Graph/Private/ActionsGraphModule.cpp @@ -0,0 +1,10 @@ +// Copyright 2015-2019 Piperift. All Rights Reserved. + +#include "ActionsGraphModule.h" + +#include + + +DEFINE_LOG_CATEGORY(LogActionsGraph) + +IMPLEMENT_GAME_MODULE(FActionsGraphModule, ActionsGraph); \ No newline at end of file diff --git a/Source/Editor/Private/Nodes/K2Node_Action.cpp b/Source/Graph/Private/K2Node_Action.cpp similarity index 68% rename from Source/Editor/Private/Nodes/K2Node_Action.cpp rename to Source/Graph/Private/K2Node_Action.cpp index c3bb793..731f726 100644 --- a/Source/Editor/Private/Nodes/K2Node_Action.cpp +++ b/Source/Graph/Private/K2Node_Action.cpp @@ -1,31 +1,28 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "K2Node_Action.h" -#include -#include +#include "ActionNodeHelpers.h" + +#include +#include #include +#include +#include #include -#include #include -#include +#include #include - -#include "ActionsEditor.h" -#include "ActionNodeHelpers.h" - -#include "ActionLibrary.h" -#include "Action.h" +#include #define LOCTEXT_NAMESPACE "ActionEditor" -const FName UK2Node_Action::ClassPinName{ "__Class" }; -const FName UK2Node_Action::OwnerPinName{ UEdGraphSchema_K2::PN_Self }; +const FName UK2Node_Action::ClassPinName{"__Class"}; +const FName UK2Node_Action::OwnerPinName{UEdGraphSchema_K2::PN_Self}; -UK2Node_Action::UK2Node_Action() - : Super() +UK2Node_Action::UK2Node_Action() : Super() { NodeTooltip = LOCTEXT("NodeTooltip", "Creates a new action"); } @@ -53,17 +50,18 @@ void UK2Node_Action::AllocateDefaultPins() SelfPin->PinFriendlyName = LOCTEXT("Owner", "Owner"); - //If we are not using a predefined class + // If we are not using a predefined class if (ShowClass()) { CreateClassPin(); } // Result pin - UClass* ReturnClass = ActionClass? ActionClass : GetClassPinBaseClass(); - UEdGraphPin* ResultPin = CreatePin(EGPD_Output, K2Schema->PC_Object, ReturnClass, K2Schema->PN_ReturnValue); + UClass* ReturnClass = ActionClass ? ActionClass : GetClassPinBaseClass(); + UEdGraphPin* ResultPin = + CreatePin(EGPD_Output, K2Schema->PC_Object, ReturnClass, K2Schema->PN_ReturnValue); - //Update class pins if we have an assigned class + // Update class pins if we have an assigned class if (ActionClass) { OnClassPinChanged(); @@ -75,7 +73,7 @@ void UK2Node_Action::AllocateDefaultPins() FLinearColor UK2Node_Action::GetNodeTitleColor() const { - return FColor{ 27, 240, 247 }; + return FColor{27, 240, 247}; } FText UK2Node_Action::GetNodeTitle(ENodeTitleType::Type TitleType) const @@ -129,7 +127,8 @@ bool UK2Node_Action::HasExternalDependencies(TArray* OptionalOut bool UK2Node_Action::IsCompatibleWithGraph(const UEdGraph* TargetGraph) const { UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(TargetGraph); - return Super::IsCompatibleWithGraph(TargetGraph) && (!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph); + return Super::IsCompatibleWithGraph(TargetGraph) && + (!Blueprint || FBlueprintEditorUtils::FindUserConstructionScript(Blueprint) != TargetGraph); } void UK2Node_Action::PinConnectionListChanged(UEdGraphPin* Pin) @@ -162,8 +161,8 @@ void UK2Node_Action::GetPinHoverText(const UEdGraphPin& Pin, FString& HoverTextO return Super::GetPinHoverText(Pin, HoverTextOut); } -//This will expand node for our custom object, with properties -//which are set as EditAnywhere and meta=(ExposeOnSpawn), or equivalent in blueprint. +// This will expand node for our custom object, with properties +// which are set as EditAnywhere and meta=(ExposeOnSpawn), or equivalent in blueprint. void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); @@ -171,38 +170,48 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(SourceGraph && Schema); - //Get static function + // Get static function static FName Create_FunctionName = GET_FUNCTION_NAME_CHECKED(UActionLibrary, CreateAction); static FName Activate_FunctionName = GET_FUNCTION_NAME_CHECKED(UAction, Activate); - //Set function parameter names + // Set function parameter names static FString ParamName_Type = FString(TEXT("Type")); /* Retrieve Pins */ - //Exec - UEdGraphPin* ExecPin = GetExecPin(); // Exec pins are those big arrows, connected with thick white lines. - UEdGraphPin* ThenPin = GetThenPin(); // Then pin is the same as exec pin, just on the other side (the out arrow). + // Exec + UEdGraphPin* ExecPin = + GetExecPin(); // Exec pins are those big arrows, connected with thick white lines. + UEdGraphPin* ThenPin = + GetThenPin(); // Then pin is the same as exec pin, just on the other side (the out arrow). - //Inputs + // Inputs UEdGraphPin* OwnerPin = GetOwnerPin(); - UEdGraphPin* ClassPin = GetClassPin(); // Get class pin which is used to determine which class to spawn. + UEdGraphPin* ClassPin = + GetClassPin(); // Get class pin which is used to determine which class to spawn. - //Outputs - UEdGraphPin* ResultPin = GetResultPin(); // Result pin, which will output our spawned object. + // Outputs + UEdGraphPin* ResultPin = GetResultPin(); // Result pin, which will output our spawned object. if (!HasWorldContext()) { - CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", "@@ node requires world context.").ToString(), this); - // we break exec links so this is the only error we get, don't want the CreateItemData node being considered and giving 'unexpected node' type warnings + CompilerContext.MessageLog.Error( + *LOCTEXT("CreateActionNodeMissingClass_Error", "@@ node requires world context.").ToString(), + this); + // we break exec links so this is the only error we get, don't want the CreateItemData node being + // considered and giving 'unexpected node' type warnings BreakAllNodeLinks(); return; } if (!ActionClass) { - CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", "Create Action node @@ must have a class specified.").ToString(), this); - // we break exec links so this is the only error we get, don't want the CreateItemData node being considered and giving 'unexpected node' type warnings + CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", + "Create Action node @@ must have a class specified.") + .ToString(), + this); + // we break exec links so this is the only error we get, don't want the CreateItemData node being + // considered and giving 'unexpected node' type warnings BreakAllNodeLinks(); return; } @@ -211,23 +220,24 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U ////////////////////////////////////////////////////////////////////////// // create 'UActionFunctionLibrary::CreateAction' call node - UK2Node_CallFunction* CreateActionNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); - //Attach function + UK2Node_CallFunction* CreateActionNode = + CompilerContext.SpawnIntermediateNode(this, SourceGraph); + // Attach function CreateActionNode->FunctionReference.SetExternalMember(Create_FunctionName, UActionLibrary::StaticClass()); CreateActionNode->AllocateDefaultPins(); - //allocate nodes for created widget. - UEdGraphPin* CreateAction_Exec = CreateActionNode->GetExecPin(); - UEdGraphPin* CreateAction_Owner = CreateActionNode->FindPinChecked(TEXT("Owner")); - UEdGraphPin* CreateAction_Type = CreateActionNode->FindPinChecked(ParamName_Type); + // allocate nodes for created widget. + UEdGraphPin* CreateAction_Exec = CreateActionNode->GetExecPin(); + UEdGraphPin* CreateAction_Owner = CreateActionNode->FindPinChecked(TEXT("Owner")); + UEdGraphPin* CreateAction_Type = CreateActionNode->FindPinChecked(ParamName_Type); UEdGraphPin* CreateAction_Result = CreateActionNode->GetReturnValuePin(); // Move 'exec' pin to 'UActionFunctionLibrary::CreateAction' CompilerContext.MovePinLinksToIntermediate(*ExecPin, *CreateAction_Exec); - //TODO: Create local variable for PrestatedClass + // TODO: Create local variable for PrestatedClass - //Move pin if connected else, copy the value + // Move pin if connected else, copy the value if (ShowClass() && ClassPin->LinkedTo.Num() > 0) { // Copy the 'blueprint' connection from the spawn node to 'UActionLibrary::CreateAction' @@ -246,7 +256,7 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U } // Move Result pin to 'UActionFunctionLibrary::CreateAction' - CreateAction_Result->PinType = ResultPin->PinType; // Copy type so it uses the right actor subclass + CreateAction_Result->PinType = ResultPin->PinType; // Copy type so it uses the right actor subclass CompilerContext.MovePinLinksToIntermediate(*ResultPin, *CreateAction_Result); @@ -254,10 +264,13 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U // create 'set var' nodes // Set all properties of the object - UEdGraphPin* LastThenPin = FKismetCompilerUtilities::GenerateAssignmentNodes(CompilerContext, SourceGraph, CreateActionNode, this, CreateAction_Result, ActionClass); + UEdGraphPin* LastThenPin = FKismetCompilerUtilities::GenerateAssignmentNodes( + CompilerContext, SourceGraph, CreateActionNode, this, CreateAction_Result, ActionClass); // For each delegate, define an event, bind it to delegate and implement a chain of assignments - for (TFieldIterator PropertyIt(ActionClass, EFieldIteratorFlags::IncludeSuper); PropertyIt && bIsErrorFree; ++PropertyIt) + for (TFieldIterator PropertyIt( + ActionClass, EFieldIteratorFlags::IncludeSuper); + PropertyIt && bIsErrorFree; ++PropertyIt) { FMulticastDelegateProperty* Property = *PropertyIt; @@ -266,19 +279,25 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U const UFunction* DelegateSignatureFunction = Property->SignatureFunction; if (DelegateSignatureFunction->NumParms < 1) { - bIsErrorFree &= FHelper::HandleDelegateImplementation(Property, CreateAction_Result, LastThenPin, this, SourceGraph, CompilerContext); + bIsErrorFree &= FHelper::HandleDelegateImplementation( + Property, CreateAction_Result, LastThenPin, this, SourceGraph, CompilerContext); } else { - bIsErrorFree &= FHelper::HandleDelegateBindImplementation(Property, CreateAction_Result, LastThenPin, this, SourceGraph, CompilerContext); + bIsErrorFree &= FHelper::HandleDelegateBindImplementation( + Property, CreateAction_Result, LastThenPin, this, SourceGraph, CompilerContext); } } } if (!bIsErrorFree) { - CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", "There was a compile error while binding delegates.").ToString(), this); - // we break exec links so this is the only error we get, don't want the CreateAction node being considered and giving 'unexpected node' type warnings + CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", + "There was a compile error while binding delegates.") + .ToString(), + this); + // we break exec links so this is the only error we get, don't want the CreateAction node being + // considered and giving 'unexpected node' type warnings BreakAllNodeLinks(); return; } @@ -286,12 +305,13 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U ////////////////////////////////////////////////////////////////////////// // create 'UAction::Activate' call node - UK2Node_CallFunction* ActivateActionNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); - //Attach function + UK2Node_CallFunction* ActivateActionNode = + CompilerContext.SpawnIntermediateNode(this, SourceGraph); + // Attach function ActivateActionNode->FunctionReference.SetExternalMember(Activate_FunctionName, UAction::StaticClass()); ActivateActionNode->AllocateDefaultPins(); - //allocate nodes for created widget. + // allocate nodes for created widget. UEdGraphPin* ActivateAction_Exec = ActivateActionNode->GetExecPin(); UEdGraphPin* ActivateAction_Self = ActivateActionNode->FindPinChecked(Schema->PN_Self); UEdGraphPin* ActivateAction_Then = ActivateActionNode->GetThenPin(); @@ -304,8 +324,12 @@ void UK2Node_Action::ExpandNode(class FKismetCompilerContext& CompilerContext, U if (!bIsErrorFree) { - CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", "There was a compile error while activating the action.").ToString(), this); - // we break exec links so this is the only error we get, don't want the CreateAction node being considered and giving 'unexpected node' type warnings + CompilerContext.MessageLog.Error(*LOCTEXT("CreateActionNodeMissingClass_Error", + "There was a compile error while activating the action.") + .ToString(), + this); + // we break exec links so this is the only error we get, don't want the CreateAction node being + // considered and giving 'unexpected node' type warnings BreakAllNodeLinks(); return; } @@ -329,7 +353,7 @@ void UK2Node_Action::ReallocatePinsDuringReconstruction(TArray& Ol void UK2Node_Action::GetNodeAttributes(TArray>& OutNodeAttributes) const { - const FString ClassToSpawnStr = ActionClass? ActionClass->GetName() : TEXT("InvalidClass"); + const FString ClassToSpawnStr = ActionClass ? ActionClass->GetName() : TEXT("InvalidClass"); OutNodeAttributes.Add(TKeyValuePair(TEXT("Type"), TEXT("CreateAction"))); OutNodeAttributes.Add(TKeyValuePair(TEXT("Class"), GetClass()->GetName())); OutNodeAttributes.Add(TKeyValuePair(TEXT("Name"), GetName())); @@ -352,7 +376,7 @@ void UK2Node_Action::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionReg } } -//Set context menu category in which our node will be present. +// Set context menu category in which our node will be present. FText UK2Node_Action::GetMenuCategory() const { return FText::FromString("Actions"); @@ -367,7 +391,7 @@ void UK2Node_Action::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContex if (!Context->Pin || Context->Pin == GetClassPin()) { FText Text; - if(ShowClass()) + if (ShowClass()) { Text = LOCTEXT("HideClass", "Hide Class pin"); } @@ -377,14 +401,10 @@ void UK2Node_Action::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContex } auto& Section = Menu->AddSection("ActionNode", LOCTEXT("ActionNodeMenuSection", "Action Node")); - Section.AddMenuEntry("ClassPinVisibility", - Text, - LOCTEXT("HideClassTooltip", "Hides the Class input pin"), - FSlateIcon(), - FUIAction( - FExecuteAction::CreateUObject(const_cast(this), &UK2Node_Action::ToogleShowClass) - ) - ); + Section.AddMenuEntry("ClassPinVisibility", Text, + LOCTEXT("HideClassTooltip", "Hides the Class input pin"), FSlateIcon(), + FUIAction(FExecuteAction::CreateUObject( + const_cast(this), &UK2Node_Action::ToogleShowClass))); } } } @@ -397,7 +417,7 @@ void UK2Node_Action::CreatePinsForClass(UClass* InClass, TArray* O { check(InClass != nullptr); - if(!ActionReflection::GetVisibleProperties(InClass, CurrentProperties)) + if (!ActionReflection::GetVisibleProperties(InClass, CurrentProperties)) { return; } @@ -405,10 +425,13 @@ void UK2Node_Action::CreatePinsForClass(UClass* InClass, TArray* O const UEdGraphSchema_K2* K2Schema = GetDefault(); const UObject* const CDO = InClass->GetDefaultObject(false); - for(const auto& Property : CurrentProperties.Variables) + for (const auto& Property : CurrentProperties.Variables) { UEdGraphPin* Pin = CreatePin(EGPD_Input, Property.GetType(), Property.GetFName()); - if(!Pin) { continue; } + if (!Pin) + { + continue; + } if (OutClassPins) { @@ -418,7 +441,8 @@ void UK2Node_Action::CreatePinsForClass(UClass* InClass, TArray* O if (CDO && K2Schema->PinDefaultValueIsEditable(*Pin)) { FString DefaultValueAsString; - const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString(Property.GetProperty(), reinterpret_cast(CDO), DefaultValueAsString); + const bool bDefaultValueSet = FBlueprintEditorUtils::PropertyValueToString( + Property.GetProperty(), reinterpret_cast(CDO), DefaultValueAsString); check(bDefaultValueSet); K2Schema->TrySetDefaultValue(*Pin, DefaultValueAsString); } @@ -427,23 +451,29 @@ void UK2Node_Action::CreatePinsForClass(UClass* InClass, TArray* O K2Schema->ConstructBasicPinTooltip(*Pin, Property.GetProperty()->GetToolTipText(), Pin->PinToolTip); } - for(const auto& Property : CurrentProperties.ComplexDelegates) + for (const auto& Property : CurrentProperties.ComplexDelegates) { if (!Property.GetFunction()) { - UE_LOG(LogActionsEd, Error, TEXT("Delegate '%s' may be corrupted"), *Property.GetFName().ToString()); + UE_LOG(LogActionsGraph, Error, TEXT("Delegate '%s' may be corrupted"), + *Property.GetFName().ToString()); } UEdGraphNode::FCreatePinParams Params{}; Params.bIsConst = true; Params.bIsReference = true; UEdGraphPin* Pin = CreatePin(EGPD_Input, K2Schema->PC_Delegate, Property.GetFName(), Params); - if(!Pin) { continue; } + if (!Pin) + { + continue; + } - Pin->PinFriendlyName = FText::Format(NSLOCTEXT("K2Node", "PinFriendlyDelegateName", "{0}"), FText::FromName(Property.GetFName())); + Pin->PinFriendlyName = FText::Format( + NSLOCTEXT("K2Node", "PinFriendlyDelegateName", "{0}"), FText::FromName(Property.GetFName())); - //Update PinType with the delegate's signature - FMemberReference::FillSimpleMemberReference(Property.GetFunction(), Pin->PinType.PinSubCategoryMemberReference); + // Update PinType with the delegate's signature + FMemberReference::FillSimpleMemberReference( + Property.GetFunction(), Pin->PinType.PinSubCategoryMemberReference); if (OutClassPins) { @@ -451,15 +481,19 @@ void UK2Node_Action::CreatePinsForClass(UClass* InClass, TArray* O } } - for(const auto& Property : CurrentProperties.SimpleDelegates) + for (const auto& Property : CurrentProperties.SimpleDelegates) { if (!Property.GetFunction()) { - UE_LOG(LogActionsEd, Error, TEXT("Delegate '%s' may be corrupted"), *Property.GetFName().ToString()); + UE_LOG(LogActionsGraph, Error, TEXT("Delegate '%s' may be corrupted"), + *Property.GetFName().ToString()); } UEdGraphPin* Pin = CreatePin(EGPD_Output, K2Schema->PC_Exec, Property.GetFName()); - if(!Pin) { continue; } + if (!Pin) + { + continue; + } if (OutClassPins) { @@ -476,14 +510,12 @@ bool UK2Node_Action::IsActionVarPin(UEdGraphPin* Pin) { const UEdGraphSchema_K2* K2Schema = GetDefault(); - return(Pin->PinName != K2Schema->PN_Execute && - Pin->PinName != K2Schema->PN_Then && - Pin->PinName != K2Schema->PN_ReturnValue && - Pin->PinName != ClassPinName && - Pin->PinName != OwnerPinName); + return (Pin->PinName != K2Schema->PN_Execute && Pin->PinName != K2Schema->PN_Then && + Pin->PinName != K2Schema->PN_ReturnValue && Pin->PinName != ClassPinName && + Pin->PinName != OwnerPinName); } -UEdGraphPin* UK2Node_Action::GetThenPin()const +UEdGraphPin* UK2Node_Action::GetThenPin() const { const UEdGraphSchema_K2* K2Schema = GetDefault(); @@ -494,7 +526,7 @@ UEdGraphPin* UK2Node_Action::GetThenPin()const UEdGraphPin* UK2Node_Action::GetClassPin(const TArray* InPinsToSearch /*= nullptr*/) const { - const TArray* PinsToSearch = InPinsToSearch? InPinsToSearch : &Pins; + const TArray* PinsToSearch = InPinsToSearch ? InPinsToSearch : &Pins; UEdGraphPin* Pin = nullptr; for (auto PinIt = PinsToSearch->CreateConstIterator(); PinIt; ++PinIt) @@ -533,7 +565,7 @@ UClass* UK2Node_Action::GetActionClassFromPin() const return ActionClass; } - if(UEdGraphPin* ClassPin = GetClassPin()) + if (UEdGraphPin* ClassPin = GetClassPin()) { if (ClassPin->DefaultObject && ClassPin->LinkedTo.Num() == 0) { @@ -542,7 +574,7 @@ UClass* UK2Node_Action::GetActionClassFromPin() const else if (ClassPin->LinkedTo.Num()) { auto ClassSource = ClassPin->LinkedTo[0]; - return ClassSource? Cast(ClassSource->PinType.PinSubCategoryObject.Get()) : nullptr; + return ClassSource ? Cast(ClassSource->PinType.PinSubCategoryObject.Get()) : nullptr; } } return nullptr; @@ -559,16 +591,16 @@ UBlueprint* UK2Node_Action::GetActionBlueprint() const ClassPath.RemoveFromEnd(TEXT("_C"), ESearchCase::CaseSensitive); const FString BPPath = FString::Printf(TEXT("Blueprint'%s'"), *ClassPath); - return Cast( - StaticFindObject(UBlueprint::StaticClass(), nullptr, *BPPath) - ); + return Cast(StaticFindObject(UBlueprint::StaticClass(), nullptr, *BPPath)); } bool UK2Node_Action::UseWorldContext() const { auto BP = GetBlueprint(); const UClass* ParentClass = BP ? BP->ParentClass : nullptr; - return ParentClass ? ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin) != nullptr : false; + return ParentClass + ? ParentClass->HasMetaDataHierarchical(FBlueprintMetadata::MD_ShowWorldContextPin) != nullptr + : false; } bool UK2Node_Action::HasWorldContext() const @@ -586,7 +618,7 @@ FText UK2Node_Action::GetNodeTitleFormat() const return LOCTEXT("Action_ClassTitle", "{ClassName}"); } -//which class can be used with this node to create objects. All childs of class can be used. +// which class can be used with this node to create objects. All childs of class can be used. UClass* UK2Node_Action::GetClassPinBaseClass() const { return UAction::StaticClass(); @@ -610,11 +642,11 @@ void UK2Node_Action::OnClassPinChanged() bool bClassVisibilityChanged = false; UEdGraphPin* ClassPin = GetClassPin(); - if(ShowClass() && !ClassPin) + if (ShowClass() && !ClassPin) { CreateClassPin(); } - else if(!ShowClass() && ClassPin) + else if (!ShowClass() && ClassPin) { // Class pin should exist, destroy it UBlueprint* Blueprint = GetBlueprint(); @@ -625,12 +657,12 @@ void UK2Node_Action::OnClassPinChanged() UEdGraphNode::DestroyPin(ClassPin); } - else // Class pin visibility doesnt change + else // Class pin visibility doesnt change { // Node will only refresh if the exposed variables and delegates changed FActionProperties Properties; const bool bFoundProperties = ActionReflection::GetVisibleProperties(ActionClass, Properties); - if(!bFoundProperties || Properties == CurrentProperties) + if (!bFoundProperties || Properties == CurrentProperties) { return; } @@ -710,9 +742,9 @@ void UK2Node_Action::BindBlueprintCompile() void UK2Node_Action::ToogleShowClass() { - if(!ShowClass()) + if (!ShowClass()) { - FScopedTransaction Transaction( LOCTEXT("ShowClassPin", "Show class pin") ); + FScopedTransaction Transaction(LOCTEXT("ShowClassPin", "Show class pin")); Modify(); bShowClass = !bShowClass; @@ -720,7 +752,7 @@ void UK2Node_Action::ToogleShowClass() } else { - FScopedTransaction Transaction( LOCTEXT("HideClassPin", "Hide class pin") ); + FScopedTransaction Transaction(LOCTEXT("HideClassPin", "Hide class pin")); Modify(); bShowClass = !bShowClass; @@ -737,47 +769,52 @@ UEdGraphPin* UK2Node_Action::CreateClassPin() // Add blueprint pin UEdGraphPin* ClassPin = CreatePin(EGPD_Input, K2Schema->PC_Class, GetClassPinBaseClass(), ClassPinName); ClassPin->PinFriendlyName = LOCTEXT("PinClassName", "Class"); - if(ActionClass) + if (ActionClass) { ClassPin->DefaultObject = ActionClass; } return ClassPin; } -bool UK2Node_Action::FHelper::ValidDataPin(const UEdGraphPin* Pin, EEdGraphPinDirection Direction, const UEdGraphSchema_K2* Schema) +bool UK2Node_Action::FHelper::ValidDataPin( + const UEdGraphPin* Pin, EEdGraphPinDirection Direction, const UEdGraphSchema_K2* Schema) { check(Schema); - const bool bValidDataPin = Pin - && (Pin->PinName != Schema->PN_Execute) - && (Pin->PinName != Schema->PN_Then) - && (Pin->PinType.PinCategory != Schema->PC_Exec); + const bool bValidDataPin = Pin && (Pin->PinName != Schema->PN_Execute) && + (Pin->PinName != Schema->PN_Then) && + (Pin->PinType.PinCategory != Schema->PC_Exec); const bool bProperDirection = Pin && (Pin->Direction == Direction); return bValidDataPin && bProperDirection; } -bool UK2Node_Action::FHelper::CreateDelegateForNewFunction(UEdGraphPin* DelegateInputPin, FName FunctionName, UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext) +bool UK2Node_Action::FHelper::CreateDelegateForNewFunction(UEdGraphPin* DelegateInputPin, FName FunctionName, + UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext) { const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(DelegateInputPin && Schema && CurrentNode && SourceGraph && (FunctionName != NAME_None)); bool bResult = true; // WORKAROUND, so we can create delegate from nonexistent function by avoiding check at expanding step - // instead simply: Schema->TryCreateConnection(AddDelegateNode->GetDelegatePin(), CurrentCENode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName)); + // instead simply: Schema->TryCreateConnection(AddDelegateNode->GetDelegatePin(), + // CurrentCENode->FindPinChecked(UK2Node_CustomEvent::DelegateOutputName)); UK2Node_Self* SelfNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); SelfNode->AllocateDefaultPins(); - UK2Node_CreateDelegate* CreateDelegateNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); + UK2Node_CreateDelegate* CreateDelegateNode = + CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); CreateDelegateNode->AllocateDefaultPins(); bResult &= Schema->TryCreateConnection(DelegateInputPin, CreateDelegateNode->GetDelegateOutPin()); - bResult &= Schema->TryCreateConnection(SelfNode->FindPinChecked(Schema->PN_Self), CreateDelegateNode->GetObjectInPin()); + bResult &= Schema->TryCreateConnection( + SelfNode->FindPinChecked(Schema->PN_Self), CreateDelegateNode->GetObjectInPin()); CreateDelegateNode->SetFunction(FunctionName); return bResult; } -bool UK2Node_Action::FHelper::CopyEventSignature(UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema) +bool UK2Node_Action::FHelper::CopyEventSignature( + UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema) { check(CENode && Function && Schema); @@ -795,54 +832,66 @@ bool UK2Node_Action::FHelper::CopyEventSignature(UK2Node_CustomEvent* CENode, UF return bResult; } -bool UK2Node_Action::FHelper::HandleDelegateImplementation( - FMulticastDelegateProperty* CurrentProperty, - UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, - UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext) +bool UK2Node_Action::FHelper::HandleDelegateImplementation(FMulticastDelegateProperty* CurrentProperty, + UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, UK2Node* CurrentNode, UEdGraph* SourceGraph, + FKismetCompilerContext& CompilerContext) { bool bIsErrorFree = true; const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); check(CurrentProperty && ProxyObjectPin && InOutLastThenPin && CurrentNode && SourceGraph && Schema); UEdGraphPin* PinForCurrentDelegateProperty = CurrentNode->FindPin(CurrentProperty->GetName()); - if (!PinForCurrentDelegateProperty || (Schema->PC_Exec != PinForCurrentDelegateProperty->PinType.PinCategory)) + if (!PinForCurrentDelegateProperty || + (Schema->PC_Exec != PinForCurrentDelegateProperty->PinType.PinCategory)) { - FText ErrorMessage = FText::Format(LOCTEXT("WrongDelegateProperty", "BaseAsyncConstructObject: Cannot find execution pin for delegate "), FText::FromString(CurrentProperty->GetName())); + FText ErrorMessage = + FText::Format(LOCTEXT("WrongDelegateProperty", + "BaseAsyncConstructObject: Cannot find execution pin for delegate "), + FText::FromString(CurrentProperty->GetName())); CompilerContext.MessageLog.Error(*ErrorMessage.ToString(), CurrentNode); return false; } - UK2Node_CustomEvent* CurrentCENode = CompilerContext.SpawnIntermediateEventNode(CurrentNode, PinForCurrentDelegateProperty, SourceGraph); + UK2Node_CustomEvent* CurrentCENode = CompilerContext.SpawnIntermediateEventNode( + CurrentNode, PinForCurrentDelegateProperty, SourceGraph); { - UK2Node_AddDelegate* AddDelegateNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); + UK2Node_AddDelegate* AddDelegateNode = + CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(CurrentNode); - bool const bIsSelfContext = Blueprint->SkeletonGeneratedClass->IsChildOf(CurrentProperty->GetOwnerClass()); + bool const bIsSelfContext = + Blueprint->SkeletonGeneratedClass->IsChildOf(CurrentProperty->GetOwnerClass()); AddDelegateNode->SetFromProperty(CurrentProperty, bIsSelfContext, CurrentProperty->GetOwnerClass()); AddDelegateNode->AllocateDefaultPins(); - bIsErrorFree &= Schema->TryCreateConnection(AddDelegateNode->FindPinChecked(Schema->PN_Self), ProxyObjectPin); - bIsErrorFree &= Schema->TryCreateConnection(InOutLastThenPin, AddDelegateNode->FindPinChecked(Schema->PN_Execute)); + bIsErrorFree &= + Schema->TryCreateConnection(AddDelegateNode->FindPinChecked(Schema->PN_Self), ProxyObjectPin); + bIsErrorFree &= Schema->TryCreateConnection( + InOutLastThenPin, AddDelegateNode->FindPinChecked(Schema->PN_Execute)); InOutLastThenPin = AddDelegateNode->FindPinChecked(Schema->PN_Then); - CurrentCENode->CustomFunctionName = *FString::Printf(TEXT("%s_%s"), *CurrentProperty->GetName(), *CompilerContext.GetGuid(CurrentNode)); + CurrentCENode->CustomFunctionName = *FString::Printf( + TEXT("%s_%s"), *CurrentProperty->GetName(), *CompilerContext.GetGuid(CurrentNode)); CurrentCENode->AllocateDefaultPins(); - bIsErrorFree &= FHelper::CreateDelegateForNewFunction(AddDelegateNode->GetDelegatePin(), CurrentCENode->GetFunctionName(), CurrentNode, SourceGraph, CompilerContext); - bIsErrorFree &= FHelper::CopyEventSignature(CurrentCENode, AddDelegateNode->GetDelegateSignature(), Schema); + bIsErrorFree &= FHelper::CreateDelegateForNewFunction(AddDelegateNode->GetDelegatePin(), + CurrentCENode->GetFunctionName(), CurrentNode, SourceGraph, CompilerContext); + bIsErrorFree &= + FHelper::CopyEventSignature(CurrentCENode, AddDelegateNode->GetDelegateSignature(), Schema); } UEdGraphPin* LastActivatedNodeThen = CurrentCENode->FindPinChecked(Schema->PN_Then); - bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*PinForCurrentDelegateProperty, *LastActivatedNodeThen).CanSafeConnect(); + bIsErrorFree &= + CompilerContext.MovePinLinksToIntermediate(*PinForCurrentDelegateProperty, *LastActivatedNodeThen) + .CanSafeConnect(); return bIsErrorFree; } -bool UK2Node_Action::FHelper::HandleDelegateBindImplementation( - FMulticastDelegateProperty* CurrentProperty, - UEdGraphPin* ObjectPin, UEdGraphPin*& InOutLastThenPin, - UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext) +bool UK2Node_Action::FHelper::HandleDelegateBindImplementation(FMulticastDelegateProperty* CurrentProperty, + UEdGraphPin* ObjectPin, UEdGraphPin*& InOutLastThenPin, UK2Node* CurrentNode, UEdGraph* SourceGraph, + FKismetCompilerContext& CompilerContext) { bool bIsErrorFree = true; const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); @@ -851,7 +900,8 @@ bool UK2Node_Action::FHelper::HandleDelegateBindImplementation( UEdGraphPin* DelegateRefPin = CurrentNode->FindPinChecked(CurrentProperty->GetName()); check(DelegateRefPin); - UK2Node_AddDelegate* AddDelegateNode = CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); + UK2Node_AddDelegate* AddDelegateNode = + CompilerContext.SpawnIntermediateNode(CurrentNode, SourceGraph); check(AddDelegateNode); @@ -859,11 +909,13 @@ bool UK2Node_Action::FHelper::HandleDelegateBindImplementation( AddDelegateNode->AllocateDefaultPins(); bIsErrorFree &= Schema->TryCreateConnection(AddDelegateNode->FindPinChecked(Schema->PN_Self), ObjectPin); - bIsErrorFree &= Schema->TryCreateConnection(InOutLastThenPin, AddDelegateNode->FindPinChecked(Schema->PN_Execute)); + bIsErrorFree &= + Schema->TryCreateConnection(InOutLastThenPin, AddDelegateNode->FindPinChecked(Schema->PN_Execute)); InOutLastThenPin = AddDelegateNode->FindPinChecked(Schema->PN_Then); UEdGraphPin* AddDelegate_DelegatePin = AddDelegateNode->GetDelegatePin(); - bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*DelegateRefPin, *AddDelegate_DelegatePin).CanSafeConnect(); + bIsErrorFree &= CompilerContext.MovePinLinksToIntermediate(*DelegateRefPin, *AddDelegate_DelegatePin) + .CanSafeConnect(); DelegateRefPin->PinType = AddDelegate_DelegatePin->PinType; return bIsErrorFree; diff --git a/Source/Graph/Public/ActionNodeHelpers.h b/Source/Graph/Public/ActionNodeHelpers.h new file mode 100644 index 0000000..d8d3e7b --- /dev/null +++ b/Source/Graph/Public/ActionNodeHelpers.h @@ -0,0 +1,28 @@ +// Copyright 2015-2023 Piperift. All Rights Reserved. + +#pragma once + +#include "BlueprintActionDatabaseRegistrar.h" +#include "BlueprintFunctionNodeSpawner.h" +#include "BlueprintNodeSpawner.h" +#include "CoreMinimal.h" +#include "UObject/ObjectMacros.h" + + +class UK2Node_Action; + +struct FActionNodeHelpers +{ + static void RegisterActionClassActions( + FBlueprintActionDatabaseRegistrar& InActionRegister, UClass* NodeClass); + + static void SetNodeFunc(UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr ClassPtr); + + + static int32 RegistryActionClassAction( + FBlueprintActionDatabaseRegistrar& InActionRegistar, UClass* NodeClass, UClass* Class); + + template + static void GetAllBlueprintSubclasses( + TSet>& OutSubclasses, bool bAllowAbstract, FString const& Path); +}; \ No newline at end of file diff --git a/Source/Graph/Public/ActionReflection.h b/Source/Graph/Public/ActionReflection.h new file mode 100644 index 0000000..d9ada4c --- /dev/null +++ b/Source/Graph/Public/ActionReflection.h @@ -0,0 +1,147 @@ +// Copyright 2015-2023 Piperift. All Rights Reserved. + +#pragma once + +#include + +#include "ActionReflection.generated.h" + + +USTRUCT() +struct FBaseActionProperty +{ + GENERATED_BODY() + +protected: + UPROPERTY() + TFieldPath Property; + + UPROPERTY() + FName Name; + + UPROPERTY() + FEdGraphPinType Type; + +public: + FBaseActionProperty() {} + + FName GetFName() const + { + return Name; + } + const FEdGraphPinType& GetType() const + { + return Type; + } + FProperty* GetProperty() const + { + return Property.Get(); + } + + bool IsValid() const + { + return Property != nullptr; + } + + bool operator==(const FBaseActionProperty& Other) const + { + return Name == Other.Name && Type == Other.Type; + } + bool operator!=(const FBaseActionProperty& Other) const + { + return !operator==(Other); + } + + friend int32 GetTypeHash(const FBaseActionProperty& Item) + { + return GetTypeHash(Item.GetFName()); + } + +protected: + FBaseActionProperty(FProperty* Property) : Property(Property) + { + RefreshProperty(); + } + +private: + void RefreshProperty(); +}; + + +USTRUCT() +struct FDelegateActionProperty : public FBaseActionProperty +{ + GENERATED_BODY() + + FDelegateActionProperty(FMulticastDelegateProperty* Property = nullptr) : FBaseActionProperty(Property) {} + + FMulticastDelegateProperty* GetDelegate() const + { + return CastFieldChecked(Property.Get()); + } + + UFunction* GetFunction() const + { + return GetDelegate()->SignatureFunction; + } + + bool HasParams() const + { + auto* Function = GetFunction(); + return Function ? Function->NumParms > 0 : false; + } +}; + + +USTRUCT() +struct FVariableActionProperty : public FBaseActionProperty +{ + GENERATED_BODY() + + FVariableActionProperty(FProperty* Property = nullptr) : FBaseActionProperty(Property) {} +}; + + +USTRUCT() +struct FActionProperties +{ + GENERATED_BODY() + + UPROPERTY() + TSet Variables; + + UPROPERTY() + TSet SimpleDelegates; + + UPROPERTY() + TSet ComplexDelegates; + + + bool operator==(const FActionProperties& Other) const; + bool operator!=(const FActionProperties& Other) const + { + return !operator==(Other); + } + +private: + template + bool Equals(const TSet& One, const TSet& Other) const + { + for (const T& Value : One) + { + const T* const OtherValue = Other.Find(Value); + if (!OtherValue || *OtherValue != Value) + { + // Element not found or not equal + return false; + } + } + return true; + } +}; + + +namespace ActionReflection +{ + bool GetVisibleProperties(UClass* Class, FActionProperties& OutProperties); +}; diff --git a/Source/Graph/Public/ActionsGraphModule.h b/Source/Graph/Public/ActionsGraphModule.h new file mode 100644 index 0000000..390d93d --- /dev/null +++ b/Source/Graph/Public/ActionsGraphModule.h @@ -0,0 +1,14 @@ +// Copyright 2015-2023 Piperift. All Rights Reserved. +#pragma once + +#include "PropertyEditorModule.h" + + +DECLARE_LOG_CATEGORY_EXTERN(LogActionsGraph, All, All) + +class FActionsGraphModule : public IModuleInterface +{ +public: + virtual void StartupModule() override {} + virtual void ShutdownModule() override {} +}; \ No newline at end of file diff --git a/Source/Editor/Private/Nodes/K2Node_Action.h b/Source/Graph/Public/K2Node_Action.h similarity index 73% rename from Source/Editor/Private/Nodes/K2Node_Action.h rename to Source/Graph/Public/K2Node_Action.h index f743696..0d09cee 100644 --- a/Source/Editor/Private/Nodes/K2Node_Action.h +++ b/Source/Graph/Public/K2Node_Action.h @@ -1,26 +1,24 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once -#include -#include -#include -#include +#include "ActionReflection.h" +#include #include #include #include -#include #include +#include #include #include +#include -#include "ActionReflection.h" #include "K2Node_Action.generated.h" UCLASS(Blueprintable) -class ACTIONSEDITOR_API UK2Node_Action : public UK2Node +class ACTIONSGRAPH_API UK2Node_Action : public UK2Node { GENERATED_BODY() @@ -28,7 +26,6 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node static const FName OwnerPinName; public: - UPROPERTY() UClass* ActionClass; @@ -36,7 +33,6 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node bool bShowClass = true; protected: - /** Output pin visibility control */ UPROPERTY(EditAnywhere, Category = PinOptions, EditFixedSize) TArray ShowPinForProperties; @@ -45,7 +41,6 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node FText NodeTooltip; private: - UPROPERTY() FActionProperties CurrentProperties; @@ -58,11 +53,9 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node public: - UK2Node_Action(); protected: - virtual void PostLoad() override; //~ Begin UEdGraphNode Interface. @@ -79,12 +72,16 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node // End UEdGraphNode interface. //~ Begin UK2Node Interface - virtual bool IsNodeSafeToIgnore() const override { return true; } + virtual bool IsNodeSafeToIgnore() const override + { + return true; + } virtual void ReallocatePinsDuringReconstruction(TArray& OldPins) override; virtual void GetNodeAttributes(TArray>& OutNodeAttributes) const override; virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override; virtual FText GetMenuCategory() const override; - virtual void GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const override; + virtual void GetNodeContextMenuActions( + UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const override; //~ End UK2Node Interface @@ -112,8 +109,14 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node virtual bool HasWorldContext() const; /** Returns if the node uses Owner input */ - virtual bool UseOwner() const { return true; } - virtual bool ShowClass() const { return bShowClass; } + virtual bool UseOwner() const + { + return true; + } + virtual bool ShowClass() const + { + return bShowClass; + } /** Gets the default node title when no class is selected */ virtual FText GetBaseNodeTitle() const; @@ -123,12 +126,12 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node virtual UClass* GetClassPinBaseClass() const; /** - * Takes the specified "MutatablePin" and sets its 'PinToolTip' field (according - * to the specified description) - * - * @param MutatablePin The pin you want to set tool-tip text on - * @param PinDescription A string describing the pin's purpose - */ + * Takes the specified "MutatablePin" and sets its 'PinToolTip' field (according + * to the specified description) + * + * @param MutatablePin The pin you want to set tool-tip text on + * @param PinDescription A string describing the pin's purpose + */ void SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const; /** Refresh pins when class was changed */ @@ -137,7 +140,6 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node void OnBlueprintCompiled(UBlueprint*); private: - void BindBlueprintCompile(); void ToogleShowClass(); @@ -145,31 +147,34 @@ class ACTIONSEDITOR_API UK2Node_Action : public UK2Node UEdGraphPin* CreateClassPin(); protected: - - struct ACTIONSEDITOR_API FHelper + struct ACTIONSGRAPH_API FHelper { struct FOutputPinAndLocalVariable { UEdGraphPin* OutputPin; UK2Node_TemporaryVariable* TempVar; - FOutputPinAndLocalVariable(UEdGraphPin* Pin, UK2Node_TemporaryVariable* Var) : OutputPin(Pin), TempVar(Var) {} + FOutputPinAndLocalVariable(UEdGraphPin* Pin, UK2Node_TemporaryVariable* Var) + : OutputPin(Pin) + , TempVar(Var) + {} }; - static bool ValidDataPin(const UEdGraphPin* Pin, EEdGraphPinDirection Direction, const UEdGraphSchema_K2* Schema); + static bool ValidDataPin( + const UEdGraphPin* Pin, EEdGraphPinDirection Direction, const UEdGraphSchema_K2* Schema); - static bool CreateDelegateForNewFunction(UEdGraphPin* DelegateInputPin, FName FunctionName, UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); + static bool CreateDelegateForNewFunction(UEdGraphPin* DelegateInputPin, FName FunctionName, + UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); - static bool CopyEventSignature(UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema); + static bool CopyEventSignature( + UK2Node_CustomEvent* CENode, UFunction* Function, const UEdGraphSchema_K2* Schema); - static bool HandleDelegateImplementation( - FMulticastDelegateProperty* CurrentProperty, - UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, - UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); + static bool HandleDelegateImplementation(FMulticastDelegateProperty* CurrentProperty, + UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, UK2Node* CurrentNode, + UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); - static bool HandleDelegateBindImplementation( - FMulticastDelegateProperty* CurrentProperty, - UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, - UK2Node* CurrentNode, UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); + static bool HandleDelegateBindImplementation(FMulticastDelegateProperty* CurrentProperty, + UEdGraphPin* ProxyObjectPin, UEdGraphPin*& InOutLastThenPin, UK2Node* CurrentNode, + UEdGraph* SourceGraph, FKismetCompilerContext& CompilerContext); }; }; \ No newline at end of file diff --git a/Source/Test/Private/Actions.spec.cpp b/Source/Test/Private/Actions.spec.cpp index 29765d9..85b8d2e 100644 --- a/Source/Test/Private/Actions.spec.cpp +++ b/Source/Test/Private/Actions.spec.cpp @@ -1,53 +1,51 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. -#include "TestHelpers.h" #include "TestAction.h" +#include "TestHelpers.h" + namespace { - constexpr uint32 Flags_Product = EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter; - constexpr uint32 Flags_Smoke = EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter; -} + constexpr uint32 Flags_Product = + EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter; + constexpr uint32 Flags_Smoke = + EAutomationTestFlags::ApplicationContextMask | EAutomationTestFlags::ProductFilter; +} // namespace #define BASE_SPEC FACESpec BEGIN_TESTSPEC(FSubsystemSpec, "ActionsExtension.Subsystem", Flags_Product) - UWorld* World = nullptr; +UWorld* World = nullptr; END_TESTSPEC(FSubsystemSpec) void FSubsystemSpec::Define() { - BeforeEach([this]() - { + BeforeEach([this]() { World = GetTestWorld(); }); - It("Subsystem is valid", [this]() - { + It("Subsystem is valid", [this]() { TestNotNull("Subsystem", UActionsSubsystem::Get(World)); }); } BEGIN_TESTSPEC(FActionsSpec, "ActionsExtension.Actions", Flags_Smoke) - UWorld* World = nullptr; +UWorld* World = nullptr; END_TESTSPEC(FActionsSpec) void FActionsSpec::Define() { - BeforeEach([this]() - { + BeforeEach([this]() { World = GetTestWorld(); }); - It("Can instantiate Action", [this]() - { - UTestAction* Action = UAction::Create(World); + It("Can instantiate Action", [this]() { + UTestAction* Action = CreateAction(World); TestNotNull("Action", Action); }); - It("Can activate Action", [this]() - { - UTestAction* Action = UAction::Create(World); + It("Can activate Action", [this]() { + UTestAction* Action = CreateAction(World); if (!Action) { Action->Activate(); diff --git a/Source/Test/Private/ActionsTest.cpp b/Source/Test/Private/ActionsTest.cpp index 16c2418..bbdd2c2 100644 --- a/Source/Test/Private/ActionsTest.cpp +++ b/Source/Test/Private/ActionsTest.cpp @@ -1,3 +1,3 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "ActionsTest.h" diff --git a/Source/Test/Private/TestAction.h b/Source/Test/Private/TestAction.h index b77d3fb..d6c5adc 100644 --- a/Source/Test/Private/TestAction.h +++ b/Source/Test/Private/TestAction.h @@ -1,12 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once +#include "Action.h" + #include -#include "Action.h" #include "TestAction.generated.h" + UCLASS() class UTestAction : public UAction { diff --git a/Source/Test/Private/TestHelpers.cpp b/Source/Test/Private/TestHelpers.cpp index 22bf327..8fbbbbd 100644 --- a/Source/Test/Private/TestHelpers.cpp +++ b/Source/Test/Private/TestHelpers.cpp @@ -1,13 +1,14 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #include "TestHelpers.h" #include #include +#include #include #include #include -#include + UWorld* FACESpec::GetTestWorld() const diff --git a/Source/Test/Public/ActionsTest.h b/Source/Test/Public/ActionsTest.h index 3f091c1..a668acc 100644 --- a/Source/Test/Public/ActionsTest.h +++ b/Source/Test/Public/ActionsTest.h @@ -1,4 +1,4 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once @@ -8,15 +8,16 @@ class FActionsTest : public IModuleInterface { public: - virtual void StartupModule() override {} virtual void ShutdownModule() override {} - static inline FActionsTest& Get() { + static inline FActionsTest& Get() + { return FModuleManager::LoadModuleChecked("ActionsTest"); } - static inline bool IsAvailable() { + static inline bool IsAvailable() + { return FModuleManager::Get().IsModuleLoaded("ActionsTest"); } }; diff --git a/Source/Test/Public/TestHelpers.h b/Source/Test/Public/TestHelpers.h index 56edcaf..cae3b69 100644 --- a/Source/Test/Public/TestHelpers.h +++ b/Source/Test/Public/TestHelpers.h @@ -1,22 +1,20 @@ -// Copyright 2015-2020 Piperift. All Rights Reserved. +// Copyright 2015-2023 Piperift. All Rights Reserved. #pragma once #include -#include #include +#include + class FACESpec : public FAutomationSpecBase { public: - - FACESpec(const FString& InName, const bool bInComplexTask) - : FAutomationSpecBase(InName, bInComplexTask) + FACESpec(const FString& InName, const bool bInComplexTask) : FAutomationSpecBase(InName, bInComplexTask) {} protected: - void TestNotImplemented() { AddWarning("Test not implemented."); @@ -27,52 +25,69 @@ class FACESpec : public FAutomationSpecBase #ifndef BASE_SPEC - #define BASE_SPEC FACESpec +# define BASE_SPEC FACESpec #endif -#define BEGIN_TESTSPEC_PRIVATE( TClass, PrettyName, TFlags, FileName, LineNumber ) \ - class TClass : public BASE_SPEC \ - { \ - using Super = BASE_SPEC; \ - public: \ - TClass( const FString& InName ) \ - : Super( InName, false ) { \ - static_assert((TFlags)&EAutomationTestFlags::ApplicationContextMask, "AutomationTest has no application flag. It shouldn't run. See AutomationTest.h."); \ - static_assert( (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::SmokeFilter) || \ - (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::EngineFilter) || \ - (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::ProductFilter) || \ - (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::PerfFilter) || \ - (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::StressFilter) || \ - (((TFlags)&EAutomationTestFlags::FilterMask) == EAutomationTestFlags::NegativeFilter), \ - "All AutomationTests must have exactly 1 filter type specified. See AutomationTest.h."); \ - } \ - virtual uint32 GetTestFlags() const override { return TFlags; } \ - using Super::GetTestSourceFileName; \ - virtual FString GetTestSourceFileName() const override { return FileName; } \ - using Super::GetTestSourceFileLine; \ - virtual int32 GetTestSourceFileLine() const override { return LineNumber; } \ - protected: \ - virtual FString GetBeautifiedTestName() const override { return PrettyName; } \ +#define BEGIN_TESTSPEC_PRIVATE(TClass, PrettyName, TFlags, FileName, LineNumber) \ + class TClass : public BASE_SPEC \ + { \ + using Super = BASE_SPEC; \ + \ + public: \ + TClass(const FString& InName) : Super(InName, false) \ + { \ + static_assert((TFlags) &EAutomationTestFlags::ApplicationContextMask, \ + "AutomationTest has no application flag. It shouldn't run. See AutomationTest.h."); \ + static_assert( \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::SmokeFilter) || \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::EngineFilter) || \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::ProductFilter) || \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::PerfFilter) || \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::StressFilter) || \ + (((TFlags) &EAutomationTestFlags::FilterMask) == EAutomationTestFlags::NegativeFilter), \ + "All AutomationTests must have exactly 1 filter type specified. See AutomationTest.h."); \ + } \ + virtual uint32 GetTestFlags() const override \ + { \ + return TFlags; \ + } \ + using Super::GetTestSourceFileName; \ + virtual FString GetTestSourceFileName() const override \ + { \ + return FileName; \ + } \ + using Super::GetTestSourceFileLine; \ + virtual int32 GetTestSourceFileLine() const override \ + { \ + return LineNumber; \ + } \ + \ + protected: \ + virtual FString GetBeautifiedTestName() const override \ + { \ + return PrettyName; \ + } \ virtual void Define() override; #if WITH_AUTOMATION_WORKER - #define TESTSPEC( TClass, PrettyName, TFlags ) \ +# define TESTSPEC(TClass, PrettyName, TFlags) \ BEGIN_TESTSPEC_PRIVATE(TClass, PrettyName, TFlags, __FILE__, __LINE__) \ - };\ - namespace\ - {\ - TClass TClass##AutomationSpecInstance( TEXT(#TClass) );\ + } \ + ; \ + namespace \ + { \ + TClass TClass##AutomationSpecInstance(TEXT(#TClass)); \ } - #define BEGIN_TESTSPEC( TClass, PrettyName, TFlags ) \ +# define BEGIN_TESTSPEC(TClass, PrettyName, TFlags) \ BEGIN_TESTSPEC_PRIVATE(TClass, PrettyName, TFlags, __FILE__, __LINE__) - #define END_TESTSPEC( TClass ) \ - };\ - namespace\ - {\ - TClass TClass##AutomationSpecInstance( TEXT(#TClass) );\ +# define END_TESTSPEC(TClass) \ + } \ + ; \ + namespace \ + { \ + TClass TClass##AutomationSpecInstance(TEXT(#TClass)); \ } #endif -