From d5310c3455f9849243b7b950deb4e910aa1f24dd Mon Sep 17 00:00:00 2001 From: ivey Date: Mon, 15 Jun 2020 15:54:16 -0400 Subject: Initial commit of the UGCExample Project --- Build/Scripts/EnablePluginByDefault.cs | 37 +++ Build/Scripts/PackageSimpleUGCPlugin.cs | 129 +++++++++ Build/Scripts/Properties/AssemblyInfo.cs | 36 +++ Build/Scripts/SimpleUGC.Automation.csproj | 79 +++++ Config/DefaultEditor.ini | 0 Config/DefaultEngine.ini | 19 ++ Config/DefaultGame.ini | 12 + Config/DefaultInput.ini | 95 ++++++ .../GameFramework/UGCExampleGameMode.uasset | Bin 0 -> 35198 bytes .../Blueprints/GameFramework/UGCExamplePawn.uasset | Bin 0 -> 21760 bytes .../Blueprints/GameObjects/CubeActor.uasset | Bin 0 -> 89947 bytes .../Blueprints/GameObjects/SphereActor.uasset | Bin 0 -> 95529 bytes Content/UGCExampleContent/Maps/ExampleMap.umap | Bin 0 -> 57936 bytes Content/UGCExampleContent/Maps/MainMenu.umap | Bin 0 -> 41252 bytes Content/UGCExampleContent/UI/ListHeader.uasset | Bin 0 -> 21655 bytes Content/UGCExampleContent/UI/ModMenuWidget.uasset | Bin 0 -> 155238 bytes .../UI/UGCActorReplacementsHeader.uasset | Bin 0 -> 44084 bytes Content/UGCExampleContent/UI/UGCClassWidget.uasset | Bin 0 -> 38550 bytes .../UGCExampleContent/UI/UGCClassesHeader.uasset | Bin 0 -> 21395 bytes .../UI/UGCClearOverridesWidget.uasset | Bin 0 -> 41924 bytes Content/UGCExampleContent/UI/UGCMapWidget.uasset | Bin 0 -> 49470 bytes Content/UGCExampleContent/UI/UGCMapsHeader.uasset | Bin 0 -> 21355 bytes .../UI/UGCOverrideActorToReplaceWidget.uasset | Bin 0 -> 21523 bytes .../UGCExampleContent/UI/UGCOverrideWidget.uasset | Bin 0 -> 71496 bytes .../UGCExampleContent/UI/UGCPackageHeader.uasset | Bin 0 -> 36668 bytes .../UGCExampleContent/UI/UGCPackageWidget.uasset | Bin 0 -> 173548 bytes .../UI/UGCRegisteredOverrideWidget.uasset | Bin 0 -> 51777 bytes Plugins/SimpleUGC/Resources/ButtonIcon_40x.png | Bin 0 -> 9212 bytes Plugins/SimpleUGC/Resources/CreateUGC_128x.png | Bin 0 -> 9780 bytes Plugins/SimpleUGC/Resources/CreateUGC_16x.png | Bin 0 -> 2149 bytes Plugins/SimpleUGC/Resources/CreateUGC_48x.png | Bin 0 -> 3266 bytes Plugins/SimpleUGC/Resources/CreateUGC_64x.png | Bin 0 -> 4567 bytes Plugins/SimpleUGC/Resources/Icon128.png | Bin 0 -> 13381 bytes Plugins/SimpleUGC/Resources/PackageUGC_128x.png | Bin 0 -> 6324 bytes Plugins/SimpleUGC/Resources/PackageUGC_16x.png | Bin 0 -> 2055 bytes Plugins/SimpleUGC/Resources/PackageUGC_48x.png | Bin 0 -> 2163 bytes Plugins/SimpleUGC/Resources/PackageUGC_64x.png | Bin 0 -> 2915 bytes Plugins/SimpleUGC/SimpleUGC.uplugin | 34 +++ .../Private/MakeReplaceableActorComponent.cpp | 9 + .../Private/ReplacementActorComponent.cpp | 9 + .../Source/SimpleUGC/Private/SimpleUGC.cpp | 8 + .../SimpleUGC/Private/UGCBaseGameInstance.cpp | 11 + .../SimpleUGC/Private/UGCBlueprintLibrary.cpp | 13 + .../Source/SimpleUGC/Private/UGCRegistry.cpp | 241 ++++++++++++++++ .../Private/UGCRegistry.cpp~RF2b8431a.TMP | 262 +++++++++++++++++ .../Private/UGCRegistry.cpp~RF2bf9f51.TMP | 319 +++++++++++++++++++++ .../Public/MakeReplaceableActorComponent.h | 22 ++ .../SimpleUGC/Public/ReplacementActorComponent.h | 21 ++ .../SimpleUGC/Source/SimpleUGC/Public/SimpleUGC.h | 10 + .../Source/SimpleUGC/Public/UGCBaseGameInstance.h | 25 ++ .../Source/SimpleUGC/Public/UGCBlueprintLibrary.h | 20 ++ .../Source/SimpleUGC/Public/UGCRegistry.h | 98 +++++++ .../SimpleUGC/Public/UGCRegistry.h~RF2b28773.TMP | 106 +++++++ .../SimpleUGC/Source/SimpleUGC/SimpleUGC.Build.cs | 59 ++++ .../SimpleUGCEditor/Private/SimpleUGCCreator.cpp | 65 +++++ .../SimpleUGCEditor/Private/SimpleUGCEditor.cpp | 130 +++++++++ .../Private/SimpleUGCEditorCommands.cpp | 51 ++++ .../Private/SimpleUGCEditorStyle.cpp | 72 +++++ .../SimpleUGCEditor/Private/SimpleUGCPackager.cpp | 205 +++++++++++++ .../Private/SimpleUGCPluginWizardDefinition.cpp | 214 ++++++++++++++ .../SimpleUGCEditor/Public/SimpleUGCCreator.h | 33 +++ .../SimpleUGCEditor/Public/SimpleUGCEditor.h | 41 +++ .../Public/SimpleUGCEditorCommands.h | 27 ++ .../SimpleUGCEditor/Public/SimpleUGCEditorStyle.h | 31 ++ .../SimpleUGCEditor/Public/SimpleUGCPackager.h | 49 ++++ .../Public/SimpleUGCPluginWizardDefinition.h | 70 +++++ .../SimpleUGCEditor/SimpleUGCEditor.Build.cs | 59 ++++ .../Templates/BaseTemplate/Resources/Icon128.png | Bin 0 -> 13381 bytes Releases/UGCExampleGame_v1/PackagedExe.zip | Bin 0 -> 82505665 bytes .../WindowsNoEditor/AssetRegistry.bin | Bin 0 -> 114085 bytes .../Metadata/BulkDataInfo.ubulkmanifest | Bin 0 -> 3449 bytes .../WindowsNoEditor/Metadata/CookedIniVersion.txt | Bin 0 -> 884834 bytes .../WindowsNoEditor/Metadata/Crypto.json | 1 + .../Metadata/DevelopmentAssetRegistry.bin | Bin 0 -> 133224 bytes .../ShaderArchive-Global-PCD3D_SM5.ushaderbytecode | Bin 0 -> 5304993 bytes ...derArchive-UGCExample-PCD3D_SM5.ushaderbytecode | Bin 0 -> 2182056 bytes Source/UGCExample.Target.cs | 14 + Source/UGCExample/UGCExample.Build.cs | 23 ++ Source/UGCExample/UGCExample.cpp | 6 + Source/UGCExample/UGCExample.h | 6 + Source/UGCExampleEditor.Target.cs | 14 + UGCExample.uproject | 13 + 82 files changed, 2798 insertions(+) create mode 100644 Build/Scripts/EnablePluginByDefault.cs create mode 100644 Build/Scripts/PackageSimpleUGCPlugin.cs create mode 100644 Build/Scripts/Properties/AssemblyInfo.cs create mode 100644 Build/Scripts/SimpleUGC.Automation.csproj create mode 100644 Config/DefaultEditor.ini create mode 100644 Config/DefaultEngine.ini create mode 100644 Config/DefaultGame.ini create mode 100644 Config/DefaultInput.ini create mode 100644 Content/UGCExampleContent/Blueprints/GameFramework/UGCExampleGameMode.uasset create mode 100644 Content/UGCExampleContent/Blueprints/GameFramework/UGCExamplePawn.uasset create mode 100644 Content/UGCExampleContent/Blueprints/GameObjects/CubeActor.uasset create mode 100644 Content/UGCExampleContent/Blueprints/GameObjects/SphereActor.uasset create mode 100644 Content/UGCExampleContent/Maps/ExampleMap.umap create mode 100644 Content/UGCExampleContent/Maps/MainMenu.umap create mode 100644 Content/UGCExampleContent/UI/ListHeader.uasset create mode 100644 Content/UGCExampleContent/UI/ModMenuWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCActorReplacementsHeader.uasset create mode 100644 Content/UGCExampleContent/UI/UGCClassWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCClassesHeader.uasset create mode 100644 Content/UGCExampleContent/UI/UGCClearOverridesWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCMapWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCMapsHeader.uasset create mode 100644 Content/UGCExampleContent/UI/UGCOverrideActorToReplaceWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCOverrideWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCPackageHeader.uasset create mode 100644 Content/UGCExampleContent/UI/UGCPackageWidget.uasset create mode 100644 Content/UGCExampleContent/UI/UGCRegisteredOverrideWidget.uasset create mode 100644 Plugins/SimpleUGC/Resources/ButtonIcon_40x.png create mode 100644 Plugins/SimpleUGC/Resources/CreateUGC_128x.png create mode 100644 Plugins/SimpleUGC/Resources/CreateUGC_16x.png create mode 100644 Plugins/SimpleUGC/Resources/CreateUGC_48x.png create mode 100644 Plugins/SimpleUGC/Resources/CreateUGC_64x.png create mode 100644 Plugins/SimpleUGC/Resources/Icon128.png create mode 100644 Plugins/SimpleUGC/Resources/PackageUGC_128x.png create mode 100644 Plugins/SimpleUGC/Resources/PackageUGC_16x.png create mode 100644 Plugins/SimpleUGC/Resources/PackageUGC_48x.png create mode 100644 Plugins/SimpleUGC/Resources/PackageUGC_64x.png create mode 100644 Plugins/SimpleUGC/SimpleUGC.uplugin create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/MakeReplaceableActorComponent.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/ReplacementActorComponent.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/SimpleUGC.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBaseGameInstance.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBlueprintLibrary.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2b8431a.TMP create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2bf9f51.TMP create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/MakeReplaceableActorComponent.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/ReplacementActorComponent.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/SimpleUGC.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBaseGameInstance.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBlueprintLibrary.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h~RF2b28773.TMP create mode 100644 Plugins/SimpleUGC/Source/SimpleUGC/SimpleUGC.Build.cs create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCCreator.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditor.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorCommands.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorStyle.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPackager.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPluginWizardDefinition.cpp create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCCreator.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditor.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorCommands.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorStyle.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPackager.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPluginWizardDefinition.h create mode 100644 Plugins/SimpleUGC/Source/SimpleUGCEditor/SimpleUGCEditor.Build.cs create mode 100644 Plugins/SimpleUGC/Templates/BaseTemplate/Resources/Icon128.png create mode 100644 Releases/UGCExampleGame_v1/PackagedExe.zip create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/AssetRegistry.bin create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/BulkDataInfo.ubulkmanifest create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/CookedIniVersion.txt create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/Crypto.json create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/DevelopmentAssetRegistry.bin create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-Global-PCD3D_SM5.ushaderbytecode create mode 100644 Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-UGCExample-PCD3D_SM5.ushaderbytecode create mode 100644 Source/UGCExample.Target.cs create mode 100644 Source/UGCExample/UGCExample.Build.cs create mode 100644 Source/UGCExample/UGCExample.cpp create mode 100644 Source/UGCExample/UGCExample.h create mode 100644 Source/UGCExampleEditor.Target.cs create mode 100644 UGCExample.uproject diff --git a/Build/Scripts/EnablePluginByDefault.cs b/Build/Scripts/EnablePluginByDefault.cs new file mode 100644 index 0000000..64c25c1 --- /dev/null +++ b/Build/Scripts/EnablePluginByDefault.cs @@ -0,0 +1,37 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Reflection; +using AutomationTool; +using UnrealBuildTool; +using Tools.DotNETCommon; + +using System.Linq; +using System.Text; +using Microsoft.Win32; +using System.Diagnostics; + +namespace SimpleUGC.Automation +{ + [Help("Sets the EnabledByDefault flag for a plugin to true")] + public class EnablePluginByDefault : BuildCommand + { + public override void ExecuteBuild() + { + string FileName = ParseParamValue("FileName", null); + if(FileName == null) + { + throw new AutomationException("Missing -FileName=... argument"); + } + + CommandUtils.SetFileAttributes(FileName, ReadOnly: false); + + PluginDescriptor Plugin = PluginDescriptor.FromFile(new FileReference(FileName)); + Plugin.bEnabledByDefault = true; + Plugin.Save(FileName); + } + } +} diff --git a/Build/Scripts/PackageSimpleUGCPlugin.cs b/Build/Scripts/PackageSimpleUGCPlugin.cs new file mode 100644 index 0000000..b14c5db --- /dev/null +++ b/Build/Scripts/PackageSimpleUGCPlugin.cs @@ -0,0 +1,129 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using AutomationTool; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnrealBuildTool; +using Tools.DotNETCommon; + +using Microsoft.Win32; +using System.Diagnostics; + +namespace SimpleUGC.Automation +{ + public class PackageUGC : BuildCommand + { + static public ProjectParams GetParams(BuildCommand Cmd, string ProjectFileName, out FileReference PluginFile) + { + string VersionString = Cmd.ParseParamValue("Version", "NOVERSION"); + + // Get the plugin filename + string PluginPath = Cmd.ParseParamValue("PluginPath"); + if (PluginPath == null) + { + throw new AutomationException("Missing -PluginPath=... argument"); + } + + // Check it exists + PluginFile = new FileReference(PluginPath); + if (!FileReference.Exists(PluginFile)) + { + throw new AutomationException("Plugin '{0}' not found", PluginFile.FullName); + } + + string ReleaseVersion = Cmd.ParseParamValue("BasedOnReleaseVersion", "UGCExampleGame_v1"); + + FileReference ProjectFile = new FileReference(ProjectFileName); + + ProjectParams Params = new ProjectParams( + RawProjectPath: ProjectFile, + Command: Cmd, + ClientTargetPlatforms: new List(){ new TargetPlatformDescriptor(UnrealTargetPlatform.Win64) }, + Build: false, + Cook: true, + Stage: true, + Pak: true, + Manifests: true, + DLCIncludeEngineContent: true, // Need this to allow engine content that wasn't cooked in the base game to be included in the PAK file + BasedOnReleaseVersion: ReleaseVersion, + DLCName: PluginFile.GetFileNameWithoutAnyExtensions(), + + RunAssetNativization: true + ); + + Params.ValidateAndLog(); + return Params; + } + + public override void ExecuteBuild() + { + int WorkingCL = -1; + FileReference PluginFile = null; + string ProjectFileName = ParseParamValue("Project"); + if(ProjectFileName == null) + { + ProjectFileName = CombinePaths(CmdEnv.LocalRoot, "SimpleGame", "SimpleGame.uproject"); + } + LogInformation(ProjectFileName); + + ProjectParams Params = GetParams(this, ProjectFileName, out PluginFile); + + // Check whether folder already exists so we know if we can delete it later + string PlatformStageDir = Path.Combine(Params.StageDirectoryParam, "WindowsNoEditor"); + bool bPreExistingStageDir = Directory.Exists(PlatformStageDir); + + PluginDescriptor Plugin = PluginDescriptor.FromFile(PluginFile); + + FileReference ProjectFile = new FileReference(ProjectFileName); + + // Add Plugin to folders excluded for nativization in config file + FileReference UserEditorIni = new FileReference(Path.Combine(Path.GetDirectoryName(ProjectFileName), "Config", "UserEditor.ini")); + bool bPreExistingUserEditorIni = FileReference.Exists(UserEditorIni); + if (!bPreExistingUserEditorIni) + { + // Expect this most of the time so we will create and clean up afterwards + DirectoryReference.CreateDirectory(UserEditorIni.Directory); + CommandUtils.WriteAllText(UserEditorIni.FullName, ""); + } + + const string ConfigSection = "BlueprintNativizationSettings"; + const string ConfigKey = "ExcludedFolderPaths"; + string ConfigValue = "/" + PluginFile.GetFileNameWithoutAnyExtensions() + "/"; + + ConfigFile UserEditorConfig = new ConfigFile(UserEditorIni); + ConfigFileSection BPNSection = UserEditorConfig.FindOrAddSection(ConfigSection); + bool bUpdateConfigFile = !BPNSection.Lines.Exists(x => String.Equals(x.Key, ConfigKey, StringComparison.OrdinalIgnoreCase) && String.Equals(x.Value, ConfigValue, StringComparison.OrdinalIgnoreCase)); + if (bUpdateConfigFile) + { + BPNSection.Lines.Add(new ConfigLine(ConfigLineAction.Add, ConfigKey, ConfigValue)); + UserEditorConfig.Write(UserEditorIni); + } + + Project.Cook(Params); + if (!bPreExistingUserEditorIni) + { + FileReference.Delete(UserEditorIni); + } + + Project.CopyBuildToStagingDirectory(Params); + Project.Package(Params, WorkingCL); + Project.Archive(Params); + Project.Deploy(Params); + + // Get path to where the plugin was staged + string StagedPluginDir = Path.Combine(PlatformStageDir, Path.GetFileNameWithoutExtension(ProjectFileName), PluginFile.Directory.MakeRelativeTo(ProjectFile.Directory)); + string ZipFile = Path.Combine(Params.StageDirectoryParam, PluginFile.GetFileNameWithoutAnyExtensions()); + CommandUtils.DeleteFile(ZipFile); + System.IO.Compression.ZipFile.CreateFromDirectory(StagedPluginDir, ZipFile + ".zip"); + + if (!bPreExistingStageDir) + { + CommandUtils.DeleteDirectory(PlatformStageDir); + } + } + } +} diff --git a/Build/Scripts/Properties/AssemblyInfo.cs b/Build/Scripts/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..67540f3 --- /dev/null +++ b/Build/Scripts/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SimpleUGC.Automation")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SimpleUGC.Automation")] +[assembly: AssemblyCopyright("Copyright 1998-2020 Epic Games")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0C67E318-2918-47E5-91EC-043D9E042647")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Build/Scripts/SimpleUGC.Automation.csproj b/Build/Scripts/SimpleUGC.Automation.csproj new file mode 100644 index 0000000..b4fb06f --- /dev/null +++ b/Build/Scripts/SimpleUGC.Automation.csproj @@ -0,0 +1,79 @@ + + + + + Debug + AnyCPU + {5EA4677F-4A4B-4B92-9D07-33687A0C4CEA} + Library + Properties + SimpleUGC.Automation + SimpleUGC.Automation + v4.6.2 + 512 + + + + true + full + false + ..\..\..\..\..\Engine\Binaries\DotNET\AutomationScripts\ + DEBUG;TRACE + prompt + 4 + AnyCPU + true + false + + + pdbonly + false + ..\..\..\..\..\Engine\Binaries\DotNET\AutomationScripts\ + TRACE + prompt + 4 + AnyCPU + true + false + + + + + + + + + + + + + + + + + + + + + + + {2c96a7f2-b1a3-4258-8e0a-e588ff41a53e} + AutomationUtils.Automation + + + {5d7d66e8-8c76-4af9-b3ec-2ef03421d730} + DotNETUtilities + + + {8aa00d65-0954-4a27-ac0d-fb8b1106120f} + AutomationScripts.Automation + False + + + {fd7c5e1a-cfe4-4fd5-a525-1eb1599a39ac} + UnrealBuildTool + False + + + + \ No newline at end of file diff --git a/Config/DefaultEditor.ini b/Config/DefaultEditor.ini new file mode 100644 index 0000000..e69de29 diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini new file mode 100644 index 0000000..46fc52c --- /dev/null +++ b/Config/DefaultEngine.ini @@ -0,0 +1,19 @@ + + +[/Script/HardwareTargeting.HardwareTargetingSettings] +TargetedHardwareClass=Desktop +AppliedTargetedHardwareClass=Desktop +DefaultGraphicsPerformance=Maximum +AppliedDefaultGraphicsPerformance=Maximum + +[/Script/EngineSettings.GameMapsSettings] +EditorStartupMap=/Game/UGCExampleContent/Maps/MainMenu.MainMenu +GameDefaultMap=/Game/UGCExampleContent/Maps/MainMenu.MainMenu +GlobalDefaultGameMode=/Game/UGCExampleContent/Blueprints/GameFramework/UGCExampleGameMode.UGCExampleGameMode_C +GameInstanceClass=/Script/SimpleUGC.UGCBaseGameInstance + +[/Script/Engine.Engine] ++ActiveGameNameRedirects=(OldGameName="TP_Blank",NewGameName="/Script/UGCExample") ++ActiveGameNameRedirects=(OldGameName="/Script/TP_Blank",NewGameName="/Script/UGCExample") ++ActiveClassRedirects=(OldClassName="TP_BlankGameModeBase",NewClassName="UGCExampleGameModeBase") + diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini new file mode 100644 index 0000000..0584b44 --- /dev/null +++ b/Config/DefaultGame.ini @@ -0,0 +1,12 @@ + +[/Script/EngineSettings.GeneralProjectSettings] +ProjectID=1573F6A744CE2F82F161BA9F844A9214 + +[StartupActions] +bAddPacks=True +InsertPack=(PackSource="StarterContent.upack",PackName="StarterContent") + +[/Script/UnrealEd.ProjectPackagingSettings] +BuildConfiguration=PPBC_Development +IncludePrerequisites=False + diff --git a/Config/DefaultInput.ini b/Config/DefaultInput.ini new file mode 100644 index 0000000..ad0e5d2 --- /dev/null +++ b/Config/DefaultInput.ini @@ -0,0 +1,95 @@ +[/Script/Engine.InputSettings] ++AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) ++AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False)) +bAltEnterTogglesFullscreen=True +bF11TogglesFullscreen=True +bUseMouseForTouch=False +bEnableMouseSmoothing=True +bEnableFOVScaling=True +bCaptureMouseOnLaunch=True +bAlwaysShowTouchInterface=False +bShowConsoleOnFourFingerTap=True +bEnableGestureRecognizer=False +bUseAutocorrect=False +DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown +DefaultViewportMouseLockMode=LockOnCapture +FOVScale=0.011110 +DoubleClickTime=0.200000 ++ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=Gamepad_FaceButton_Bottom) ++ActionMappings=(ActionName="Jump",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=SpaceBar) ++ActionMappings=(ActionName="ResetVR",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=R) ++AxisMappings=(AxisName="LookUp",Scale=-1.000000,Key=MouseY) ++AxisMappings=(AxisName="LookUpRate",Scale=1.000000,Key=Gamepad_RightY) ++AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=Down) ++AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Gamepad_LeftY) ++AxisMappings=(AxisName="MoveForward",Scale=-1.000000,Key=S) ++AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=Up) ++AxisMappings=(AxisName="MoveForward",Scale=1.000000,Key=W) ++AxisMappings=(AxisName="MoveRight",Scale=-1.000000,Key=A) ++AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=D) ++AxisMappings=(AxisName="MoveRight",Scale=1.000000,Key=Gamepad_LeftX) ++AxisMappings=(AxisName="Turn",Scale=1.000000,Key=MouseX) ++AxisMappings=(AxisName="TurnRate",Scale=1.000000,Key=Gamepad_RightX) ++AxisMappings=(AxisName="TurnRate",Scale=-1.000000,Key=Left) ++AxisMappings=(AxisName="TurnRate",Scale=1.000000,Key=Right) +DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks ++ConsoleKeys=Tilde + diff --git a/Content/UGCExampleContent/Blueprints/GameFramework/UGCExampleGameMode.uasset b/Content/UGCExampleContent/Blueprints/GameFramework/UGCExampleGameMode.uasset new file mode 100644 index 0000000..48e6b03 Binary files /dev/null and b/Content/UGCExampleContent/Blueprints/GameFramework/UGCExampleGameMode.uasset differ diff --git a/Content/UGCExampleContent/Blueprints/GameFramework/UGCExamplePawn.uasset b/Content/UGCExampleContent/Blueprints/GameFramework/UGCExamplePawn.uasset new file mode 100644 index 0000000..7fdab55 Binary files /dev/null and b/Content/UGCExampleContent/Blueprints/GameFramework/UGCExamplePawn.uasset differ diff --git a/Content/UGCExampleContent/Blueprints/GameObjects/CubeActor.uasset b/Content/UGCExampleContent/Blueprints/GameObjects/CubeActor.uasset new file mode 100644 index 0000000..163ae4b Binary files /dev/null and b/Content/UGCExampleContent/Blueprints/GameObjects/CubeActor.uasset differ diff --git a/Content/UGCExampleContent/Blueprints/GameObjects/SphereActor.uasset b/Content/UGCExampleContent/Blueprints/GameObjects/SphereActor.uasset new file mode 100644 index 0000000..59ba7e9 Binary files /dev/null and b/Content/UGCExampleContent/Blueprints/GameObjects/SphereActor.uasset differ diff --git a/Content/UGCExampleContent/Maps/ExampleMap.umap b/Content/UGCExampleContent/Maps/ExampleMap.umap new file mode 100644 index 0000000..bfcd298 Binary files /dev/null and b/Content/UGCExampleContent/Maps/ExampleMap.umap differ diff --git a/Content/UGCExampleContent/Maps/MainMenu.umap b/Content/UGCExampleContent/Maps/MainMenu.umap new file mode 100644 index 0000000..7d266b9 Binary files /dev/null and b/Content/UGCExampleContent/Maps/MainMenu.umap differ diff --git a/Content/UGCExampleContent/UI/ListHeader.uasset b/Content/UGCExampleContent/UI/ListHeader.uasset new file mode 100644 index 0000000..e53525c Binary files /dev/null and b/Content/UGCExampleContent/UI/ListHeader.uasset differ diff --git a/Content/UGCExampleContent/UI/ModMenuWidget.uasset b/Content/UGCExampleContent/UI/ModMenuWidget.uasset new file mode 100644 index 0000000..d2efe00 Binary files /dev/null and b/Content/UGCExampleContent/UI/ModMenuWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCActorReplacementsHeader.uasset b/Content/UGCExampleContent/UI/UGCActorReplacementsHeader.uasset new file mode 100644 index 0000000..a757a3a Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCActorReplacementsHeader.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCClassWidget.uasset b/Content/UGCExampleContent/UI/UGCClassWidget.uasset new file mode 100644 index 0000000..64a0632 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCClassWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCClassesHeader.uasset b/Content/UGCExampleContent/UI/UGCClassesHeader.uasset new file mode 100644 index 0000000..c06a528 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCClassesHeader.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCClearOverridesWidget.uasset b/Content/UGCExampleContent/UI/UGCClearOverridesWidget.uasset new file mode 100644 index 0000000..0aed21a Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCClearOverridesWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCMapWidget.uasset b/Content/UGCExampleContent/UI/UGCMapWidget.uasset new file mode 100644 index 0000000..48729c1 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCMapWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCMapsHeader.uasset b/Content/UGCExampleContent/UI/UGCMapsHeader.uasset new file mode 100644 index 0000000..5515e51 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCMapsHeader.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCOverrideActorToReplaceWidget.uasset b/Content/UGCExampleContent/UI/UGCOverrideActorToReplaceWidget.uasset new file mode 100644 index 0000000..d0d2b39 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCOverrideActorToReplaceWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCOverrideWidget.uasset b/Content/UGCExampleContent/UI/UGCOverrideWidget.uasset new file mode 100644 index 0000000..12b94a2 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCOverrideWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCPackageHeader.uasset b/Content/UGCExampleContent/UI/UGCPackageHeader.uasset new file mode 100644 index 0000000..054d7c2 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCPackageHeader.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCPackageWidget.uasset b/Content/UGCExampleContent/UI/UGCPackageWidget.uasset new file mode 100644 index 0000000..ebbf1bf Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCPackageWidget.uasset differ diff --git a/Content/UGCExampleContent/UI/UGCRegisteredOverrideWidget.uasset b/Content/UGCExampleContent/UI/UGCRegisteredOverrideWidget.uasset new file mode 100644 index 0000000..8a7e269 Binary files /dev/null and b/Content/UGCExampleContent/UI/UGCRegisteredOverrideWidget.uasset differ diff --git a/Plugins/SimpleUGC/Resources/ButtonIcon_40x.png b/Plugins/SimpleUGC/Resources/ButtonIcon_40x.png new file mode 100644 index 0000000..2e8bbfd Binary files /dev/null and b/Plugins/SimpleUGC/Resources/ButtonIcon_40x.png differ diff --git a/Plugins/SimpleUGC/Resources/CreateUGC_128x.png b/Plugins/SimpleUGC/Resources/CreateUGC_128x.png new file mode 100644 index 0000000..5ffc3c6 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/CreateUGC_128x.png differ diff --git a/Plugins/SimpleUGC/Resources/CreateUGC_16x.png b/Plugins/SimpleUGC/Resources/CreateUGC_16x.png new file mode 100644 index 0000000..f61daf0 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/CreateUGC_16x.png differ diff --git a/Plugins/SimpleUGC/Resources/CreateUGC_48x.png b/Plugins/SimpleUGC/Resources/CreateUGC_48x.png new file mode 100644 index 0000000..a958056 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/CreateUGC_48x.png differ diff --git a/Plugins/SimpleUGC/Resources/CreateUGC_64x.png b/Plugins/SimpleUGC/Resources/CreateUGC_64x.png new file mode 100644 index 0000000..c4278cd Binary files /dev/null and b/Plugins/SimpleUGC/Resources/CreateUGC_64x.png differ diff --git a/Plugins/SimpleUGC/Resources/Icon128.png b/Plugins/SimpleUGC/Resources/Icon128.png new file mode 100644 index 0000000..85b65e7 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/Icon128.png differ diff --git a/Plugins/SimpleUGC/Resources/PackageUGC_128x.png b/Plugins/SimpleUGC/Resources/PackageUGC_128x.png new file mode 100644 index 0000000..4221113 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/PackageUGC_128x.png differ diff --git a/Plugins/SimpleUGC/Resources/PackageUGC_16x.png b/Plugins/SimpleUGC/Resources/PackageUGC_16x.png new file mode 100644 index 0000000..cbe6e06 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/PackageUGC_16x.png differ diff --git a/Plugins/SimpleUGC/Resources/PackageUGC_48x.png b/Plugins/SimpleUGC/Resources/PackageUGC_48x.png new file mode 100644 index 0000000..8ca1ebd Binary files /dev/null and b/Plugins/SimpleUGC/Resources/PackageUGC_48x.png differ diff --git a/Plugins/SimpleUGC/Resources/PackageUGC_64x.png b/Plugins/SimpleUGC/Resources/PackageUGC_64x.png new file mode 100644 index 0000000..0657a05 Binary files /dev/null and b/Plugins/SimpleUGC/Resources/PackageUGC_64x.png differ diff --git a/Plugins/SimpleUGC/SimpleUGC.uplugin b/Plugins/SimpleUGC/SimpleUGC.uplugin new file mode 100644 index 0000000..ac3cf0a --- /dev/null +++ b/Plugins/SimpleUGC/SimpleUGC.uplugin @@ -0,0 +1,34 @@ +{ + "FileVersion": 3, + "Version": 1, + "VersionName": "1.0", + "FriendlyName": "SimpleUGC", + "Description": "", + "Category": "Other", + "CreatedBy": "", + "CreatedByURL": "", + "DocsURL": "", + "MarketplaceURL": "", + "SupportURL": "", + "CanContainContent": true, + "IsBetaVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "SimpleUGC", + "Type": "Runtime", + "LoadingPhase": "PreLoadingScreen" + }, + { + "Name": "SimpleUGCEditor", + "Type": "Editor", + "LoadingPhase": "Default" + } + ], + "Plugins": [ + { + "Name": "PluginBrowser", + "Enabled": true + } + ] +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/MakeReplaceableActorComponent.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/MakeReplaceableActorComponent.cpp new file mode 100644 index 0000000..3e3d638 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/MakeReplaceableActorComponent.cpp @@ -0,0 +1,9 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "MakeReplaceableActorComponent.h" + +// Sets default values for this component's properties +UMakeReplaceableActorComponent::UMakeReplaceableActorComponent() +{ + SetAutoActivate(true); +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/ReplacementActorComponent.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/ReplacementActorComponent.cpp new file mode 100644 index 0000000..773cec8 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/ReplacementActorComponent.cpp @@ -0,0 +1,9 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "ReplacementActorComponent.h" + +// Sets default values for this component's properties +UReplacementActorComponent::UReplacementActorComponent() +{ + SetAutoActivate(true); +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/SimpleUGC.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/SimpleUGC.cpp new file mode 100644 index 0000000..67ee730 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/SimpleUGC.cpp @@ -0,0 +1,8 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGC.h" + +#define LOCTEXT_NAMESPACE "FSimpleUGCModule" +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FSimpleUGCModule, SimpleUGC) diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBaseGameInstance.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBaseGameInstance.cpp new file mode 100644 index 0000000..39ef343 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBaseGameInstance.cpp @@ -0,0 +1,11 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "UGCBaseGameInstance.h" + +void UUGCBaseGameInstance::Init() +{ + // Instnatiate the registry and find mod packages + UGCRegistry = NewObject(this); + UGCRegistry->FindUGCPackages(); + Super::Init(); +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBlueprintLibrary.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBlueprintLibrary.cpp new file mode 100644 index 0000000..39956f9 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCBlueprintLibrary.cpp @@ -0,0 +1,13 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "UGCBlueprintLibrary.h" +#include "UGCBaseGameInstance.h" +#include "UGCRegistry.h" +#include "Kismet/GameplayStatics.h" +#include "SimpleUGC.h" + +UUGCRegistry * UUGCBlueprintLibrary::GetUGCRegistry(UObject* WorldContextObject) +{ + UUGCBaseGameInstance* GameInstance = Cast(UGameplayStatics::GetGameInstance(WorldContextObject)); + return (GameInstance) ? GameInstance->UGCRegistry : nullptr; +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp new file mode 100644 index 0000000..90680fb --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp @@ -0,0 +1,241 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "UGCRegistry.h" +#include "AssetRegistryModule.h" +#include "ARFilter.h" +#include "Interfaces/IPluginManager.h" +#include "HAL/PlatformFilemanager.h" +#include "Runtime/Json/Public/Dom/JsonObject.h" +#include "Misc/PackageName.h" +#include "Kismet/GameplayStatics.h" +#include "Misc/Paths.h" +#include "MakeReplaceableActorComponent.h" +#include "ReplacementActorComponent.h" +#include "SimpleUGC.h" + +bool UUGCRegistry::FindUGCPackages() +{ + TArray> EnabledPlugins = IPluginManager::Get().GetEnabledPlugins(); + for (const TSharedRef& Plugin : EnabledPlugins) + { + if (Plugin->GetLoadedFrom() == EPluginLoadedFrom::Project && Plugin->GetDescriptor().Category == "UGC") + { + FUGCPackage Package; + Package.PackagePath = *Plugin->GetMountedAssetPath().LeftChop(1); + Package.EngineVersion = *Plugin->GetDescriptor().EngineVersion; + Package.Author = *Plugin->GetDescriptor().CreatedBy; + Package.Description = *Plugin->GetDescriptor().Description; + UGCPackages.Add(Package); + } + } + + return UGCPackages.Num() > 0; +} + +bool UUGCRegistry::GetAllClassesInPackage(FUGCPackage Package, TArray &Classes) +{ + // Load up the AssetRegistry, Filter for Blueprints + + IAssetRegistry& AssetRegistry = GetAsstRegistry(); + FARFilter ARFilter; + TArray AssetList; + ARFilter.bRecursivePaths = true; + ARFilter.bIncludeOnlyOnDiskAssets = true; + ARFilter.bRecursiveClasses = true; + ARFilter.ClassNames.Add(UBlueprint::StaticClass()->GetFName()); + ARFilter.PackagePaths.Add(FName(*Package.PackagePath)); + AssetRegistry.GetAssets(ARFilter, AssetList); + + for (FAssetData Asset : AssetList) + { + FAssetDataTagMapSharedView::FFindTagResult GeneratedClassResult = Asset.TagsAndValues.FindTag("GeneratedClass"); + if (GeneratedClassResult.IsSet()) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*GeneratedClassResult.GetValue()); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + if (AssetClass) + { + Classes.Add(AssetClass); + } + } + } + + return Classes.Num() > 0; +} + +bool UUGCRegistry::GetMapsInPackage(FUGCPackage Package, TArray &Maps) +{ + // Load up the AssetRegistry, FIlter for Maps + IAssetRegistry& AssetRegistry = GetAsstRegistry(); + FARFilter ARFilter; + TArray AssetList; + ARFilter.bRecursivePaths = true; + ARFilter.bIncludeOnlyOnDiskAssets = true; + ARFilter.bRecursiveClasses = true; + ARFilter.ClassNames.Add(UWorld::StaticClass()->GetFName()); + ARFilter.PackagePaths.Add(FName(*Package.PackagePath)); + AssetRegistry.GetAssets(ARFilter, AssetList); + + for (FAssetData Asset : AssetList) + { + Maps.Add(Asset.AssetName); + } + + return Maps.Num() > 0; +} + +bool UUGCRegistry::GetActorClassesWithReplacementActorComponentsInPackage(FUGCPackage Package, TArray> &ActorClasses) +{ + // Load up the AssetRegistry, Filter for Blueprints + IAssetRegistry& AssetRegistry = GetAsstRegistry(); + FARFilter ARFilter; + TArray AssetList; + ARFilter.bRecursivePaths = true; + ARFilter.bIncludeOnlyOnDiskAssets = true; + ARFilter.bRecursiveClasses = true; + ARFilter.ClassNames.Add(UBlueprint::StaticClass()->GetFName()); + ARFilter.PackagePaths.Add(FName(*Package.PackagePath)); + AssetRegistry.GetAssets(ARFilter, AssetList); + + for (FAssetData Asset : AssetList) + { + FAssetDataTagMapSharedView::FFindTagResult GeneratedClassResult = Asset.TagsAndValues.FindTag("GeneratedClass"); + if (GeneratedClassResult.IsSet()) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*GeneratedClassResult.GetValue()); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + if (AssetClass) + { + + if (UBlueprintGeneratedClass* BlueprintModClass = Cast(AssetClass)) + { + // Not All Blueprint Classes have a SCS, so ensure that it has one before we get at dem nodes. + if (BlueprintModClass->SimpleConstructionScript != nullptr) + { + // Find UGCOverrideComponent + for (USCS_Node* Node : BlueprintModClass->SimpleConstructionScript->GetRootNodes()) + { + // If we found it, add to Classes + if (UReplacementActorComponent * ReplacementActorComponent = Cast(Node->ComponentTemplate)) + { + ActorClasses.Add(AssetClass); + } + } + } + } + } + } + } + return ActorClasses.Num() > 0; +} +bool UUGCRegistry::ApplyAllOverridesInPackage(FUGCPackage Package) +{ + bool bSuccess = false; + // Load up the AssetRegistry + IAssetRegistry& AssetRegistry = GetAsstRegistry(); + TArray AssetList; + AssetRegistry.GetAssetsByPath(FName(*Package.PackagePath), AssetList, true); + + for (FAssetData Asset : AssetList) + { + FAssetDataTagMapSharedView::FFindTagResult GeneratedClassResult = Asset.TagsAndValues.FindTag("GeneratedClass"); + if (GeneratedClassResult.IsSet()) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*GeneratedClassResult.GetValue()); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Try to apply an override + if (AssetClass) + { + bSuccess = ApplyOverridesForActorClass(AssetClass) || bSuccess; + } + } + } + return bSuccess; +} + +bool UUGCRegistry::ApplyOverridesForActorClass(TSubclassOf ActorClass) +{ + bool bSuccess = false; + + // Check Blueprints + if (UBlueprintGeneratedClass* BlueprintActorClass = Cast(ActorClass)) + { + // Null check on SCS + if (BlueprintActorClass->SimpleConstructionScript != nullptr) + { + // Find UGCOverrideComponent so we know what to override + for (USCS_Node* Node : BlueprintActorClass->SimpleConstructionScript->GetRootNodes()) + { + // If we found it, + if (UReplacementActorComponent* ReplacementActorComponent = Cast(Node->ComponentTemplate)) + { + // Check The Classes To Override + for (UClass* ActorClassToReplace : ReplacementActorComponent->ActorClassesToReplace) + { + // Check Blueprint First.. + if (UBlueprintGeneratedClass* BlueprintClassToReplace = Cast< UBlueprintGeneratedClass>(ActorClassToReplace)) + { + if (BlueprintClassToReplace->SimpleConstructionScript != nullptr) + { + // Null check on SCS + for (USCS_Node* ActorClassToReplaceNode : BlueprintClassToReplace->SimpleConstructionScript->GetRootNodes()) + { + // Find UGCOverrideableComponent so we can ensure compatibility + if (UMakeReplaceableActorComponent* MakeReplaceableActorComponent = Cast(ActorClassToReplaceNode->ComponentTemplate)) + { + // Ensure compatibility + if (ActorClass->IsChildOf(MakeReplaceableActorComponent->CompatibleReplacement)) + { + // Register + RegisterOverrideForClass(ActorClassToReplace, ActorClass); + bSuccess = true; + } + } + } + } + } + // If doing code mods, implement check for UUGCOverrideableComponent on non BP classes here. + // else{} + } + } + } + } + } + // If doing code mods, implement check for UUGCOverrideComponent on non-BP classes here. + // else{} + + return bSuccess; +} + +void UUGCRegistry::RegisterOverrideForClass(TSubclassOf ClassToOverride, TSubclassOf OverrideClass) +{ + RegisteredOverrides.Emplace(ClassToOverride, OverrideClass); +} + +void UUGCRegistry::ClearOverrideForClass(TSubclassOf ActorClass) +{ + RegisteredOverrides.Remove(ActorClass); +} + +TSubclassOf UUGCRegistry::GetOverrideForActorClass(TSubclassOf ActorClass) +{ + if (RegisteredOverrides.Contains(ActorClass)) + { + return *RegisteredOverrides.Find(ActorClass); + } + return ActorClass; +} + +IAssetRegistry& UUGCRegistry::GetAsstRegistry() +{ + if (!CachedAssetRegistryModule) + { + CachedAssetRegistryModule = &FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + } + + check(CachedAssetRegistryModule); + return CachedAssetRegistryModule->Get(); +} \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2b8431a.TMP b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2b8431a.TMP new file mode 100644 index 0000000..ab0d98e --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2b8431a.TMP @@ -0,0 +1,262 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "UGCRegistry.h" +#include "UGCKit.h" + + + +bool UUGCRegistry::FindUGCPackages() +{ + // Now let's find all new root paths in the asset registry. + TArray RootPaths; + FPackageName::QueryRootContentPaths(RootPaths); + + // Remove any plugins your game is shipping with, as well as engine and game roots. We only care about NEW paths. + RootPaths.Remove(TEXT("/Engine/")); + RootPaths.Remove(TEXT("/Game/")); + RootPaths.Remove(TEXT("/Paper2D/")); + RootPaths.Remove(TEXT("/UGCKit/")); + + // Remove any other plugins you've defined in Blueprints + for (FName Package : PackageBlacklist) + { + RootPaths.Remove(Package.ToString()); + } + + if (RootPaths.Num() <= 0) + { + return false; + } + + for (FString Path : RootPaths) + { + UGCPackages.Add(FName(*Path.LeftChop(1))); + } + + return true; + +} + +bool UUGCRegistry::GetAssetsFromPackage(FName Package, TArray &Assets, bool bOnlyRegisteredModTypes) +{ + // Load up the AssetRegistry + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray AssetList; + AssetRegistry.GetAssetsByPath(Package, AssetList, true); + if (AssetList.Num() == 0) + { + return false; + } + + for (FAssetData Asset : AssetList) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + if (bOnlyRegisteredModTypes) + { + for (UClass * ModClass : RegisteredModTypes) + { + if (AssetClass->IsChildOf(ModClass)) + { + Assets.Add(Asset); + break; + } + } + } + else + { + Assets.Add(Asset); + } + } + } + } + return Assets.Num() > 0; + +} + +void UUGCRegistry::ShowAllPossibleOverridesForClass(UClass * ModClass, TArray &Overrides) +{ + TArray PackageAssets; + for (FName Package : UGCPackages) + { + GetAssetsFromPackage(Package, PackageAssets, true); + } + + for (FAssetData Asset : PackageAssets) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass && AssetClass->IsChildOf(ModClass)) + { + Overrides.Add(AssetClass); + } + } + } +} + +UClass * UUGCRegistry::GetClassForAssetData(FAssetData Asset) +{ + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + return AssetClass; + } + } + return nullptr; +} + +bool UUGCRegistry::ApplyAllModsInPackage(FName Package) +{ + + bool success = false; + // Load up the AssetRegistry + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray AssetList; + AssetRegistry.GetAssetsByPath(Package, AssetList, true); + if (AssetList.Num() == 0) + { + return success; + } + + for (FAssetData Asset : AssetList) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + for (UClass * ModClass : RegisteredModTypes) + { + if (AssetClass->IsChildOf(ModClass)) + { + success = ApplyOverridesForModClass(AssetClass) || success; + } + } + } + } + } + return success; +} + +bool UUGCRegistry::ApplyOverridesForModClass(UClass * ModClass) +{ + // Check Actors + AUGCBaseActor *UGCActor = Cast(ModClass->GetDefaultObject()); + if (UGCActor != nullptr) + { + for (UClass * ClassToOverride : UGCActor->ClassesToOverride) + { + if (ClassToOverride->IsChildOf(AUGCBaseActor::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCActor->GetClass()); + } + } + return true; + } + + // Check Pawns & Characters + AUGCBasePawn *UGCPawn = Cast(ModClass->GetDefaultObject()); + if (UGCPawn != nullptr) + { + for (UClass * ClassToOverride : UGCPawn->ClassesToOverride) + { + if (ClassToOverride->IsChildOf(AUGCBasePawn::StaticClass()) || + ClassToOverride->IsChildOf(AUGCBaseCharacter::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCPawn->GetClass()); + } + } + return true; + } + + // Check Characters + AUGCBaseCharacter *UGCCharacter = Cast(ModClass->GetDefaultObject()); + if (UGCCharacter != nullptr) + { + for (UClass * ClassToOverride : UGCCharacter->ClassesToOverride) + { + if (ClassToOverride->IsChildOf(AUGCBasePawn::StaticClass()) || + ClassToOverride->IsChildOf(AUGCBaseCharacter::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCCharacter->GetClass()); + } + } + return true; + } + return false; +} + +void UUGCRegistry::AssignOverrideForBaseClass(UClass * OriginClass, UClass * OverrideClass) +{ + // If already registered, Update Registration + for (FModOverridePairing &Pairing : RegisteredOverrides) + { + if (Pairing.Origin == OriginClass) + { + Pairing.Override = OverrideClass; + + return; + } + } + + // If not, make new + FModOverridePairing NewPairing; + NewPairing.Origin = OriginClass; + NewPairing.Override = OverrideClass; + RegisteredOverrides.Add(NewPairing); +} + +bool UUGCRegistry::ClearOverrideForClass(UClass *OriginClass) +{ + // Find Pairing, remove From Array + for (int i = 0; i < RegisteredOverrides.Num(); i++) + { + FModOverridePairing & Pairing = RegisteredOverrides[i]; + if (Pairing.Origin == OriginClass) + { + RegisteredOverrides.RemoveAt(i, 1, true); + return true; + } + } + + // Returns False if there was no pairing to begin with + return false; +} + +UClass * UUGCRegistry::GetOverrideForClass(UClass *OriginClass) +{ + for (FModOverridePairing &Pairing : RegisteredOverrides) + { + if (Pairing.Origin == OriginClass) + { + return Pairing.Override; + } + } + return OriginClass; +} \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2bf9f51.TMP b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2bf9f51.TMP new file mode 100644 index 0000000..a0c74da --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Private/UGCRegistry.cpp~RF2bf9f51.TMP @@ -0,0 +1,319 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "UGCRegistry.h" +#include "UGCKit.h" + + + +bool UUGCRegistry::FindUGCPackages() +{ + // Now let's find all new root paths in the asset registry. + TArray RootPaths; + FPackageName::QueryRootContentPaths(RootPaths); + + // Remove any plugins your game is shipping with, as well as engine and game roots. We only care about NEW paths. + RootPaths.Remove(TEXT("/Engine/")); + RootPaths.Remove(TEXT("/Game/")); + RootPaths.Remove(TEXT("/Paper2D/")); + RootPaths.Remove(TEXT("/UGCKit/")); + + // Remove any other plugins you've defined in Blueprints + for (FName Package : PackageBlacklist) + { + RootPaths.Remove(Package.ToString()); + } + + if (RootPaths.Num() <= 0) + { + return false; + } + + for (FString Path : RootPaths) + { + UGCPackages.Add(FName(*Path.LeftChop(1))); + } + + return true; + +} + +bool UUGCRegistry::GetAssetsFromPackage(FName Package, TArray &Assets, bool bOnlyRegisteredModTypes) +{ + // Load up the AssetRegistry + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray AssetList; + AssetRegistry.GetAssetsByPath(Package, AssetList, true); + if (AssetList.Num() == 0) + { + return false; + } + + for (FAssetData Asset : AssetList) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + if (bOnlyRegisteredModTypes) + { + for (UClass * ModClass : RegisteredModTypes) + { + if (AssetClass->IsChildOf(ModClass)) + { + Assets.Add(Asset); + break; + } + } + } + else + { + Assets.Add(Asset); + } + } + } + } + return Assets.Num() > 0; +} + +bool UUGCRegistry::GetMapsFromPackage(FName Package, TArray &Maps) +{ + // Load up the AssetRegistry + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + FARFilter ARFilter; + TArray AssetList; + // Add any old names to the list in case things haven't been resaved + ARFilter.ClassNames.Append(OldNames); + ARFilter.bRecursivePaths = true; + ARFilter.bIncludeOnlyOnDiskAssets = true; + ARFilter.bRecursiveClasses = true; + + //////////////////////////////////////// + // Blueprint and CPP classes of our archetypes. + ARFilter.ClassNames.Add(UWorld::StaticClass()->GetFName()); + ARFilter.PackagePaths.Add(Package); + AssetRegistry.GetAssets(ARFilter, AssetList); + AssetRegistry.GetAssetsByPath(Package, AssetList, true); + if (AssetList.Num() == 0) + { + return false; + } + + for (FAssetData Asset : AssetList) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + if (bOnlyRegisteredModTypes) + { + for (UClass * ModClass : RegisteredModTypes) + { + if (AssetClass->IsChildOf(ModClass)) + { + Assets.Add(Asset); + break; + } + } + } + else + { + Assets.Add(Asset); + } + } + } + } + return Assets.Num() > 0; +} + +void UUGCRegistry::ShowAllPossibleOverridesForClass(UClass * ModClass, TArray &Overrides) +{ + TArray PackageAssets; + for (FName Package : UGCPackages) + { + GetAssetsFromPackage(Package, PackageAssets, true); + } + + for (FAssetData Asset : PackageAssets) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass && AssetClass->IsChildOf(ModClass)) + { + Overrides.Add(AssetClass); + } + } + } +} + +UClass * UUGCRegistry::GetClassForAssetData(FAssetData Asset) +{ + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + return AssetClass; + } + } + return nullptr; +} + +bool UUGCRegistry::ApplyAllModsInPackage(FName Package) +{ + + bool success = false; + // Load up the AssetRegistry + FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); + IAssetRegistry& AssetRegistry = AssetRegistryModule.Get(); + + TArray AssetList; + AssetRegistry.GetAssetsByPath(Package, AssetList, true); + if (AssetList.Num() == 0) + { + return success; + } + + for (FAssetData Asset : AssetList) + { + const FString* ClassTextPath = Asset.TagsAndValues.Find("GeneratedClass"); + if (ClassTextPath != NULL) + { + FString ClassPath = FPackageName::ExportTextPathToObjectPath(*ClassTextPath); + UClass* AssetClass = LoadObject(NULL, *ClassPath); + + // Check if it's a mod type + if (AssetClass) + { + for (UClass * ModClass : RegisteredModTypes) + { + if (AssetClass->IsChildOf(ModClass)) + { + success = ApplyOverridesForModClass(AssetClass) || success; + } + } + } + } + } + return success; +} + +bool UUGCRegistry::ApplyOverridesForModClass(UClass * ModClass) +{ + // Check Actors + AUGCBaseActor *UGCActor = Cast(ModClass->GetDefaultObject()); + if (UGCActor != nullptr) + { + for (UClass * ClassToOverride : UGCActor->ClassesToOverride) + { + if (ClassToOverride->IsChildOf(AUGCBaseActor::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCActor->GetClass()); + } + } + return true; + } + + // Check Pawns + AUGCBasePawn *UGCPawn = Cast(ModClass->GetDefaultObject()); + if (UGCPawn != nullptr) + { + for (UClass * ClassToOverride : UGCPawn->ClassesToOverride) + { + // Character is a subclass of Pawn, so we're looking for either + if (ClassToOverride->IsChildOf(AUGCBasePawn::StaticClass()) || + ClassToOverride->IsChildOf(AUGCBaseCharacter::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCPawn->GetClass()); + } + } + return true; + } + + // Check Characters + AUGCBaseCharacter *UGCCharacter = Cast(ModClass->GetDefaultObject()); + if (UGCCharacter != nullptr) + { + for (UClass * ClassToOverride : UGCCharacter->ClassesToOverride) + { + // Pawn is a superclass of Character, so we're looking for either + if (ClassToOverride->IsChildOf(AUGCBasePawn::StaticClass()) || + ClassToOverride->IsChildOf(AUGCBaseCharacter::StaticClass())) + { + AssignOverrideForBaseClass(ClassToOverride, UGCCharacter->GetClass()); + } + } + return true; + } + return false; +} + +void UUGCRegistry::AssignOverrideForBaseClass(UClass * OriginClass, UClass * OverrideClass) +{ + // If already registered, Update Registration + for (FModOverridePairing &Pairing : RegisteredOverrides) + { + if (Pairing.Origin == OriginClass) + { + Pairing.Override = OverrideClass; + return; + } + } + + // If not, make new + FModOverridePairing NewPairing; + NewPairing.Origin = OriginClass; + NewPairing.Override = OverrideClass; + RegisteredOverrides.Add(NewPairing); +} + +bool UUGCRegistry::ClearOverrideForClass(UClass *OriginClass) +{ + // Find Pairing, remove From Array + for (int i = 0; i < RegisteredOverrides.Num(); i++) + { + FModOverridePairing & Pairing = RegisteredOverrides[i]; + if (Pairing.Origin == OriginClass) + { + RegisteredOverrides.RemoveAt(i, 1, true); + return true; + } + } + + // Returns False if there was no pairing to begin with + return false; +} + +UClass * UUGCRegistry::GetOverrideForClass(UClass *OriginClass) +{ + for (FModOverridePairing &Pairing : RegisteredOverrides) + { + if (Pairing.Origin == OriginClass) + { + return Pairing.Override; + } + } + return OriginClass; +} \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/MakeReplaceableActorComponent.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/MakeReplaceableActorComponent.h new file mode 100644 index 0000000..4cc7289 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/MakeReplaceableActorComponent.h @@ -0,0 +1,22 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "MakeReplaceableActorComponent.generated.h" + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class SIMPLEUGC_API UMakeReplaceableActorComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + // Sets default values for this component's properties + UMakeReplaceableActorComponent(); + + // Only Classes of these Types can override this Actor. This is typically the type of actor you've places this component on or a safe superclass shared with an Override class. + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category ="SimpleUGC | Actor Replacement") + TSubclassOf CompatibleReplacement; + +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/ReplacementActorComponent.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/ReplacementActorComponent.h new file mode 100644 index 0000000..f35429c --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/ReplacementActorComponent.h @@ -0,0 +1,21 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "ReplacementActorComponent.generated.h" + + +UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) ) +class SIMPLEUGC_API UReplacementActorComponent : public UActorComponent +{ + GENERATED_BODY() + +public: + UReplacementActorComponent(); + // Add Classes you want to override here. Note: Classes added to this list MUST have a UMakeReplaceableActorComponent and This class's type be included in the component's ValidOverrideTypes + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SimpleUGC | Actor Replacement") + TArray> ActorClassesToReplace; + +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/SimpleUGC.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/SimpleUGC.h new file mode 100644 index 0000000..f89e596 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/SimpleUGC.h @@ -0,0 +1,10 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Modules\ModuleManager.h" + +class FSimpleUGCModule : public IModuleInterface +{ +public: +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBaseGameInstance.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBaseGameInstance.h new file mode 100644 index 0000000..121886f --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBaseGameInstance.h @@ -0,0 +1,25 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Engine/GameInstance.h" +#include "UGCRegistry.h" +#include "UGCBaseGameInstance.generated.h" + +/** + * + */ +UCLASS(BlueprintType) +class SIMPLEUGC_API UUGCBaseGameInstance : public UGameInstance +{ + GENERATED_BODY() + + public: + virtual void Init() override; + + // The Registry that holds information about UGC and assigned class overrides + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + UUGCRegistry* UGCRegistry; + +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBlueprintLibrary.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBlueprintLibrary.h new file mode 100644 index 0000000..a0ed67c --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCBlueprintLibrary.h @@ -0,0 +1,20 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "Kismet/BlueprintFunctionLibrary.h" +#include "UGCRegistry.h" +#include "UGCBlueprintLibrary.generated.h" + + +UCLASS() +class SIMPLEUGC_API UUGCBlueprintLibrary : public UBlueprintFunctionLibrary +{ + + GENERATED_BODY() +public: + + // Gets the UGC Registry found in the GameInstance + UFUNCTION(BlueprintPure, Category = "SimpleUGC", meta = (WorldContext = "WorldContextObject")) + static UUGCRegistry * GetUGCRegistry(UObject* WorldContextObject); +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h new file mode 100644 index 0000000..a2dfeea --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h @@ -0,0 +1,98 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "AssetRegistryModule.h" +#include "Engine.h" +#include "SimpleUGC.h" +#include "Engine/World.h" +#include "Engine/BlendableInterface.h" +#include "UGCRegistry.generated.h" + +USTRUCT(BlueprintType) +struct FUGCPackage +{ + GENERATED_BODY() + + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + FString PackagePath; + + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + FString EngineVersion; + + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + FString Author; + + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + FString Description; + + FUGCPackage() + { + PackagePath = ""; + EngineVersion = ""; + Author = ""; + Description = ""; + } +}; + +UCLASS(BlueprintType) +class SIMPLEUGC_API UUGCRegistry : public UObject +{ + GENERATED_BODY() +public: + + // This is our list of UGC packages. Populated by FindUGCPackages() + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC") + TArray UGCPackages; + + // A pairing of Origins and Overrides. This is what the gameplay logic references when loading an effective class + UPROPERTY(BlueprintReadOnly, Category = "SimpleUGC|Actor Replacement") + TMap /*Origin*/, TSubclassOf /*Override*/> RegisteredOverrides; + + // This populates UGCPackages based on what is found in UGC plugin files. If you're mounting new /Plugin paks at runtime. Expose this to Blueprints. + UFUNCTION(Blueprintcallable) + bool FindUGCPackages(); + + // General DLC Asset Access. Create Similar Methods For Getting Materials, Textures, etc. + + // Returns All Classes in a UGC package. + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SimpleUGC") + bool GetAllClassesInPackage(FUGCPackage Package, TArray &Classes); + + // Returns All Maps in a UGC package. + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SimpleUGC") + bool GetMapsInPackage(FUGCPackage Package, TArray &Maps); + + // Actor Replacement Specific Calls + + // Returns All Actor Classes In Package that have the ability to replace a a base class. Use this when you want to register only specific class overrides from a UGC package. + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SimpleUGC|Actor Replacement") + bool GetActorClassesWithReplacementActorComponentsInPackage(FUGCPackage Package, TArray> &ActorClasses); + + // Applies entire package of Actor Replacements. This is common for applying an entire class-based "Mod." + UFUNCTION(BlueprintCallable, Category = "SimpleUGC|Actor Replacement") + bool ApplyAllOverridesInPackage(FUGCPackage Package); + + // Applies an override for a specific Class. Find valid classes to use here by calling GetActorClassesWithReplacementActorComponentsInPackage + UFUNCTION(BlueprintCallable, Category = "SimpleUGC|Actor Replacement") + bool ApplyOverridesForActorClass(TSubclassOf ActorClass); + + // A manual override assignment. Not reccommended for mod packages, but useful for big UGC drops (100 UGuns pack, etc) + UFUNCTION(BlueprintCallable, Category = "SimpleUGC|Actor Replacement") + void RegisterOverrideForClass(TSubclassOf ClassToOverride, TSubclassOf OverrideClass); + + // Used to clear the override from the registry. To clear all, loop through RegisterredOverrides, break the struct and run the Origin into this function. + UFUNCTION(BlueprintCallable, Category = "SimpleUGC|Actor Replacement") + void ClearOverrideForClass(TSubclassOf ActorClass); + + // Used in gameplay to look up what class is actually supposed to be spawned + UFUNCTION(BlueprintCallable, BlueprintPure, Category = "SimpleUGC|Actor Replacement") + TSubclassOf GetOverrideForActorClass(TSubclassOf ActorClass); + + +private: + FAssetRegistryModule* CachedAssetRegistryModule; + IAssetRegistry& GetAsstRegistry(); + +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h~RF2b28773.TMP b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h~RF2b28773.TMP new file mode 100644 index 0000000..17cdab8 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/Public/UGCRegistry.h~RF2b28773.TMP @@ -0,0 +1,106 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "UGCKit.h" +#include "Public/AssetRegistryModule.h" +#include "Public/ARFilter.h" +#include "UGCRegistry.generated.h" + +/** + * + */ + +USTRUCT(BlueprintType) +struct FModOverridePairing +{ + GENERATED_BODY() + UPROPERTY(BlueprintReadOnly, Category = "UGCKit|Modding") + UClass* Origin; + + UPROPERTY(BlueprintReadOnly, Category = "UGCKit|Modding") + UClass* Override; + + FModOverridePairing() + { + Origin = nullptr; + Override = nullptr; + } +}; + + +UCLASS(BlueprintType) +class UGCKIT_API UUGCRegistry : public UObject +{ + GENERATED_BODY() +public: + + // Init + UUGCRegistry() + { + RegisteredModTypes.Empty(); + RegisteredModTypes.Add(AUGCBaseActor::StaticClass()); + RegisteredModTypes.Add(AUGCBaseCharacter::StaticClass()); + RegisteredModTypes.Add(AUGCBaseGameMode::StaticClass()); + RegisteredModTypes.Add(AUGCBasePawn::StaticClass()); + RegisteredModTypes.Add(AUGCBasePlayerController::StaticClass()); + RegisteredModTypes.Add(AUGCBaseWorldSettings::StaticClass()); + + } + + // PROPERTIES + UPROPERTY(BlueprintReadOnly, Category = "UGCKit") + TArray UGCPackages; + + UPROPERTY(BlueprintReadWrite, Category = "UGCKit") + TArray PackageBlacklist; + + UPROPERTY(BlueprintReadOnly, Category = "UGCKit|Modding") + TArray RegisteredModTypes; + + UPROPERTY(BlueprintReadOnly, Category = "UGCKit|Modding") + TArray PossibleModOverrides; + + UPROPERTY(BlueprintReadOnly, Category = "UGCKit|Modding") + TArray RegisteredOverrides; + + // This populates UGCPackages based on what is found in mounted pak files. If you're mounting new /Plugin paks at runtime, call this again to update UGCPacakges + UFUNCTION(BlueprintCallable, Category = "UGCKit") + bool FindUGCPackages(); + + // For querying assets in a specific package. + UFUNCTION(BlueprintCallable, Category = "UGCKit") + bool GetAssetsFromPackage(FName Package, TArray &Assets, bool bOnlyRegisteredModTypes); + + // Helper for Blueprints to use cached AssetData info to get an actual UClass. Mainly used in conjunction with ApplyOverrideForModClass + UFUNCTION(BlueprintCallable, Category = "UGCKit") + UClass * GetClassForAssetData(FAssetData Asset); + + // Applies entire package of mods + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + bool ApplyAllModsInPackage(FName Package); + + // Assigns overrides for just one mod class. + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + bool ApplyOverridesForModClass(UClass * ModClass); + + // A manual override assignment. Not reccommended for mod packages, but useful for bug UGC drops (100 gun pack, etc) + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + void AssignOverrideForBaseClass(UClass * OriginClass, UClass * OverrideClass); + + // Usually used with the above, this looks at all UGC Packages and provides a list of possible overrides (Give me all new guns) + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + void ShowAllPossibleOverridesForClass(UClass * ModClass, TArray &Overrides); + + // Used to clear the override from the registry. To clear all, loop through RegisterredOverrides, break the struct and run the Origin into this function. + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + bool ClearOverrideForClass(UClass *OriginClass); + + // Used in gameplay to look up what class is actually supposed to be spawned + UFUNCTION(BlueprintCallable, Category = "UGCKit|Modding") + UClass * GetOverrideForClass(UClass *OriginClass); + + + +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGC/SimpleUGC.Build.cs b/Plugins/SimpleUGC/Source/SimpleUGC/SimpleUGC.Build.cs new file mode 100644 index 0000000..60621c3 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGC/SimpleUGC.Build.cs @@ -0,0 +1,59 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class SimpleUGC : ModuleRules +{ + public SimpleUGC(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "AssetRegistry" + + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "AssetRegistry", + "Projects", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCCreator.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCCreator.cpp new file mode 100644 index 0000000..d7403a9 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCCreator.cpp @@ -0,0 +1,65 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +//#include "SimpleUGCEditorPrivatePCH.h" +#include "SimpleUGCCreator.h" + +#include "SimpleUGCPluginWizardDefinition.h" +#include "Widgets/Docking/SDockTab.h" + +// This depends on the Plugin Browser module to work correctly... +#include "IPluginBrowser.h" + + + +#define LOCTEXT_NAMESPACE "FSimpleUGCCreator" + +const FName FSimpleUGCCreator::SimpleUGCEditorPluginCreatorName("SimpleUGCPluginCreator"); + +FSimpleUGCCreator::FSimpleUGCCreator() +{ + RegisterTabSpawner(); +} + +FSimpleUGCCreator::~FSimpleUGCCreator() +{ + UnregisterTabSpawner(); +} + +void FSimpleUGCCreator::OpenNewPluginWizard(bool bSuppressErrors) const +{ + if (IPluginBrowser::IsAvailable()) + { + FGlobalTabmanager::Get()->InvokeTab(SimpleUGCEditorPluginCreatorName); + } + else if (!bSuppressErrors) + { + FMessageDialog::Open(EAppMsgType::Ok, + LOCTEXT("PluginBrowserDisabled", "Creating a game mod requires the use of the Plugin Browser, but it is currently disabled.")); + } +} + +void FSimpleUGCCreator::RegisterTabSpawner() +{ + FTabSpawnerEntry& Spawner = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(SimpleUGCEditorPluginCreatorName, + FOnSpawnTab::CreateRaw(this, &FSimpleUGCCreator::HandleSpawnPluginTab)); + + // Set a default size for this tab + FVector2D DefaultSize(800.0f, 500.0f); + FTabManager::RegisterDefaultTabWindowSize(SimpleUGCEditorPluginCreatorName, DefaultSize); + + Spawner.SetDisplayName(LOCTEXT("NewUGCTabHeader", "Create New UGC Package")); + Spawner.SetMenuType(ETabSpawnerMenuType::Hidden); +} + +void FSimpleUGCCreator::UnregisterTabSpawner() +{ + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(SimpleUGCEditorPluginCreatorName); +} + +TSharedRef FSimpleUGCCreator::HandleSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs) +{ + check(IPluginBrowser::IsAvailable()); + return IPluginBrowser::Get().SpawnPluginCreatorTab(SpawnTabArgs, MakeShared()); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditor.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditor.cpp new file mode 100644 index 0000000..ce43dde --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditor.cpp @@ -0,0 +1,130 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGCEditor.h" +#include "SimpleUGCEditorStyle.h" +#include "SimpleUGCEditorCommands.h" +#include "SimpleUGCCreator.h" +#include "Misc/MessageDialog.h" +#include "Framework/MultiBox/MultiBoxBuilder.h" + +#include "LevelEditor.h" + +static const FName SimpleUGCEditorTabName("SimpleUGCEditor"); + +#define LOCTEXT_NAMESPACE "FSimpleUGCEditorModule" + +void FSimpleUGCEditorModule::StartupModule() +{ + // This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module + + UGCCreator = MakeShared(); + UGCPackager = MakeShared(); + + FSimpleUGCEditorStyle::Initialize(); + FSimpleUGCEditorStyle::ReloadTextures(); + + FSimpleUGCEditorCommands::Register(); + + PluginCommands = MakeShareable(new FUICommandList); + + PluginCommands->MapAction( + FSimpleUGCEditorCommands::Get().CreateUGCAction, + FExecuteAction::CreateRaw(this, &FSimpleUGCEditorModule::CreateUGCButtonClicked), + FCanExecuteAction() + ); + + FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked("LevelEditor"); + // Add commands + { + FName MenuSection = "FileProject"; + FName ToolbarSection = "Misc"; + + // Add creator button to the menu + { + TSharedPtr MenuExtender = MakeShareable(new FExtender()); + MenuExtender->AddMenuExtension(MenuSection, EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FSimpleUGCEditorModule::AddUGCCreatorMenuExtension)); + + LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); + } + + // Add creator button to the toolbar + { + TSharedPtr ToolbarExtender = MakeShareable(new FExtender); + ToolbarExtender->AddToolBarExtension(ToolbarSection, EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FSimpleUGCEditorModule::AddUGCCreatorToolbarExtension)); + + LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); + } + + // Add packager button to the menu + { + TSharedPtr MenuExtender = MakeShareable(new FExtender()); + MenuExtender->AddMenuExtension(MenuSection, EExtensionHook::After, PluginCommands, FMenuExtensionDelegate::CreateRaw(this, &FSimpleUGCEditorModule::AddUGCPackagerMenuExtension)); + + LevelEditorModule.GetMenuExtensibilityManager()->AddExtender(MenuExtender); + } + + // Add packager button to the toolbar + { + TSharedPtr ToolbarExtender = MakeShareable(new FExtender); + ToolbarExtender->AddToolBarExtension(ToolbarSection, EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FSimpleUGCEditorModule::AddUGCPackagerToolbarExtension)); + + LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender); + } + } +} + +void FSimpleUGCEditorModule::ShutdownModule() +{ + // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, + // we call this function before unloading the module. + FSimpleUGCEditorStyle::Shutdown(); + + FSimpleUGCEditorCommands::Unregister(); + +} + +void FSimpleUGCEditorModule::CreateUGCButtonClicked() +{ + if (UGCCreator.IsValid()) + { + UGCCreator->OpenNewPluginWizard(); + } +} + +void FSimpleUGCEditorModule::AddUGCCreatorMenuExtension(FMenuBuilder& Builder) +{ + Builder.AddMenuEntry(FSimpleUGCEditorCommands::Get().CreateUGCAction); +} + +void FSimpleUGCEditorModule::AddUGCCreatorToolbarExtension(FToolBarBuilder& Builder) +{ + Builder.AddToolBarButton(FSimpleUGCEditorCommands::Get().CreateUGCAction); +} + +void FSimpleUGCEditorModule::AddUGCPackagerMenuExtension(FMenuBuilder& Builder) +{ + FSimpleUGCPackager* Packager = UGCPackager.Get(); + + Builder.AddSubMenu(LOCTEXT("PackageUGCMenu_Label", "Package UGC"), + LOCTEXT("PackageUGCMenu_Tooltip", "Share and distribute UGC"), + FNewMenuDelegate::CreateRaw(Packager, &FSimpleUGCPackager::GeneratePackagerMenuContent), + false, + FSlateIcon(FSimpleUGCEditorStyle::GetStyleSetName(), "SimpleUGCEditor.PackageUGCAction") + ); +} + +void FSimpleUGCEditorModule::AddUGCPackagerToolbarExtension(FToolBarBuilder& Builder) +{ + FSimpleUGCPackager* Packager = UGCPackager.Get(); + + Builder.AddComboButton(FUIAction(), + FOnGetContent::CreateSP(Packager, &FSimpleUGCPackager::GeneratePackagerComboButtonContent), + LOCTEXT("PackageUGC_Label", "Package UGC"), + LOCTEXT("PackageUGC_Tooltip", "Share and distribute UGC"), + FSlateIcon(FSimpleUGCEditorStyle::GetStyleSetName(), "SimpleUGCEditor.PackageUGCAction") + ); +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FSimpleUGCEditorModule, SimpleUGCEditor) diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorCommands.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorCommands.cpp new file mode 100644 index 0000000..846c5a3 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorCommands.cpp @@ -0,0 +1,51 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGCEditorCommands.h" +#include "Interfaces/IPluginManager.h" + +#define LOCTEXT_NAMESPACE "FSimpleUGCEditorModule" + +void FSimpleUGCEditorCommands::RegisterCommands() +{ + UI_COMMAND(CreateUGCAction, "Create UGC", "Create a new UGC package in a mod plugin", EUserInterfaceActionType::Button, FInputGesture()); + UI_COMMAND(PackageUGCAction, "Package UGC", "Share and distribute your UGC", EUserInterfaceActionType::Button, FInputGesture()); +} + +TArray> FSimpleUGCEditorCommands::RegisterUGCCommands(const TArray>& UGCList) const +{ + TArray> AvailableUGCActions; + AvailableUGCActions.Reserve(UGCList.Num()); + + FSimpleUGCEditorCommands* MutableThis = const_cast(this); + + for (int32 Index = 0; Index < UGCList.Num(); ++Index) + { + AvailableUGCActions.Add(TSharedPtr()); + TSharedRef UGC = UGCList[Index]; + + FString CommandName = "UGCEditorUGC_" + UGC->GetName(); + + FUICommandInfo::MakeCommandInfo(MutableThis->AsShared(), + AvailableUGCActions[Index], + FName(*CommandName), + FText::FromString(UGC->GetName()), + FText::FromString(UGC->GetBaseDir()), + FSlateIcon(), + EUserInterfaceActionType::Button, + FInputGesture()); + } + + return AvailableUGCActions; +} + +void FSimpleUGCEditorCommands::UnregisterUGCCommands(TArray>& UICommands) const +{ + FSimpleUGCEditorCommands* MutableThis = const_cast(this); + + for (TSharedPtr Command : UICommands) + { + FUICommandInfo::UnregisterCommandInfo(MutableThis->AsShared(), Command.ToSharedRef()); + } +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorStyle.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorStyle.cpp new file mode 100644 index 0000000..309261a --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorStyle.cpp @@ -0,0 +1,72 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGCEditorStyle.h" +#include "SimpleUGCEditor.h" +#include "Framework/Application/SlateApplication.h" +#include "Styling/SlateStyleRegistry.h" +#include "Slate/SlateGameResources.h" +#include "Interfaces/IPluginManager.h" + +TSharedPtr< FSlateStyleSet > FSimpleUGCEditorStyle::StyleInstance = NULL; + +void FSimpleUGCEditorStyle::Initialize() +{ + if (!StyleInstance.IsValid()) + { + StyleInstance = Create(); + FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance); + } +} + +void FSimpleUGCEditorStyle::Shutdown() +{ + FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance); + ensure(StyleInstance.IsUnique()); + StyleInstance.Reset(); +} + +FName FSimpleUGCEditorStyle::GetStyleSetName() +{ + static FName StyleSetName(TEXT("SimpleUGCEditorStyle")); + return StyleSetName; +} + +#define IMAGE_BRUSH( RelativePath, ... ) FSlateImageBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define BOX_BRUSH( RelativePath, ... ) FSlateBoxBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define BORDER_BRUSH( RelativePath, ... ) FSlateBorderBrush( Style->RootToContentDir( RelativePath, TEXT(".png") ), __VA_ARGS__ ) +#define TTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".ttf") ), __VA_ARGS__ ) +#define OTF_FONT( RelativePath, ... ) FSlateFontInfo( Style->RootToContentDir( RelativePath, TEXT(".otf") ), __VA_ARGS__ ) + +const FVector2D Icon16x16(16.0f, 16.0f); +const FVector2D Icon20x20(20.0f, 20.0f); +const FVector2D Icon40x40(40.0f, 40.0f); + +TSharedRef< FSlateStyleSet > FSimpleUGCEditorStyle::Create() +{ + TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("SimpleUGCEditorStyle")); + Style->SetContentRoot(IPluginManager::Get().FindPlugin("SimpleUGC")->GetBaseDir() / TEXT("Resources")); + + Style->Set("SimpleUGCEditor.PackageUGCAction", new IMAGE_BRUSH(TEXT("PackageUGC_64x"), Icon40x40)); + Style->Set("SimpleUGCEditor.CreateUGCAction", new IMAGE_BRUSH(TEXT("CreateUGC_64x"), Icon40x40)); + + return Style; +} + +#undef IMAGE_BRUSH +#undef BOX_BRUSH +#undef BORDER_BRUSH +#undef TTF_FONT +#undef OTF_FONT + +void FSimpleUGCEditorStyle::ReloadTextures() +{ + if (FSlateApplication::IsInitialized()) + { + FSlateApplication::Get().GetRenderer()->ReloadTextureResources(); + } +} + +const ISlateStyle& FSimpleUGCEditorStyle::Get() +{ + return *StyleInstance; +} diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPackager.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPackager.cpp new file mode 100644 index 0000000..c7bc2db --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPackager.cpp @@ -0,0 +1,205 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGCPackager.h" +#include "SimpleUGCEditor.h" +#include "SimpleUGCEditorCommands.h" +#include "SimpleUGCEditorStyle.h" +#include "Editor.h" +#include "Widgets/SWindow.h" +#include "Widgets/SWidget.h" +#include "Interfaces/IPluginManager.h" +#include "Developer/DesktopPlatform/Public/DesktopPlatformModule.h" +#include "Editor/UATHelper/Public/IUATHelperModule.h" +#include "Editor/MainFrame/Public/Interfaces/IMainFrameModule.h" + +#include "FileHelpers.h" +#include "Misc/PackageName.h" + +#define LOCTEXT_NAMESPACE "SimpleUGCPackager" + +FSimpleUGCPackager::FSimpleUGCPackager() +{ +} + +FSimpleUGCPackager::~FSimpleUGCPackager() +{ +} + +void FSimpleUGCPackager::OpenPluginPackager(TSharedRef Plugin) +{ + IDesktopPlatform* DesktopPlatform = FDesktopPlatformModule::Get(); + + FString DefaultDirectory = FPaths::ConvertRelativePathToFull(Plugin->GetBaseDir()); + FString OutputDirectory; + + // Prompt the user to save all dirty packages. We'll ensure that if any packages from the mod that the user wants to + // package are dirty that they will not be able to save them. + + if (!IsAllContentSaved(Plugin)) + { + FEditorFileUtils::SaveDirtyPackages( true, true, true); + } + + if (IsAllContentSaved(Plugin)) + { + void* ParentWindowWindowHandle = nullptr; + IMainFrameModule& MainFrameModule = FModuleManager::LoadModuleChecked(TEXT("MainFrame")); + const TSharedPtr& MainFrameParentWindow = MainFrameModule.GetParentWindow(); + if (MainFrameParentWindow.IsValid() && MainFrameParentWindow->GetNativeWindow().IsValid()) + { + ParentWindowWindowHandle = MainFrameParentWindow->GetNativeWindow()->GetOSWindowHandle(); + } + + if (DesktopPlatform->OpenDirectoryDialog(ParentWindowWindowHandle, LOCTEXT("SelectOutputFolderTitle", "Select UGC output directory:").ToString(), DefaultDirectory, OutputDirectory)) + { + PackagePlugin(Plugin, OutputDirectory); + } + } + else + { + FText PackageModError = FText::Format(LOCTEXT("PackageUGCError_UnsavedContent", "You must save all assets in {0} before you can share it."), + FText::FromString(Plugin->GetName())); + + FMessageDialog::Open(EAppMsgType::Ok, PackageModError); + } +} + +bool FSimpleUGCPackager::IsAllContentSaved(TSharedRef Plugin) +{ + bool bAllContentSaved = true; + + TArray UnsavedPackages; + FEditorFileUtils::GetDirtyContentPackages(UnsavedPackages); + FEditorFileUtils::GetDirtyWorldPackages(UnsavedPackages); + + if (UnsavedPackages.Num() > 0) + { + FString PluginBaseDir = Plugin->GetBaseDir(); + + for (UPackage* Package : UnsavedPackages) + { + FString PackageFilename; + if (FPackageName::TryConvertLongPackageNameToFilename(Package->GetName(), PackageFilename)) + { + if (PackageFilename.Find(PluginBaseDir) == 0) + { + bAllContentSaved = false; + break; + } + } + } + } + + return bAllContentSaved; +} + +void FSimpleUGCPackager::PackagePlugin(TSharedRef Plugin, const FString& OutputDirectory) +{ +#if PLATFORM_WINDOWS + FText PlatformName = LOCTEXT("PlatformName_Windows", "Windows"); +#elif PLATFORM_MAC + FText PlatformName = LOCTEXT("PlatformName_Mac", "Mac"); +#elif PLATFORM_LINUX + FText PlatformName = LOCTEXT("PlatformName_Linux", "Linux"); +#else + FText PlatformName = LOCTEXT("PlatformName_Desktop", "Desktop"); +#endif + + // Hard coded here for simplicity. You will probably want to read this from an ini file + FString ReleaseVersion = TEXT("UGCExampleGame_v1"); + + FString CommandLine = FString::Printf(TEXT("PackageUGC -Project=\"%s\" -PluginPath=\"%s\" -basedonreleaseversion=\"%s\" -StagingDirectory=\"%s\" -nocompile"), + *FPaths::ConvertRelativePathToFull(FPaths::GetProjectFilePath()), + *FPaths::ConvertRelativePathToFull(Plugin->GetDescriptorFileName()), + *ReleaseVersion, + *OutputDirectory); + + FText PackagingText = FText::Format(LOCTEXT("SimpleUGCEditor_PackagePluginTaskName", "Packaging {0}"), FText::FromString(Plugin->GetName())); + + FString FriendlyName = Plugin->GetDescriptor().FriendlyName; + IUATHelperModule::Get().CreateUatTask(CommandLine, PlatformName, PackagingText, + PackagingText, FSimpleUGCEditorStyle::Get().GetBrush(TEXT("SimpleUGCEditor.PackageUGCAction")), + [ReleaseVersion, PlatformName, FriendlyName](FString TaskResult, double TimeSec) {}); +} + +void FSimpleUGCPackager::FindAvailableGameMods(TArray>& OutAvailableGameMods) +{ + OutAvailableGameMods.Empty(); + + // Find available game mods from the list of discovered plugins + + for (TSharedRef Plugin : IPluginManager::Get().GetDiscoveredPlugins()) + { + // All game project plugins that are marked as mods are valid + if (Plugin->GetLoadedFrom() == EPluginLoadedFrom::Project && Plugin->GetType() == EPluginType::Mod) + { + UE_LOG(LogTemp, Display, TEXT("Adding %s"), *Plugin->GetName()); + OutAvailableGameMods.AddUnique(Plugin); + } + } +} + +void FSimpleUGCPackager::GeneratePackagerMenuContent_Internal(class FMenuBuilder& MenuBuilder, const TArray>& Commands) +{ + for (TSharedPtr Command : Commands) + { + MenuBuilder.AddMenuEntry(Command, NAME_None, TAttribute(), TAttribute(), FSlateIcon(FSimpleUGCEditorStyle::GetStyleSetName(), "SimpleUGCEditor.Folder")); + } +} + +void FSimpleUGCPackager::GeneratePackagerMenuContent(class FMenuBuilder& MenuBuilder) +{ + TArray> AvailableGameMods; + FindAvailableGameMods(AvailableGameMods); + + TArray> Commands; + + GeneratePackagerMenuContent_Internal(MenuBuilder, UGCCommands); +} + +TSharedRef FSimpleUGCPackager::GeneratePackagerComboButtonContent() +{ + // Regenerate the game mod commands + TArray> AvailableGameMods; + FindAvailableGameMods(AvailableGameMods); + + GetAvailableUGCCommands(AvailableGameMods); + + // Regenerate the action list + TSharedPtr GameModActionsList = MakeShareable(new FUICommandList); + + for (int32 Index = 0; Index < UGCCommands.Num(); ++Index) + { + GameModActionsList->MapAction( + UGCCommands[Index], + FExecuteAction::CreateRaw(this, &FSimpleUGCPackager::OpenPluginPackager, AvailableGameMods[Index]), + FCanExecuteAction() + ); + } + + // Show the drop down menu + const bool bShouldCloseWindowAfterMenuSelection = true; + FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GameModActionsList); + + MenuBuilder.BeginSection(NAME_None, LOCTEXT("PackageUGC", "Share...")); + { + GeneratePackagerMenuContent_Internal(MenuBuilder, UGCCommands); + } + MenuBuilder.EndSection(); + + return MenuBuilder.MakeWidget(); +} + +void FSimpleUGCPackager::GetAvailableUGCCommands(const TArray>& AvailableUGC) +{ + if (UGCCommands.Num() > 0) + { + // Unregister UI Commands + FSimpleUGCEditorCommands::Get().UnregisterUGCCommands(UGCCommands); + } + UGCCommands.Empty(AvailableUGC.Num()); + + UGCCommands = FSimpleUGCEditorCommands::Get().RegisterUGCCommands(AvailableUGC); +} + +#undef LOCTEXT_NAMESPACE \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPluginWizardDefinition.cpp b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPluginWizardDefinition.cpp new file mode 100644 index 0000000..53abeb4 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPluginWizardDefinition.cpp @@ -0,0 +1,214 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "SimpleUGCPluginWizardDefinition.h" +#include "ContentBrowserModule.h" +#include "EngineAnalytics.h" +#include "Interfaces/IPluginManager.h" +#include "IContentBrowserSingleton.h" +#include "Algo/Transform.h" +#include "SlateBasics.h" +#include "SourceCodeNavigation.h" + +#define LOCTEXT_NAMESPACE "SimpleUGCPluginWizard" + +FSimpleUGCPluginWizardDefinition::FSimpleUGCPluginWizardDefinition() +{ + PluginBaseDir = IPluginManager::Get().FindPlugin(TEXT("SimpleUGC"))->GetBaseDir(); + // Find the Content Only Template that ships with the plugin. + // Download the Robo Recall Mod Kit and check the Plugins/OdinEditor code for how to build and use your own UGC templates from your game content + BackingTemplate = MakeShareable(new FPluginTemplateDescription(FText(), FText(), TEXT("BaseTemplate"), true, EHostType::Runtime)); + BackingTemplatePath = PluginBaseDir / TEXT("Templates") / BackingTemplate->OnDiskPath; +} + +const TArray>& FSimpleUGCPluginWizardDefinition::GetTemplatesSource() const +{ + return TemplateDefinitions; +} + +void FSimpleUGCPluginWizardDefinition::OnTemplateSelectionChanged(TArray> InSelectedItems, ESelectInfo::Type SelectInfo) +{ + SelectedTemplates = InSelectedItems; +} + +TArray> FSimpleUGCPluginWizardDefinition::GetSelectedTemplates() const +{ + TArray> SelectedTemplatePtrs; + + for (TSharedRef Ref : SelectedTemplates) + { + SelectedTemplatePtrs.Add(Ref); + } + + return SelectedTemplatePtrs; +} + +void FSimpleUGCPluginWizardDefinition::ClearTemplateSelection() +{ + SelectedTemplates.Empty(); +} + +bool FSimpleUGCPluginWizardDefinition::HasValidTemplateSelection() const +{ + // A mod should be created even if no templates are actually selected + return true; +} + +bool FSimpleUGCPluginWizardDefinition::CanContainContent() const +{ + bool bHasContent = SelectedTemplates.Num() == 0; // if no templates are selected, by default it is a content mod + + if (!bHasContent) + { + for (TSharedPtr Template : SelectedTemplates) + { + // If at least one module can contain content, it's a content mod. Otherwise, it's a pure code mod. + if (Template->bCanContainContent) + { + bHasContent = true; + break; + } + } + } + + return bHasContent; +} + +bool FSimpleUGCPluginWizardDefinition::HasModules() const +{ + bool bHasModules = false; + + for (TSharedPtr Template : SelectedTemplates) + { + if (FPaths::DirectoryExists(PluginBaseDir / TEXT("Templates") / Template->OnDiskPath / TEXT("Source"))) + { + bHasModules = true; + break; + } + } + + return bHasModules; +} + +bool FSimpleUGCPluginWizardDefinition::IsMod() const +{ + return true; +} + +void FSimpleUGCPluginWizardDefinition::OnShowOnStartupCheckboxChanged(ECheckBoxState CheckBoxState) +{ +} + +ECheckBoxState FSimpleUGCPluginWizardDefinition::GetShowOnStartupCheckBoxState() const +{ + return ECheckBoxState(); +} + +FText FSimpleUGCPluginWizardDefinition::GetInstructions() const +{ + return LOCTEXT("CreateNewUGCPanel", "Give your new UGC package a name and Click 'Create Mod' to make a new content only UGC package."); +} + +TSharedPtr FSimpleUGCPluginWizardDefinition::GetCustomHeaderWidget() +{ + if ( !CustomHeaderWidget.IsValid() ) + { + FString IconPath; + GetPluginIconPath(IconPath); + + const FName BrushName(*IconPath); + const FIntPoint Size = FSlateApplication::Get().GetRenderer()->GenerateDynamicImageResource(BrushName); + if ((Size.X > 0) && (Size.Y > 0)) + { + IconBrush = MakeShareable(new FSlateDynamicImageBrush(BrushName, FVector2D(Size.X, Size.Y))); + } + + CustomHeaderWidget = SNew(SHorizontalBox) + // Header image + + SHorizontalBox::Slot() + .AutoWidth() + .Padding(4.0f) + [ + SNew(SBox) + .WidthOverride(80.0f) + .HeightOverride(80.0f) + [ + SNew(SImage) + .Image(IconBrush.IsValid() ? IconBrush.Get() : nullptr) + ] + ]; + } + + return CustomHeaderWidget; +} + +bool FSimpleUGCPluginWizardDefinition::GetPluginIconPath(FString& OutIconPath) const +{ + // Replace this file with your own 128x128 image if desired. + OutIconPath = BackingTemplatePath / TEXT("Resources/Icon128.png"); + return false; +} + +bool FSimpleUGCPluginWizardDefinition::GetTemplateIconPath(TSharedRef InTemplate, FString& OutIconPath) const +{ + FString TemplateName = InTemplate->Name.ToString(); + + OutIconPath = PluginBaseDir / TEXT("Resources"); + + if (TemplateToIconMap.Contains(TemplateName)) + { + OutIconPath /= TemplateToIconMap[TemplateName]; + } + else + { + // Couldn't find a suitable icon to use for this template, so use the default one instead + OutIconPath /= TEXT("Icon128.png"); + } + + return false; +} + +FString FSimpleUGCPluginWizardDefinition::GetPluginFolderPath() const +{ + return BackingTemplatePath; +} + +EHostType::Type FSimpleUGCPluginWizardDefinition::GetPluginModuleDescriptor() const +{ + return BackingTemplate->ModuleDescriptorType; +} + +ELoadingPhase::Type FSimpleUGCPluginWizardDefinition::GetPluginLoadingPhase() const +{ + return BackingTemplate->LoadingPhase; +} + +TArray FSimpleUGCPluginWizardDefinition::GetFoldersForSelection() const +{ + TArray SelectedFolders; + SelectedFolders.Add(BackingTemplatePath); // This will always be a part of the mod plugin + + for (TSharedPtr Template : SelectedTemplates) + { + SelectedFolders.AddUnique(PluginBaseDir / TEXT("Templates") / Template->OnDiskPath); + } + + return SelectedFolders; +} + +void FSimpleUGCPluginWizardDefinition::PluginCreated(const FString& PluginName, bool bWasSuccessful) const +{ + // Override Category to UGC + if (bWasSuccessful) + { + TSharedPtr Plugin = IPluginManager::Get().FindPlugin(PluginName); + if (Plugin != nullptr) + { + FPluginDescriptor Desc = Plugin->GetDescriptor(); + Desc.Category = "UGC"; + FText UpdateFailureText; + Plugin->UpdateDescriptor(Desc, UpdateFailureText); + } + } +} + +#undef LOCTEXT_NAMESPACE diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCCreator.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCCreator.h new file mode 100644 index 0000000..43e41ea --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCCreator.h @@ -0,0 +1,33 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +class FSimpleUGCPluginWizardDefinition; +class SDockTab; + +class FSimpleUGCCreator : public TSharedFromThis +{ +public: + + FSimpleUGCCreator(); + ~FSimpleUGCCreator(); + + /** + * Opens the mod creator wizard. + * @param bSuppressErrors If false, a dialog will be shown if the wizard cannot be opened for whatever reason + */ + void OpenNewPluginWizard(bool bSuppressErrors = false) const; + + /** The name to use when creating the tab for the tab spawner */ + static const FName SimpleUGCEditorPluginCreatorName; + +private: + /** Registers a nomad tab spawner that will create the mod wizard */ + void RegisterTabSpawner(); + + /** Unregisters the nomad tab spawner */ + void UnregisterTabSpawner(); + + /** Spawns the tab that hosts the mod creator wizard widget */ + TSharedRef HandleSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs); +}; \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditor.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditor.h new file mode 100644 index 0000000..574e7d9 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditor.h @@ -0,0 +1,41 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Slate.h" +#include "SimpleUGCPackager.h" +#include "Modules\ModuleManager.h" + +class FToolBarBuilder; +class FMenuBuilder; + +class FSimpleUGCEditorModule : public IModuleInterface +{ +public: + + /** IModuleInterface implementation */ + virtual void StartupModule() override; + virtual void ShutdownModule() override; + + // When the Create Button is clicked + void CreateUGCButtonClicked(); + + /** Adds the plugin creator as a new toolbar button */ + void AddUGCCreatorToolbarExtension(FToolBarBuilder& Builder); + + /** Adds the plugin creator as a new menu option */ + void AddUGCCreatorMenuExtension(FMenuBuilder& Builder); + + /** Adds the plugin packager as a new toolbar button */ + void AddUGCPackagerToolbarExtension(FToolBarBuilder& Builder); + + /** Adds the plugin packager as a new menu option */ + void AddUGCPackagerMenuExtension(FMenuBuilder& Builder); + +private: + + TSharedPtr UGCCreator; + TSharedPtr UGCPackager; + TSharedPtr PluginCommands; +}; \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorCommands.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorCommands.h new file mode 100644 index 0000000..247340c --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorCommands.h @@ -0,0 +1,27 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Framework/Commands/Commands.h" +#include "SimpleUGCEditorStyle.h" + +class FSimpleUGCEditorCommands : public TCommands +{ +public: + + FSimpleUGCEditorCommands() + : TCommands(TEXT("SimpleUGCEditor"), NSLOCTEXT("Contexts", "SimpleUGCEditor", "SimpleUGCEditor Plugin"), NAME_None, FSimpleUGCEditorStyle::GetStyleSetName()) + { + } + + // TCommands<> interface + virtual void RegisterCommands() override; + + TArray> RegisterUGCCommands(const TArray>& UGCList) const; + void UnregisterUGCCommands(TArray>& UICommands) const; + +public: + TSharedPtr< FUICommandInfo > CreateUGCAction; + TSharedPtr< FUICommandInfo > PackageUGCAction; +}; \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorStyle.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorStyle.h new file mode 100644 index 0000000..46f4843 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCEditorStyle.h @@ -0,0 +1,31 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "Styling/SlateStyle.h" + +class FSimpleUGCEditorStyle +{ +public: + + static void Initialize(); + + static void Shutdown(); + + /** reloads textures used by slate renderer */ + static void ReloadTextures(); + + /** @return The Slate style set for the Shooter game */ + static const ISlateStyle& Get(); + + static FName GetStyleSetName(); + +private: + + static TSharedRef< class FSlateStyleSet > Create(); + +private: + + static TSharedPtr< class FSlateStyleSet > StyleInstance; +}; \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPackager.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPackager.h new file mode 100644 index 0000000..f287732 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPackager.h @@ -0,0 +1,49 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +struct FSimpleUGCCommand +{ + TSharedPtr PluginInfo; + TSharedPtr CommandInfo; +}; + +class FSimpleUGCPackager : public TSharedFromThis +{ +public: + FSimpleUGCPackager(); + ~FSimpleUGCPackager(); + + void OpenPluginPackager(TSharedRef Plugin); + + void PackagePlugin(TSharedRef Plugin, const FString& OutputDirectory); + + /** Generates submenu content for the plugin packager command */ + void GeneratePackagerMenuContent(class FMenuBuilder& MenuBuilder); + + /** Generates the menu content for the plugin packager toolbar button */ + TSharedRef GeneratePackagerComboButtonContent(); + +private: + /** Gets all available game mod plugin packages */ + void FindAvailableGameMods(TArray>& OutAvailableGameMods); + + /** Gets all available game mod plugins and registers command info for them */ + void GetAvailableUGCCommands(const TArray>& AvailableUGC); + + /** Generates menu content for the supplied set of commands */ + void GeneratePackagerMenuContent_Internal(class FMenuBuilder& MenuBuilder, const TArray>& Commands); + + /** + * Checks if a plugin has any unsaved content + * + * @param Plugin The plugin to check for unsaved content + * @return True if all mod content has been saved, false otherwise + */ + bool IsAllContentSaved(TSharedRef Plugin); + +private: + TArray> UGCCommands; +}; diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPluginWizardDefinition.h b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPluginWizardDefinition.h new file mode 100644 index 0000000..9543a27 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/Public/SimpleUGCPluginWizardDefinition.h @@ -0,0 +1,70 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +// Depends on code from the plugin browser to work correctly +#include "../../../../Plugins/Editor/PluginBrowser/Source/PluginBrowser/Public/IPluginWizardDefinition.h" + +class FSimpleUGCPluginWizardDefinition : public IPluginWizardDefinition +{ +public: + FSimpleUGCPluginWizardDefinition(); + + // Begin IPluginWizardDefinition interface + virtual const TArray>& GetTemplatesSource() const override; + virtual void OnTemplateSelectionChanged(TArray> InSelectedItems, ESelectInfo::Type SelectInfo) override; + virtual TArray> GetSelectedTemplates() const override; + virtual void ClearTemplateSelection() override; + virtual bool HasValidTemplateSelection() const override; + + virtual ESelectionMode::Type GetSelectionMode() const override { return ESelectionMode::Multi; } + virtual bool AllowsEnginePlugins() const override { return false; } + virtual bool CanShowOnStartup() const override { return true; } + virtual bool CanContainContent() const override; + virtual bool HasModules() const override; + virtual bool IsMod() const override; + virtual void OnShowOnStartupCheckboxChanged(ECheckBoxState CheckBoxState) override; + virtual ECheckBoxState GetShowOnStartupCheckBoxState() const override; + virtual TSharedPtr GetCustomHeaderWidget() override; + virtual FText GetInstructions() const override; + + virtual bool GetPluginIconPath(FString& OutIconPath) const override; + virtual EHostType::Type GetPluginModuleDescriptor() const override; + virtual ELoadingPhase::Type GetPluginLoadingPhase() const override; + virtual bool GetTemplateIconPath(TSharedRef InTemplate, FString& OutIconPath) const override; + virtual FString GetPluginFolderPath() const override; + virtual TArray GetFoldersForSelection() const override; + virtual void PluginCreated(const FString& PluginName, bool bWasSuccessful) const override; + // End IPluginWizardDefinition interface + +private: + /** The available templates for the mod. They should function as mixins to the backing template */ + TArray> TemplateDefinitions; + + /** The content that will be used when creating the mod */ + TArray> SelectedTemplates; + + /** The base directory of this plugin. Used for accessing the templates used to create mods */ + FString PluginBaseDir; + + /** + * The path to the template that ultimately serves as the template that the mod will be based on. It's not intended to be + * selected directly, but rather other templates will act as mixins to define what content will exist in the plugin. + */ + FString BackingTemplatePath; + + /** The backing template definition for the mod. This should never be directly selectable */ + TSharedPtr BackingTemplate; + + /** The base code template definition. Can be directly selectable to create an "empty" code mod, but should be included with any code mod selection */ + TSharedPtr BaseCodeTemplate; + + /** Maps a specific template to a specific icon file */ + TMap TemplateToIconMap; + + /** Brush used for drawing the custom header widget */ + TSharedPtr IconBrush; + + /** Custom header widget */ + TSharedPtr CustomHeaderWidget; +}; \ No newline at end of file diff --git a/Plugins/SimpleUGC/Source/SimpleUGCEditor/SimpleUGCEditor.Build.cs b/Plugins/SimpleUGC/Source/SimpleUGCEditor/SimpleUGCEditor.Build.cs new file mode 100644 index 0000000..b2168f5 --- /dev/null +++ b/Plugins/SimpleUGC/Source/SimpleUGCEditor/SimpleUGCEditor.Build.cs @@ -0,0 +1,59 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class SimpleUGCEditor : ModuleRules +{ + public SimpleUGCEditor(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicIncludePaths.AddRange( + new string[] { + // ... add public include paths required here ... + } + ); + + + PrivateIncludePaths.AddRange( + new string[] { + // ... add other private include paths required here ... + } + ); + + + PublicDependencyModuleNames.AddRange( + new string[] + { + "Core", + "PluginBrowser", + // ... add other public dependencies that you statically link with here ... + } + ); + + + PrivateDependencyModuleNames.AddRange( + new string[] + { + "Projects", + "InputCore", + "UnrealEd", + "LevelEditor", + "CoreUObject", + "Engine", + "PluginBrowser", + "Slate", + "SlateCore", + // ... add private dependencies that you statically link with here ... + } + ); + + + DynamicallyLoadedModuleNames.AddRange( + new string[] + { + // ... add any modules that your module loads dynamically here ... + } + ); + } +} diff --git a/Plugins/SimpleUGC/Templates/BaseTemplate/Resources/Icon128.png b/Plugins/SimpleUGC/Templates/BaseTemplate/Resources/Icon128.png new file mode 100644 index 0000000..85b65e7 Binary files /dev/null and b/Plugins/SimpleUGC/Templates/BaseTemplate/Resources/Icon128.png differ diff --git a/Releases/UGCExampleGame_v1/PackagedExe.zip b/Releases/UGCExampleGame_v1/PackagedExe.zip new file mode 100644 index 0000000..46f046d Binary files /dev/null and b/Releases/UGCExampleGame_v1/PackagedExe.zip differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/AssetRegistry.bin b/Releases/UGCExampleGame_v1/WindowsNoEditor/AssetRegistry.bin new file mode 100644 index 0000000..3fff4f0 Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/AssetRegistry.bin differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/BulkDataInfo.ubulkmanifest b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/BulkDataInfo.ubulkmanifest new file mode 100644 index 0000000..dad37ad Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/BulkDataInfo.ubulkmanifest differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/CookedIniVersion.txt b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/CookedIniVersion.txt new file mode 100644 index 0000000..b663383 Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/CookedIniVersion.txt differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/Crypto.json b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/Crypto.json new file mode 100644 index 0000000..55b9800 --- /dev/null +++ b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/Crypto.json @@ -0,0 +1 @@ +{"$types":{"UnrealBuildTool.EncryptionAndSigning+CryptoSettings, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"1","UnrealBuildTool.EncryptionAndSigning+EncryptionKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null":"2"},"$type":"1","EncryptionKey":{"$type":"2","Name":null,"Guid":null,"Key":null},"SigningKey":null,"bEnablePakSigning":false,"bEnablePakIndexEncryption":false,"bEnablePakIniEncryption":false,"bEnablePakUAssetEncryption":false,"bEnablePakFullAssetEncryption":false,"bDataCryptoRequired":true,"PakEncryptionRequired":true,"PakSigningRequired":true,"SecondaryEncryptionKeys":null} \ No newline at end of file diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/DevelopmentAssetRegistry.bin b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/DevelopmentAssetRegistry.bin new file mode 100644 index 0000000..c4d8756 Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/DevelopmentAssetRegistry.bin differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-Global-PCD3D_SM5.ushaderbytecode b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-Global-PCD3D_SM5.ushaderbytecode new file mode 100644 index 0000000..e69d9f7 Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-Global-PCD3D_SM5.ushaderbytecode differ diff --git a/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-UGCExample-PCD3D_SM5.ushaderbytecode b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-UGCExample-PCD3D_SM5.ushaderbytecode new file mode 100644 index 0000000..276f3a6 Binary files /dev/null and b/Releases/UGCExampleGame_v1/WindowsNoEditor/Metadata/ShaderLibrarySource/ShaderArchive-UGCExample-PCD3D_SM5.ushaderbytecode differ diff --git a/Source/UGCExample.Target.cs b/Source/UGCExample.Target.cs new file mode 100644 index 0000000..41965d9 --- /dev/null +++ b/Source/UGCExample.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class UGCExampleTarget : TargetRules +{ + public UGCExampleTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Game; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "UGCExample" } ); + } +} diff --git a/Source/UGCExample/UGCExample.Build.cs b/Source/UGCExample/UGCExample.Build.cs new file mode 100644 index 0000000..53380d6 --- /dev/null +++ b/Source/UGCExample/UGCExample.Build.cs @@ -0,0 +1,23 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; + +public class UGCExample : ModuleRules +{ + public UGCExample(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + + PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); + + PrivateDependencyModuleNames.AddRange(new string[] { }); + + // Uncomment if you are using Slate UI + // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); + + // Uncomment if you are using online features + // PrivateDependencyModuleNames.Add("OnlineSubsystem"); + + // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true + } +} diff --git a/Source/UGCExample/UGCExample.cpp b/Source/UGCExample/UGCExample.cpp new file mode 100644 index 0000000..e2148da --- /dev/null +++ b/Source/UGCExample/UGCExample.cpp @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "UGCExample.h" +#include "Modules/ModuleManager.h" + +IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, UGCExample, "UGCExample" ); diff --git a/Source/UGCExample/UGCExample.h b/Source/UGCExample/UGCExample.h new file mode 100644 index 0000000..677c8e2 --- /dev/null +++ b/Source/UGCExample/UGCExample.h @@ -0,0 +1,6 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + diff --git a/Source/UGCExampleEditor.Target.cs b/Source/UGCExampleEditor.Target.cs new file mode 100644 index 0000000..c7ba124 --- /dev/null +++ b/Source/UGCExampleEditor.Target.cs @@ -0,0 +1,14 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +using UnrealBuildTool; +using System.Collections.Generic; + +public class UGCExampleEditorTarget : TargetRules +{ + public UGCExampleEditorTarget( TargetInfo Target) : base(Target) + { + Type = TargetType.Editor; + DefaultBuildSettings = BuildSettingsVersion.V2; + ExtraModuleNames.AddRange( new string[] { "UGCExample" } ); + } +} diff --git a/UGCExample.uproject b/UGCExample.uproject new file mode 100644 index 0000000..70f4826 --- /dev/null +++ b/UGCExample.uproject @@ -0,0 +1,13 @@ +{ + "FileVersion": 3, + "EngineAssociation": "", + "Category": "", + "Description": "", + "Modules": [ + { + "Name": "UGCExample", + "Type": "Runtime", + "LoadingPhase": "Default" + } + ] +} \ No newline at end of file -- cgit v1.2.3