diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SetDefaults.h b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SetDefaults.h index 8124234f..8185d9fe 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SetDefaults.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_SetDefaults.h @@ -94,8 +94,8 @@ public: UEdGraphNode* Node = F.Node(Entry.Node).Cast(); if (!Node) return; - TArray All = WingProperty::GetAll(Node, CPF_Edit); - WingProperty P = WingProperty::FindOneExactMatch(All, Entry.Name); + TArray All = FWingProperty::GetAll(Node, CPF_Edit); + FWingProperty P = FWingProperty::FindOneExactMatch(All, Entry.Name); if (!P) return; UWingServer::AddTouchedObject(Node); diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h index ee444a15..5b9684e2 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h @@ -43,17 +43,17 @@ public: WingFetcher F; UObject* Template = F.Walk(Object).Cast(); if (!Template) return; - TArray AllProps = WingProperty::GetAll(Template, CPF_Edit); - TArray Props = WingProperty::FindAllSubstring(AllProps, Query); + TArray AllProps = FWingProperty::GetAll(Template, CPF_Edit); + TArray Props = FWingProperty::FindAllSubstring(AllProps, Query); if (Local) { UClass* ObjClass = Template->GetClass(); - Props.RemoveAll([ObjClass](const WingProperty& P) { return P->GetOwnerStruct() != ObjClass; }); + Props.RemoveAll([ObjClass](const FWingProperty& P) { return P->GetOwnerStruct() != ObjClass; }); } // Group properties by category. - TMap> ByCategory; - for (WingProperty& P : Props) + TMap> ByCategory; + for (FWingProperty& P : Props) { FString Category = P->HasMetaData(TEXT("Category")) ? P->GetMetaData(TEXT("Category")) : FString(); ByCategory.FindOrAdd(Category).Add(P); @@ -75,7 +75,7 @@ public: else UWingServer::Printf(TEXT("\n%s:\n"), *Category); - for (WingProperty& P : ByCategory[Category]) + for (FWingProperty& P : ByCategory[Category]) { FString PropName = WingUtils::FormatName(P.Prop); FString ValueStr = P.GetText(); diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h index 7905ff45..1919c154 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h @@ -36,8 +36,8 @@ public: UObject* Obj = F.Walk(Object).Cast(); if (!Obj) return; - TArray All = WingProperty::GetAll(Obj, CPF_Edit); - WingProperty P = WingProperty::FindOneExactMatch(All, Property); + TArray All = FWingProperty::GetAll(Obj, CPF_Edit); + FWingProperty P = FWingProperty::FindOneExactMatch(All, Property); if (!P) return; UWingServer::Print(P.GetText()); diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h index 404adc25..8719ff46 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h @@ -44,11 +44,11 @@ public: } // Validation pass — resolve all properties and values before modifying anything. - TArray All = WingProperty::GetAll(Obj, CPF_Edit); - TArray> Resolved; + TArray All = FWingProperty::GetAll(Obj, CPF_Edit); + TArray> Resolved; for (const auto& Pair : Properties.Json->Values) { - WingProperty P = WingProperty::FindOneExactMatch(All, Pair.Key); + FWingProperty P = FWingProperty::FindOneExactMatch(All, Pair.Key); if (!P) return; FString ValueStr; diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingBlueprintVar.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingBlueprintVar.cpp index 12d5c774..77b25b67 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingBlueprintVar.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingBlueprintVar.cpp @@ -23,7 +23,7 @@ FWingBlueprintVar::FWingBlueprintVar(UBlueprint* BP, const FString& VarName) UObject* CDO = BP->GeneratedClass->GetDefaultObject(); FProperty* Prop = BP->GeneratedClass->FindPropertyByName(VarFName); if (CDO && Prop) - DefaultValueProp = WingProperty(Prop, CDO); + DefaultValueProp = FWingProperty(Prop, CDO); } } @@ -31,8 +31,8 @@ void FWingBlueprintVar::Dump() { LoadFlags(); LoadDefault(); - TArray Props = MergedProperties(); - for (WingProperty& P : Props) + TArray Props = MergedProperties(); + for (FWingProperty& P : Props) { UWingServer::Printf(TEXT(" %s %s = %s\n"), *UWingTypes::TypeToText(P.Prop), @@ -56,7 +56,7 @@ bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json) LoadFlags(); - TArray Props = MergedProperties(); + TArray Props = MergedProperties(); if (!WingJson::PopulateFromJson(Props, Json, true)) return false; @@ -131,23 +131,23 @@ bool FWingBlueprintVar::SaveDefault() return true; } -TArray FWingBlueprintVar::MergedProperties() +TArray FWingBlueprintVar::MergedProperties() { - TArray Props = WingProperty::GetAll( + TArray Props = FWingProperty::GetAll( FBPVariableDescription::StaticStruct(), Desc, CPF_Edit); - WingProperty::Remove(Props, TEXT("PropertyFlags")); - WingProperty::Remove(Props, TEXT("MetaDataArray")); - WingProperty::Remove(Props, TEXT("VarName")); - WingProperty::Remove(Props, TEXT("VarGuid")); - WingProperty::Remove(Props, TEXT("DefaultValue")); + FWingProperty::Remove(Props, TEXT("PropertyFlags")); + FWingProperty::Remove(Props, TEXT("MetaDataArray")); + FWingProperty::Remove(Props, TEXT("VarName")); + FWingProperty::Remove(Props, TEXT("VarGuid")); + FWingProperty::Remove(Props, TEXT("DefaultValue")); - Props.Append(WingProperty::GetAll( + Props.Append(FWingProperty::GetAll( FWingBlueprintVar::StaticStruct(), this, (EPropertyFlags)0)); // Remove DefaultValue if we don't have a CDO property to back it. if (!DefaultValueProp) - WingProperty::Remove(Props, TEXT("DefaultValue")); + FWingProperty::Remove(Props, TEXT("DefaultValue")); return Props; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp index fd9401a9..cf404dbb 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingFetcher.cpp @@ -27,7 +27,8 @@ WingFetcher::WalkFunc WingFetcher::GetWalker(const FString& Step) void WingFetcher::SetObj(UObject* InObj) { - UWingServer::AddTouchedObject(InObj); + if (!bSkipNotify) + UWingServer::AddTouchedObject(InObj); Obj = InObj; ResultPin = nullptr; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingJson.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingJson.cpp index d27277f7..5afe9faa 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingJson.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingJson.cpp @@ -6,7 +6,7 @@ #include "Dom/JsonValue.h" -bool WingJson::PopulateFromJson(WingProperty& P, const FJsonObject* Json, bool AllOptional) +bool WingJson::PopulateFromJson(FWingProperty& P, const FJsonObject* Json, bool AllOptional) { FString JsonKey = P.Prop->GetName(); bool bOptional = AllOptional || P.Prop->HasMetaData(TEXT("Optional")); @@ -85,13 +85,13 @@ bool WingJson::PopulateFromJson(WingProperty& P, const FJsonObject* Json, bool A } bool WingJson::PopulateFromJson( - TArray& Props, const FJsonObject* Json, bool AllOptional) + TArray& Props, const FJsonObject* Json, bool AllOptional) { bool Ok = true; // Build a set of known property names for the unknown-field check. TSet KnownKeys; - for (const WingProperty& P : Props) + for (const FWingProperty& P : Props) KnownKeys.Add(P.Prop->GetName()); // Check for unknown fields in the JSON @@ -105,7 +105,7 @@ bool WingJson::PopulateFromJson( } // Populate each property from JSON - for (WingProperty& P : Props) + for (FWingProperty& P : Props) { if (!PopulateFromJson(P, Json, AllOptional)) Ok = false; } @@ -115,7 +115,7 @@ bool WingJson::PopulateFromJson( bool WingJson::PopulateFromJson( UStruct* StructType, void* Container, const FJsonObject* Json) { - TArray Props = WingProperty::GetAll(StructType, Container, (EPropertyFlags)0); + TArray Props = FWingProperty::GetAll(StructType, Container, (EPropertyFlags)0); return PopulateFromJson(Props, Json); } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingNotifier.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingNotifier.cpp index 25e5056a..fdd9ee4f 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingNotifier.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingNotifier.cpp @@ -3,29 +3,29 @@ #include "EdGraph/EdGraph.h" #include "Engine/Blueprint.h" #include "Materials/Material.h" +#include "Materials/MaterialExpression.h" #include "Kismet2/BlueprintEditorUtils.h" #include "MaterialEditingLibrary.h" -void WingNotifier::AddTouchedObject(UObject* Obj) +void FWingNotifier::AddTouchedObject(UObject* Obj) { if (!Obj) return; bool bAlreadyInSet = false; TouchedSet.Add(Obj, &bAlreadyInSet); if (bAlreadyInSet) return; TouchedArray.Add(Obj); - Obj->PreEditChange(nullptr); } -void WingNotifier::SendNotifications() +void FWingNotifier::SendNotifications() { TSet Nodes; TSet Graphs; TSet Materials; + TSet MaterialExpressions; TSet Blueprints; for (int32 i = TouchedArray.Num() - 1; i >= 0; --i) { UObject* Obj = TouchedArray[i]; - Obj->PostEditChange(); Obj->MarkPackageDirty(); if (UEdGraphNode* Node = ::Cast(Obj)) @@ -37,12 +37,21 @@ void WingNotifier::SendNotifications() if (UBlueprint* BP = ::Cast(Obj)) Blueprints.Add(BP); + if (UMaterialExpression* Expr = ::Cast(Obj)) + MaterialExpressions.Add(Expr); + + if (UMaterial* Mat = ::Cast(Obj)) + Materials.Add(Mat); + if (UMaterialInterface* MatIface = ::Cast(Obj)) if (UMaterial* BaseMat = MatIface->GetMaterial()) Materials.Add(BaseMat); } - for (UEdGraphNode* Node : Nodes) - Node->ReconstructNode(); + + for (UMaterialExpression* Expr : MaterialExpressions) + Expr->RefreshNode(true); + // for (UEdGraphNode* Node : Nodes) + // Node->ReconstructNode(); for (UEdGraph* Graph : Graphs) Graph->NotifyGraphChanged(); for (UMaterial *Material : Materials) diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp index 38385f90..8fb7c449 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp @@ -3,7 +3,6 @@ #include "WingServer.h" #include "WingTypes.h" #include "Engine/Blueprint.h" -#include "Materials/MaterialExpression.h" #include "MaterialGraph/MaterialGraphNode.h" #include "EdGraph/EdGraphPin.h" #include "UObject/EnumProperty.h" @@ -14,10 +13,13 @@ static bool IsPinTypeProperty(FProperty* Prop) return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct(); } -WingProperty::WingProperty(FProperty* InProp, void* InContainer) +FWingProperty::FWingProperty(FProperty* InProp, void* InContainer) : Prop(InProp), Container(InContainer) {} -FString WingProperty::GetText() const +FWingProperty::FWingProperty(FProperty* InProp, UObject* InContainer) + : Prop(InProp), Container(static_cast(InContainer)) {} + +FString FWingProperty::GetText() const { void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); if (IsPinTypeProperty(Prop)) @@ -27,7 +29,7 @@ FString WingProperty::GetText() const return Result; } -bool WingProperty::TryParseEnum(UEnum* Enum, const FString& Text, int64 &OutValue) +bool FWingProperty::TryParseEnum(UEnum* Enum, const FString& Text, int64 &OutValue) { int Index = Enum->GetIndexByNameString(Text); if (Index == INDEX_NONE) @@ -52,10 +54,14 @@ bool WingProperty::TryParseEnum(UEnum* Enum, const FString& Text, int64 &OutValu } } -bool WingProperty::TrySetText(const FString &Value) +bool FWingProperty::SetText(const FString &Value) { void* ValuePtr = Prop->ContainerPtrToValuePtr(Container); + // Notify that we're modifying the containing object. + if (Prop->GetOwnerClass()) + UWingServer::AddTouchedObject(static_cast(Container)); + // Pin types get parsed by UWingTypes. if (IsPinTypeProperty(Prop)) return UWingTypes::TextToType(Value, *static_cast(ValuePtr)); @@ -92,20 +98,7 @@ bool WingProperty::TrySetText(const FString &Value) return true; } -bool WingProperty::SetText(const FString& Value) -{ - if (!TrySetText(Value)) return false; - - if (Prop->GetOwnerClass()->IsChildOf(UMaterialExpression::StaticClass())) - { - UMaterialExpression* Expr = static_cast(Container); - Expr->ForcePropertyValueChanged(Prop); - } - - return true; -} - -void WingProperty::Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags) +void FWingProperty::Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags) { for (TFieldIterator It(StructType); It; ++It) { @@ -114,15 +107,24 @@ void WingProperty::Collect(UStruct* StructType, void* Container, TArray& Props, const FString& Name) +void FWingProperty::Collect(UObject* Container, TArray &Props, EPropertyFlags Flags) { - Props.RemoveAll([&](const WingProperty& P) { return P.Prop->GetName() == Name; }); + for (TFieldIterator It(Container->GetClass()); It; ++It) + { + if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue; + Props.Emplace(*It, Container); + } } -TArray WingProperty::GetAll(UObject* Obj, EPropertyFlags Flags) +void FWingProperty::Remove(TArray& Props, const FString& Name) +{ + Props.RemoveAll([&](const FWingProperty& P) { return P.Prop->GetName() == Name; }); +} + +TArray FWingProperty::GetAll(UObject* Obj, EPropertyFlags Flags) { if (!Obj) return {}; - TArray Result; + TArray Result; // Blueprints don't have editable properties. So // instead, we fetch properties from the generated CDO, @@ -138,7 +140,7 @@ TArray WingProperty::GetAll(UObject* Obj, EPropertyFlags Flags) Obj = BP->GeneratedClass->GetDefaultObject(); } - Collect(Obj->GetClass(), Obj, Result, Flags); + Collect(Obj, Result, Flags); // If it's a Material Graph node, also collect properties from // the associated material expression. @@ -147,24 +149,24 @@ TArray WingProperty::GetAll(UObject* Obj, EPropertyFlags Flags) { if (UMaterialExpression* Expr = MatNode->MaterialExpression) { - Collect(Expr->GetClass(), Expr, Result, Flags); + Collect(Expr, Result, Flags); } } return Result; } -TArray WingProperty::GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags) +TArray FWingProperty::GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags) { - TArray Result; + TArray Result; Collect(StructType, Container, Result, Flags); return Result; } -TArray WingProperty::FindAllSubstring(const TArray& Props, const FString& Substring) +TArray FWingProperty::FindAllSubstring(const TArray& Props, const FString& Substring) { if (Substring.IsEmpty()) return Props; - TArray Result; - for (const WingProperty& P : Props) + TArray Result; + for (const FWingProperty& P : Props) { if (WingUtils::FormatName(P.Prop).Contains(Substring, ESearchCase::IgnoreCase)) Result.Add(P); @@ -172,10 +174,10 @@ TArray WingProperty::FindAllSubstring(const TArray& return Result; } -WingProperty WingProperty::FindOneExactMatch(const TArray& Props, const FString& Name) +FWingProperty FWingProperty::FindOneExactMatch(const TArray& Props, const FString& Name) { - TArray Matches; - for (const WingProperty& P : Props) + TArray Matches; + for (const FWingProperty& P : Props) { if (WingUtils::Identifies(Name, P.Prop)) Matches.Add(P); @@ -183,12 +185,12 @@ WingProperty WingProperty::FindOneExactMatch(const TArray& Props, if (Matches.Num() == 0) { UWingServer::Printf(TEXT("ERROR: Property '%s' not found\n"), *Name); - return WingProperty(); + return FWingProperty(); } if (Matches.Num() > 1) { UWingServer::Printf(TEXT("ERROR: Ambiguous property '%s'\n"), *Name); - return WingProperty(); + return FWingProperty(); } return Matches[0]; } diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp index 97a908fa..3541c927 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingServer.cpp @@ -304,7 +304,7 @@ void UWingServer::TryCallHandler(const FString &Line) Request->RemoveField(TEXT("command")); // Find the handler UClass for the specified command. - UClass** HandlerClass = WingHandlerRegistry.Find(Command); + TObjectPtr* HandlerClass = WingHandlerRegistry.Find(Command); if (!HandlerClass) { UWingServer::Printf(TEXT("Unknown command: %s"), *Command); diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingBlueprintVar.h b/Plugins/UEWingman/Source/UEWingman/Public/WingBlueprintVar.h index bb0c1d8c..244aac12 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingBlueprintVar.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingBlueprintVar.h @@ -15,7 +15,7 @@ struct FWingBlueprintVar GENERATED_BODY() FBPVariableDescription* Desc = nullptr; - WingProperty DefaultValueProp; + FWingProperty DefaultValueProp; FWingBlueprintVar() = default; FWingBlueprintVar(UBlueprint* BP, const FString& VarName); @@ -54,5 +54,5 @@ private: void LoadDefault(); void SaveFlags(); bool SaveDefault(); - TArray MergedProperties(); + TArray MergedProperties(); }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingFetcher.h b/Plugins/UEWingman/Source/UEWingman/Public/WingFetcher.h index 243142ab..9e108730 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingFetcher.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingFetcher.h @@ -105,6 +105,12 @@ public: return static_cast(Editor); } + // Calling SkipNotify disables change notifications + // for objects visited by this fetcher. Useful for + // read-only operations that don't modify anything. + // + WingFetcher& SkipNotify() { bSkipNotify = true; return *this; } + // Initialize empty. You need to call Asset, or walk // a path that starts with an asset. // @@ -128,6 +134,7 @@ private: // True if an error has occurred. bool bError = false; + bool bSkipNotify = false; // Internal methods. using WalkFunc = WingFetcher& (WingFetcher::*)(const FString&); diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingJson.h b/Plugins/UEWingman/Source/UEWingman/Public/WingJson.h index 3eefa64b..bb5bee3b 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingJson.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingJson.h @@ -10,8 +10,8 @@ class WingJson { public: - static bool PopulateFromJson(WingProperty& Prop, const FJsonObject* Json, bool AllOptional = false); - static bool PopulateFromJson(TArray& Props, const FJsonObject* Json, bool AllOptional = false); + static bool PopulateFromJson(FWingProperty& Prop, const FJsonObject* Json, bool AllOptional = false); + static bool PopulateFromJson(TArray& Props, const FJsonObject* Json, bool AllOptional = false); static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr& JsonValue); static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json); }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingNotifier.h b/Plugins/UEWingman/Source/UEWingman/Public/WingNotifier.h index a9643638..14069b2d 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingNotifier.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingNotifier.h @@ -1,18 +1,24 @@ #pragma once #include "CoreMinimal.h" +#include "WingNotifier.generated.h" // Tracks objects that have been touched during an editing operation. // Handles PreEditChange/PostEditChange, ReconstructNode, and other // notifications that need to happen after modifications. // -class WingNotifier +USTRUCT() +struct FWingNotifier { -public: + GENERATED_BODY() + void AddTouchedObject(UObject* Obj); void SendNotifications(); private: - TSet TouchedSet; - TArray TouchedArray; + UPROPERTY() + TSet> TouchedSet; + + UPROPERTY() + TArray> TouchedArray; }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h b/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h index 81a0bc11..79e0cc71 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h @@ -2,17 +2,17 @@ #include "CoreMinimal.h" #include "WingUtils.h" - // A resolved property: the FProperty descriptor plus a pointer to // the value's storage. operator-> forwards to the FProperty. -struct WingProperty +struct FWingProperty { -public: + FProperty* Prop = nullptr; void* Container = nullptr; - WingProperty() = default; - WingProperty(FProperty* InProp, void* Container); + FWingProperty() = default; + FWingProperty(FProperty* InProp, void* InContainer); + FWingProperty(FProperty* InProp, UObject* InContainer); FString GetText() const; bool SetText(const FString& Value); @@ -20,14 +20,14 @@ public: explicit operator bool() const { return Prop != nullptr; } FProperty* operator->() const { return Prop; } - static void Remove(TArray& Props, const FString& Name); - static TArray GetAll(UObject* Obj, EPropertyFlags Flags); - static TArray GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags); - static TArray FindAllSubstring(const TArray& Props, const FString& Substring); - static WingProperty FindOneExactMatch(const TArray& Props, const FString& Name); + static void Remove(TArray& Props, const FString& Name); + static TArray GetAll(UObject* Obj, EPropertyFlags Flags); + static TArray GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags); + static TArray FindAllSubstring(const TArray& Props, const FString& Substring); + static FWingProperty FindOneExactMatch(const TArray& Props, const FString& Name); private: bool TryParseEnum(UEnum* Enum, const FString& Text, int64 &OutValue); - bool TrySetText(const FString &Text); - static void Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags); + static void Collect(UStruct* StructType, void* Container, TArray &Props, EPropertyFlags Flags); + static void Collect(UObject* Container, TArray &Props, EPropertyFlags Flags); }; diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h index e6580881..2367f4c3 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingServer.h @@ -69,10 +69,12 @@ private: static UWingServer* GWingServer; // ----- Tool dispatch ----- - WingNotifier Notifier; + UPROPERTY() + FWingNotifier Notifier; TStringBuilder<16384> HandlerOutput; FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request - TMap WingHandlerRegistry; // tool name -> UWingHandler subclass + UPROPERTY() + TMap> WingHandlerRegistry; // tool name -> UWingHandler subclass void BuildWingHandlerRegistry(); // Handle a complete JSON line and return the response JSON