More work on blueprint MCP

This commit is contained in:
2026-03-18 14:18:53 -04:00
parent a2f6a21d29
commit b0743a3c3d
9 changed files with 123 additions and 23 deletions

View File

@@ -24,13 +24,13 @@ class UWing_Blueprint_Dump : public UObject, public IWingHandler
GENERATED_BODY() GENERATED_BODY()
public: public:
UPROPERTY(meta=(Description="Blueprint name or package path")) UPROPERTY(meta=(Description="Blueprint path"))
FString Blueprint; FString Blueprint;
virtual FString GetDescription() const override virtual FString GetDescription() const override
{ {
return TEXT("Dump a Blueprint's structure: variables, interfaces, components, " return TEXT("Dump a Blueprint's structure: variables, interfaces, components, "
"and graph names. Does not include graph contents (use DumpGraphs for that)."); "and graph names. Does not include graph contents (use Graph_Dump for that).");
} }
virtual void Handle() override virtual void Handle() override

View File

@@ -0,0 +1,103 @@
#pragma once
#include "CoreMinimal.h"
#include "WingServer.h"
#include "WingHandler.h"
#include "WingUtils.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "AssetRegistry/IAssetRegistry.h"
#include "Asset_ContentBrowse.generated.h"
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
UCLASS()
class UWing_Asset_ContentBrowse : public UObject, public IWingHandler
{
GENERATED_BODY()
public:
UPROPERTY(meta=(Description="Content browser path to list, e.g. /Game or /Game/Maps"))
FString Path;
virtual FString GetDescription() const override
{
return TEXT("List the subfolders and assets inside a content browser folder.");
}
static FString GetExtraInfoString(const FAssetData& Data)
{
FString ParentClassName;
if (Data.GetTagValue(FName(TEXT("ParentClass")), ParentClassName))
{
int32 DotIndex;
if (ParentClassName.FindLastChar('.', DotIndex))
ParentClassName = ParentClassName.Mid(DotIndex + 1);
ParentClassName.RemoveFromEnd(TEXT("'"));
return FString::Printf(TEXT("(parent: %s)"), *ParentClassName);
}
return FString();
}
static FString GetAssetTypeString(const FAssetData& Data)
{
FString AssetType = WingUtils::FormatName(Data.GetClass());
FString BlueprintTypeTag;
if (Data.GetTagValue(FName(TEXT("BlueprintType")), BlueprintTypeTag))
{
BlueprintTypeTag.RemoveFromStart(TEXT("BPTYPE_"));
if (BlueprintTypeTag == TEXT("Normal"))
AssetType = TEXT("Blueprint");
else if (BlueprintTypeTag == TEXT("Const"))
AssetType = TEXT("ConstBlueprint");
else
AssetType = BlueprintTypeTag;
}
return AssetType;
}
virtual void Handle() override
{
// Normalize: strip trailing slash
while (Path.EndsWith(TEXT("/")))
{
Path.LeftChopInline(1);
}
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
TArray<FString> Results;
AR.EnumerateSubPaths(Path, [&Results](FString SubPath)
{
Results.Add(FString::Printf(TEXT("[Folder] %s\n"), *SubPath));
return true;
}, false);
// Collect assets in this folder (non-recursive)
FARFilter Filter;
Filter.PackagePaths.Add(FName(*Path));
Filter.bRecursivePaths = false;
TArray<FAssetData> Assets;
AR.GetAssets(Filter, Assets);
for (const FAssetData &Asset : Assets)
{
if (Asset.IsRedirector()) continue;
FString TypeString = GetAssetTypeString(Asset);
FString ExtraInfo = GetExtraInfoString(Asset);
Results.Add(FString::Printf(TEXT("%-30s %s %s\n"),
*TypeString, *Asset.PackageName.ToString(), *ExtraInfo));
}
Results.Sort();
for (const FString &Result : Results) UWingServer::Print(Result);
if (Results.IsEmpty())
{
UWingServer::Printf(TEXT("No contents found at '%s'.\n"), *Path);
return;
}
}
};

View File

