Working on MI SetParameter
This commit is contained in:
BIN
Content/Testing/M_Test.uasset
LFS
BIN
Content/Testing/M_Test.uasset
LFS
Binary file not shown.
BIN
Content/Testing/M_Test_Inst.uasset
LFS
Normal file
BIN
Content/Testing/M_Test_Inst.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Testing/M_Test_Inst_Inst.uasset
LFS
Normal file
BIN
Content/Testing/M_Test_Inst_Inst.uasset
LFS
Normal file
Binary file not shown.
@@ -63,10 +63,7 @@
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"llvm-vs-code-extensions.vscode-clangd",
|
"llvm-vs-code-extensions.vscode-clangd",
|
||||||
"vadimcn.vscode-lldb",
|
"vadimcn.vscode-lldb"
|
||||||
"dfarley1.file-picker",
|
|
||||||
"ms-python.python",
|
|
||||||
"ms-vscode.mono-debug"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tasks": {
|
"tasks": {
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ public class BlueprintMCP : ModuleRules
|
|||||||
"MaterialEditor",
|
"MaterialEditor",
|
||||||
"AnimGraph",
|
"AnimGraph",
|
||||||
"AnimGraphRuntime",
|
"AnimGraphRuntime",
|
||||||
"RHI"
|
"RHI",
|
||||||
|
"Slate",
|
||||||
|
"SlateCore"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
#include "Materials/MaterialExpressionTextureSampleParameter2D.h"
|
||||||
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
#include "Materials/MaterialExpressionStaticSwitchParameter.h"
|
||||||
#include "Engine/Texture.h"
|
#include "Engine/Texture.h"
|
||||||
#include "Material_DumpInstanceParameters.generated.h"
|
#include "MaterialInstance_DumpParameters.generated.h"
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
UCLASS()
|
UCLASS()
|
||||||
class UMCP_Material_DumpInstanceParameters : public UObject, public IMCPHandler
|
class UMCP_MaterialInstance_DumpParameters : public UObject, public IMCPHandler
|
||||||
{
|
{
|
||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "MCPHandler.h"
|
||||||
|
#include "MCPFetcher.h"
|
||||||
|
#include "MCPUtils.h"
|
||||||
|
#include "Materials/MaterialInstanceConstant.h"
|
||||||
|
#include "Misc/DefaultValueHelper.h"
|
||||||
|
#include "MaterialInstance_SetParameter.generated.h"
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class UMCP_MaterialInstance_SetParameter : public UObject, public IMCPHandler
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
UPROPERTY(meta=(Description="Material Instance path"))
|
||||||
|
FString Path;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Description="Parameter name to set"))
|
||||||
|
FString Parameter;
|
||||||
|
|
||||||
|
UPROPERTY(meta=(Description="Value to set (uses Unreal text format, e.g. '0.5' for scalar, '(R=1,G=0,B=0,A=1)' for vector)"))
|
||||||
|
FString Value;
|
||||||
|
|
||||||
|
virtual FString GetDescription() const override
|
||||||
|
{
|
||||||
|
return TEXT("Set a parameter override on a Material Instance.");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
||||||
|
{
|
||||||
|
MCPFetcher F(Result);
|
||||||
|
UMaterialInstanceConstant* MI = F.Asset(Path).Cast<UMaterialInstanceConstant>();
|
||||||
|
if (!MI) return;
|
||||||
|
|
||||||
|
auto Parameters = MCPUtils::MaterialParameters(MI);
|
||||||
|
|
||||||
|
// Find a parameter with a matching name.
|
||||||
|
FName Name(*Parameter);
|
||||||
|
MCPMaterialParameter* Found = nullptr;
|
||||||
|
int MatchCount = 0;
|
||||||
|
for (auto& P : Parameters)
|
||||||
|
{
|
||||||
|
if (P.Name == Name)
|
||||||
|
{
|
||||||
|
MatchCount++;
|
||||||
|
Found = &P;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (MatchCount > 1)
|
||||||
|
{
|
||||||
|
Result.Appendf(TEXT("More than one parameter named '%s'"), *Parameter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (MatchCount == 0)
|
||||||
|
{
|
||||||
|
Result.Appendf(TEXT("No parameter named '%s'"), *Parameter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Found->Metadata.PrimitiveDataIndex != INDEX_NONE)
|
||||||
|
{
|
||||||
|
Result.Appendf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMaterialParameterInfo Info(*Parameter);
|
||||||
|
EMaterialParameterType Type = Found->Metadata.Value.Type;
|
||||||
|
|
||||||
|
F.PreEdit();
|
||||||
|
switch (Type)
|
||||||
|
{
|
||||||
|
case EMaterialParameterType::Scalar:
|
||||||
|
{
|
||||||
|
float ScalarValue;
|
||||||
|
if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue))
|
||||||
|
{
|
||||||
|
Result.Appendf(TEXT("Failed to parse '%s' as a float"), *Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MI->SetScalarParameterValueEditorOnly(Info, ScalarValue);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case EMaterialParameterType::Vector:
|
||||||
|
{
|
||||||
|
FLinearColor Color;
|
||||||
|
if (!Color.InitFromString(Value))
|
||||||
|
{
|
||||||
|
Result.Appendf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MI->SetVectorParameterValueEditorOnly(Info, Color);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
Result.Appendf(TEXT("Unsupported parameter type for '%s'"), *Parameter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
F.PostEdit();
|
||||||
|
MCPUtils::SaveGenericPackage(MI);
|
||||||
|
|
||||||
|
Result.Appendf(TEXT("Set '%s' = %s on %s\n"),
|
||||||
|
*Parameter, *Value, *MCPUtils::FormatName(MI));
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -48,7 +48,6 @@ MCPFetcher& MCPFetcher::Walk(const FString& Path)
|
|||||||
{
|
{
|
||||||
if (bError) return *this;
|
if (bError) return *this;
|
||||||
|
|
||||||
// Split on commas
|
|
||||||
TArray<FString> Segments;
|
TArray<FString> Segments;
|
||||||
Path.ParseIntoArray(Segments, TEXT(","));
|
Path.ParseIntoArray(Segments, TEXT(","));
|
||||||
if (Segments.Num() == 0)
|
if (Segments.Num() == 0)
|
||||||
@@ -57,46 +56,33 @@ MCPFetcher& MCPFetcher::Walk(const FString& Path)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no object yet, first segment is an asset path
|
for (int32 i = 0; i < Segments.Num(); i++)
|
||||||
int32 Start = 0;
|
|
||||||
if (!Obj && !ResultPin)
|
|
||||||
{
|
{
|
||||||
LoadUAsset(Segments[0]);
|
if (!Obj && !ResultPin)
|
||||||
if (bError) return *this;
|
{
|
||||||
Start = 1;
|
Asset(Segments[i]);
|
||||||
}
|
if (bError) return *this;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const TArray<FWalker>& Walkers = GetWalkerTable();
|
|
||||||
|
|
||||||
// Walk each subsequent segment
|
|
||||||
for (int32 i = Start; i < Segments.Num(); i++)
|
|
||||||
{
|
|
||||||
FString Key, Value;
|
FString Key, Value;
|
||||||
if (!Segments[i].Split(TEXT(":"), &Key, &Value))
|
if (!Segments[i].Split(TEXT(":"), &Key, &Value))
|
||||||
Key = Segments[i];
|
Key = Segments[i];
|
||||||
|
|
||||||
bool bFound = false;
|
const FWalker* W = GetWalker(Key);
|
||||||
for (const FWalker& W : Walkers)
|
if (!W)
|
||||||
{
|
{
|
||||||
if (!StrEq(Key, W.Key)) continue;
|
SetError(FString::Printf(TEXT("Unknown path step '%s'"), *Key));
|
||||||
(this->*W.Func)(Value);
|
|
||||||
bFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bFound)
|
|
||||||
{
|
|
||||||
SetError(FString::Printf(TEXT("Unknown walker '%s'"), *Key));
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
(this->*W->Func)(Value);
|
||||||
if (bError) return *this;
|
if (bError) return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MCPFetcher::LoadUAsset(const FString& PackagePath)
|
MCPFetcher& MCPFetcher::Asset(const FString& PackagePath)
|
||||||
{
|
{
|
||||||
SetObj(LoadObject<UObject>(nullptr, *PackagePath));
|
SetObj(LoadObject<UObject>(nullptr, *PackagePath));
|
||||||
if (!Obj)
|
if (!Obj)
|
||||||
@@ -105,6 +91,17 @@ void MCPFetcher::LoadUAsset(const FString& PackagePath)
|
|||||||
// If this is a material open in the editor, use the editor's transient copy.
|
// If this is a material open in the editor, use the editor's transient copy.
|
||||||
if (UMaterial* Mat = ::Cast<UMaterial>(Obj))
|
if (UMaterial* Mat = ::Cast<UMaterial>(Obj))
|
||||||
SetObj(MCPUtils::ReplaceMaterialWithTransientCopy(Mat));
|
SetObj(MCPUtils::ReplaceMaterialWithTransientCopy(Mat));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MCPFetcher::FWalker* MCPFetcher::GetWalker(const FString& Key)
|
||||||
|
{
|
||||||
|
for (const FWalker& W : GetWalkerTable())
|
||||||
|
{
|
||||||
|
if (StrEq(Key, W.Key))
|
||||||
|
return &W;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MCPFetcher& MCPFetcher::Graph(const FString& Value)
|
MCPFetcher& MCPFetcher::Graph(const FString& Value)
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
#include "MaterialGraph/MaterialGraphNode.h"
|
#include "MaterialGraph/MaterialGraphNode.h"
|
||||||
#include "MaterialGraph/MaterialGraphSchema.h"
|
#include "MaterialGraph/MaterialGraphSchema.h"
|
||||||
#include "IMaterialEditor.h"
|
#include "IMaterialEditor.h"
|
||||||
|
#include "MaterialEditingLibrary.h"
|
||||||
#include "Subsystems/AssetEditorSubsystem.h"
|
#include "Subsystems/AssetEditorSubsystem.h"
|
||||||
|
|
||||||
// Mesh, animation, texture support
|
// Mesh, animation, texture support
|
||||||
@@ -1125,6 +1126,8 @@ void MCPUtils::PreEdit(const TArray<UObject*>& Objects)
|
|||||||
|
|
||||||
void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
||||||
{
|
{
|
||||||
|
TSet<UMaterial*> Materials;
|
||||||
|
TSet<UBlueprint*> Blueprints;
|
||||||
for (int32 i = Objects.Num() - 1; i >= 0; --i)
|
for (int32 i = Objects.Num() - 1; i >= 0; --i)
|
||||||
{
|
{
|
||||||
UObject* Obj = Objects[i];
|
UObject* Obj = Objects[i];
|
||||||
@@ -1132,8 +1135,36 @@ void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
|||||||
Obj->MarkPackageDirty();
|
Obj->MarkPackageDirty();
|
||||||
|
|
||||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
||||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
Blueprints.Add(BP);
|
||||||
|
|
||||||
|
if (UMaterialInterface* MatIface = Cast<UMaterialInterface>(Obj))
|
||||||
|
if (UMaterial* BaseMat = MatIface->GetMaterial())
|
||||||
|
Materials.Add(BaseMat);
|
||||||
}
|
}
|
||||||
|
for (UMaterial *Material : Materials)
|
||||||
|
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(Material);
|
||||||
|
for (UBlueprint *Blueprint : Blueprints)
|
||||||
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
||||||
|
|
||||||
|
if (GEditor)
|
||||||
|
GEditor->RedrawAllViewports();
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<MCPMaterialParameter> MCPUtils::MaterialParameters(UMaterialInstanceConstant *MI)
|
||||||
|
{
|
||||||
|
TArray<MCPMaterialParameter> Results;
|
||||||
|
UMaterial* BaseMat = MI->GetMaterial();
|
||||||
|
if (!BaseMat) return Results;
|
||||||
|
for (UMaterialExpression* Expr : BaseMat->GetExpressions())
|
||||||
|
{
|
||||||
|
if (!Expr || !Expr->bIsParameterExpression) continue;
|
||||||
|
MCPMaterialParameter P;
|
||||||
|
P.Name = Expr->GetParameterName();
|
||||||
|
if (P.Name.IsNone()) continue;
|
||||||
|
if (!Expr->GetParameterValue(P.Metadata)) continue;
|
||||||
|
Results.Add(P);
|
||||||
|
}
|
||||||
|
return Results;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MCPUtils::SaveMaterialPackage(UMaterial* Material)
|
bool MCPUtils::SaveMaterialPackage(UMaterial* Material)
|
||||||
@@ -1790,6 +1821,19 @@ bool MCPUtils::SetPropertyValueText(UObject* Container, FProperty* Prop, const F
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MCPUtils::SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner, MCPErrorCallback Error)
|
||||||
|
{
|
||||||
|
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
||||||
|
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, Owner, PPF_None);
|
||||||
|
if (!ImportResult)
|
||||||
|
{
|
||||||
|
Error.SetError(FString::Printf(TEXT("Failed to parse '%s' for property '%s' (type: %s)"),
|
||||||
|
*Value, *FormatName(Prop), *Prop->GetCPPType()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// SearchProperties
|
// SearchProperties
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ public:
|
|||||||
MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {}
|
MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {}
|
||||||
MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {}
|
MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {}
|
||||||
|
|
||||||
|
// Starting point is always Asset.
|
||||||
|
MCPFetcher& Asset(const FString& PackagePath);
|
||||||
|
|
||||||
// Walk one step.
|
// Walk one step.
|
||||||
MCPFetcher& Graph(const FString& Value);
|
MCPFetcher& Graph(const FString& Value);
|
||||||
MCPFetcher& Node(const FString& Value);
|
MCPFetcher& Node(const FString& Value);
|
||||||
@@ -43,13 +46,13 @@ public:
|
|||||||
MCPFetcher& LevelBlueprint(const FString& Value);
|
MCPFetcher& LevelBlueprint(const FString& Value);
|
||||||
MCPFetcher& MatExp(const FString& Value);
|
MCPFetcher& MatExp(const FString& Value);
|
||||||
|
|
||||||
|
// Parse string and walk multiple steps.
|
||||||
|
MCPFetcher& Walk(const FString& Path);
|
||||||
|
|
||||||
// C++-only walk step: resolve to the editable template
|
// C++-only walk step: resolve to the editable template
|
||||||
// (e.g. Blueprint → CDO, everything else → as-is).
|
// (e.g. Blueprint → CDO, everything else → as-is).
|
||||||
MCPFetcher& Template();
|
MCPFetcher& Template();
|
||||||
|
|
||||||
// Parse string and walk multiple steps.
|
|
||||||
MCPFetcher& Walk(const FString& Path);
|
|
||||||
|
|
||||||
// Walker table entry.
|
// Walker table entry.
|
||||||
struct FWalker
|
struct FWalker
|
||||||
{
|
{
|
||||||
@@ -74,6 +77,7 @@ public:
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TArray<UObject*> Chain;
|
TArray<UObject*> Chain;
|
||||||
static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); }
|
static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); }
|
||||||
@@ -81,7 +85,7 @@ private:
|
|||||||
void SetPin(UEdGraphPin* InPin) { ResultPin = InPin; Obj = nullptr; }
|
void SetPin(UEdGraphPin* InPin) { ResultPin = InPin; Obj = nullptr; }
|
||||||
MCPFetcher& SetError(const FString& Msg);
|
MCPFetcher& SetError(const FString& Msg);
|
||||||
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
|
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
|
||||||
void LoadUAsset(const FString& PackagePath);
|
const FWalker* GetWalker(const FString& Key);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>()
|
template<> inline UEdGraphPin* MCPFetcher::Cast<UEdGraphPin>()
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "CoreMinimal.h"
|
#include "CoreMinimal.h"
|
||||||
#include "Dom/JsonObject.h"
|
#include "Dom/JsonObject.h"
|
||||||
#include "EdGraph/EdGraphPin.h"
|
#include "EdGraph/EdGraphPin.h"
|
||||||
|
#include "Materials/MaterialInstanceConstant.h"
|
||||||
|
#include "MaterialTypes.h"
|
||||||
|
|
||||||
class UBlueprint;
|
class UBlueprint;
|
||||||
class UEdGraph;
|
class UEdGraph;
|
||||||
@@ -96,6 +98,12 @@ struct MCPErrorCallback
|
|||||||
void SetError(const FString& Msg) const { Func(Msg); }
|
void SetError(const FString& Msg) const { Func(Msg); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MCPMaterialParameter
|
||||||
|
{
|
||||||
|
FName Name;
|
||||||
|
FMaterialParameterMetadata Metadata;
|
||||||
|
};
|
||||||
|
|
||||||
// Stateless utility functions used by MCP handlers and the MCP server.
|
// Stateless utility functions used by MCP handlers and the MCP server.
|
||||||
// This is effectively a namespace — all methods are static.
|
// This is effectively a namespace — all methods are static.
|
||||||
class MCPUtils
|
class MCPUtils
|
||||||
@@ -277,6 +285,9 @@ public:
|
|||||||
static void PreEdit(const TArray<UObject*>& Objects);
|
static void PreEdit(const TArray<UObject*>& Objects);
|
||||||
static void PostEdit(const TArray<UObject*>& Objects);
|
static void PostEdit(const TArray<UObject*>& Objects);
|
||||||
|
|
||||||
|
// ----- Material Instance -----
|
||||||
|
static TArray<MCPMaterialParameter> MaterialParameters(UMaterialInstanceConstant *MI);
|
||||||
|
|
||||||
// ----- Editable template -----
|
// ----- Editable template -----
|
||||||
// Given an object, returns the appropriate template object for generic
|
// Given an object, returns the appropriate template object for generic
|
||||||
// property editing, or nullptr if the type isn't whitelisted.
|
// property editing, or nullptr if the type isn't whitelisted.
|
||||||
@@ -287,6 +298,7 @@ public:
|
|||||||
static FProperty* FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error = nullptr);
|
static FProperty* FindPropertyByName(UObject* Obj, const FString& Name, MCPErrorCallback Error = nullptr);
|
||||||
static FString GetPropertyValueText(UObject* Container, FProperty* Prop);
|
static FString GetPropertyValueText(UObject* Container, FProperty* Prop);
|
||||||
static bool SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value, MCPErrorCallback Error = nullptr);
|
static bool SetPropertyValueText(UObject* Container, FProperty* Prop, const FString& Value, MCPErrorCallback Error = nullptr);
|
||||||
|
static bool SetPropertyValueText(void* Container, FProperty* Prop, const FString& Value, UObject* Owner, MCPErrorCallback Error = nullptr);
|
||||||
|
|
||||||
// ----- Property population -----
|
// ----- Property population -----
|
||||||
static FString PropertyNameToJsonKey(const FString& PropName);
|
static FString PropertyNameToJsonKey(const FString& PropName);
|
||||||
|
|||||||
Reference in New Issue
Block a user