Better handling of JSON property setters
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "WingBlueprintVar.h"
|
#include "WingBlueprintVar.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "WingBlueprintVar.h"
|
#include "WingBlueprintVar.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingFunctionArgs.h"
|
#include "WingFunctionArgs.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
#include "WingProperty.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "WingGraphActions.h"
|
#include "WingGraphActions.h"
|
||||||
#include "EdGraph/EdGraph.h"
|
#include "EdGraph/EdGraph.h"
|
||||||
@@ -64,7 +64,7 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
||||||
{
|
{
|
||||||
FSpawnNodeEntry Entry;
|
FSpawnNodeEntry Entry;
|
||||||
if (!WingJson::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
|
if (!FWingProperty::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Find the action by exact full name
|
// Find the action by exact full name
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "EdGraph/EdGraphPin.h"
|
#include "EdGraph/EdGraphPin.h"
|
||||||
#include "EdGraphSchema_K2.h"
|
#include "EdGraphSchema_K2.h"
|
||||||
@@ -124,7 +123,7 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
|
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
|
||||||
{
|
{
|
||||||
FSetNodeDefaultEntry Entry;
|
FSetNodeDefaultEntry Entry;
|
||||||
if (!WingJson::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
|
if (!FWingProperty::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (K2Schema)
|
if (K2Schema)
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "EdGraph/EdGraphNode.h"
|
#include "EdGraph/EdGraphNode.h"
|
||||||
@@ -59,7 +58,7 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
||||||
{
|
{
|
||||||
FMoveNodeEntry Entry;
|
FMoveNodeEntry Entry;
|
||||||
if (!WingJson::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue;
|
if (!FWingProperty::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue;
|
||||||
|
|
||||||
WingFetcher FN(TargetGraph);
|
WingFetcher FN(TargetGraph);
|
||||||
UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>();
|
UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
#include "WingProperty.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "EdGraph/EdGraph.h"
|
#include "EdGraph/EdGraph.h"
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
|
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
|
||||||
{
|
{
|
||||||
FConnectPinsEntry Entry;
|
FConnectPinsEntry Entry;
|
||||||
if (!WingJson::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
|
if (!FWingProperty::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
WingFetcher FS(G);
|
WingFetcher FS(G);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingJson.h"
|
#include "WingProperty.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
#include "EdGraph/EdGraph.h"
|
#include "EdGraph/EdGraph.h"
|
||||||
@@ -59,7 +59,7 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
|
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
|
||||||
{
|
{
|
||||||
FDisconnectPinEntry Entry;
|
FDisconnectPinEntry Entry;
|
||||||
if (!WingJson::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
|
if (!FWingProperty::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
|
||||||
|
|
||||||
WingFetcher FP(G);
|
WingFetcher FP(G);
|
||||||
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
|
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
|
||||||
|
|||||||
@@ -42,31 +42,21 @@ public:
|
|||||||
UWingServer::Print(TEXT("Error: No properties specified\n"));
|
UWingServer::Print(TEXT("Error: No properties specified\n"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
TArray<FWingProperty> All = FWingProperty::GetAll(Obj, CPF_Edit);
|
||||||
|
int SuccessCount = 0;
|
||||||
|
|
||||||
// Validation pass — resolve all properties and values before modifying anything.
|
// Validation pass — resolve all properties and values before modifying anything.
|
||||||
TArray<FWingProperty> All = FWingProperty::GetAll(Obj, CPF_Edit);
|
|
||||||
TArray<TPair<FWingProperty, FString>> Resolved;
|
|
||||||
for (const auto& Pair : Properties.Json->Values)
|
for (const auto& Pair : Properties.Json->Values)
|
||||||
{
|
{
|
||||||
FWingProperty P = FWingProperty::FindOneExactMatch(All, Pair.Key);
|
FWingProperty P = FWingProperty::FindOneExactMatch(All, Pair.Key);
|
||||||
if (!P) return;
|
if (!P) return;
|
||||||
|
|
||||||
FString ValueStr;
|
|
||||||
if (!Pair.Value->TryGetString(ValueStr))
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("Error: Value for '%s' must be a string\n"), *Pair.Key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Resolved.Emplace(P, ValueStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply all changes.
|
// Assignment Pass - store the values.
|
||||||
int32 SuccessCount = 0;
|
for (const auto& Pair : Properties.Json->Values)
|
||||||
for (auto& [P, ValueStr] : Resolved)
|
|
||||||
{
|
{
|
||||||
if (!P.SetText(ValueStr))
|
FWingProperty P = FWingProperty::FindOneExactMatch(All, Pair.Key);
|
||||||
continue;
|
if (P.SetJson(Pair.Value)) SuccessCount++;
|
||||||
SuccessCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
|
UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include "WingFetcher.h"
|
#include "WingFetcher.h"
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "WingJson.h"
|
#include "WingProperty.h"
|
||||||
#include "WingManual.h"
|
#include "WingManual.h"
|
||||||
#include "ShowCommands.generated.h"
|
#include "ShowCommands.generated.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
#include "WingBlueprintVar.h"
|
#include "WingBlueprintVar.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
@@ -57,7 +56,7 @@ bool FWingBlueprintVar::ApplyJson(const FJsonObject* Json)
|
|||||||
LoadFlags();
|
LoadFlags();
|
||||||
|
|
||||||
TArray<FWingProperty> Props = MergedProperties();
|
TArray<FWingProperty> Props = MergedProperties();
|
||||||
if (!WingJson::PopulateFromJson(Props, Json, true))
|
if (!FWingProperty::PopulateFromJson(Props, Json, true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SaveFlags();
|
SaveFlags();
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
#include "WingJson.h"
|
|
||||||
#include "WingTypes.h"
|
|
||||||
#include "WingServer.h"
|
|
||||||
#include "UObject/UnrealType.h"
|
|
||||||
#include "UObject/EnumProperty.h"
|
|
||||||
#include "Dom/JsonValue.h"
|
|
||||||
|
|
||||||
|
|
||||||
bool WingJson::PopulateFromJson(FWingProperty& P, const FJsonObject* Json, bool AllOptional)
|
|
||||||
{
|
|
||||||
FString JsonKey = P.Prop->GetName();
|
|
||||||
bool bOptional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
|
|
||||||
|
|
||||||
if (!Json->HasField(JsonKey))
|
|
||||||
{
|
|
||||||
if (!bOptional)
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ValuePtr = P.Prop->ContainerPtrToValuePtr<void>(P.Container);
|
|
||||||
|
|
||||||
// Special handling for FWingJsonObject and FWingJsonArray
|
|
||||||
if (FStructProperty* StructProp = CastField<FStructProperty>(P.Prop))
|
|
||||||
{
|
|
||||||
if (StructProp->Struct == FWingJsonObject::StaticStruct())
|
|
||||||
{
|
|
||||||
if (!Json->HasTypedField<EJson::Object>(JsonKey))
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' must be an object\n"), *JsonKey);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static_cast<FWingJsonObject*>(ValuePtr)->Json = Json->GetObjectField(JsonKey);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (StructProp->Struct == FWingJsonArray::StaticStruct())
|
|
||||||
{
|
|
||||||
if (!Json->HasTypedField<EJson::Array>(JsonKey))
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' must be an array\n"), *JsonKey);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
static_cast<FWingJsonArray*>(ValuePtr)->Array = Json->GetArrayField(JsonKey);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle based on JSON value type.
|
|
||||||
TSharedPtr<FJsonValue> JsonValue = Json->TryGetField(JsonKey);
|
|
||||||
|
|
||||||
if (JsonValue->Type == EJson::Number)
|
|
||||||
{
|
|
||||||
double D = JsonValue->AsNumber();
|
|
||||||
if (FIntProperty* IntProp = CastField<FIntProperty>(P.Prop))
|
|
||||||
{ IntProp->SetPropertyValue(ValuePtr, (int32)D); return true; }
|
|
||||||
if (FFloatProperty* FloatProp = CastField<FFloatProperty>(P.Prop))
|
|
||||||
{ FloatProp->SetPropertyValue(ValuePtr, (float)D); return true; }
|
|
||||||
if (FDoubleProperty* DoubleProp = CastField<FDoubleProperty>(P.Prop))
|
|
||||||
{ DoubleProp->SetPropertyValue(ValuePtr, D); return true; }
|
|
||||||
if (FByteProperty* ByteProp = CastField<FByteProperty>(P.Prop))
|
|
||||||
{ ByteProp->SetPropertyValue(ValuePtr, (uint8)D); return true; }
|
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' received a number but expects %s\n"), *JsonKey, *P.Prop->GetCPPType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JsonValue->Type == EJson::Boolean)
|
|
||||||
{
|
|
||||||
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(P.Prop))
|
|
||||||
{ BoolProp->SetPropertyValue(ValuePtr, JsonValue->AsBool()); return true; }
|
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' received a boolean but expects %s\n"), *JsonKey, *P.Prop->GetCPPType());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (JsonValue->Type == EJson::String)
|
|
||||||
{
|
|
||||||
return P.SetText(JsonValue->AsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
UWingServer::Printf(TEXT("ERROR: '%s' must be a string, number, or boolean\n"), *JsonKey);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WingJson::PopulateFromJson(
|
|
||||||
TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional)
|
|
||||||
{
|
|
||||||
bool Ok = true;
|
|
||||||
|
|
||||||
// Build a set of known property names for the unknown-field check.
|
|
||||||
TSet<FString> KnownKeys;
|
|
||||||
for (const FWingProperty& P : Props)
|
|
||||||
KnownKeys.Add(P.Prop->GetName());
|
|
||||||
|
|
||||||
// Check for unknown fields in the JSON
|
|
||||||
for (const auto& KV : Json->Values)
|
|
||||||
{
|
|
||||||
if (!KnownKeys.Contains(KV.Key))
|
|
||||||
{
|
|
||||||
UWingServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
|
|
||||||
Ok = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Populate each property from JSON
|
|
||||||
for (FWingProperty& P : Props)
|
|
||||||
{
|
|
||||||
if (!PopulateFromJson(P, Json, AllOptional)) Ok = false;
|
|
||||||
}
|
|
||||||
return Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WingJson::PopulateFromJson(
|
|
||||||
UStruct* StructType, void* Container, const FJsonObject* Json)
|
|
||||||
{
|
|
||||||
TArray<FWingProperty> Props = FWingProperty::GetAll(StructType, Container, (EPropertyFlags)0);
|
|
||||||
return PopulateFromJson(Props, Json);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool WingJson::PopulateFromJson(
|
|
||||||
UStruct* StructType, void* Container,
|
|
||||||
const TSharedPtr<FJsonValue>& JsonValue)
|
|
||||||
{
|
|
||||||
if (!JsonValue.IsValid() || (JsonValue->Type != EJson::Object))
|
|
||||||
{
|
|
||||||
UWingServer::Print(TEXT("ERROR: Expected a JSON object\n"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return PopulateFromJson(StructType, Container, JsonValue->AsObject().Get());
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "WingProperty.h"
|
#include "WingProperty.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
|
#include "WingHandler.h"
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "Engine/Blueprint.h"
|
#include "Engine/Blueprint.h"
|
||||||
@@ -9,6 +10,8 @@
|
|||||||
#include "Components/PanelSlot.h"
|
#include "Components/PanelSlot.h"
|
||||||
#include "EdGraph/EdGraphPin.h"
|
#include "EdGraph/EdGraphPin.h"
|
||||||
#include "UObject/EnumProperty.h"
|
#include "UObject/EnumProperty.h"
|
||||||
|
#include "Dom/JsonValue.h"
|
||||||
|
|
||||||
|
|
||||||
static bool IsPinTypeProperty(FProperty* Prop)
|
static bool IsPinTypeProperty(FProperty* Prop)
|
||||||
{
|
{
|
||||||
@@ -52,8 +55,17 @@ FString FWingProperty::GetTruncatedText(int32 MaxLen) const
|
|||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FWingProperty::SetText(const FString &Value)
|
void FWingProperty::PrintExpectsReceived(const TCHAR *Type)
|
||||||
{
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"),
|
||||||
|
*WingUtils::FormatName(Prop), Type, *Prop->GetCPPType());
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Notify that we're modifying the containing object.
|
||||||
if (Prop->GetOwnerClass())
|
if (Prop->GetOwnerClass())
|
||||||
UWingServer::AddTouchedObject(static_cast<UObject*>(Container));
|
UWingServer::AddTouchedObject(static_cast<UObject*>(Container));
|
||||||
@@ -71,35 +83,24 @@ bool FWingProperty::SetText(const FString &Value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Byte Enum types.
|
// If it's an enum type, use our parsing routine which is smarter about
|
||||||
|
// prefixes than ImportText. We canonicalize the string, and then send
|
||||||
|
// it onward to ImportText.
|
||||||
|
UEnum *Enum = nullptr;
|
||||||
if (FByteProperty* ByteProp = CastField<FByteProperty>(Prop))
|
if (FByteProperty* ByteProp = CastField<FByteProperty>(Prop))
|
||||||
{
|
Enum = ByteProp->Enum;
|
||||||
if (UEnum* Enum = ByteProp->Enum)
|
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
|
||||||
|
Enum = EnumProp->GetEnum();
|
||||||
|
if (Enum != nullptr)
|
||||||
{
|
{
|
||||||
int64 EnumValue;
|
int64 EnumValue;
|
||||||
if (!WingUtils::StringToEnum(Enum, Value, EnumValue)) return false;
|
if (!WingUtils::StringToEnum(Enum, Value, EnumValue)) return false;
|
||||||
uint8 ByteValue = (uint8)EnumValue;
|
Value = Enum->GetNameStringByValue(EnumValue);
|
||||||
Prop->SetValue_InContainer(Container, &ByteValue);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular Enum types.
|
// Now Use ImportText
|
||||||
// TODO: This doesn't use an incontainer setter, which means it
|
|
||||||
// doesn't call property setters.
|
|
||||||
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
|
|
||||||
{
|
|
||||||
int64 EnumValue;
|
|
||||||
if (!WingUtils::StringToEnum(EnumProp->GetEnum(), Value, EnumValue)) return false;
|
|
||||||
FNumericProperty* Underlying = EnumProp->GetUnderlyingProperty();
|
|
||||||
void* ValuePtr = Underlying->ContainerPtrToValuePtr<void>(Container);
|
|
||||||
Underlying->SetIntPropertyValue(ValuePtr, EnumValue);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-enum properties use ImportText
|
|
||||||
const TCHAR* Result = Prop->ImportText_InContainer(*Value, Container, nullptr, PPF_None);
|
const TCHAR* Result = Prop->ImportText_InContainer(*Value, Container, nullptr, PPF_None);
|
||||||
if (!Result)
|
if ((!Result) || (*Result != 0))
|
||||||
{
|
{
|
||||||
UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
|
UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
|
||||||
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
|
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
|
||||||
@@ -108,6 +109,106 @@ bool FWingProperty::SetText(const FString &Value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::SetJson(const TSharedPtr<FJsonValue> &JsonValue)
|
||||||
|
{
|
||||||
|
if (JsonValue->Type == EJson::String)
|
||||||
|
{
|
||||||
|
return SetText(JsonValue->AsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValue->Type == EJson::Number)
|
||||||
|
{
|
||||||
|
// If the property is float or double, just store the value.
|
||||||
|
double D = JsonValue->AsNumber();
|
||||||
|
if (FFloatProperty* FloatProp = CastField<FFloatProperty>(Prop))
|
||||||
|
{
|
||||||
|
float Value = (float)D;
|
||||||
|
Prop->SetValue_InContainer(Container, &Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (FDoubleProperty* DoubleProp = CastField<FDoubleProperty>(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<FNumericProperty>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValue->Type == EJson::Boolean)
|
||||||
|
{
|
||||||
|
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
|
||||||
|
{
|
||||||
|
bool Value = JsonValue->AsBool();
|
||||||
|
Prop->SetValue_InContainer(Container, &Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PrintExpectsReceived(TEXT("boolean"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValue->Type == EJson::Object)
|
||||||
|
{
|
||||||
|
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
||||||
|
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
||||||
|
if (StructProp && (StructProp->Struct == FWingJsonObject::StaticStruct()))
|
||||||
|
{
|
||||||
|
static_cast<FWingJsonObject*>(ValuePtr)->Json = JsonValue->AsObject();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PrintExpectsReceived(TEXT("json object"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (JsonValue->Type == EJson::Array)
|
||||||
|
{
|
||||||
|
void* ValuePtr = Prop->ContainerPtrToValuePtr<void>(Container);
|
||||||
|
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
|
||||||
|
if (StructProp && (StructProp->Struct == FWingJsonArray::StaticStruct()))
|
||||||
|
{
|
||||||
|
static_cast<FWingJsonArray*>(ValuePtr)->Array = JsonValue->AsArray();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PrintExpectsReceived(TEXT("json array"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintExpectsReceived(TEXT("Unrecognized Json Data"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void FWingProperty::Collect(UStruct* StructType, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags)
|
void FWingProperty::Collect(UStruct* StructType, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags)
|
||||||
{
|
{
|
||||||
TMap<FString, TArray<FWingProperty>> Grouped;
|
TMap<FString, TArray<FWingProperty>> Grouped;
|
||||||
@@ -253,3 +354,63 @@ FWingProperty FWingProperty::FindOneExactMatch(const TArray<FWingProperty>& Prop
|
|||||||
}
|
}
|
||||||
return Matches[0];
|
return Matches[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::PopulateFromJson(FWingProperty& P, const FJsonObject* Json, bool AllOptional)
|
||||||
|
{
|
||||||
|
FString JsonKey = WingUtils::FormatName(P.Prop);
|
||||||
|
TSharedPtr<FJsonValue> Value = Json->TryGetField(JsonKey);
|
||||||
|
|
||||||
|
if (Value == nullptr)
|
||||||
|
{
|
||||||
|
bool Optional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
|
||||||
|
if (Optional) return true;
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return P.SetJson(Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::PopulateFromJson(
|
||||||
|
TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional)
|
||||||
|
{
|
||||||
|
bool Ok = true;
|
||||||
|
|
||||||
|
// Build a set of known property names for the unknown-field check.
|
||||||
|
TSet<FString> KnownKeys;
|
||||||
|
for (const FWingProperty& P : Props)
|
||||||
|
KnownKeys.Add(WingUtils::FormatName(P.Prop));
|
||||||
|
|
||||||
|
// Check for unknown fields in the JSON
|
||||||
|
for (const auto& KV : Json->Values)
|
||||||
|
{
|
||||||
|
if (!KnownKeys.Contains(KV.Key))
|
||||||
|
{
|
||||||
|
UWingServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
|
||||||
|
Ok = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate each property from JSON
|
||||||
|
for (FWingProperty& P : Props)
|
||||||
|
{
|
||||||
|
if (!PopulateFromJson(P, Json, AllOptional)) Ok = false;
|
||||||
|
}
|
||||||
|
return Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object)
|
||||||
|
{
|
||||||
|
TArray<FWingProperty> Props = FWingProperty::GetAll(StructType, Container, (EPropertyFlags)0);
|
||||||
|
return PopulateFromJson(Props, Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object)
|
||||||
|
{
|
||||||
|
if (!Object.IsValid() || (Object->Type != EJson::Object))
|
||||||
|
{
|
||||||
|
UWingServer::Print(TEXT("ERROR: Expected a JSON object\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return PopulateFromJson(StructType, Container, Object->AsObject().Get());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
|
#include "WingProperty.h"
|
||||||
#include "WingManual.h"
|
#include "WingManual.h"
|
||||||
#include "WingJson.h"
|
|
||||||
#include "WingLogCapture.h"
|
#include "WingLogCapture.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "UObject/StrongObjectPtr.h"
|
#include "UObject/StrongObjectPtr.h"
|
||||||
@@ -327,7 +327,7 @@ void UWingServer::TryCallHandler(const FString &Line)
|
|||||||
IWingHandler* Handler = Cast<IWingHandler>(HandlerObj.Get());
|
IWingHandler* Handler = Cast<IWingHandler>(HandlerObj.Get());
|
||||||
|
|
||||||
// Populate the handler object with the request parameters.
|
// Populate the handler object with the request parameters.
|
||||||
if (!WingJson::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
|
if (!FWingProperty::PopulateFromJson(HandlerObj->GetClass(), HandlerObj.Get(), &*Request))
|
||||||
{
|
{
|
||||||
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
#include "WingActorComponent.h"
|
#include "WingActorComponent.h"
|
||||||
#include "WingJson.h"
|
#include "WingProperty.h"
|
||||||
#include "WingTypes.h"
|
#include "WingTypes.h"
|
||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "CoreMinimal.h"
|
|
||||||
#include "WingHandler.h"
|
|
||||||
#include "WingProperty.h"
|
|
||||||
#include "Dom/JsonObject.h"
|
|
||||||
|
|
||||||
// JSON utility functions used by MCP handlers.
|
|
||||||
// This is effectively a namespace — all methods are static.
|
|
||||||
class WingJson
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static bool PopulateFromJson(FWingProperty& Prop, const FJsonObject* Json, bool AllOptional = false);
|
|
||||||
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional = false);
|
|
||||||
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& JsonValue);
|
|
||||||
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Json);
|
|
||||||
};
|
|
||||||
@@ -15,7 +15,8 @@ struct FWingProperty
|
|||||||
FWingProperty(FProperty* InProp, UObject* InContainer);
|
FWingProperty(FProperty* InProp, UObject* InContainer);
|
||||||
|
|
||||||
FString GetText() const;
|
FString GetText() const;
|
||||||
bool SetText(const FString& Value);
|
bool SetText(FString Value);
|
||||||
|
bool SetJson(const TSharedPtr<FJsonValue> &Value);
|
||||||
|
|
||||||
// Get the Category metadata.
|
// Get the Category metadata.
|
||||||
FString GetCategory();
|
FString GetCategory();
|
||||||
@@ -34,6 +35,12 @@ struct FWingProperty
|
|||||||
static void Remove(TArray<FWingProperty>& Props, const FString& Name);
|
static void Remove(TArray<FWingProperty>& Props, const FString& Name);
|
||||||
static void Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, const FString &Name);
|
static void Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, const FString &Name);
|
||||||
|
|
||||||
|
static bool PopulateFromJson(FWingProperty& Prop, const FJsonObject* Json, bool AllOptional = false);
|
||||||
|
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional = false);
|
||||||
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object);
|
||||||
|
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void PrintExpectsReceived(const TCHAR *Type);
|
||||||
static void Collect(UStruct* Struct, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags);
|
static void Collect(UStruct* Struct, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user