123 lines
3.4 KiB
C++
123 lines
3.4 KiB
C++
#include "MCPProperty.h"
|
|
#include "MCPUtils.h"
|
|
#include "MCPServer.h"
|
|
#include "Materials/MaterialExpression.h"
|
|
#include "MaterialGraph/MaterialGraphNode.h"
|
|
|
|
MCPProperty::MCPProperty(FProperty* InProp, void* InContainer)
|
|
: Prop(InProp), Container(InContainer) {}
|
|
|
|
FString MCPProperty::GetText() const
|
|
{
|
|
FString Result;
|
|
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
|
Prop->ExportTextItem_Direct(Result, ValuePtr, nullptr, nullptr, PPF_None);
|
|
return Result;
|
|
}
|
|
|
|
bool MCPProperty::SetText(const FString& Value)
|
|
{
|
|
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
|
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, nullptr, PPF_None);
|
|
if (!ImportResult)
|
|
{
|
|
UMCPServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
|
|
*Value, *MCPUtils::FormatName(Prop), *Prop->GetCPPType());
|
|
return false;
|
|
}
|
|
|
|
if (Prop->GetOwnerClass()->IsChildOf(UMaterialExpression::StaticClass()))
|
|
{
|
|
UMaterialExpression* Expr = static_cast<UMaterialExpression*>(Container);
|
|
Expr->ForcePropertyValueChanged(Prop);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void MCPProperty::Collect(UObject *Obj, TArray<MCPProperty> &Props, EPropertyFlags Flags)
|
|
{
|
|
for (TFieldIterator<FProperty> It(Obj->GetClass()); It; ++It)
|
|
{
|
|
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
|
|
Props.Emplace(*It, Obj);
|
|
}
|
|
}
|
|
|
|
TArray<MCPProperty> MCPProperty::GetAll(UObject* Obj, EPropertyFlags Flags)
|
|
{
|
|
if (!Obj) return {};
|
|
TArray<MCPProperty> Result;
|
|
|
|
// Blueprints don't have editable properties. So
|
|
// instead, we fetch properties from the generated CDO,
|
|
// which is probably what the user intended.
|
|
//
|
|
if (UBlueprint *BP = ::Cast<UBlueprint>(Obj))
|
|
{
|
|
if (BP->GeneratedClass == nullptr)
|
|
{
|
|
UMCPServer::Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
|
|
return {};
|
|
}
|
|
Obj = BP->GeneratedClass->GetDefaultObject();
|
|
}
|
|
|
|
Collect(Obj, Result, Flags);
|
|
|
|
// If it's a Material Graph node, also collect properties from
|
|
// the associated material expression.
|
|
//
|
|
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Obj))
|
|
{
|
|
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
|
|
{
|
|
Collect(Expr, Result, Flags);
|
|
}
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TArray<MCPProperty> MCPProperty::GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring)
|
|
{
|
|
TArray<MCPProperty> All = GetAll(Obj, Flags);
|
|
if (Substring.IsEmpty()) return All;
|
|
TArray<MCPProperty> Result;
|
|
for (const MCPProperty& P : All)
|
|
{
|
|
if (MCPUtils::FormatName(P.Prop).Contains(Substring, ESearchCase::IgnoreCase))
|
|
Result.Add(P);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
TArray<MCPProperty> MCPProperty::GetAllExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name)
|
|
{
|
|
TArray<MCPProperty> All = GetAll(Obj, Flags);
|
|
TArray<MCPProperty> Result;
|
|
for (const MCPProperty& P : All)
|
|
{
|
|
if (MCPUtils::Identifies(Name, P.Prop))
|
|
Result.Add(P);
|
|
}
|
|
return Result;
|
|
}
|
|
|
|
MCPProperty MCPProperty::GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name)
|
|
{
|
|
TArray<MCPProperty> Matches = GetAllExactMatch(Obj, Flags, Name);
|
|
if (Matches.Num() == 0)
|
|
{
|
|
UMCPServer::Printf(TEXT("ERROR: Property '%s' not found on %s\n"),
|
|
*Name, *MCPUtils::FormatName(Obj->GetClass()));
|
|
return MCPProperty();
|
|
}
|
|
if (Matches.Num() > 1)
|
|
{
|
|
UMCPServer::Printf(TEXT("ERROR: Ambiguous property '%s' on %s\n"),
|
|
*Name, *MCPUtils::FormatName(Obj->GetClass()));
|
|
return MCPProperty();
|
|
}
|
|
return Matches[0];
|
|
}
|