diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h index b044f365..1fdd6246 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Dump.h @@ -4,8 +4,9 @@ #include "WingServer.h" #include "WingHandler.h" #include "WingFetcher.h" -#include "WingPropHandle.h" +#include "WingProperty.h" #include "WingUtils.h" +#include "WingTypes.h" #include "Details_Dump.generated.h" UCLASS() @@ -23,7 +24,7 @@ public: virtual void Register() override { UWingServer::AddHandler(this, - TEXT("Test handler: dump properties using IPropertyHandle.")); + TEXT("Test handler: dump properties using FWingProperty.")); } virtual void Handle() override @@ -32,28 +33,31 @@ public: UObject* Target = F.Walk(Object).Cast(); if (!Target) return; - WingPropHandle Props; - WingPropHandle::Handles Handles = Props.GetDetails(Target, false); + TArray Props = FWingProperty::GetDetailsImmutable(Target, CPF_Edit); + Props = FWingProperty::FindAllSubstring(Props, Query); // Group by category, preserving within-category order. - FString QueryLower = Query.ToLower(); - TSortedMap>> Categories; - for (const TSharedPtr& H : Handles) + TSortedMap> Categories; + for (const FWingProperty& P : Props) { - FString Name = WingUtils::FormatName(H); - if (!QueryLower.IsEmpty() && !Name.ToLower().Contains(QueryLower)) - continue; - FString Category = H->GetDefaultCategoryName().ToString(); + FString Category = P.Prop->GetMetaData(TEXT("Category")); if (Category.IsEmpty()) Category = TEXT("Unclassified"); - Categories.FindOrAdd(Category).Add(H); + Categories.FindOrAdd(Category).Add(P); } FStringBuilderBase& Out = UWingServer::GetPrintBuffer(); for (const auto& Pair : Categories) { Out.Appendf(TEXT("\n%s:\n"), *Pair.Key); - for (const TSharedPtr& H : Pair.Value) - WingPropHandle::Print(*H, Out); + for (const FWingProperty& P : Pair.Value) + { + bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst); + Out.Appendf(TEXT(" %s %s %s = %s\n"), + bEditable ? TEXT("editable") : TEXT("readonly"), + *UWingTypes::TypeToText(P.Prop), + *WingUtils::FormatName(P.Prop), + *P.GetTruncatedText(100)); + } } } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Get.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Get.h index 5fd65056..1c9db12c 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Get.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Get.h @@ -4,7 +4,7 @@ #include "WingServer.h" #include "WingHandler.h" #include "WingFetcher.h" -#include "WingPropHandle.h" +#include "WingProperty.h" #include "WingUtils.h" #include "Details_Get.generated.h" @@ -32,11 +32,10 @@ public: UObject* Obj = F.Walk(Object).Cast(); if (!Obj) return; - WingPropHandle Props; - WingPropHandle::Handles Handles = Props.GetDetails(Obj, false); - TSharedPtr* P = WingUtils::FindOneWithExternalID(Property, Handles, TEXT("Property")); + TArray Props = FWingProperty::GetDetailsImmutable(Obj, CPF_Edit); + FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property")); if (!P) return; - UWingServer::Print(WingPropHandle::GetText(**P)); + UWingServer::Print(P->GetText()); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h index 82b7f314..1ea00790 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_Set.h @@ -4,7 +4,7 @@ #include "WingServer.h" #include "WingHandler.h" #include "WingFetcher.h" -#include "WingPropHandle.h" +#include "WingProperty.h" #include "WingUtils.h" #include "Details_Set.generated.h" @@ -35,12 +35,11 @@ public: UObject* Obj = F.Walk(Object).Cast(); if (!Obj) return; - WingPropHandle Props; - WingPropHandle::Handles Handles = Props.GetDetails(Obj, true); - TSharedPtr* P = WingUtils::FindOneWithExternalID(Property, Handles, TEXT("Property")); + TArray Props = FWingProperty::GetDetailsMutable(Obj, CPF_Edit); + FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property")); if (!P) return; - if (WingPropHandle::SetText(**P, Value)) + if (P->SetText(Value)) UWingServer::Print(TEXT("OK\n")); } }; diff --git a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_SetMany.h b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_SetMany.h index 6dbad0bc..774d60a8 100644 --- a/Plugins/UEWingman/Source/UEWingman/Handlers/Details_SetMany.h +++ b/Plugins/UEWingman/Source/UEWingman/Handlers/Details_SetMany.h @@ -4,7 +4,7 @@ #include "WingServer.h" #include "WingHandler.h" #include "WingFetcher.h" -#include "WingPropHandle.h" +#include "WingProperty.h" #include "WingUtils.h" #include "Details_SetMany.generated.h" @@ -38,13 +38,12 @@ public: return; } - WingPropHandle Props; - WingPropHandle::Handles Handles = Props.GetDetails(Obj, true); + TArray Props = FWingProperty::GetDetailsMutable(Obj, CPF_Edit); // Validation pass — resolve all properties before modifying anything. for (const auto& Pair : Properties.Json->Values) { - TSharedPtr* P = WingUtils::FindOneWithExternalID(Pair.Key, Handles, TEXT("Property")); + FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property")); if (!P) return; } @@ -52,8 +51,8 @@ public: int SuccessCount = 0; for (const auto& Pair : Properties.Json->Values) { - TSharedPtr* P = WingUtils::FindOneWithExternalID(Pair.Key, Handles, TEXT("Property")); - if (WingPropHandle::SetJson(**P, Pair.Value)) SuccessCount++; + FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property")); + if (P->SetJson(Pair.Value)) SuccessCount++; } UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num()); diff --git a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp index 744a54ea..97099572 100644 --- a/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp +++ b/Plugins/UEWingman/Source/UEWingman/Private/WingProperty.cpp @@ -62,15 +62,25 @@ void FWingProperty::PrintExpectsReceived(const TCHAR *Type) *WingUtils::FormatName(Prop), Type, *Prop->GetCPPType()); } + +bool FWingProperty::CheckImportTextResult(const FString &Value) +{ + uint8 *VP = Prop->ContainerPtrToValuePtr(Container); + if (FObjectPropertyBase *OProp = CastField(Prop)) + { + UObject *Obj = OProp->GetObjectPropertyValue(VP); + if (Obj == nullptr && Value.TrimStartAndEnd().Compare(TEXT("None"), ESearchCase::IgnoreCase) != 0) + { + UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s'\n"), + *Value, *WingUtils::FormatName(Prop)); + return false; + } + } + return true; +} + bool FWingProperty::SetText(FString Value) { - // Mostly, this is implemented by Unreal's ImportText_Incontainer. - // We override it for a few very specific types. - - // 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)) { @@ -111,20 +121,101 @@ bool FWingProperty::SetText(FString Value) return true; } -bool FWingProperty::CheckImportTextResult(const FString &Value) +bool FWingProperty::SetDouble(double D) { - uint8 *VP = Prop->ContainerPtrToValuePtr(Container); - if (FObjectPropertyBase *OProp = CastField(Prop)) + FNumericProperty *NProp = CastField(Prop); + if (!NProp) { - UObject *Obj = OProp->GetObjectPropertyValue(VP); - if (Obj == nullptr && Value.TrimStartAndEnd().Compare(TEXT("None"), ESearchCase::IgnoreCase) != 0) + PrintExpectsReceived(TEXT("double")); + return false; + } + if (NProp->IsFloatingPoint()) + { + uint8 buffer[16]; + NProp->SetFloatingPointPropertyValue(buffer, D); + if (!FMath::IsFinite(NProp->GetFloatingPointPropertyValue(buffer))) { - UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s'\n"), - *Value, *WingUtils::FormatName(Prop)); + UWingServer::Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lf\n"), + *WingUtils::FormatName(Prop), *Prop->GetCPPType(), D); return false; } + Prop->SetValue_InContainer(Container, buffer); + return true; } - return true; + else + { + uint8 buffer[16]; + if (FMath::Floor(D) != D) + { + PrintExpectsReceived(TEXT("double")); + return false; + } + if (FMath::Abs(D) > (double)((1LL)<<53)) + { + UWingServer::Printf(TEXT("ERROR: To store very large numbers in '%s', do not pass them as double. Use string or int.\n"), + *WingUtils::FormatName(Prop)); + return false; + } + int64 I = (int64)D; + NProp->SetIntPropertyValue(buffer, I); + if (NProp->GetSignedIntPropertyValue(buffer) != I) + { + UWingServer::Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"), + *WingUtils::FormatName(Prop), *Prop->GetCPPType(), I); + return false; + } + NProp->SetValue_InContainer(Container, buffer); + return true; + } +} + +bool FWingProperty::SetInt64(int64 I) +{ + FNumericProperty *NProp = CastField(Prop); + if (!NProp) + { + PrintExpectsReceived(TEXT("int")); + return false; + } + if (NProp->IsFloatingPoint()) + { + uint8 buffer[16]; + double D = I; + NProp->SetFloatingPointPropertyValue(buffer, D); + int64 RT = (int64)NProp->GetFloatingPointPropertyValue(buffer); + if (RT != I) + { + UWingServer::Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"), + *WingUtils::FormatName(Prop), *Prop->GetCPPType(), I); + return false; + } + Prop->SetValue_InContainer(Container, buffer); + return true; + } + else + { + uint8 buffer[16]; + NProp->SetIntPropertyValue(buffer, I); + if (NProp->GetSignedIntPropertyValue(buffer) != I) + { + UWingServer::Printf(TEXT("ERROR: Property '%s' of type %s cannot hold %lld\n"), + *WingUtils::FormatName(Prop), *Prop->GetCPPType(), I); + return false; + } + NProp->SetValue_InContainer(Container, buffer); + return true; + } +} + +bool FWingProperty::SetBool(bool B) +{ + if (FBoolProperty* BoolProp = CastField(Prop)) + { + Prop->SetValue_InContainer(Container, &B); + return true; + } + PrintExpectsReceived(TEXT("boolean")); + return false; } @@ -137,65 +228,12 @@ bool FWingProperty::SetJson(const TSharedPtr &JsonValue) if (JsonValue->Type == EJson::Number) { - // If the property is float or double, just store the value. - double D = JsonValue->AsNumber(); - if (FFloatProperty* FloatProp = CastField(Prop)) - { - float Value = (float)D; - Prop->SetValue_InContainer(Container, &Value); - return true; - } - else if (FDoubleProperty* DoubleProp = CastField(Prop)) - { - double Value = (double)D; - Prop->SetValue_InContainer(Container, &Value); - return true; - } - - // At this point, we've ruled out it being a float or double property. - // All that's left is integers. Verify that the number can be converted - // losslessly to an int64, and then do so. - if (FMath::Floor(D) != D) - { - PrintExpectsReceived(TEXT("float")); - return false; - } - if (FMath::Abs(D) > (double)((1LL)<<53)) - { - UWingServer::Printf(TEXT("ERROR: To store very large numbers in '%s', please pass a json string\n"), - *WingUtils::FormatName(Prop)); - return false; - } - int64 I = (int64)D; - - // Now store the integer. Make sure it fits first. - if (FNumericProperty *NumericProperty = CastField(Prop)) - { - uint8 buffer[16]; - NumericProperty->SetIntPropertyValue(buffer, I); - if (NumericProperty->GetSignedIntPropertyValue(buffer) != I) - { - UWingServer::Printf(TEXT("ERROR: Property '%s' of type %s is too small to hold %lld\n"), - *WingUtils::FormatName(Prop), *Prop->GetCPPType(), I); - return false; - } - NumericProperty->SetValue_InContainer(Container, buffer); - return true; - } - PrintExpectsReceived(TEXT("integer")); - return false; + return SetDouble(JsonValue->AsNumber()); } if (JsonValue->Type == EJson::Boolean) { - if (FBoolProperty* BoolProp = CastField(Prop)) - { - bool Value = JsonValue->AsBool(); - Prop->SetValue_InContainer(Container, &Value); - return true; - } - PrintExpectsReceived(TEXT("boolean")); - return false; + return SetBool(JsonValue->AsBool()); } if (JsonValue->Type == EJson::Object) diff --git a/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h b/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h index d302a9c4..924ac22f 100644 --- a/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h +++ b/Plugins/UEWingman/Source/UEWingman/Public/WingProperty.h @@ -14,8 +14,14 @@ struct FWingProperty FWingProperty(FProperty* InProp, void* InContainer); FWingProperty(FProperty* InProp, UObject* InContainer); + bool SetDouble(double D); + bool SetInt64(int64 I); + bool SetBool(bool B); + FString GetText() const; bool SetText(FString Value); + + // Store data from a json object. bool SetJson(const TSharedPtr &Value); // Get the Category metadata.