aboutsummaryrefslogtreecommitdiff
path: root/Plugins/SimpleUGC/Source/SimpleUGCEditor/Private
diff options
context:
space:
mode:
authorivey <[email protected]>2020-06-15 15:54:16 -0400
committerivey <[email protected]>2020-06-15 15:54:16 -0400
commitd5310c3455f9849243b7b950deb4e910aa1f24dd (patch)
tree994a81eec10538d2a1efd9ed78469a249ff086f2 /Plugins/SimpleUGC/Source/SimpleUGCEditor/Private
parentUpdated image paths. (diff)
downloadugcexample-d5310c3455f9849243b7b950deb4e910aa1f24dd.tar.xz
ugcexample-d5310c3455f9849243b7b950deb4e910aa1f24dd.zip
Initial commit of the UGCExample Project
Diffstat (limited to 'Plugins/SimpleUGC/Source/SimpleUGCEditor/Private')
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCCreator.cpp65
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditor.cpp130
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorCommands.cpp51
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCEditorStyle.cpp72
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPackager.cpp205
-rw-r--r--Plugins/SimpleUGC/Source/SimpleUGCEditor/Private/SimpleUGCPluginWizardDefinition.cpp214
6 files changed, 737 insertions, 0 deletions
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<SDockTab> FSimpleUGCCreator::HandleSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
+{
+ check(IPluginBrowser::IsAvailable());
+ return IPluginBrowser::Get().SpawnPluginCreatorTab(SpawnTabArgs, MakeShared<FSimpleUGCPluginWizardDefinition>());
+}
+
+#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<FSimpleUGCCreator>();
+ UGCPackager = MakeShared<FSimpleUGCPackager>();
+
+ 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<FLevelEditorModule>("LevelEditor");
+ // Add commands
+ {
+ FName MenuSection = "FileProject";
+ FName ToolbarSection = "Misc";
+
+ // Add creator button to the menu
+ {
+ TSharedPtr<FExtender> 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<FExtender> 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<FExtender> 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<FExtender> 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<TSharedPtr<FUICommandInfo>> FSimpleUGCEditorCommands::RegisterUGCCommands(const TArray<TSharedRef<class IPlugin>>& UGCList) const
+{
+ TArray<TSharedPtr<FUICommandInfo>> AvailableUGCActions;
+ AvailableUGCActions.Reserve(UGCList.Num());
+
+ FSimpleUGCEditorCommands* MutableThis = const_cast<FSimpleUGCEditorCommands*>(this);
+
+ for (int32 Index = 0; Index < UGCList.Num(); ++Index)
+ {
+ AvailableUGCActions.Add(TSharedPtr<FUICommandInfo>());
+ TSharedRef<IPlugin> 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<TSharedPtr<FUICommandInfo>>& UICommands) const
+{
+ FSimpleUGCEditorCommands* MutableThis = const_cast<FSimpleUGCEditorCommands*>(this);
+
+ for (TSharedPtr<FUICommandInfo> 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<IPlugin> 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<IMainFrameModule>(TEXT("MainFrame"));
+ const TSharedPtr<SWindow>& 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<IPlugin> Plugin)
+{
+ bool bAllContentSaved = true;
+
+ TArray<UPackage*> 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<class IPlugin> 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<TSharedRef<IPlugin>>& OutAvailableGameMods)
+{
+ OutAvailableGameMods.Empty();
+
+ // Find available game mods from the list of discovered plugins
+
+ for (TSharedRef<IPlugin> 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<TSharedPtr<FUICommandInfo>>& Commands)
+{
+ for (TSharedPtr<FUICommandInfo> Command : Commands)
+ {
+ MenuBuilder.AddMenuEntry(Command, NAME_None, TAttribute<FText>(), TAttribute<FText>(), FSlateIcon(FSimpleUGCEditorStyle::GetStyleSetName(), "SimpleUGCEditor.Folder"));
+ }
+}
+
+void FSimpleUGCPackager::GeneratePackagerMenuContent(class FMenuBuilder& MenuBuilder)
+{
+ TArray<TSharedRef<IPlugin>> AvailableGameMods;
+ FindAvailableGameMods(AvailableGameMods);
+
+ TArray<TSharedPtr<FUICommandInfo>> Commands;
+
+ GeneratePackagerMenuContent_Internal(MenuBuilder, UGCCommands);
+}
+
+TSharedRef<SWidget> FSimpleUGCPackager::GeneratePackagerComboButtonContent()
+{
+ // Regenerate the game mod commands
+ TArray<TSharedRef<IPlugin>> AvailableGameMods;
+ FindAvailableGameMods(AvailableGameMods);
+
+ GetAvailableUGCCommands(AvailableGameMods);
+
+ // Regenerate the action list
+ TSharedPtr<FUICommandList> 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<TSharedRef<IPlugin>>& 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<TSharedRef<FPluginTemplateDescription>>& FSimpleUGCPluginWizardDefinition::GetTemplatesSource() const
+{
+ return TemplateDefinitions;
+}
+
+void FSimpleUGCPluginWizardDefinition::OnTemplateSelectionChanged(TArray<TSharedRef<FPluginTemplateDescription>> InSelectedItems, ESelectInfo::Type SelectInfo)
+{
+ SelectedTemplates = InSelectedItems;
+}
+
+TArray<TSharedPtr<FPluginTemplateDescription>> FSimpleUGCPluginWizardDefinition::GetSelectedTemplates() const
+{
+ TArray<TSharedPtr<FPluginTemplateDescription>> SelectedTemplatePtrs;
+
+ for (TSharedRef<FPluginTemplateDescription> 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<FPluginTemplateDescription> 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<FPluginTemplateDescription> 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<SWidget> 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<FPluginTemplateDescription> 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<FString> FSimpleUGCPluginWizardDefinition::GetFoldersForSelection() const
+{
+ TArray<FString> SelectedFolders;
+ SelectedFolders.Add(BackingTemplatePath); // This will always be a part of the mod plugin
+
+ for (TSharedPtr<FPluginTemplateDescription> 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<IPlugin> Plugin = IPluginManager::Get().FindPlugin(PluginName);
+ if (Plugin != nullptr)
+ {
+ FPluginDescriptor Desc = Plugin->GetDescriptor();
+ Desc.Category = "UGC";
+ FText UpdateFailureText;
+ Plugin->UpdateDescriptor(Desc, UpdateFailureText);
+ }
+ }
+}
+
+#undef LOCTEXT_NAMESPACE