Plugins FAB and Electronic Nodes Added + fixed compilation
This commit is contained in:
parent
7697680b48
commit
e689430fc8
|
@ -0,0 +1,2 @@
|
||||||
|
[/Script/Fab.MegascansMaterialParentSettings]
|
||||||
|
MaterialParents=((Base3D, (StandardMaterial="/Fab/Materials/Standard/M_MS_Base.M_MS_Base",VTMaterial="/Fab/Materials/VT/M_MS_Base_VT.M_MS_Base_VT")),(Fuzz3D, (StandardMaterial="/Fab/Materials/Standard/M_MS_Base_Fuzz.M_MS_Base_Fuzz",VTMaterial="/Fab/Materials/VT/M_MS_Base_Fuzz_VT.M_MS_Base_Fuzz_VT")),(Transmission3D, (StandardMaterial="/Fab/Materials/Standard/M_MS_Base_Trm.M_MS_Base_Trm",VTMaterial="/Fab/Materials/VT/M_MS_Base_Trm_VT.M_MS_Base_Trm_VT")),(Glass3D, (StandardMaterial="/Fab/Materials/Standard/M_MS_Glass.M_MS_Glass",VTMaterial="/Fab/Materials/VT/M_MS_Glass_VT.M_MS_Glass_VT")),(Surface, (StandardMaterial="/Fab/Materials/Standard/M_MS_Srf.M_MS_Srf",VTMaterial="/Fab/Materials/VT/M_MS_Srf_VT.M_MS_Srf_VT")),(TransmissionSurface, (StandardMaterial="/Fab/Materials/Standard/M_MS_Base_Trm.M_MS_Base_Trm",VTMaterial="/Fab/Materials/VT/M_MS_Base_Trm_VT.M_MS_Base_Trm_VT")),(FabricSurface, (StandardMaterial="/Fab/Materials/Standard/M_MS_FabricMasked.M_MS_FabricMasked",VTMaterial="/Fab/Materials/VT/M_MS_FabricMasked_VT.M_MS_FabricMasked_VT")),(Decal, (StandardMaterial="/Fab/Materials/Standard/M_MS_Decal.M_MS_Decal",VTMaterial="/Fab/Materials/VT/M_MS_Decal_VT.M_MS_Decal_VT")),(Plant, (StandardMaterial="/Fab/Materials/Standard/M_MS_Foliage.M_MS_Foliage",VTMaterial="/Fab/Materials/VT/M_MS_Foliage_VT.M_MS_Foliage_VT")),(PlantBillboard, (StandardMaterial="/Fab/Materials/Standard/M_MS_Billboard.M_MS_Billboard",VTMaterial="/Fab/Materials/VT/M_MS_Billboard_VT.M_MS_Billboard_VT")))
|
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/BP_GlobalFoliageActor_UE5.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/BP_GlobalFoliageActor_UE5.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Arrow.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Arrow.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Arrow_Test.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Arrow_Test.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Base.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Base.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Sock.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Geometry/Icon_Sock.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Materials/MA_UI_Element.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Materials/MA_UI_Element.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Materials/MA_UI_Element_Inst.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Actors/GlobalFoliageActor/Materials/MA_UI_Element_Inst.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AOAdjustments.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AOAdjustments.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AdjustTwoSidedBaseColor.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AdjustTwoSidedBaseColor.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AdjustTwoSidedNormal.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AdjustTwoSidedNormal.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AlbedoAdjustments.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_AlbedoAdjustments.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_DistanceBasedGradient.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_DistanceBasedGradient.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_DistanceBasedOpacity.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_DistanceBasedOpacity.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_FabricSSAdjustments.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_FabricSSAdjustments.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_FabricSpecular.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_FabricSpecular.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateBranchMask_Plants.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateBranchMask_Plants.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateTranslucency.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateTranslucency.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateTwoSidedSpecular.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_GenerateTwoSidedSpecular.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_NormalAdjustments.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_NormalAdjustments.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_PerInstanceActorRandom.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_PerInstanceActorRandom.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_RoughnessAdjustments.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_RoughnessAdjustments.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_TwoSidedRoughness.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_TwoSidedRoughness.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WindAnimation.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WindAnimation.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WindowImperfection.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WindowImperfection.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WorldspaceColorVariation.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialFunctions/QMF_WorldspaceColorVariation.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/MaterialParameterCollection/QMPC_GlobalFoliageActor.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/MaterialParameterCollection/QMPC_GlobalFoliageActor.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/Materials/Standard/M_MS_FabricMasked.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Materials/Standard/M_MS_FabricMasked.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Materials/Standard/M_MS_FabricOpaque.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Materials/Standard/M_MS_FabricOpaque.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/Placeholders/FabMeshPlaceholderMaterial.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Placeholders/FabMeshPlaceholderMaterial.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
BIN
Plugins/Fab/Content/Placeholders/FabMeshPlaceholderMaterialInstance.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Placeholders/FabMeshPlaceholderMaterialInstance.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/Textures/Standard/T_DefaultDisplacement.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Textures/Standard/T_DefaultDisplacement.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/Textures/Standard/T_Default_WindNoise.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Textures/Standard/T_Default_WindNoise.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
BIN
Plugins/Fab/Content/Textures/VT/T_DefaultDisplacement_VT.uasset (Stored with Git LFS)
Normal file
BIN
Plugins/Fab/Content/Textures/VT/T_DefaultDisplacement_VT.uasset (Stored with Git LFS)
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,42 @@
|
||||||
|
{
|
||||||
|
"FileVersion": 3,
|
||||||
|
"Version": 3,
|
||||||
|
"VersionName": "0.0.3",
|
||||||
|
"FriendlyName": "Fab",
|
||||||
|
"Description": "Fab Plugin",
|
||||||
|
"Category": "Other",
|
||||||
|
"CreatedBy": "Epic Games, Inc.",
|
||||||
|
"CreatedByURL": "http://epicgames.com",
|
||||||
|
"DocsURL": "",
|
||||||
|
"MarketplaceURL": "",
|
||||||
|
"SupportURL": "",
|
||||||
|
"EngineVersion": "5.5.0",
|
||||||
|
"EnabledByDefault": true,
|
||||||
|
"CanContainContent": true,
|
||||||
|
"SupportedTargetPlatforms": [
|
||||||
|
"Win64",
|
||||||
|
"Mac",
|
||||||
|
"Linux"
|
||||||
|
],
|
||||||
|
"Modules": [
|
||||||
|
{
|
||||||
|
"Name": "Fab",
|
||||||
|
"Type": "Editor",
|
||||||
|
"LoadingPhase": "PostDefault"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Plugins": [
|
||||||
|
{
|
||||||
|
"Name": "Interchange",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "EditorScriptingUtilities",
|
||||||
|
"Enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "EOSShared",
|
||||||
|
"Enabled": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Binary file not shown.
|
@ -0,0 +1,9 @@
|
||||||
|
<svg width="237" height="237" viewBox="0 -38 237 237" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M232.779 71.3823C234.28 70.3925 235.358 69.1697 236.014 67.7089C236.671 66.2498 236.999 64.4371 236.999 62.2691C236.999 59.4186 236.126 57.0523 234.387 55.1685C229.23 70.3462 216.664 86.4938 198.712 101.262H213.149C222.107 101.262 228.228 99.2375 231.511 95.1873C233.809 92.4096 234.959 88.642 234.959 83.8846C234.959 81.2011 234.22 79.1058 232.742 77.5988C231.264 76.0917 229.26 74.9846 226.727 74.279L226.798 73.5734C229.049 73.2446 231.042 72.5142 232.779 71.3839V71.3823Z" fill="white"/>
|
||||||
|
<path d="M37.9884 50.8276H10.7351L1.12021 101.263C5.15742 84.8629 18.3339 67.0182 37.9868 50.8259L37.9884 50.8276Z" fill="white"/>
|
||||||
|
<path d="M122.531 96.6712H95.7964L92.6298 101.263H61.5319L70.1593 89.184H31.5597L29.2375 101.263H1.12013C-0.453278 109.514 -0.631027 117.398 2.30348 124.523C15.6018 156.827 78.3621 161.29 142.484 134.491C164.292 125.376 183.448 113.819 198.712 101.263H124.008L122.53 96.6712H122.531Z" fill="white"/>
|
||||||
|
<path d="M113.596 70.394L104.801 83.3211H118.239L114.088 70.394H113.596Z" fill="white"/>
|
||||||
|
<path d="M78.066 68.6276H35.4999L34.3742 74.4194H74.618L72.327 86.1468L97.5542 50.8259H136.462L152.186 97.7916L161.226 50.8259H220.326C226.095 50.8259 230.317 51.8504 232.991 53.8994C233.508 54.2944 233.972 54.719 234.388 55.1702C237.736 45.315 237.961 35.8696 234.507 27.4769C221.209 -4.82669 158.448 -9.28993 94.3267 17.5094C72.4652 26.6457 53.2699 38.2376 37.9884 50.8275H81.4432L78.066 68.6293V68.6276Z" fill="white"/>
|
||||||
|
<path d="M203.019 64.3198H186.766L185.501 70.8188H201.753C203.442 70.8188 204.684 70.3247 205.483 69.3349C206.139 68.5351 206.468 67.7337 206.468 66.9339C206.468 65.1923 205.318 64.3198 203.02 64.3198H203.019Z" fill="white"/>
|
||||||
|
<path d="M200.555 81.4836H183.458L182.122 88.4057H199.148C201.022 88.4057 202.36 87.8885 203.159 86.8524C203.44 86.4757 203.674 86.0526 203.861 85.5801C204.049 85.1108 204.143 84.6861 204.143 84.3093C204.143 82.4255 202.946 81.4836 200.555 81.4836Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,4 @@
|
||||||
|
<svg width="237" height="237" viewBox="0 -38 237 237" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M142.434 133.602C206.074 107.046 246.874 59.6588 233.562 27.7592C220.251 -4.14031 157.87 -8.47218 94.2302 18.0837C30.5903 44.6397 -10.2092 92.0272 3.10201 123.927C16.4132 155.826 78.7944 160.158 142.434 133.602Z" fill="#6D6E71"/>
|
||||||
|
<path d="M231.888 71.2796C233.378 70.3016 234.449 69.0914 235.102 67.6474C235.754 66.205 236.08 64.4109 236.08 62.2686C236.08 58.7754 234.752 56.0165 232.097 53.9903C229.443 51.964 225.252 50.9517 219.523 50.9517H160.845L151.869 97.3951L136.257 50.9517H97.6277L72.5801 85.8803L74.8549 74.2821H34.8992L36.0161 68.5534H78.2778L81.6303 50.9501H11.4314L1.86108 100.829H29.8033L32.1092 88.8845H70.4328L61.8667 100.829H92.7428L95.886 96.2895H122.43L123.897 100.829H212.401C221.296 100.829 227.373 98.8277 230.632 94.8226C232.913 92.0751 234.055 88.3497 234.055 83.6464C234.055 80.9921 233.321 78.9201 231.855 77.4303C230.388 75.9404 228.398 74.8447 225.883 74.1464L225.953 73.4481C228.189 73.1226 230.168 72.3998 231.891 71.2828L231.888 71.2796ZM104.825 83.0854L113.556 70.3016H114.045L118.167 83.0854H104.825ZM203.179 85.321C202.992 85.7871 202.76 86.2057 202.48 86.5786C201.689 87.604 200.361 88.1159 198.498 88.1159H181.593L182.921 81.2701H199.895C202.27 81.2701 203.457 82.2023 203.457 84.065C203.457 84.4379 203.364 84.8565 203.177 85.3226L203.179 85.321ZM204.786 69.255C203.993 70.2329 202.76 70.7219 201.084 70.7219H184.948L186.205 64.2948H202.341C204.623 64.2948 205.764 65.1567 205.764 66.8787C205.764 67.6703 205.437 68.4618 204.786 69.2533V69.255Z" fill="white"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
using System.IO;
|
||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
public class Fab : ModuleRules
|
||||||
|
{
|
||||||
|
public Fab(ReadOnlyTargetRules Target) : base(Target)
|
||||||
|
{
|
||||||
|
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||||
|
IWYUSupport = IWYUSupport.None;
|
||||||
|
|
||||||
|
PublicIncludePaths.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
// ... add public include paths required here ...
|
||||||
|
// Path.Combine(ModuleDirectory, "ThirdParty")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateIncludePaths.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
// ... add other private include paths required here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PublicDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"Core",
|
||||||
|
// ... add other public dependencies that you statically link with here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
PrivateDependencyModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
"ApplicationCore",
|
||||||
|
"AssetTools",
|
||||||
|
"BuildPatchServices",
|
||||||
|
"ContentBrowser",
|
||||||
|
"CoreUObject",
|
||||||
|
"DesktopWidgets",
|
||||||
|
"EditorScriptingUtilities",
|
||||||
|
"EditorStyle",
|
||||||
|
"EditorSubsystem",
|
||||||
|
"Engine",
|
||||||
|
"EOSSDK",
|
||||||
|
"EOSShared",
|
||||||
|
"FileUtilities",
|
||||||
|
"Foliage",
|
||||||
|
"GameProjectGeneration",
|
||||||
|
"HTTP",
|
||||||
|
"InputCore",
|
||||||
|
"InterchangeCore",
|
||||||
|
"InterchangeEngine",
|
||||||
|
"InterchangePipelines",
|
||||||
|
"Json",
|
||||||
|
"JsonUtilities",
|
||||||
|
"LevelEditor",
|
||||||
|
"MainFrame",
|
||||||
|
"PlacementMode",
|
||||||
|
"Projects",
|
||||||
|
"RenderCore",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore",
|
||||||
|
"ToolMenus",
|
||||||
|
"ToolWidgets",
|
||||||
|
"UMG",
|
||||||
|
"UnrealEd",
|
||||||
|
"WebBrowser",
|
||||||
|
"InterchangeImport",
|
||||||
|
"InterchangeNodes",
|
||||||
|
"InterchangeFactoryNodes",
|
||||||
|
"DeveloperSettings"
|
||||||
|
// ... add private dependencies that you statically link with here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
DynamicallyLoadedModuleNames.AddRange(
|
||||||
|
new string[]
|
||||||
|
{
|
||||||
|
// ... add any modules that your module loads dynamically here ...
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,415 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabAuthentication.h"
|
||||||
|
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "FabSettings.h"
|
||||||
|
#include "IEOSSDKManager.h"
|
||||||
|
|
||||||
|
#include "Async/Async.h"
|
||||||
|
|
||||||
|
#include "Misc/CommandLine.h"
|
||||||
|
|
||||||
|
namespace FabAuthentication
|
||||||
|
{
|
||||||
|
void Init()
|
||||||
|
{
|
||||||
|
IEOSSDKManager* SDKManager = IEOSSDKManager::Get();
|
||||||
|
if (SDKManager && SDKManager->IsInitialized())
|
||||||
|
{
|
||||||
|
const UFabSettings* FabSettings = GetDefault<UFabSettings>();
|
||||||
|
FString ProductId;
|
||||||
|
FString SandboxId;
|
||||||
|
FString DeploymentId;
|
||||||
|
FString ClientId;
|
||||||
|
FString ClientSecret;
|
||||||
|
FString EncryptionKey;
|
||||||
|
const UEosConstants* const Constants = Cast<UEosConstants>(FSoftObjectPath("/Fab/Data/FabEos.FabEos").TryLoad());
|
||||||
|
switch (FabSettings->Environment)
|
||||||
|
{
|
||||||
|
case EFabEnvironment::Prod:
|
||||||
|
{
|
||||||
|
ProductId = Constants->Prod.ProductId;
|
||||||
|
SandboxId = Constants->Prod.SandboxId;
|
||||||
|
DeploymentId = Constants->Prod.DeploymentId;
|
||||||
|
ClientId = Constants->Prod.ClientCredentialsId;
|
||||||
|
ClientSecret = Constants->Prod.ClientCredentialsSecret;
|
||||||
|
EncryptionKey = Constants->Prod.EncryptionKey;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EFabEnvironment::Gamedev:
|
||||||
|
{
|
||||||
|
ProductId = Constants->GameDev.ProductId;
|
||||||
|
SandboxId = Constants->GameDev.SandboxId;
|
||||||
|
DeploymentId = Constants->GameDev.DeploymentId;
|
||||||
|
ClientId = Constants->GameDev.ClientCredentialsId;
|
||||||
|
ClientSecret = Constants->GameDev.ClientCredentialsSecret;
|
||||||
|
EncryptionKey = Constants->GameDev.EncryptionKey;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
ProductId = Constants->Prod.ProductId;
|
||||||
|
SandboxId = Constants->Prod.SandboxId;
|
||||||
|
DeploymentId = Constants->Prod.DeploymentId;
|
||||||
|
ClientId = Constants->Prod.ClientCredentialsId;
|
||||||
|
ClientSecret = Constants->Prod.ClientCredentialsSecret;
|
||||||
|
EncryptionKey = Constants->Prod.EncryptionKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FTCHARToUTF8 Utf8ProductId(*ProductId);
|
||||||
|
const FTCHARToUTF8 Utf8SandboxId(*SandboxId);
|
||||||
|
const FTCHARToUTF8 Utf8ClientId(*ClientId);
|
||||||
|
const FTCHARToUTF8 Utf8ClientSecret(*ClientSecret);
|
||||||
|
const FTCHARToUTF8 Utf8EncryptionKey(*EncryptionKey);
|
||||||
|
const FTCHARToUTF8 Utf8DeploymentId(*DeploymentId);
|
||||||
|
//const FTCHARToUTF8 Utf8CacheDirectory(*CacheDirectory);
|
||||||
|
|
||||||
|
EOS_Platform_Options PlatformOptions = {};
|
||||||
|
PlatformOptions.ApiVersion = EOS_PLATFORM_OPTIONS_API_LATEST;
|
||||||
|
|
||||||
|
PlatformOptions.ClientCredentials.ClientId = Utf8ClientId.Get();
|
||||||
|
PlatformOptions.ClientCredentials.ClientSecret = Utf8ClientSecret.Get();
|
||||||
|
PlatformOptions.ProductId = Utf8ProductId.Get();
|
||||||
|
PlatformOptions.DeploymentId = Utf8DeploymentId.Get();
|
||||||
|
PlatformOptions.SandboxId = Utf8SandboxId.Get();
|
||||||
|
PlatformOptions.EncryptionKey = Utf8EncryptionKey.Get();
|
||||||
|
// PlatformOptions.CacheDirectory = Utf8CacheDirectory.Get();
|
||||||
|
PlatformOptions.bIsServer = EOS_FALSE;
|
||||||
|
PlatformOptions.Flags = 0;
|
||||||
|
PlatformOptions.Flags |= EOS_PF_DISABLE_OVERLAY;
|
||||||
|
PlatformOptions.Reserved = nullptr;
|
||||||
|
PlatformOptions.TickBudgetInMilliseconds = 0;
|
||||||
|
PlatformOptions.IntegratedPlatformOptionsContainerHandle = nullptr;
|
||||||
|
|
||||||
|
if (FabSettings->Environment == EFabEnvironment::Gamedev)
|
||||||
|
{
|
||||||
|
static struct FReservedOptions
|
||||||
|
{
|
||||||
|
int32_t ApiVersion;
|
||||||
|
const char* BackendEnvironment;
|
||||||
|
}
|
||||||
|
ReservedOptions = {1, "GameDev"};
|
||||||
|
PlatformOptions.Reserved = &ReservedOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlatformHandle = SDKManager->CreatePlatform(PlatformOptions);
|
||||||
|
|
||||||
|
// Sequence of events
|
||||||
|
// 1. Login using persist
|
||||||
|
// 2. If that fails - login using the exchange code
|
||||||
|
LoginUsingPersist();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Shutdown()
|
||||||
|
{
|
||||||
|
PlatformHandle.Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EOS_CALL ExchangeCodeLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data)
|
||||||
|
{
|
||||||
|
if (Data->ResultCode == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
FAB_LOG("User logged in");
|
||||||
|
const int32_t AccountsCount = EOS_Auth_GetLoggedInAccountsCount(AuthHandle);
|
||||||
|
for (int32_t AccountIdx = 0; AccountIdx < AccountsCount; ++AccountIdx)
|
||||||
|
{
|
||||||
|
const EOS_EpicAccountId AccountId = EOS_Auth_GetLoggedInAccountByIndex(AuthHandle, AccountIdx);
|
||||||
|
EOS_ELoginStatus LoginStatus = EOS_Auth_GetLoginStatus(AuthHandle, Data->LocalUserId);
|
||||||
|
EpicAccountId = AccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggedIn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login pin grant code");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_MFARequired)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login MFA required");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_InvalidUser)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Invalid user");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_AccountFeatureRestricted)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login failed, account is restricted");
|
||||||
|
}
|
||||||
|
else if (EOS_EResult_IsOperationComplete(Data->ResultCode))
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EOS_CALL PersistLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data)
|
||||||
|
{
|
||||||
|
if (Data->ResultCode == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
FAB_LOG("User logged in");
|
||||||
|
const int32_t AccountsCount = EOS_Auth_GetLoggedInAccountsCount(AuthHandle);
|
||||||
|
for (int32_t AccountIdx = 0; AccountIdx < AccountsCount; ++AccountIdx)
|
||||||
|
{
|
||||||
|
const EOS_EpicAccountId AccountId = EOS_Auth_GetLoggedInAccountByIndex(AuthHandle, AccountIdx);
|
||||||
|
EOS_ELoginStatus LoginStatus = EOS_Auth_GetLoginStatus(AuthHandle, Data->LocalUserId);
|
||||||
|
EpicAccountId = AccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggedIn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login pin grant code");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_MFARequired)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login MFA required");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_InvalidUser)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Invalid user");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_AccountFeatureRestricted)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login failed, account is restricted");
|
||||||
|
}
|
||||||
|
else if (EOS_EResult_IsOperationComplete(Data->ResultCode))
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback login using exchange code
|
||||||
|
// Sending empty code reads from commandline
|
||||||
|
LoginUsingExchangeCode("");
|
||||||
|
}
|
||||||
|
|
||||||
|
void EOS_CALL AccountPortalLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data)
|
||||||
|
{
|
||||||
|
if (Data->ResultCode == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
FAB_LOG("User logged in");
|
||||||
|
const int32_t AccountsCount = EOS_Auth_GetLoggedInAccountsCount(AuthHandle);
|
||||||
|
for (int32_t AccountIdx = 0; AccountIdx < AccountsCount; ++AccountIdx)
|
||||||
|
{
|
||||||
|
const EOS_EpicAccountId AccountId = EOS_Auth_GetLoggedInAccountByIndex(AuthHandle, AccountIdx);
|
||||||
|
EOS_ELoginStatus LoginStatus = EOS_Auth_GetLoginStatus(AuthHandle, Data->LocalUserId);
|
||||||
|
EpicAccountId = AccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggedIn();
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_PinGrantCode)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login pin grant code");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_MFARequired)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login MFA required");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_InvalidUser)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Invalid user");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_Auth_AccountFeatureRestricted)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Login failed, account is restricted");
|
||||||
|
}
|
||||||
|
else if (EOS_EResult_IsOperationComplete(Data->ResultCode))
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const FString Code = EOS_EResult_ToString(Data->ResultCode);
|
||||||
|
FAB_LOG_ERROR("Login failed - error code: %s", *Code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoginUsingExchangeCode(FString ExchangeCode)
|
||||||
|
{
|
||||||
|
FAB_LOG("Logging in using exchange code");
|
||||||
|
|
||||||
|
if (ExchangeCode.IsEmpty()) // Read exchange code from commandline if it is not passed in
|
||||||
|
{
|
||||||
|
FAB_LOG("Reading exchange code from commandline");
|
||||||
|
FString AuthType;
|
||||||
|
if (FParse::Value(FCommandLine::Get(), TEXT("AUTH_TYPE="), AuthType) && AuthType == TEXT("exchangecode"))
|
||||||
|
{
|
||||||
|
FParse::Value(FCommandLine::Get(), TEXT("AUTH_PASSWORD="), ExchangeCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthHandle = EOS_Platform_GetAuthInterface(*PlatformHandle);
|
||||||
|
|
||||||
|
EOS_Auth_Credentials Credentials = {0};
|
||||||
|
Credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST;
|
||||||
|
|
||||||
|
Credentials.Type = EOS_ELoginCredentialType::EOS_LCT_ExchangeCode;
|
||||||
|
Credentials.Id = "";
|
||||||
|
|
||||||
|
const FTCHARToUTF8 UTF8Token(*ExchangeCode);
|
||||||
|
Credentials.Token = UTF8Token.Get();
|
||||||
|
|
||||||
|
EOS_Auth_LoginOptions LoginOptions = {0};
|
||||||
|
LoginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST;
|
||||||
|
LoginOptions.ScopeFlags = EOS_EAuthScopeFlags::EOS_AS_BasicProfile;
|
||||||
|
LoginOptions.Credentials = &Credentials;
|
||||||
|
|
||||||
|
EOS_Auth_Login(AuthHandle, &LoginOptions, nullptr, ExchangeCodeLoginCompleteCallbackFn);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoginUsingPersist()
|
||||||
|
{
|
||||||
|
FAB_LOG("Logging in using persist");
|
||||||
|
|
||||||
|
AuthHandle = EOS_Platform_GetAuthInterface(*PlatformHandle);
|
||||||
|
|
||||||
|
EOS_Auth_Credentials Credentials = {0};
|
||||||
|
Credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST;
|
||||||
|
|
||||||
|
Credentials.Type = EOS_ELoginCredentialType::EOS_LCT_PersistentAuth;
|
||||||
|
|
||||||
|
EOS_Auth_LoginOptions LoginOptions = {0};
|
||||||
|
LoginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST;
|
||||||
|
LoginOptions.ScopeFlags = EOS_EAuthScopeFlags::EOS_AS_BasicProfile;
|
||||||
|
LoginOptions.Credentials = &Credentials;
|
||||||
|
|
||||||
|
EOS_Auth_Login(AuthHandle, &LoginOptions, nullptr, PersistLoginCompleteCallbackFn);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoginUsingAccountPortal()
|
||||||
|
{
|
||||||
|
FAB_LOG("Logging in using account portal");
|
||||||
|
|
||||||
|
AuthHandle = EOS_Platform_GetAuthInterface(*PlatformHandle);
|
||||||
|
|
||||||
|
EOS_Auth_Credentials Credentials = {0};
|
||||||
|
Credentials.ApiVersion = EOS_AUTH_CREDENTIALS_API_LATEST;
|
||||||
|
|
||||||
|
Credentials.Type = EOS_ELoginCredentialType::EOS_LCT_AccountPortal;
|
||||||
|
|
||||||
|
EOS_Auth_LoginOptions LoginOptions = {0};
|
||||||
|
LoginOptions.ApiVersion = EOS_AUTH_LOGIN_API_LATEST;
|
||||||
|
LoginOptions.ScopeFlags = EOS_EAuthScopeFlags::EOS_AS_BasicProfile;
|
||||||
|
LoginOptions.Credentials = &Credentials;
|
||||||
|
|
||||||
|
EOS_Auth_Login(AuthHandle, &LoginOptions, nullptr, AccountPortalLoginCompleteCallbackFn);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EOS_CALL DeletePersistentAuthCompleteCallbackFn(const EOS_Auth_DeletePersistentAuthCallbackInfo* Data)
|
||||||
|
{
|
||||||
|
if (Data->ResultCode == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
FAB_LOG("Persistent auth deleted");
|
||||||
|
}
|
||||||
|
else if (Data->ResultCode == EOS_EResult::EOS_NotFound)
|
||||||
|
{
|
||||||
|
FAB_LOG("Persistent auth not found - unable to delete");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Unable to delete persistent auth");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintAuthToken(const EOS_Auth_Token* InAuthToken)
|
||||||
|
{
|
||||||
|
FAB_LOG("User client id: %s", *FString(InAuthToken->ClientId));
|
||||||
|
// FAB_LOG("User access token: %s", *FString(InAuthToken->AccessToken));
|
||||||
|
// FAB_LOG("User refresh token: %s", *FString(InAuthToken->RefreshToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoggedIn()
|
||||||
|
{
|
||||||
|
EOS_Auth_Token* UserAuthToken = nullptr;
|
||||||
|
|
||||||
|
EOS_Auth_CopyUserAuthTokenOptions CopyTokenOptions = {0};
|
||||||
|
CopyTokenOptions.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST;
|
||||||
|
|
||||||
|
if (EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions, EpicAccountId, &UserAuthToken) == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
const FString AccessToken = FString(UserAuthToken->AccessToken);
|
||||||
|
PrintAuthToken(UserAuthToken);
|
||||||
|
EOS_Auth_Token_Release(UserAuthToken);
|
||||||
|
FFabBrowser::LoggedIn(AccessToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("User auth token is invalid");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeletePersistentAuth()
|
||||||
|
{
|
||||||
|
FAB_LOG("Delete persist auth");
|
||||||
|
|
||||||
|
EOS_Auth_DeletePersistentAuthOptions Options = {};
|
||||||
|
Options.ApiVersion = EOS_AUTH_DELETEPERSISTENTAUTH_API_LATEST;
|
||||||
|
EOS_Auth_DeletePersistentAuth(AuthHandle, &Options, nullptr, DeletePersistentAuthCompleteCallbackFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString GetAuthToken()
|
||||||
|
{
|
||||||
|
EOS_Auth_Token* UserAuthToken = nullptr;
|
||||||
|
|
||||||
|
EOS_Auth_CopyUserAuthTokenOptions CopyTokenOptions = {0};
|
||||||
|
CopyTokenOptions.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST;
|
||||||
|
|
||||||
|
if (EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions, EpicAccountId, &UserAuthToken) == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
const FString AccessToken = FString(UserAuthToken->AccessToken);
|
||||||
|
EOS_Auth_Token_Release(UserAuthToken);
|
||||||
|
return AccessToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("User auth token is invalid - unable to get auth token");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString GetRefreshToken()
|
||||||
|
{
|
||||||
|
EOS_Auth_Token* UserAuthToken = nullptr;
|
||||||
|
|
||||||
|
EOS_Auth_CopyUserAuthTokenOptions CopyTokenOptions = {0};
|
||||||
|
CopyTokenOptions.ApiVersion = EOS_AUTH_COPYUSERAUTHTOKEN_API_LATEST;
|
||||||
|
|
||||||
|
if (EOS_Auth_CopyUserAuthToken(AuthHandle, &CopyTokenOptions, EpicAccountId, &UserAuthToken) == EOS_EResult::EOS_Success)
|
||||||
|
{
|
||||||
|
const FString RefreshToken = FString(UserAuthToken->RefreshToken);
|
||||||
|
EOS_Auth_Token_Release(UserAuthToken);
|
||||||
|
return RefreshToken;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("User auth token is invalid - unable to get refresh token");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "eos_auth.h"
|
||||||
|
#include "eos_common.h"
|
||||||
|
#include "eos_sdk.h"
|
||||||
|
|
||||||
|
#include "Engine/DataAsset.h"
|
||||||
|
|
||||||
|
#include "FabAuthentication.generated.h"
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FEosConstantsGameDev
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** The product id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ProductId;
|
||||||
|
|
||||||
|
/** The sandbox id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString SandboxId;
|
||||||
|
|
||||||
|
/** The deployment id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString DeploymentId;
|
||||||
|
|
||||||
|
/** Client id of the service permissions entry, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ClientCredentialsId;
|
||||||
|
|
||||||
|
/** Client secret for accessing the set of permissions, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ClientCredentialsSecret;
|
||||||
|
|
||||||
|
/** Game name */
|
||||||
|
UPROPERTY()
|
||||||
|
FString GameName;
|
||||||
|
|
||||||
|
/** Encryption key. */
|
||||||
|
UPROPERTY()
|
||||||
|
FString EncryptionKey;
|
||||||
|
|
||||||
|
/** Product Version. */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ProductVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FEosConstantsProd
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
/** The product id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ProductId;
|
||||||
|
|
||||||
|
/** The sandbox id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString SandboxId;
|
||||||
|
|
||||||
|
/** The deployment id for the running application, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString DeploymentId;
|
||||||
|
|
||||||
|
/** Client id of the service permissions entry, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ClientCredentialsId;
|
||||||
|
|
||||||
|
/** Client secret for accessing the set of permissions, found on the dev portal */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ClientCredentialsSecret;
|
||||||
|
|
||||||
|
/** Game name */
|
||||||
|
UPROPERTY()
|
||||||
|
FString GameName;
|
||||||
|
|
||||||
|
/** Encryption key. */
|
||||||
|
UPROPERTY()
|
||||||
|
FString EncryptionKey;
|
||||||
|
|
||||||
|
/** Product Version. */
|
||||||
|
UPROPERTY()
|
||||||
|
FString ProductVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UEosConstants : public UDataAsset
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY()
|
||||||
|
FEosConstantsGameDev GameDev;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FEosConstantsProd Prod;
|
||||||
|
};
|
||||||
|
|
||||||
|
using IEOSPlatformHandlePtr = TSharedPtr<class IEOSPlatformHandle>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages all user authentication
|
||||||
|
*/
|
||||||
|
namespace FabAuthentication
|
||||||
|
{
|
||||||
|
inline IEOSPlatformHandlePtr PlatformHandle;
|
||||||
|
inline EOS_HAuth AuthHandle;
|
||||||
|
inline EOS_EpicAccountId EpicAccountId;
|
||||||
|
|
||||||
|
void Init();
|
||||||
|
void Shutdown();
|
||||||
|
|
||||||
|
bool LoginUsingExchangeCode(FString ExchangeCode);
|
||||||
|
bool LoginUsingRefreshToken(FString RefreshToken);
|
||||||
|
bool LoginUsingAccountPortal();
|
||||||
|
bool LoginUsingPersist();
|
||||||
|
|
||||||
|
FString GetAuthToken();
|
||||||
|
FString GetRefreshToken();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes any locally stored persistent auth credentials for the currently logged in user of the local device.
|
||||||
|
*/
|
||||||
|
void DeletePersistentAuth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for printing auth token info
|
||||||
|
*/
|
||||||
|
void PrintAuthToken(const EOS_Auth_Token* InAuthToken);
|
||||||
|
|
||||||
|
/** Called when successfully logged in */
|
||||||
|
void LoggedIn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is fired when the login operation completes, either successfully or in error
|
||||||
|
*/
|
||||||
|
static void EOS_CALL ExchangeCodeLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is fired when the login operation completes, either successfully or in error
|
||||||
|
*/
|
||||||
|
static void EOS_CALL AccountPortalLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is fired when the login operation completes, either successfully or in error
|
||||||
|
*/
|
||||||
|
static void EOS_CALL PersistLoginCompleteCallbackFn(const EOS_Auth_LoginCallbackInfo* Data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback that is fired when the delete persistent auth operation completes, either successfully or in error
|
||||||
|
*/
|
||||||
|
static void EOS_CALL DeletePersistentAuthCompleteCallbackFn(const EOS_Auth_DeletePersistentAuthCallbackInfo* Data);
|
||||||
|
}
|
|
@ -0,0 +1,443 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
|
||||||
|
#include "ContentBrowserModule.h"
|
||||||
|
#include "FabBrowserApi.h"
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "FabSettings.h"
|
||||||
|
#include "FabSettingsWindow.h"
|
||||||
|
#include "IWebBrowserPopupFeatures.h"
|
||||||
|
#include "IWebBrowserWindow.h"
|
||||||
|
#include "JsonObjectConverter.h"
|
||||||
|
#include "LevelEditor.h"
|
||||||
|
#include "ToolMenu.h"
|
||||||
|
#include "ToolMenuEntry.h"
|
||||||
|
#include "ToolMenuSection.h"
|
||||||
|
#include "ToolMenus.h"
|
||||||
|
#include "WebBrowserModule.h"
|
||||||
|
|
||||||
|
#include "Framework/Application/SlateApplication.h"
|
||||||
|
#include "Framework/MultiBox/MultiBoxExtender.h"
|
||||||
|
|
||||||
|
#include "HAL/FileManager.h"
|
||||||
|
#include "HAL/PlatformFileManager.h"
|
||||||
|
|
||||||
|
#include "Interfaces/IMainFrameModule.h"
|
||||||
|
#include "Interfaces/IPluginManager.h"
|
||||||
|
|
||||||
|
#include "Math/Vector2D.h"
|
||||||
|
|
||||||
|
#include "Misc/FileHelper.h"
|
||||||
|
#include "Misc/MessageDialog.h"
|
||||||
|
|
||||||
|
#include "Styling/CoreStyle.h"
|
||||||
|
#include "Styling/SlateStyle.h"
|
||||||
|
#include "Styling/SlateStyleRegistry.h"
|
||||||
|
|
||||||
|
#include "UObject/GCObject.h"
|
||||||
|
|
||||||
|
#include "Utilities/FabLocalAssets.h"
|
||||||
|
|
||||||
|
#include "Widgets/Images/SImage.h"
|
||||||
|
#include "Widgets/SBoxPanel.h"
|
||||||
|
#include "Widgets/Docking/SDockTab.h"
|
||||||
|
|
||||||
|
#define LOCTEXT_NAMESPACE "Fab"
|
||||||
|
|
||||||
|
TSharedPtr<SWebBrowser> FFabBrowser::WebBrowserInstance = nullptr;
|
||||||
|
|
||||||
|
TObjectPtr<UFabBrowserApi> FFabBrowser::JavascriptApi = nullptr;
|
||||||
|
|
||||||
|
TSharedPtr<SDockTab> FFabBrowser::DockTab = nullptr;
|
||||||
|
TUniquePtr<FSlateStyleSet> FFabBrowser::SlateStyleSet = nullptr;
|
||||||
|
TSharedPtr<IWebBrowserWindow> FFabBrowser::WebBrowserWindow = nullptr;
|
||||||
|
TObjectPtr<const UFabSettings> FFabBrowser::FabPluginSettings = nullptr;
|
||||||
|
|
||||||
|
const FName FFabBrowser::TabId = TEXT("FabTab");
|
||||||
|
|
||||||
|
const FText FFabBrowser::FabLabel = LOCTEXT("Fab.Label", "Fab");
|
||||||
|
const FText FFabBrowser::FabTooltip = LOCTEXT("Fab.Tooltip", "Get content from Fab");
|
||||||
|
const FName FFabBrowser::FabMenuIconName = TEXT("Fab.MenuIcon");
|
||||||
|
const FName FFabBrowser::FabAssetIconName = TEXT("Fab.AssetIcon");
|
||||||
|
const FName FFabBrowser::FabToolbarIconName = TEXT("Fab.ToolbarIcon");
|
||||||
|
|
||||||
|
void FFabBrowser::Init()
|
||||||
|
{
|
||||||
|
RegisterSlateStyle();
|
||||||
|
RegisterNomadTab();
|
||||||
|
SetupEntryPoints();
|
||||||
|
ExtendContextMenuInContentBrowser();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::ExtendContextMenuInContentBrowser()
|
||||||
|
{
|
||||||
|
FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
|
||||||
|
TArray<FContentBrowserMenuExtender_SelectedAssets>& MenuExtenders = ContentBrowserModule.GetAllAssetViewContextMenuExtenders();
|
||||||
|
FAssetViewExtraStateGenerator StateGenerator(
|
||||||
|
FOnGenerateAssetViewExtraStateIndicators::CreateStatic(&FFabBrowser::OnFabAssetIconGenerate),
|
||||||
|
FOnGenerateAssetViewExtraStateIndicators()
|
||||||
|
);
|
||||||
|
ContentBrowserModule.AddAssetViewExtraStateGenerator(StateGenerator);
|
||||||
|
MenuExtenders.Add(FContentBrowserMenuExtender_SelectedAssets::CreateStatic(&FFabBrowser::OnExtendContentBrowserAssetSelectionMenu));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::RegisterSlateStyle()
|
||||||
|
{
|
||||||
|
SlateStyleSet = MakeUnique<FSlateStyleSet>(TEXT("FabStyle"));
|
||||||
|
SlateStyleSet->SetContentRoot(IPluginManager::Get().FindPlugin(TEXT("Fab"))->GetBaseDir() / TEXT("Resources"));
|
||||||
|
|
||||||
|
const FString IconPath = SlateStyleSet->RootToContentDir(TEXT("FabLogo.svg"));
|
||||||
|
const FString AlternateIconPath = SlateStyleSet->RootToContentDir(TEXT("FabLogoAlternate.svg"));
|
||||||
|
SlateStyleSet->Set(FabMenuIconName, new FSlateVectorImageBrush(IconPath, CoreStyleConstants::Icon16x16));
|
||||||
|
SlateStyleSet->Set(FabAssetIconName, new FSlateVectorImageBrush(AlternateIconPath, CoreStyleConstants::Icon20x20));
|
||||||
|
SlateStyleSet->Set(FabToolbarIconName, new FSlateVectorImageBrush(IconPath, CoreStyleConstants::Icon20x20));
|
||||||
|
|
||||||
|
FSlateStyleRegistry::RegisterSlateStyle(*SlateStyleSet);
|
||||||
|
|
||||||
|
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::SetupEntryPoints()
|
||||||
|
{
|
||||||
|
const FUIAction InvokeTabAction = FUIAction(
|
||||||
|
FExecuteAction::CreateLambda(
|
||||||
|
[LevelEditorModuleName = FName("LevelEditor")]()
|
||||||
|
{
|
||||||
|
FModuleManager::GetModuleChecked<FLevelEditorModule>(LevelEditorModuleName).GetLevelEditorTabManager()->TryInvokeTab(TabId);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
FCanExecuteAction()
|
||||||
|
);
|
||||||
|
|
||||||
|
FToolMenuEntry& ToolMenuEntry = UToolMenus::Get()->ExtendMenu("ContentBrowser.Toolbar")->FindOrAddSection("New").AddEntry(
|
||||||
|
FToolMenuEntry::InitToolBarButton(
|
||||||
|
"OpenFabWindow",
|
||||||
|
InvokeTabAction,
|
||||||
|
FabLabel,
|
||||||
|
FabTooltip,
|
||||||
|
FSlateIcon(SlateStyleSet->GetStyleSetName(), FabToolbarIconName),
|
||||||
|
EUserInterfaceActionType::Button
|
||||||
|
)
|
||||||
|
);
|
||||||
|
ToolMenuEntry.StyleNameOverride = FName("CalloutToolbar");
|
||||||
|
|
||||||
|
UToolMenu* WindowMenu = UToolMenus::Get()->ExtendMenu("MainFrame.MainMenu.Window");
|
||||||
|
|
||||||
|
FToolMenuSection* ContentSectionPtr = WindowMenu->FindSection("GetContent");
|
||||||
|
if (!ContentSectionPtr)
|
||||||
|
{
|
||||||
|
ContentSectionPtr = &WindowMenu->AddSection("GetContent", NSLOCTEXT("MainAppMenu", "GetContentHeader", "Get Content"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentSectionPtr->AddMenuEntry(
|
||||||
|
TEXT("OpenFabTab"),
|
||||||
|
LOCTEXT("OpenFabTab_Label", "Fab"),
|
||||||
|
LOCTEXT("OpenFabTab_Desc", "Opens the Fab Plugin."),
|
||||||
|
FSlateIcon(SlateStyleSet->GetStyleSetName(), FabMenuIconName),
|
||||||
|
InvokeTabAction
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add a Fab entry to the Content Browser's Add popup menu
|
||||||
|
UToolMenus::Get()->ExtendMenu(TEXT("ContentBrowser.AddNewContextMenu"))->AddSection(TEXT("ContentBrowserGetContent"), LOCTEXT("GetContentText", "Get Content")).AddEntry(
|
||||||
|
FToolMenuEntry::InitMenuEntry(TEXT("OpenFabWindow"), FabLabel, FabTooltip, FSlateIcon(SlateStyleSet->GetStyleSetName(), FabMenuIconName), InvokeTabAction)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the context menu to view listings in Fab
|
||||||
|
TSharedRef<FExtender> FFabBrowser::OnExtendContentBrowserAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets)
|
||||||
|
{
|
||||||
|
TSharedRef<FExtender> Extender = MakeShared<FExtender>();
|
||||||
|
|
||||||
|
if (SelectedAssets.Num() != 1)
|
||||||
|
{
|
||||||
|
return Extender;
|
||||||
|
}
|
||||||
|
const FAssetData AssetData = SelectedAssets[0];
|
||||||
|
const FString ObjectPath = AssetData.GetObjectPathString();
|
||||||
|
FString FabListingId;
|
||||||
|
UFabLocalAssets::GetListingID(ObjectPath, FabListingId);
|
||||||
|
|
||||||
|
if (FabListingId.IsEmpty())
|
||||||
|
{
|
||||||
|
return Extender;
|
||||||
|
}
|
||||||
|
|
||||||
|
Extender->AddMenuExtension(
|
||||||
|
"CommonAssetActions",
|
||||||
|
EExtensionHook::After,
|
||||||
|
nullptr,
|
||||||
|
FMenuExtensionDelegate::CreateLambda(
|
||||||
|
[FabListingId](FMenuBuilder& MenuBuilder)
|
||||||
|
{
|
||||||
|
MenuBuilder.AddMenuEntry(
|
||||||
|
FText::FromString("View in Fab"),
|
||||||
|
FText::FromString("View the asset in Fab plugin"),
|
||||||
|
FSlateIcon(SlateStyleSet->GetStyleSetName(), FabMenuIconName),
|
||||||
|
FUIAction(
|
||||||
|
FExecuteAction::CreateLambda(
|
||||||
|
[FabListingId]()
|
||||||
|
{
|
||||||
|
FFabBrowser::OpenURL(GetUrl() / "listings" / FabListingId);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return Extender;
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SWidget> FFabBrowser::OnFabAssetIconGenerate(const FAssetData& AssetData)
|
||||||
|
{
|
||||||
|
const FSlateBrush* FabImage = nullptr;
|
||||||
|
|
||||||
|
const FString ObjectPath = AssetData.GetObjectPathString();
|
||||||
|
FString FabListingId;
|
||||||
|
UFabLocalAssets::GetListingID(ObjectPath, FabListingId);
|
||||||
|
|
||||||
|
if (!FabListingId.IsEmpty())
|
||||||
|
{
|
||||||
|
FabImage = SlateStyleSet->GetBrush(FabAssetIconName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SNew(SBox)
|
||||||
|
.Padding(4.0f, 4.0f, 0.0f, 0.0f)
|
||||||
|
.IsEnabled(FabImage != nullptr)
|
||||||
|
[
|
||||||
|
SNew(SImage)
|
||||||
|
.Image(FabImage)
|
||||||
|
.ToolTipText(FText::FromString("Imported from FAB"))
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::RegisterNomadTab()
|
||||||
|
{
|
||||||
|
auto RegisterSpawner = [](TSharedPtr<ILevelEditor>)
|
||||||
|
{
|
||||||
|
FGlobalTabmanager::Get()->RegisterNomadTabSpawner(TabId, FOnSpawnTab::CreateStatic(OpenTab)).SetAutoGenerateMenuEntry(false).SetDisplayName(FabLabel).
|
||||||
|
SetTooltipTextAttribute(FabTooltip).SetIcon(FSlateIcon(SlateStyleSet->GetStyleSetName(), FabMenuIconName));
|
||||||
|
};
|
||||||
|
|
||||||
|
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||||
|
if (LevelEditorModule.GetLevelEditorInstance().IsValid())
|
||||||
|
{
|
||||||
|
RegisterSpawner(LevelEditorModule.GetLevelEditorInstance().Pin());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LevelEditorModule.OnLevelEditorCreated().AddLambda(RegisterSpawner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FFabBrowser::GetUrl()
|
||||||
|
{
|
||||||
|
const FString ProdUrl = TEXT("https://www.fab.com/plugins/ue5");
|
||||||
|
if (FabPluginSettings == nullptr)
|
||||||
|
{
|
||||||
|
return ProdUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (FabPluginSettings->Environment)
|
||||||
|
{
|
||||||
|
case EFabEnvironment::Prod:
|
||||||
|
{
|
||||||
|
return ProdUrl;
|
||||||
|
}
|
||||||
|
case EFabEnvironment::Gamedev:
|
||||||
|
{
|
||||||
|
return TEXT("https://fab.cceb.dev.use1a.on.epicgames.com/plugins/ue5");
|
||||||
|
}
|
||||||
|
case EFabEnvironment::Test:
|
||||||
|
{
|
||||||
|
return TEXT("https://fab.daec.live.use1a.on.epicgames.com/plugins/ue5");
|
||||||
|
}
|
||||||
|
case EFabEnvironment::CustomUrl:
|
||||||
|
{
|
||||||
|
return FabPluginSettings->CustomUrl;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
return ProdUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TSharedRef<SDockTab> FFabBrowser::OpenTab(const FSpawnTabArgs&)
|
||||||
|
{
|
||||||
|
LogEvent(
|
||||||
|
{
|
||||||
|
"Open Tab",
|
||||||
|
"Plugin",
|
||||||
|
"Click"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
FabPluginSettings = GetDefault<UFabSettings>();
|
||||||
|
|
||||||
|
JavascriptApi = NewObject<UFabBrowserApi>();
|
||||||
|
JavascriptApi->AddToRoot(); //Don't garbage collect
|
||||||
|
|
||||||
|
IWebBrowserModule& WebBrowserModule = IWebBrowserModule::Get();
|
||||||
|
if (!IWebBrowserModule::IsAvailable() || !WebBrowserModule.IsWebModuleAvailable())
|
||||||
|
{
|
||||||
|
FMessageDialog::Open(EAppMsgType::Ok, FText::FromString("Failed to load the plugin. Please enable Web WebBrowserWindow in the plugin manager to use Emporium."));
|
||||||
|
return SNew(SDockTab).TabRole(ETabRole::NomadTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
FCreateBrowserWindowSettings WindowSettings;
|
||||||
|
|
||||||
|
FString PluginPath = IPluginManager::Get().FindPlugin(TEXT("Fab"))->GetBaseDir();
|
||||||
|
FString IndexUrl = FPaths::ConvertRelativePathToFull(FPaths::Combine(PluginPath, TEXT("ThirdParty"), TEXT("index.html")));
|
||||||
|
FString FinalUrl = FPaths::Combine(TEXT("file:///"), IndexUrl);
|
||||||
|
WindowSettings.InitialURL = FinalUrl;
|
||||||
|
WindowSettings.BrowserFrameRate = 60;
|
||||||
|
|
||||||
|
IWebBrowserSingleton* WebBrowserSingleton = WebBrowserModule.GetSingleton();
|
||||||
|
WebBrowserSingleton->SetDevToolsShortcutEnabled(true);
|
||||||
|
|
||||||
|
WebBrowserWindow = WebBrowserSingleton->CreateBrowserWindow(WindowSettings);
|
||||||
|
WebBrowserWindow->OnUnhandledKeyUp().BindLambda([](const FKeyEvent&) { return true; });
|
||||||
|
WebBrowserWindow->OnUnhandledKeyDown().BindLambda([](const FKeyEvent&) { return true; });
|
||||||
|
WebBrowserWindow->OnUrlChanged().AddLambda(
|
||||||
|
[](const FString& Url)
|
||||||
|
{
|
||||||
|
if (FabPluginSettings->Environment != EFabEnvironment::Prod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FString Domain, Protocol;
|
||||||
|
Url.Split("://", &Protocol, &Domain);
|
||||||
|
if (!Protocol.Contains("http"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int32 PathIndex; Domain.FindChar('/', PathIndex))
|
||||||
|
{
|
||||||
|
Domain = Domain.Left(PathIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain = Domain.Replace(TEXT("www."), TEXT(""));
|
||||||
|
|
||||||
|
if (!Domain.Contains("fab.com"))
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Trying to access thirdparty url [%s] in plugin browser. Redirecting back to fab.com", *Url);
|
||||||
|
WebBrowserWindow->LoadURL(GetUrl());
|
||||||
|
FPlatformProcess::LaunchURL(*Url, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (FabPluginSettings->bEnableDebugOptions)
|
||||||
|
{
|
||||||
|
WebBrowserWindow.Get()->OnCreateWindow().BindLambda(
|
||||||
|
[](const TWeakPtr<IWebBrowserWindow>& NewBrowserWindow, const TWeakPtr<IWebBrowserPopupFeatures>& PopupFeatures)
|
||||||
|
{
|
||||||
|
const TSharedRef<SWindow> DialogMainWindow = SNew(SWindow)
|
||||||
|
.ClientSize(FVector2D(700, 700))
|
||||||
|
.SupportsMaximize(true)
|
||||||
|
.SupportsMinimize(true)
|
||||||
|
[
|
||||||
|
SNew(SVerticalBox)
|
||||||
|
+ SVerticalBox::Slot().HAlign(HAlign_Fill).VAlign(VAlign_Fill)
|
||||||
|
[
|
||||||
|
SNew(SWebBrowser, NewBrowserWindow.Pin())
|
||||||
|
]
|
||||||
|
];
|
||||||
|
FSlateApplication::Get().AddWindow(DialogMainWindow);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bShowAddressBar = FabPluginSettings->Environment == EFabEnvironment::CustomUrl;
|
||||||
|
|
||||||
|
SAssignNew(WebBrowserInstance, SWebBrowser, WebBrowserWindow).ShowAddressBar(bShowAddressBar).ShowControls(bShowAddressBar);
|
||||||
|
|
||||||
|
WebBrowserInstance->BindUObject(TEXT("fab"), Cast<UObject>(JavascriptApi), true);
|
||||||
|
WebBrowserWindow->Reload();
|
||||||
|
|
||||||
|
SAssignNew(DockTab, SDockTab).TabRole(ETabRole::NomadTab).OnTabClosed_Lambda(
|
||||||
|
[](TSharedRef<class SDockTab> InParentTab)
|
||||||
|
{
|
||||||
|
WebBrowserInstance->UnbindUObject(TEXT("fab"), Cast<UObject>(JavascriptApi), true);
|
||||||
|
WebBrowserInstance.Reset();
|
||||||
|
WebBrowserWindow.Reset();
|
||||||
|
DockTab.Reset();
|
||||||
|
}
|
||||||
|
)
|
||||||
|
[
|
||||||
|
WebBrowserInstance.ToSharedRef()
|
||||||
|
];
|
||||||
|
|
||||||
|
return DockTab.ToSharedRef();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::ExecuteJavascript(const FString& InSrcScript)
|
||||||
|
{
|
||||||
|
if (WebBrowserInstance == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebBrowserInstance->ExecuteJavascript(InSrcScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::Shutdown()
|
||||||
|
{
|
||||||
|
WebBrowserInstance.Reset();
|
||||||
|
WebBrowserWindow.Reset();
|
||||||
|
DockTab.Reset();
|
||||||
|
FSlateStyleRegistry::UnRegisterSlateStyle(*SlateStyleSet);
|
||||||
|
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(TabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::LoggedIn(const FString& InAccessToken)
|
||||||
|
{
|
||||||
|
ExecuteJavascript(FString::Printf(TEXT("window.ue.fab.onLoginSuccessful('%s');"), *InAccessToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::GetSignedUrl(const FString& AssetId, const int32 Tier)
|
||||||
|
{
|
||||||
|
ExecuteJavascript(FString::Printf(TEXT("window.ue.fab.getSignedUrl('%s', %d)"), *AssetId, Tier));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::LogEvent(const FFabAnalyticsPayload& Payload)
|
||||||
|
{
|
||||||
|
FString JSONPayload;
|
||||||
|
FJsonObjectConverter::UStructToJsonObjectString(Payload, JSONPayload, 0, 0);
|
||||||
|
FAB_LOG("%s", *JSONPayload);
|
||||||
|
|
||||||
|
// ExecuteJavascript(FString::Printf(TEXT("window.ue.fab.onDownloaded()")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::ShowSettings()
|
||||||
|
{
|
||||||
|
const TSharedRef<SWindow> Window = SNew(SWindow)
|
||||||
|
.Title(LOCTEXT("FabSettingsLabel", "Fab Settings"))
|
||||||
|
.ClientSize(FVector2D(600.f, 300.f))
|
||||||
|
.SizingRule(ESizingRule::UserSized);
|
||||||
|
|
||||||
|
TSharedPtr<SFabSettingsWindow> SettingsWindow;
|
||||||
|
Window->SetContent(SAssignNew(SettingsWindow, SFabSettingsWindow).WidgetWindow(Window));
|
||||||
|
TSharedPtr<SWindow> ParentWindow;
|
||||||
|
if (FModuleManager::Get().IsModuleLoaded("MainFrame"))
|
||||||
|
{
|
||||||
|
const IMainFrameModule& MainFrame = FModuleManager::LoadModuleChecked<IMainFrameModule>("MainFrame");
|
||||||
|
ParentWindow = MainFrame.GetParentWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
FSlateApplication::Get().AddModalWindow(Window, ParentWindow, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabBrowser::OpenURL(const FString& InURL)
|
||||||
|
{
|
||||||
|
FModuleManager::GetModuleChecked<FLevelEditorModule>("LevelEditor").GetLevelEditorTabManager()->TryInvokeTab(TabId);
|
||||||
|
if (WebBrowserWindow.Get()->GetUrl() != InURL)
|
||||||
|
{
|
||||||
|
WebBrowserWindow.Get()->LoadURL(InURL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef LOCTEXT_NAMESPACE
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "ContentBrowserDelegates.h"
|
||||||
|
#include "FabBrowser.generated.h"
|
||||||
|
|
||||||
|
class SDockTab;
|
||||||
|
class SWebBrowser;
|
||||||
|
class FSpawnTabArgs;
|
||||||
|
class IWebBrowserWindow;
|
||||||
|
class UFabBrowserApi;
|
||||||
|
class UFabSettings;
|
||||||
|
class FSlateStyleSet;
|
||||||
|
class FExtender;
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FFabAnalyticsPayload
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString InteractionType;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString EventCategory;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString EventAction;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FFabBrowser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static TSharedPtr<SWebBrowser> WebBrowserInstance;
|
||||||
|
static TObjectPtr<UFabBrowserApi> JavascriptApi;
|
||||||
|
static TSharedPtr<SDockTab> DockTab;
|
||||||
|
static TUniquePtr<FSlateStyleSet> SlateStyleSet;
|
||||||
|
static TSharedPtr<IWebBrowserWindow> WebBrowserWindow;
|
||||||
|
static TObjectPtr<const UFabSettings> FabPluginSettings;
|
||||||
|
|
||||||
|
static const FName TabId;
|
||||||
|
static const FText FabLabel;
|
||||||
|
static const FText FabTooltip;
|
||||||
|
static const FName FabMenuIconName;
|
||||||
|
static const FName FabAssetIconName;
|
||||||
|
static const FName FabToolbarIconName;
|
||||||
|
|
||||||
|
static void RegisterSlateStyle();
|
||||||
|
static void RegisterNomadTab();
|
||||||
|
static void ExtendContextMenuInContentBrowser();
|
||||||
|
static void SetupEntryPoints();
|
||||||
|
static void ExecuteJavascript(const FString& InSrcScript);
|
||||||
|
static TSharedRef<SDockTab> OpenTab(const FSpawnTabArgs& InArgs);
|
||||||
|
static TSharedRef<FExtender> OnExtendContentBrowserAssetSelectionMenu(const TArray<FAssetData>& SelectedAssets);
|
||||||
|
static TSharedRef<SWidget> OnFabAssetIconGenerate(const FAssetData& AssetData);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void Init();
|
||||||
|
static void Shutdown();
|
||||||
|
|
||||||
|
static void LogEvent(const FFabAnalyticsPayload& Payload);
|
||||||
|
static void LoggedIn(const FString& InAccessToken);
|
||||||
|
static void GetSignedUrl(const FString& AssetId, const int32 Tier);
|
||||||
|
static TObjectPtr<UFabBrowserApi> GetBrowserApi() { return JavascriptApi; }
|
||||||
|
static void ShowSettings();
|
||||||
|
static void OpenURL(const FString& InURL = GetUrl());
|
||||||
|
static FString GetUrl();
|
||||||
|
};
|
|
@ -0,0 +1,315 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabBrowserApi.h"
|
||||||
|
#include "FabAuthentication.h"
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
#include "FabSettings.h"
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "HAL/PlatformApplicationMisc.h"
|
||||||
|
#include "Interfaces/IPluginManager.h"
|
||||||
|
#include "Misc/EngineVersion.h"
|
||||||
|
#include "Workflows/GenericImportWorkflow.h"
|
||||||
|
#include "Workflows/PackImportWorkflow.h"
|
||||||
|
#include "Workflows/QuixelImportWorkflow.h"
|
||||||
|
#include "Workflows/GenericDragDropWorkflow.h"
|
||||||
|
#include "Workflows/QuixelDragDropWorkflow.h"
|
||||||
|
|
||||||
|
void UFabBrowserApi::CompleteWorkflow(const FString& Id)
|
||||||
|
{
|
||||||
|
TSharedPtr<IFabWorkflow> CompletedWorkflow;
|
||||||
|
for (const TSharedPtr<IFabWorkflow>& ActiveWorkflow : this->ActiveWorkflows)
|
||||||
|
{
|
||||||
|
if (ActiveWorkflow->AssetId == Id)
|
||||||
|
{
|
||||||
|
CompletedWorkflow = ActiveWorkflow;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->ActiveWorkflows.Remove(CompletedWorkflow);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::AddToProject(const FString& DownloadUrl, const FFabAssetMetadata& AssetMetadata)
|
||||||
|
{
|
||||||
|
// Check if the listing is already being downloaded
|
||||||
|
for (const TSharedPtr<IFabWorkflow>& ActiveWorkflow : this->ActiveWorkflows)
|
||||||
|
{
|
||||||
|
if (ActiveWorkflow->AssetId == AssetMetadata.AssetId)
|
||||||
|
{
|
||||||
|
FAB_LOG("The listing with Id %s is already being processed.", *AssetMetadata.AssetId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FAB_LOG("Asset Type = %s", *AssetMetadata.AssetType);
|
||||||
|
FAB_LOG("Is Quixel = %d", AssetMetadata.IsQuixel);
|
||||||
|
|
||||||
|
if (AssetMetadata.AssetType == "unreal-engine")
|
||||||
|
{
|
||||||
|
const FString BaseUrls = FString::Join(AssetMetadata.DistributionPointBaseUrls, TEXT(","));
|
||||||
|
FAB_LOG("Base Url %s", *BaseUrls);
|
||||||
|
|
||||||
|
const TSharedPtr<FPackImportWorkflow> PackImportWorkflow = MakeShared<FPackImportWorkflow>(AssetMetadata.AssetId, AssetMetadata.AssetName, DownloadUrl, BaseUrls);
|
||||||
|
PackImportWorkflow->OnFabWorkflowComplete().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
PackImportWorkflow->OnFabWorkflowCancel().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
PackImportWorkflow->Execute();
|
||||||
|
this->ActiveWorkflows.Add(PackImportWorkflow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AssetMetadata.IsQuixel)
|
||||||
|
{
|
||||||
|
const TSharedPtr<FQuixelImportWorkflow> QuixelImportWorkflow = MakeShared<FQuixelImportWorkflow>(AssetMetadata.AssetId, AssetMetadata.AssetName, DownloadUrl);
|
||||||
|
QuixelImportWorkflow->Execute();
|
||||||
|
QuixelImportWorkflow->OnFabWorkflowComplete().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
QuixelImportWorkflow->OnFabWorkflowCancel().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this->ActiveWorkflows.Add(QuixelImportWorkflow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AssetMetadata.AssetType == "gltf" || AssetMetadata.AssetType == "glb" || AssetMetadata.AssetType == "fbx")
|
||||||
|
{
|
||||||
|
const TSharedPtr<FGenericImportWorkflow> InterchangeImportWorkflow = MakeShared<FGenericImportWorkflow>(AssetMetadata.AssetId, AssetMetadata.AssetName, DownloadUrl);
|
||||||
|
InterchangeImportWorkflow->OnFabWorkflowComplete().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
InterchangeImportWorkflow->OnFabWorkflowCancel().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
InterchangeImportWorkflow->Execute();
|
||||||
|
this->ActiveWorkflows.Add(InterchangeImportWorkflow);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAB_LOG_ERROR("Asset type not handled %s", *AssetMetadata.AssetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::DragStart(const FFabAssetMetadata& AssetMetadata)
|
||||||
|
{
|
||||||
|
// Check if the listing is already being downloaded
|
||||||
|
for (const TSharedPtr<IFabWorkflow>& ActiveWorkflow : this->ActiveWorkflows)
|
||||||
|
{
|
||||||
|
if (ActiveWorkflow->AssetId == AssetMetadata.AssetId)
|
||||||
|
{
|
||||||
|
FAB_LOG("The listing with Id %s is already being processed.", *AssetMetadata.AssetId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FAB_LOG("Listing Type = %s", *AssetMetadata.ListingType);
|
||||||
|
FAB_LOG("Is Quixel = %d", AssetMetadata.IsQuixel);
|
||||||
|
if (AssetMetadata.IsQuixel)
|
||||||
|
{
|
||||||
|
const TSharedPtr<FQuixelDragDropWorkflow> QuixelDragDropWorkflow = MakeShared<FQuixelDragDropWorkflow>(
|
||||||
|
AssetMetadata.AssetId,
|
||||||
|
AssetMetadata.AssetName,
|
||||||
|
AssetMetadata.ListingType
|
||||||
|
);
|
||||||
|
QuixelDragDropWorkflow->OnFabWorkflowComplete().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
FAB_LOG("Quixel Drag workflow completed!");
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
QuixelDragDropWorkflow->OnFabWorkflowCancel().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
FAB_LOG("Quixel Drag workflow cancelled!");
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this->ActiveWorkflows.Add(QuixelDragDropWorkflow);
|
||||||
|
QuixelDragDropWorkflow->Execute();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const TSharedPtr<FGenericDragDropWorkflow> DragDropWorkflow = MakeShared<FGenericDragDropWorkflow>(AssetMetadata.AssetId, AssetMetadata.AssetName);
|
||||||
|
DragDropWorkflow->OnFabWorkflowComplete().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
FAB_LOG("Drag workflow completed!");
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
DragDropWorkflow->OnFabWorkflowCancel().BindLambda(
|
||||||
|
[this, AssetId = AssetMetadata.AssetId]()
|
||||||
|
{
|
||||||
|
FAB_LOG("Drag workflow cancelled!");
|
||||||
|
CompleteWorkflow(AssetId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
this->ActiveWorkflows.Add(DragDropWorkflow);
|
||||||
|
DragDropWorkflow->Execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::OnDragInfoSuccess(const FString& DownloadUrl, const FFabAssetMetadata& AssetMetadata)
|
||||||
|
{
|
||||||
|
OnSignedUrlGenerated().Broadcast(DownloadUrl, AssetMetadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::OnDragInfoFailure(const FString& AssetId)
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Drag drop failure for asset id %s", *AssetId);
|
||||||
|
// Get the drag workflow
|
||||||
|
FFabAssetMetadata Metadata;
|
||||||
|
Metadata.AssetId = AssetId;
|
||||||
|
OnSignedUrlGenerated().Broadcast("", Metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
FDelegateHandle UFabBrowserApi::AddSignedUrlCallback(TFunction<void(const FString&, const FFabAssetMetadata&)> Callback)
|
||||||
|
{
|
||||||
|
return OnSignedUrlGenerated().AddLambda(Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::Login()
|
||||||
|
{
|
||||||
|
FabAuthentication::LoginUsingAccountPortal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::Logout()
|
||||||
|
{
|
||||||
|
FabAuthentication::DeletePersistentAuth();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::OpenPluginSettings()
|
||||||
|
{
|
||||||
|
FAB_LOG("Open plugin settings");
|
||||||
|
FFabBrowser::ShowSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
FFabFrontendSettings UFabBrowserApi::GetSettings()
|
||||||
|
{
|
||||||
|
const UFabSettings* FabSettings = GetDefault<UFabSettings>();
|
||||||
|
|
||||||
|
FFabFrontendSettings FrontendSettings;
|
||||||
|
if (FabSettings->PreferredDefaultFormat == EFabPreferredFormats::GLTF)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredFormat = "gltf";
|
||||||
|
}
|
||||||
|
else if (FabSettings->PreferredDefaultFormat == EFabPreferredFormats::FBX)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredFormat = "fbx";
|
||||||
|
}
|
||||||
|
if (FabSettings->PreferredQualityTier == EFabPreferredQualityTier::Low)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredQuality = "low";
|
||||||
|
}
|
||||||
|
else if (FabSettings->PreferredQualityTier == EFabPreferredQualityTier::Medium)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredQuality = "medium";
|
||||||
|
}
|
||||||
|
else if (FabSettings->PreferredQualityTier == EFabPreferredQualityTier::High)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredQuality = "high";
|
||||||
|
}
|
||||||
|
else if (FabSettings->PreferredQualityTier == EFabPreferredQualityTier::Raw)
|
||||||
|
{
|
||||||
|
FrontendSettings.PreferredQuality = "raw";
|
||||||
|
}
|
||||||
|
|
||||||
|
return FrontendSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::SetPreferredQualityTier(const FString& PreferredQuality)
|
||||||
|
{
|
||||||
|
UFabSettings* FabSettings = GetMutableDefault<UFabSettings>();
|
||||||
|
if (PreferredQuality == "low")
|
||||||
|
{
|
||||||
|
FabSettings->PreferredQualityTier = EFabPreferredQualityTier::Low;
|
||||||
|
}
|
||||||
|
else if (PreferredQuality == "medium")
|
||||||
|
{
|
||||||
|
FabSettings->PreferredQualityTier = EFabPreferredQualityTier::Medium;
|
||||||
|
}
|
||||||
|
else if (PreferredQuality == "high")
|
||||||
|
{
|
||||||
|
FabSettings->PreferredQualityTier = EFabPreferredQualityTier::High;
|
||||||
|
}
|
||||||
|
else if (PreferredQuality == "raw")
|
||||||
|
{
|
||||||
|
FabSettings->PreferredQualityTier = EFabPreferredQualityTier::Raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
FabSettings->SaveConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
FFabApiVersion UFabBrowserApi::GetApiVersion()
|
||||||
|
{
|
||||||
|
FFabApiVersion ApiVersion;
|
||||||
|
const FEngineVersion EngineVersion = FEngineVersion::Current();
|
||||||
|
const uint16 MajorVersion = EngineVersion.GetMajor();
|
||||||
|
const uint16 MinorVersion = EngineVersion.GetMinor();
|
||||||
|
|
||||||
|
ApiVersion.Ue = FString::FromInt(MajorVersion) + "." + FString::FromInt(MinorVersion);
|
||||||
|
ApiVersion.Api = "1.0.0";
|
||||||
|
|
||||||
|
if (const TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin("Fab"); Plugin.IsValid())
|
||||||
|
{
|
||||||
|
const FPluginDescriptor& PluginDescriptor = Plugin->GetDescriptor();
|
||||||
|
|
||||||
|
const FString PluginVersion = PluginDescriptor.VersionName;
|
||||||
|
ApiVersion.PluginVersion = PluginVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::OpenUrlInBrowser(const FString& Url)
|
||||||
|
{
|
||||||
|
FPlatformProcess::LaunchURL(*Url, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabBrowserApi::CopyToClipboard(const FString& Content)
|
||||||
|
{
|
||||||
|
FPlatformApplicationMisc::ClipboardCopy(*Content);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UFabBrowserApi::GetUrl()
|
||||||
|
{
|
||||||
|
return FFabBrowser::GetUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UFabBrowserApi::GetAuthToken()
|
||||||
|
{
|
||||||
|
const UFabSettings* FabSettings = GetDefault<UFabSettings>();
|
||||||
|
if (!FabSettings->CustomAuthToken.IsEmpty())
|
||||||
|
{
|
||||||
|
FAB_LOG("Returning custom auth token: %s", *FabSettings->CustomAuthToken);
|
||||||
|
return FabSettings->CustomAuthToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FabAuthentication::GetAuthToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
FString UFabBrowserApi::GetRefreshToken()
|
||||||
|
{
|
||||||
|
return FabAuthentication::GetRefreshToken();
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
|
||||||
|
#include "FabBrowserApi.generated.h"
|
||||||
|
|
||||||
|
class IFabWorkflow;
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FFabAssetMetadata
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString AssetId;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString AssetName;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString AssetType;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString ListingType;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
bool IsQuixel = false;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString AssetNamespace;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
TArray<FString> DistributionPointBaseUrls;
|
||||||
|
};
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FFabApiVersion
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Ue;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString Api;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString PluginVersion;
|
||||||
|
};
|
||||||
|
|
||||||
|
USTRUCT()
|
||||||
|
struct FFabFrontendSettings
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString PreferredFormat;
|
||||||
|
|
||||||
|
UPROPERTY()
|
||||||
|
FString PreferredQuality;
|
||||||
|
};
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UFabBrowserApi : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnSignedUrlGenerated, const FString& /*DownloadUrl*/, FFabAssetMetadata /*Metadata*/);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FOnSignedUrlGenerated OnSignedUrlGeneratedDelegate;
|
||||||
|
void CompleteWorkflow(const FString& Id);
|
||||||
|
|
||||||
|
public:
|
||||||
|
TArray<TSharedPtr<IFabWorkflow>> ActiveWorkflows;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFUNCTION()
|
||||||
|
void AddToProject(const FString& DownloadUrl, const FFabAssetMetadata& AssetMetadata);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void DragStart(const FFabAssetMetadata& AssetMetadata);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnDragInfoSuccess(const FString& DownloadUrl, const FFabAssetMetadata& AssetMetadata);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OnDragInfoFailure(const FString& AssetId);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void Login();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void Logout();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
FString GetAuthToken();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
FString GetRefreshToken();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OpenPluginSettings();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
FFabFrontendSettings GetSettings();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void SetPreferredQualityTier(const FString& PreferredQuality);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
FFabApiVersion GetApiVersion();
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void OpenUrlInBrowser(const FString& Url);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
void CopyToClipboard(const FString& Content);
|
||||||
|
|
||||||
|
UFUNCTION()
|
||||||
|
FString GetUrl();
|
||||||
|
|
||||||
|
FDelegateHandle AddSignedUrlCallback(TFunction<void(const FString&, const FFabAssetMetadata&)> Callback);
|
||||||
|
FOnSignedUrlGenerated& OnSignedUrlGenerated() { return OnSignedUrlGeneratedDelegate; }
|
||||||
|
void RemoveSignedUrlHandle(const FDelegateHandle& Handle) { OnSignedUrlGeneratedDelegate.Remove(Handle); }
|
||||||
|
};
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabAuthentication.h"
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "FabSettings.h"
|
||||||
|
|
||||||
|
#include "HAL/IConsoleManager.h"
|
||||||
|
#include "Utilities/FabAssetsCache.h"
|
||||||
|
|
||||||
|
static FAutoConsoleCommand ConsoleCmd_FabShowSettings(
|
||||||
|
TEXT("Fab.ShowSettings"),
|
||||||
|
TEXT("Display the Fab settings window"),
|
||||||
|
FConsoleCommandDelegate::CreateStatic(&FFabBrowser::ShowSettings)
|
||||||
|
);
|
||||||
|
|
||||||
|
static FAutoConsoleCommand ConsoleCmd_FabLogout(
|
||||||
|
TEXT("Fab.Logout"),
|
||||||
|
TEXT("Trigger a manual logout for Fab plugin"),
|
||||||
|
FConsoleCommandDelegate::CreateLambda([]()
|
||||||
|
{
|
||||||
|
FabAuthentication::DeletePersistentAuth();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
static FAutoConsoleCommand ConsoleCmd_FabLogin(
|
||||||
|
TEXT("Fab.Login"),
|
||||||
|
TEXT("Trigger a manual login for Fab plugin"),
|
||||||
|
FConsoleCommandDelegate::CreateLambda([]()
|
||||||
|
{
|
||||||
|
FabAuthentication::LoginUsingAccountPortal();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
static FAutoConsoleCommand ConsoleCmd_FabClearCache(
|
||||||
|
TEXT("Fab.ClearCache"),
|
||||||
|
TEXT("Clear download cache for Fab plugin"),
|
||||||
|
FConsoleCommandDelegate::CreateStatic(&FFabAssetsCache::ClearCache)
|
||||||
|
);
|
||||||
|
|
||||||
|
static FAutoConsoleCommand ConsoleCmd_FabSetEnvironment(
|
||||||
|
TEXT("Fab.SetEnvironment"),
|
||||||
|
TEXT("Set Fab plugin environment"),
|
||||||
|
FConsoleCommandWithArgsDelegate::CreateLambda([](const TArray<FString>& Args)
|
||||||
|
{
|
||||||
|
if (Args.IsEmpty())
|
||||||
|
{
|
||||||
|
FAB_LOG("Need to provide a valid environment arg");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FString Environment = Args[0];
|
||||||
|
UFabSettings* FabSettings = GetMutableDefault<UFabSettings>();
|
||||||
|
if (Environment == "prod")
|
||||||
|
{
|
||||||
|
FabSettings->Environment = EFabEnvironment::Prod;
|
||||||
|
}
|
||||||
|
else if (Environment == "gamedev")
|
||||||
|
{
|
||||||
|
FabSettings->Environment = EFabEnvironment::Gamedev;
|
||||||
|
}
|
||||||
|
else if (Environment == "test")
|
||||||
|
{
|
||||||
|
FabSettings->Environment = EFabEnvironment::Test;
|
||||||
|
}
|
||||||
|
|
||||||
|
FabAuthentication::DeletePersistentAuth();
|
||||||
|
FabSettings->SaveConfig();
|
||||||
|
})
|
||||||
|
);
|
|
@ -0,0 +1,350 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabDownloader.h"
|
||||||
|
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "HttpModule.h"
|
||||||
|
|
||||||
|
#include "Importers/BuildPatchInstallerLibHelper.h"
|
||||||
|
|
||||||
|
#include "Interfaces/IHttpResponse.h"
|
||||||
|
#include "Interfaces/IPluginManager.h"
|
||||||
|
|
||||||
|
#include "Misc/DateTime.h"
|
||||||
|
#include "Misc/Timespan.h"
|
||||||
|
#include "Misc/Paths.h"
|
||||||
|
#include "Misc/FileHelper.h"
|
||||||
|
|
||||||
|
#include "Runtime/Launch/Resources/Version.h"
|
||||||
|
|
||||||
|
#include "Utilities/FabAssetsCache.h"
|
||||||
|
|
||||||
|
TUniquePtr<BpiLib::IBpiLib> FFabDownloadRequest::BuildPatchServices;
|
||||||
|
FTSTicker::FDelegateHandle FFabDownloadRequest::BpsTickerHandle;
|
||||||
|
|
||||||
|
FFabDownloadRequest::FFabDownloadRequest(const FString& InAssetID, const FString& InDownloadURL, const FString& InDownloadLocation, EFabDownloadType InDownloadType)
|
||||||
|
: AssetID(InAssetID)
|
||||||
|
, DownloadURL(InDownloadURL)
|
||||||
|
, DownloadLocation(InDownloadLocation)
|
||||||
|
, DownloadType(InDownloadType)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool FFabDownloadRequest::LoadBuildPatchServices()
|
||||||
|
{
|
||||||
|
if (BuildPatchServices)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto WinLibName = TEXT("BuildPatchInstallerLib.dll");
|
||||||
|
constexpr auto LinuxLibName = TEXT("libBuildPatchInstallerLib.so");
|
||||||
|
constexpr auto MacArmLibName = TEXT("BuildPatchInstallerLib-arm.dylib");
|
||||||
|
constexpr auto Macx86LibName = TEXT("BuildPatchInstallerLib-x86.dylib");
|
||||||
|
constexpr auto DLLName = PLATFORM_WINDOWS ? WinLibName : PLATFORM_LINUX ? LinuxLibName : PLATFORM_MAC_ARM64 ? MacArmLibName : PLATFORM_MAC_X86 ? Macx86LibName : nullptr;
|
||||||
|
if constexpr (DLLName == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FString PluginPath = IPluginManager::Get().FindPlugin(TEXT("Fab"))->GetBaseDir();
|
||||||
|
const FString LibPath = FPaths::ConvertRelativePathToFull(FPaths::Combine(PluginPath, TEXT("ThirdParty"), DLLName));
|
||||||
|
|
||||||
|
BuildPatchServices = BpiLib::FBpiLibHelperFactory::Create(LibPath);
|
||||||
|
if (BuildPatchServices)
|
||||||
|
{
|
||||||
|
BpsTickerHandle = FTSTicker::GetCoreTicker().AddTicker(
|
||||||
|
FTickerDelegate::CreateLambda(
|
||||||
|
[](const float Delta)
|
||||||
|
{
|
||||||
|
BuildPatchServices->Tick(Delta);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BuildPatchServices != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::ShutdownBpsModule()
|
||||||
|
{
|
||||||
|
if (BpsTickerHandle.IsValid())
|
||||||
|
{
|
||||||
|
FTSTicker::GetCoreTicker().RemoveTicker(BpsTickerHandle);
|
||||||
|
}
|
||||||
|
if (BuildPatchServices.IsValid())
|
||||||
|
{
|
||||||
|
BuildPatchServices.Reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::ExecuteHTTPRequest()
|
||||||
|
{
|
||||||
|
const FString FullFileName = GetFilenameFromURL(DownloadURL);
|
||||||
|
const FString SaveFilename = DownloadLocation / GetFilenameFromURL(DownloadURL);
|
||||||
|
|
||||||
|
FHttpModule& HTTPModule = FHttpModule::Get();
|
||||||
|
|
||||||
|
DownloadRequest = HTTPModule.CreateRequest().ToSharedPtr();
|
||||||
|
DownloadRequest->SetURL(DownloadURL);
|
||||||
|
DownloadRequest->OnHeaderReceived().BindLambda(
|
||||||
|
[this, FullFileName](FHttpRequestPtr Request, const FString& HeaderName, const FString& HeaderValue)
|
||||||
|
{
|
||||||
|
if (HeaderName == "Content-Length")
|
||||||
|
{
|
||||||
|
DownloadStats.TotalBytes = FCString::Atoi(*HeaderValue);
|
||||||
|
const FString CachedAssetId = AssetID / FullFileName;
|
||||||
|
OnDownloadProgressDelegate.Broadcast(this, DownloadStats);
|
||||||
|
|
||||||
|
if (FFabAssetsCache::IsCached(CachedAssetId, DownloadStats.TotalBytes))
|
||||||
|
{
|
||||||
|
DownloadStats.PercentComplete = 100.0f;
|
||||||
|
DownloadStats.DownloadCompletedAt = FDateTime::Now().ToUnixTimestamp();
|
||||||
|
DownloadStats.bIsSuccess = true;
|
||||||
|
DownloadStats.CompletedBytes = DownloadStats.TotalBytes;
|
||||||
|
DownloadStats.DownloadedFiles = {
|
||||||
|
FFabAssetsCache::GetCachedFile(CachedAssetId)
|
||||||
|
};
|
||||||
|
|
||||||
|
Request->CancelRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
#if (ENGINE_MAJOR_VERSION >=5 && ENGINE_MINOR_VERSION <=3)
|
||||||
|
DownloadRequest->OnRequestProgress().BindLambda(
|
||||||
|
[this](FHttpRequestPtr Request, uint32 UploadedBytes, uint32 DownloadedBytes)
|
||||||
|
#else
|
||||||
|
DownloadRequest->OnRequestProgress64().BindLambda(
|
||||||
|
[this](FHttpRequestPtr Request, uint64 UploadedBytes, uint64 DownloadedBytes)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
DownloadStats.CompletedBytes = static_cast<uint64>(DownloadedBytes);
|
||||||
|
DownloadStats.PercentComplete = 100.0f * static_cast<float>(DownloadStats.CompletedBytes) / static_cast<float>(DownloadStats.TotalBytes);
|
||||||
|
|
||||||
|
// TODO: Calculate Download Speed
|
||||||
|
OnDownloadProgressDelegate.Broadcast(this, DownloadStats);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
DownloadRequest->OnProcessRequestComplete().BindLambda(
|
||||||
|
[this, SaveFilename](FHttpRequestPtr Request, FHttpResponsePtr Response, const bool bRequestComplete)
|
||||||
|
{
|
||||||
|
if (bRequestComplete)
|
||||||
|
{
|
||||||
|
const TArray<uint8>& Data = Response->GetContent();
|
||||||
|
DownloadStats.bIsSuccess = FFileHelper::SaveArrayToFile(Data, *SaveFilename);
|
||||||
|
DownloadStats.DownloadCompletedAt = FDateTime::Now().ToUnixTimestamp();
|
||||||
|
if (DownloadStats.bIsSuccess)
|
||||||
|
{
|
||||||
|
DownloadStats.DownloadedFiles = {
|
||||||
|
SaveFilename
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
DownloadStats.DownloadStartedAt = FDateTime::Now().ToUnixTimestamp();
|
||||||
|
DownloadRequest->ProcessRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::ExecuteBuildPatchRequest()
|
||||||
|
{
|
||||||
|
DownloadStats.DownloadStartedAt = FDateTime::Now().ToUnixTimestamp();
|
||||||
|
if (!LoadBuildPatchServices())
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Failed to load BuildPatchServicesModule");
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FString ManifestURL, BaseURL;
|
||||||
|
DownloadURL.Split(",", &ManifestURL, &BaseURL, ESearchCase::CaseSensitive);
|
||||||
|
FHttpModule& HTTPModule = FHttpModule::Get();
|
||||||
|
|
||||||
|
DownloadRequest = HTTPModule.CreateRequest().ToSharedPtr();
|
||||||
|
DownloadRequest->SetURL(ManifestURL);
|
||||||
|
DownloadRequest->OnProcessRequestComplete().BindLambda(
|
||||||
|
[this, BaseURL](FHttpRequestPtr Request, FHttpResponsePtr Response, const bool bRequestComplete)
|
||||||
|
{
|
||||||
|
if (bRequestComplete)
|
||||||
|
{
|
||||||
|
ManifestData = Response->GetContent();
|
||||||
|
OnManifestDownloaded(BaseURL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
DownloadRequest->ProcessRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::OnManifestDownloaded(const FString& BaseURL)
|
||||||
|
{
|
||||||
|
if (bPendingCancel)
|
||||||
|
{
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildPatchServices::FBuildInstallerConfiguration BuildInstallerConfiguration({});
|
||||||
|
BuildInstallerConfiguration.InstallDirectory = DownloadLocation;
|
||||||
|
BuildInstallerConfiguration.StagingDirectory = FFabAssetsCache::GetCacheLocation() / AssetID;
|
||||||
|
BuildInstallerConfiguration.InstallMode = BuildPatchServices::EInstallMode::NonDestructiveInstall;
|
||||||
|
BaseURL.ParseIntoArray(BuildInstallerConfiguration.CloudDirectories, TEXT(","));
|
||||||
|
|
||||||
|
auto Manifest = BuildPatchServices->MakeManifestFromData(ManifestData);
|
||||||
|
if (!Manifest.IsValid())
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Invalid Manifest");
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DownloadStats.DownloadedFiles = Manifest.GetBuildFileList();
|
||||||
|
if (DownloadStats.DownloadedFiles.ContainsByPredicate(
|
||||||
|
[](const FString& File)
|
||||||
|
{
|
||||||
|
const FString Ext = FPaths::GetExtension(File);
|
||||||
|
return Ext == "uproject" || Ext == "uplugin";
|
||||||
|
}
|
||||||
|
))
|
||||||
|
{
|
||||||
|
FAB_LOG_ERROR("Invalid pack - either contains a uproject or a uplugin file");
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
DownloadStats.DownloadedFiles.Empty();
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto OnComplete = FBuildPatchInstallerDelegate::CreateLambda(
|
||||||
|
[this](const IBuildInstallerRef& Installer)
|
||||||
|
{
|
||||||
|
DownloadStats.DownloadCompletedAt = FDateTime::Now().ToUnixTimestamp();
|
||||||
|
DownloadStats.PercentComplete = 100.0f;
|
||||||
|
DownloadStats.bIsSuccess = true;
|
||||||
|
if (BpsProgressTickerHandle.IsValid())
|
||||||
|
{
|
||||||
|
FTSTicker::GetCoreTicker().RemoveTicker(BpsProgressTickerHandle);
|
||||||
|
}
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
BpsInstaller = BuildPatchServices->CreateInstaller(Manifest, MoveTemp(BuildInstallerConfiguration), MoveTemp(OnComplete)).ToSharedPtr();
|
||||||
|
BpsInstaller->StartInstallation();
|
||||||
|
|
||||||
|
auto OnProgress = FTickerDelegate::CreateLambda(
|
||||||
|
[this, Installer = BpsInstaller.ToSharedRef()](const float Delta)
|
||||||
|
{
|
||||||
|
const int64 TotalDownloaded = BuildPatchServices->GetTotalDownloaded(Installer);
|
||||||
|
// const float UpdateProgress = BuildPatchServices->GetUpdateProgress(Installer);
|
||||||
|
const int64 TotalDownloadRequired = BuildPatchServices->GetTotalDownloadRequired(Installer);
|
||||||
|
DownloadStats.CompletedBytes = TotalDownloaded;
|
||||||
|
DownloadStats.TotalBytes = TotalDownloadRequired;
|
||||||
|
DownloadStats.PercentComplete = (static_cast<float>(DownloadStats.CompletedBytes) / DownloadStats.TotalBytes) * 100.0f;
|
||||||
|
|
||||||
|
|
||||||
|
OnDownloadProgressDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
BpsProgressTickerHandle = FTSTicker::GetCoreTicker().AddTicker(MoveTemp(OnProgress), 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FString FFabDownloadRequest::GetFilenameFromURL(const FString& URL)
|
||||||
|
{
|
||||||
|
int32 SlashIndex = -1;
|
||||||
|
if (!URL.FindLastChar('/', SlashIndex) || SlashIndex == -1)
|
||||||
|
{
|
||||||
|
SlashIndex = 0; // Local file without scheme maybe?
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 QuestionIndex = -1;
|
||||||
|
if (!URL.FindChar('?', QuestionIndex) || QuestionIndex == -1)
|
||||||
|
{
|
||||||
|
QuestionIndex = URL.Len();
|
||||||
|
}
|
||||||
|
return URL.Mid(SlashIndex + 1, QuestionIndex - 1 - SlashIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::StartDownload()
|
||||||
|
{
|
||||||
|
if (bPendingCancel)
|
||||||
|
{
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
OnDownloadCompleteDelegate.Broadcast(this, DownloadStats);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (DownloadType == EFabDownloadType::HTTP)
|
||||||
|
{
|
||||||
|
ExecuteHTTPRequest();
|
||||||
|
}
|
||||||
|
else if (DownloadType == EFabDownloadType::BuildPatchRequest)
|
||||||
|
{
|
||||||
|
ExecuteBuildPatchRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::ExecuteRequest()
|
||||||
|
{
|
||||||
|
FFabDownloadQueue::AddDownloadToQueue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabDownloadRequest::Cancel()
|
||||||
|
{
|
||||||
|
bool bWasCancelled = false;
|
||||||
|
if (DownloadRequest.IsValid() && DownloadRequest->GetStatus() == EHttpRequestStatus::Processing)
|
||||||
|
{
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
DownloadStats.DownloadedFiles.Empty();
|
||||||
|
DownloadRequest->CancelRequest();
|
||||||
|
bWasCancelled = true;
|
||||||
|
}
|
||||||
|
if (BpsInstaller.IsValid() && !BpsInstaller->IsComplete() && !BpsInstaller->IsCanceled())
|
||||||
|
{
|
||||||
|
DownloadStats.bIsSuccess = false;
|
||||||
|
DownloadStats.DownloadedFiles.Empty();
|
||||||
|
BuildPatchServices->CancelInstall(BpsInstaller.ToSharedRef());
|
||||||
|
bWasCancelled = true;
|
||||||
|
}
|
||||||
|
if (!bWasCancelled)
|
||||||
|
{
|
||||||
|
bPendingCancel = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 FFabDownloadQueue::DownloadQueueLimit = 2;
|
||||||
|
TSet<FFabDownloadRequest*> FFabDownloadQueue::DownloadQueue;
|
||||||
|
TQueue<FFabDownloadRequest*> FFabDownloadQueue::WaitingQueue;
|
||||||
|
|
||||||
|
void FFabDownloadQueue::AddDownloadToQueue(FFabDownloadRequest* DownloadRequest)
|
||||||
|
{
|
||||||
|
if (DownloadQueue.Num() >= DownloadQueueLimit)
|
||||||
|
{
|
||||||
|
WaitingQueue.Enqueue(DownloadRequest);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DownloadQueue.Add(DownloadRequest);
|
||||||
|
DownloadRequest->OnDownloadComplete().AddLambda(
|
||||||
|
[DownloadRequest](const FFabDownloadRequest* Request, const FFabDownloadStats& Stats)
|
||||||
|
{
|
||||||
|
DownloadQueue.Remove(DownloadRequest);
|
||||||
|
if (FFabDownloadRequest* NewRequest = nullptr; WaitingQueue.Dequeue(NewRequest))
|
||||||
|
{
|
||||||
|
AddDownloadToQueue(NewRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
DownloadRequest->StartDownload();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Containers/Queue.h"
|
||||||
|
|
||||||
|
#include "Containers/Ticker.h"
|
||||||
|
|
||||||
|
#include "Importers/BuildPatchInstallerLibHelper.h"
|
||||||
|
|
||||||
|
#include "Interfaces/IHttpRequest.h"
|
||||||
|
|
||||||
|
enum class EFabDownloadType
|
||||||
|
{
|
||||||
|
// Download asset using HTTP
|
||||||
|
HTTP,
|
||||||
|
|
||||||
|
// Download asset using BuildPatchServices (for Unreal Engine Marketplace Assets)
|
||||||
|
BuildPatchRequest
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FFabDownloadStats
|
||||||
|
{
|
||||||
|
float PercentComplete = 0.0f;
|
||||||
|
|
||||||
|
uint64 CompletedBytes = 0;
|
||||||
|
uint64 TotalBytes = 0;
|
||||||
|
|
||||||
|
uint64 DownloadStartedAt = 0;
|
||||||
|
uint64 DownloadCompletedAt = 0;
|
||||||
|
|
||||||
|
float DownloadSpeed = 0.0f;
|
||||||
|
|
||||||
|
bool bIsSuccess = false;
|
||||||
|
|
||||||
|
TArray<FString> DownloadedFiles;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FFabDownloadRequest
|
||||||
|
{
|
||||||
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDownloadProgress, const FFabDownloadRequest*, const FFabDownloadStats&);
|
||||||
|
DECLARE_MULTICAST_DELEGATE_TwoParams(FOnDownloadComplete, const FFabDownloadRequest*, const FFabDownloadStats&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
FString GetFilenameFromURL(const FString& URL);
|
||||||
|
|
||||||
|
void ExecuteHTTPRequest();
|
||||||
|
|
||||||
|
static bool LoadBuildPatchServices();
|
||||||
|
void ExecuteBuildPatchRequest();
|
||||||
|
void OnManifestDownloaded(const FString& BaseURL);
|
||||||
|
|
||||||
|
void StartDownload();
|
||||||
|
|
||||||
|
public:
|
||||||
|
FFabDownloadRequest(const FString& AssetID, const FString& InDownloadURL, const FString& InDownloadLocation, EFabDownloadType InDownloadType = EFabDownloadType::HTTP);
|
||||||
|
|
||||||
|
~FFabDownloadRequest() = default;
|
||||||
|
|
||||||
|
void ExecuteRequest();
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
static void ShutdownBpsModule();
|
||||||
|
|
||||||
|
const FFabDownloadStats& GetDownloadStats() { return DownloadStats; }
|
||||||
|
|
||||||
|
FOnDownloadProgress& OnDownloadProgress() { return OnDownloadProgressDelegate; }
|
||||||
|
FOnDownloadComplete& OnDownloadComplete() { return OnDownloadCompleteDelegate; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FString AssetID;
|
||||||
|
|
||||||
|
FString DownloadURL;
|
||||||
|
|
||||||
|
FString DownloadLocation;
|
||||||
|
|
||||||
|
EFabDownloadType DownloadType;
|
||||||
|
|
||||||
|
FFabDownloadStats DownloadStats;
|
||||||
|
|
||||||
|
FOnDownloadProgress OnDownloadProgressDelegate;
|
||||||
|
FOnDownloadComplete OnDownloadCompleteDelegate;
|
||||||
|
|
||||||
|
FHttpRequestPtr DownloadRequest;
|
||||||
|
IBuildInstallerPtr BpsInstaller;
|
||||||
|
|
||||||
|
bool bPendingCancel = false;
|
||||||
|
|
||||||
|
TArray<uint8> ManifestData;
|
||||||
|
FTSTicker::FDelegateHandle BpsProgressTickerHandle;
|
||||||
|
static FTSTicker::FDelegateHandle BpsTickerHandle;
|
||||||
|
static TUniquePtr<BpiLib::IBpiLib> BuildPatchServices;
|
||||||
|
|
||||||
|
friend class FFabDownloadQueue;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FFabDownloadQueue
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
static int32 DownloadQueueLimit;
|
||||||
|
static TSet<FFabDownloadRequest*> DownloadQueue;
|
||||||
|
static TQueue<FFabDownloadRequest*> WaitingQueue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void AddDownloadToQueue(FFabDownloadRequest* DownloadRequest);
|
||||||
|
};
|
|
@ -0,0 +1,11 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Logging/LogMacros.h"
|
||||||
|
|
||||||
|
DECLARE_LOG_CATEGORY_EXTERN(LogFab, Log, All);
|
||||||
|
|
||||||
|
#define FAB_LOG(Format, ...) UE_LOG(LogFab, Display, TEXT(Format), ##__VA_ARGS__)
|
||||||
|
#define FAB_LOG_ERROR(Format, ...) UE_LOG(LogFab, Error, TEXT(Format), ##__VA_ARGS__)
|
||||||
|
#define FAB_LOG_VERBOSE(Format, ...) UE_LOG(LogFab, Display, TEXT(Format), ##__VA_ARGS__)
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabModule.h"
|
||||||
|
|
||||||
|
#include "Engine.h"
|
||||||
|
|
||||||
|
#include "FabAuthentication.h"
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
#include "FabDownloader.h"
|
||||||
|
#include "FabLog.h"
|
||||||
|
#include "FabSettingsCustomization.h"
|
||||||
|
#include "InterchangeManager.h"
|
||||||
|
|
||||||
|
#include "PropertyEditorModule.h"
|
||||||
|
#include "Engine/RendererSettings.h"
|
||||||
|
#include "Modules/ModuleManager.h"
|
||||||
|
|
||||||
|
#include "Pipelines/Factories/InterchangeInstancedFoliageTypeFactory.h"
|
||||||
|
|
||||||
|
#include "Runtime/Launch/Resources/Version.h"
|
||||||
|
|
||||||
|
#if (ENGINE_MAJOR_VERSION >=5 && ENGINE_MINOR_VERSION <=3)
|
||||||
|
|
||||||
|
#include "Settings/EditorExperimentalSettings.h"
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY(LogFab)
|
||||||
|
|
||||||
|
class FFabModule : public IFabModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void StartupModule() override
|
||||||
|
{
|
||||||
|
if (GIsEditor)
|
||||||
|
{
|
||||||
|
URendererSettings* RendererSettings = GetMutableDefault<URendererSettings>();
|
||||||
|
RendererSettings->bEnableVirtualTextureOpacityMask = true;
|
||||||
|
RendererSettings->PostEditChange();
|
||||||
|
|
||||||
|
#if (ENGINE_MAJOR_VERSION >=5 && ENGINE_MINOR_VERSION <=3)
|
||||||
|
{
|
||||||
|
UEditorExperimentalSettings* EditorSettings = GetMutableDefault<UEditorExperimentalSettings>();
|
||||||
|
EditorSettings->bEnableAsyncTextureCompilation = false;
|
||||||
|
EditorSettings->PostEditChange();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GIsEditor && !IsRunningCommandlet())
|
||||||
|
{
|
||||||
|
FFabBrowser::Init();
|
||||||
|
FabAuthentication::Init();
|
||||||
|
|
||||||
|
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||||
|
PropertyModule.RegisterCustomClassLayout("FabSettings", FOnGetDetailCustomizationInstance::CreateStatic(&FFabSettingsCustomization::MakeInstance));
|
||||||
|
|
||||||
|
auto RegisterItems = []()
|
||||||
|
{
|
||||||
|
UInterchangeManager::GetInterchangeManager().RegisterFactory(UInterchangeInstancedFoliageTypeFactory::StaticClass());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (GEngine)
|
||||||
|
{
|
||||||
|
RegisterItems();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FCoreDelegates::OnPostEngineInit.AddLambda(RegisterItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void ShutdownModule() override
|
||||||
|
{
|
||||||
|
if (GIsEditor && !IsRunningCommandlet())
|
||||||
|
{
|
||||||
|
if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
|
||||||
|
{
|
||||||
|
FPropertyEditorModule& PropertyModule = FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||||
|
PropertyModule.UnregisterCustomClassLayout("FabSettings");
|
||||||
|
}
|
||||||
|
FabAuthentication::Shutdown();
|
||||||
|
FFabBrowser::Shutdown();
|
||||||
|
FFabDownloadRequest::ShutdownBpsModule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMPLEMENT_MODULE(FFabModule, Fab);
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabSettings.h"
|
||||||
|
|
||||||
|
#include "FabAuthentication.h"
|
||||||
|
#include "FabBrowser.h"
|
||||||
|
#include "Misc/Paths.h"
|
||||||
|
#include "UObject/UnrealType.h"
|
||||||
|
|
||||||
|
UFabSettings::UFabSettings()
|
||||||
|
{
|
||||||
|
#if WITH_EDITOR
|
||||||
|
for (TFieldIterator<FProperty> It(GetClass()); It; ++It)
|
||||||
|
{
|
||||||
|
FProperty* Property = *It;
|
||||||
|
if (Property && Property->GetMetaData(TEXT("DevOnly")).ToBool())
|
||||||
|
{
|
||||||
|
Property->SetMetaData(TEXT("Category"), TEXT("HiddenProperties"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void UFabSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||||
|
{
|
||||||
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
||||||
|
if (PropertyChangedEvent.ChangeType != EPropertyChangeType::Interactive)
|
||||||
|
{
|
||||||
|
if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFabSettings, CacheDirectoryPath))
|
||||||
|
{
|
||||||
|
if (!FPaths::DirectoryExists(CacheDirectoryPath.Path) || FPaths::IsRelative(CacheDirectoryPath.Path))
|
||||||
|
{
|
||||||
|
CacheDirectoryPath = FDirectoryPath { FPlatformProcess::UserTempDir() / FString("FabLibrary") };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveConfig();
|
||||||
|
|
||||||
|
if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFabSettings, Environment))
|
||||||
|
{
|
||||||
|
FabAuthentication::DeletePersistentAuth();
|
||||||
|
FabAuthentication::Init();
|
||||||
|
if (Environment != EFabEnvironment::CustomUrl)
|
||||||
|
{
|
||||||
|
FFabBrowser::OpenURL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (PropertyChangedEvent.GetMemberPropertyName() == GET_MEMBER_NAME_CHECKED(UFabSettings, CustomUrl))
|
||||||
|
{
|
||||||
|
if (Environment == EFabEnvironment::CustomUrl)
|
||||||
|
{
|
||||||
|
FFabBrowser::OpenURL();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "HAL/PlatformProcess.h"
|
||||||
|
#include "Misc/Paths.h"
|
||||||
|
#include "UObject/SoftObjectPath.h"
|
||||||
|
|
||||||
|
#include "FabSettings.generated.h"
|
||||||
|
|
||||||
|
UENUM()
|
||||||
|
enum class EFabEnvironment : uint8
|
||||||
|
{
|
||||||
|
Prod UMETA(DisplayName = "Prod"),
|
||||||
|
Gamedev UMETA(DisplayName = "Gamedev"),
|
||||||
|
Test UMETA(DisplayName = "Test"),
|
||||||
|
CustomUrl UMETA(DisplayName = "Custom URL"),
|
||||||
|
};
|
||||||
|
|
||||||
|
UENUM()
|
||||||
|
enum class EFabPreferredFormats : uint8
|
||||||
|
{
|
||||||
|
GLTF UMETA(DisplayName = "gltf / glb"),
|
||||||
|
FBX UMETA(DisplayName = "fbx"),
|
||||||
|
};
|
||||||
|
|
||||||
|
UENUM()
|
||||||
|
enum class EFabPreferredQualityTier : uint8
|
||||||
|
{
|
||||||
|
Low UMETA(DisplayName = "low"),
|
||||||
|
Medium UMETA(DisplayName = "medium"),
|
||||||
|
High UMETA(DisplayName = "high"),
|
||||||
|
Raw UMETA(DisplayName = "raw")
|
||||||
|
};
|
||||||
|
|
||||||
|
UCLASS(config=EditorPerProjectUserSettings, hideCategories=HiddenProperties)
|
||||||
|
class UFabSettings : public UObject
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UFabSettings();
|
||||||
|
|
||||||
|
/** Frontend used by the Fab plugin (reopen the tab to see the change) */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category=Frontend, meta=(DevOnly=true))
|
||||||
|
EFabEnvironment Environment = EFabEnvironment::Prod;
|
||||||
|
|
||||||
|
/** URL used when the [Fab (custom)] frontend is selected */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category=Frontend, meta=(DevOnly=true))
|
||||||
|
FString CustomUrl;
|
||||||
|
|
||||||
|
/** Custom auth token used when it's non empty */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category=Frontend, meta=(DevOnly=true))
|
||||||
|
FString CustomAuthToken;
|
||||||
|
|
||||||
|
/** Enable chrome debug options - default is false */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category = General)
|
||||||
|
bool bEnableDebugOptions = false;
|
||||||
|
|
||||||
|
/** Path to the local library */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category = General)
|
||||||
|
FDirectoryPath CacheDirectoryPath { FPlatformProcess::UserTempDir() / FString("FabLibrary") };
|
||||||
|
|
||||||
|
/** Cache directory */
|
||||||
|
UPROPERTY(config, VisibleAnywhere, Category = General)
|
||||||
|
FString CacheDirectorySize;
|
||||||
|
|
||||||
|
/** Preferred default format */
|
||||||
|
/* The preferred format will always be selected, if not available, the best available format for the product will be chosen. */
|
||||||
|
UPROPERTY(config, VisibleAnywhere, Category = ProductFormats, meta=(DevOnly=true))
|
||||||
|
FString ProductFormatsSectionSubText = "";
|
||||||
|
|
||||||
|
/** Preferred default format */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category = ProductFormats, meta=(DevOnly=true))
|
||||||
|
EFabPreferredFormats PreferredDefaultFormat = EFabPreferredFormats::GLTF;
|
||||||
|
|
||||||
|
/** Preferred default quality for MS assets */
|
||||||
|
UPROPERTY(config, EditAnywhere, Category = Megascans)
|
||||||
|
EFabPreferredQualityTier PreferredQualityTier = EFabPreferredQualityTier::Medium;
|
||||||
|
|
||||||
|
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||||
|
};
|
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#include "FabSettingsCustomization.h"
|
||||||
|
|
||||||
|
#include "DetailCategoryBuilder.h"
|
||||||
|
#include "DetailLayoutBuilder.h"
|
||||||
|
#include "FabSettings.h"
|
||||||
|
#include "DetailWidgetRow.h"
|
||||||
|
|
||||||
|
#include "Widgets/Input/SButton.h"
|
||||||
|
#include "Widgets/Text/STextBlock.h"
|
||||||
|
#include "Widgets/Input/SEditableTextBox.h"
|
||||||
|
#include "Widgets/Layout/SBox.h"
|
||||||
|
|
||||||
|
#include "PropertyEditing.h"
|
||||||
|
|
||||||
|
#include "Utilities/FabAssetsCache.h"
|
||||||
|
|
||||||
|
TSharedRef<IDetailCustomization> FFabSettingsCustomization::MakeInstance()
|
||||||
|
{
|
||||||
|
return MakeShareable(new FFabSettingsCustomization);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFabSettingsCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||||
|
{
|
||||||
|
IDetailCategoryBuilder& GeneralCategory = DetailBuilder.EditCategory("General");
|
||||||
|
IDetailCategoryBuilder& FormatsCategory = DetailBuilder.EditCategory("ProductFormats");
|
||||||
|
|
||||||
|
// General section modifications
|
||||||
|
|
||||||
|
DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UFabSettings, CacheDirectorySize));
|
||||||
|
TArray<TSharedRef<IPropertyHandle>> GeneralProperties;
|
||||||
|
GeneralCategory.GetDefaultProperties(GeneralProperties);
|
||||||
|
for (const TSharedRef<IPropertyHandle>& PropertyHandle : GeneralProperties)
|
||||||
|
{
|
||||||
|
// Skip the already customized NonEditableString property
|
||||||
|
if (PropertyHandle->GetProperty()->GetFName() == GET_MEMBER_NAME_CHECKED(UFabSettings, CacheDirectorySize))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the property
|
||||||
|
GeneralCategory.AddProperty(PropertyHandle);
|
||||||
|
}
|
||||||
|
|
||||||
|
const TSharedRef<IPropertyHandle> CacheSizeStringHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFabSettings, CacheDirectorySize));
|
||||||
|
GeneralCategory.AddCustomRow(CacheSizeStringHandle->GetPropertyDisplayName())
|
||||||
|
.NameContent()
|
||||||
|
[
|
||||||
|
CacheSizeStringHandle->CreatePropertyNameWidget()
|
||||||
|
]
|
||||||
|
.ValueContent()
|
||||||
|
[
|
||||||
|
SNew(SBox)
|
||||||
|
.MinDesiredWidth(1400.f)
|
||||||
|
.HAlign(HAlign_Fill)
|
||||||
|
[
|
||||||
|
SNew(SOverlay)
|
||||||
|
.FlowDirectionPreference(EFlowDirectionPreference::LeftToRight)
|
||||||
|
|
||||||
|
+ SOverlay::Slot()
|
||||||
|
.HAlign(HAlign_Left)
|
||||||
|
[
|
||||||
|
SNew(SEditableTextBox)
|
||||||
|
.Text_Static(&FFabAssetsCache::GetCacheSizeString)
|
||||||
|
.IsReadOnly(true)
|
||||||
|
]
|
||||||
|
|
||||||
|
+ SOverlay::Slot()
|
||||||
|
.HAlign(HAlign_Right)
|
||||||
|
[
|
||||||
|
SNew(SButton).Text(FText::FromString("Clean Directory"))
|
||||||
|
.OnClicked(FOnClicked::CreateRaw(this, &FFabSettingsCustomization::OnButtonClick))
|
||||||
|
]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Product format section modifications
|
||||||
|
|
||||||
|
// DetailBuilder.HideProperty(GET_MEMBER_NAME_CHECKED(UFabSettings, ProductFormatsSectionSubText));
|
||||||
|
// const TSharedRef<IPropertyHandle> ProductFormatSubTextHandle = DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UFabSettings, ProductFormatsSectionSubText));
|
||||||
|
// FormatsCategory.AddCustomRow(ProductFormatSubTextHandle->GetPropertyDisplayName())
|
||||||
|
// .WholeRowContent()
|
||||||
|
// [
|
||||||
|
// SNew(SHorizontalBox)
|
||||||
|
// + SHorizontalBox::Slot()
|
||||||
|
// .HAlign(HAlign_Fill)
|
||||||
|
// .FillWidth(1.0f)
|
||||||
|
// [
|
||||||
|
// SNew(STextBlock)
|
||||||
|
// .Text(FText::FromString("The preferred format will always be selected, if not available, the best available format for the product will be chosen."))
|
||||||
|
// ]
|
||||||
|
// ]
|
||||||
|
// .ValueContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
FReply FFabSettingsCustomization::OnButtonClick()
|
||||||
|
{
|
||||||
|
FFabAssetsCache::ClearCache();
|
||||||
|
return FReply::Handled();
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Input/Reply.h"
|
||||||
|
#include "IDetailCustomization.h"
|
||||||
|
|
||||||
|
class FFabSettingsCustomization : public IDetailCustomization
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||||
|
|
||||||
|
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
FReply OnButtonClick();
|
||||||
|
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue