Lots of crap
This commit is contained in:
BIN
Content/Testing/M_Test.uasset
LFS
BIN
Content/Testing/M_Test.uasset
LFS
Binary file not shown.
@@ -45,9 +45,6 @@ public:
|
|||||||
// Returns true if the blueprint compiled cleanly (no errors).
|
// Returns true if the blueprint compiled cleanly (no errors).
|
||||||
static bool CompileAndReport(UBlueprint* BP, FStringBuilderBase& Out)
|
static bool CompileAndReport(UBlueprint* BP, FStringBuilderBase& Out)
|
||||||
{
|
{
|
||||||
FLogCaptureOutputDevice LogCapture;
|
|
||||||
GLog->AddOutputDevice(&LogCapture);
|
|
||||||
|
|
||||||
EBlueprintCompileOptions CompileOpts =
|
EBlueprintCompileOptions CompileOpts =
|
||||||
EBlueprintCompileOptions::SkipSave |
|
EBlueprintCompileOptions::SkipSave |
|
||||||
EBlueprintCompileOptions::SkipGarbageCollection |
|
EBlueprintCompileOptions::SkipGarbageCollection |
|
||||||
@@ -55,8 +52,6 @@ public:
|
|||||||
|
|
||||||
FKismetEditorUtilities::CompileBlueprint(BP, CompileOpts, nullptr);
|
FKismetEditorUtilities::CompileBlueprint(BP, CompileOpts, nullptr);
|
||||||
|
|
||||||
GLog->RemoveOutputDevice(&LogCapture);
|
|
||||||
|
|
||||||
int32 ErrorCount = 0;
|
int32 ErrorCount = 0;
|
||||||
int32 WarningCount = 0;
|
int32 WarningCount = 0;
|
||||||
|
|
||||||
@@ -74,18 +69,6 @@ public:
|
|||||||
*Node->ErrorMsg);
|
*Node->ErrorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect log-captured errors/warnings
|
|
||||||
for (const FString& Msg : LogCapture.CapturedErrors)
|
|
||||||
{
|
|
||||||
ErrorCount++;
|
|
||||||
Out.Appendf(TEXT(" ERROR: (log) %s\n"), *Msg);
|
|
||||||
}
|
|
||||||
for (const FString& Msg : LogCapture.CapturedWarnings)
|
|
||||||
{
|
|
||||||
WarningCount++;
|
|
||||||
Out.Appendf(TEXT(" WARNING: (log) %s\n"), *Msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
FString StatusStr = MCPUtils::EnumToString((EBlueprintStatus)BP->Status, TEXT("BS_"));
|
FString StatusStr = MCPUtils::EnumToString((EBlueprintStatus)BP->Status, TEXT("BS_"));
|
||||||
bool bIsValid = (BP->Status == BS_UpToDate) && (ErrorCount == 0);
|
bool bIsValid = (BP->Status == BS_UpToDate) && (ErrorCount == 0);
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "MCPHandler.h"
|
#include "MCPHandler.h"
|
||||||
#include "MCPAssetFinder.h"
|
#include "MCPFetcher.h"
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
#include "Materials/Material.h"
|
#include "Materials/Material.h"
|
||||||
#include "MaterialDomain.h"
|
|
||||||
#include "Material_Compile.generated.h"
|
#include "Material_Compile.generated.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -30,27 +29,25 @@ public:
|
|||||||
virtual void Handle(FStringBuilderBase& Result) override
|
virtual void Handle(FStringBuilderBase& Result) override
|
||||||
{
|
{
|
||||||
// Load material
|
// Load material
|
||||||
MCPAssets<UMaterial> Assets;
|
MCPFetcher F(Result);
|
||||||
if (!Assets.Exact(Material).Errors(Result).ENone().ETwo().Load()) return;
|
UMaterial* MaterialObj = F.Asset(Material).Cast<UMaterial>();
|
||||||
UMaterial* MaterialObj = Assets.Object();
|
if (!MaterialObj) return;
|
||||||
|
|
||||||
// Force recompile by triggering PreEdit/PostEdit
|
// Force recompile
|
||||||
TArray<UObject*> Chain = { MaterialObj };
|
MaterialObj->ForceRecompileForRendering();
|
||||||
MCPUtils::PreEdit(Chain);
|
|
||||||
MCPUtils::PostEdit(Chain);
|
|
||||||
|
|
||||||
// Check for compilation errors via FMaterialResource on current platform
|
// Wait for compilation to finish, then check for errors
|
||||||
TArray<FString> Errors;
|
|
||||||
FMaterialResource* Resource = MaterialObj->GetMaterialResource(GMaxRHIFeatureLevel);
|
FMaterialResource* Resource = MaterialObj->GetMaterialResource(GMaxRHIFeatureLevel);
|
||||||
|
TArray<FString> Errors;
|
||||||
if (Resource)
|
if (Resource)
|
||||||
{
|
{
|
||||||
|
Resource->FinishCompilation();
|
||||||
Errors = Resource->GetCompileErrors();
|
Errors = Resource->GetCompileErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Errors.IsEmpty())
|
if (Errors.IsEmpty())
|
||||||
{
|
{
|
||||||
Result.Appendf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj));
|
Result.Appendf(TEXT("%s compiled successfully.\n"), *MCPUtils::FormatName(MaterialObj));
|
||||||
Result.Append(TEXT("WARNING: Error detection is not working. GetCompileErrors() returns empty even when shader compilation fails. Do not trust this result.\n"));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
|
||||||
|
class FLogCaptureOutputDevice : public FOutputDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TArray<FString> CapturedErrors;
|
||||||
|
bool bEnabled = true;
|
||||||
|
|
||||||
|
void Install() { GLog->AddOutputDevice(this); }
|
||||||
|
void Uninstall() { GLog->RemoveOutputDevice(this); }
|
||||||
|
|
||||||
|
// If the device is marked 'CanBeUsedOnMultipleThreads,'
|
||||||
|
// then UE_LOG will call Serialize from the current
|
||||||
|
// thread, otherwise, it will call Serialize from the
|
||||||
|
// logging thread. Without this, we wouldn't be able to
|
||||||
|
// tell whether an error is coming from the game thread.
|
||||||
|
virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||||
|
|
||||||
|
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
|
||||||
|
{
|
||||||
|
// Only capture messages from the game thread.
|
||||||
|
// Other threads generate noise we don't care about.
|
||||||
|
if (!IsInGameThread() || !bEnabled) return;
|
||||||
|
|
||||||
|
if (Verbosity == ELogVerbosity::Warning ||
|
||||||
|
Verbosity == ELogVerbosity::Error ||
|
||||||
|
Verbosity == ELogVerbosity::Fatal)
|
||||||
|
{
|
||||||
|
CapturedErrors.Add(FString(V));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include "MCPProperty.h"
|
||||||
|
#include "MCPUtils.h"
|
||||||
|
|
||||||
|
MCPProperty::MCPProperty(FProperty* InProp, void* Container)
|
||||||
|
: Prop(InProp), ValuePtr(InProp ? InProp->ContainerPtrToValuePtr<void>(Container) : nullptr) {}
|
||||||
|
|
||||||
|
FString MCPProperty::GetText() const
|
||||||
|
{
|
||||||
|
FString Result;
|
||||||
|
Prop->ExportTextItem_Direct(Result, ValuePtr, nullptr, nullptr, PPF_None);
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MCPProperty::SetText(const FString& Value, MCPErrorCallback Error)
|
||||||
|
{
|
||||||
|
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, nullptr, PPF_None);
|
||||||
|
if (!ImportResult)
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"),
|
||||||
|
*Value, *MCPUtils::FormatName(Prop), *Prop->GetCPPType()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "MCPServer.h"
|
#include "MCPServer.h"
|
||||||
#include "MCPHandler.h"
|
#include "MCPHandler.h"
|
||||||
|
#include "LogCapture.h"
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
#include "MCPAssetFinder.h"
|
#include "MCPAssetFinder.h"
|
||||||
#include "UObject/StrongObjectPtr.h"
|
#include "UObject/StrongObjectPtr.h"
|
||||||
@@ -151,6 +152,8 @@ void UMCPServer::Initialize(FSubsystemCollectionBase& Collection)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BuildMCPHandlerRegistry();
|
BuildMCPHandlerRegistry();
|
||||||
|
LogCapture.bEnabled = false;
|
||||||
|
LogCapture.Install();
|
||||||
bRunning = true;
|
bRunning = true;
|
||||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: MCP server listening on tcp://localhost:%d"), Port);
|
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: MCP server listening on tcp://localhost:%d"), Port);
|
||||||
}
|
}
|
||||||
@@ -204,6 +207,7 @@ void UMCPServer::Deinitialize()
|
|||||||
ListenSocket = nullptr;
|
ListenSocket = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogCapture.Uninstall();
|
||||||
bRunning = false;
|
bRunning = false;
|
||||||
bShuttingDown = false;
|
bShuttingDown = false;
|
||||||
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Server stopped."));
|
UE_LOG(LogTemp, Display, TEXT("BlueprintMCP: Server stopped."));
|
||||||
@@ -294,9 +298,19 @@ FString UMCPServer::HandleRequest(const FString& Line)
|
|||||||
return PopulateError.ToString();
|
return PopulateError.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke the handler.
|
// Invoke the handler with log capture.
|
||||||
|
LogCapture.CapturedErrors.Empty();
|
||||||
|
LogCapture.bEnabled = true;
|
||||||
TStringBuilder<32768> TextResult;
|
TStringBuilder<32768> TextResult;
|
||||||
Handler->Handle(TextResult);
|
Handler->Handle(TextResult);
|
||||||
|
LogCapture.bEnabled = false;
|
||||||
|
for (const FString& Msg : LogCapture.CapturedErrors)
|
||||||
|
{
|
||||||
|
TextResult.Append(TEXT("LOG: "));
|
||||||
|
TextResult.Append(Msg);
|
||||||
|
TextResult.Append(TEXT("\n"));
|
||||||
|
}
|
||||||
|
LogCapture.CapturedErrors.Empty();
|
||||||
FString Result = TextResult.ToString();
|
FString Result = TextResult.ToString();
|
||||||
for (int32 i = 0; i < Result.Len(); ++i)
|
for (int32 i = 0; i < Result.Len(); ++i)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "MCPUtils.h"
|
||||||
|
|
||||||
|
// A resolved property: the FProperty descriptor plus a pointer to
|
||||||
|
// the value's storage. operator-> forwards to the FProperty.
|
||||||
|
struct MCPProperty
|
||||||
|
{
|
||||||
|
FProperty* Prop = nullptr;
|
||||||
|
void* ValuePtr = nullptr;
|
||||||
|
|
||||||
|
MCPProperty() = default;
|
||||||
|
MCPProperty(FProperty* InProp, void* Container);
|
||||||
|
|
||||||
|
FString GetText() const;
|
||||||
|
bool SetText(const FString& Value, MCPErrorCallback Error = nullptr);
|
||||||
|
|
||||||
|
explicit operator bool() const { return Prop != nullptr; }
|
||||||
|
FProperty* operator->() const { return Prop; }
|
||||||
|
};
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Async/Future.h"
|
#include "Async/Future.h"
|
||||||
#include "Dom/JsonObject.h"
|
#include "Dom/JsonObject.h"
|
||||||
#include "MCPUtils.h"
|
#include "MCPUtils.h"
|
||||||
|
#include "LogCapture.h"
|
||||||
#include "MCPServer.generated.h"
|
#include "MCPServer.generated.h"
|
||||||
|
|
||||||
class FSocket;
|
class FSocket;
|
||||||
@@ -52,6 +53,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
|
|
||||||
// ----- Tool dispatch -----
|
// ----- Tool dispatch -----
|
||||||
|
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
|
||||||
TMap<FString, UClass*> MCPHandlerRegistry; // tool name -> UMCPHandler subclass
|
TMap<FString, UClass*> MCPHandlerRegistry; // tool name -> UMCPHandler subclass
|
||||||
void BuildMCPHandlerRegistry();
|
void BuildMCPHandlerRegistry();
|
||||||
|
|
||||||
|
|||||||
@@ -30,58 +30,6 @@ class UEnum;
|
|||||||
struct FMemberReference;
|
struct FMemberReference;
|
||||||
struct FBPVariableDescription;
|
struct FBPVariableDescription;
|
||||||
|
|
||||||
// ----- Log capture -----
|
|
||||||
|
|
||||||
class FLogCaptureOutputDevice : public FOutputDevice
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TArray<FString> CapturedErrors;
|
|
||||||
TArray<FString> CapturedWarnings;
|
|
||||||
|
|
||||||
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
|
|
||||||
{
|
|
||||||
FString Msg(V);
|
|
||||||
|
|
||||||
if (Verbosity == ELogVerbosity::Error || Verbosity == ELogVerbosity::Fatal)
|
|
||||||
{
|
|
||||||
CapturedErrors.Add(Msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Verbosity == ELogVerbosity::Warning)
|
|
||||||
{
|
|
||||||
if (!Msg.Contains(TEXT("BlueprintMCP:")))
|
|
||||||
{
|
|
||||||
CapturedWarnings.Add(Msg);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const TCHAR* ErrorPatterns[] = {
|
|
||||||
TEXT("Can't connect pins"),
|
|
||||||
TEXT("Fixed up function"),
|
|
||||||
TEXT("is not compatible with"),
|
|
||||||
TEXT("could not find a pin"),
|
|
||||||
TEXT("has an invalid"),
|
|
||||||
TEXT("orphaned pin"),
|
|
||||||
TEXT("is deprecated"),
|
|
||||||
TEXT("does not implement"),
|
|
||||||
TEXT("Missing function"),
|
|
||||||
TEXT("Unable to find"),
|
|
||||||
TEXT("Failed to resolve"),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const TCHAR* Pattern : ErrorPatterns)
|
|
||||||
{
|
|
||||||
if (Msg.Contains(Pattern))
|
|
||||||
{
|
|
||||||
CapturedWarnings.Add(Msg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ----- Error callback -----
|
// ----- Error callback -----
|
||||||
|
|
||||||
struct MCPErrorCallback
|
struct MCPErrorCallback
|
||||||
|
|||||||
Reference in New Issue
Block a user