#pragma once #include "CoreMinimal.h" #include "MCPHandler.h" #include "MCPAssetFinder.h" #include "MCPUtils.h" #include "Engine/Blueprint.h" #include "Kismet2/BlueprintEditorUtils.h" #include "UObject/UObjectIterator.h" #include "Blueprint_AddInterface.generated.h" // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- // --------------------------------------------------------------------------- UCLASS() class UMCP_Blueprint_AddInterface : public UObject, public IMCPHandler { GENERATED_BODY() public: UPROPERTY(meta=(Description="Blueprint name or package path")) FString Blueprint; UPROPERTY(meta=(Description="Interface name (e.g. 'BPI_MyInterface') or native UInterface class name")) FString InterfaceName; virtual FString GetDescription() const override { return TEXT("Add a Blueprint Interface implementation to a Blueprint. " "Creates stub function graphs for each interface function."); } virtual void Handle(FStringBuilderBase& Result) override { MCPAssets Assets; if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return; UBlueprint* BP = Assets.Object(); // Resolve the interface class UClass* InterfaceClass = FindInterfaceClass(InterfaceName, Result); if (!InterfaceClass) return; // Check for duplicates for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) { if (IfaceDesc.Interface == InterfaceClass) { MCPErrorCallback(Result).SetError(FString::Printf( TEXT("Interface '%s' is already implemented by this Blueprint."), *MCPUtils::FormatName(InterfaceClass))); return; } } FTopLevelAssetPath InterfacePath = InterfaceClass->GetClassPathName(); MCPUtils::PreEdit({BP}); bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath); if (!bAdded) { MCPErrorCallback(Result).SetError(FString::Printf( TEXT("ImplementNewInterface failed for '%s'."), *MCPUtils::FormatName(InterfaceClass))); return; } // Collect stub function graph names from the newly added interface entry MCPUtils::PostEdit({BP}); Result.Appendf(TEXT("Added interface %s\n"), *MCPUtils::FormatName(InterfaceClass)); Result.Appendf(TEXT("Function stubs:\n")); for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) { if (IfaceDesc.Interface != InterfaceClass) continue; for (const UEdGraph* Graph : IfaceDesc.Graphs) { if (Graph) Result.Appendf(TEXT(" %s\n"), *MCPUtils::FormatName(Graph)); } break; } } private: // Resolve an interface name to a UClass. Tries loaded UInterface classes // first (for native interfaces), then falls back to loading a Blueprint // Interface asset. static UClass* FindInterfaceClass(const FString& Name, FStringBuilderBase& Result) { // Strategy 1: Search loaded UInterface classes by name for (TObjectIterator It; It; ++It) { if (!It->IsChildOf(UInterface::StaticClass())) continue; if (MCPUtils::Identifies(Name, *It)) return *It; } // Strategy 2: Try loading as a Blueprint Interface asset MCPAssets IfaceAssets; if (!IfaceAssets.Exact(Name).AllContent().Errors(Result).ETwo().Load()) return nullptr; if (!IfaceAssets.Objects().IsEmpty()) { UClass* GenClass = IfaceAssets.Object()->GeneratedClass; if (GenClass && GenClass->IsChildOf(UInterface::StaticClass())) return GenClass; } MCPErrorCallback(Result).SetError(FString::Printf( TEXT("Interface '%s' not found. Provide a Blueprint Interface asset name (e.g. 'BPI_MyInterface') or a native UInterface class name."), *Name)); return nullptr; } };