@@ -63,7 +63,7 @@ public:
} }
// Find the newly created variable description // Find the newly created variable description
FBlueprintVar Editor(BP, Name); FWingBlueprintVar Editor(BP, Name);
if (Editor.NotFound()) return; if (Editor.NotFound()) return;
// Apply config if provided // Apply config if provided

View File

@@ -38,7 +38,7 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
FBlueprintVar Editor(BP, Variable); FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return; if (Editor.NotFound()) return;
FBlueprintEditorUtils::RemoveMemberVariable(BP, Editor.Desc->VarName); FBlueprintEditorUtils::RemoveMemberVariable(BP, Editor.Desc->VarName);

View File

@@ -38,7 +38,7 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
FBlueprintVar Editor(BP, Variable); FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return; if (Editor.NotFound()) return;
UWingServer::Printf(TEXT("Variable %s in %s:\n"), *Variable, *WingUtils::FormatName(BP)); UWingServer::Printf(TEXT("Variable %s in %s:\n"), *Variable, *WingUtils::FormatName(BP));

View File

@@ -44,7 +44,7 @@ public:
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
FBlueprintVar Editor(BP, Variable); FWingBlueprintVar Editor(BP, Variable);
if (Editor.NotFound()) return; if (Editor.NotFound()) return;
if (!Properties.Json || Properties.Json->Values.Num() == 0) if (!Properties.Json || Properties.Json->Values.Num() == 0)

View File

