Overhaul asset creation handlers.
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include "MCPHandler.h"
|
#include "MCPHandler.h"
|
||||||
#include "MCPAssetFinder.h"
|
#include "MCPAssetFinder.h"
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
|
#include "MCPPackageMaker.h"
|
||||||
#include "Animation/AnimBlueprint.h"
|
#include "Animation/AnimBlueprint.h"
|
||||||
#include "Animation/AnimBlueprintGeneratedClass.h"
|
#include "Animation/AnimBlueprintGeneratedClass.h"
|
||||||
#include "Animation/AnimInstance.h"
|
#include "Animation/AnimInstance.h"
|
||||||
@@ -22,11 +23,8 @@ class UMCP_AnimBlueprint_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new Animation Blueprint asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new Animation Blueprint (e.g. '/Game/AnimBP/ABP_Character')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Name or path of the skeleton asset to use"))
|
UPROPERTY(meta=(Description="Name or path of the skeleton asset to use"))
|
||||||
FString Skeleton;
|
FString Skeleton;
|
||||||
@@ -43,19 +41,8 @@ public:
|
|||||||
{
|
{
|
||||||
MCPErrorCallback CB(Result);
|
MCPErrorCallback CB(Result);
|
||||||
|
|
||||||
if (Name.IsEmpty() || PackagePath.IsEmpty() || Skeleton.IsEmpty())
|
MCPPackageMaker Maker(AssetPath, CB);
|
||||||
{
|
if (!Maker.Ok()) return;
|
||||||
return CB.SetError(TEXT("Missing required fields: name, packagePath, skeleton"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
|
||||||
{
|
|
||||||
return CB.SetError(TEXT("packagePath must start with '/Game'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if asset already exists
|
|
||||||
MCPAssets<UBlueprint> ExistCheck;
|
|
||||||
if (!ExistCheck.Exact(Name).Errors(CB).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Resolve skeleton
|
// Resolve skeleton
|
||||||
MCPAssets<USkeleton> SkeletonAssets;
|
MCPAssets<USkeleton> SkeletonAssets;
|
||||||
@@ -82,20 +69,14 @@ public:
|
|||||||
ParentClassObj = Found;
|
ParentClassObj = Found;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the package
|
// Create the package and Animation Blueprint
|
||||||
FString FullPackagePath = PackagePath / Name;
|
if (!Maker.Make()) return;
|
||||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
|
||||||
if (!Package)
|
|
||||||
{
|
|
||||||
return CB.SetError(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Animation Blueprint
|
|
||||||
UAnimBlueprint* NewAnimBP = CastChecked<UAnimBlueprint>(
|
UAnimBlueprint* NewAnimBP = CastChecked<UAnimBlueprint>(
|
||||||
FKismetEditorUtilities::CreateBlueprint(
|
FKismetEditorUtilities::CreateBlueprint(
|
||||||
ParentClassObj,
|
ParentClassObj,
|
||||||
Package,
|
Maker.Package(),
|
||||||
FName(*Name),
|
FName(*Maker.Name()),
|
||||||
BPTYPE_Normal,
|
BPTYPE_Normal,
|
||||||
UAnimBlueprint::StaticClass(),
|
UAnimBlueprint::StaticClass(),
|
||||||
UAnimBlueprintGeneratedClass::StaticClass()
|
UAnimBlueprintGeneratedClass::StaticClass()
|
||||||
@@ -113,7 +94,7 @@ public:
|
|||||||
FKismetEditorUtilities::CompileBlueprint(NewAnimBP);
|
FKismetEditorUtilities::CompileBlueprint(NewAnimBP);
|
||||||
bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP);
|
bool bSaved = MCPUtils::SaveBlueprintPackage(NewAnimBP);
|
||||||
|
|
||||||
Result.Appendf(TEXT("Created: %s\n"), *FullPackagePath);
|
Result.Appendf(TEXT("Created: %s\n"), *AssetPath);
|
||||||
Result.Appendf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj));
|
Result.Appendf(TEXT("ParentClass: %s\n"), *MCPUtils::FormatName(ParentClassObj));
|
||||||
Result.Appendf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false"));
|
Result.Appendf(TEXT("Saved: %s\n"), bSaved ? TEXT("true") : TEXT("false"));
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "MCPHandler.h"
|
#include "MCPHandler.h"
|
||||||
#include "MCPAssetFinder.h"
|
#include "MCPAssetFinder.h"
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
|
#include "MCPPackageMaker.h"
|
||||||
#include "Animation/Skeleton.h"
|
#include "Animation/Skeleton.h"
|
||||||
#include "Animation/BlendSpace.h"
|
#include "Animation/BlendSpace.h"
|
||||||
#include "BlendSpace_Create.generated.h"
|
#include "BlendSpace_Create.generated.h"
|
||||||
@@ -19,11 +20,8 @@ class UMCP_BlendSpace_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new Blend Space asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new Blend Space (e.g. '/Game/BlendSpaces/BS_Locomotion')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Name or path of the skeleton asset to use"))
|
UPROPERTY(meta=(Description="Name or path of the skeleton asset to use"))
|
||||||
FString Skeleton;
|
FString Skeleton;
|
||||||
@@ -35,32 +33,19 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
MCPErrorCallback CB(Result);
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: PackagePath must start with '/Game'\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if an asset with this name already exists.
|
MCPPackageMaker Maker(AssetPath, CB);
|
||||||
MCPAssets<UBlendSpace> ExistCheck;
|
if (!Maker.Ok()) return;
|
||||||
if (!ExistCheck.Exact(Name).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Resolve skeleton.
|
// Resolve skeleton.
|
||||||
MCPAssets<USkeleton> SkeletonAssets;
|
MCPAssets<USkeleton> SkeletonAssets;
|
||||||
if (!SkeletonAssets.Exact(Skeleton).Errors(Result).ENone().ETwo().Load()) return;
|
if (!SkeletonAssets.Exact(Skeleton).Errors(CB).ENone().ETwo().Load()) return;
|
||||||
USkeleton* SkeletonObj = SkeletonAssets.Object();
|
USkeleton* SkeletonObj = SkeletonAssets.Object();
|
||||||
|
|
||||||
// Create the package.
|
// Create the package and Blend Space.
|
||||||
FString FullPackagePath = PackagePath / Name;
|
if (!Maker.Make()) return;
|
||||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
UBlendSpace* NewBS = NewObject<UBlendSpace>(Maker.Package(), FName(*Maker.Name()), RF_Public | RF_Standalone);
|
||||||
if (!Package)
|
|
||||||
{
|
|
||||||
Result.Appendf(TEXT("ERROR: Failed to create package at '%s'\n"), *FullPackagePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Blend Space.
|
|
||||||
UBlendSpace* NewBS = NewObject<UBlendSpace>(Package, FName(*Name), RF_Public | RF_Standalone);
|
|
||||||
if (!NewBS)
|
if (!NewBS)
|
||||||
{
|
{
|
||||||
Result.Append(TEXT("ERROR: Failed to create Blend Space object\n"));
|
Result.Append(TEXT("ERROR: Failed to create Blend Space object\n"));
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "MCPHandler.h"
|
#include "MCPHandler.h"
|
||||||
#include "MCPAssetFinder.h"
|
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
|
#include "MCPPackageMaker.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "Kismet2/KismetEditorUtilities.h"
|
#include "Kismet2/KismetEditorUtilities.h"
|
||||||
#include "Blueprint_Create.generated.h"
|
#include "Blueprint_Create.generated.h"
|
||||||
@@ -19,11 +19,8 @@ class UMCP_Blueprint_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="New Blueprint asset name"))
|
UPROPERTY(meta=(Description="Full asset path for the new Blueprint (e.g. '/Game/Blueprints/BP_MyActor')"))
|
||||||
FString Blueprint;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Parent class name (C++ class name or Blueprint name)"))
|
UPROPERTY(meta=(Description="Parent class name (C++ class name or Blueprint name)"))
|
||||||
FString ParentClass;
|
FString ParentClass;
|
||||||
@@ -40,15 +37,8 @@ public:
|
|||||||
{
|
{
|
||||||
MCPErrorCallback Error(Result);
|
MCPErrorCallback Error(Result);
|
||||||
|
|
||||||
// Validate packagePath starts with /Game
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
if (!Maker.Ok()) return;
|
||||||
{
|
|
||||||
return Error.SetError(TEXT("PackagePath must start with '/Game'"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if asset already exists
|
|
||||||
MCPAssets<UBlueprint> ExistCheck;
|
|
||||||
if (!ExistCheck.Exact(Blueprint).Errors(Error).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Resolve parent class — try C++ class first, then Blueprint asset
|
// Resolve parent class — try C++ class first, then Blueprint asset
|
||||||
UClass* ParentClassObj = MCPUtils::FindClassByName(ParentClass);
|
UClass* ParentClassObj = MCPUtils::FindClassByName(ParentClass);
|
||||||
@@ -81,19 +71,13 @@ public:
|
|||||||
ParentClassObj = UInterface::StaticClass();
|
ParentClassObj = UInterface::StaticClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the package
|
// Create the package and Blueprint
|
||||||
FString FullPackagePath = PackagePath / Blueprint;
|
if (!Maker.Make()) return;
|
||||||
UPackage* Package = CreatePackage(*FullPackagePath);
|
|
||||||
if (!Package)
|
|
||||||
{
|
|
||||||
return Error.SetError(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPackagePath));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the Blueprint
|
|
||||||
UBlueprint* NewBP = FKismetEditorUtilities::CreateBlueprint(
|
UBlueprint* NewBP = FKismetEditorUtilities::CreateBlueprint(
|
||||||
ParentClassObj,
|
ParentClassObj,
|
||||||
Package,
|
Maker.Package(),
|
||||||
FName(*Blueprint),
|
FName(*Maker.Name()),
|
||||||
BlueprintTypeEnum,
|
BlueprintTypeEnum,
|
||||||
UBlueprint::StaticClass(),
|
UBlueprint::StaticClass(),
|
||||||
UBlueprintGeneratedClass::StaticClass()
|
UBlueprintGeneratedClass::StaticClass()
|
||||||
|
|||||||
@@ -6,9 +6,8 @@
|
|||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
#include "Engine/UserDefinedEnum.h"
|
#include "Engine/UserDefinedEnum.h"
|
||||||
#include "Kismet2/EnumEditorUtils.h"
|
#include "Kismet2/EnumEditorUtils.h"
|
||||||
#include "AssetToolsModule.h"
|
|
||||||
#include "IAssetTools.h"
|
|
||||||
#include "Factories/EnumFactory.h"
|
#include "Factories/EnumFactory.h"
|
||||||
|
#include "MCPPackageMaker.h"
|
||||||
#include "Enum_Create.generated.h"
|
#include "Enum_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -35,12 +34,10 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
FString PackagePath, AssetName;
|
MCPErrorCallback Error(Result);
|
||||||
if (!MCPUtils::SplitAssetPath(AssetPath, PackagePath, AssetName))
|
|
||||||
{
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
Result.Append(TEXT("ERROR: AssetPath must be a full path (e.g. '/Game/DataTypes/E_MyEnum')\n"));
|
if (!Maker.Ok()) return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TArray<FString> EnumValues;
|
TArray<FString> EnumValues;
|
||||||
for (const TSharedPtr<FJsonValue>& Val : Values.Array)
|
for (const TSharedPtr<FJsonValue>& Val : Values.Array)
|
||||||
@@ -50,25 +47,12 @@ public:
|
|||||||
}
|
}
|
||||||
if (EnumValues.Num() == 0)
|
if (EnumValues.Num() == 0)
|
||||||
{
|
{
|
||||||
Result.Append(TEXT("ERROR: Values must be a non-empty array of strings\n"));
|
return Error.SetError(TEXT("Values must be a non-empty array of strings"));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that no enum with this name already exists.
|
|
||||||
MCPAssets<UUserDefinedEnum> ExistCheck;
|
|
||||||
if (!ExistCheck.Exact(AssetName).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Create the enum using AssetTools.
|
// Create the enum using AssetTools.
|
||||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
UUserDefinedEnum* NewEnum = Maker.CreateAsset<UUserDefinedEnum, UEnumFactory>();
|
||||||
UEnumFactory* Factory = NewObject<UEnumFactory>();
|
if (!NewEnum) return;
|
||||||
UObject* NewAsset = AssetTools.CreateAsset(AssetName, PackagePath, UUserDefinedEnum::StaticClass(), Factory);
|
|
||||||
|
|
||||||
UUserDefinedEnum* NewEnum = Cast<UUserDefinedEnum>(NewAsset);
|
|
||||||
if (!NewEnum)
|
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: Failed to create UserDefinedEnum asset\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add enum values — UUserDefinedEnum starts with a MAX value.
|
// Add enum values — UUserDefinedEnum starts with a MAX value.
|
||||||
// We need to add entries before MAX.
|
// We need to add entries before MAX.
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
#include "Materials/MaterialFunction.h"
|
#include "Materials/MaterialFunction.h"
|
||||||
#include "Factories/MaterialFunctionFactoryNew.h"
|
#include "Factories/MaterialFunctionFactoryNew.h"
|
||||||
#include "AssetToolsModule.h"
|
#include "MCPPackageMaker.h"
|
||||||
#include "IAssetTools.h"
|
|
||||||
#include "MaterialFunction_Create.generated.h"
|
#include "MaterialFunction_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -21,11 +20,8 @@ class UMCP_MaterialFunction_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new material function asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new material function (e.g. '/Game/Materials/MF_MyFunc')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="Description for the material function"))
|
UPROPERTY(meta=(Optional, Description="Description for the material function"))
|
||||||
FString Description;
|
FString Description;
|
||||||
@@ -37,27 +33,14 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
MCPErrorCallback Error(Result);
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: PackagePath must start with '/Game'\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if asset already exists.
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
MCPAssets<UMaterialFunction> ExistCheck;
|
if (!Maker.Ok()) return;
|
||||||
if (!ExistCheck.Exact(Name).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Create via IAssetTools + factory.
|
// Create via IAssetTools + factory.
|
||||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
UMaterialFunction* MF = Maker.CreateAsset<UMaterialFunction, UMaterialFunctionFactoryNew>();
|
||||||
UMaterialFunctionFactoryNew* Factory = NewObject<UMaterialFunctionFactoryNew>();
|
if (!MF) return;
|
||||||
UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UMaterialFunction::StaticClass(), Factory);
|
|
||||||
|
|
||||||
UMaterialFunction* MF = Cast<UMaterialFunction>(NewAsset);
|
|
||||||
if (!MF)
|
|
||||||
{
|
|
||||||
Result.Appendf(TEXT("ERROR: Failed to create Material Function '%s' in '%s'\n"), *Name, *PackagePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set optional description.
|
// Set optional description.
|
||||||
if (!Description.IsEmpty())
|
if (!Description.IsEmpty())
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
#include "Materials/MaterialInterface.h"
|
#include "Materials/MaterialInterface.h"
|
||||||
#include "Materials/MaterialInstanceConstant.h"
|
#include "Materials/MaterialInstanceConstant.h"
|
||||||
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
||||||
#include "AssetToolsModule.h"
|
#include "MCPPackageMaker.h"
|
||||||
#include "IAssetTools.h"
|
|
||||||
#include "MaterialInstance_Create.generated.h"
|
#include "MaterialInstance_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -23,11 +22,8 @@ class UMCP_MaterialInstance_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new Material Instance asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new Material Instance (e.g. '/Game/Materials/MI_GoldShiny')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Parent material name or path (Material or Material Instance)"))
|
UPROPERTY(meta=(Description="Parent material name or path (Material or Material Instance)"))
|
||||||
FString ParentMaterial;
|
FString ParentMaterial;
|
||||||
@@ -39,15 +35,10 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
MCPErrorCallback Error(Result);
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: PackagePath must start with '/Game'\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if asset already exists.
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
MCPAssets<UMaterialInstanceConstant> ExistCheck;
|
if (!Maker.Ok()) return;
|
||||||
if (!ExistCheck.Exact(Name).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Load parent material -- try as Material first, then as Material Instance.
|
// Load parent material -- try as Material first, then as Material Instance.
|
||||||
UMaterialInterface* ParentMaterialObj = nullptr;
|
UMaterialInterface* ParentMaterialObj = nullptr;
|
||||||
@@ -79,16 +70,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create via factory + AssetTools.
|
// Create via factory + AssetTools.
|
||||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
UMaterialInstanceConstant* MI = Maker.CreateAsset<UMaterialInstanceConstant, UMaterialInstanceConstantFactoryNew>();
|
||||||
UMaterialInstanceConstantFactoryNew* Factory = NewObject<UMaterialInstanceConstantFactoryNew>();
|
if (!MI) return;
|
||||||
|
|
||||||
UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UMaterialInstanceConstant::StaticClass(), Factory);
|
|
||||||
UMaterialInstanceConstant* MI = Cast<UMaterialInstanceConstant>(NewAsset);
|
|
||||||
if (!MI)
|
|
||||||
{
|
|
||||||
Result.Appendf(TEXT("ERROR: Failed to create Material Instance '%s' in '%s'\n"), *Name, *PackagePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set parent.
|
// Set parent.
|
||||||
TArray<UObject*> Chain = { MI };
|
TArray<UObject*> Chain = { MI };
|
||||||
|
|||||||
@@ -7,8 +7,7 @@
|
|||||||
#include "Materials/Material.h"
|
#include "Materials/Material.h"
|
||||||
#include "MaterialDomain.h"
|
#include "MaterialDomain.h"
|
||||||
#include "Factories/MaterialFactoryNew.h"
|
#include "Factories/MaterialFactoryNew.h"
|
||||||
#include "AssetToolsModule.h"
|
#include "MCPPackageMaker.h"
|
||||||
#include "IAssetTools.h"
|
|
||||||
#include "Material_Create.generated.h"
|
#include "Material_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -22,11 +21,8 @@ class UMCP_Material_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new material asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new material (e.g. '/Game/Materials/M_Gold')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="Material domain: Surface, DeferredDecal, LightFunction, Volume, PostProcess, UI"))
|
UPROPERTY(meta=(Optional, Description="Material domain: Surface, DeferredDecal, LightFunction, Volume, PostProcess, UI"))
|
||||||
FString Domain;
|
FString Domain;
|
||||||
@@ -44,42 +40,29 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
MCPErrorCallback Error(Result);
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: PackagePath must start with '/Game'\n"));
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
return;
|
if (!Maker.Ok()) return;
|
||||||
}
|
|
||||||
|
|
||||||
// Parse optional enum properties before creating the asset.
|
// Parse optional enum properties before creating the asset.
|
||||||
EMaterialDomain ParsedDomain = MD_Surface;
|
EMaterialDomain ParsedDomain = MD_Surface;
|
||||||
if (!Domain.IsEmpty())
|
if (!Domain.IsEmpty())
|
||||||
{
|
{
|
||||||
if (!MCPUtils::StringToEnum(Domain, ParsedDomain, Result, TEXT("MD_")))
|
if (!MCPUtils::StringToEnum(Domain, ParsedDomain, Error, TEXT("MD_")))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EBlendMode ParsedBlendMode = BLEND_Opaque;
|
EBlendMode ParsedBlendMode = BLEND_Opaque;
|
||||||
if (!BlendMode.IsEmpty())
|
if (!BlendMode.IsEmpty())
|
||||||
{
|
{
|
||||||
if (!MCPUtils::StringToEnum(BlendMode, ParsedBlendMode, Result, TEXT("BLEND_")))
|
if (!MCPUtils::StringToEnum(BlendMode, ParsedBlendMode, Error, TEXT("BLEND_")))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if an asset with this name already exists.
|
|
||||||
MCPAssets<UMaterial> ExistCheck;
|
|
||||||
if (!ExistCheck.Exact(Name).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Create via IAssetTools + factory.
|
// Create via IAssetTools + factory.
|
||||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
UMaterial* MaterialObj = Maker.CreateAsset<UMaterial, UMaterialFactoryNew>();
|
||||||
UMaterialFactoryNew* Factory = NewObject<UMaterialFactoryNew>();
|
if (!MaterialObj) return;
|
||||||
UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UMaterial::StaticClass(), Factory);
|
|
||||||
|
|
||||||
UMaterial* MaterialObj = Cast<UMaterial>(NewAsset);
|
|
||||||
if (!MaterialObj)
|
|
||||||
{
|
|
||||||
Result.Appendf(TEXT("ERROR: Failed to create Material '%s' in '%s'\n"), *Name, *PackagePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply optional properties.
|
// Apply optional properties.
|
||||||
TArray<UObject*> Chain = { MaterialObj };
|
TArray<UObject*> Chain = { MaterialObj };
|
||||||
|
|||||||
@@ -44,19 +44,33 @@ public:
|
|||||||
|
|
||||||
TArray<FProperty*> Props = MCPUtils::SearchProperties(Template, Query, CPF_Edit, Local);
|
TArray<FProperty*> Props = MCPUtils::SearchProperties(Template, Query, CPF_Edit, Local);
|
||||||
|
|
||||||
UStruct* CurrentOwner = nullptr;
|
// Group properties by category.
|
||||||
|
TMap<FString, TArray<FProperty*>> ByCategory;
|
||||||
for (FProperty* Prop : Props)
|
for (FProperty* Prop : Props)
|
||||||
{
|
{
|
||||||
FString PropName = MCPUtils::FormatName(Prop);
|
FString Category = Prop->HasMetaData(TEXT("Category")) ? Prop->GetMetaData(TEXT("Category")) : FString();
|
||||||
|
ByCategory.FindOrAdd(Category).Add(Prop);
|
||||||
// Print section heading when the owning class changes.
|
|
||||||
UStruct* Owner = Prop->GetOwnerStruct();
|
|
||||||
if (Owner != CurrentOwner)
|
|
||||||
{
|
|
||||||
CurrentOwner = Owner;
|
|
||||||
Result.Appendf(TEXT("\nFrom %s:\n\n"), *MCPUtils::FormatName(Owner));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort category names, putting empty category last.
|
||||||
|
TArray<FString> Categories;
|
||||||
|
ByCategory.GetKeys(Categories);
|
||||||
|
Categories.Sort([](const FString& A, const FString& B) {
|
||||||
|
if (A.IsEmpty()) return false;
|
||||||
|
if (B.IsEmpty()) return true;
|
||||||
|
return A < B;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const FString& Category : Categories)
|
||||||
|
{
|
||||||
|
if (Category.IsEmpty())
|
||||||
|
Result.Append(TEXT("\nUncategorized:\n"));
|
||||||
|
else
|
||||||
|
Result.Appendf(TEXT("\n%s:\n"), *Category);
|
||||||
|
|
||||||
|
for (FProperty* Prop : ByCategory[Category])
|
||||||
|
{
|
||||||
|
FString PropName = MCPUtils::FormatName(Prop);
|
||||||
FString ValueStr = MCPUtils::GetPropertyValueText(Template, Prop);
|
FString ValueStr = MCPUtils::GetPropertyValueText(Template, Prop);
|
||||||
|
|
||||||
if (Truncate && (ValueStr.Len() > 80))
|
if (Truncate && (ValueStr.Len() > 80))
|
||||||
@@ -69,6 +83,7 @@ public:
|
|||||||
*PropName,
|
*PropName,
|
||||||
*ValueStr);
|
*ValueStr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Props.IsEmpty())
|
if (Props.IsEmpty())
|
||||||
Result.Append(TEXT(" (no blueprint-visible properties found)\n"));
|
Result.Append(TEXT(" (no blueprint-visible properties found)\n"));
|
||||||
|
|||||||
@@ -7,9 +7,8 @@
|
|||||||
#include "StructUtils/UserDefinedStruct.h"
|
#include "StructUtils/UserDefinedStruct.h"
|
||||||
#include "Kismet2/BlueprintEditorUtils.h"
|
#include "Kismet2/BlueprintEditorUtils.h"
|
||||||
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
#include "UserDefinedStructure/UserDefinedStructEditorData.h"
|
||||||
#include "AssetToolsModule.h"
|
|
||||||
#include "IAssetTools.h"
|
|
||||||
#include "Factories/StructureFactory.h"
|
#include "Factories/StructureFactory.h"
|
||||||
|
#include "MCPPackageMaker.h"
|
||||||
#include "Struct_Create.generated.h"
|
#include "Struct_Create.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -35,11 +34,8 @@ class UMCP_Struct_Create : public UObject, public IMCPHandler
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
public:
|
public:
|
||||||
UPROPERTY(meta=(Description="Name for the new struct asset"))
|
UPROPERTY(meta=(Description="Full asset path for the new struct (e.g. '/Game/DataTypes/S_MyStruct')"))
|
||||||
FString Name;
|
FString AssetPath;
|
||||||
|
|
||||||
UPROPERTY(meta=(Description="Package path where the asset will be created (must start with /Game)"))
|
|
||||||
FString PackagePath;
|
|
||||||
|
|
||||||
UPROPERTY(meta=(Optional, Description="Array of initial properties, each with 'name' and 'type' fields"))
|
UPROPERTY(meta=(Optional, Description="Array of initial properties, each with 'name' and 'type' fields"))
|
||||||
FMCPJsonArray Properties;
|
FMCPJsonArray Properties;
|
||||||
@@ -51,27 +47,14 @@ public:
|
|||||||
|
|
||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
if (!PackagePath.StartsWith(TEXT("/Game")))
|
MCPErrorCallback Error(Result);
|
||||||
{
|
|
||||||
Result.Append(TEXT("ERROR: PackagePath must start with '/Game'\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if an asset with this name already exists.
|
MCPPackageMaker Maker(AssetPath, Error);
|
||||||
MCPAssets<UUserDefinedStruct> ExistCheck;
|
if (!Maker.Ok()) return;
|
||||||
if (!ExistCheck.Exact(Name).Errors(Result).EAny().Info()) return;
|
|
||||||
|
|
||||||
// Create the struct using the AssetTools factory.
|
// Create the struct using the AssetTools factory.
|
||||||
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
UUserDefinedStruct* NewStruct = Maker.CreateAsset<UUserDefinedStruct, UStructureFactory>();
|
||||||
UStructureFactory* Factory = NewObject<UStructureFactory>();
|
if (!NewStruct) return;
|
||||||
UObject* NewAsset = AssetTools.CreateAsset(Name, PackagePath, UUserDefinedStruct::StaticClass(), Factory);
|
|
||||||
|
|
||||||
UUserDefinedStruct* NewStruct = Cast<UUserDefinedStruct>(NewAsset);
|
|
||||||
if (!NewStruct)
|
|
||||||
{
|
|
||||||
Result.Appendf(TEXT("ERROR: Failed to create struct '%s' in '%s'\n"), *Name, *PackagePath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add properties if specified.
|
// Add properties if specified.
|
||||||
int32 PropsAdded = 0;
|
int32 PropsAdded = 0;
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "MCPUtils.h"
|
||||||
|
#include "UObject/Package.h"
|
||||||
|
#include "AssetToolsModule.h"
|
||||||
|
#include "IAssetTools.h"
|
||||||
|
|
||||||
|
// Helper for creating new asset packages. Validates the path and checks for
|
||||||
|
// conflicting assets in the constructor. Call Ok() to check, then Make() to
|
||||||
|
// actually create the UPackage.
|
||||||
|
class MCPPackageMaker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MCPPackageMaker(const FString& InFullPath, MCPErrorCallback InError)
|
||||||
|
: FullPath(InFullPath)
|
||||||
|
, Error(InError)
|
||||||
|
{
|
||||||
|
// Path must start with /Game.
|
||||||
|
if (!FullPath.StartsWith(TEXT("/Game")))
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Package path '%s' must start with '/Game'"), *FullPath));
|
||||||
|
bError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for an existing asset at this path.
|
||||||
|
if (FindObject<UPackage>(nullptr, *FullPath))
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("An asset already exists at '%s'"), *FullPath));
|
||||||
|
bError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ok() const { return !bError; }
|
||||||
|
|
||||||
|
bool Make()
|
||||||
|
{
|
||||||
|
if (bError) return false;
|
||||||
|
Pkg = CreatePackage(*FullPath);
|
||||||
|
if (!Pkg)
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Failed to create package at '%s'"), *FullPath));
|
||||||
|
bError = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UPackage* Package() const { return Pkg; }
|
||||||
|
FString Name() const { return FPackageName::GetShortName(FullPath); }
|
||||||
|
|
||||||
|
template<typename AssetClass, typename FactoryClass>
|
||||||
|
AssetClass* CreateAsset()
|
||||||
|
{
|
||||||
|
if (bError) return nullptr;
|
||||||
|
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
|
||||||
|
FactoryClass* Factory = NewObject<FactoryClass>();
|
||||||
|
FString PkgPath = FPackageName::GetLongPackagePath(FullPath);
|
||||||
|
FString AssetName = FPackageName::GetShortName(FullPath);
|
||||||
|
UObject* NewAsset = AssetTools.CreateAsset(AssetName, PkgPath, AssetClass::StaticClass(), Factory);
|
||||||
|
AssetClass* Result = Cast<AssetClass>(NewAsset);
|
||||||
|
if (!Result)
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Failed to create asset at '%s'"), *FullPath));
|
||||||
|
bError = true;
|
||||||
|
}
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FString FullPath;
|
||||||
|
MCPErrorCallback Error;
|
||||||
|
UPackage* Pkg = nullptr;
|
||||||
|
bool bError = false;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user