Files
UnrealEngineUWP/Engine/Source/Developer/GameplayDebugger/Private/GameplayDebuggerLocalController.cpp
Marc Audy ad1d2f130f Copying //UE4/Dev-Framework to //UE4/Dev-Main (Source: //UE4/Dev-Framework @ 2994668)
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2967465 on 2016/05/05 by Marc.Audy

	Fix VS2015 shadow variables fixes

Change 2970637 on 2016/05/09 by Martin.Wilson

	Fix incorrect conflict resolve in merge from main

Change 2976393 on 2016/05/12 by Zak.Middleton

	#ue4 - Set AudioComponent bUseAttachParentBounds=true to reduce cost of transform updates (avoids virtual CalcBounds() when transform changes).

	#tests PIE w/ audio

Change 2976770 on 2016/05/13 by Lina.Halper

	Improvement on mirroring fix branch

	#code review: Zak.Middleton

Change 2976774 on 2016/05/13 by Lukasz.Furman

	fixed missing memory callbacks for instanced behavior tree nodes
	#jira UE-30305

Change 2976775 on 2016/05/13 by Lukasz.Furman

	fixed multiple calls to behavior tree's instance cleanup
	#jira UE-30593

Change 2976801 on 2016/05/13 by Jon.Nabozny

	Add an optional argument FSkeletalMeshMerge. When set to EMeshBufferAccess::ForceCPUAndGPU, keeps a reference to the vertex buffer on CPU (e.g. for spawning particle effects).

	#JIRA UE-30405

Change 2976985 on 2016/05/13 by Jon.Nabozny

	Fix initializer list ordering for FSkeletalMeshMerge.

Change 2977532 on 2016/05/13 by Marc.Audy

	PR #2348: [Local Multiplayer] Gamepad player assignment improvements (Contributed by kukiric)
	#jira UE-30162

Change 2977637 on 2016/05/13 by Marc.Audy

	Add Get/Set controller ID for a player in gameplay statics
	#jira UE-28718

Change 2979387 on 2016/05/16 by Jon.Nabozny

	Initialize FBox used to store results for CalculateQuatACF96Bounds.

	#JIRA UE-30846

Change 2979968 on 2016/05/17 by bruce.nesbit

	Added comment in FCanvasLineItem to warn only SE_BLEND_Opaque will be used when rendering.

Change 2979969 on 2016/05/17 by bruce.nesbit

	Added comment in AddLine/AddPoint to warn only SE_BLEND_Opaque will be used when rendering.

Change 2980271 on 2016/05/17 by Lina.Halper

	Improved comment

	#code review: Benn.Gallagher

Change 2980317 on 2016/05/17 by Lukasz.Furman

	removing NavCollision from static mesh on property change and PostLoad if static mesh is not supposed to have one
	#ue4

Change 2980717 on 2016/05/17 by Zak.Middleton

	#ue4 - Optimize UCapsuleComponent::CalcBounds() to remove sqrt and use tighter bounding sphere radius.

Change 2981193 on 2016/05/17 by Lukasz.Furman

	fixed missing observers in behavior tree when dynamic subtree is changed while waiting for full restart (out of nodes)
	#ue4

Change 2981927 on 2016/05/18 by Lina.Halper

	- Remove vertex animation code
	  - Removing UVertexAnimation
	  - Fixed reinitialization issue that was triggered by SetVertexAnimation

	- todo: consolidate UMorphtarget and UVertexAnimBase

	#code review: James.Golding, Martin.Wilson

Change 2981957 on 2016/05/18 by Lina.Halper

	Add recursive stack check on update animation

	#code review: Martin.Wilson

Change 2982116 on 2016/05/18 by Benn.Gallagher

	Removed optimize macros accidentally left after bounds extension feature for skel meshes

Change 2982255 on 2016/05/18 by Jon.Nabozny

	FSkeletalMeshMerge constructor "MeshBufferAccess" default should be EMeshBufferAccess:Default instead of EMeshBufferAccess::ForceCPUAndGPU.

Change 2982607 on 2016/05/18 by Marc.Audy

	Cleanup places calling GetWorld() multiple times

Change 2982621 on 2016/05/18 by Marc.Audy

	Make UActorComponent::GetWorld final and inlined to avoid unnecessary function calls
	Put uncached logic in to a separate function

Change 2983424 on 2016/05/19 by Marc.Audy

	Minor tweaks to reduce GetWorld calls

Change 2983465 on 2016/05/19 by Lina.Halper

	Combine VertexAnimBase and MorphTarget to just MorphTarget

	 - VertexAnimBase is gone
	 - Modified most of VertexAnim to MorphTarget
	 - Removed state, time related data

	#code review: James.Golding, Rolando.Caloca

Change 2983609 on 2016/05/19 by Marc.Audy

	Inline AActor::GetLevel
	Half GetWorld() calls from AActor::GetNetMode()

Change 2983772 on 2016/05/19 by Marc.Audy

	Fix Mac compile

Change 2983931 on 2016/05/19 by Marc.Audy

	Remove pointless AccelMouse function

Change 2984061 on 2016/05/19 by Marc.Audy

	Reorg some headers to fix compilation issues

Change 2984409 on 2016/05/19 by Aaron.McLeran

	#jira UE-31049 Updating the Oculus Audio SDK to vs 1.02

	Implementing 2984316 from Releases/4.12 to Dev-Framework

Change 2984574 on 2016/05/19 by Aaron.McLeran

	Fix for platform headroom scalar. Using correct dB to linear formula (not one for power)

	dB = 20 * log(Linear), not dB = 10 * log(Linear)

Change 2985041 on 2016/05/20 by Jon.Nabozny

	ConvertQueryOverlap only returns the base actor if multiple actors have bSimulatePhysics enabled and are welded together.

	#JIRA UE-30484

Change 2985118 on 2016/05/20 by Marc.Audy

	Another attempt to convince Mac to build

Change 2985192 on 2016/05/20 by Marc.Audy

	Properly forward declare ABrush

