More progress on MCP
This commit is contained in:
BIN
Content/Testing/M_Kaleidoscope.uasset
LFS
Normal file
BIN
Content/Testing/M_Kaleidoscope.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Testing/M_Mandelbrot.uasset
LFS
Normal file
BIN
Content/Testing/M_Mandelbrot.uasset
LFS
Normal file
Binary file not shown.
BIN
Content/Testing/M_Test.uasset
LFS
BIN
Content/Testing/M_Test.uasset
LFS
Binary file not shown.
@@ -3,11 +3,12 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPNotifier.h"
|
||||
#include "MCPProperty.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "MaterialGraph/MaterialGraphSchema.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "GraphNode_SetDefaults.generated.h"
|
||||
|
||||
|
||||
@@ -53,9 +54,9 @@ public:
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema,
|
||||
TSet<UEdGraphNode*>& ModifiedNodes, FStringBuilderBase& Result)
|
||||
MCPNotifier& N, FStringBuilderBase& Result)
|
||||
{
|
||||
MCPFetcher F(Result, GraphObj);
|
||||
MCPFetcher F(Result, N, GraphObj);
|
||||
UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
|
||||
if (!Pin) return;
|
||||
|
||||
@@ -79,9 +80,8 @@ public:
|
||||
Result.Appendf(TEXT("error: %s: %s\n"), *MCPUtils::FormatName(Pin), *Error);
|
||||
return;
|
||||
}
|
||||
N.PreEditAddObject(Node);
|
||||
K2Schema->TrySetDefaultValue(*Pin, Entry.Value);
|
||||
|
||||
ModifiedNodes.Add(Node);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -89,34 +89,28 @@ public:
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj,
|
||||
TSet<UEdGraphNode*>& ModifiedNodes, FStringBuilderBase& Result)
|
||||
MCPNotifier& N, FStringBuilderBase& Result)
|
||||
{
|
||||
MCPFetcher F(Result, GraphObj);
|
||||
UMaterialGraphNode* MatNode = F.Node(Entry.Node).Cast<UMaterialGraphNode>();
|
||||
if (!MatNode) return;
|
||||
if (!MatNode->MaterialExpression)
|
||||
{
|
||||
Result.Appendf(TEXT("error: %s has no material expression\n"), *MCPUtils::FormatName(MatNode));
|
||||
MCPFetcher F(Result, N, GraphObj);
|
||||
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
|
||||
if (!Node) return;
|
||||
|
||||
MCPProperty P = MCPProperty::GetOneExactMatch(Node, CPF_Edit, Entry.Name, Result);
|
||||
if (!P) return;
|
||||
|
||||
N.PreEditAddObject(Node);
|
||||
|
||||
if (!P.SetText(Entry.Value, Result))
|
||||
return;
|
||||
}
|
||||
|
||||
UMaterialExpression* Expression = MatNode->MaterialExpression;
|
||||
FProperty* Prop = MCPUtils::FindPropertyByName(Expression, Entry.Name, Result);
|
||||
if (!Prop) return;
|
||||
|
||||
if (!MCPUtils::SetPropertyValueText(Expression, Prop, Entry.Value, Result))
|
||||
return;
|
||||
|
||||
Expression->ForcePropertyValueChanged(Prop);
|
||||
ModifiedNodes.Add(MatNode);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
virtual void Handle(FStringBuilderBase& Result) override
|
||||
{
|
||||
// Fetch the graph once.
|
||||
MCPFetcher GraphFetcher(Result);
|
||||
MCPNotifier N;
|
||||
MCPFetcher GraphFetcher(Result, N);
|
||||
UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!GraphObj) return;
|
||||
|
||||
@@ -130,9 +124,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
TSet<UEdGraphNode*> ModifiedNodes;
|
||||
|
||||
GraphFetcher.PreEdit();
|
||||
N.PreEdit();
|
||||
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
|
||||
{
|
||||
FSetNodeDefaultEntry Entry;
|
||||
@@ -140,16 +132,12 @@ public:
|
||||
continue;
|
||||
|
||||
if (K2Schema)
|
||||
HandleK2Entry(Entry, GraphObj, K2Schema, ModifiedNodes, Result);
|
||||
HandleK2Entry(Entry, GraphObj, K2Schema, N, Result);
|
||||
else if (MGSchema)
|
||||
HandleMaterialEntry(Entry, GraphObj, ModifiedNodes, Result);
|
||||
HandleMaterialEntry(Entry, GraphObj, N, Result);
|
||||
}
|
||||
|
||||
for (UEdGraphNode* Node : ModifiedNodes)
|
||||
{
|
||||
Node->ReconstructNode();
|
||||
}
|
||||
GraphFetcher.PostEdit();
|
||||
N.PostEdit();
|
||||
|
||||
Result.Appendf(TEXT("Done.\n"));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPProperty.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Property_Dump.generated.h"
|
||||
|
||||
@@ -42,14 +43,19 @@ public:
|
||||
UObject* Template = F.Walk(Path).Template().Cast<UObject>();
|
||||
if (!Template) return;
|
||||
|
||||
TArray<FProperty*> Props = MCPUtils::SearchProperties(Template, Query, CPF_Edit, Local);
|
||||
TArray<MCPProperty> Props = MCPProperty::GetAllSubstring(Template, CPF_Edit, Query);
|
||||
if (Local)
|
||||
{
|
||||
UClass* ObjClass = Template->GetClass();
|
||||
Props.RemoveAll([ObjClass](const MCPProperty& P) { return P->GetOwnerStruct() != ObjClass; });
|
||||
}
|
||||
|
||||
// Group properties by category.
|
||||
TMap<FString, TArray<FProperty*>> ByCategory;
|
||||
for (FProperty* Prop : Props)
|
||||
TMap<FString, TArray<MCPProperty>> ByCategory;
|
||||
for (MCPProperty& P : Props)
|
||||
{
|
||||
FString Category = Prop->HasMetaData(TEXT("Category")) ? Prop->GetMetaData(TEXT("Category")) : FString();
|
||||
ByCategory.FindOrAdd(Category).Add(Prop);
|
||||
FString Category = P->HasMetaData(TEXT("Category")) ? P->GetMetaData(TEXT("Category")) : FString();
|
||||
ByCategory.FindOrAdd(Category).Add(P);
|
||||
}
|
||||
|
||||
// Sort category names, putting empty category last.
|
||||
@@ -68,18 +74,18 @@ public:
|
||||
else
|
||||
Result.Appendf(TEXT("\n%s:\n"), *Category);
|
||||
|
||||
for (FProperty* Prop : ByCategory[Category])
|
||||
for (MCPProperty& P : ByCategory[Category])
|
||||
{
|
||||
FString PropName = MCPUtils::FormatName(Prop);
|
||||
FString ValueStr = MCPUtils::GetPropertyValueText(Template, Prop);
|
||||
FString PropName = MCPUtils::FormatName(P.Prop);
|
||||
FString ValueStr = P.GetText();
|
||||
|
||||
if (Truncate && (ValueStr.Len() > 80))
|
||||
ValueStr = ValueStr.Left(80) + TEXT("...");
|
||||
|
||||
bool bEditable = !Prop->HasAnyPropertyFlags(CPF_EditConst);
|
||||
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
|
||||
Result.Appendf(TEXT(" %s %s %s = %s\n"),
|
||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||
*MCPUtils::FormatPropertyType(Prop),
|
||||
*MCPUtils::FormatPropertyType(P.Prop),
|
||||
*PropName,
|
||||
*ValueStr);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPProperty.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Property_Get.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UMCP_Property_Get : public UObject, public IMCPHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="MCPFetcher path to the object (e.g. /Game/Materials/M_Gold or /Game/Tangibles/TAN_Char,component:Mesh0)"))
|
||||
FString Path;
|
||||
|
||||
UPROPERTY(meta=(Description="Property name"))
|
||||
FString Property;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Get the value of a single property on an object resolved via MCPFetcher path.");
|
||||
}
|
||||
|
||||
virtual void Handle(FStringBuilderBase& Result) override
|
||||
{
|
||||
MCPFetcher F(Result);
|
||||
UObject* Template = F.Walk(Path).Template().Cast<UObject>();
|
||||
if (!Template) return;
|
||||
|
||||
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Property, Result);
|
||||
if (!P) return;
|
||||
|
||||
Result.Append(P.GetText());
|
||||
Result.Append(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPHandler.h"
|
||||
#include "MCPFetcher.h"
|
||||
#include "MCPProperty.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Property_Set.generated.h"
|
||||
|
||||
@@ -42,17 +43,12 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// Validation pass — check all properties before modifying anything.
|
||||
// Validation pass — resolve all properties and values before modifying anything.
|
||||
TArray<TPair<MCPProperty, FString>> Resolved;
|
||||
for (const auto& Pair : Properties.Json->Values)
|
||||
{
|
||||
FProperty* Prop = MCPUtils::FindPropertyByName(Template, Pair.Key, Result);
|
||||
if (!Prop) return;
|
||||
|
||||
if (!Prop->HasAnyPropertyFlags(CPF_Edit))
|
||||
{
|
||||
Result.Appendf(TEXT("Error: Property '%s' is not editable (no Edit flag)\n"), *Pair.Key);
|
||||
return;
|
||||
}
|
||||
MCPProperty P = MCPProperty::GetOneExactMatch(Template, CPF_Edit, Pair.Key, Result);
|
||||
if (!P) return;
|
||||
|
||||
FString ValueStr;
|
||||
if (!Pair.Value->TryGetString(ValueStr))
|
||||
@@ -60,25 +56,17 @@ public:
|
||||
Result.Appendf(TEXT("Error: Value for '%s' must be a string\n"), *Pair.Key);
|
||||
return;
|
||||
}
|
||||
Resolved.Emplace(P, ValueStr);
|
||||
}
|
||||
|
||||
// Apply all changes in a single Pre/PostEditChange bracket.
|
||||
F.PreEdit();
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
for (const auto& Pair : Properties.Json->Values)
|
||||
for (auto& [P, ValueStr] : Resolved)
|
||||
{
|
||||
FProperty* Prop = MCPUtils::FindPropertyByName(Template, Pair.Key);
|
||||
FString ValueStr;
|
||||
Pair.Value->TryGetString(ValueStr);
|
||||
|
||||
FString OldValue = MCPUtils::GetPropertyValueText(Template, Prop);
|
||||
|
||||
if (!MCPUtils::SetPropertyValueText(Template, Prop, ValueStr, Result))
|
||||
if (!P.SetText(ValueStr, Result))
|
||||
continue;
|
||||
|
||||
FString NewValue = MCPUtils::GetPropertyValueText(Template, Prop);
|
||||
Result.Appendf(TEXT("%s: %s -> %s\n"), *MCPUtils::FormatName(Prop), *OldValue, *NewValue);
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -330,3 +330,4 @@ MCPFetcher& MCPFetcher::ToGraph()
|
||||
|
||||
return TypeMismatch(TEXT("ToGraph"), TEXT("Graph or Material"));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "MCPNotifier.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "MaterialEditingLibrary.h"
|
||||
|
||||
void MCPNotifier::PreEditAddObject(UObject* Obj)
|
||||
{
|
||||
if (!Obj) return;
|
||||
bool bAlreadyInSet = false;
|
||||
TouchedSet.Add(Obj, &bAlreadyInSet);
|
||||
if (bAlreadyInSet) return;
|
||||
TouchedArray.Add(Obj);
|
||||
if (bInsidePrePost)
|
||||
Obj->PreEditChange(nullptr);
|
||||
}
|
||||
|
||||
void MCPNotifier::PreEdit()
|
||||
{
|
||||
bInsidePrePost = true;
|
||||
for (UObject* Obj : TouchedArray)
|
||||
Obj->PreEditChange(nullptr);
|
||||
}
|
||||
|
||||
void MCPNotifier::PostEdit()
|
||||
{
|
||||
TSet<UEdGraphNode*> Nodes;
|
||||
TSet<UEdGraph*> Graphs;
|
||||
TSet<UMaterial*> Materials;
|
||||
TSet<UBlueprint*> Blueprints;
|
||||
for (int32 i = TouchedArray.Num() - 1; i >= 0; --i)
|
||||
{
|
||||
UObject* Obj = TouchedArray[i];
|
||||
Obj->PostEditChange();
|
||||
Obj->MarkPackageDirty();
|
||||
|
||||
if (UEdGraphNode* Node = ::Cast<UEdGraphNode>(Obj))
|
||||
Nodes.Add(Node);
|
||||
|
||||
if (UEdGraph* Graph = ::Cast<UEdGraph>(Obj))
|
||||
Graphs.Add(Graph);
|
||||
|
||||
if (UBlueprint* BP = ::Cast<UBlueprint>(Obj))
|
||||
Blueprints.Add(BP);
|
||||
|
||||
if (UMaterialInterface* MatIface = ::Cast<UMaterialInterface>(Obj))
|
||||
if (UMaterial* BaseMat = MatIface->GetMaterial())
|
||||
Materials.Add(BaseMat);
|
||||
}
|
||||
for (UEdGraphNode* Node : Nodes)
|
||||
Node->ReconstructNode();
|
||||
for (UEdGraph* Graph : Graphs)
|
||||
Graph->NotifyGraphChanged();
|
||||
for (UMaterial *Material : Materials)
|
||||
UMaterialEditingLibrary::RebuildMaterialInstanceEditors(Material);
|
||||
for (UBlueprint *Blueprint : Blueprints)
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
||||
|
||||
if (GEditor)
|
||||
GEditor->RedrawAllViewports();
|
||||
|
||||
bInsidePrePost = false;
|
||||
}
|
||||
@@ -1,18 +1,22 @@
|
||||
#include "MCPProperty.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "Materials/MaterialExpression.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
|
||||
MCPProperty::MCPProperty(FProperty* InProp, void* Container)
|
||||
: Prop(InProp), ValuePtr(InProp ? InProp->ContainerPtrToValuePtr<void>(Container) : nullptr) {}
|
||||
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, MCPErrorCallback Error)
|
||||
{
|
||||
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
||||
const TCHAR* ImportResult = Prop->ImportText_Direct(*Value, ValuePtr, nullptr, PPF_None);
|
||||
if (!ImportResult)
|
||||
{
|
||||
@@ -20,5 +24,78 @@ bool MCPProperty::SetText(const FString& Value, MCPErrorCallback Error)
|
||||
*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;
|
||||
}
|
||||
|
||||
TArray<MCPProperty> MCPProperty::GetAll(UObject* Obj, EPropertyFlags Flags)
|
||||
{
|
||||
TArray<MCPProperty> Result;
|
||||
if (!Obj) return Result;
|
||||
for (TFieldIterator<FProperty> It(Obj->GetClass()); It; ++It)
|
||||
{
|
||||
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
|
||||
Result.Emplace(*It, Obj);
|
||||
}
|
||||
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Obj))
|
||||
{
|
||||
if (UMaterialExpression* Expr = MatNode->MaterialExpression)
|
||||
{
|
||||
for (TFieldIterator<FProperty> It(Expr->GetClass()); It; ++It)
|
||||
{
|
||||
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
|
||||
Result.Emplace(*It, Expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
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, MCPErrorCallback Error)
|
||||
{
|
||||
TArray<MCPProperty> Matches = GetAllExactMatch(Obj, Flags, Name);
|
||||
if (Matches.Num() == 0)
|
||||
{
|
||||
Error.SetError(FString::Printf(TEXT("Property '%s' not found on %s"),
|
||||
*Name, *MCPUtils::FormatName(Obj->GetClass())));
|
||||
return MCPProperty();
|
||||
}
|
||||
if (Matches.Num() > 1)
|
||||
{
|
||||
Error.SetError(FString::Printf(TEXT("Ambiguous property '%s' on %s"),
|
||||
*Name, *MCPUtils::FormatName(Obj->GetClass())));
|
||||
return MCPProperty();
|
||||
}
|
||||
return Matches[0];
|
||||
}
|
||||
|
||||
@@ -810,6 +810,7 @@ void MCPUtils::PreEdit(const TArray<UObject*>& Objects)
|
||||
|
||||
void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
||||
{
|
||||
TSet<UEdGraphNode*> Nodes;
|
||||
TSet<UEdGraph*> Graphs;
|
||||
TSet<UMaterial*> Materials;
|
||||
TSet<UBlueprint*> Blueprints;
|
||||
@@ -819,6 +820,9 @@ void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
||||
Obj->PostEditChange();
|
||||
Obj->MarkPackageDirty();
|
||||
|
||||
if (UEdGraphNode* Node = Cast<UEdGraphNode>(Obj))
|
||||
Nodes.Add(Node);
|
||||
|
||||
if (UEdGraph* Graph = Cast<UEdGraph>(Obj))
|
||||
Graphs.Add(Graph);
|
||||
|
||||
@@ -829,6 +833,8 @@ void MCPUtils::PostEdit(const TArray<UObject*>& Objects)
|
||||
if (UMaterial* BaseMat = MatIface->GetMaterial())
|
||||
Materials.Add(BaseMat);
|
||||
}
|
||||
for (UEdGraphNode* Node : Nodes)
|
||||
Node->ReconstructNode();
|
||||
for (UEdGraph* Graph : Graphs)
|
||||
Graph->NotifyGraphChanged();
|
||||
for (UMaterial *Material : Materials)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "MCPUtils.h"
|
||||
#include "MCPNotifier.h"
|
||||
|
||||
class UEdGraphPin;
|
||||
class IAssetEditorInstance;
|
||||
@@ -29,7 +30,9 @@ class MCPFetcher
|
||||
{
|
||||
public:
|
||||
MCPFetcher(MCPErrorCallback CB) : ErrorCB(CB) {}
|
||||
MCPFetcher(MCPErrorCallback CB, UObject* O) : Obj(O), ErrorCB(CB) {}
|
||||
MCPFetcher(MCPErrorCallback CB, UObject* O) : ErrorCB(CB), Obj(O) {}
|
||||
MCPFetcher(MCPErrorCallback CB, MCPNotifier& N) : ErrorCB(CB), Notifier(&N) {}
|
||||
MCPFetcher(MCPErrorCallback CB, MCPNotifier& N, UObject* O) : ErrorCB(CB), Obj(O), Notifier(&N) {}
|
||||
|
||||
// Starting point is always Asset.
|
||||
MCPFetcher& Asset(const FString& PackagePath);
|
||||
@@ -77,9 +80,7 @@ public:
|
||||
if (!CheckAssetIsA(AssetType::StaticClass())) return nullptr;
|
||||
return static_cast<EditorType*>(Editor);
|
||||
}
|
||||
const TArray<UObject*>& Visited() const { return Chain; }
|
||||
void PreEdit() { MCPUtils::PreEdit(Chain); }
|
||||
void PostEdit() { MCPUtils::PostEdit(Chain); }
|
||||
|
||||
template<class T> T *Cast()
|
||||
{
|
||||
if (bError) return nullptr;
|
||||
@@ -89,17 +90,33 @@ public:
|
||||
return Result;
|
||||
}
|
||||
|
||||
void PreEditAddObject(UObject* Obj) { Notifier->PreEditAddObject(Obj); }
|
||||
void PreEdit() { Notifier->PreEdit(); }
|
||||
void PostEdit() { Notifier->PostEdit(); }
|
||||
|
||||
|
||||
private:
|
||||
bool bError = false;
|
||||
// The error callback is invoked whenever an error is detected.
|
||||
MCPErrorCallback ErrorCB = nullptr;
|
||||
|
||||
// The Current Object or Pin
|
||||
UObject* Obj = nullptr;
|
||||
UEdGraphPin* ResultPin = nullptr;
|
||||
|
||||
// The Starting Asset and the Editor we Opened
|
||||
UObject* OriginalAsset = nullptr;
|
||||
IAssetEditorInstance* Editor = nullptr;
|
||||
UEdGraphPin* ResultPin = nullptr;
|
||||
MCPErrorCallback ErrorCB = nullptr;
|
||||
TArray<UObject*> Chain;
|
||||
|
||||
// True if an error has occurred.
|
||||
bool bError = false;
|
||||
|
||||
// Notifier for tracking touched objects.
|
||||
MCPNotifier OwnedNotifier;
|
||||
MCPNotifier* Notifier = &OwnedNotifier;
|
||||
|
||||
// Internal methods.
|
||||
static bool StrEq(const FString& A, const TCHAR* B) { return A.Equals(B, ESearchCase::IgnoreCase); }
|
||||
void SetObj(UObject* InObj) { if (InObj) Chain.AddUnique(InObj); Obj = InObj; ResultPin = nullptr; }
|
||||
void SetObj(UObject* InObj) { Notifier->PreEditAddObject(InObj); Obj = InObj; ResultPin = nullptr; }
|
||||
void SetPin(UEdGraphPin* InPin) { ResultPin = InPin; Obj = nullptr; }
|
||||
MCPFetcher& SetError(const FString& Msg);
|
||||
MCPFetcher& TypeMismatch(const TCHAR* Walker, const TCHAR* Expected);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
// Tracks objects that have been touched during an editing operation.
|
||||
// Handles PreEditChange/PostEditChange, ReconstructNode, and other
|
||||
// notifications that need to happen after modifications.
|
||||
//
|
||||
// Usage:
|
||||
// MCPNotifier N;
|
||||
// MCPFetcher F(Result, N);
|
||||
// F.Walk(Path)...
|
||||
// N.PreEdit();
|
||||
// // modify stuff
|
||||
// N.PostEdit();
|
||||
//
|
||||
class MCPNotifier
|
||||
{
|
||||
public:
|
||||
void PreEditAddObject(UObject* Obj);
|
||||
void PreEdit();
|
||||
void PostEdit();
|
||||
|
||||
private:
|
||||
bool bInsidePrePost = false;
|
||||
TSet<UObject*> TouchedSet;
|
||||
TArray<UObject*> TouchedArray;
|
||||
};
|
||||
@@ -8,7 +8,7 @@
|
||||
struct MCPProperty
|
||||
{
|
||||
FProperty* Prop = nullptr;
|
||||
void* ValuePtr = nullptr;
|
||||
void* Container = nullptr;
|
||||
|
||||
MCPProperty() = default;
|
||||
MCPProperty(FProperty* InProp, void* Container);
|
||||
@@ -18,4 +18,9 @@ struct MCPProperty
|
||||
|
||||
explicit operator bool() const { return Prop != nullptr; }
|
||||
FProperty* operator->() const { return Prop; }
|
||||
|
||||
static TArray<MCPProperty> GetAll(UObject* Obj, EPropertyFlags Flags);
|
||||
static TArray<MCPProperty> GetAllSubstring(UObject* Obj, EPropertyFlags Flags, const FString& Substring);
|
||||
static TArray<MCPProperty> GetAllExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name);
|
||||
static MCPProperty GetOneExactMatch(UObject* Obj, EPropertyFlags Flags, const FString& Name, MCPErrorCallback Error);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user