实现模组新建/打包
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
Binaries
|
||||
DerivedDataCache
|
||||
Intermediate
|
||||
Saved
|
||||
Build
|
||||
.vscode
|
||||
.vs
|
||||
*.VC.db
|
||||
*.opensdf
|
||||
*.opendb
|
||||
*.sdf
|
||||
*.sln
|
||||
*.suo
|
||||
*.xcodeproj
|
||||
*.xcworkspace
|
34
ModSupport.uplugin
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "Mod Support",
|
||||
"Description": "",
|
||||
"Category": "Other",
|
||||
"CreatedBy": "_Redstone_c_",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "",
|
||||
"SupportURL": "",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": true,
|
||||
"Installed": false,
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "ModSupport",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PreDefault"
|
||||
},
|
||||
{
|
||||
"Name": "ModSupportEditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "PluginBrowser",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
5
README.md
Normal file
@ -0,0 +1,5 @@
|
||||
## UE4 Plugin: ModSupport
|
||||
[ModSupport](http://git.myredstone.top/summary/UE4-Plugins!ModSupport.git) 是一个辅助制作 UE4 游戏模块化模组的插件,借鉴 [UGCExample](https://github.com/EpicGames/UGCExample/tree/a30eb37ddd71c6b958000af35506085d263e6934) 的思路管理和组织模组,配合 [HotPatcher](https://github.com/EpicGames/UGCExample/tree/a30eb37ddd71c6b958000af35506085d263e6934) 进行打包来实现单 Pak 包模组,可以在发布版的 UE4 上运行而无需源码。
|
||||
|
||||
目前支持的 UE4 版本:4.25.4
|
||||
前置插件:[HotPatcher](https://github.com/EpicGames/UGCExample/tree/a30eb37ddd71c6b958000af35506085d263e6934)
|
BIN
Resources/ButtonIcon_40x.png
Normal file
After Width: | Height: | Size: 9.0 KiB |
BIN
Resources/CreateMod_128x.png
Normal file
After Width: | Height: | Size: 9.6 KiB |
BIN
Resources/CreateMod_16x.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Resources/CreateMod_48x.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
Resources/CreateMod_64x.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
Resources/Icon128.png
Normal file
After Width: | Height: | Size: 12 KiB |
61
Resources/ModPackageCofnig.json
Normal file
@ -0,0 +1,61 @@
|
||||
{
|
||||
"bByBaseVersion": false,
|
||||
"baseVersion":
|
||||
{
|
||||
"filePath": ""
|
||||
},
|
||||
"versionId": "%%%PluginName%%%",
|
||||
"assetIncludeFilters": [
|
||||
{
|
||||
"path": "%%%PluginContentDir%%%"
|
||||
}
|
||||
],
|
||||
"assetIgnoreFilters": [],
|
||||
"bForceSkipContent": true,
|
||||
"forceSkipContentRules": [
|
||||
{
|
||||
"path": "/Engine/Editor"
|
||||
},
|
||||
{
|
||||
"path": "/Engine/VREditor"
|
||||
}
|
||||
],
|
||||
"forceSkipAssets": [],
|
||||
"bIncludeHasRefAssetsOnly": false,
|
||||
"bAnalysisFilterDependencies": false,
|
||||
"bRecursiveWidgetTree": false,
|
||||
"assetRegistryDependencyTypes": [],
|
||||
"includeSpecifyAssets": [],
|
||||
"bIncludeAssetRegistry": false,
|
||||
"bIncludeGlobalShaderCache": false,
|
||||
"bIncludeShaderBytecode": false,
|
||||
"bIncludeEngineIni": false,
|
||||
"bIncludePluginIni": false,
|
||||
"bIncludeProjectIni": false,
|
||||
"bEnableExternFilesDiff": false,
|
||||
"ignoreDeletionModulesAsset": [],
|
||||
"addExternAssetsToPlatform": [],
|
||||
"bEnableChunk": false,
|
||||
"chunkInfos": [],
|
||||
"bCookPatchAssets": false,
|
||||
"pakCommandOptions": [],
|
||||
"replacePakCommandTexts": [],
|
||||
"unrealPakOptions": [
|
||||
"-compress",
|
||||
"-compressionformats=Zlib"
|
||||
],
|
||||
"pakTargetPlatforms": [
|
||||
"%%%TargetPlatform%%%"
|
||||
],
|
||||
"bCustomPakNameRegular": true,
|
||||
"pakNameRegular": "{VERSION}",
|
||||
"bSaveDeletedAssetsToNewReleaseJson": false,
|
||||
"bSavePakList": false,
|
||||
"bSaveDiffAnalysis": false,
|
||||
"bSaveAssetRelatedInfo": false,
|
||||
"bSavePatchConfig": false,
|
||||
"savePath":
|
||||
{
|
||||
"path": "%%%OutputDirectory%%%"
|
||||
}
|
||||
}
|
BIN
Resources/PackageMod_128x.png
Normal file
After Width: | Height: | Size: 6.2 KiB |
BIN
Resources/PackageMod_16x.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Resources/PackageMod_48x.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Resources/PackageMod_64x.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
53
Source/ModSupport/ModSupport.Build.cs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ModSupport : ModuleRules
|
||||
{
|
||||
public ModSupport(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",
|
||||
// ... add other public dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"CoreUObject",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
// ... add private dependencies that you statically link with here ...
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
DynamicallyLoadedModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
// ... add any modules that your module loads dynamically here ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
3
Source/ModSupport/Private/ModInfo.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "ModInfo.h"
|
||||
|
||||
#include "Interfaces/IPluginManager.h"
|
20
Source/ModSupport/Private/ModSupport.cpp
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModSupport.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FModSupportModule"
|
||||
|
||||
void FModSupportModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
}
|
||||
|
||||
void FModSupportModule::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.
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FModSupportModule, ModSupport)
|
3
Source/ModSupport/Private/ModSupportLog.cpp
Normal file
@ -0,0 +1,3 @@
|
||||
#include "ModSupportLog.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogModSupport);
|
67
Source/ModSupport/Public/ModInfo.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "ModInfo.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType, Category = "ModSupport|ModInfo")
|
||||
struct MODSUPPORT_API FModInfo
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString ContentDir;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString VirtualMountPoint;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
int32 Version;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString VersionName;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString FriendlyName;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString Description;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString Category;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString CreatedBy;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString CreatedByURL;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString DocsURL;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString MarketplaceURL;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString SupportURL;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString EngineVersion;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
FString ParentPluginName;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
bool bIsBetaVersion;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
bool bIsExperimentalVersion;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
bool bIsHidden;
|
||||
|
||||
UPROPERTY(BlueprintReadOnly, Category = "ModSupport|ModInfo")
|
||||
TArray<FString> PluginsRequire;
|
||||
};
|
15
Source/ModSupport/Public/ModSupport.h
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FModSupportModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
};
|
6
Source/ModSupport/Public/ModSupportLog.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
MODSUPPORT_API DECLARE_LOG_CATEGORY_EXTERN(LogModSupport, Log, All);
|
60
Source/ModSupportEditor/ModSupportEditor.Build.cs
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class ModSupportEditor : ModuleRules
|
||||
{
|
||||
public ModSupportEditor(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",
|
||||
"ModSupport",
|
||||
"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 ...
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
64
Source/ModSupportEditor/Private/ModCreator.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModCreator.h"
|
||||
|
||||
#include "ModPluginWizardDefinition.h"
|
||||
#include "Widgets/Docking/SDockTab.h"
|
||||
|
||||
// This depends on the Plugin Browser module to work correctly...
|
||||
#include "IPluginBrowser.h"
|
||||
|
||||
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FModCreator"
|
||||
|
||||
const FName FModCreator::ModSupportEditorPluginCreatorName("ModCreator");
|
||||
|
||||
FModCreator::FModCreator()
|
||||
{
|
||||
RegisterTabSpawner();
|
||||
}
|
||||
|
||||
FModCreator::~FModCreator()
|
||||
{
|
||||
UnregisterTabSpawner();
|
||||
}
|
||||
|
||||
void FModCreator::OpenNewPluginWizard(bool bSuppressErrors) const
|
||||
{
|
||||
if (IPluginBrowser::IsAvailable())
|
||||
{
|
||||
FGlobalTabmanager::Get()->InvokeTab(ModSupportEditorPluginCreatorName);
|
||||
}
|
||||
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 FModCreator::RegisterTabSpawner()
|
||||
{
|
||||
FTabSpawnerEntry& Spawner = FGlobalTabmanager::Get()->RegisterNomadTabSpawner(ModSupportEditorPluginCreatorName,
|
||||
FOnSpawnTab::CreateRaw(this, &FModCreator::HandleSpawnPluginTab));
|
||||
|
||||
// Set a default size for this tab
|
||||
FVector2D DefaultSize(800.0f, 500.0f);
|
||||
FTabManager::RegisterDefaultTabWindowSize(ModSupportEditorPluginCreatorName, DefaultSize);
|
||||
|
||||
Spawner.SetDisplayName(LOCTEXT("NewModTabHeader", "Create New Mod Package"));
|
||||
Spawner.SetMenuType(ETabSpawnerMenuType::Hidden);
|
||||
}
|
||||
|
||||
void FModCreator::UnregisterTabSpawner()
|
||||
{
|
||||
FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(ModSupportEditorPluginCreatorName);
|
||||
}
|
||||
|
||||
TSharedRef<SDockTab> FModCreator::HandleSpawnPluginTab(const FSpawnTabArgs& SpawnTabArgs)
|
||||
{
|
||||
check(IPluginBrowser::IsAvailable());
|
||||
return IPluginBrowser::Get().SpawnPluginCreatorTab(SpawnTabArgs, MakeShared<FModPluginWizardDefinition>());
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
221
Source/ModSupportEditor/Private/ModPackager.cpp
Normal file
@ -0,0 +1,221 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModPackager.h"
|
||||
#include "ModSupportEditor.h"
|
||||
#include "ModSupportEditorCommands.h"
|
||||
#include "ModSupportEditorStyle.h"
|
||||
#include "ModSupportEditorLog.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/FileHelper.h"
|
||||
#include "Misc/PackageName.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "ModPackager"
|
||||
|
||||
FModPackager::FModPackager()
|
||||
{
|
||||
}
|
||||
|
||||
FModPackager::~FModPackager()
|
||||
{
|
||||
}
|
||||
|
||||
void FModPackager::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 Mod output directory:").ToString(), DefaultDirectory, OutputDirectory))
|
||||
{
|
||||
PackagePlugin(Plugin, OutputDirectory);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FText PackageModError = FText::Format(LOCTEXT("PackageModError_UnsavedContent", "You must save all assets in {0} before you can share it."),
|
||||
FText::FromString(Plugin->GetName()));
|
||||
|
||||
FMessageDialog::Open(EAppMsgType::Ok, PackageModError);
|
||||
}
|
||||
}
|
||||
|
||||
bool FModPackager::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 FModPackager::PackagePlugin(TSharedRef<class IPlugin> Plugin, const FString& OutputDirectory)
|
||||
{
|
||||
FString PackageCofnig;
|
||||
FString PackageCofnigTemplate;
|
||||
PackageCofnigTemplate = IPluginManager::Get().FindPlugin(TEXT("ModSupport"))->GetBaseDir() / TEXT("Resources") / TEXT("ModPackageCofnig.json");
|
||||
|
||||
if (!FFileHelper::LoadFileToString(PackageCofnig, *PackageCofnigTemplate))
|
||||
{
|
||||
UE_LOG(LogModSupportEditor, Error, TEXT("Failed to load configuration template"));
|
||||
return;
|
||||
}
|
||||
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%PluginName%%%"), *Plugin->GetName());
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%PluginContentDir%%%"), *Plugin->GetMountedAssetPath());
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%TargetPlatform%%%"), TEXT("WindowsNoEditor"));
|
||||
#elif PLATFORM_MAC
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%TargetPlatform%%%"), TEXT("MacNoEditor"));
|
||||
#elif PLATFORM_LINUX
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%TargetPlatform%%%"), TEXT("LinuxNoEditor"));
|
||||
#else
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%TargetPlatform%%%"), TEXT("AllDesktop"));
|
||||
#endif
|
||||
|
||||
PackageCofnig = PackageCofnig.Replace(TEXT("%%%OutputDirectory%%%"), *OutputDirectory);
|
||||
|
||||
FString PackageCofnigSavePath;
|
||||
PackageCofnigSavePath = FPaths::ProjectSavedDir() / TEXT("ModInfo") / TEXT("ModPackageCofnig.json");
|
||||
|
||||
if (!FFileHelper::SaveStringToFile(PackageCofnig, *PackageCofnigSavePath))
|
||||
{
|
||||
UE_LOG(LogModSupportEditor, Error, TEXT("Failed to save configuration"));
|
||||
return;
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
PackageCofnigSavePath = PackageCofnigSavePath.Replace(TEXT("/"), TEXT("\\"));
|
||||
#endif
|
||||
|
||||
FText OptTitle = LOCTEXT("PackageCofnigDialog", "Saved packaging configuration file");
|
||||
FText Message = FText::FromString(PackageCofnigSavePath);
|
||||
FMessageDialog::Open(EAppMsgType::Ok, Message, &OptTitle);
|
||||
UE_LOG(LogModSupportEditor, Display, TEXT("Saved packaging configuration file to %s"), *PackageCofnigSavePath);
|
||||
}
|
||||
|
||||
void FModPackager::FindAvailableGameMods(TArray<TSharedRef<IPlugin>>& OutAvailableGameMods)
|
||||
{
|
||||
OutAvailableGameMods.Empty();
|
||||
|
||||
for (TSharedRef<IPlugin> Plugin : IPluginManager::Get().GetDiscoveredPlugins())
|
||||
{
|
||||
if (Plugin->GetLoadedFrom() == EPluginLoadedFrom::Project && Plugin->GetType() == EPluginType::Mod)
|
||||
{
|
||||
UE_LOG(LogModSupportEditor, Display, TEXT("Adding %s"), *Plugin->GetName());
|
||||
OutAvailableGameMods.AddUnique(Plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FModPackager::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(FModSupportEditorStyle::GetStyleSetName(), "ModSupportEditor.Folder"));
|
||||
}
|
||||
}
|
||||
|
||||
void FModPackager::GeneratePackagerMenuContent(class FMenuBuilder& MenuBuilder)
|
||||
{
|
||||
TArray<TSharedRef<IPlugin>> AvailableGameMods;
|
||||
FindAvailableGameMods(AvailableGameMods);
|
||||
|
||||
TArray<TSharedPtr<FUICommandInfo>> Commands;
|
||||
|
||||
GeneratePackagerMenuContent_Internal(MenuBuilder, ModCommands);
|
||||
}
|
||||
|
||||
TSharedRef<SWidget> FModPackager::GeneratePackagerComboButtonContent()
|
||||
{
|
||||
// Regenerate the game mod commands
|
||||
TArray<TSharedRef<IPlugin>> AvailableGameMods;
|
||||
FindAvailableGameMods(AvailableGameMods);
|
||||
|
||||
GetAvailableModCommands(AvailableGameMods);
|
||||
|
||||
// Regenerate the action list
|
||||
TSharedPtr<FUICommandList> GameModActionsList = MakeShareable(new FUICommandList);
|
||||
|
||||
for (int32 Index = 0; Index < ModCommands.Num(); ++Index)
|
||||
{
|
||||
GameModActionsList->MapAction(
|
||||
ModCommands[Index],
|
||||
FExecuteAction::CreateRaw(this, &FModPackager::OpenPluginPackager, AvailableGameMods[Index]),
|
||||
FCanExecuteAction()
|
||||
);
|
||||
}
|
||||
|
||||
// Show the drop down menu
|
||||
const bool bShouldCloseWindowAfterMenuSelection = true;
|
||||
FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, GameModActionsList);
|
||||
|
||||
MenuBuilder.BeginSection(NAME_None, LOCTEXT("PackageMod", "Share..."));
|
||||
{
|
||||
GeneratePackagerMenuContent_Internal(MenuBuilder, ModCommands);
|
||||
}
|
||||
MenuBuilder.EndSection();
|
||||
|
||||
return MenuBuilder.MakeWidget();
|
||||
}
|
||||
|
||||
void FModPackager::GetAvailableModCommands(const TArray<TSharedRef<IPlugin>>& AvailableMod)
|
||||
{
|
||||
if (ModCommands.Num() > 0)
|
||||
{
|
||||
// Unregister UI Commands
|
||||
FModSupportEditorCommands::Get().UnregisterModCommands(ModCommands);
|
||||
}
|
||||
ModCommands.Empty(AvailableMod.Num());
|
||||
|
||||
ModCommands = FModSupportEditorCommands::Get().RegisterModCommands(AvailableMod);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
201
Source/ModSupportEditor/Private/ModPluginWizardDefinition.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModPluginWizardDefinition.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 "SimpleModPluginWizard"
|
||||
|
||||
FModPluginWizardDefinition::FModPluginWizardDefinition()
|
||||
{
|
||||
PluginBaseDir = IPluginManager::Get().FindPlugin(TEXT("ModSupport"))->GetBaseDir();
|
||||
BackingTemplate = MakeShareable(new FPluginTemplateDescription(FText(), FText(), TEXT("BaseTemplate"), true, EHostType::Runtime));
|
||||
BackingTemplatePath = PluginBaseDir / TEXT("Templates") / BackingTemplate->OnDiskPath;
|
||||
}
|
||||
|
||||
const TArray<TSharedRef<FPluginTemplateDescription>>& FModPluginWizardDefinition::GetTemplatesSource() const
|
||||
{
|
||||
return TemplateDefinitions;
|
||||
}
|
||||
|
||||
void FModPluginWizardDefinition::OnTemplateSelectionChanged(TArray<TSharedRef<FPluginTemplateDescription>> InSelectedItems, ESelectInfo::Type SelectInfo)
|
||||
{
|
||||
SelectedTemplates = InSelectedItems;
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FPluginTemplateDescription>> FModPluginWizardDefinition::GetSelectedTemplates() const
|
||||
{
|
||||
TArray<TSharedPtr<FPluginTemplateDescription>> SelectedTemplatePtrs;
|
||||
|
||||
for (TSharedRef<FPluginTemplateDescription> Ref : SelectedTemplates)
|
||||
{
|
||||
SelectedTemplatePtrs.Add(Ref);
|
||||
}
|
||||
|
||||
return SelectedTemplatePtrs;
|
||||
}
|
||||
|
||||
void FModPluginWizardDefinition::ClearTemplateSelection()
|
||||
{
|
||||
SelectedTemplates.Empty();
|
||||
}
|
||||
|
||||
bool FModPluginWizardDefinition::HasValidTemplateSelection() const
|
||||
{
|
||||
// A mod should be created even if no templates are actually selected
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FModPluginWizardDefinition::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 FModPluginWizardDefinition::HasModules() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FModPluginWizardDefinition::IsMod() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FModPluginWizardDefinition::OnShowOnStartupCheckboxChanged(ECheckBoxState CheckBoxState)
|
||||
{
|
||||
}
|
||||
|
||||
ECheckBoxState FModPluginWizardDefinition::GetShowOnStartupCheckBoxState() const
|
||||
{
|
||||
return ECheckBoxState();
|
||||
}
|
||||
|
||||
FText FModPluginWizardDefinition::GetInstructions() const
|
||||
{
|
||||
return LOCTEXT("CreateNewModPanel", "Give your new Mod package a name and Click 'Create Mod' to make a new content only Mod package.");
|
||||
}
|
||||
|
||||
TSharedPtr<SWidget> FModPluginWizardDefinition::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 FModPluginWizardDefinition::GetPluginIconPath(FString& OutIconPath) const
|
||||
{
|
||||
// Replace this file with your own 128x128 image if desired.
|
||||
OutIconPath = BackingTemplatePath / TEXT("Resources/Icon128.png");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FModPluginWizardDefinition::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 FModPluginWizardDefinition::GetPluginFolderPath() const
|
||||
{
|
||||
return BackingTemplatePath;
|
||||
}
|
||||
|
||||
EHostType::Type FModPluginWizardDefinition::GetPluginModuleDescriptor() const
|
||||
{
|
||||
return BackingTemplate->ModuleDescriptorType;
|
||||
}
|
||||
|
||||
ELoadingPhase::Type FModPluginWizardDefinition::GetPluginLoadingPhase() const
|
||||
{
|
||||
return BackingTemplate->LoadingPhase;
|
||||
}
|
||||
|
||||
TArray<FString> FModPluginWizardDefinition::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 FModPluginWizardDefinition::PluginCreated(const FString& PluginName, bool bWasSuccessful) const
|
||||
{
|
||||
// Override Category to Mod
|
||||
if (bWasSuccessful)
|
||||
{
|
||||
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(PluginName);
|
||||
if (Plugin != nullptr)
|
||||
{
|
||||
FPluginDescriptor Desc = Plugin->GetDescriptor();
|
||||
Desc.Category = "Mod";
|
||||
FText UpdateFailureText;
|
||||
Plugin->UpdateDescriptor(Desc, UpdateFailureText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
96
Source/ModSupportEditor/Private/ModSupportEditor.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModSupportEditor.h"
|
||||
|
||||
#include "ModCreator.h"
|
||||
#include "ModPackager.h"
|
||||
#include "Misc/MessageDialog.h"
|
||||
#include "ModSupportEditorStyle.h"
|
||||
#include "ModSupportEditorCommands.h"
|
||||
#include "Framework/MultiBox/MultiBoxBuilder.h"
|
||||
|
||||
#include "LevelEditor.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FModSupportEditorModule"
|
||||
|
||||
void FModSupportEditorModule::StartupModule()
|
||||
{
|
||||
// This code will execute after your module is loaded into memory; the exact timing is specified in the .uplugin file per-module
|
||||
|
||||
ModCreator = MakeShared<FModCreator>();
|
||||
ModPackager = MakeShared<FModPackager>();
|
||||
|
||||
FModSupportEditorStyle::Initialize();
|
||||
FModSupportEditorStyle::ReloadTextures();
|
||||
|
||||
FModSupportEditorCommands::Register();
|
||||
|
||||
PluginCommands = MakeShareable(new FUICommandList);
|
||||
|
||||
PluginCommands->MapAction(
|
||||
FModSupportEditorCommands::Get().CreateModAction,
|
||||
FExecuteAction::CreateRaw(this, &FModSupportEditorModule::CreateModButtonClicked),
|
||||
FCanExecuteAction()
|
||||
);
|
||||
|
||||
FLevelEditorModule& LevelEditorModule = FModuleManager::LoadModuleChecked<FLevelEditorModule>("LevelEditor");
|
||||
// Add commands
|
||||
{
|
||||
FName MenuSection = "FileProject";
|
||||
FName ToolbarSection = "Misc";
|
||||
|
||||
// Add creator button to the toolbar
|
||||
{
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
|
||||
ToolbarExtender->AddToolBarExtension(ToolbarSection, EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FModSupportEditorModule::AddModCreatorToolbarExtension));
|
||||
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
}
|
||||
|
||||
// Add packager button to the toolbar
|
||||
{
|
||||
TSharedPtr<FExtender> ToolbarExtender = MakeShareable(new FExtender);
|
||||
ToolbarExtender->AddToolBarExtension(ToolbarSection, EExtensionHook::After, PluginCommands, FToolBarExtensionDelegate::CreateRaw(this, &FModSupportEditorModule::AddModPackagerToolbarExtension));
|
||||
|
||||
LevelEditorModule.GetToolBarExtensibilityManager()->AddExtender(ToolbarExtender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FModSupportEditorModule::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.
|
||||
|
||||
FModSupportEditorStyle::Shutdown();
|
||||
FModSupportEditorCommands::Unregister();
|
||||
}
|
||||
|
||||
void FModSupportEditorModule::CreateModButtonClicked()
|
||||
{
|
||||
if (ModCreator.IsValid())
|
||||
{
|
||||
ModCreator->OpenNewPluginWizard();
|
||||
}
|
||||
}
|
||||
|
||||
void FModSupportEditorModule::AddModCreatorToolbarExtension(FToolBarBuilder& Builder)
|
||||
{
|
||||
Builder.AddToolBarButton(FModSupportEditorCommands::Get().CreateModAction);
|
||||
}
|
||||
|
||||
void FModSupportEditorModule::AddModPackagerToolbarExtension(FToolBarBuilder& Builder)
|
||||
{
|
||||
FModPackager* Packager = ModPackager.Get();
|
||||
|
||||
Builder.AddComboButton(FUIAction(),
|
||||
FOnGetContent::CreateSP(Packager, &FModPackager::GeneratePackagerComboButtonContent),
|
||||
LOCTEXT("PackageMod_Label", "Package Mod"),
|
||||
LOCTEXT("PackageMod_Tooltip", "Share and distribute Mod"),
|
||||
FSlateIcon(FModSupportEditorStyle::GetStyleSetName(), "ModSupportEditor.PackageModAction")
|
||||
);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(FModSupportEditorModule, ModSupportEditor)
|
51
Source/ModSupportEditor/Private/ModSupportEditorCommands.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModSupportEditorCommands.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FModSupportEditorModule"
|
||||
|
||||
void FModSupportEditorCommands::RegisterCommands()
|
||||
{
|
||||
UI_COMMAND(CreateModAction, "Create Mod", "Create a new Mod package in a mod plugin", EUserInterfaceActionType::Button, FInputGesture());
|
||||
UI_COMMAND(PackageModAction, "Package Mod", "Share and distribute your Mod", EUserInterfaceActionType::Button, FInputGesture());
|
||||
}
|
||||
|
||||
TArray<TSharedPtr<FUICommandInfo>> FModSupportEditorCommands::RegisterModCommands(const TArray<TSharedRef<class IPlugin>>& ModList) const
|
||||
{
|
||||
TArray<TSharedPtr<FUICommandInfo>> AvailableModActions;
|
||||
AvailableModActions.Reserve(ModList.Num());
|
||||
|
||||
FModSupportEditorCommands* MutableThis = const_cast<FModSupportEditorCommands*>(this);
|
||||
|
||||
for (int32 Index = 0; Index < ModList.Num(); ++Index)
|
||||
{
|
||||
AvailableModActions.Add(TSharedPtr<FUICommandInfo>());
|
||||
TSharedRef<IPlugin> Mod = ModList[Index];
|
||||
|
||||
FString CommandName = "ModEditor_" + Mod->GetName();
|
||||
|
||||
FUICommandInfo::MakeCommandInfo(MutableThis->AsShared(),
|
||||
AvailableModActions[Index],
|
||||
FName(*CommandName),
|
||||
FText::FromString(Mod->GetName()),
|
||||
FText::FromString(Mod->GetBaseDir()),
|
||||
FSlateIcon(),
|
||||
EUserInterfaceActionType::Button,
|
||||
FInputGesture());
|
||||
}
|
||||
|
||||
return AvailableModActions;
|
||||
}
|
||||
|
||||
void FModSupportEditorCommands::UnregisterModCommands(TArray<TSharedPtr<FUICommandInfo>>& UICommands) const
|
||||
{
|
||||
FModSupportEditorCommands* MutableThis = const_cast<FModSupportEditorCommands*>(this);
|
||||
|
||||
for (TSharedPtr<FUICommandInfo> Command : UICommands)
|
||||
{
|
||||
FUICommandInfo::UnregisterCommandInfo(MutableThis->AsShared(), Command.ToSharedRef());
|
||||
}
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
4
Source/ModSupportEditor/Private/ModSupportEditorLog.cpp
Normal file
@ -0,0 +1,4 @@
|
||||
#include "ModSupportEditorLog.h"
|
||||
|
||||
|
||||
DEFINE_LOG_CATEGORY(LogModSupportEditor);
|
72
Source/ModSupportEditor/Private/ModSupportEditorStyle.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ModSupportEditorStyle.h"
|
||||
#include "ModSupportEditor.h"
|
||||
#include "Framework/Application/SlateApplication.h"
|
||||
#include "Styling/SlateStyleRegistry.h"
|
||||
#include "Slate/SlateGameResources.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
|
||||
TSharedPtr< FSlateStyleSet > FModSupportEditorStyle::StyleInstance = NULL;
|
||||
|
||||
void FModSupportEditorStyle::Initialize()
|
||||
{
|
||||
if (!StyleInstance.IsValid())
|
||||
{
|
||||
StyleInstance = Create();
|
||||
FSlateStyleRegistry::RegisterSlateStyle(*StyleInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void FModSupportEditorStyle::Shutdown()
|
||||
{
|
||||
FSlateStyleRegistry::UnRegisterSlateStyle(*StyleInstance);
|
||||
ensure(StyleInstance.IsUnique());
|
||||
StyleInstance.Reset();
|
||||
}
|
||||
|
||||
FName FModSupportEditorStyle::GetStyleSetName()
|
||||
{
|
||||
static FName StyleSetName(TEXT("ModSupportEditorStyle"));
|
||||
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 > FModSupportEditorStyle::Create()
|
||||
{
|
||||
TSharedRef< FSlateStyleSet > Style = MakeShareable(new FSlateStyleSet("ModSupportEditorStyle"));
|
||||
Style->SetContentRoot(IPluginManager::Get().FindPlugin("ModSupport")->GetBaseDir() / TEXT("Resources"));
|
||||
|
||||
Style->Set("ModSupportEditor.PackageModAction", new IMAGE_BRUSH(TEXT("PackageMod_64x"), Icon40x40));
|
||||
Style->Set("ModSupportEditor.CreateModAction", new IMAGE_BRUSH(TEXT("CreateMod_64x"), Icon40x40));
|
||||
|
||||
return Style;
|
||||
}
|
||||
|
||||
#undef IMAGE_BRUSH
|
||||
#undef BOX_BRUSH
|
||||
#undef BORDER_BRUSH
|
||||
#undef TTF_FONT
|
||||
#undef OTF_FONT
|
||||
|
||||
void FModSupportEditorStyle::ReloadTextures()
|
||||
{
|
||||
if (FSlateApplication::IsInitialized())
|
||||
{
|
||||
FSlateApplication::Get().GetRenderer()->ReloadTextureResources();
|
||||
}
|
||||
}
|
||||
|
||||
const ISlateStyle& FModSupportEditorStyle::Get()
|
||||
{
|
||||
return *StyleInstance;
|
||||
}
|
33
Source/ModSupportEditor/Public/ModCreator.h
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
class FModSupportPluginWizardDefinition;
|
||||
class SDockTab;
|
||||
|
||||
class FModCreator : public TSharedFromThis<FModCreator>
|
||||
{
|
||||
public:
|
||||
|
||||
FModCreator();
|
||||
~FModCreator();
|
||||
|
||||
/**
|
||||
* 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 ModSupportEditorPluginCreatorName;
|
||||
|
||||
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<SDockTab> HandleSpawnPluginTab(const class FSpawnTabArgs& SpawnTabArgs);
|
||||
};
|
49
Source/ModSupportEditor/Public/ModPackager.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
struct FModSupportCommand
|
||||
{
|
||||
TSharedPtr<class IPlugin> PluginInfo;
|
||||
TSharedPtr<class FUICommandInfo> CommandInfo;
|
||||
};
|
||||
|
||||
class FModPackager : public TSharedFromThis<FModPackager>
|
||||
{
|
||||
public:
|
||||
FModPackager();
|
||||
~FModPackager();
|
||||
|
||||
void OpenPluginPackager(TSharedRef<class IPlugin> Plugin);
|
||||
|
||||
void PackagePlugin(TSharedRef<class IPlugin> 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<class SWidget> GeneratePackagerComboButtonContent();
|
||||
|
||||
private:
|
||||
/** Gets all available game mod plugin packages */
|
||||
void FindAvailableGameMods(TArray<TSharedRef<class IPlugin>>& OutAvailableGameMods);
|
||||
|
||||
/** Gets all available game mod plugins and registers command info for them */
|
||||
void GetAvailableModCommands(const TArray<TSharedRef<class IPlugin>>& AvailableMod);
|
||||
|
||||
/** Generates menu content for the supplied set of commands */
|
||||
void GeneratePackagerMenuContent_Internal(class FMenuBuilder& MenuBuilder, const TArray<TSharedPtr<FUICommandInfo>>& 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<class IPlugin> Plugin);
|
||||
|
||||
private:
|
||||
TArray<TSharedPtr<class FUICommandInfo>> ModCommands;
|
||||
};
|
70
Source/ModSupportEditor/Public/ModPluginWizardDefinition.h
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
// Depends on code from the plugin browser to work correctly
|
||||
#include "IPluginWizardDefinition.h"
|
||||
|
||||
class FModPluginWizardDefinition : public IPluginWizardDefinition
|
||||
{
|
||||
public:
|
||||
FModPluginWizardDefinition();
|
||||
|
||||
// Begin IPluginWizardDefinition interface
|
||||
virtual const TArray<TSharedRef<FPluginTemplateDescription>>& GetTemplatesSource() const override;
|
||||
virtual void OnTemplateSelectionChanged(TArray<TSharedRef<FPluginTemplateDescription>> InSelectedItems, ESelectInfo::Type SelectInfo) override;
|
||||
virtual TArray<TSharedPtr<FPluginTemplateDescription>> 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<class SWidget> 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<FPluginTemplateDescription> InTemplate, FString& OutIconPath) const override;
|
||||
virtual FString GetPluginFolderPath() const override;
|
||||
virtual TArray<FString> 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<TSharedRef<FPluginTemplateDescription>> TemplateDefinitions;
|
||||
|
||||
/** The content that will be used when creating the mod */
|
||||
TArray<TSharedRef<FPluginTemplateDescription>> 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<FPluginTemplateDescription> 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<FPluginTemplateDescription> BaseCodeTemplate;
|
||||
|
||||
/** Maps a specific template to a specific icon file */
|
||||
TMap<FString, FString> TemplateToIconMap;
|
||||
|
||||
/** Brush used for drawing the custom header widget */
|
||||
TSharedPtr<struct FSlateDynamicImageBrush> IconBrush;
|
||||
|
||||
/** Custom header widget */
|
||||
TSharedPtr<class SWidget> CustomHeaderWidget;
|
||||
};
|
30
Source/ModSupportEditor/Public/ModSupportEditor.h
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
class FModSupportEditorModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
|
||||
/** IModuleInterface implementation */
|
||||
virtual void StartupModule() override;
|
||||
virtual void ShutdownModule() override;
|
||||
|
||||
// When the Create Button is clicked
|
||||
void CreateModButtonClicked();
|
||||
|
||||
/** Adds the plugin creator as a new toolbar button */
|
||||
void AddModCreatorToolbarExtension(FToolBarBuilder& Builder);
|
||||
|
||||
/** Adds the plugin packager as a new toolbar button */
|
||||
void AddModPackagerToolbarExtension(FToolBarBuilder& Builder);
|
||||
|
||||
private:
|
||||
|
||||
TSharedPtr<class FModCreator> ModCreator;
|
||||
TSharedPtr<class FModPackager> ModPackager;
|
||||
TSharedPtr<class FUICommandList> PluginCommands;
|
||||
};
|
27
Source/ModSupportEditor/Public/ModSupportEditorCommands.h
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Framework/Commands/Commands.h"
|
||||
#include "ModSupportEditorStyle.h"
|
||||
|
||||
class FModSupportEditorCommands : public TCommands<FModSupportEditorCommands>
|
||||
{
|
||||
public:
|
||||
|
||||
FModSupportEditorCommands()
|
||||
: TCommands<FModSupportEditorCommands>(TEXT("ModSupportEditor"), NSLOCTEXT("Contexts", "ModSupportEditor", "ModSupportEditor Plugin"), NAME_None, FModSupportEditorStyle::GetStyleSetName())
|
||||
{
|
||||
}
|
||||
|
||||
// TCommands<> interface
|
||||
virtual void RegisterCommands() override;
|
||||
|
||||
TArray<TSharedPtr<FUICommandInfo>> RegisterModCommands(const TArray<TSharedRef<class IPlugin>>& ModList) const;
|
||||
void UnregisterModCommands(TArray<TSharedPtr<FUICommandInfo>>& UICommands) const;
|
||||
|
||||
public:
|
||||
TSharedPtr< FUICommandInfo > CreateModAction;
|
||||
TSharedPtr< FUICommandInfo > PackageModAction;
|
||||
};
|
6
Source/ModSupportEditor/Public/ModSupportEditorLog.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Logging/LogMacros.h"
|
||||
|
||||
MODSUPPORTEDITOR_API DECLARE_LOG_CATEGORY_EXTERN(LogModSupportEditor, Log, All);
|
31
Source/ModSupportEditor/Public/ModSupportEditorStyle.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Styling/SlateStyle.h"
|
||||
|
||||
class FModSupportEditorStyle
|
||||
{
|
||||
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;
|
||||
};
|
BIN
Templates/BaseTemplate/Resources/Icon128.png
Normal file
After Width: | Height: | Size: 13 KiB |