Change 2985724 on 2016/05/20 by Zak.Middleton

	#ue4 - Optimize NaN and Infinite checks for FVector, FQuat, FRotator, FMatrix, FTransform. SIMD version VectorContainsNaNOrInfinite() also optimized on relevant platforms. Added startup tests for VectorContainsNaNOrInfinite().

	- All our "ContainsNaN()" tests are in fact "IsNaN() || IsInfinite()", which is the same as "!IsFinite()", so exploited this to simplify the tests. Both NaN and +/-Inf are not finite (http://en.cppreference.com/w/cpp/numeric/math/isfinite). In the future we should rename ContainsNaN().
	- Still need to audit some uses in shipping configs.

	#jira UE-30999

Change 2986016 on 2016/05/20 by Zak.Middleton

	#ue4 - Fix uint32 used for int32 values. Behavior was unaffected.

Change 2986017 on 2016/05/20 by Zak.Middleton

	#ue4 - Fix overlaps being dropped from within a FScopedMoveUpdate when rotation occurs. Remove invalid assert.

Change 2986833 on 2016/05/23 by Zak.Middleton

	#ue4 - Move ETeleportType from ActorComponent.h to EngineTypes.h. Add comment to FHitResult for ImpactPoint when it's penetrating.

Change 2986916 on 2016/05/23 by Rolando.Caloca

	DF - GPU morph targets proof of concept
	- Disabled by default
	- Not compatible with gpu skin cache
	- No extra memory required yet until it's used; creates buffers per frame (very slow!)

Change 2987539 on 2016/05/23 by Rolando.Caloca

	DF - Remove unused member and downgrade FActiveMorphTarget to not be a USTRUCT

Change 2987981 on 2016/05/24 by James.Golding

	PR #2162 : Exported AAudioVolume so that projects can derive custom classes.
	https://github.com/EpicGames/UnrealEngine/pull/2162
	#github 2162
	#jira UE-28533

Change 2987982 on 2016/05/24 by James.Golding

	PR #2318 : Fix memory allocation in CustomMeshComponent.cpp
	https://github.com/EpicGames/UnrealEngine/pull/2318
	#github 2318
	#jira UE-29864

Change 2987983 on 2016/05/24 by James.Golding

	Merging engine changes back from GDC demo:
	- Export FFIRFilterTimeBased from Engine module
	- Add FFIRFilterTimeBased::SetWindowDuration
	- Add FBaseCompactPose::CopyBonesTo

Change 2987984 on 2016/05/24 by James.Golding

	UE-30137 No longer include collision in StatiMeshComponent bounds (bounds no longer user for any collision calculation)

Change 2987985 on 2016/05/24 by James.Golding

	UE-27801 Export ConvertToCollisionChannel, ConvertToObjectType, and ConvertToTraceType members of UCollisionProfile

Change 2987987 on 2016/05/24 by James.Golding

	OR-17910 : Support 'show collision' in Test configuration

Change 2988123 on 2016/05/24 by Jon.Nabozny

	Prevent FBodyInstance::Weld causing duplicate PhsyXShapes to be created / added to ShapeToBodiesMap when toggling SimulatePhysics on PrimitiveComponent.

	#JIRA UE-31189

Change 2988449 on 2016/05/24 by Rolando.Caloca

	DF - Split FActiveMorphTarget's weight into its own array in prep for GPU friendly data

Change 2988485 on 2016/05/24 by Jon.Nabozny

	Swap order of setRigidBodyFlag and setRigidDynamicFlag inside UpdateInstanceSimulatePhysics to prevent PhysX error about Kinematic bodies not using CCD.

	#JIRA UE-30993

Change 2988969 on 2016/05/24 by Rolando.Caloca

	DF - GPU morph targets
	- Enable using r.MorphTarget.Mode 1

Change 2989645 on 2016/05/25 by Marc.Audy

	Apply CL# 2989481 to Dev-Framework
	#jira UE-31055

Change 2989987 on 2016/05/25 by Wes.Hunt

	Redo of CL#2982707 2982716 2982723 2983780 2983864 from //Orion/Dev-General in preparation for continuing Analytics refactor in a Dev branch.

	AnalyticsET support for arbitrary Json events.
	* AnalyticsET supports a new API, RecordEventJson.
	* API supports rvalue refs to avoid unnecessary copies of the attribute array.
	#AUTOMERGE using branch Orion-Dev-General-To-UE4-Dev-Framework of change#2982707 by Wes.Hunt on 2016/05/18 17:22:20.

	Remove Analytics code to divert legacy code to source data collector.
	#AUTOMERGE using branch Orion-Dev-General-To-UE4-Dev-Framework of change#2982716 by Wes.Hunt on 2016/05/18 17:27:25.

	Analytics no longer adds IsEditor attribute to all events. Wasn't actually used by anyone anymore.
	#AUTOMERGE using branch Orion-Dev-General-To-UE4-Dev-Framework of change#2982723 by Wes.Hunt on 2016/05/18 17:29:40.

	Modernize FAnalyticsEventAttribute usage.

	Replaced FAnalyticsEventAttribute various ctors with a perfect forwarding one that can convert them to strings.
	* The Name must be convertible to a string
	* The value must be convertible to a string via an AnalyticsConversion::ToString() overload.
	* Added/expanded the supported conversions to strings to analytics attribute values. See AnalyticsConversion.h which contains all the previously supported conversions and more.

	Added MakeAnalyticsEventAttributeArray(), which uses variadic templates to create an array of event attributes inline, which can be passed to RecordEvent[Json] and efficiently taken ownership of:
	    RecordEvent("EventName", MakeAnalyticsEventAttributeArray(
	        "Attr1", false,
	        "Attr2", 42.0,
	        "Attr3",  SomeMap,
	        "Attr4", SomeArray);

	#AUTOMERGE using branch Orion-Dev-General-To-UE4-Dev-Framework of change#2983780 by Wes.Hunt on 2016/05/19 13:51:48.

	Added missing assignment copy/move ops to FAnalyticsEventAttribute. Doh, should have looked at more usages of PLATFORM_COMPILER_HAS_DEFAULTED_FUNCTIONS...
	#AUTOMERGE using branch Orion-Dev-General-To-UE4-Dev-Framework of change#2983864 by Wes.Hunt on 2016/05/19 14:17:05.

Change 2989988 on 2016/05/25 by Wes.Hunt

	Fix usage of FAnalyticsEventAttribute constructor using a bitfield that was invalidated by the change to make FAnalyticsEventAttribute use a perfect forwarding constructor. bitfields cannot be perfect-forwarded... :(

Change 2990493 on 2016/05/25 by Joe.Graf

	Added tooltips to the collision analyzer's buttons

	#CodeReview: james.golding

Change 2991064 on 2016/05/26 by James.Golding

	- Remove many Animation headers from Engine.h
	- Remove AnimationAsset.h from SkeletalMeshComponent.h, Character.h, CharacterMovementComponent.h

Change 2991113 on 2016/05/26 by Benn.Gallagher

	Fixed edge case in random sequence player where we might trigger an extra loop when waiting on a blend to finish. And made it impossible for a shuffle list to start with the animation that is currently playing (seemingly duplicating the anim).

Change 2991163 on 2016/05/26 by Rolando.Caloca

	DF - Rename and refactor some morph target compute shader in prep for interop with skin cache

Change 2991167 on 2016/05/26 by Jon.Nabozny

	Add `#include "DataTable.h"` to GameplayTagsManager.h. FGameplayTagTableRow is derived from FTableRowBase which isn't necessarily included. This issue is hidden in most cases as "Engine.h" includes "DataTable.h".

Change 2991183 on 2016/05/26 by Wes.Hunt

	Disable general forwarding constructor for AnalyticsEventAttribute for non arithmetic types, so they are free to choose other overloads more appropriately.

Change 2991199 on 2016/05/26 by Wes.Hunt

	Drastically reducing the headers dependencies on analytics headers. Analytics headers no longer appear in PCH files, and rarely if ever appear in a header file.

	IAnalyticsProviderModule.h only touches 8 source files.
	Analytics.h only touches 8 source files.
	IAnalyticsProvider.h only touches 5 headers and 97 source files.
	AnalyticsET.h only touches 12 source files.

Change 2991301 on 2016/05/26 by James.Golding

	Fix CIS for Fortnite

Change 2991319 on 2016/05/26 by James.Golding

	Fix CIS for Orion

Change 2991373 on 2016/05/26 by Joe.Graf

	Tweaked the tooltip text for the collision analyzer record button to be correct for both states
	Added a common button style so that the buttons have a consistent look

	#CodeReview: james.golding

Change 2991401 on 2016/05/26 by James.Golding

	Fix UT CIS

Change 2991406 on 2016/05/26 by James.Golding

	Fix Ocean CIS

Change 2991491 on 2016/05/26 by Lina.Halper

	Moved MorphTarget.h

	- Checked in modified functions fo AnimationRuntime for other features coming up
	 - Should not change any behavior of existing content

	#code review: James.Golding, Rolando.Caloca

Change 2991494 on 2016/05/26 by Wes.Hunt

	Fix for Unity error in AnalyticsET module after hedaer dependency reduction

Change 2991503 on 2016/05/26 by Jon.Nabozny

	Fix issue where FConstraintInstance (inside UPhysicsConstraintComponent) is not editable in InstanceEditor but is editable in BlueprintEditor.

	#JIRA UE-31267

Change 2991562 on 2016/05/26 by Zak.Middleton

	#ue4 - Reduce allocations during movement and overlap queries and when grabbing shapes from physx actors.

Change 2991586 on 2016/05/26 by James.Golding

	More CIS fixes for Orion and Fortnite

Change 2991673 on 2016/05/26 by Wes.Hunt

	Another non-unity fix for Analytics include dependency reduction.

Change 2991733 on 2016/05/26 by Zak.Middleton

	#dev - Test map, 50 walking dudes.

Change 2991781 on 2016/05/26 by Lina.Halper

	Back out revision 15 from //UE4/Dev-Framework/Engine/Source/Runtime/Engine/Private/Animation/AnimationRuntime.cpp

	- Back out a part of changes that I didn't mean to check in.

Change 2991922 on 2016/05/26 by Zak.Middleton

	#ue4 - Maybe fix Mac build.

Change 2991957 on 2016/05/26 by Joe.Graf

	Fixed the collision analyzer file open text (said project instead of collision)

Change 2991991 on 2016/05/26 by Lina.Halper

	Fix compile error

Change 2992089 on 2016/05/26 by Zak.Middleton

	#ue4 - Fix Mac/PS4 build.

Change 2992108 on 2016/05/26 by Wes.Hunt

	Yet another non-unity fix for Analytics header inclusion reduction.

Change 2992190 on 2016/05/26 by Zak.Middleton

	#ue4 - Mark FHitResult, FOverlapResult, FOverlapInfo as POD types. Avoids destructors etc when in TArrays.

Change 2992593 on 2016/05/27 by Martin.Wilson

	Build fixes for non editor platforms

Change 2992885 on 2016/05/27 by Rolando.Caloca

	DF - Fix crash on thumbnails
	#jira UE-31398

Change 2993058 on 2016/05/27 by Lukasz.Furman

	fixed behavior tree getting stuck on ResumeLogic call
	#jira OR-22498

Change 2993064 on 2016/05/27 by Zak.Middleton

	#ue4 - Fix root motion network corrections not clearing root motion data.

	udn: https://udn.unrealengine.com/questions/294985/jittering-in-character-movement-during-networked-m.html
	#jira UE-31316

Change 2993215 on 2016/05/27 by Lukasz.Furman

	gameplay debugger fixes: navmesh rendering is not hidden after disabling tool, player stuck in spectator mode after disabling tool, confusing version description for categories without data packs
	added replicated input bindings for debugger's categories
	#ue4

Change 2993521 on 2016/05/27 by Zak.Middleton

	#ue4 - Reduce allocations in UI Canvas items.

Change 2993995 on 2016/05/30 by Mieszko.Zielinski

	Temporary fix for BBKeySelector not handling properly multiple UObject subtypes #UE4

	#jira UE-31435

Change 2993998 on 2016/05/30 by Mieszko.Zielinski

	Improves handling of a special case in EQS score normalization, where all items have the same score #UE4

	We used to set the normalized score of 1 for all items, which was counter intuitive if all items have scored 0 in an unnormalized test. The improve handling detects that and assigns score of 0 in that case.

Change 2993999 on 2016/05/30 by Mieszko.Zielinski

	Fixed FEQSParametrizedQueryExecutionRequest converting non-BB values into EQS params wrong #UE4

Change 2994000 on 2016/05/30 by Mieszko.Zielinski

	Exposed UNavigationInvokerComponent as part of ENGINE_API so that it can be spawned procedurally in C++ in game specific code #UE4

Change 2994003 on 2016/05/30 by Mieszko.Zielinski

	Fixed naming of console variable controllin v-logging of FGameplayAttribute #UE4

	The old name was copy-pasted from somewhere.

Change 2994007 on 2016/05/30 by Mieszko.Zielinski

	Fixed unregistering listeners from perception system not clearing up all data #UE4

	Also, introduced two precisely named functions, GetCurrentlyPerceivedActors and GetKnownPerceivedActors to replace ambiguous GetPerceivedActors
	Also, renamed UAIPerceptionComponent::TActorPerceptionContainer to UAIPerceptionComponent::FActorPerceptionContainer

Change 2994475 on 2016/05/31 by Wes.Hunt

	Fix Unity build failure for analytics inclusion reduction for IOSFlurry.

[CL 2994701 by Marc Audy in Main branch]
2016-05-31 13:51:34 -04:00

793 lines
28 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#include "GameplayDebuggerPrivatePCH.h"
#include "GameplayDebuggerLocalController.h"
#include "GameplayDebuggerPlayerManager.h"
#include "GameplayDebuggerAddonManager.h"
#include "GameplayDebuggerExtension.h"
#include "GameplayDebuggerConfig.h"
#include "Debug/DebugDrawService.h"
#include "DrawDebugHelpers.h"
#include "Engine/Selection.h"
#include "Engine/Canvas.h"
#include "GameFramework/PlayerInput.h"
#include "GameFramework/Pawn.h"
UGameplayDebuggerLocalController::UGameplayDebuggerLocalController(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer)
{
bSimulateMode = false;
bNeedsCleanup = false;
bIsSelectingActor = false;
bIsLocallyEnabled = false;
bPrevLocallyEnabled = false;
ActiveRowIdx = 0;
}
void UGameplayDebuggerLocalController::Initialize(AGameplayDebuggerCategoryReplicator& Replicator, AGameplayDebuggerPlayerManager& Manager)
{
CachedReplicator = &Replicator;
CachedPlayerManager = &Manager;
bSimulateMode = FGameplayDebuggerAddonBase::IsSimulateInEditor();
UDebugDrawService::Register(bSimulateMode ? TEXT("DebugAI") : TEXT("Game"), FDebugDrawDelegate::CreateUObject(this, &UGameplayDebuggerLocalController::OnDebugDraw));
if (bSimulateMode)
{
USelection::SelectionChangedEvent.AddUObject(this, &UGameplayDebuggerLocalController::OnSelectionChanged);
}
FGameplayDebuggerAddonManager& AddonManager = FGameplayDebuggerAddonManager::GetCurrent();
AddonManager.OnExtensionsChanged.AddUObject(this, &UGameplayDebuggerLocalController::OnExtensionsChanged);
AddonManager.OnCategoriesChanged.AddUObject(this, &UGameplayDebuggerLocalController::OnCategoriesChanged);
AddonManager.CreateExtensions(Replicator, Extensions);
OnCategoriesChanged();
const UGameplayDebuggerConfig* SettingsCDO = UGameplayDebuggerConfig::StaticClass()->GetDefaultObject<UGameplayDebuggerConfig>();
const FKey NumpadKeys[] = { EKeys::NumPadZero, EKeys::NumPadOne, EKeys::NumPadTwo, EKeys::NumPadThree, EKeys::NumPadFour,
EKeys::NumPadFive, EKeys::NumPadSix, EKeys::NumPadSeven, EKeys::NumPadEight, EKeys::NumPadNine };
const FKey CategorySlots[] = { SettingsCDO->CategorySlot0, SettingsCDO->CategorySlot1, SettingsCDO->CategorySlot2, SettingsCDO->CategorySlot3, SettingsCDO->CategorySlot4,
SettingsCDO->CategorySlot5, SettingsCDO->CategorySlot6, SettingsCDO->CategorySlot7, SettingsCDO->CategorySlot8, SettingsCDO->CategorySlot9 };
bool bIsNumpadOnly = true;
for (int32 Idx = 0; Idx < ARRAY_COUNT(CategorySlots); Idx++)
{
bool bHasPattern = false;
for (int32 PatternIdx = 0; PatternIdx < ARRAY_COUNT(NumpadKeys); PatternIdx++)
{
if (CategorySlots[Idx] == NumpadKeys[PatternIdx])
{
bHasPattern = true;
break;
}
}
if (!bHasPattern)
{
bIsNumpadOnly = false;
break;
}
}
ActivationKeyDesc = GetKeyDescriptionLong(SettingsCDO->ActivationKey);
RowUpKeyDesc = GetKeyDescriptionShort(SettingsCDO->CategoryRowPrevKey);
RowDownKeyDesc = GetKeyDescriptionShort(SettingsCDO->CategoryRowNextKey);
CategoryKeysDesc = bIsNumpadOnly ? TEXT("{yellow}Numpad{white}") : TEXT("highlighted keys");
PaddingLeft = SettingsCDO->DebugCanvasPaddingLeft;
PaddingRight = SettingsCDO->DebugCanvasPaddingRight;
PaddingTop = SettingsCDO->DebugCanvasPaddingTop;
PaddingBottom = SettingsCDO->DebugCanvasPaddingBottom;
bNeedsCleanup = true;
}
void UGameplayDebuggerLocalController::Cleanup()
{
if (bSimulateMode)
{
USelection::SelectionChangedEvent.RemoveAll(this);
}
bNeedsCleanup = false;
}
void UGameplayDebuggerLocalController::BeginDestroy()
{
Super::BeginDestroy();
if (bNeedsCleanup)
{
Cleanup();
}
}
void UGameplayDebuggerLocalController::OnDebugDraw(class UCanvas* Canvas, class APlayerController* PC)
{
if (CachedReplicator && CachedReplicator->IsEnabled())
{
FGameplayDebuggerCanvasContext CanvasContext(Canvas, GEngine->GetSmallFont());
CanvasContext.CursorX = CanvasContext.DefaultX = PaddingLeft;
CanvasContext.CursorY = CanvasContext.DefaultY = PaddingTop;
if (bSimulateMode)
{
DrawSimulateHeader(CanvasContext);
}
else
{
DrawHeader(CanvasContext);
}
if (DataPackMap.Num() != NumCategories)
{
RebuildDataPackMap();
}
const bool bHasDebugActor = CachedReplicator->HasDebugActor();
for (int32 Idx = 0; Idx < NumCategories; Idx++)
{
TSharedRef<FGameplayDebuggerCategory> Category = CachedReplicator->GetCategory(Idx);
if (Category->ShouldDrawCategory(bHasDebugActor))
{
if (Category->IsCategoryHeaderVisible())
{
DrawCategoryHeader(Idx, Category, CanvasContext);
}
Category->DrawCategory(CachedReplicator->GetReplicationOwner(), CanvasContext);
}
}
}
}
extern RENDERCORE_API FTexture* GWhiteTexture;
void UGameplayDebuggerLocalController::DrawHeader(FGameplayDebuggerCanvasContext& CanvasContext)
{
const int32 NumRows = (NumCategorySlots + 9) / 10;
const float LineHeight = CanvasContext.GetLineHeight();
const int32 NumExtensionRows = (Extensions.Num() > 0) ? 1 : 0;
const float CanvasSizeX = CanvasContext.Canvas->SizeX - PaddingLeft - PaddingRight;
const float BackgroundPadding = 5.0f;
const float BackgroundPaddingBothSides = BackgroundPadding * 2.0f;
if (NumRows > 1)
{
FCanvasTileItem TileItemUpper(FVector2D(0, 0), GWhiteTexture, FVector2D(CanvasSizeX + BackgroundPaddingBothSides, (LineHeight * (ActiveRowIdx + NumExtensionRows + 1)) + BackgroundPadding), FLinearColor(0, 0, 0, 0.2f));
FCanvasTileItem ActiveRowTileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(CanvasSizeX + BackgroundPaddingBothSides, LineHeight), FLinearColor(0, 0.5f, 0, 0.3f));
FCanvasTileItem TileItemLower(FVector2D(0, 0), GWhiteTexture, FVector2D(CanvasSizeX + BackgroundPaddingBothSides, LineHeight * ((NumRows - ActiveRowIdx - 1)) + BackgroundPadding), FLinearColor(0, 0, 0, 0.2f));
TileItemUpper.BlendMode = SE_BLEND_Translucent;
ActiveRowTileItem.BlendMode = SE_BLEND_Translucent;
TileItemLower.BlendMode = SE_BLEND_Translucent;
CanvasContext.DrawItem(TileItemUpper, PaddingLeft - BackgroundPadding, PaddingTop - BackgroundPadding);
CanvasContext.DrawItem(ActiveRowTileItem, PaddingLeft - BackgroundPadding, PaddingTop - BackgroundPadding + TileItemUpper.Size.Y);
CanvasContext.DrawItem(TileItemLower, PaddingLeft - BackgroundPadding, PaddingTop - BackgroundPadding + TileItemUpper.Size.Y + ActiveRowTileItem.Size.Y);
}
else
{
FCanvasTileItem TileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(CanvasSizeX, LineHeight * (NumRows + NumExtensionRows + 1)) + BackgroundPaddingBothSides, FLinearColor(0, 0, 0, 0.2f));
TileItem.BlendMode = SE_BLEND_Translucent;
CanvasContext.DrawItem(TileItem, PaddingLeft - BackgroundPadding, PaddingTop - BackgroundPadding);
}
CanvasContext.Printf(TEXT("Tap {yellow}%s{white} to close, use %s to toggle catories."), *ActivationKeyDesc, *CategoryKeysDesc);
const FString DebugActorDesc = FString::Printf(TEXT("Debug actor: {cyan}%s"), *CachedReplicator->GetDebugActorName().ToString());
float DebugActorSizeX = 0.0f, DebugActorSizeY = 0.0f;
CanvasContext.MeasureString(DebugActorDesc, DebugActorSizeX, DebugActorSizeY);
CanvasContext.PrintAt(CanvasContext.Canvas->SizeX - PaddingRight - DebugActorSizeX, PaddingTop, DebugActorDesc);
const FString TimestampDesc = FString::Printf(TEXT("Time: %.2fs"), CachedReplicator->GetWorld()->GetTimeSeconds());
float TimestampSizeX = 0.0f, TimestampSizeY = 0.0f;
CanvasContext.MeasureString(TimestampDesc, TimestampSizeX, TimestampSizeY);
CanvasContext.PrintAt((CanvasSizeX - TimestampSizeX) * 0.5f, PaddingTop, TimestampDesc);
if (NumRows > 1)
{
const FString ChangeRowDesc = FString::Printf(TEXT("Prev row: {yellow}%s\n{white}Next row: {yellow}%s"), *RowUpKeyDesc, *RowDownKeyDesc);
float RowDescSizeX = 0.0f, RowDescSizeY = 0.0f;
CanvasContext.MeasureString(ChangeRowDesc, RowDescSizeX, RowDescSizeY);
CanvasContext.PrintAt(CanvasContext.Canvas->SizeX - PaddingRight - RowDescSizeX, PaddingTop + LineHeight * (NumExtensionRows + 1), ChangeRowDesc);
}
if (NumExtensionRows)
{
FString ExtensionRowDesc;
for (int32 ExtensionIdx = 0; ExtensionIdx < Extensions.Num(); ExtensionIdx++)
{
FString ExtensionDesc = Extensions[ExtensionIdx]->GetDescription();
ExtensionDesc.ReplaceInline(TEXT("\n"), TEXT(""));
if (ExtensionDesc.Len())
{
if (ExtensionRowDesc.Len())
{
ExtensionRowDesc += FGameplayDebuggerCanvasStrings::SeparatorSpace;
}
ExtensionRowDesc += ExtensionDesc;
}
}
CanvasContext.Print(ExtensionRowDesc);
}
for (int32 RowIdx = 0; RowIdx < NumRows; RowIdx++)
{
FString CategoryRowDesc;
for (int32 Idx = 0; Idx < 10; Idx++)
{
const int32 CategorySlotIdx = (RowIdx * 10) + Idx;
if (SlotCategoryIds.IsValidIndex(CategorySlotIdx) &&
SlotNames.IsValidIndex(CategorySlotIdx) &&
SlotCategoryIds[CategorySlotIdx].Num())
{
TSharedRef<FGameplayDebuggerCategory> Category0 = CachedReplicator->GetCategory(SlotCategoryIds[CategorySlotIdx][0]);
const bool bIsEnabled = Category0->IsCategoryEnabled();
const FString CategoryColorName = (RowIdx == ActiveRowIdx) && (NumRows > 1) ?
(bIsEnabled ? *FGameplayDebuggerCanvasStrings::ColorNameEnabledActiveRow : *FGameplayDebuggerCanvasStrings::ColorNameDisabledActiveRow) :
(bIsEnabled ? *FGameplayDebuggerCanvasStrings::ColorNameEnabled : *FGameplayDebuggerCanvasStrings::ColorNameDisabled);
const FString CategoryDesc = (RowIdx == ActiveRowIdx) ?
FString::Printf(TEXT("%s{%s}%d:{%s}%s"),
Idx ? *FGameplayDebuggerCanvasStrings::SeparatorSpace : TEXT(""),
*FGameplayDebuggerCanvasStrings::ColorNameInput,
Idx,
*CategoryColorName,
*SlotNames[CategorySlotIdx]) :
FString::Printf(TEXT("%s{%s}%s"),
Idx ? *FGameplayDebuggerCanvasStrings::Separator : TEXT(""),
*CategoryColorName,
*SlotNames[CategorySlotIdx]);
CategoryRowDesc += CategoryDesc;
}
}
CanvasContext.Print(CategoryRowDesc);
}
CanvasContext.DefaultY = CanvasContext.CursorY + LineHeight;
}
void UGameplayDebuggerLocalController::DrawSimulateHeader(FGameplayDebuggerCanvasContext& CanvasContext)
{
const float LineHeight = CanvasContext.GetLineHeight();
const float NewCategoryPosY = (CanvasContext.DefaultY * 2.0f) + (LineHeight * 3.0f);
const float BackgroundPadding = 5.0f;
const FString TimestampDesc = FString::Printf(TEXT("GameplayDebugger time: %.2fs"), CachedReplicator->GetWorld()->GetTimeSeconds());
float TimestampSizeX = 0.0f, TimestampSizeY = 0.0f;
CanvasContext.MeasureString(TimestampDesc, TimestampSizeX, TimestampSizeY);
const FString DebugActorDesc = FString::Printf(TEXT("Selected actor: {cyan}%s"), *CachedReplicator->GetDebugActorName().ToString());
float DebugActorSizeX = 0.0f, DebugActorSizeY = 0.0f;
CanvasContext.MeasureString(DebugActorDesc, DebugActorSizeX, DebugActorSizeY);
const float BackgroundSizeX = FMath::Max(DebugActorSizeX, TimestampSizeX) + (BackgroundPadding * 2.f);
FCanvasTileItem TileItem(FVector2D(0, 0), GWhiteTexture, FVector2D(BackgroundSizeX, (2.0f * LineHeight) + (2.0f * BackgroundPadding)), FLinearColor(0, 0, 0, 0.2f));
TileItem.BlendMode = SE_BLEND_Translucent;
CanvasContext.DrawItem(TileItem, (CanvasContext.Canvas->SizeX - BackgroundSizeX) * 0.5f, PaddingTop - BackgroundPadding);
CanvasContext.PrintAt((CanvasContext.Canvas->SizeX - TimestampSizeX) * 0.5f, PaddingTop, TimestampDesc);
CanvasContext.PrintAt((CanvasContext.Canvas->SizeX - DebugActorSizeX) * 0.5f, PaddingTop + LineHeight, DebugActorDesc);
CanvasContext.DefaultY = CanvasContext.CursorY = NewCategoryPosY;
}
void UGameplayDebuggerLocalController::DrawCategoryHeader(int32 CategoryId, TSharedRef<FGameplayDebuggerCategory> Category, FGameplayDebuggerCanvasContext& CanvasContext)
{
FString DataPackDesc;
if (DataPackMap.IsValidIndex(CategoryId) &&
!Category->IsCategoryAuth() &&
!Category->ShouldDrawReplicationStatus() &&
Category->GetNumDataPacks() > 0)
{
// collect brief data pack status, detailed info is displayed only when ShouldDrawReplicationStatus is true
const int32 CurrentSyncCounter = CachedReplicator->GetDebugActorCounter();
DataPackDesc = TEXT("{white} ver[");
bool bIsPrevOutdated = false;
bool bAddSeparator = false;
for (int32 Idx = 0; Idx < DataPackMap[CategoryId].Num(); Idx++)
{
TSharedRef<FGameplayDebuggerCategory> MappedCategory = CachedReplicator->GetCategory(DataPackMap[CategoryId][Idx]);
for (int32 DataPackIdx = 0; DataPackIdx < MappedCategory->GetNumDataPacks(); DataPackIdx++)
{
FGameplayDebuggerDataPack::FHeader DataHeader = MappedCategory->GetDataPackHeaderCopy(DataPackIdx);
const bool bIsOutdated = (DataHeader.SyncCounter != CurrentSyncCounter);
if (bAddSeparator)
{
DataPackDesc += TEXT(';');
}
if (bIsOutdated != bIsPrevOutdated)
{
DataPackDesc += bIsOutdated ? TEXT("{red}") : TEXT("{white}");
bIsPrevOutdated = bIsOutdated;
}
DataPackDesc += TTypeToString<int16>::ToString(DataHeader.DataVersion);
bAddSeparator = true;
}
}
if (bIsPrevOutdated)
{
DataPackDesc += TEXT("{white}");
}
DataPackDesc += TEXT(']');
}
CanvasContext.MoveToNewLine();
CanvasContext.Printf(FColor::Green, TEXT("[CATEGORY: %s]%s"), *Category->GetCategoryName().ToString(), *DataPackDesc);
}
void UGameplayDebuggerLocalController::BindInput(UInputComponent& InputComponent)
{
TSet<FName> UsedBindings;
const UGameplayDebuggerConfig* SettingsCDO = UGameplayDebuggerConfig::StaticClass()->GetDefaultObject<UGameplayDebuggerConfig>();
InputComponent.BindKey(SettingsCDO->ActivationKey, IE_Pressed, this, &UGameplayDebuggerLocalController::OnActivationPressed);
InputComponent.BindKey(SettingsCDO->ActivationKey, IE_Released, this, &UGameplayDebuggerLocalController::OnActivationReleased);
UsedBindings.Add(SettingsCDO->ActivationKey.GetFName());
if (bIsLocallyEnabled)
{
InputComponent.BindKey(SettingsCDO->CategorySlot0, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory0Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot1, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory1Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot2, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory2Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot3, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory3Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot4, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory4Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot5, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory5Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot6, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory6Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot7, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory7Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot8, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory8Pressed);
InputComponent.BindKey(SettingsCDO->CategorySlot9, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategory9Pressed);
InputComponent.BindKey(SettingsCDO->CategoryRowPrevKey, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategoryRowUpPressed);
InputComponent.BindKey(SettingsCDO->CategoryRowNextKey, IE_Pressed, this, &UGameplayDebuggerLocalController::OnCategoryRowDownPressed);
UsedBindings.Add(SettingsCDO->CategorySlot0.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot1.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot2.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot3.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot4.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot5.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot6.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot7.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot8.GetFName());
UsedBindings.Add(SettingsCDO->CategorySlot9.GetFName());
UsedBindings.Add(SettingsCDO->CategoryRowPrevKey.GetFName());
UsedBindings.Add(SettingsCDO->CategoryRowNextKey.GetFName());
for (int32 Idx = 0; Idx < NumCategories; Idx++)
{
TSharedRef<FGameplayDebuggerCategory> Category = CachedReplicator->GetCategory(Idx);
const int32 NumInputHandlers = Category->GetNumInputHandlers();
for (int32 HandlerIdx = 0; HandlerIdx < NumInputHandlers; HandlerIdx++)
{
FGameplayDebuggerInputHandler& HandlerData = Category->GetInputHandler(HandlerIdx);
if (HandlerData.Modifier.bPressed || HandlerData.Modifier.bReleased)
{
FInputChord InputChord(FKey(HandlerData.KeyName), HandlerData.Modifier.bShift, HandlerData.Modifier.bCtrl, HandlerData.Modifier.bAlt, HandlerData.Modifier.bCmd);
FInputKeyBinding NewBinding(InputChord, HandlerData.Modifier.bPressed ? IE_Pressed : IE_Released);
NewBinding.KeyDelegate.GetDelegateForManualSet().BindUObject(this, &UGameplayDebuggerLocalController::OnCategoryBindingEvent, Idx, HandlerIdx);
InputComponent.KeyBindings.Add(NewBinding);
UsedBindings.Add(HandlerData.KeyName);
}
}
}
for (int32 Idx = 0; Idx < Extensions.Num(); Idx++)
{
TSharedRef<FGameplayDebuggerExtension> Extension = Extensions[Idx];
const int32 NumInputHandlers = Extension->GetNumInputHandlers();
for (int32 HandlerIdx = 0; HandlerIdx < NumInputHandlers; HandlerIdx++)
{
FGameplayDebuggerInputHandler& HandlerData = Extension->GetInputHandler(HandlerIdx);
if (HandlerData.Mode != EGameplayDebuggerInputMode::Local)
{
UE_LOG(LogGameplayDebug, Warning, TEXT("Gameplay debugger extension [%d] tried to use replicated input handler [%s], skipping!"), Idx, *HandlerData.ToString());
continue;
}
if (HandlerData.Modifier.bPressed || HandlerData.Modifier.bReleased)
{
FInputChord InputChord(FKey(HandlerData.KeyName), HandlerData.Modifier.bShift, HandlerData.Modifier.bCtrl, HandlerData.Modifier.bAlt, HandlerData.Modifier.bCmd);
FInputKeyBinding NewBinding(InputChord, HandlerData.Modifier.bPressed ? IE_Pressed : IE_Released);
NewBinding.KeyDelegate.GetDelegateForManualSet().BindUObject(this, &UGameplayDebuggerLocalController::OnExtensionBindingEvent, Idx, HandlerIdx);
InputComponent.KeyBindings.Add(NewBinding);
UsedBindings.Add(HandlerData.KeyName);
}
}
}
}
if (CachedReplicator && CachedReplicator->GetReplicationOwner() && CachedReplicator->GetReplicationOwner()->PlayerInput)
{
TSet<FName> RemovedMasks = MaskedDebugBindings.Difference(UsedBindings);
TSet<FName> AddedMasks = UsedBindings.Difference(MaskedDebugBindings);
UPlayerInput* Input = CachedReplicator->GetReplicationOwner()->PlayerInput;
for (int32 Idx = 0; Idx < Input->DebugExecBindings.Num(); Idx++)
{
FKeyBind& DebugBinding = Input->DebugExecBindings[Idx];
const bool bRemoveMask = RemovedMasks.Contains(DebugBinding.Key.GetFName());
const bool bAddMask = AddedMasks.Contains(DebugBinding.Key.GetFName());
if (bAddMask || bRemoveMask)
{
DebugBinding.bDisabled = bAddMask;
}
}
MaskedDebugBindings = UsedBindings;
}
}
void UGameplayDebuggerLocalController::OnActivationPressed()
{
bPrevLocallyEnabled = bIsLocallyEnabled;
if (CachedReplicator)
{
CachedReplicator->GetWorldTimerManager().SetTimer(StartSelectingActorHandle, this, &UGameplayDebuggerLocalController::OnStartSelectingActor, 0.2f);
}
}
void UGameplayDebuggerLocalController::OnActivationReleased()
{
if (CachedReplicator)
{
UWorld* World = CachedReplicator->GetWorld();
if (StartSelectingActorHandle.IsValid())
{
bIsLocallyEnabled = !CachedReplicator->IsEnabled();
CachedReplicator->SetEnabled(bIsLocallyEnabled);
if (bIsLocallyEnabled)
{
DebugActorCandidate = nullptr;
OnSelectActorTick();
}
}
World->GetTimerManager().ClearTimer(StartSelectingActorHandle);
World->GetTimerManager().ClearTimer(SelectActorTickHandle);
CachedReplicator->MarkComponentsRenderStateDirty();
}
StartSelectingActorHandle.Invalidate();
SelectActorTickHandle.Invalidate();
bIsSelectingActor = false;
if (bPrevLocallyEnabled != bIsLocallyEnabled)
{
if (bIsLocallyEnabled)
{
NotifyExtensionsActivation();
}
else
{
NotifyExtensionsDeactivation();
}
CachedPlayerManager->RefreshInputBindings(*CachedReplicator);
}
}
void UGameplayDebuggerLocalController::OnCategory0Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 0);
}
void UGameplayDebuggerLocalController::OnCategory1Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 1);
}
void UGameplayDebuggerLocalController::OnCategory2Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 2);
}
void UGameplayDebuggerLocalController::OnCategory3Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 3);
}
void UGameplayDebuggerLocalController::OnCategory4Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 4);
}
void UGameplayDebuggerLocalController::OnCategory5Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 5);
}
void UGameplayDebuggerLocalController::OnCategory6Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 6);
}
void UGameplayDebuggerLocalController::OnCategory7Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 7);
}
void UGameplayDebuggerLocalController::OnCategory8Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 8);
}
void UGameplayDebuggerLocalController::OnCategory9Pressed()
{
ToggleSlotState((ActiveRowIdx * 10) + 9);
}
void UGameplayDebuggerLocalController::OnCategoryRowUpPressed()
{
const int32 NumRows = (NumCategorySlots + 9) / 10;
ActiveRowIdx = (NumRows > 1) ? ((ActiveRowIdx + NumRows - 1) % NumRows) : 0;
}
void UGameplayDebuggerLocalController::OnCategoryRowDownPressed()
{
const int32 NumRows = (NumCategorySlots + 9) / 10;
ActiveRowIdx = (NumRows > 1) ? ((ActiveRowIdx + 1) % NumRows) : 0;
}
void UGameplayDebuggerLocalController::OnCategoryBindingEvent(int32 CategoryId, int32 HandlerId)
{
if (CachedReplicator)
{
CachedReplicator->SendInputEvent(CategoryId, HandlerId);
}
}
void UGameplayDebuggerLocalController::OnExtensionBindingEvent(int32 ExtensionId, int32 HandlerId)
{
if (Extensions.IsValidIndex(ExtensionId))
{
TSharedRef<FGameplayDebuggerExtension> Extension = Extensions[ExtensionId];
FGameplayDebuggerInputHandler& InputHandler = Extension->GetInputHandler(HandlerId);
InputHandler.Delegate.ExecuteIfBound();
}
}
void UGameplayDebuggerLocalController::OnStartSelectingActor()
{
StartSelectingActorHandle.Invalidate();
if (CachedReplicator)
{
if (!CachedReplicator->IsEnabled())
{
bIsLocallyEnabled = true;
CachedReplicator->SetEnabled(bIsLocallyEnabled);
}
bIsSelectingActor = true;
DebugActorCandidate = nullptr;
const bool bLooping = true;
CachedReplicator->GetWorldTimerManager().SetTimer(SelectActorTickHandle, this, &UGameplayDebuggerLocalController::OnSelectActorTick, 0.01f, bLooping);
OnSelectActorTick();
}
}
void UGameplayDebuggerLocalController::OnSelectActorTick()
{
APlayerController* OwnerPC = CachedReplicator ? CachedReplicator->GetReplicationOwner() : nullptr;
if (OwnerPC)
{
FVector CameraLocation;
FRotator CameraRotation;
OwnerPC->GetPlayerViewPoint(CameraLocation, CameraRotation);
// TODO: move to module's settings
const float MaxScanDistance = 25000.0f;
const float MinViewDirDot = 0.8f;
AActor* BestCandidate = nullptr;
float BestScore = MinViewDirDot;
const FVector ViewDir = CameraRotation.Vector();
for (FConstPawnIterator It = OwnerPC->GetWorld()->GetPawnIterator(); It; ++It)
{
APawn* TestPawn = *It;
if (TestPawn && !TestPawn->bHidden && TestPawn->GetActorEnableCollision() && TestPawn != OwnerPC->GetPawn())
{
FVector DirToPawn = (TestPawn->GetActorLocation() - CameraLocation);
float DistToPawn = DirToPawn.Size();
if (FMath::IsNearlyZero(DistToPawn))
{
DirToPawn = ViewDir;
DistToPawn = 1.0f;
}
else
{
DirToPawn /= DistToPawn;
}
const float ViewDot = FVector::DotProduct(ViewDir, DirToPawn);
if (DistToPawn < MaxScanDistance && ViewDot > BestScore)
{
BestScore = ViewDot;
BestCandidate = TestPawn;
}
}
}
// cache to avoid multiple RPC with the same actor
if (DebugActorCandidate != BestCandidate)
{
DebugActorCandidate = BestCandidate;
CachedReplicator->SetDebugActor(BestCandidate);
}
}
}
void UGameplayDebuggerLocalController::ToggleSlotState(int32 SlotIdx)
{
if (CachedReplicator && SlotCategoryIds.IsValidIndex(SlotIdx) && SlotCategoryIds[SlotIdx].Num())
{
const bool bIsEnabled = CachedReplicator->IsCategoryEnabled(SlotCategoryIds[SlotIdx][0]);
for (int32 Idx = 0; Idx < SlotCategoryIds[SlotIdx].Num(); Idx++)
{
const int32 CategoryId = SlotCategoryIds[SlotIdx][Idx];
CachedReplicator->SetCategoryEnabled(CategoryId, !bIsEnabled);
}
CachedReplicator->MarkComponentsRenderStateDirty();
}
}
FString UGameplayDebuggerLocalController::GetKeyDescriptionShort(const FKey& KeyBind) const
{
return FString::Printf(TEXT("[%s]"), *KeyBind.GetFName().ToString());
}
FString UGameplayDebuggerLocalController::GetKeyDescriptionLong(const FKey& KeyBind) const
{
const FString KeyDisplay = KeyBind.GetDisplayName().ToString();
const FString KeyName = KeyBind.GetFName().ToString();
return (KeyDisplay == KeyName) ? FString::Printf(TEXT("[%s]"), *KeyDisplay) : FString::Printf(TEXT("%s [%s key])"), *KeyDisplay, *KeyName);
}
void UGameplayDebuggerLocalController::OnSelectionChanged(UObject* Object)
{
USelection* Selection = Cast<USelection>(Object);
if (Selection && CachedReplicator)
{
APawn* SelectedPawn = nullptr;
for (int32 Idx = 0; Idx < Selection->Num(); Idx++)
{
AController* SelectedController = Cast<AController>(Selection->GetSelectedObject(Idx));
SelectedPawn = SelectedController ? SelectedController->GetPawn() : Cast<APawn>(Selection->GetSelectedObject(Idx));
if (SelectedPawn)
{
break;
}
}
CachedReplicator->SetDebugActor(SelectedPawn);
}
}
void UGameplayDebuggerLocalController::NotifyExtensionsActivation()
{
for (int32 Idx = 0; Idx < Extensions.Num(); Idx++)
{
Extensions[Idx]->OnActivated();
}
}
void UGameplayDebuggerLocalController::NotifyExtensionsDeactivation()
{
for (int32 Idx = 0; Idx < Extensions.Num(); Idx++)
{
Extensions[Idx]->OnDeactivated();
}
}
void UGameplayDebuggerLocalController::OnExtensionsChanged()
{
if (CachedReplicator == nullptr || CachedPlayerManager == nullptr)
{
return;
}
if (bIsLocallyEnabled)
{
NotifyExtensionsDeactivation();
}
Extensions.Reset();
FGameplayDebuggerAddonManager& AddonManager = FGameplayDebuggerAddonManager::GetCurrent();
AddonManager.CreateExtensions(*CachedReplicator, Extensions);
if (bIsLocallyEnabled)
{
NotifyExtensionsActivation();
CachedPlayerManager->RefreshInputBindings(*CachedReplicator);
}
}
void UGameplayDebuggerLocalController::OnCategoriesChanged()
{
FGameplayDebuggerAddonManager& AddonManager = FGameplayDebuggerAddonManager::GetCurrent();
SlotNames.Reset();
SlotNames.Append(AddonManager.GetSlotNames());
// categories are already sorted using AddonManager.SlotMap, build Slot to Category Id map accordingly
const TArray< TArray<int32> >& SlotMap = AddonManager.GetSlotMap();
SlotCategoryIds.Reset();
SlotCategoryIds.AddDefaulted(SlotMap.Num());
int32 CategoryId = 0;
for (int32 SlotIdx = 0; SlotIdx < SlotMap.Num(); SlotIdx++)
{
for (int32 InnerIdx = 0; InnerIdx < SlotMap[SlotIdx].Num(); InnerIdx++)
{
SlotCategoryIds[SlotIdx].Add(CategoryId);
CategoryId++;
}
}
NumCategorySlots = SlotCategoryIds.Num();
NumCategories = AddonManager.GetNumCategories();
DataPackMap.Reset();
}
void UGameplayDebuggerLocalController::RebuildDataPackMap()
{
DataPackMap.SetNum(NumCategories);
// category: get all categories from slot and combine data pack data if category header is not displayed
for (int32 SlotIdx = 0; SlotIdx < NumCategorySlots; SlotIdx++)
{
TArray<int32> NoHeaderCategories;
int32 FirstVisibleCategoryId = INDEX_NONE;
for (int32 InnerIdx = 0; InnerIdx < SlotCategoryIds[SlotIdx].Num(); InnerIdx++)
{
const int32 CategoryId = SlotCategoryIds[SlotIdx][InnerIdx];
TSharedRef<FGameplayDebuggerCategory> Category = CachedReplicator->GetCategory(CategoryId);
if (!Category->IsCategoryHeaderVisible())
{
NoHeaderCategories.Add(CategoryId);
}
else
{
DataPackMap[CategoryId].Add(CategoryId);
if (FirstVisibleCategoryId == INDEX_NONE)
{
FirstVisibleCategoryId = CategoryId;
}
}
}
if ((FirstVisibleCategoryId != INDEX_NONE) && NoHeaderCategories.Num())
{
DataPackMap[FirstVisibleCategoryId].Append(NoHeaderCategories);
}
}
}