@@ -6,7 +6,7 @@
#include "EdGraphSchema_K2.h" #include "EdGraphSchema_K2.h"
#include "Kismet2/BlueprintEditorUtils.h" #include "Kismet2/BlueprintEditorUtils.h"
FBlueprintVar::FBlueprintVar(UBlueprint* BP, const FString& VarName) FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName)
{ {
FName VarFName(*VarName); FName VarFName(*VarName);
int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName); int32 VarIndex = FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName);
@@ -27,7 +27,7 @@ FBlueprintVar::FBlueprintVar(UBlueprint* BP, const FString& VarName)
} }
} }
void FBlueprintVar::Dump() void FWingBlueprintVar::Dump()
{ {
LoadFlags(); LoadFlags();
LoadDefault(); LoadDefault();
@@ -41,7 +41,7 @@ void FBlueprintVar::Dump()
} }
} }
bool FBlueprintVar::ApplyJson(const FJsonObject* Json) bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
{ {
bool bHasDefault = Json->HasField(TEXT("DefaultValue")); bool bHasDefault = Json->HasField(TEXT("DefaultValue"));
bool bHasType = Json->HasField(TEXT("VarType")); bool bHasType = Json->HasField(TEXT("VarType"));
@@ -66,7 +66,7 @@ bool FBlueprintVar::ApplyJson(const FJsonObject* Json)
return true; return true;
} }
void FBlueprintVar::LoadFlags() void FWingBlueprintVar::LoadFlags()
{ {
InstanceEditable = !(Desc->PropertyFlags & CPF_DisableEditOnInstance); InstanceEditable = !(Desc->PropertyFlags & CPF_DisableEditOnInstance);
BlueprintReadOnly = (Desc->PropertyFlags & CPF_BlueprintReadOnly) != 0; BlueprintReadOnly = (Desc->PropertyFlags & CPF_BlueprintReadOnly) != 0;
@@ -80,7 +80,7 @@ void FBlueprintVar::LoadFlags()
Description.Empty(); Description.Empty();
} }
void FBlueprintVar::LoadDefault() void FWingBlueprintVar::LoadDefault()
{ {
if (DefaultValueProp) if (DefaultValueProp)
DefaultValue = DefaultValueProp.GetText(); DefaultValue = DefaultValueProp.GetText();
@@ -88,7 +88,7 @@ void FBlueprintVar::LoadDefault()
DefaultValue.Empty(); DefaultValue.Empty();
} }
void FBlueprintVar::SaveFlags() void FWingBlueprintVar::SaveFlags()
{ {
// CPF flags // CPF flags
if (InstanceEditable) if (InstanceEditable)
@@ -124,14 +124,14 @@ void FBlueprintVar::SaveFlags()
Desc->RemoveMetaData(TEXT("tooltip")); Desc->RemoveMetaData(TEXT("tooltip"));
} }
bool FBlueprintVar::SaveDefault() bool FWingBlueprintVar::SaveDefault()
{ {
if (DefaultValueProp) if (DefaultValueProp)
return DefaultValueProp.SetText(DefaultValue); return DefaultValueProp.SetText(DefaultValue);
return true; return true;
} }
TArray<WingProperty> FBlueprintVar::MergedProperties() TArray<WingProperty> FWingBlueprintVar::MergedProperties()
{ {
TArray<WingProperty> Props = WingProperty::GetAll( TArray<WingProperty> Props = WingProperty::GetAll(
FBPVariableDescription::StaticStruct(), Desc, CPF_Edit); FBPVariableDescription::StaticStruct(), Desc, CPF_Edit);
@@ -143,7 +143,7 @@ TArray<WingProperty> FBlueprintVar::MergedProperties()
WingProperty::Remove(Props, TEXT("DefaultValue")); WingProperty::Remove(Props, TEXT("DefaultValue"));
Props.Append(WingProperty::GetAll( Props.Append(WingProperty::GetAll(
FBlueprintVar::StaticStruct(), this, (EPropertyFlags)0)); FWingBlueprintVar::StaticStruct(), this, (EPropertyFlags)0));
// Remove DefaultValue if we don't have a CDO property to back it. // Remove DefaultValue if we don't have a CDO property to back it.
if (!DefaultValueProp) if (!DefaultValueProp)

View File

@@ -313,7 +313,6 @@ TArray<UEdGraphNode*> WingUtils::AllNodes(UBlueprint* BP)
bool WingUtils::SaveBlueprintPackage(UBlueprint* BP) bool WingUtils::SaveBlueprintPackage(UBlueprint* BP)
{ {
UPackage* Package = BP->GetPackage(); UPackage* Package = BP->GetPackage();
UE_LOG(LogTemp, Display, TEXT("UEWingman: SaveBlueprintPackage — begin for '%s'"), *BP->GetName());
// 1. Build absolute package filename — use .umap for map packages, .uasset otherwise // 1. Build absolute package filename — use .umap for map packages, .uasset otherwise
FString PackageExtension = Package->ContainsMap() FString PackageExtension = Package->ContainsMap()
@@ -688,8 +687,7 @@ FString WingUtils::GetHandlerName(UClass* HandlerClass)
{ {
FString Name = HandlerClass->GetName(); FString Name = HandlerClass->GetName();
// Strip "Wing_" prefix // Strip "Wing_" prefix
if (Name.StartsWith(TEXT("Wing_"))) Name.RemoveFromStart(TEXT("Wing_"));
Name = Name.Mid(4);
return Name; return Name;
} }
@@ -701,8 +699,7 @@ FString WingUtils::GetHandlerGroup(UClass* HandlerClass)
{ {
FString Name = HandlerClass->GetName(); FString Name = HandlerClass->GetName();
// Strip "Wing_" prefix // Strip "Wing_" prefix
if (Name.StartsWith(TEXT("Wing_"))) Name.RemoveFromStart(TEXT("Wing_"));
Name = Name.Mid(4);
// Everything before the underscore is the group // Everything before the underscore is the group
int32 UnderscoreIdx; int32 UnderscoreIdx;
if (Name.FindChar(TEXT('_'), UnderscoreIdx)) if (Name.FindChar(TEXT('_'), UnderscoreIdx))

View File

@@ -10,15 +10,15 @@
// and metadata as simple UPROPERTYs that the property system can // and metadata as simple UPROPERTYs that the property system can
// populate from JSON. // populate from JSON.
USTRUCT() USTRUCT()
struct FBlueprintVar struct FWingBlueprintVar
{ {
GENERATED_BODY() GENERATED_BODY()
FBPVariableDescription* Desc = nullptr; FBPVariableDescription* Desc = nullptr;
WingProperty DefaultValueProp; WingProperty DefaultValueProp;
FBlueprintVar() = default; FWingBlueprintVar() = default;
FBlueprintVar(UBlueprint* BP, const FString& VarName); FWingBlueprintVar(UBlueprint* BP, const FString& VarName);
bool NotFound() const { return Desc == nullptr; } bool NotFound() const { return Desc == nullptr; }