IntialCommit
This commit is contained in:
75
.gitignore
vendored
Normal file
75
.gitignore
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
# ---> UnrealEngine
|
||||
# Visual Studio 2015 user specific files
|
||||
.vs/
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.ipa
|
||||
|
||||
# These project files can be generated by the engine
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
*.sln
|
||||
*.suo
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.VC.db
|
||||
*.VC.opendb
|
||||
|
||||
# Precompiled Assets
|
||||
SourceArt/**/*.png
|
||||
SourceArt/**/*.tga
|
||||
|
||||
# Binary Files
|
||||
Binaries/*
|
||||
Plugins/**/Binaries/*
|
||||
|
||||
# Builds
|
||||
Build/*
|
||||
|
||||
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
|
||||
!Build/*/
|
||||
Build/*/**
|
||||
!Build/*/PakBlacklist*.txt
|
||||
|
||||
# Don't ignore icon files in Build
|
||||
!Build/**/*.ico
|
||||
|
||||
# Built data for maps
|
||||
*_BuiltData.uasset
|
||||
|
||||
# Configuration files generated by the Editor
|
||||
Saved/*
|
||||
|
||||
# Compiled source files for the engine to use
|
||||
Intermediate/*
|
||||
Plugins/**/Intermediate/*
|
||||
|
||||
# Cache files for the editor to use
|
||||
DerivedDataCache/*
|
||||
17
.vsconfig
Normal file
17
.vsconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "1.0",
|
||||
"components": [
|
||||
"Component.Unreal.Debugger",
|
||||
"Component.Unreal.Ide",
|
||||
"Microsoft.Net.Component.4.6.2.TargetingPack",
|
||||
"Microsoft.VisualStudio.Component.VC.14.38.17.8.ATL",
|
||||
"Microsoft.VisualStudio.Component.VC.14.38.17.8.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.VC.Llvm.Clang",
|
||||
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
|
||||
"Microsoft.VisualStudio.Component.Windows11SDK.22621",
|
||||
"Microsoft.VisualStudio.Workload.CoreEditor",
|
||||
"Microsoft.VisualStudio.Workload.ManagedDesktop",
|
||||
"Microsoft.VisualStudio.Workload.NativeDesktop",
|
||||
"Microsoft.VisualStudio.Workload.NativeGame"
|
||||
]
|
||||
}
|
||||
2
Config/DefaultEditor.ini
Normal file
2
Config/DefaultEditor.ini
Normal file
@@ -0,0 +1,2 @@
|
||||
|
||||
|
||||
73
Config/DefaultEngine.ini
Normal file
73
Config/DefaultEngine.ini
Normal file
@@ -0,0 +1,73 @@
|
||||
[URL]
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=True
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
EditorStartupMap=/Game/Maps/Main.Main
|
||||
GameDefaultMap=/Game/Maps/Main.Main
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Desktop
|
||||
AppliedTargetedHardwareClass=Desktop
|
||||
DefaultGraphicsPerformance=Maximum
|
||||
AppliedDefaultGraphicsPerformance=Maximum
|
||||
|
||||
[/Script/Engine.PhysicsSettings]
|
||||
DefaultGravityZ=-980.000000
|
||||
DefaultTerminalVelocity=4000.000000
|
||||
DefaultFluidFriction=0.300000
|
||||
SimulateScratchMemorySize=262144
|
||||
RagdollAggregateThreshold=4
|
||||
TriangleMeshTriangleMinAreaThreshold=5.000000
|
||||
bEnableShapeSharing=False
|
||||
bEnablePCM=True
|
||||
bEnableStabilization=False
|
||||
bWarnMissingLocks=True
|
||||
bEnable2DPhysics=False
|
||||
PhysicErrorCorrection=(PingExtrapolation=0.100000,PingLimit=100.000000,ErrorPerLinearDifference=1.000000,ErrorPerAngularDifference=1.000000,MaxRestoredStateError=1.000000,MaxLinearHardSnapDistance=400.000000,PositionLerp=0.000000,AngleLerp=0.400000,LinearVelocityCoefficient=100.000000,AngularVelocityCoefficient=10.000000,ErrorAccumulationSeconds=0.500000,ErrorAccumulationDistanceSq=15.000000,ErrorAccumulationSimilarity=100.000000)
|
||||
LockedAxis=Invalid
|
||||
DefaultDegreesOfFreedom=Full3D
|
||||
BounceThresholdVelocity=200.000000
|
||||
FrictionCombineMode=Average
|
||||
RestitutionCombineMode=Average
|
||||
MaxAngularVelocity=3600.000000
|
||||
MaxDepenetrationVelocity=0.000000
|
||||
ContactOffsetMultiplier=0.020000
|
||||
MinContactOffset=2.000000
|
||||
MaxContactOffset=8.000000
|
||||
bSimulateSkeletalMeshOnDedicatedServer=True
|
||||
DefaultShapeComplexity=CTF_UseSimpleAndComplex
|
||||
bDefaultHasComplexCollision=True
|
||||
bSuppressFaceRemapTable=False
|
||||
bSupportUVFromHitResults=False
|
||||
bDisableActiveActors=False
|
||||
bDisableKinematicStaticPairs=False
|
||||
bDisableKinematicKinematicPairs=False
|
||||
bDisableCCD=False
|
||||
bEnableEnhancedDeterminism=False
|
||||
MaxPhysicsDeltaTime=0.033333
|
||||
bSubstepping=False
|
||||
bSubsteppingAsync=False
|
||||
MaxSubstepDeltaTime=0.016667
|
||||
MaxSubsteps=6
|
||||
SyncSceneSmoothingFactor=0.000000
|
||||
InitialAverageFrameRate=0.016667
|
||||
PhysXTreeRebuildRate=10
|
||||
DefaultBroadphaseSettings=(bUseMBPOnClient=False,bUseMBPOnServer=False,MBPBounds=(Min=(X=0.000000,Y=0.000000,Z=0.000000),Max=(X=0.000000,Y=0.000000,Z=0.000000),IsValid=0),MBPNumSubdivs=2)
|
||||
|
||||
[/Script/AndroidFileServerEditor.AndroidFileServerRuntimeSettings]
|
||||
bEnablePlugin=True
|
||||
bAllowNetworkConnection=True
|
||||
SecurityToken=BBE94693458F09DA4743C09C2D0CA7F9
|
||||
bIncludeInShipping=False
|
||||
bAllowExternalStartInShipping=False
|
||||
bCompileAFSProject=False
|
||||
bUseCompression=False
|
||||
bLogFiles=False
|
||||
bReportStats=False
|
||||
ConnectionType=USBOnly
|
||||
bUseManualIPAddress=False
|
||||
ManualIPAddress=
|
||||
|
||||
5
Config/DefaultGame.ini
Normal file
5
Config/DefaultGame.ini
Normal file
@@ -0,0 +1,5 @@
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=52B42AE740CC6830E80445A56D5FF608
|
||||
|
||||
[StartupActions]
|
||||
bAddPacks=False
|
||||
100
Config/DefaultInput.ini
Normal file
100
Config/DefaultInput.ini
Normal file
@@ -0,0 +1,100 @@
|
||||
[/Script/Engine.InputSettings]
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
bAltEnterTogglesFullscreen=True
|
||||
bF11TogglesFullscreen=True
|
||||
bUseMouseForTouch=False
|
||||
bEnableMouseSmoothing=True
|
||||
bEnableFOVScaling=True
|
||||
bCaptureMouseOnLaunch=True
|
||||
bEnableLegacyInputScales=True
|
||||
bEnableMotionControls=True
|
||||
bFilterInputByPlatformUser=False
|
||||
bShouldFlushPressedKeysOnViewportFocusLost=True
|
||||
bEnableDynamicComponentInputBinding=True
|
||||
bAlwaysShowTouchInterface=False
|
||||
bShowConsoleOnFourFingerTap=True
|
||||
bEnableGestureRecognizer=False
|
||||
bUseAutocorrect=False
|
||||
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
|
||||
DefaultViewportMouseLockMode=LockOnCapture
|
||||
FOVScale=0.011110
|
||||
DoubleClickTime=0.200000
|
||||
+ActionMappings=(ActionName="Fire",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftMouseButton)
|
||||
+AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W)
|
||||
+AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S)
|
||||
+AxisMappings=(AxisName="Turn",Scale=1.000000,Key=D)
|
||||
+AxisMappings=(AxisName="Turn",Scale=-1.000000,Key=A)
|
||||
+AxisMappings=(AxisName="RotateTurret",Scale=1.000000,Key=MouseX)
|
||||
DefaultPlayerInputClass=/Script/EnhancedInput.EnhancedPlayerInput
|
||||
DefaultInputComponentClass=/Script/EnhancedInput.EnhancedInputComponent
|
||||
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
|
||||
-ConsoleKeys=Tilde
|
||||
+ConsoleKeys=Tilde
|
||||
|
||||
BIN
Content/Assets/Audio/Explode_Audio.uasset
Normal file
BIN
Content/Assets/Audio/Explode_Audio.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Audio/Thud_Audio.uasset
Normal file
BIN
Content/Assets/Audio/Thud_Audio.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Effects/P_DeathEffect.uasset
Normal file
BIN
Content/Assets/Effects/P_DeathEffect.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Effects/P_HitEffect.uasset
Normal file
BIN
Content/Assets/Effects/P_HitEffect.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Effects/P_ProjectileTrail.uasset
Normal file
BIN
Content/Assets/Effects/P_ProjectileTrail.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Base/M_GridBase.uasset
Normal file
BIN
Content/Assets/Materials/Base/M_GridBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Base/M_Grid_Inst.uasset
Normal file
BIN
Content/Assets/Materials/Base/M_Grid_Inst.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Functions/Basic/BreakSet.uasset
Normal file
BIN
Content/Assets/Materials/Functions/Basic/BreakSet.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Functions/Basic/MakeSet.uasset
Normal file
BIN
Content/Assets/Materials/Functions/Basic/MakeSet.uasset
Normal file
Binary file not shown.
Binary file not shown.
BIN
Content/Assets/Materials/Functions/Texture/Tex_SimpleGrid.uasset
Normal file
BIN
Content/Assets/Materials/Functions/Texture/Tex_SimpleGrid.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Functions/UV/UV_ObjectScalable.uasset
Normal file
BIN
Content/Assets/Materials/Functions/UV/UV_ObjectScalable.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/Functions/UV/UV_SetupGrid.uasset
Normal file
BIN
Content/Assets/Materials/Functions/UV/UV_SetupGrid.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_EmissiveBase.uasset
Normal file
BIN
Content/Assets/Materials/M_EmissiveBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_EmissiveGreen_Inst.uasset
Normal file
BIN
Content/Assets/Materials/M_EmissiveGreen_Inst.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_EmissiveRed_Inst.uasset
Normal file
BIN
Content/Assets/Materials/M_EmissiveRed_Inst.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_ParticleBase.uasset
Normal file
BIN
Content/Assets/Materials/M_ParticleBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_Particle_Inst.uasset
Normal file
BIN
Content/Assets/Materials/M_Particle_Inst.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_TextureBase.uasset
Normal file
BIN
Content/Assets/Materials/M_TextureBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Materials/M_Texture_Inst.uasset
Normal file
BIN
Content/Assets/Materials/M_Texture_Inst.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_Projectile.uasset
Normal file
BIN
Content/Assets/Meshes/SM_Projectile.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_Sphere.uasset
Normal file
BIN
Content/Assets/Meshes/SM_Sphere.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_TankBase.uasset
Normal file
BIN
Content/Assets/Meshes/SM_TankBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_TankTurret.uasset
Normal file
BIN
Content/Assets/Meshes/SM_TankTurret.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_TowerBase.uasset
Normal file
BIN
Content/Assets/Meshes/SM_TowerBase.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Meshes/SM_TowerTurret.uasset
Normal file
BIN
Content/Assets/Meshes/SM_TowerTurret.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Textures/T_BasicGrid_M.uasset
Normal file
BIN
Content/Assets/Textures/T_BasicGrid_M.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Textures/T_BasicGrid_N.uasset
Normal file
BIN
Content/Assets/Textures/T_BasicGrid_N.uasset
Normal file
Binary file not shown.
BIN
Content/Assets/Textures/T_ColourGrid.uasset
Normal file
BIN
Content/Assets/Textures/T_ColourGrid.uasset
Normal file
Binary file not shown.
BIN
Content/ComponentsActor.uasset
Normal file
BIN
Content/ComponentsActor.uasset
Normal file
Binary file not shown.
BIN
Content/Maps/Main.umap
Normal file
BIN
Content/Maps/Main.umap
Normal file
Binary file not shown.
421
Plugins/VisualStudioTools/.gitignore
vendored
Normal file
421
Plugins/VisualStudioTools/.gitignore
vendored
Normal file
@@ -0,0 +1,421 @@
|
||||
## Based on VisualStudio and UnrealEngine templates
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
## Get latest from https://github.com/github/gitignore/blob/main/UnrealEngine.gitignore
|
||||
|
||||
## VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
## UnrealEngine.gitignore
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.ipa
|
||||
|
||||
# These project files can be generated by the engine
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
||||
*.opensdf
|
||||
*.sdf
|
||||
|
||||
# Precompiled Assets
|
||||
SourceArt/**/*.png
|
||||
SourceArt/**/*.tga
|
||||
|
||||
# Binary Files
|
||||
Binaries/*
|
||||
Plugins/*/Binaries/*
|
||||
|
||||
# Builds
|
||||
Build/*
|
||||
|
||||
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
|
||||
!Build/*/
|
||||
Build/*/**
|
||||
!Build/*/PakBlacklist*.txt
|
||||
|
||||
# Don't ignore icon files in Build
|
||||
!Build/**/*.ico
|
||||
|
||||
# Built data for maps
|
||||
*_BuiltData.uasset
|
||||
|
||||
# Configuration files generated by the Editor
|
||||
Saved/*
|
||||
|
||||
# Compiled source files for the engine to use
|
||||
Intermediate/*
|
||||
Plugins/*/Intermediate/*
|
||||
|
||||
# Cache files for the editor to use
|
||||
DerivedDataCache/*
|
||||
9
Plugins/VisualStudioTools/CODE_OF_CONDUCT.md
Normal file
9
Plugins/VisualStudioTools/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
Resources:
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
49
Plugins/VisualStudioTools/CONTRIBUTING.md
Normal file
49
Plugins/VisualStudioTools/CONTRIBUTING.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit <https://cla.opensource.microsoft.com>.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
## Code Style Guide
|
||||
|
||||
The code in the repo follows the existing code conventions described in the Unreal Engine's [Code Standard document](https://docs.unrealengine.com/INT/epic-cplusplus-coding-standard-for-unreal-engine/). The `.editorconfig` file at the source root is used for Visual Studio to check the conventions and report violations.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
When submitting a pull request, make sure that it has a clean build using the instructions below. A core contributor will review your pull request and provide feedback. Once all the feedback is addressed and the PR is approved, we will merge the changes.
|
||||
|
||||
## Build workflow
|
||||
The plugin source can be built in isolation using the command below (which wrap the RunUAT.bat script) to ensure it's correct for submition to the Unreal Engine Marketplace.
|
||||
|
||||
From a Visual Studio Developer Prompt (or PowerShell Dev Prompt), run the following:
|
||||
|
||||
```cmd
|
||||
> msbuild -p:UnrealEngine=[path_or_version] -p:OutputPath=[absolute_path]
|
||||
``````
|
||||
|
||||
- `UnrealEngine` can be either a path to a source build (e.g. `c:\src\ue`) or a version identifier for an installed engine (e.g. `4.27`, `5.2`).
|
||||
- `OutputPath` cannot be under the Unreal Engine's folder due to a restriction from `RunUAT.bat`.
|
||||
|
||||
> Note: The contents of `OutputPath` will be overwritten!
|
||||
|
||||
By default the script will disable Unity Builds in the plugin modules to catch errors from cpp files not including all the required headers. It does not affect the build of other targets and modules.
|
||||
|
||||
## Unity build errors
|
||||
|
||||
If you get errors due to unity build problems, you get the same errors in Visual Studio by generating the solution with the command below. This will allow Visual Studio to suggest the includes as code fixes. Note that this will overwrite any existing solution and projects that are already present.
|
||||
|
||||
```powershell
|
||||
$env:VSTUE_IsCustomDevBuild=1; & "C:\Program Files\Epic Games\UE_5.2\Engine\Build\BatchFiles\Build.bat" -projectfiles -project="full_path_to_game.uproject" -game
|
||||
```
|
||||
|
||||
The module rules for the plugin check the enviroment variable above to use the more strict include settings.
|
||||
|
||||
|
||||
9
Plugins/VisualStudioTools/Config/FilterPlugin.ini
Normal file
9
Plugins/VisualStudioTools/Config/FilterPlugin.ini
Normal file
@@ -0,0 +1,9 @@
|
||||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
||||
/Docs/...
|
||||
57
Plugins/VisualStudioTools/Docs/Marketplace_Readme.md
Normal file
57
Plugins/VisualStudioTools/Docs/Marketplace_Readme.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# Visual Studio Integration Tool
|
||||
|
||||
Visual Studio Integration Tool is an Unreal Engine plugin that works in conjunction with Visual Studio to display information about Blueprints assets in C++ code (requires Visual Studio 2022 17.4 or later).
|
||||
|
||||
## Installing
|
||||
|
||||
### Visual Studio
|
||||
|
||||
The tool requires the `Visual Studio Tools for Unreal Engine` component from Visual Studio to be installed. You can find it under the "Game development with C++" workload in the Visual Studio Installer (figure 1).
|
||||
|
||||
 \
|
||||
*Figure 1 - Installing the Visual Studio component*
|
||||
|
||||
### Unreal Engine
|
||||
You can install the plugin in a couple of ways:
|
||||
|
||||
- Through the Epic Games Launcher:
|
||||
- Select the "Install to Engine" option within the Launcher. From there, you can select an engine version for installation.
|
||||
- If you're using the Marketplace website, you can add the plugin to your account and you will have an option to open the Launcher in order to install it as detailed above.
|
||||
- If you already added the plugin to your account, go Library -> Vault in the and locate the plugin there.
|
||||
|
||||
- Through source distribution:
|
||||
- If you're unable to use the Marketplace-based distribution (e.g. you're building the Unreal Engine from source), then you can install the plugin manually by following the instructions found at <https://github.com/microsoft/vc-ue-extensions#readme>
|
||||
|
||||
## Enabling the plugin
|
||||
|
||||
- Through the Unreal Editor
|
||||
- Open your project and then use the Plugin Manager to enable "VisualStudioTools".
|
||||
- See [official documentation](https://docs.unrealengine.com/INT/working-with-plugins-in-unreal-engine/) for more information on how to install and enable plugins.
|
||||
- (Advanced) Alternatively, you can manually edit the '.uproject' descriptor for your project and add an entry for the "VisualStudioTools" plugin.
|
||||
|
||||
## Usage
|
||||
|
||||
Test discovery in Visual Studio 2022
|
||||
|
||||
1. Begin by installing and enabling the `Visual Studio Tools for Unreal Engine` plugin.
|
||||
2. Open your solution in Visual Studio.
|
||||
3. Click on the Test Explorer to show a pop-up that will display available tests. (figure 3).
|
||||
4. You can find the logs from the plugin execution in the Tests Output Window.
|
||||
5. To refresh your filters for test discovery, you can select the "Options > Unreal Engine > Test Adapter" option under the "Tests" menu. (figure 4)
|
||||
|
||||
 \
|
||||
*Figure 2 - Unreal Engine project Configuration Page
|
||||
|
||||
 \
|
||||
*Figure 3 - Menu to rescan the blueprint assets in the game project*
|
||||
|
||||
 \
|
||||
*Figure 3 - Menu to change options for Tests Discovery
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter any issues when setting up Visual Studio in conjunction with the Unreal Editor plugin, please refer to the [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/Troubleshooting.md) guide in the repository. This guide provides solutions for common issues and is periodically updated to ensure that the latest solutions are available.
|
||||
|
||||
## Reporting issues
|
||||
|
||||
To report new issues, provide feedback, or request new features, please use the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest). These options will allow you to submit your issue or feedback directly to our team and help us improve the plugin moving forward.
|
||||
55
Plugins/VisualStudioTools/Docs/Troubleshooting.md
Normal file
55
Plugins/VisualStudioTools/Docs/Troubleshooting.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Troubleshooting guide
|
||||
|
||||
This document describes some of the errors that might happen in the integration with the Unreal Engine and potential ways to mitigate them.
|
||||
|
||||
The integration works by Visual Studio being able to invoke the `VisualStudioTools` plugin using the Unreal Editor executable in commandlet mode. That means the following must be true:
|
||||
|
||||
- The `Visual Studio Tools for Unreal Engine` component from Visual Studio must be installed. You can find it under the "Game Development with C++" workload in the VS Installer.
|
||||
- The game project must be built in a Editor target (e.g., `"Development_Editor"`).
|
||||
- The `VisualStudioTools` plugin must be enabled for the project, either explicitly in the .uproject descriptor file or be enabled by default to all projects if installed at the as an engine plugin (via the `"EnabledByDefault=true"` entry in the .uplugin file).
|
||||
- Starting on version 17.5, Visual Studio will wait to scan the game project until a file with the `UCLASS/UPROPERTY/UFUNCTION` macros is opened and the Code Lens hints are requested.
|
||||
- At the moment, the Code Lens hints will only be displayed for game projects. In particular, the files in the _engine_ project of the solution (with name like "UE4" or "UE5") will not display the hints yet.
|
||||
|
||||
## Code Lens are not visible
|
||||
|
||||
### Verify the required VS component is installed
|
||||
|
||||
In recent versions of UE, the generated solution comes with a `.vsconfig` file, which allows right-clicking on the Solution in VS and selecting "Install Missing Feature(s)". The component is part of the "Game Development with C++" workload.
|
||||
|
||||
You can also see this [help page](https://learn.microsoft.com/en-us/visualstudio/install/install-visual-studio?view=vs-2022#step-4---choose-workloads) about installing features using the Visual Studio installer.
|
||||
|
||||
### Check if the opened documents have any class decorated with the Unreal macros
|
||||
|
||||
For real world projects, scanning the blueprints information might take several seconds and be expensive in terms of machine resources. Visual Studio will only start the operation when the Code Lens are rendered. That means it will wait until a file from the game project with the Unreal macros is opened in the editor.
|
||||
|
||||
### Check if a `cpp.hint` file is redefining the relevant Unreal macros
|
||||
|
||||
Some projects might have a cpp.hint file that includes the `UCLASS`, `UPROPERTY`, `UFUNCTION` macros. That might suppress the new logic in Visual Studio that uses the macros to display the Code Lens hints.
|
||||
|
||||
If that is the case, you can remove those macros from the hint file, save it and try reloading the project.
|
||||
|
||||
Note that other macros in the hint file can be left as-is and do not affect the Code Lens hints.
|
||||
|
||||
### Ensure the C++ Database is enabled
|
||||
|
||||
In Tools > Options > Text Editor > C/C++ > Advanced > Browsing/Navigation, the setting "Disable Database" should be set to "False". This is the default value of this setting.
|
||||
|
||||
## Errors showing up in the Output Window and/or Task Center notification
|
||||
|
||||
### Message "LogInit: Error: VisualStudioToolsCommandlet looked like a commandlet, but we could not find the class."
|
||||
|
||||
Possible causes are the plugin not being installed correctly or installed but not yet enabled for the game project (which is required on installation from the Marketplace).
|
||||
|
||||
- See [this section](../README.md#building-and-installing) for installation instructions.
|
||||
|
||||
- See [this section](../README.md#optional-enabling-the-plugin) for instructions on how to enable the plugin.
|
||||
|
||||
### Message "Command finished with exit code 1" without other errors
|
||||
|
||||
Either the game project or the plugin DLL is not yet built. Rebuilding the project should ensure they are available. Then manually rescan the game project using the `Project > Rescan UE Blueprints` menu.
|
||||
|
||||
### Task Center error: "Your task failed with the message: Could not find a part of the path...'
|
||||
|
||||
This was a known issue when trying to locate the path the Unreal Editor executable, fixed in Visual Studio 17.5-Preview3. This usually happens when the selected Configuration in VS is not one with an "Editor" target.
|
||||
|
||||
A workaround is to switch to such configuration and manually rescan the game project using the `Project > Rescan UE Blueprints` menu.
|
||||
Binary file not shown.
BIN
Plugins/VisualStudioTools/Docs/images/configuration_page.png
Normal file
BIN
Plugins/VisualStudioTools/Docs/images/configuration_page.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
BIN
Plugins/VisualStudioTools/Docs/images/ide_support_ue.png
Normal file
BIN
Plugins/VisualStudioTools/Docs/images/ide_support_ue.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
BIN
Plugins/VisualStudioTools/Docs/images/test_explorer.png
Normal file
BIN
Plugins/VisualStudioTools/Docs/images/test_explorer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
BIN
Plugins/VisualStudioTools/Docs/images/test_options.png
Normal file
BIN
Plugins/VisualStudioTools/Docs/images/test_options.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
22
Plugins/VisualStudioTools/LICENSE
Normal file
22
Plugins/VisualStudioTools/LICENSE
Normal file
@@ -0,0 +1,22 @@
|
||||
Visual Studio Tools for Unreal Engine
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
116
Plugins/VisualStudioTools/README.md
Normal file
116
Plugins/VisualStudioTools/README.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Unreal Engine plugin for Visual Studio
|
||||
|
||||
This project contains an Unreal Editor plugin that works in conjunction with Visual Studio to help discover and run tests in C++ code.
|
||||
|
||||
The plugin can be installed in either the Engine or Game project sources, and it is automatically activated when an Unreal Engine project is opened in Visual Studio.
|
||||
|
||||
## Requirements
|
||||
|
||||
Before you begin, please make sure you have the following software and tools set up:
|
||||
|
||||
1. Visual Studio 2022 has the "Visual Studio Tools for Unreal Engine" component installed.
|
||||
1. The component can be found in the "Game development with C++" workload or as an individual component.
|
||||
2. Unreal Engine, either installed or built from source.
|
||||
1. To learn how to install or build Unreal Engine, please refer to the following guide: [Installing Unreal Engine](https://docs.unrealengine.com/5.0/en-US/installing-unreal-engine).
|
||||
1. The source code and instructions have been tested on Unreal Engine versions 4.27 and 5.0+.
|
||||
|
||||
## Building and Installing the Plugin
|
||||
|
||||
> If you have Unreal Engine installed and set up through the Epic Games Launcher, and you only want to use the plugin, you can skip the steps below and install it directly from the [Unreal Engine Marketplace](https://aka.ms/vsueplugin).
|
||||
|
||||
The most straightforward way to use the plugin is to clone the repo under the `Plugins` folder of your game project or engine source. If you have multiple projects in the same Visual Studio solution, it is recommended to install the plugin at the engine level and share the binaries across the projects.
|
||||
|
||||
1. Clone the repo by using the following commands:
|
||||
```powershell
|
||||
git clone https://github.com/microsoft/vc-ue-extensions.git
|
||||
```
|
||||
|
||||
2. Build the plugin from source:
|
||||
```powershell
|
||||
msbuild -p:UnrealEngine=<AbsolutePathToEngine>
|
||||
```
|
||||
Note#1: `<AbsolutePathToEngine>` can be path to source code folder of the engine or the one installed by `Epic Games Launcher` (e.g `C:\Program Files\Epic Games\UE_5.4`)
|
||||
Note#2: Alternatevly you can follow [Unreal Engine building plugins](https://dev.epicgames.com/community/learning/tutorials/qz93/unreal-engine-building-plugins) guide.
|
||||
|
||||
3. Clone built plugin.
|
||||
|
||||
3.1. To Project folder:
|
||||
```powershell
|
||||
move-item -path ./bin -destination <ProjectRoot>\Plugins\VisualStudioTools
|
||||
```
|
||||
Note: You have to create `Plugins` folder in the root of the game project if it doens't exisist yet.
|
||||
|
||||
3.2. To Engine folder:
|
||||
```powershell
|
||||
move-item -path ./bin -destination <AbsolutePathToEngine\Engine\Plugins
|
||||
```
|
||||
|
||||
2. Optional: Regenerate the Solution for your game project so that the plugin source will be visible in Visual Studio.
|
||||
|
||||
3. Rebuild the game project, which will also build the plugin.
|
||||
|
||||
After completing these steps, Visual Studio should automatically recognize the plugin when you open a solution or project, and it will start processing tests in your game.
|
||||
|
||||
You can also use the option `Run All Tests` in the `Tests` menu to manually force Visual Studio to invoke the the plugin.
|
||||
|
||||
### Cloning outside of engine or project sources
|
||||
|
||||
If you prefer to have the plugin's repository located separately from the engine or project sources (for example, if you want to share it between multiple engines), you can follow the instructions provided in the file [Building and Installing the Plugin](./README.md#building-and-installing-the-plugin) to learn how to build and install the plugin in such a scenario.
|
||||
|
||||
## Enabling the Plugin (Optional)
|
||||
|
||||
By default, the plugin descriptor is already set with `"EnabledByDefault = true"`, so it should function automatically without any additional steps. However, if you encounter difficulties with Unreal Engine building the plugin (e.g., UE fails to build the plugin when building the project), you can enable the plugin explicitly by using one of the following methods:
|
||||
|
||||
1. Navigate to the plugin manager in the Unreal Editor and select `VisualStudioTools`.
|
||||
2. Manually edit the game project's `.uproject` descriptor file by adding an entry for the plugin.
|
||||
|
||||
In either case, the end result should be a new entry in the `Plugins` array in the JSON file, as shown below:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Category": "...",
|
||||
"Description": "...",
|
||||
"Modules": ["..."],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "VisualStudioTools",
|
||||
"Enabled": true,
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
>Note: To ensure proper activation of the plugin, make sure the correct plugin is selected or the desired changes are made in the `.uproject` file.
|
||||
|
||||
## Manually invoking the plugin
|
||||
|
||||
The plugin is designed to be used with Visual Studio, and as such, it does not provide any user interfaces, commands, or logs within the Unreal Editor. However, it is still possible to test the plugin's execution by running the **sample** command below:
|
||||
|
||||
```powershell
|
||||
& "<AbsolutePathToEngine>\Engine\Binaries\Win64\UnrealEditor-Cmd.exe" "$Env:UserProfile\Unreal Projects\EmptyProject\EmptyProject.uproject" -run=VisualStudioTools -output "$Env:Temp\vs-ue-tools.json" [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]
|
||||
```
|
||||
|
||||
This command will run the plugin for the specified project and save Unreal Engine Blueprint information in the output file. Optional parameters are included to run the command faster.
|
||||
|
||||
For more information on the specific command line parameters, you can run the following command in the powershell prompt with `-help`:
|
||||
|
||||
```powershell
|
||||
& "<Editor-Cmd.exe>" "<path_to_uproject>" -run=VisualStudioTools -help [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]
|
||||
```
|
||||
|
||||
>Note: The executable name is `UE4Editor-cmd.exe` for UE4.x, located under a similar path.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you encounter any issues when setting up Visual Studio in conjunction with the Unreal Editor plugin, please refer to the [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/Troubleshooting.md) guide in the repository. This guide provides solutions for common issues and is periodically updated to ensure that the latest solutions are available.
|
||||
|
||||
To report new issues, provide feedback, or request new features, please use the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest). These options will allow you to submit your issue or feedback directly to our team and help us improve the plugin moving forward.
|
||||
|
||||
## Contributing
|
||||
This project welcomes contributions and suggestions. Check out our [contributing guide](CONTRIBUTING.md) for instructions on how to contribute to the project.
|
||||
|
||||
## Trademarks
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow [Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
||||
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
||||
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
||||
41
Plugins/VisualStudioTools/SECURITY.md
Normal file
41
Plugins/VisualStudioTools/SECURITY.md
Normal file
@@ -0,0 +1,41 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.8 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
13
Plugins/VisualStudioTools/SUPPORT.md
Normal file
13
Plugins/VisualStudioTools/SUPPORT.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses the Visual Studio Developer Community to track bugs and feature requests. Please search the existing feedback before filing new ones to avoid duplicates.
|
||||
|
||||
For common issues, please refer to our [Troubleshooting](https://github.com/microsoft/vc-ue-extensions/blob/main/Docs/troubleshooting.md) guide in the repository. We will periodically update the guide to provide solutions for common issues.
|
||||
|
||||
To report issues, provide feedback, and request features, please use one of the following options: [Report a Problem](https://aka.ms/feedback/cpp/unrealengine/report) and [Suggest a Feature](https://aka.ms/feedback/cpp/unrealengine/suggest).
|
||||
|
||||
## Microsoft Support Policy
|
||||
|
||||
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
|
||||
43
Plugins/VisualStudioTools/Scripts/Package-Plugin.ps1
Normal file
43
Plugins/VisualStudioTools/Scripts/Package-Plugin.ps1
Normal file
@@ -0,0 +1,43 @@
|
||||
param(
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$EnginePath,
|
||||
[Parameter(Mandatory=$true)]
|
||||
[string]
|
||||
$EngineVersion
|
||||
)
|
||||
|
||||
function New-TemporaryDirectory {
|
||||
$parent = [System.IO.Path]::GetTempPath()
|
||||
$name = [System.IO.Path]::GetRandomFileName()
|
||||
New-Item -ItemType Directory -Path (Join-Path $parent $name)
|
||||
}
|
||||
|
||||
$PackagePath = New-TemporaryDirectory
|
||||
& msbuild "-p:UnrealEngine=$EnginePath;OutputPath=$PackagePath;Versioned=true"
|
||||
|
||||
# Add EnabledByDefault property in the descriptor file
|
||||
Write-Host "Patch plugin descriptor file"
|
||||
$descriptor = "$PackagePath/VisualStudioTools.uplugin"
|
||||
$a = Get-Content $descriptor | ConvertFrom-Json
|
||||
$a | Add-Member -NotePropertyName EnabledByDefault -NotePropertyValue $true -ErrorAction Ignore
|
||||
$a | ConvertTo-Json -depth 100 | Out-File $descriptor -Encoding utf8
|
||||
|
||||
Write-Host "Copy Config folder"
|
||||
Copy-Item -Path Config -Destination $PackagePath/Config -Recurse
|
||||
|
||||
$PublishPath = "publish"
|
||||
If(!(test-path -PathType Container $PublishPath))
|
||||
{
|
||||
New-Item -ItemType Directory -Path $PublishPath | Out-Null
|
||||
}
|
||||
|
||||
Write-Host "Create ZIP package"
|
||||
$tag = $EngineVersion.Replace(".", "")
|
||||
$files = Get-ChildItem $PackagePath -Exclude @("Binaries", "Intermediate")
|
||||
$zip = "$PublishPath/VisualStudioTools_v$($a.VersionName)_ue$tag.zip"
|
||||
Compress-Archive -Path $files -DestinationPath "$PublishPath/VisualStudioTools_v$($a.VersionName)_ue$tag.zip" -CompressionLevel Fastest
|
||||
|
||||
Remove-Item $PackagePath -Force -Recurse
|
||||
|
||||
Write-Host "Done: $($zip | Resolve-Path)"
|
||||
19
Plugins/VisualStudioTools/Scripts/SignDetached.proj
Normal file
19
Plugins/VisualStudioTools/Scripts/SignDetached.proj
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="SignFiles" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="packages\Microsoft.VisualStudioEng.MicroBuild.Core.0.4.1\build\Microsoft.VisualStudioEng.MicroBuild.Core.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<BaseOutputDirectory>$(MSBuildThisFileDirectory)../../out/</BaseOutputDirectory>
|
||||
<!-- These properties are required by MicroBuild, which only signs files that are under these paths -->
|
||||
<IntermediateOutputPath>$(BaseOutputDirectory)</IntermediateOutputPath>
|
||||
<OutDir>$(BaseOutputDirectory)</OutDir>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FilesToSign Include="$(OutDir)\VisualStudioTools.zip.cat">
|
||||
<Authenticode>Microsoft400</Authenticode>
|
||||
</FilesToSign>
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="packages\Microsoft.VisualStudioEng.MicroBuild.Core.0.4.1\build\Microsoft.VisualStudioEng.MicroBuild.Core.targets" />
|
||||
</Project>
|
||||
4
Plugins/VisualStudioTools/Scripts/packages.config
Normal file
4
Plugins/VisualStudioTools/Scripts/packages.config
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.VisualStudioEng.MicroBuild.Core" version="0.4.1" targetFramework="native" developmentDependency="true" />
|
||||
</packages>
|
||||
91
Plugins/VisualStudioTools/Source/.editorconfig
Normal file
91
Plugins/VisualStudioTools/Source/.editorconfig
Normal file
@@ -0,0 +1,91 @@
|
||||
[*.{cpp,h}]
|
||||
|
||||
# Naming convention rules (note: currently need to be ordered from more to less specific)
|
||||
|
||||
cpp_naming_rule.aactor_prefixed.symbols = aactor_class
|
||||
cpp_naming_rule.aactor_prefixed.style = aactor_style
|
||||
|
||||
cpp_naming_rule.swidget_prefixed.symbols = swidget_class
|
||||
cpp_naming_rule.swidget_prefixed.style = swidget_style
|
||||
|
||||
cpp_naming_rule.uobject_prefixed.symbols = uobject_class
|
||||
cpp_naming_rule.uobject_prefixed.style = uobject_style
|
||||
|
||||
cpp_naming_rule.booleans_prefixed.symbols = boolean_vars
|
||||
cpp_naming_rule.booleans_prefixed.style = boolean_style
|
||||
|
||||
cpp_naming_rule.structs_prefixed.symbols = structs
|
||||
cpp_naming_rule.structs_prefixed.style = unreal_engine_structs
|
||||
|
||||
cpp_naming_rule.enums_prefixed.symbols = enums
|
||||
cpp_naming_rule.enums_prefixed.style = unreal_engine_enums
|
||||
|
||||
cpp_naming_rule.templates_prefixed.symbols = templates
|
||||
cpp_naming_rule.templates_prefixed.style = unreal_engine_templates
|
||||
|
||||
cpp_naming_rule.general_names.symbols = all_symbols
|
||||
cpp_naming_rule.general_names.style = unreal_engine_default
|
||||
|
||||
# Naming convention symbols
|
||||
|
||||
cpp_naming_symbols.aactor_class.applicable_kinds = class
|
||||
cpp_naming_symbols.aactor_class.applicable_type = AActor
|
||||
|
||||
cpp_naming_symbols.swidget_class.applicable_kinds = class
|
||||
cpp_naming_symbols.swidget_class.applicable_type = SWidget
|
||||
|
||||
cpp_naming_symbols.uobject_class.applicable_kinds = class
|
||||
cpp_naming_symbols.uobject_class.applicable_type = UObject
|
||||
|
||||
cpp_naming_symbols.boolean_vars.applicable_kinds = local,parameter,field
|
||||
cpp_naming_symbols.boolean_vars.applicable_type = bool
|
||||
|
||||
cpp_naming_symbols.enums.applicable_kinds = enum
|
||||
|
||||
cpp_naming_symbols.templates.applicable_kinds = template_class
|
||||
|
||||
cpp_naming_symbols.structs.applicable_kinds = struct
|
||||
|
||||
cpp_naming_symbols.all_symbols.applicable_kinds = *
|
||||
|
||||
# Naming convention styles
|
||||
|
||||
cpp_naming_style.unreal_engine_default.capitalization = pascal_case
|
||||
cpp_naming_style.unreal_engine_default.required_prefix =
|
||||
cpp_naming_style.unreal_engine_default.required_suffix =
|
||||
cpp_naming_style.unreal_engine_default.word_separator =
|
||||
|
||||
cpp_naming_style.unreal_engine_enums.capitalization = pascal_case
|
||||
cpp_naming_style.unreal_engine_enums.required_prefix = E
|
||||
cpp_naming_style.unreal_engine_enums.required_suffix =
|
||||
cpp_naming_style.unreal_engine_enums.word_separator =
|
||||
|
||||
cpp_naming_style.unreal_engine_templates.capitalization = pascal_case
|
||||
cpp_naming_style.unreal_engine_templates.required_prefix = T
|
||||
cpp_naming_style.unreal_engine_templates.required_suffix =
|
||||
cpp_naming_style.unreal_engine_templates.word_separator =
|
||||
|
||||
cpp_naming_style.unreal_engine_structs.capitalization = pascal_case
|
||||
cpp_naming_style.unreal_engine_structs.required_prefix = F
|
||||
cpp_naming_style.unreal_engine_structs.required_suffix =
|
||||
cpp_naming_style.unreal_engine_structs.word_separator =
|
||||
|
||||
cpp_naming_style.uobject_style.capitalization = pascal_case
|
||||
cpp_naming_style.uobject_style.required_prefix = U
|
||||
cpp_naming_style.uobject_style.required_suffix =
|
||||
cpp_naming_style.uobject_style.word_separator =
|
||||
|
||||
cpp_naming_style.aactor_style.capitalization = pascal_case
|
||||
cpp_naming_style.aactor_style.required_prefix = A
|
||||
cpp_naming_style.aactor_style.required_suffix =
|
||||
cpp_naming_style.aactor_style.word_separator =
|
||||
|
||||
cpp_naming_style.swidget_style.capitalization = pascal_case
|
||||
cpp_naming_style.swidget_style.required_prefix = S
|
||||
cpp_naming_style.swidget_style.required_suffix =
|
||||
cpp_naming_style.swidget_style.word_separator =
|
||||
|
||||
cpp_naming_style.boolean_style.capitalization = pascal_case
|
||||
cpp_naming_style.boolean_style.required_prefix = b
|
||||
cpp_naming_style.boolean_style.required_suffix =
|
||||
cpp_naming_style.boolean_style.word_separator =
|
||||
@@ -0,0 +1,246 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#include "VisualStudioBlueprintDebuggerHelperModule.h"
|
||||
#include <Modules/ModuleManager.h>
|
||||
#include <UObject/Script.h>
|
||||
#include <UObject/Stack.h>
|
||||
#include <UObject/Object.h>
|
||||
#include <Templates/Casts.h>
|
||||
#include <Kismet2/KismetDebugUtilities.h>
|
||||
#include <Containers/Array.h>
|
||||
#include <UObject/Class.h>
|
||||
#include <Engine/BlueprintGeneratedClass.h>
|
||||
#include <Engine/Blueprint.h>
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 4
|
||||
#include <Blueprint/BlueprintExceptionInfo.h>
|
||||
#endif
|
||||
#include <Internationalization/Text.h>
|
||||
#include <HAL/Platform.h>
|
||||
#include <EdGraph/EdGraphNode.h>
|
||||
#include <EdGraph/EdGraphPin.h>
|
||||
#include <Templates/SharedPointer.h>
|
||||
#include <Templates/Tuple.h>
|
||||
#include <CoreGlobals.h>
|
||||
#include <map>
|
||||
|
||||
IMPLEMENT_MODULE(FVisualStudioBlueprintDebuggerHelper, VisualStudioBlueprintDebuggerHelper);
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogVisualStudioBlueprintDebuggerHelper);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
#define FCustomBlueprintPropertyInfo TSharedPtr<FPropertyInstanceInfo>
|
||||
#else
|
||||
#define FCustomBlueprintPropertyInfo FDebugInfo
|
||||
#endif
|
||||
|
||||
struct FVSNodePinRuntimeInformation
|
||||
{
|
||||
UEdGraphPin* Pin;
|
||||
FCustomBlueprintPropertyInfo Property;
|
||||
|
||||
FVSNodePinRuntimeInformation(UEdGraphPin* InPin, FCustomBlueprintPropertyInfo InProperty)
|
||||
: Pin(InPin)
|
||||
, Property(InProperty)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct FVSNodeData
|
||||
{
|
||||
FText NodeName;
|
||||
TArray<TSharedPtr<FVSNodePinRuntimeInformation>> Properties;
|
||||
int32 ScriptEntryTag;
|
||||
const UEdGraphNode* Node;
|
||||
};
|
||||
|
||||
struct FVSNodesRuntimeInformation
|
||||
{
|
||||
TArray<TSharedPtr<FVSNodeData>> Nodes;
|
||||
};
|
||||
|
||||
struct FVSBlueprintRuntimeInformation
|
||||
{
|
||||
TArray<TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>> RunningBlueprints;
|
||||
};
|
||||
|
||||
struct StackTraceHelper
|
||||
{
|
||||
int32 ScriptEntryTag;
|
||||
FString NodeName;
|
||||
};
|
||||
|
||||
// Keep exported so we can read it.
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API FVSBlueprintRuntimeInformation BlueprintsRuntimeInformation;
|
||||
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API std::map<void*, StackTraceHelper> StackFrameInformation;
|
||||
|
||||
VISUALSTUDIOBLUEPRINTDEBUGGERHELPER_API const char* DebuggerHelperVersion = "1.0.0";
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::StartupModule()
|
||||
{
|
||||
CurrentScriptEntryTag = 0;
|
||||
|
||||
FBlueprintContextTracker::OnEnterScriptContext.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnEnterScriptContext);
|
||||
|
||||
FBlueprintContextTracker::OnExitScriptContext.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnExitScriptContext);
|
||||
|
||||
FBlueprintCoreDelegates::OnScriptException.AddRaw(
|
||||
this,
|
||||
&FVisualStudioBlueprintDebuggerHelper::OnScriptException);
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::ShutdownModule()
|
||||
{
|
||||
FBlueprintCoreDelegates::OnScriptException.RemoveAll(this);
|
||||
FBlueprintContextTracker::OnExitScriptContext.RemoveAll(this);
|
||||
FBlueprintContextTracker::OnEnterScriptContext.RemoveAll(this);
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnEnterScriptContext(
|
||||
const struct FBlueprintContextTracker& Context,
|
||||
const UObject* SourceObject,
|
||||
const UFunction* Function)
|
||||
{
|
||||
if (!IsInGameThread())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentScriptEntryTag = Context.GetScriptEntryTag();
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnExitScriptContext(const struct FBlueprintContextTracker& Context)
|
||||
{
|
||||
if (!IsInGameThread())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto ItRunningBlueprints = BlueprintsRuntimeInformation.RunningBlueprints.CreateIterator(); ItRunningBlueprints; ++ItRunningBlueprints)
|
||||
{
|
||||
auto& RunningBlueprint = ItRunningBlueprints->Value;
|
||||
for (auto ItNodeData = RunningBlueprint->Nodes.CreateIterator(); ItNodeData; ++ItNodeData)
|
||||
{
|
||||
if ((*ItNodeData)->ScriptEntryTag == Context.GetScriptEntryTag())
|
||||
{
|
||||
ItNodeData.RemoveCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
if (!RunningBlueprint->Nodes.Num())
|
||||
{
|
||||
ItRunningBlueprints.RemoveCurrent();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto ItStackFrameInfo = StackFrameInformation.begin(); ItStackFrameInfo != StackFrameInformation.end();)
|
||||
{
|
||||
if (ItStackFrameInfo->second.ScriptEntryTag == Context.GetScriptEntryTag())
|
||||
{
|
||||
ItStackFrameInfo = StackFrameInformation.erase(ItStackFrameInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
++ItStackFrameInfo;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentScriptEntryTag--;
|
||||
}
|
||||
|
||||
void FVisualStudioBlueprintDebuggerHelper::OnScriptException(
|
||||
const UObject* Owner,
|
||||
const struct FFrame& Stack,
|
||||
const FBlueprintExceptionInfo& ExceptionInfo)
|
||||
{
|
||||
EBlueprintExceptionType::Type ExceptionType = ExceptionInfo.GetType();
|
||||
if (ExceptionType != EBlueprintExceptionType::Type::Tracepoint &&
|
||||
ExceptionType != EBlueprintExceptionType::Type::WireTracepoint &&
|
||||
ExceptionType != EBlueprintExceptionType::Type::Breakpoint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UFunction* NodeFunction = Cast<UFunction>(Stack.Node);
|
||||
if (!NodeFunction)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBlueprintGeneratedClass* BlueprintGeneratedClass = Cast<UBlueprintGeneratedClass>(NodeFunction->GetOuter());
|
||||
if (!BlueprintGeneratedClass)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UBlueprint* Blueprint = Cast<UBlueprint>(BlueprintGeneratedClass->ClassGeneratedBy);
|
||||
if (!Blueprint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int32 BreakpointOffset = Stack.Code - Stack.Node->Script.GetData() - 1;
|
||||
const UEdGraphNode* NodeStoppedAt = FKismetDebugUtilities::FindSourceNodeForCodeLocation(Owner, Stack.Node, BreakpointOffset, /*bAllowImpreciseHit=*/ true);
|
||||
if (!NodeStoppedAt)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StackFrameInformation[NodeFunction] = { CurrentScriptEntryTag, FString::Printf(TEXT("%s::%s"), *Blueprint->GetFriendlyName(), *NodeStoppedAt->GetNodeTitle(ENodeTitleType::Type::ListView).ToString()) };
|
||||
TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>* ExistingNodesRuntimeInformationTuple = BlueprintsRuntimeInformation.RunningBlueprints.FindByPredicate([&Blueprint](const TTuple<UBlueprint*, TSharedPtr<FVSNodesRuntimeInformation>>& Tuple) {
|
||||
return Tuple.Key == Blueprint;
|
||||
});
|
||||
|
||||
TSharedPtr<FVSNodesRuntimeInformation> NodesRuntimeInformation;
|
||||
if (!ExistingNodesRuntimeInformationTuple)
|
||||
{
|
||||
NodesRuntimeInformation = MakeShared<FVSNodesRuntimeInformation>();
|
||||
BlueprintsRuntimeInformation.RunningBlueprints.Add(MakeTuple(Blueprint, NodesRuntimeInformation));
|
||||
}
|
||||
else
|
||||
{
|
||||
NodesRuntimeInformation = ExistingNodesRuntimeInformationTuple->Value;
|
||||
}
|
||||
|
||||
TSharedPtr<FVSNodeData> CurrentNodeData;
|
||||
if (NodesRuntimeInformation->Nodes.Num() == 0 || NodeStoppedAt != NodesRuntimeInformation->Nodes.Top()->Node)
|
||||
{
|
||||
CurrentNodeData = MakeShared<FVSNodeData>();
|
||||
CurrentNodeData->Node = NodeStoppedAt;
|
||||
CurrentNodeData->NodeName = NodeStoppedAt->GetNodeTitle(ENodeTitleType::Type::ListView);
|
||||
CurrentNodeData->ScriptEntryTag = CurrentScriptEntryTag;
|
||||
NodesRuntimeInformation->Nodes.Push(CurrentNodeData);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentNodeData = NodesRuntimeInformation->Nodes.Top();
|
||||
}
|
||||
|
||||
FCustomBlueprintPropertyInfo PinInstanceInfo;
|
||||
for (auto GraphPin : NodeStoppedAt->Pins)
|
||||
{
|
||||
FKismetDebugUtilities::EWatchTextResult DebugResult = FKismetDebugUtilities::GetDebugInfo(PinInstanceInfo, Blueprint, (UObject*)Owner, GraphPin);
|
||||
if (DebugResult != FKismetDebugUtilities::EWTR_Valid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TSharedPtr<FVSNodePinRuntimeInformation>* Existing = CurrentNodeData->Properties.FindByPredicate([&GraphPin](TSharedPtr<FVSNodePinRuntimeInformation>& PinInfo) {
|
||||
return PinInfo->Pin == GraphPin;
|
||||
});
|
||||
|
||||
if (!Existing)
|
||||
{
|
||||
CurrentNodeData->Properties.Add(MakeShared<FVSNodePinRuntimeInformation>(GraphPin, PinInstanceInfo));
|
||||
}
|
||||
else
|
||||
{
|
||||
(*Existing)->Property = PinInstanceInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
#include <Modules/ModuleInterface.h>
|
||||
#include <Modules/ModuleManager.h>
|
||||
#include <UObject/Script.h>
|
||||
#include <UObject/Stack.h>
|
||||
#include <UObject/Object.h>
|
||||
#include <Logging/LogMacros.h>
|
||||
#include <UObject/Class.h>
|
||||
#include <HAL/Platform.h>
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogVisualStudioBlueprintDebuggerHelper, Log, All);
|
||||
|
||||
class FVisualStudioBlueprintDebuggerHelper : public FDefaultModuleImpl
|
||||
{
|
||||
private:
|
||||
void OnScriptException(const UObject* Owner, const struct FFrame& Stack, const FBlueprintExceptionInfo& ExceptionInfo);
|
||||
void OnEnterScriptContext(const struct FBlueprintContextTracker& Context, const UObject* SourceObject, const UFunction* Function);
|
||||
void OnExitScriptContext(const struct FBlueprintContextTracker& Context);
|
||||
|
||||
int32 CurrentScriptEntryTag;
|
||||
|
||||
public:
|
||||
void StartupModule() override;
|
||||
void ShutdownModule() override;
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class VisualStudioBlueprintDebuggerHelper: ModuleRules
|
||||
{
|
||||
public VisualStudioBlueprintDebuggerHelper(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
OptimizeCode = CodeOptimization.Never;
|
||||
PrivateDependencyModuleNames.AddRange(new string[] {
|
||||
"Core",
|
||||
"ApplicationCore",
|
||||
"AssetRegistry",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"Kismet",
|
||||
"UnrealEd",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"ToolMenus",
|
||||
"EditorSubsystem",
|
||||
"MainFrame",
|
||||
"BlueprintGraph",
|
||||
"VisualStudioDTE",
|
||||
"EditorStyle",
|
||||
"Projects"
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "BlueprintAssetHelpers.h"
|
||||
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Blueprint/BlueprintSupport.h"
|
||||
#include "Engine/BlueprintCore.h"
|
||||
#include "Engine/BlueprintGeneratedClass.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/StreamableManager.h"
|
||||
#include "Misc/ScopeExit.h"
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
namespace VisualStudioTools
|
||||
{
|
||||
namespace AssetHelpers
|
||||
{
|
||||
/*
|
||||
* These helpers handle the usage of some APIs that were deprecated in 5.1
|
||||
* but the replacements are not available in older versions.
|
||||
* Might be overridden by the `Build.cs` rules
|
||||
*/
|
||||
#if FILTER_ASSETS_BY_CLASS_PATH
|
||||
|
||||
void SetBlueprintClassFilter(FARFilter& InOutFilter)
|
||||
{
|
||||
// UE5.1 deprecated the API to filter using class names
|
||||
InOutFilter.ClassPaths.Add(UBlueprintCore::StaticClass()->GetClassPathName());
|
||||
}
|
||||
|
||||
static FString GetObjectPathString(const FAssetData& InAssetData)
|
||||
{
|
||||
// UE5.1 deprecated 'FAssetData::ObjectPath' in favor of 'FAssetData::GetObjectPathString()'
|
||||
return InAssetData.GetObjectPathString();
|
||||
}
|
||||
|
||||
#else // FILTER_ASSETS_BY_CLASS_PATH
|
||||
|
||||
void SetBlueprintClassFilter(FARFilter& InOutFilter)
|
||||
{
|
||||
InOutFilter.ClassNames.Add(UBlueprintCore::StaticClass()->GetFName());
|
||||
}
|
||||
|
||||
static FString GetObjectPathString(const FAssetData& InAssetData)
|
||||
{
|
||||
return InAssetData.ObjectPath.ToString();
|
||||
}
|
||||
|
||||
#endif // FILTER_ASSETS_BY_CLASS_PATH
|
||||
|
||||
void ForEachAsset(
|
||||
const TArray<FAssetData>& TargetAssets,
|
||||
TFunctionRef<void(UBlueprintGeneratedClass*, const FAssetData& AssetData)> Callback)
|
||||
{
|
||||
// Show a simpler logging output.
|
||||
// LogTimes are still useful to tell how long it takes to process each asset.
|
||||
TGuardValue<bool> DisableLogVerbosity(GPrintLogVerbosity, false);
|
||||
TGuardValue<bool> DisableLogCategory(GPrintLogCategory, false);
|
||||
|
||||
// We're about to load the assets which might trigger a ton of log messages
|
||||
// Temporarily suppress them during this stage.
|
||||
GEngine->Exec(nullptr, TEXT("log LogVisualStudioTools only"));
|
||||
ON_SCOPE_EXIT
|
||||
{
|
||||
GEngine->Exec(nullptr, TEXT("log reset"));
|
||||
};
|
||||
|
||||
FStreamableManager AssetLoader;
|
||||
|
||||
for (int32 Idx = 0; Idx < TargetAssets.Num(); Idx++)
|
||||
{
|
||||
const FAssetData AssetData = TargetAssets[Idx];
|
||||
FSoftClassPath GenClassPath = AssetData.GetTagValueRef<FString>(FBlueprintTags::GeneratedClassPath);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Processing blueprints [%d/%d]: %s"), Idx + 1, TargetAssets.Num(), *GenClassPath.ToString());
|
||||
|
||||
TSharedPtr<FStreamableHandle> Handle = AssetLoader.RequestSyncLoad(GenClassPath);
|
||||
ON_SCOPE_EXIT
|
||||
{
|
||||
// We're done, notify an unload.
|
||||
Handle->ReleaseHandle();
|
||||
};
|
||||
|
||||
if (!Handle.IsValid())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Warning, TEXT("Failed to get a streamable handle for Blueprint. Skipping. GenClassPath: %s"), *GenClassPath.ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto BlueprintGeneratedClass = Cast<UBlueprintGeneratedClass>(Handle->GetLoadedAsset()))
|
||||
{
|
||||
Callback(BlueprintGeneratedClass, AssetData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log some extra information to help the user understand why the asset failed to load.
|
||||
|
||||
FString ObjectPathString = AssetHelpers::GetObjectPathString(AssetData);
|
||||
|
||||
FString Msg = !GenClassPath.ToString().Contains(ObjectPathString)
|
||||
? FString::Printf(
|
||||
TEXT("ObjectPath is not compatible with GenClassPath, consider re-saving it to avoid future issues. { ObjectPath: %s, GenClassPath: %s }"),
|
||||
*ObjectPathString,
|
||||
*GenClassPath.ToString())
|
||||
: FString::Printf(TEXT("ClassPath: %s"), *GenClassPath.ToString());
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Warning, TEXT("Failed to load Blueprint. Skipping. %s"), *Msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/NoExportTypes.h"
|
||||
|
||||
class UBlueprintGeneratedClass;
|
||||
|
||||
namespace VisualStudioTools
|
||||
{
|
||||
namespace AssetHelpers
|
||||
{
|
||||
void SetBlueprintClassFilter(FARFilter& InOutFilter);
|
||||
|
||||
/**
|
||||
* Loads each blueprint asset and invokes the callback with the resulting blueprint generated class.
|
||||
* Each iteration will load the asset using a FStreamableHandle and verify that is a valid blueprint
|
||||
* before invoking the callback.
|
||||
*/
|
||||
void ForEachAsset(
|
||||
const TArray<FAssetData>& TargetAssets,
|
||||
TFunctionRef<void(UBlueprintGeneratedClass*, const FAssetData& AssetData)> Callback);
|
||||
|
||||
} // namespace AssetHelpers
|
||||
} // namespace VisualStudioTools
|
||||
@@ -0,0 +1,248 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "BlueprintReferencesCommandlet.h"
|
||||
|
||||
#include "Algo/Find.h"
|
||||
#include "Algo/Transform.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "BlueprintAssetHelpers.h"
|
||||
#include "Engine/BlueprintGeneratedClass.h"
|
||||
#include "FindInBlueprintManager.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/ScopeExit.h"
|
||||
#include "Policies/CondensedJsonPrintPolicy.h"
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
namespace VisualStudioTools
|
||||
{
|
||||
static FString StripClassPrefix(const FString& InClassName)
|
||||
{
|
||||
if (InClassName.IsEmpty())
|
||||
{
|
||||
return InClassName;
|
||||
}
|
||||
|
||||
size_t PrefixSize = 0;
|
||||
|
||||
const TCHAR ClassPrefixChar = InClassName[0];
|
||||
switch (ClassPrefixChar)
|
||||
{
|
||||
case TEXT('I'):
|
||||
case TEXT('A'):
|
||||
case TEXT('U'):
|
||||
// If it is a class prefix, check for deprecated class prefix also
|
||||
if (InClassName.Len() > 12 && FCString::Strncmp(&(InClassName[1]), TEXT("DEPRECATED_"), 11) == 0)
|
||||
{
|
||||
PrefixSize = 12;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrefixSize = 1;
|
||||
}
|
||||
break;
|
||||
case TEXT('F'):
|
||||
case TEXT('T'):
|
||||
// Struct prefixes are also fine.
|
||||
PrefixSize = 1;
|
||||
break;
|
||||
default:
|
||||
PrefixSize = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return InClassName.RightChop(PrefixSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the asset data matching the given FindInBlueprints query.
|
||||
*/
|
||||
TArray<FAssetData> SearchForCandidateAssets(const FString& SearchQuery)
|
||||
{
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
AssetRegistry.SearchAllAssets(true);
|
||||
|
||||
TArray<FSearchResult> OutItemsFound;
|
||||
FStreamSearch StreamSearch(SearchQuery);
|
||||
while (!StreamSearch.IsComplete())
|
||||
{
|
||||
FFindInBlueprintSearchManager::Get().Tick(0.0);
|
||||
}
|
||||
|
||||
// Execute the search and get all the assets in the result.
|
||||
StreamSearch.GetFilteredItems(OutItemsFound);
|
||||
|
||||
|
||||
TArray<FAssetData> OutTargetAssets;
|
||||
Algo::Transform(OutItemsFound, OutTargetAssets,
|
||||
[&](const FSearchResult& Item)
|
||||
{
|
||||
// The DisplayText property of the result contains the blueprint's object path
|
||||
// Use that to find the respective asset in the registry
|
||||
#if FILTER_ASSETS_BY_CLASS_PATH
|
||||
return AssetRegistry.GetAssetByObjectPath(FSoftObjectPath(*Item->DisplayText.ToString()));
|
||||
#else
|
||||
return AssetRegistry.GetAssetByObjectPath(*Item->DisplayText.ToString());
|
||||
#endif // FILTER_ASSETS_BY_CLASS_PATH
|
||||
});
|
||||
|
||||
return OutTargetAssets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads each blueprint asset and filters the collection to items which use the
|
||||
* target UFunction in their call graph, matching the native class and function names.
|
||||
*/
|
||||
TMap<FString, FAssetData> GetConfirmedAssets(
|
||||
const FString& FunctionName, const FString& ClassNameWithoutPrefix, const TArray<FAssetData>& InAssets)
|
||||
{
|
||||
TMap<FString, FAssetData> OutResults;
|
||||
|
||||
AssetHelpers::ForEachAsset(InAssets,
|
||||
[&](UBlueprintGeneratedClass* BlueprintClassName, const FAssetData AssetData)
|
||||
{
|
||||
auto MatchingFunction = Algo::FindByPredicate(BlueprintClassName->CalledFunctions,
|
||||
[&](const UFunction* Fn)
|
||||
{
|
||||
return Fn->HasAnyFunctionFlags(EFunctionFlags::FUNC_Native)
|
||||
&& Fn->GetName() == FunctionName
|
||||
&& Fn->GetOwnerClass()->GetName() == ClassNameWithoutPrefix;
|
||||
});
|
||||
|
||||
if (MatchingFunction != nullptr)
|
||||
{
|
||||
OutResults.Add(BlueprintClassName->GetName(), AssetData);
|
||||
}
|
||||
});
|
||||
|
||||
return OutResults;
|
||||
}
|
||||
using JsonWriter = TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>;
|
||||
|
||||
static void SerializeBlueprintReference(
|
||||
TSharedRef<JsonWriter>& Json, const FString& BlueprintClassName, const FAssetData& Asset)
|
||||
{
|
||||
FString PackageFileName;
|
||||
FString PackageFile;
|
||||
FString PackageFilePath;
|
||||
if (FPackageName::TryConvertLongPackageNameToFilename(Asset.GetPackage()->GetName(), PackageFileName) &&
|
||||
FPackageName::FindPackageFileWithoutExtension(PackageFileName, PackageFile))
|
||||
{
|
||||
PackageFilePath = FPaths::ConvertRelativePathToFull(MoveTemp(PackageFile));
|
||||
}
|
||||
|
||||
Json->WriteObjectStart();
|
||||
Json->WriteValue(TEXT("name"), BlueprintClassName);
|
||||
Json->WriteValue(TEXT("path"), PackageFilePath);
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
|
||||
static void SerializeBlueprints(
|
||||
TSharedRef<JsonWriter>& Json, const TMap<FString, FAssetData>& InAssets)
|
||||
{
|
||||
Json->WriteIdentifierPrefix(TEXT("blueprints"));
|
||||
Json->WriteArrayStart();
|
||||
|
||||
for (auto& Item : InAssets)
|
||||
{
|
||||
const FString& BlueprintClassName = Item.Key;
|
||||
const FAssetData& Asset = Item.Value;
|
||||
SerializeBlueprintReference(Json, BlueprintClassName, Asset);
|
||||
}
|
||||
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
static void SerializeMetadata(
|
||||
TSharedRef<JsonWriter>& Json, int TotalAssetCount)
|
||||
{
|
||||
Json->WriteIdentifierPrefix(TEXT("metadata"));
|
||||
Json->WriteObjectStart();
|
||||
{
|
||||
Json->WriteValue(TEXT("asset_count"), TotalAssetCount);
|
||||
}
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
|
||||
static void SerializeResults(
|
||||
const TMap<FString, FAssetData>& InAssets,
|
||||
FArchive& OutArchive,
|
||||
int TotalAssetCount)
|
||||
{
|
||||
TSharedRef<JsonWriter> Json = JsonWriter::Create(&OutArchive);
|
||||
Json->WriteObjectStart();
|
||||
|
||||
SerializeBlueprints(Json, InAssets);
|
||||
SerializeMetadata(Json, TotalAssetCount);
|
||||
|
||||
Json->WriteObjectEnd();
|
||||
Json->Close();
|
||||
}
|
||||
} // namespace VisualStudioTools
|
||||
|
||||
static constexpr auto SymbolParamVal = TEXT("symbol");
|
||||
|
||||
UVsBlueprintReferencesCommandlet::UVsBlueprintReferencesCommandlet()
|
||||
: Super()
|
||||
{
|
||||
HelpDescription = TEXT("Commandlet for generating data used by Blueprint support in Visual Studio.");
|
||||
|
||||
HelpParamNames.Add(SymbolParamVal);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Fully qualified symbol to search for in the blueprints."));
|
||||
|
||||
HelpUsage = TEXT("<Editor-Cmd.exe> <path_to_uproject> -run=VsBlueprintReferences -output=<path_to_output_file> -symbol=<ClassName::FunctionName> [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]");
|
||||
}
|
||||
|
||||
int32 UVsBlueprintReferencesCommandlet::Run(
|
||||
TArray<FString>& Tokens,
|
||||
TArray<FString>& Switches,
|
||||
TMap<FString, FString>& ParamVals,
|
||||
FArchive& OutArchive)
|
||||
{
|
||||
using namespace VisualStudioTools;
|
||||
GIsRunning = true; // Required for the blueprint search to work.
|
||||
|
||||
FString* ReferencesSymbol = ParamVals.Find(SymbolParamVal);
|
||||
if (ReferencesSymbol->IsEmpty())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Missing required symbol parameter."));
|
||||
PrintHelp();
|
||||
return -1;
|
||||
}
|
||||
|
||||
FString FunctionName;
|
||||
FString ClassNameNative;
|
||||
if (!ReferencesSymbol->Split(TEXT("::"), &ClassNameNative, &FunctionName))
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Reference parameter should be in the qualified 'NativeClassName::MethodName' format."));
|
||||
PrintHelp();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Execute the search in two stages:
|
||||
// 1. Use FindInBlueprints to get all candidate blueprints with calls to functions that match the requested symbol
|
||||
// 2. Confirm the blueprints reference the requested function, by matching the target UFunction in their call graph.
|
||||
// The first step acts as a filter to avoid loading too many blueprints to inspect their call graph.
|
||||
// The second step is required because the FiB data does not always allow for searching with the function
|
||||
// qualified with the owned class name, if the function is static.
|
||||
|
||||
FString ClassNameWithoutPrefix = StripClassPrefix(ClassNameNative);
|
||||
|
||||
// Create a FiB search query for function nodes where the native name matches the requested symbol
|
||||
FString SearchValue = FString::Printf(TEXT("Nodes(\"Native Name\"=+%s & ClassName=K2Node_CallFunction)"), *FunctionName);
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Blueprint search query: %s"), *SearchValue);
|
||||
|
||||
// Step 1: Execute the Fib search
|
||||
TArray<FAssetData> TargetAssets = SearchForCandidateAssets(SearchValue);
|
||||
|
||||
// Step 2: Load the assets to confirm they are a match
|
||||
TMap<FString, FAssetData> MatchAssets = GetConfirmedAssets(FunctionName, ClassNameWithoutPrefix, TargetAssets);
|
||||
|
||||
// Finally, write the results back to the output
|
||||
SerializeResults(MatchAssets, OutArchive, TargetAssets.Num());
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Found %d blueprints."), MatchAssets.Num());
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "VisualStudioToolsCommandletBase.h"
|
||||
|
||||
#include "BlueprintReferencesCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UVsBlueprintReferencesCommandlet
|
||||
: public UVisualStudioToolsCommandletBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVsBlueprintReferencesCommandlet();
|
||||
|
||||
int32 Run(
|
||||
TArray<FString>& Tokens,
|
||||
TArray<FString>& Switches,
|
||||
TMap<FString, FString>& ParamVals,
|
||||
FArchive& OutArchive) override;
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "VisualStudioDTE.h"
|
||||
#include <utility>
|
||||
|
||||
class FSmartBSTR
|
||||
{
|
||||
public:
|
||||
FSmartBSTR() : data(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
FSmartBSTR(const FSmartBSTR& Other)
|
||||
{
|
||||
if (Other.data) data = SysAllocString(Other.data);
|
||||
else data = nullptr;
|
||||
}
|
||||
|
||||
FSmartBSTR(FSmartBSTR&& Other)
|
||||
{
|
||||
data = std::exchange(Other.data, nullptr);
|
||||
}
|
||||
|
||||
FSmartBSTR(const FString& Other)
|
||||
{
|
||||
data = SysAllocString(*Other);
|
||||
}
|
||||
|
||||
FSmartBSTR(const OLECHAR *Ptr)
|
||||
{
|
||||
if (Ptr) data = SysAllocString(Ptr);
|
||||
else data = nullptr;
|
||||
}
|
||||
|
||||
~FSmartBSTR()
|
||||
{
|
||||
if (data) SysFreeString(data);
|
||||
}
|
||||
|
||||
FSmartBSTR& operator=(const FSmartBSTR& Other)
|
||||
{
|
||||
if (this == &Other) return *this;
|
||||
if (data) SysFreeString(data);
|
||||
if (Other.data) data = SysAllocString(Other.data);
|
||||
else data = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FSmartBSTR& operator=(FSmartBSTR&& Other)
|
||||
{
|
||||
if (data) SysFreeString(data);
|
||||
data = std::exchange(Other.data, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
BSTR operator*() const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
private:
|
||||
BSTR data;
|
||||
};
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#include "VSServerCommandlet.h"
|
||||
#include "VSTestAdapterCommandlet.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
|
||||
#include "HAL/PlatformNamedPipe.h"
|
||||
#include "Runtime/Core/Public/Async/TaskGraphInterfaces.h"
|
||||
#include "Runtime/Core/Public/Containers/Ticker.h"
|
||||
#include "Runtime/Engine/Classes/Engine/World.h"
|
||||
#include "Runtime/Engine/Public/TimerManager.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include "Runtime\CoreUObject\Public\UObject\UObjectGlobals.h"
|
||||
#include <chrono>
|
||||
#include <codecvt>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <windows.h>
|
||||
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
static constexpr auto NamedPipeParam = TEXT("NamedPipe");
|
||||
static constexpr auto KillServerParam = TEXT("KillVSServer");
|
||||
|
||||
UVSServerCommandlet::UVSServerCommandlet()
|
||||
{
|
||||
HelpDescription = TEXT("Commandlet for Unreal Engine server mode.");
|
||||
HelpUsage = TEXT("<Editor-Cmd.exe> <path_to_uproject> -run=VSServer [-stdout -multiprocess -silent -unattended -AllowStdOutLogVerbosity -NoShaderCompile]");
|
||||
|
||||
HelpParamNames.Add(NamedPipeParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Required] The name of the named pipe used to communicate with Visual Studio."));
|
||||
|
||||
HelpParamNames.Add(KillServerParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Quit the server mode commandlet immediately."));
|
||||
}
|
||||
|
||||
void UVSServerCommandlet::ExecuteSubCommandlet(FString ueServerNamedPipe)
|
||||
{
|
||||
char buffer[1024];
|
||||
DWORD dwRead;
|
||||
std::string result = "0";
|
||||
|
||||
// Open the named pipe.
|
||||
std::wstring pipeName = L"\\\\.\\pipe\\";
|
||||
pipeName.append(ueServerNamedPipe.GetCharArray().GetData());
|
||||
HANDLE HPipe = CreateFile(pipeName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if (HPipe != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
ConnectNamedPipe(HPipe, NULL);
|
||||
DWORD dwState;
|
||||
BOOL bSuccess = GetNamedPipeHandleState(HPipe, &dwState, NULL, NULL, NULL, NULL, 0);
|
||||
if (bSuccess)
|
||||
{
|
||||
// Read data from the named pipe.
|
||||
ReadFile(HPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL);
|
||||
buffer[dwRead] = '\0';
|
||||
std::string strSubCommandletParams(buffer, dwRead);
|
||||
FString SubCommandletParams = FString(strSubCommandletParams.c_str());
|
||||
|
||||
// Determine which sub-commandlet to invoke, and write back result response.
|
||||
if (SubCommandletParams.Contains("VSTestAdapter"))
|
||||
{
|
||||
UVSTestAdapterCommandlet *Commandlet = NewObject<UVSTestAdapterCommandlet>();
|
||||
try
|
||||
{
|
||||
int32 subCommandletResult = Commandlet->Main(SubCommandletParams);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Exception invoking VSTestAdapter commandlet: %s"), UTF8_TO_TCHAR(ex.what()));
|
||||
result = "0";
|
||||
}
|
||||
}
|
||||
else if (SubCommandletParams.Contains("KillVSServer"))
|
||||
{
|
||||
// When KillVSServer is passed in, then kill the Unreal Editor process to end server mode.
|
||||
exit(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If cannot find which sub-commandlet to run, then return error.
|
||||
result = "1";
|
||||
}
|
||||
|
||||
WriteFile(HPipe, result.c_str(), result.size(), &dwRead, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32 UVSServerCommandlet::Main(const FString &ServerParams)
|
||||
{
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
TMap<FString, FString> ParamVals;
|
||||
|
||||
ParseCommandLine(*ServerParams, Tokens, Switches, ParamVals);
|
||||
if (ParamVals.Contains(NamedPipeParam))
|
||||
{
|
||||
FString ueServerNamedPipe = ParamVals[NamedPipeParam];
|
||||
|
||||
// Infinite loop that listens to requests every second.
|
||||
while (true)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
ExecuteSubCommandlet(ueServerNamedPipe);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Missing named pipe parameter."));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include <string>
|
||||
|
||||
#include <Runtime/Core/Public/Misc/AutomationTest.h>
|
||||
#include <Runtime/CoreUObject/Public/UObject/ObjectMacros.h>
|
||||
#include <Runtime/Engine/Classes/Commandlets/Commandlet.h>
|
||||
|
||||
#include "VSServerCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UVSServerCommandlet
|
||||
: public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVSServerCommandlet();
|
||||
|
||||
public:
|
||||
virtual int32 Main(const FString& Params) override;
|
||||
|
||||
private:
|
||||
void ExecuteSubCommandlet(FString ueServerNamedPipe);
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#include "VSTestAdapterCommandlet.h"
|
||||
|
||||
#include "Runtime/Core/Public/Async/TaskGraphInterfaces.h"
|
||||
#include "Runtime/Core/Public/Containers/Ticker.h"
|
||||
#include "Runtime/Launch/Resources/Version.h"
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
static constexpr auto FiltersParam = TEXT("filters");
|
||||
static constexpr auto ListTestsParam = TEXT("listtests");
|
||||
static constexpr auto RunTestsParam = TEXT("runtests");
|
||||
static constexpr auto TestResultsFileParam = TEXT("testresultfile");
|
||||
static constexpr auto HelpParam = TEXT("help");
|
||||
|
||||
static void GetAllTests(TArray<FAutomationTestInfo>& OutTestList)
|
||||
{
|
||||
FAutomationTestFramework& Framework = FAutomationTestFramework::GetInstance();
|
||||
Framework.GetValidTestNames(OutTestList);
|
||||
}
|
||||
|
||||
static void ReadTestsFromFile(const FString& InFile, TArray<FAutomationTestInfo>& OutTestList)
|
||||
{
|
||||
TSet<FString> TestCommands;
|
||||
|
||||
// Wrapping in an inner scope to ensure automatic destruction of InStream object without explicitly calling .close().
|
||||
{
|
||||
std::wifstream InStream(*InFile);
|
||||
if (!InStream.good())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Failed to open file at path: %s"), *InFile);
|
||||
return;
|
||||
}
|
||||
|
||||
std::wstring Line;
|
||||
while (std::getline(InStream, Line))
|
||||
{
|
||||
if (Line.length() > 0)
|
||||
{
|
||||
TestCommands.Add(FString(Line.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GetAllTests(OutTestList);
|
||||
for (int32 Idx = OutTestList.Num() - 1; Idx >= 0; Idx--)
|
||||
{
|
||||
if (!TestCommands.Contains(OutTestList[Idx].GetTestName()))
|
||||
{
|
||||
OutTestList.RemoveAt(Idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32 ListTests(const FString& TargetFile)
|
||||
{
|
||||
std::wofstream OutFile(*TargetFile);
|
||||
if (!OutFile.good())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Failed to open file at path: %s"), *TargetFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
FAutomationTestFramework& Framework = FAutomationTestFramework::GetInstance();
|
||||
|
||||
TArray<FAutomationTestInfo> TestInfos;
|
||||
GetAllTests(TestInfos);
|
||||
|
||||
for (const auto& TestInfo : TestInfos)
|
||||
{
|
||||
const FString TestCommand = TestInfo.GetTestName();
|
||||
const FString DisplayName = TestInfo.GetDisplayName();
|
||||
const FString SourceFile = TestInfo.GetSourceFile();
|
||||
const int32 Line = TestInfo.GetSourceFileLine();
|
||||
|
||||
OutFile << *TestCommand << TEXT("|") << *DisplayName << TEXT("|") << Line << TEXT("|") << *SourceFile << std::endl;
|
||||
}
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Found %d tests"), TestInfos.Num());
|
||||
OutFile.close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int32 RunTests(const FString& TestListFile, const FString& ResultsFile)
|
||||
{
|
||||
std::wofstream OutFile(*ResultsFile);
|
||||
if (!OutFile.good())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Failed to open file at path: %s"), *ResultsFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TArray<FAutomationTestInfo> TestInfos;
|
||||
if (TestListFile.Equals(TEXT("All"), ESearchCase::IgnoreCase))
|
||||
{
|
||||
GetAllTests(TestInfos);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReadTestsFromFile(TestListFile, TestInfos);
|
||||
}
|
||||
|
||||
bool AllSuccessful = true;
|
||||
|
||||
FAutomationTestFramework& Framework = FAutomationTestFramework::GetInstance();
|
||||
|
||||
for (const FAutomationTestInfo& TestInfo : TestInfos)
|
||||
{
|
||||
const FString TestCommand = TestInfo.GetTestName();
|
||||
const FString DisplayName = TestInfo.GetDisplayName();
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Log, TEXT("Running %s"), *DisplayName);
|
||||
|
||||
const int32 RoleIndex = 0; // always default to "local" role index. Only used for multi-participant tests
|
||||
Framework.StartTestByName(TestCommand, RoleIndex);
|
||||
|
||||
FDateTime Last = FDateTime::UtcNow();
|
||||
|
||||
while (!Framework.ExecuteLatentCommands())
|
||||
{
|
||||
// Because we are not 'ticked' by the Engine we need to pump the TaskGraph
|
||||
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
||||
|
||||
const FDateTime Now = FDateTime::UtcNow();
|
||||
const float Delta = static_cast<float>((Now - Last).GetTotalSeconds());
|
||||
|
||||
// .. and the core FTicker
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
FTSTicker::GetCoreTicker().Tick(Delta);
|
||||
#else
|
||||
FTicker::GetCoreTicker().Tick(Delta);
|
||||
#endif
|
||||
|
||||
Last = Now;
|
||||
}
|
||||
|
||||
FAutomationTestExecutionInfo ExecutionInfo;
|
||||
const bool CurrentTestSuccessful = Framework.StopTest(ExecutionInfo) && ExecutionInfo.GetErrorTotal() == 0;
|
||||
AllSuccessful = AllSuccessful && CurrentTestSuccessful;
|
||||
|
||||
const FString Result = CurrentTestSuccessful ? TEXT("OK") : TEXT("FAIL");
|
||||
|
||||
// [RUNTEST] is part of the protocol, so do not remove.
|
||||
OutFile << TEXT("[RUNTEST]") << *TestCommand << TEXT("|") << *DisplayName << TEXT("|") << *Result << TEXT("|") << ExecutionInfo.Duration << std::endl;
|
||||
|
||||
if (!CurrentTestSuccessful)
|
||||
{
|
||||
for (const auto& Entry : ExecutionInfo.GetEntries())
|
||||
{
|
||||
if (Entry.Event.Type == EAutomationEventType::Error)
|
||||
{
|
||||
OutFile << *Entry.Event.Message << std::endl;
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("%s"), *Entry.Event.Message);
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Log, TEXT("Failed %s"), *DisplayName);
|
||||
}
|
||||
|
||||
OutFile.flush();
|
||||
}
|
||||
|
||||
return AllSuccessful ? 0 : 1;
|
||||
}
|
||||
|
||||
UVSTestAdapterCommandlet::UVSTestAdapterCommandlet()
|
||||
{
|
||||
HelpDescription = TEXT("Commandlet for generating data used by Blueprint support in Visual Studio.");
|
||||
HelpUsage = TEXT("<Editor-Cmd.exe> <path_to_uproject> -run=VSTestAdapter [-stdout -multiprocess -silent -unattended -AllowStdOutLogVerbosity -NoShaderCompile]");
|
||||
|
||||
HelpParamNames.Add(ListTestsParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Required] The file path to write the test cases retrieved from FAutomationTestFramework"));
|
||||
|
||||
HelpParamNames.Add(RunTestsParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Required] The test cases that will be sent to FAutomationTestFramework to run."));
|
||||
|
||||
HelpParamNames.Add(TestResultsFileParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Required] The output file from running test cases that we parse to retrieve test case results."));
|
||||
|
||||
HelpParamNames.Add(FiltersParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] List of test filters to enable separated by '+'. Default is 'application+smoke+product+perf+stress+negative'"));
|
||||
|
||||
HelpParamNames.Add(HelpParam);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Print this help message and quit the commandlet immediately."));
|
||||
}
|
||||
|
||||
void UVSTestAdapterCommandlet::PrintHelp() const
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("%s"), *HelpDescription);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Usage: %s"), *HelpUsage);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Parameters:"));
|
||||
for (int32 Idx = 0; Idx < HelpParamNames.Num(); ++Idx)
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("\t-%s: %s"), *HelpParamNames[Idx], *HelpParamDescriptions[Idx]);
|
||||
}
|
||||
}
|
||||
|
||||
int32 UVSTestAdapterCommandlet::Main(const FString& Params)
|
||||
{
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
TMap<FString, FString> ParamVals;
|
||||
|
||||
// Functionality for Unreal Engine Test Adapter.
|
||||
ParseCommandLine(*Params, Tokens, Switches, ParamVals);
|
||||
if (ParamVals.Contains(HelpParam))
|
||||
{
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Default to all the test filters.
|
||||
auto filter = EAutomationTestFlags::ProductFilter | EAutomationTestFlags::SmokeFilter | EAutomationTestFlags::PerfFilter | EAutomationTestFlags::EngineFilter;
|
||||
if (ParamVals.Contains(FiltersParam))
|
||||
{
|
||||
FString filters = ParamVals[FiltersParam];
|
||||
if (filters.Contains("smoke"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::SmokeFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::SmokeFilter;
|
||||
}
|
||||
|
||||
if (filters.Contains("engine"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::EngineFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::EngineFilter;
|
||||
}
|
||||
|
||||
if (filters.Contains("product"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::ProductFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::ProductFilter;
|
||||
}
|
||||
|
||||
if (filters.Contains("perf"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::PerfFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::PerfFilter;
|
||||
}
|
||||
|
||||
if (filters.Contains("stress"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::StressFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::StressFilter;
|
||||
}
|
||||
|
||||
if (filters.Contains("negative"))
|
||||
{
|
||||
filter |= EAutomationTestFlags::NegativeFilter;
|
||||
}
|
||||
else
|
||||
{
|
||||
filter &= ~EAutomationTestFlags::NegativeFilter;
|
||||
}
|
||||
}
|
||||
|
||||
FAutomationTestFramework::GetInstance().SetRequestedTestFilter(filter);
|
||||
if (ParamVals.Contains(ListTestsParam))
|
||||
{
|
||||
return ListTests(ParamVals[ListTestsParam]);
|
||||
}
|
||||
else if (ParamVals.Contains(RunTestsParam) && ParamVals.Contains(TestResultsFileParam))
|
||||
{
|
||||
return RunTests(ParamVals[RunTestsParam], ParamVals[TestResultsFileParam]);
|
||||
}
|
||||
|
||||
PrintHelp();
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Commandlets/Commandlet.h"
|
||||
|
||||
#include <Runtime/Core/Public/Misc/AutomationTest.h>
|
||||
#include <Runtime/CoreUObject/Public/UObject/ObjectMacros.h>
|
||||
#include <Runtime/Engine/Classes/Commandlets/Commandlet.h>
|
||||
|
||||
#include "VSTestAdapterCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UVSTestAdapterCommandlet
|
||||
: public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVSTestAdapterCommandlet();
|
||||
|
||||
public:
|
||||
virtual int32 Main(const FString &Params) override;
|
||||
|
||||
private:
|
||||
void PrintHelp() const;
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogVisualStudioTools);
|
||||
|
||||
class FVisualStudioToolsModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override {}
|
||||
virtual void ShutdownModule() override {}
|
||||
};
|
||||
|
||||
IMPLEMENT_MODULE(FVisualStudioToolsModule, VisualStudioTools)
|
||||
@@ -0,0 +1,576 @@
|
||||
#include "VisualStudioToolsBlueprintBreakpointExtension.h"
|
||||
#include "FSmartBSTR.h"
|
||||
#include <Modules/ModuleManager.h>
|
||||
#include <Framework/MultiBox/MultiBoxBuilder.h>
|
||||
#include <BlueprintGraphClasses.h>
|
||||
#include <EditorSubsystem.h>
|
||||
#include <unknwn.h>
|
||||
#include <Windows/WindowsPlatformStackWalk.h>
|
||||
#include <Subsystems/Subsystem.h>
|
||||
#include <Windows/WindowsPlatformMisc.h>
|
||||
#include <Windows/WindowsPlatformProcess.h>
|
||||
#include <SourceCodeNavigation.h>
|
||||
#include <GraphEditorModule.h>
|
||||
#include <Containers/Array.h>
|
||||
#include <EdGraph/EdGraph.h>
|
||||
#include <EdGraph/EdGraphNode.h>
|
||||
#include <EdGraph/EdGraphPin.h>
|
||||
#include <Framework/Commands/UIAction.h>
|
||||
#include <Widgets/Notifications/SNotificationList.h>
|
||||
#include <Framework/Notifications/NotificationManager.h>
|
||||
#include <Misc/FileHelper.h>
|
||||
#include <Interfaces/IProjectManager.h>
|
||||
#include <Misc/UProjectInfo.h>
|
||||
#include <ProjectDescriptor.h>
|
||||
#include <Misc/App.h>
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#include <EditorStyleSet.h>
|
||||
|
||||
#if ENGINE_MAJOR_VERSION < 5
|
||||
#include <DbgHelp.h>
|
||||
#include <Psapi.h>
|
||||
#endif
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogUVisualStudioToolsBlueprintBreakpointExtension);
|
||||
|
||||
static const FName GraphEditorModuleName(TEXT("GraphEditor"));
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::Initialize(FSubsystemCollectionBase& Collection)
|
||||
{
|
||||
FGraphEditorModule& GraphEditorModule = FModuleManager::LoadModuleChecked<FGraphEditorModule>(GraphEditorModuleName);
|
||||
GraphEditorModule.GetAllGraphEditorContextMenuExtender().Add(
|
||||
FGraphEditorModule::FGraphEditorMenuExtender_SelectedNode::CreateUObject(this, &ThisClass::HandleOnExtendGraphEditorContextMenu));
|
||||
}
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::Deinitialize()
|
||||
{
|
||||
FGraphEditorModule* GraphEditorModule = FModuleManager::GetModulePtr<FGraphEditorModule>(GraphEditorModuleName);
|
||||
if (!GraphEditorModule)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GraphEditorModule->GetAllGraphEditorContextMenuExtender().RemoveAll(
|
||||
[](const FGraphEditorModule::FGraphEditorMenuExtender_SelectedNode& Delegate) {
|
||||
FName LocalFunction = GET_FUNCTION_NAME_CHECKED(ThisClass, HandleOnExtendGraphEditorContextMenu);
|
||||
return Delegate.TryGetBoundFunctionName() == LocalFunction;
|
||||
});
|
||||
}
|
||||
|
||||
TSharedRef<FExtender> UVisualStudioToolsBlueprintBreakpointExtension::HandleOnExtendGraphEditorContextMenu(
|
||||
const TSharedRef<FUICommandList> CommandList,
|
||||
const UEdGraph* Graph,
|
||||
const UEdGraphNode* Node,
|
||||
const UEdGraphPin* Pin,
|
||||
bool /* bIsConst */)
|
||||
{
|
||||
TSharedRef<FExtender> Extender = MakeShared<FExtender>();
|
||||
if (!CanAddVisualStudioBreakpoint(Node, nullptr, nullptr))
|
||||
{
|
||||
return Extender;
|
||||
}
|
||||
|
||||
const FName ExtensionHook(TEXT("EdGraphSchemaNodeActions"));
|
||||
Extender->AddMenuExtension(
|
||||
ExtensionHook,
|
||||
EExtensionHook::After,
|
||||
CommandList,
|
||||
FMenuExtensionDelegate::CreateUObject(this, &ThisClass::AddVisualStudioBlueprintBreakpointMenuOption, Node));
|
||||
|
||||
return Extender;
|
||||
}
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::AddVisualStudioBlueprintBreakpointMenuOption(FMenuBuilder& MenuBuilder, const UEdGraphNode *Node)
|
||||
{
|
||||
MenuBuilder.BeginSection(TEXT("VisualStudioTools"), FText::FromString("Visual Studio Tools"));
|
||||
MenuBuilder.AddMenuEntry(
|
||||
FText::FromString("Set breakpoint in Visual Studio"),
|
||||
FText::FromString("This will set a breakpoint in Visual Studio so the native debugger can break the execution"),
|
||||
FSlateIcon(),
|
||||
FUIAction(FExecuteAction::CreateUObject(this, &ThisClass::AddVisualStudioBreakpoint, Node)));
|
||||
MenuBuilder.EndSection();
|
||||
}
|
||||
|
||||
FString UVisualStudioToolsBlueprintBreakpointExtension::GetProjectPath(const FString &ProjectDir)
|
||||
{
|
||||
FString ProjectPath;
|
||||
if (!FFileHelper::LoadFileToString(ProjectPath, *(FPaths::EngineIntermediateDir() / TEXT("ProjectFiles") / TEXT("PrimaryProjectPath.txt"))))
|
||||
{
|
||||
const FProjectDescriptor* CurrentProject = IProjectManager::Get().GetCurrentProject();
|
||||
|
||||
if ((CurrentProject == nullptr || CurrentProject->Modules.Num() == 0) || !FUProjectDictionary::GetDefault().IsForeignProject(ProjectDir))
|
||||
{
|
||||
ProjectPath = FPaths::RootDir() / TEXT("UE5");
|
||||
}
|
||||
else
|
||||
{
|
||||
const FString BaseName = FApp::HasProjectName() ? FApp::GetProjectName() : FPaths::GetBaseFilename(ProjectDir);
|
||||
ProjectPath = ProjectDir / BaseName;
|
||||
}
|
||||
}
|
||||
|
||||
ProjectPath = ProjectPath + TEXT(".sln");
|
||||
|
||||
FPaths::NormalizeFilename(ProjectPath);
|
||||
|
||||
return ProjectPath;
|
||||
}
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::GetRunningVisualStudioDTE(TComPtr<EnvDTE::_DTE>& OutDTE)
|
||||
{
|
||||
IRunningObjectTable* RunningObjectTable;
|
||||
bool bResult = false;
|
||||
FString ProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir());
|
||||
FPaths::NormalizeDirectoryName(ProjectDir);
|
||||
FString SolutionPath = GetProjectPath(ProjectDir);
|
||||
|
||||
if (SUCCEEDED(GetRunningObjectTable(0, &RunningObjectTable)) && RunningObjectTable)
|
||||
{
|
||||
IEnumMoniker* MonikersTable;
|
||||
if (SUCCEEDED(RunningObjectTable->EnumRunning(&MonikersTable)))
|
||||
{
|
||||
MonikersTable->Reset();
|
||||
|
||||
// Look for all visual studio instances in the ROT
|
||||
IMoniker* CurrentMoniker;
|
||||
while (MonikersTable->Next(1, &CurrentMoniker, NULL) == S_OK)
|
||||
{
|
||||
IBindCtx* BindContext;
|
||||
LPOLESTR OutName;
|
||||
if (SUCCEEDED(CreateBindCtx(0, &BindContext)) && SUCCEEDED(CurrentMoniker->GetDisplayName(BindContext, NULL, &OutName)))
|
||||
{
|
||||
TComPtr<IUnknown> ComObject;
|
||||
if (SUCCEEDED(RunningObjectTable->GetObject(CurrentMoniker, &ComObject)))
|
||||
{
|
||||
TComPtr<EnvDTE::_DTE> TempDTE;
|
||||
if (SUCCEEDED(TempDTE.FromQueryInterface(__uuidof(EnvDTE::_DTE), ComObject)))
|
||||
{
|
||||
TComPtr<EnvDTE::_Solution> Solution;
|
||||
BSTR OutPath = nullptr;
|
||||
if (SUCCEEDED(TempDTE->get_Solution(&Solution)) &&
|
||||
SUCCEEDED(Solution->get_FullName(&OutPath)))
|
||||
{
|
||||
FString Filename(OutPath);
|
||||
FPaths::NormalizeFilename(Filename);
|
||||
if (Filename == SolutionPath || Filename == ProjectDir)
|
||||
{
|
||||
OutDTE = TempDTE;
|
||||
bResult = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Could not get solution from DTE"));
|
||||
}
|
||||
|
||||
SysFreeString(OutPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Could not get display name for moniker"));
|
||||
}
|
||||
BindContext->Release();
|
||||
CurrentMoniker->Release();
|
||||
if (bResult) break;
|
||||
}
|
||||
MonikersTable->Release();
|
||||
RunningObjectTable->Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Could not enumerate Running Object Table"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Could not get Running Object Table"));
|
||||
}
|
||||
|
||||
return bResult;
|
||||
}
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::CanAddVisualStudioBreakpoint(const UEdGraphNode* Node, UClass **OutOwnerClass, UFunction **OutFunction)
|
||||
{
|
||||
const UK2Node_CallFunction* K2Node = Cast<const UK2Node_CallFunction>(Node);
|
||||
if (!K2Node)
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Node is not a UK2Node_CallFunction"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UFunction* Function = K2Node->GetTargetFunction();
|
||||
if (!Function || !Function->IsNative())
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Function is not native"));
|
||||
return false;
|
||||
}
|
||||
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Trying to get function definition for %s"), *Function->GetName());
|
||||
|
||||
UClass* OwnerClass = Function->GetOwnerClass();
|
||||
if (!OwnerClass->HasAllClassFlags(CLASS_Native))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Owning class is not native"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OutOwnerClass) *OutOwnerClass = OwnerClass;
|
||||
if (OutFunction) *OutFunction = Function;
|
||||
return true;
|
||||
}
|
||||
|
||||
#if ENGINE_MAJOR_VERSION < 5
|
||||
|
||||
#define PRINT_PLATFORM_ERROR_MSG(_TXT) \
|
||||
do { \
|
||||
TCHAR _ErrorBuffer[MAX_SPRINTF] = { 0 }; \
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("" #_TXT ": [%s]"), \
|
||||
FPlatformMisc::GetSystemErrorMessage(_ErrorBuffer, MAX_SPRINTF, 0)); \
|
||||
} while (0)
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::PreloadModule(HANDLE ProcessHandle, HMODULE ModuleHandle, const FString& RemoteStorage)
|
||||
{
|
||||
int32 ErrorCode = 0;
|
||||
MODULEINFO ModuleInfo = { 0 };
|
||||
WCHAR ModuleName[FProgramCounterSymbolInfo::MAX_NAME_LENGTH] = { 0 };
|
||||
WCHAR ImageName[FProgramCounterSymbolInfo::MAX_NAME_LENGTH] = { 0 };
|
||||
#if PLATFORM_64BITS
|
||||
static_assert(sizeof(MODULEINFO) == 24, "Broken alignment for 64bit Windows include.");
|
||||
#else
|
||||
static_assert(sizeof(MODULEINFO) == 12, "Broken alignment for 32bit Windows include.");
|
||||
#endif
|
||||
|
||||
if (!GetModuleInformation(ProcessHandle, ModuleHandle, &ModuleInfo, sizeof(ModuleInfo)))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not read GetModuleInformation");
|
||||
return false;
|
||||
}
|
||||
|
||||
IMAGEHLP_MODULE64 ImageHelpModule = { 0 };
|
||||
ImageHelpModule.SizeOfStruct = sizeof(ImageHelpModule);
|
||||
if (!SymGetModuleInfo64(ProcessHandle, (DWORD64)ModuleInfo.EntryPoint, &ImageHelpModule))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not SymGetModuleInfo64 from module");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ImageHelpModule.SymType != SymDeferred && ImageHelpModule.SymType != SymNone)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!GetModuleFileNameExW(ProcessHandle, ModuleHandle, ImageName, 1024))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not GetModuleFileNameExW");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetModuleBaseNameW(ProcessHandle, ModuleHandle, ModuleName, 1024))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not GetModuleBaseNameW");
|
||||
return false;
|
||||
}
|
||||
|
||||
WCHAR SearchPath[MAX_PATH] = { 0 };
|
||||
WCHAR* FileName = NULL;
|
||||
const auto Result = GetFullPathNameW(ImageName, MAX_PATH, SearchPath, &FileName);
|
||||
|
||||
FString SearchPathList;
|
||||
if (Result != 0 && Result < MAX_PATH)
|
||||
{
|
||||
*FileName = 0;
|
||||
SearchPathList = SearchPath;
|
||||
}
|
||||
|
||||
if (!RemoteStorage.IsEmpty())
|
||||
{
|
||||
if (!SearchPathList.IsEmpty())
|
||||
{
|
||||
SearchPathList.AppendChar(TEXT(';'));
|
||||
}
|
||||
SearchPathList.Append(RemoteStorage);
|
||||
}
|
||||
|
||||
if (!SymSetSearchPathW(ProcessHandle, *SearchPathList))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not SymSetSearchPathW");
|
||||
return false;
|
||||
}
|
||||
|
||||
const DWORD64 BaseAddress = SymLoadModuleExW(
|
||||
ProcessHandle,
|
||||
ModuleHandle,
|
||||
ImageName,
|
||||
ModuleName,
|
||||
(DWORD64)ModuleInfo.lpBaseOfDll,
|
||||
ModuleInfo.SizeOfImage,
|
||||
NULL,
|
||||
0);
|
||||
if (!BaseAddress)
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not load the module");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::GetFunctionDefinitionLocation(const FString& FunctionSymbolName, const FString& FunctionModuleName, FString& SourceFilePath, uint32& SourceLineNumber)
|
||||
{
|
||||
const HANDLE ProcessHandle = GetCurrentProcess();
|
||||
HMODULE ModuleHandle = GetModuleHandle(*FunctionModuleName);
|
||||
if (!ModuleHandle || !PreloadModule(ProcessHandle, ModuleHandle, FPlatformStackWalk::GetDownstreamStorage()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ANSICHAR SymbolInfoBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME];
|
||||
PIMAGEHLP_SYMBOL64 SymbolInfoPtr = reinterpret_cast<IMAGEHLP_SYMBOL64*>(SymbolInfoBuffer);
|
||||
SymbolInfoPtr->SizeOfStruct = sizeof(SymbolInfoBuffer);
|
||||
SymbolInfoPtr->MaxNameLength = MAX_SYM_NAME;
|
||||
|
||||
FString FullyQualifiedSymbolName = FunctionSymbolName;
|
||||
if (!FunctionModuleName.IsEmpty())
|
||||
{
|
||||
FullyQualifiedSymbolName = FString::Printf(TEXT("%s!%s"), *FunctionModuleName, *FunctionSymbolName);
|
||||
}
|
||||
|
||||
if (!SymGetSymFromName64(ProcessHandle, TCHAR_TO_ANSI(*FullyQualifiedSymbolName), SymbolInfoPtr))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not load module symbol information");
|
||||
return false;
|
||||
}
|
||||
|
||||
IMAGEHLP_LINE64 FileAndLineInfo;
|
||||
FileAndLineInfo.SizeOfStruct = sizeof(FileAndLineInfo);
|
||||
|
||||
uint32 SourceColumnNumber = 0;
|
||||
if (!SymGetLineFromAddr64(ProcessHandle, SymbolInfoPtr->Address, (::DWORD*)&SourceColumnNumber, &FileAndLineInfo))
|
||||
{
|
||||
PRINT_PLATFORM_ERROR_MSG("Could not query module file and line number");
|
||||
return false;
|
||||
}
|
||||
|
||||
SourceLineNumber = FileAndLineInfo.LineNumber;
|
||||
SourceFilePath = FString((const ANSICHAR*)(FileAndLineInfo.FileName));
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::GetFunctionDefinitionLocation(const UEdGraphNode* Node, FString& SourceFilePath, FString& SymbolName, uint32& SourceLineNumber)
|
||||
{
|
||||
UClass* OwningClass;
|
||||
UFunction* Function;
|
||||
if (!CanAddVisualStudioBreakpoint(Node, &OwningClass, &Function))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FString ModuleName;
|
||||
|
||||
// Find module name for class
|
||||
if (!FSourceCodeNavigation::FindClassModuleName(OwningClass, ModuleName))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to find module name for class"));
|
||||
return false;
|
||||
}
|
||||
|
||||
SymbolName = FString::Printf(
|
||||
TEXT("%s%s::%s"),
|
||||
OwningClass->GetPrefixCPP(),
|
||||
*OwningClass->GetName(),
|
||||
*Function->GetName());
|
||||
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Symbol %s is defined in module %s"), *SymbolName, *ModuleName);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION >= 5
|
||||
uint32 SourceColumnNumber = 0;
|
||||
return FPlatformStackWalk::GetFunctionDefinitionLocation(
|
||||
SymbolName,
|
||||
ModuleName,
|
||||
SourceFilePath,
|
||||
SourceLineNumber,
|
||||
SourceColumnNumber);
|
||||
#else
|
||||
return GetFunctionDefinitionLocation(SymbolName, ModuleName, SourceFilePath, SourceLineNumber);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::GetProcessById(const TComPtr<EnvDTE::Processes>& Processes, DWORD CurrentProcessId, TComPtr<EnvDTE::Process>& OutProcess)
|
||||
{
|
||||
long Count = 0;
|
||||
if (FAILED(Processes->get_Count(&Count)))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Could not get the process count"));
|
||||
return false;
|
||||
}
|
||||
|
||||
TComPtr<EnvDTE::Process> Process;
|
||||
for (long i = 1; i <= Count; i++)
|
||||
{
|
||||
VARIANT Index;
|
||||
Index.vt = VT_I4;
|
||||
Index.lVal = i;
|
||||
if (SUCCEEDED(Processes->Item(Index, &Process)))
|
||||
{
|
||||
long PID = 0;
|
||||
if (SUCCEEDED(Process->get_ProcessID(&PID)) && CurrentProcessId == PID)
|
||||
{
|
||||
OutProcess = Process;
|
||||
return true;
|
||||
}
|
||||
|
||||
Process.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::AttachDebuggerIfNecessary(const TComPtr<EnvDTE::Debugger>& Debugger)
|
||||
{
|
||||
TComPtr<EnvDTE::Processes> Processes;
|
||||
if (FAILED(Debugger->get_DebuggedProcesses(&Processes)))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to get debugging proccess"));
|
||||
return;
|
||||
}
|
||||
|
||||
TComPtr<EnvDTE::Process> Process;
|
||||
DWORD CurrentProcessId = GetCurrentProcessId();
|
||||
if (!GetProcessById(Processes, CurrentProcessId, Process))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to check if UE is already in debug mode"));
|
||||
return;
|
||||
}
|
||||
|
||||
// currently debugging this process
|
||||
if (Process.Get() != nullptr)
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Already debugging UE."));
|
||||
return;
|
||||
}
|
||||
|
||||
Processes.Reset();
|
||||
if (FAILED(Debugger->get_LocalProcesses(&Processes)))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to attach to process"));
|
||||
return;
|
||||
}
|
||||
|
||||
Process.Reset();
|
||||
if (!GetProcessById(Processes, CurrentProcessId, Process))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to get all process"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Process.Get() == nullptr)
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("No UE proccess running."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (FAILED(Process->Attach()))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to attach to process"));
|
||||
}
|
||||
}
|
||||
|
||||
bool UVisualStudioToolsBlueprintBreakpointExtension::SetVisualStudioBreakpoint(const UEdGraphNode* Node, const FString& SourceFilePath, const FString& SymbolName, uint32 SourceLineNumber)
|
||||
{
|
||||
TComPtr<EnvDTE::_DTE> DTE;
|
||||
bool bBreakpointAdded = false;
|
||||
if (!GetRunningVisualStudioDTE(DTE))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to access Visual Studio via DTE"));
|
||||
return bBreakpointAdded;
|
||||
}
|
||||
|
||||
TComPtr<EnvDTE::Debugger> Debugger;
|
||||
TComPtr<EnvDTE::Breakpoints> Breakpoints;
|
||||
if (SUCCEEDED(DTE->get_Debugger(&Debugger)) && SUCCEEDED(Debugger->get_Breakpoints(&Breakpoints)))
|
||||
{
|
||||
FSmartBSTR BSTREmptyStr;
|
||||
FSmartBSTR BSTRFilePath(SourceFilePath);
|
||||
HRESULT Result = Breakpoints->Add(
|
||||
*BSTREmptyStr,
|
||||
*BSTRFilePath,
|
||||
SourceLineNumber,
|
||||
1,
|
||||
*BSTREmptyStr,
|
||||
EnvDTE::dbgBreakpointConditionType::dbgBreakpointConditionTypeWhenTrue,
|
||||
*BSTREmptyStr,
|
||||
*BSTREmptyStr,
|
||||
0,
|
||||
*BSTREmptyStr,
|
||||
0,
|
||||
EnvDTE::dbgHitCountType::dbgHitCountTypeNone,
|
||||
&Breakpoints);
|
||||
|
||||
if (FAILED(Result))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to add breakpoint"));
|
||||
}
|
||||
else
|
||||
{
|
||||
bBreakpointAdded = true;
|
||||
AttachDebuggerIfNecessary(Debugger);
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Breakpoint set for %s"), *SymbolName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to get debugger or breakpoints"));
|
||||
}
|
||||
|
||||
return bBreakpointAdded;
|
||||
}
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::AddVisualStudioBreakpoint(const UEdGraphNode* Node)
|
||||
{
|
||||
FWindowsPlatformMisc::CoInitialize();
|
||||
FPlatformStackWalk::InitStackWalking();
|
||||
FString SourceFilePath;
|
||||
FString SymbolName;
|
||||
uint32 SourceLineNumber;
|
||||
bool bBreakpointAdded = false;
|
||||
|
||||
if (GetFunctionDefinitionLocation(Node, SourceFilePath, SymbolName, SourceLineNumber))
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, TEXT("Method defined in %s at line %d"), *SourceFilePath, SourceLineNumber);
|
||||
bBreakpointAdded = SetVisualStudioBreakpoint(Node, SourceFilePath, SymbolName, SourceLineNumber);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogUVisualStudioToolsBlueprintBreakpointExtension, Error, TEXT("Failed to get function definition location"));
|
||||
}
|
||||
|
||||
ShowOperationResultNotification(bBreakpointAdded, SymbolName);
|
||||
FWindowsPlatformMisc::CoUninitialize();
|
||||
}
|
||||
|
||||
void UVisualStudioToolsBlueprintBreakpointExtension::ShowOperationResultNotification(bool bBreakpointAdded, const FString &SymbolName)
|
||||
{
|
||||
FNotificationInfo Info(bBreakpointAdded ? FText::FromString(FString::Printf(TEXT("Breakpoint added at %s"), *SymbolName)) : FText::FromString("Could not add Breakpoint in Visual Studio"));
|
||||
#if ENGINE_MAJOR_VERSION >= 5 && ENGINE_MINOR_VERSION >= 1
|
||||
Info.Image = FAppStyle::GetBrush(TEXT("LevelEditor.RecompileGameCode"));
|
||||
#else
|
||||
Info.Image = FEditorStyle::GetBrush(TEXT("LevelEditor.RecompileGameCode"));
|
||||
#endif
|
||||
Info.FadeInDuration = 0.1f;
|
||||
Info.FadeOutDuration = 0.5f;
|
||||
Info.ExpireDuration = 3.0f;
|
||||
Info.bUseThrobber = false;
|
||||
Info.bUseSuccessFailIcons = true;
|
||||
Info.bUseLargeFont = true;
|
||||
Info.bFireAndForget = false;
|
||||
Info.bAllowThrottleWhenFrameRateIsLow = false;
|
||||
Info.WidthOverride = 400.0f;
|
||||
TSharedPtr<SNotificationItem> NotificationItem = FSlateNotificationManager::Get().AddNotification(Info);
|
||||
NotificationItem->SetCompletionState(bBreakpointAdded ? SNotificationItem::CS_Success : SNotificationItem::CS_Fail);
|
||||
NotificationItem->ExpireAndFadeout();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <CoreMinimal.h>
|
||||
#include <EditorSubsystem.h>
|
||||
#include <EdGraph/EdGraph.h>
|
||||
#include <EdGraph/EdGraphNode.h>
|
||||
#include <EdGraph/EdGraphPin.h>
|
||||
#include <GraphEditorModule.h>
|
||||
#include <VisualStudioDTE.h>
|
||||
#include <Microsoft/COMPointer.h>
|
||||
#include <Runtime/Launch/Resources/Version.h>
|
||||
#include "VisualStudioToolsBlueprintBreakpointExtension.generated.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogUVisualStudioToolsBlueprintBreakpointExtension, Log, All);
|
||||
|
||||
UCLASS()
|
||||
class UVisualStudioToolsBlueprintBreakpointExtension : public UEditorSubsystem
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnNodeMenuExtensionHookRequestDelegate, const UEdGraphNode*, const UEdGraph*, TSet<FName>&);
|
||||
|
||||
void Initialize(FSubsystemCollectionBase& Collection) override;
|
||||
void Deinitialize() override;
|
||||
|
||||
FOnNodeMenuExtensionHookRequestDelegate& OnNodeMenuExtensionHookRequest() { return OnNodeMenuExtensionHookRequestDelegate; }
|
||||
|
||||
private:
|
||||
FOnNodeMenuExtensionHookRequestDelegate OnNodeMenuExtensionHookRequestDelegate;
|
||||
|
||||
TSharedRef<FExtender> HandleOnExtendGraphEditorContextMenu(
|
||||
const TSharedRef<FUICommandList> CommandList,
|
||||
const UEdGraph* Graph,
|
||||
const UEdGraphNode* Node,
|
||||
const UEdGraphPin* Pin,
|
||||
bool bIsConst);
|
||||
|
||||
void AddVisualStudioBlueprintBreakpointMenuOption(FMenuBuilder& MenuBuilder, const UEdGraphNode* node);
|
||||
|
||||
void AddVisualStudioBreakpoint(const UEdGraphNode* Node);
|
||||
|
||||
bool GetFunctionDefinitionLocation(const UEdGraphNode* Node, FString& SourceFilePath, FString& SymbolName, uint32& SourceLineNumber);
|
||||
|
||||
bool SetVisualStudioBreakpoint(const UEdGraphNode* Node, const FString& SourceFilePath, const FString& SymbolName, uint32 SourceLineNumber);
|
||||
|
||||
bool CanAddVisualStudioBreakpoint(const UEdGraphNode* Node, UClass** OutOwnerClass, UFunction** OutFunction);
|
||||
|
||||
void ShowOperationResultNotification(bool bBreakpointAdded, const FString& SymbolName);
|
||||
|
||||
FString GetProjectPath(const FString& ProjectDir);
|
||||
|
||||
bool GetRunningVisualStudioDTE(TComPtr<EnvDTE::_DTE>& OutDTE);
|
||||
|
||||
void AttachDebuggerIfNecessary(const TComPtr<EnvDTE::Debugger>& Debugger);
|
||||
|
||||
bool GetProcessById(const TComPtr<EnvDTE::Processes>& Processes, DWORD CurrentProcessId, TComPtr<EnvDTE::Process>& OutProcess);
|
||||
|
||||
#if ENGINE_MAJOR_VERSION < 5
|
||||
bool PreloadModule(HANDLE ProcessHandle, HMODULE ModuleHandle, const FString& RemoteStorage);
|
||||
|
||||
bool GetFunctionDefinitionLocation(const FString& FunctionSymbolName, const FString& FunctionModuleName, FString& SourceFilePath, uint32& SourceLineNumber);
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,492 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#include "VisualStudioToolsCommandlet.h"
|
||||
|
||||
#include "Algo/Transform.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "Blueprint/BlueprintSupport.h"
|
||||
#include "BlueprintAssetHelpers.h"
|
||||
#include "Engine/BlueprintGeneratedClass.h"
|
||||
#include "JsonObjectConverter.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/ScopeExit.h"
|
||||
#include "Policies/CondensedJsonPrintPolicy.h"
|
||||
#include "SourceCodeNavigation.h"
|
||||
#include "UObject/CoreRedirects.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
namespace VisualStudioTools
|
||||
{
|
||||
static const FName CategoryFName = TEXT("Category");
|
||||
static const FName ModuleNameFName = TEXT("ModuleName");
|
||||
|
||||
static TArray<FProperty*> GetChangedPropertiesList(
|
||||
UStruct* InStruct, const uint8* DataPtr, const uint8* DefaultDataPtr)
|
||||
{
|
||||
TArray<FProperty*> Result;
|
||||
|
||||
const UClass* OwnerClass = Cast<UClass>(InStruct);
|
||||
|
||||
// Walk only in the properties defined in the current class, the super classes are processed individually
|
||||
for (TFieldIterator<FProperty> It(OwnerClass, EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
||||
{
|
||||
FProperty* Property = *It;
|
||||
for (int32 Idx = 0; Idx < Property->ArrayDim; Idx++)
|
||||
{
|
||||
const uint8* PropertyValue = Property->ContainerPtrToValuePtr<uint8>(DataPtr, Idx);
|
||||
const uint8* DefaultPropertyValue = Property->ContainerPtrToValuePtrForDefaults<uint8>(InStruct, DefaultDataPtr, Idx);
|
||||
|
||||
if (!Property->Identical(PropertyValue, DefaultPropertyValue))
|
||||
{
|
||||
Result.Add(Property);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
static bool FindBlueprintNativeParents(
|
||||
const UClass* BlueprintGeneratedClass, TFunctionRef<void(UClass*)> Callback)
|
||||
{
|
||||
bool bAnyNativeParent = false;
|
||||
for (UClass* Super = BlueprintGeneratedClass->GetSuperClass(); Super; Super = Super->GetSuperClass())
|
||||
{
|
||||
// Ignore the root `UObject` class and non-native parents.
|
||||
if (Super->HasAnyClassFlags(CLASS_Native) && Super->GetFName() != NAME_Object)
|
||||
{
|
||||
bAnyNativeParent = true;
|
||||
Callback(Super);
|
||||
}
|
||||
}
|
||||
|
||||
return bAnyNativeParent;
|
||||
}
|
||||
|
||||
struct FPropertyEntry
|
||||
{
|
||||
FProperty* Property;
|
||||
TArray<int32> Blueprints;
|
||||
};
|
||||
|
||||
struct FFunctionEntry
|
||||
{
|
||||
UFunction* Function;
|
||||
TArray<int32> Blueprints;
|
||||
};
|
||||
|
||||
struct FClassEntry
|
||||
{
|
||||
const UClass* Class;
|
||||
TArray<int32> Blueprints;
|
||||
TMap<FString, FPropertyEntry> Properties;
|
||||
TMap<FString, FFunctionEntry> Functions;
|
||||
};
|
||||
|
||||
using ClassMap = TMap<FString, FClassEntry>;
|
||||
|
||||
struct FAssetIndex
|
||||
{
|
||||
TSet<FString> AssetPathCache;
|
||||
ClassMap Classes;
|
||||
TArray<const UClass*> Blueprints;
|
||||
|
||||
void ProcessBlueprint(const UBlueprintGeneratedClass* BlueprintGeneratedClass)
|
||||
{
|
||||
if (BlueprintGeneratedClass == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int32 BlueprintIndex = Blueprints.Num();
|
||||
|
||||
bool bHasAnyParent = FindBlueprintNativeParents(BlueprintGeneratedClass, [&](UClass* Parent)
|
||||
{
|
||||
FString ParentName = Parent->GetFName().ToString();
|
||||
if (!Classes.Contains(ParentName))
|
||||
{
|
||||
Classes.Add(ParentName).Class = Parent;
|
||||
}
|
||||
|
||||
FClassEntry& ClassEntry = Classes[ParentName];
|
||||
|
||||
ClassEntry.Blueprints.Add(BlueprintIndex);
|
||||
|
||||
// Retrieve the properties from the parent class that changed in the Blueprint class, by comparing their CDOs.
|
||||
UObject* GeneratedClassDefault = BlueprintGeneratedClass->ClassDefaultObject;
|
||||
UObject* SuperClassDefault = Parent->GetDefaultObject(false);
|
||||
TArray<FProperty*> ChangedProperties = GetChangedPropertiesList(Parent, (uint8*)GeneratedClassDefault, (uint8*)SuperClassDefault);
|
||||
|
||||
for (FProperty* Property : ChangedProperties)
|
||||
{
|
||||
FString PropertyName = Property->GetFName().ToString();
|
||||
if (!ClassEntry.Properties.Contains(PropertyName))
|
||||
{
|
||||
ClassEntry.Properties.Add(PropertyName).Property = Property;
|
||||
}
|
||||
|
||||
FPropertyEntry& PropEntry = ClassEntry.Properties[PropertyName];
|
||||
PropEntry.Blueprints.Add(BlueprintIndex);
|
||||
}
|
||||
|
||||
// Iterate over the functions originally from the parent class
|
||||
// and check if they are implemented in the BP class as well.
|
||||
for (TFieldIterator<UFunction> It(Parent, EFieldIteratorFlags::ExcludeSuper); It; ++It)
|
||||
{
|
||||
UFunction* Fn = BlueprintGeneratedClass->FindFunctionByName((*It)->GetFName(), EIncludeSuperFlag::ExcludeSuper);
|
||||
// If the function not present in the BP class directly, it means it was implemented. Otherwise, ignore.
|
||||
if (!Fn)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FString FnName = Fn->GetFName().ToString();
|
||||
if (!ClassEntry.Functions.Contains(FnName))
|
||||
{
|
||||
ClassEntry.Functions.Add(FnName).Function = Fn;
|
||||
}
|
||||
|
||||
FFunctionEntry& FuncEntry = ClassEntry.Functions[FnName];
|
||||
FuncEntry.Blueprints.Add(BlueprintIndex);
|
||||
}
|
||||
});
|
||||
|
||||
if (bHasAnyParent)
|
||||
{
|
||||
check(Blueprints.Add(BlueprintGeneratedClass) == BlueprintIndex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
using JsonWriter = TJsonWriter<TCHAR, TCondensedJsonPrintPolicy<TCHAR>>;
|
||||
|
||||
static bool ShouldSerializePropertyValue(FProperty* Property)
|
||||
{
|
||||
if (Property->ArrayDim > 1) // Skip properties that are not scalars
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FEnumProperty* EnumProperty = CastField<FEnumProperty>(Property))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Property))
|
||||
{
|
||||
UEnum* EnumDef = NumericProperty->GetIntPropertyEnum();
|
||||
if (EnumDef != NULL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NumericProperty->IsFloatingPoint())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (NumericProperty->IsInteger())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void SerializeBlueprints(TSharedRef<JsonWriter>& Json, TArray<const UClass*> Items)
|
||||
{
|
||||
Json->WriteArrayStart();
|
||||
for (const UClass* Blueprint : Items)
|
||||
{
|
||||
Json->WriteObjectStart();
|
||||
|
||||
Json->WriteValue(TEXT("name"), Blueprint->GetName());
|
||||
Json->WriteValue(TEXT("path"), Blueprint->GetPathName());
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
static void SerializeProperties(TSharedRef<JsonWriter>& Json, FClassEntry& Entry, TArray<const UClass*>& Blueprints)
|
||||
{
|
||||
Json->WriteArrayStart();
|
||||
for (auto& Item : Entry.Properties)
|
||||
{
|
||||
auto& PropName = Item.Key;
|
||||
auto& PropEntry = Item.Value;
|
||||
FProperty* Property = PropEntry.Property;
|
||||
|
||||
Json->WriteObjectStart();
|
||||
|
||||
Json->WriteValue(TEXT("name"), PropName);
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("metadata"));
|
||||
{
|
||||
Json->WriteObjectStart();
|
||||
if (Property->HasMetaData(CategoryFName))
|
||||
{
|
||||
Json->WriteValue(TEXT("categories"), Property->GetMetaData(CategoryFName));
|
||||
}
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("values"));
|
||||
{
|
||||
Json->WriteArrayStart();
|
||||
for (auto& BlueprintEntry : PropEntry.Blueprints)
|
||||
{
|
||||
Json->WriteObjectStart();
|
||||
|
||||
Json->WriteValue(TEXT("blueprint"), BlueprintEntry);
|
||||
|
||||
UObject* GeneratedClassDefault = Blueprints[BlueprintEntry]->ClassDefaultObject;
|
||||
const uint8* PropData = PropEntry.Property->ContainerPtrToValuePtr<uint8>(GeneratedClassDefault);
|
||||
|
||||
if (ShouldSerializePropertyValue(PropEntry.Property))
|
||||
{
|
||||
TSharedPtr<FJsonValue> JsonValue = FJsonObjectConverter::UPropertyToJsonValue(Property, PropData);
|
||||
FJsonSerializer::Serialize(JsonValue.ToSharedRef(), TEXT("value"), Json);
|
||||
}
|
||||
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
static void SerializeFunctions(TSharedRef<JsonWriter>& Json, FClassEntry& Entry)
|
||||
{
|
||||
Json->WriteArrayStart();
|
||||
for (auto& Item : Entry.Functions)
|
||||
{
|
||||
auto& Name = Item.Key;
|
||||
auto& FnEntry = Item.Value;
|
||||
Json->WriteObjectStart();
|
||||
Json->WriteValue(TEXT("name"), Name);
|
||||
Json->WriteValue(TEXT("blueprints"), FnEntry.Blueprints);
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
static void SerializeClasses(TSharedRef<JsonWriter>& Json, ClassMap& Items, TArray<const UClass*> Blueprints)
|
||||
{
|
||||
Json->WriteArrayStart();
|
||||
for (auto& Item : Items)
|
||||
{
|
||||
auto& ClassName = Item.Key;
|
||||
auto& Entry = Item.Value;
|
||||
Json->WriteObjectStart();
|
||||
Json->WriteValue(TEXT("name"), FString::Printf(TEXT("%s%s"), Entry.Class->GetPrefixCPP(), *Entry.Class->GetName()));
|
||||
|
||||
Json->WriteValue(TEXT("blueprints"), Entry.Blueprints);
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("properties"));
|
||||
SerializeProperties(Json, Entry, Blueprints);
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("functions"));
|
||||
SerializeFunctions(Json, Entry);
|
||||
|
||||
Json->WriteObjectEnd();
|
||||
}
|
||||
Json->WriteArrayEnd();
|
||||
}
|
||||
|
||||
static void SerializeToIndex(FAssetIndex Index, FArchive& IndexFile)
|
||||
{
|
||||
TSharedRef<JsonWriter> Json = JsonWriter::Create(&IndexFile);
|
||||
|
||||
Json->WriteObjectStart();
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("blueprints"));
|
||||
SerializeBlueprints(Json, Index.Blueprints);
|
||||
|
||||
Json->WriteIdentifierPrefix(TEXT("classes"));
|
||||
SerializeClasses(Json, Index.Classes, Index.Blueprints);
|
||||
|
||||
Json->WriteObjectEnd();
|
||||
Json->Close();
|
||||
}
|
||||
|
||||
static TArray<FString> GetModulesByPath(const FString& InDir)
|
||||
{
|
||||
TArray<FString> OutResult;
|
||||
Algo::TransformIf(
|
||||
FSourceCodeNavigation::GetSourceFileDatabase().GetModuleNames(),
|
||||
OutResult,
|
||||
[&](const FString& Module) {
|
||||
return FPaths::IsUnderDirectory(Module, InDir);
|
||||
},
|
||||
[](const FString& Module) {
|
||||
#if 0
|
||||
// Old version assumes that each module is in a folder with the same name as the module
|
||||
return FPaths::GetBaseFilename(FPaths::GetPath(*Module));
|
||||
#else
|
||||
// New version assumes that each module is in a file with the name Module.Build.cs
|
||||
FString TempString = FPaths::GetBaseFilename(*Module);
|
||||
TempString.RemoveFromEnd(TEXT(".Build"));
|
||||
return TempString;
|
||||
#endif
|
||||
});
|
||||
|
||||
return OutResult;
|
||||
}
|
||||
|
||||
static void GetNativeClassesByPath(const FString& InDir, TArray<TWeakObjectPtr<UClass>>& OutClasses)
|
||||
{
|
||||
TArray<FString> Modules = GetModulesByPath(InDir);
|
||||
|
||||
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
|
||||
{
|
||||
UClass* TestClass = *ClassIt;
|
||||
if (!TestClass->HasAnyClassFlags(CLASS_Native))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FAssetData ClassAssetData(TestClass);
|
||||
FString ModuleName = ClassAssetData.GetTagValueRef<FString>(ModuleNameFName);
|
||||
|
||||
if (!ModuleName.IsEmpty() && Modules.Contains(ModuleName))
|
||||
{
|
||||
OutClasses.Add(TestClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void RunAssetScan(
|
||||
FAssetIndex& Index,
|
||||
const TArray<TWeakObjectPtr<UClass>>& FilterBaseClasses)
|
||||
{
|
||||
FARFilter Filter;
|
||||
Filter.bRecursivePaths = true;
|
||||
Filter.bRecursiveClasses = true;
|
||||
AssetHelpers::SetBlueprintClassFilter(Filter);
|
||||
|
||||
// Add all base classes to the tag filter for native parent
|
||||
Algo::Transform(FilterBaseClasses, Filter.TagsAndValues, [](const TWeakObjectPtr<UClass>& Class) {
|
||||
return MakeTuple(
|
||||
FBlueprintTags::NativeParentClassPath,
|
||||
FObjectPropertyBase::GetExportPath(Class.Get(), nullptr /*Parent*/, nullptr /*ExportRootScope*/, 0 /*PortFlags*/));
|
||||
});
|
||||
|
||||
// Take account of any core redirects for the blueprint classes we want to scan.
|
||||
for (const auto& BaseClass : FilterBaseClasses)
|
||||
{
|
||||
if (BaseClass.IsValid())
|
||||
{
|
||||
TArray<FCoreRedirectObjectName> PreviousNames;
|
||||
if (FCoreRedirects::FindPreviousNames(ECoreRedirectFlags::Type_Class, BaseClass->GetPathName(), PreviousNames))
|
||||
{
|
||||
for (const auto& PreviousName : PreviousNames)
|
||||
{
|
||||
// FString PreviousString = FObjectPropertyBase::GetExportPath(BaseClass->GetClass()->GetClassPathName(), PreviousName.ToString()); // Alternative way to add /Script/CoreUObject.Class'' wrapper - but not sure it makes sense to use the new class when referencing a previous name
|
||||
FString PreviousString = "/Script/CoreUObject.Class'" + PreviousName.ToString() + "'";
|
||||
Filter.TagsAndValues.Add(FBlueprintTags::NativeParentClassPath, PreviousString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry")).Get();
|
||||
|
||||
TArray<FAssetData> TargetAssets;
|
||||
AssetRegistry.GetAssets(Filter, TargetAssets);
|
||||
|
||||
AssetHelpers::ForEachAsset(TargetAssets,
|
||||
[&](UBlueprintGeneratedClass* BlueprintGeneratedClass, const FAssetData& /*AssetData*/)
|
||||
{
|
||||
Index.ProcessBlueprint(BlueprintGeneratedClass);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace VS
|
||||
|
||||
static constexpr auto FilterSwitch = TEXT("filter");
|
||||
static constexpr auto FullSwitch = TEXT("full");
|
||||
|
||||
UVisualStudioToolsCommandlet::UVisualStudioToolsCommandlet()
|
||||
: Super()
|
||||
{
|
||||
HelpDescription = TEXT("Commandlet for generating data used by Blueprint support in Visual Studio.");
|
||||
|
||||
HelpParamNames.Add(FilterSwitch);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Scan only blueprints derived from native classes under the provided path. Defaults to `FPaths::ProjectDir`. Incompatible with `-full`."));
|
||||
|
||||
HelpParamNames.Add(FullSwitch);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Scan blueprints derived from native classes from ALL modules, include the Engine. This can be _very slow_ for large projects. Incompatible with `-filter`."));
|
||||
|
||||
HelpUsage = TEXT("<Editor-Cmd.exe> <path_to_uproject> -run=VisualStudioTools -output=<path_to_output_file> [-filter=<subdir_native_classes>|-full] [-unattended -noshadercompile -nosound -nullrhi -nocpuprofilertrace -nocrashreports -nosplash]");
|
||||
}
|
||||
|
||||
int32 UVisualStudioToolsCommandlet::Run(
|
||||
TArray<FString>& Tokens,
|
||||
TArray<FString>& Switches,
|
||||
TMap<FString, FString>& ParamVals,
|
||||
FArchive& OutArchive)
|
||||
{
|
||||
using namespace VisualStudioTools;
|
||||
|
||||
FString* Filter = ParamVals.Find(FilterSwitch);
|
||||
const bool bFullScan = Switches.Contains(FullSwitch);
|
||||
|
||||
if (Filter != nullptr && bFullScan)
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Incompatible scan options."));
|
||||
PrintHelp();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TArray<TWeakObjectPtr<UClass>> FilterBaseClasses;
|
||||
if (!bFullScan)
|
||||
{
|
||||
if (Filter)
|
||||
{
|
||||
FPaths::NormalizeDirectoryName(*Filter);
|
||||
GetNativeClassesByPath(*Filter, FilterBaseClasses);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetNativeClassesByPath(FPaths::ProjectDir(), FilterBaseClasses);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
|
||||
{
|
||||
UClass* TestClass = *ClassIt;
|
||||
if (!TestClass->HasAnyClassFlags(CLASS_Native))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FilterBaseClasses.Add(TestClass);
|
||||
}
|
||||
}
|
||||
|
||||
FAssetIndex Index;
|
||||
RunAssetScan(Index, FilterBaseClasses);
|
||||
SerializeToIndex(Index, OutArchive);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Found %d blueprints."), Index.Blueprints.Num());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "VisualStudioToolsCommandletBase.h"
|
||||
|
||||
#include "VisualStudioToolsCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UVisualStudioToolsCommandlet
|
||||
: public UVisualStudioToolsCommandletBase
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UVisualStudioToolsCommandlet();
|
||||
int32 Run(
|
||||
TArray<FString>& Tokens,
|
||||
TArray<FString>& Switches,
|
||||
TMap<FString, FString>& ParamVals,
|
||||
FArchive& OutArchive) override;
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#include "VisualStudioToolsCommandletBase.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "VisualStudioTools.h"
|
||||
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
static constexpr auto HelpSwitch = TEXT("help");
|
||||
static constexpr auto OutputSwitch = TEXT("output");
|
||||
|
||||
UVisualStudioToolsCommandletBase::UVisualStudioToolsCommandletBase()
|
||||
{
|
||||
IsClient = false;
|
||||
IsEditor = true;
|
||||
IsServer = false;
|
||||
LogToConsole = false;
|
||||
ShowErrorCount = false;
|
||||
|
||||
HelpParamNames.Add(OutputSwitch);
|
||||
HelpParamDescriptions.Add(TEXT("[Required] The file path to write the command output."));
|
||||
|
||||
HelpParamNames.Add(HelpSwitch);
|
||||
HelpParamDescriptions.Add(TEXT("[Optional] Print this help message and quit the commandlet immediately."));
|
||||
}
|
||||
|
||||
void UVisualStudioToolsCommandletBase::PrintHelp() const
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("%s"), *HelpDescription);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Usage: %s"), *HelpUsage);
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Parameters:"));
|
||||
for (int32 i = 0; i < HelpParamNames.Num(); ++i)
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("\t-%s: %s"), *HelpParamNames[i], *HelpParamDescriptions[i]);
|
||||
}
|
||||
}
|
||||
|
||||
int32 UVisualStudioToolsCommandletBase::Main(const FString& Params)
|
||||
{
|
||||
TArray<FString> Tokens;
|
||||
TArray<FString> Switches;
|
||||
TMap<FString, FString> ParamVals;
|
||||
|
||||
ParseCommandLine(*Params, Tokens, Switches, ParamVals);
|
||||
|
||||
if (Switches.Contains(HelpSwitch))
|
||||
{
|
||||
PrintHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
UE_LOG(LogVisualStudioTools, Display, TEXT("Init VS Tools cmdlet."));
|
||||
|
||||
if (!FPaths::IsProjectFilePathSet())
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("You must invoke this commandlet with a project file."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
FString FullPath = ParamVals.FindRef(OutputSwitch);
|
||||
|
||||
if (FullPath.IsEmpty() && !FParse::Value(*Params, TEXT("output "), FullPath))
|
||||
{
|
||||
// VS:1678426 - Initial version was using `-output "path-to-file"` (POSIX style).
|
||||
// However, that does not support paths with spaces, even when surrounded with
|
||||
// quotes because `FParse::Value` only handles that case when there's no space
|
||||
// between the parameter name and quoted value.
|
||||
// For back-compatibility reasons, parse that style by including the space in
|
||||
// the parameter token like it's usually done for the `=` sign.
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Missing file output parameter."));
|
||||
PrintHelp();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TUniquePtr<FArchive> OutArchive{ IFileManager::Get().CreateFileWriter(*FullPath) };
|
||||
if (!OutArchive)
|
||||
{
|
||||
UE_LOG(LogVisualStudioTools, Error, TEXT("Failed to create index with path: %s."), *FullPath);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this->Run(Tokens, Switches, ParamVals, *OutArchive);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Commandlets/Commandlet.h"
|
||||
|
||||
#include "VisualStudioToolsCommandletBase.generated.h"
|
||||
|
||||
UCLASS(Abstract)
|
||||
class UVisualStudioToolsCommandletBase
|
||||
: public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
int32 Main(const FString& Params) override;
|
||||
|
||||
protected:
|
||||
UVisualStudioToolsCommandletBase();
|
||||
|
||||
void PrintHelp() const;
|
||||
|
||||
virtual int32 Run(
|
||||
TArray<FString>& Tokens,
|
||||
TArray<FString>& Switches,
|
||||
TMap<FString, FString>& ParamVals,
|
||||
FArchive& OutArchive) PURE_VIRTUAL(UVisualStudioToolsCommandletBase::Run, return 0;);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogVisualStudioTools, Log, All);
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2022 (c) Microsoft. All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class VisualStudioTools : ModuleRules
|
||||
{
|
||||
public VisualStudioTools(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
bool bIsCustomDevBuild = System.Environment.GetEnvironmentVariable("VSTUE_IsCustomDevBuild") == "1";
|
||||
if (bIsCustomDevBuild)
|
||||
{
|
||||
// Get correct header suggestions in the IDE and validate
|
||||
// the plugin build without having to affect for the whole target,
|
||||
// which is expensive in source-builds of the engine.
|
||||
PCHUsage = ModuleRules.PCHUsageMode.NoPCHs;
|
||||
bUseUnity = false;
|
||||
|
||||
// When debugging the commandlet code, disable optimizations to get
|
||||
// proper local variable inspection and less inlined stack frames
|
||||
OptimizeCode = CodeOptimization.Never;
|
||||
|
||||
// Enable more restrict warnings during compilation in UE5.
|
||||
// Required by tasks in the compliance pipeline.
|
||||
if (Target.Version.MajorVersion >= 5)
|
||||
{
|
||||
UnsafeTypeCastWarningLevel = WarningLevel.Error;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
}
|
||||
|
||||
// To support UE5.1+, the code is using the new FTopLevelAssetPath API
|
||||
// with a detection of support via version numbers.
|
||||
// If the check is producing a false positive/negative in your version of the engine
|
||||
// you can change the block below and force the check as enabled/disabled.
|
||||
if ((Target.Version.MajorVersion == 5 && Target.Version.MinorVersion >= 1) || Target.Version.MajorVersion > 5)
|
||||
{
|
||||
PrivateDefinitions.Add("FILTER_ASSETS_BY_CLASS_PATH=1");
|
||||
}
|
||||
else
|
||||
{
|
||||
PrivateDefinitions.Add("FILTER_ASSETS_BY_CLASS_PATH=0");
|
||||
}
|
||||
|
||||
PublicDependencyModuleNames.AddRange(
|
||||
new[]
|
||||
{
|
||||
"Core",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"ApplicationCore",
|
||||
"AssetRegistry",
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Json",
|
||||
"JsonUtilities",
|
||||
"Kismet",
|
||||
"UnrealEd",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"ToolMenus",
|
||||
"EditorSubsystem",
|
||||
"MainFrame",
|
||||
"BlueprintGraph",
|
||||
"VisualStudioDTE",
|
||||
"EditorStyle",
|
||||
"Projects"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
38
Plugins/VisualStudioTools/VisualStudioTools.uplugin
Normal file
38
Plugins/VisualStudioTools/VisualStudioTools.uplugin
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "2.7",
|
||||
"FriendlyName": "Visual Studio Integration Tools",
|
||||
"Description": "Enables integration with Visual Studio IDE.",
|
||||
"Category": "Programming",
|
||||
"CreatedBy": "Microsoft",
|
||||
"CreatedByURL": "http://www.microsoft.com",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/362651520df94e4fa65492dbcba44ae2",
|
||||
"SupportURL": "https://developercommunity.visualstudio.com/",
|
||||
"EnabledByDefault": true,
|
||||
"Installed": false,
|
||||
"bExplicitlyLoaded": true,
|
||||
"CanContainContent": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win64"
|
||||
],
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "VisualStudioTools",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default",
|
||||
"PlatformAllowList": [
|
||||
"Win64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "VisualStudioBlueprintDebuggerHelper",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "None",
|
||||
"PlatformAllowList": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
From f7238064c8680f6392793eb664ee2c773daff594 Mon Sep 17 00:00:00 2001
|
||||
From: Oleksandr Kozlov <okozlov@microsoft.com>
|
||||
Date: Tue, 1 Apr 2025 15:22:14 +0200
|
||||
Subject: [PATCH] Support extra UBT args in UAT.BuildPlugin
|
||||
|
||||
Forwarding extra parameters to UBT to allow customizing the build of a plugin.
|
||||
Example: runuat.bat buildpluing -plugin=... -ubtargs="-LinkerArguments=\"/profile\""
|
||||
|
||||
---
|
||||
.../Scripts/BuildPluginCommand.Automation.cs | 13 +++++++++++++
|
||||
1 file changed, 13 insertions(+)
|
||||
|
||||
diff --git a/Engine/Source/Programs/AutomationTool/Scripts/BuildPluginCommand.Automation.cs b/Engine/Source/Programs/AutomationTool/Scripts/BuildPluginCommand.Automation.cs
|
||||
index 5a43dc0c4..aaf3f192f 100644
|
||||
--- a/Engine/Source/Programs/AutomationTool/Scripts/BuildPluginCommand.Automation.cs
|
||||
+++ b/Engine/Source/Programs/AutomationTool/Scripts/BuildPluginCommand.Automation.cs
|
||||
@@ -64,6 +64,9 @@ public sealed class BuildPlugin : BuildCommand
|
||||
// Option for verifying that all include directive s
|
||||
bool bStrictIncludes = ParseParam("StrictIncludes");
|
||||
|
||||
+ // Extra arguments forwarded to UBT
|
||||
+ string UBTArgs = ParseParamValue("ubtargs");
|
||||
+
|
||||
// Make sure the packaging directory is valid
|
||||
DirectoryReference PackageDir = new DirectoryReference(PackageParam);
|
||||
|
||||
@@ -126,6 +129,16 @@ public sealed class BuildPlugin : BuildCommand
|
||||
AdditionalArgs.Append(" -NoPCH -NoSharedPCH -DisableUnity");
|
||||
}
|
||||
|
||||
+ // Pass extra parameters to UBT
|
||||
+ if (string.IsNullOrEmpty(UBTArgs) == false)
|
||||
+ {
|
||||
+ Logger.LogInformation("Building with extra UBT parameters: {UBTArgs}", UBTArgs);
|
||||
+ string Arg = UBTArgs;
|
||||
+ Arg = Arg.TrimStart(new char[] { '\"' });
|
||||
+ Arg = Arg.TrimEnd(new char[] { '\"' });
|
||||
+ AdditionalArgs.Append(' ').Append(Arg);
|
||||
+ }
|
||||
+
|
||||
// check if any architectures were specified
|
||||
foreach (UnrealTargetPlatform Platform in UnrealTargetPlatform.GetValidPlatforms())
|
||||
{
|
||||
--
|
||||
2.49.0.windows.1
|
||||
|
||||
21
Plugins/VisualStudioTools/azure-pipelines/TSAOptions.json
Normal file
21
Plugins/VisualStudioTools/azure-pipelines/TSAOptions.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"tsaVersion": "TsaV2",
|
||||
"codebase": "NewOrUpdate",
|
||||
"codebaseName": "vc-ue-extensions",
|
||||
"tsaStamp": "DevDiv",
|
||||
"tsaEnvironment": "PROD",
|
||||
"notificationAliases": [
|
||||
"hebaggio@microsoft.com",
|
||||
"cpp-apogee@microsoft.com"
|
||||
],
|
||||
"codebaseAdmins": [
|
||||
"REDMOND\\hebaggio",
|
||||
"REDMOND\\cpp-apogee"
|
||||
],
|
||||
"instanceUrl": "https://devdiv.visualstudio.com",
|
||||
"projectName": "DevDiv",
|
||||
"areaPath": "DevDiv\\Cpp Developer Experience\\GameDev Experience\\Unreal Engine integrations",
|
||||
"iterationPath": "DevDiv",
|
||||
"alltools": true,
|
||||
"repositoryName": "vc-ue-extensions"
|
||||
}
|
||||
123
Plugins/VisualStudioTools/azure-pipelines/compliance.yml
Normal file
123
Plugins/VisualStudioTools/azure-pipelines/compliance.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
# IMPORTANT:
|
||||
# Do not run BinSkim because we do not control producing the binaries. That process is owned by
|
||||
# Epic. We just provide source code. Since we do not control the build, BinSkim is not needed.
|
||||
|
||||
variables:
|
||||
Codeql.Enabled: true
|
||||
Codeql.SourceRoot: '$(Build.SourcesDirectory)'
|
||||
|
||||
trigger:
|
||||
- main
|
||||
|
||||
pr:
|
||||
autoCancel: true
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
- dev/*
|
||||
|
||||
schedules:
|
||||
- cron: "0 12 * * Sun"
|
||||
displayName: Weekly run
|
||||
branches:
|
||||
include:
|
||||
- main
|
||||
always: true
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1ESPipelineTemplates
|
||||
type: git
|
||||
name: 1ESPipelineTemplates/1ESPipelineTemplates
|
||||
ref: refs/tags/release
|
||||
|
||||
extends:
|
||||
template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
|
||||
parameters:
|
||||
pool:
|
||||
name: VSEngSS-MicroBuild2022-1ES
|
||||
os: windows
|
||||
customBuildTags:
|
||||
- ES365AIMigrationTooling
|
||||
stages:
|
||||
- stage: stage
|
||||
jobs:
|
||||
- job: 'UnrealEngine_VisualStudioTools_Compliance'
|
||||
timeoutInMinutes: 1440
|
||||
steps:
|
||||
- task: CmdLine@2
|
||||
displayName: 'Clone Unreal Engine Repository'
|
||||
inputs:
|
||||
script: 'git clone "https://$(token)@github.com/EpicGames/UnrealEngine" --single-branch --branch $(ue_branch) --depth 1 ue'
|
||||
workingDirectory: '$(Agent.BuildDirectory)'
|
||||
- task: CmdLine@2
|
||||
displayName: 'Apply patch to allow us to pass linker flags to the build'
|
||||
inputs:
|
||||
script: 'git apply --ignore-whitespace $(Build.SourcesDirectory)/azure-pipelines/Support-extra-UBT-args-in-UAT.BuildPlugin.patch'
|
||||
workingDirectory: '$(Agent.BuildDirectory)\ue'
|
||||
- task: PowerShell@2
|
||||
displayName: '[UE] Append /unattended option'
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script:
|
||||
$filePath = "$(Agent.BuildDirectory)/ue/Setup.bat";
|
||||
(Get-Content $filePath).Replace("/register", "/register /unattended") | Set-Content $filePath
|
||||
- task: CmdLine@2
|
||||
displayName: '[UE] Run Setup.bat'
|
||||
inputs:
|
||||
script: 'ue\Setup.bat --force'
|
||||
workingDirectory: '$(Agent.BuildDirectory)'
|
||||
- task: MSBuild@1
|
||||
displayName: 'Build Plugin'
|
||||
timeoutInMinutes: 300
|
||||
inputs:
|
||||
solution: '$(Build.SourcesDirectory)/build.proj'
|
||||
msbuildArguments: '/p:UnrealEngine=$(Agent.BuildDirectory)\ue /p:OutputPath=$(Build.ArtifactStagingDirectory)\drop /p:VulkanReadyBinaries=true'
|
||||
createLogFile: true
|
||||
- task: CopyFiles@2
|
||||
displayName: 'Collect binaries to analyze'
|
||||
inputs:
|
||||
SourceFolder: '$(Build.ArtifactStagingDirectory)\drop'
|
||||
Contents: '**\unrealeditor-visualstudiotools.*'
|
||||
TargetFolder: '$(Build.ArtifactStagingDirectory)\binariesToAnalyze'
|
||||
CleanTargetFolder: true
|
||||
OverWrite: true
|
||||
- task: PoliCheck@2
|
||||
inputs:
|
||||
targetType: 'F'
|
||||
targetArgument: '$(Build.SourcesDirectory)'
|
||||
- task: ComponentGovernanceComponentDetection@0
|
||||
inputs:
|
||||
ignoreDirectories: '$(Agent.BuildDirectory)\ue'
|
||||
displayName: 'Component Detection'
|
||||
- task: APIScan@2
|
||||
displayName: 'Run APIScan'
|
||||
inputs:
|
||||
softwareFolder: '$(Build.ArtifactStagingDirectory)/binariesToAnalyze'
|
||||
softwareName: 'Visual Studio Tools for Unreal Engine'
|
||||
softwareVersionNum: '2.4'
|
||||
softwareBuildNum: '$(Build.BuildId)'
|
||||
toolVersion: 'Latest'
|
||||
env:
|
||||
AzureServicesAuthConnectionString: runAs=App;AppId=$(ApiScanClientId)
|
||||
- task: SDLNativeRules@3
|
||||
displayName: 'Run the PREfast SDL Native Rules for MSBuild'
|
||||
env:
|
||||
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
|
||||
inputs:
|
||||
publishXML: true
|
||||
userProvideBuildInfo: auto
|
||||
rulesetName: Recommended
|
||||
setupCommandlinePicker: 'vs2022'
|
||||
- task: PublishSecurityAnalysisLogs@3
|
||||
displayName: 'Publish security analysis logs'
|
||||
inputs:
|
||||
ArtifactName: 'CodeAnalysisLogs'
|
||||
ArtifactType: 'Container'
|
||||
AllTools: true
|
||||
ToolLogsNotFoundAction: 'Standard'
|
||||
- task: TSAUpload@2
|
||||
displayName: 'TSA upload'
|
||||
inputs:
|
||||
GdnPublishTsaOnboard: True
|
||||
GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)/azure-pipelines/TSAOptions.json'
|
||||
117
Plugins/VisualStudioTools/azure-pipelines/release.yml
Normal file
117
Plugins/VisualStudioTools/azure-pipelines/release.yml
Normal file
@@ -0,0 +1,117 @@
|
||||
# IMPORTANT:
|
||||
# Do not run BinSkim because we do not control producing the binaries. That process is owned by
|
||||
# Epic. We just provide source code. Since we do not control the build, BinSkim is not needed.
|
||||
|
||||
# Manual trigger for now
|
||||
trigger: none
|
||||
pr: none
|
||||
|
||||
parameters:
|
||||
- name: SignTypeOverride
|
||||
displayName: Signing Type (default is real for the main branch and test otherwise)
|
||||
type: string
|
||||
default: default
|
||||
values:
|
||||
- default
|
||||
- test
|
||||
- real
|
||||
|
||||
variables:
|
||||
# MicroBuild requires TeamName to be set.
|
||||
- name: TeamName
|
||||
value: C++ Unreal Engine
|
||||
- name: TagName
|
||||
value: $[replace(variables['Build.SourceBranch'], 'refs/tags/', '')]
|
||||
# If the user didn't override the signing type, then only real-sign on main.
|
||||
- ${{ if ne(parameters.SignTypeOverride, 'default') }}:
|
||||
- name: SignType
|
||||
value: ${{ parameters.SignTypeOverride }}
|
||||
- ${{ if and(eq(parameters.SignTypeOverride, 'default'), or(eq(variables['Build.SourceBranchName'], 'main'), startsWith(variables['Build.SourceBranch'], 'refs/tags'))) }}:
|
||||
- name: SignType
|
||||
value: real
|
||||
- ${{ if and(eq(parameters.SignTypeOverride, 'default'), not(or(eq(variables['Build.SourceBranchName'], 'main'), startsWith(variables['Build.SourceBranch'], 'refs/tags')))) }}:
|
||||
- name: SignType
|
||||
value: test
|
||||
|
||||
resources:
|
||||
repositories:
|
||||
- repository: 1ESPipelineTemplates
|
||||
type: git
|
||||
name: 1ESPipelineTemplates/1ESPipelineTemplates
|
||||
ref: refs/tags/release
|
||||
|
||||
extends:
|
||||
template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
|
||||
parameters:
|
||||
pool:
|
||||
name: VSEngSS-MicroBuild2022-1ES
|
||||
os: windows
|
||||
customBuildTags:
|
||||
- ES365AIMigrationTooling
|
||||
stages:
|
||||
- stage: stage
|
||||
jobs:
|
||||
- job: 'UnrealEngine_VisualStudioTools_Release'
|
||||
timeoutInMinutes: 1440
|
||||
templateContext:
|
||||
outputParentDirectory: $(Agent.BuildDirectory)/out/
|
||||
outputs:
|
||||
- output: pipelineArtifact
|
||||
displayName: 'Publish signed plugin files'
|
||||
targetPath: $(Agent.BuildDirectory)/out
|
||||
artifactName: SignedPlugin
|
||||
artifactType: container
|
||||
sbomEnabled: false
|
||||
steps:
|
||||
- powershell: |
|
||||
Write-Host "##vso[task.setVariable variable=SHA1]$(git rev-parse HEAD)"
|
||||
displayName: Save tag commit hash
|
||||
workingDirectory: '$(Build.SourcesDirectory)'
|
||||
- task: MicroBuildSigningPlugin@4
|
||||
displayName: Install MicroBuild Signing
|
||||
inputs:
|
||||
signType: $(SignType)
|
||||
zipSources: true
|
||||
- task: PowerShell@2
|
||||
displayName: "Create zip excluding .git folder"
|
||||
inputs:
|
||||
targetType: 'inline'
|
||||
script: |
|
||||
$destinationDirectory = "$(Agent.BuildDirectory)/out"
|
||||
New-Item -Path $destinationDirectory -ItemType Directory
|
||||
git archive -o "$destinationDirectory/VisualStudioTools.zip" $(SHA1)
|
||||
- powershell: New-FileCatalog -Path .\VisualStudioTools.zip -CatalogFilePath .\VisualStudioTools.zip.cat -CatalogVersion 2.0
|
||||
displayName: Create standalone catalog
|
||||
workingDirectory: '$(Agent.BuildDirectory)\out'
|
||||
- task: NuGetToolInstaller@1
|
||||
inputs:
|
||||
versionSpec: 5.7
|
||||
- task: NuGetCommand@2
|
||||
displayName: 'NuGet Restore MicroBuild Signing Extension'
|
||||
inputs:
|
||||
command: 'restore'
|
||||
restoreSolution: 'Scripts/SignDetached.proj'
|
||||
feedsToUse: 'config'
|
||||
restoreDirectory: '$(Build.SourcesDirectory)\Scripts\packages'
|
||||
- task: MSBuild@1
|
||||
displayName: Sign catalogs
|
||||
inputs:
|
||||
solution: Scripts/SignDetached.proj
|
||||
msbuildArguments: /p:SignType=$(SignType) /p:BaseOutputDirectory=$(Agent.BuildDirectory)\out
|
||||
# === Disabled for now in favor of uploading the artifacts to the pipeline ===
|
||||
# - task: GitHubRelease@1
|
||||
# displayName: Pre-release to public repo
|
||||
# condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags'))
|
||||
# inputs:
|
||||
# gitHubConnection: GitHub-VSCodeExtensions
|
||||
# repositoryName: microsoft/vc-ue-extensions
|
||||
# action: create
|
||||
# target: $(SHA1)
|
||||
# tagSource: gitTag
|
||||
# tag: $(TagName)
|
||||
# title: $(TagName)
|
||||
# isDraft: true
|
||||
# isPreRelease: true
|
||||
# assets: |
|
||||
# $(Agent.BuildDirectory)\out\VisualStudioTools.zip
|
||||
# $(Agent.BuildDirectory)\out\VisualStudioTools.zip.cat
|
||||
24
Plugins/VisualStudioTools/build.proj
Normal file
24
Plugins/VisualStudioTools/build.proj
Normal file
@@ -0,0 +1,24 @@
|
||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<PluginFolder>$(MSBuildProjectDirectory)</PluginFolder>
|
||||
<PluginFile>$([System.IO.Path]::Combine($(PluginFolder), `VisualStudioTools.uplugin`))</PluginFile>
|
||||
<OutputPath Condition=" '$(OutputPath)'=='' ">$([System.IO.Path]::Combine($(PluginFolder), "bin"))</OutputPath>
|
||||
<EnginePath>$(UnrealEngine)</EnginePath>
|
||||
<EnginePath Condition="!Exists('$(EnginePath)')">
|
||||
$([MSBuild]::GetRegistryValue('HKEY_LOCAL_MACHINE\SOFTWARE\EpicGames\Unreal Engine\$(UnrealEngine)', 'InstalledDirectory'))
|
||||
</EnginePath>
|
||||
<UATScript>$(EnginePath.Trim())\Engine\Build\BatchFiles\RunUAT.bat</UATScript>
|
||||
<UnversionedFlag Condition=" '$(Versioned)' != 'true'">-Unversioned</UnversionedFlag>
|
||||
<AdditionalFlags Condition=" '$(VulkanReadyBinaries)' == 'true'">-ubtargs="-LinkerArguments=\"/profile\" "</AdditionalFlags>
|
||||
</PropertyGroup>
|
||||
<Target Name="Build">
|
||||
<Error Text="Cannot locate the RunUAT.bat script at $(UATScript). Check if the $UnrealEngine property is a valid path or installed version." Condition="!Exists('$(UATScript)')"></Error>
|
||||
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
|
||||
<Exec
|
||||
Command=""$(UATScript)" BuildPlugin -Plugin="$(PluginFile)" -TargetPlatforms=Win64 -Package="$(OutputPath)" $(AdditionalFlags) $(UnversionedFlag) -FromMsBuild"
|
||||
EnvironmentVariables="VSTUE_IsCustomDevBuild=1"/>
|
||||
</Target>
|
||||
<Target Name="Clean" >
|
||||
<RemoveDir Directories="$(OutputPath);"/>
|
||||
</Target>
|
||||
</Project>
|
||||
15
Source/ToonTanks.Target.cs
Normal file
15
Source/ToonTanks.Target.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ToonTanksTarget : TargetRules
|
||||
{
|
||||
public ToonTanksTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Game;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V5;
|
||||
|
||||
ExtraModuleNames.AddRange( new string[] { "ToonTanks" } );
|
||||
}
|
||||
}
|
||||
34
Source/ToonTanks/BasePawn.cpp
Normal file
34
Source/ToonTanks/BasePawn.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "BasePawn.h"
|
||||
|
||||
// Sets default values
|
||||
ABasePawn::ABasePawn()
|
||||
{
|
||||
// Set this pawn to call Tick() every frame. You can turn this off to improve performance if you don't need it.
|
||||
PrimaryActorTick.bCanEverTick = true;
|
||||
|
||||
}
|
||||
|
||||
// Called when the game starts or when spawned
|
||||
void ABasePawn::BeginPlay()
|
||||
{
|
||||
Super::BeginPlay();
|
||||
|
||||
}
|
||||
|
||||
// Called every frame
|
||||
void ABasePawn::Tick(float DeltaTime)
|
||||
{
|
||||
Super::Tick(DeltaTime);
|
||||
|
||||
}
|
||||
|
||||
// Called to bind functionality to input
|
||||
void ABasePawn::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
|
||||
{
|
||||
Super::SetupPlayerInputComponent(PlayerInputComponent);
|
||||
|
||||
}
|
||||
|
||||
33
Source/ToonTanks/BasePawn.h
Normal file
33
Source/ToonTanks/BasePawn.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "BasePawn.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class TOONTANKS_API ABasePawn : public APawn
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
// Sets default values for this pawn's properties
|
||||
ABasePawn();
|
||||
|
||||
protected:
|
||||
// Called when the game starts or when spawned
|
||||
virtual void BeginPlay() override;
|
||||
|
||||
private:
|
||||
UPROPERTY()
|
||||
class UCapsuleComponent* CapsuleComp;
|
||||
|
||||
public:
|
||||
// Called every frame
|
||||
virtual void Tick(float DeltaTime) override;
|
||||
|
||||
// Called to bind functionality to input
|
||||
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
||||
|
||||
};
|
||||
23
Source/ToonTanks/ToonTanks.Build.cs
Normal file
23
Source/ToonTanks/ToonTanks.Build.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ToonTanks : ModuleRules
|
||||
{
|
||||
public ToonTanks(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
|
||||
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" });
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(new string[] { });
|
||||
|
||||
// Uncomment if you are using Slate UI
|
||||
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
|
||||
|
||||
// Uncomment if you are using online features
|
||||
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
|
||||
|
||||
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
|
||||
}
|
||||
}
|
||||
6
Source/ToonTanks/ToonTanks.cpp
Normal file
6
Source/ToonTanks/ToonTanks.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#include "ToonTanks.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, ToonTanks, "ToonTanks" );
|
||||
6
Source/ToonTanks/ToonTanks.h
Normal file
6
Source/ToonTanks/ToonTanks.h
Normal file
@@ -0,0 +1,6 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
15
Source/ToonTanksEditor.Target.cs
Normal file
15
Source/ToonTanksEditor.Target.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
using UnrealBuildTool;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ToonTanksEditorTarget : TargetRules
|
||||
{
|
||||
public ToonTanksEditorTarget(TargetInfo Target) : base(Target)
|
||||
{
|
||||
Type = TargetType.Editor;
|
||||
DefaultBuildSettings = BuildSettingsVersion.V5;
|
||||
|
||||
ExtraModuleNames.AddRange( new string[] { "ToonTanks" } );
|
||||
}
|
||||
}
|
||||
26
ToonTanks.uproject
Normal file
26
ToonTanks.uproject
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "5.6",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "ToonTanks",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "VisualStudioTools",
|
||||
"Enabled": true,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"TargetPlatforms": [],
|
||||
"AdditionalRootDirectories": [],
|
||||
"AdditionalPluginDirectories": [],
|
||||
"EpicSampleNameHash": ""
|
||||
}
|
||||
Reference in New Issue
Block a user