Back in business
This commit is contained in:
@@ -23,7 +23,7 @@ struct FSpawnNodeEntry
|
|||||||
GENERATED_BODY()
|
GENERATED_BODY()
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
FString ActionName;
|
FString Type;
|
||||||
|
|
||||||
UPROPERTY()
|
UPROPERTY()
|
||||||
int32 PosX = 0;
|
int32 PosX = 0;
|
||||||
@@ -70,8 +70,8 @@ public:
|
|||||||
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
|
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
|
||||||
{
|
{
|
||||||
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) return;
|
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) return;
|
||||||
TArray<FWingGraphAction*> Results = GraphActions.Search(Entry.ActionName, 2, true);
|
TArray<FWingGraphAction*> Results = GraphActions.Search(Entry.Type, 2, true);
|
||||||
if (!WingUtils::CheckExactlyOneNamed(Results.Num(), TEXT("node type"), Entry.ActionName, WingOut::Stdout)) return;
|
if (!WingUtils::CheckExactlyOneNamed(Results.Num(), TEXT("node type"), Entry.Type, WingOut::Stdout)) return;
|
||||||
Entry.Action = Results[0];
|
Entry.Action = Results[0];
|
||||||
Entries.Add(Entry);
|
Entries.Add(Entry);
|
||||||
}
|
}
|
||||||
@@ -82,11 +82,11 @@ public:
|
|||||||
UEdGraphNode* NewNode = Entry.Action->Execute(FVector2D(Entry.PosX, Entry.PosY));
|
UEdGraphNode* NewNode = Entry.Action->Execute(FVector2D(Entry.PosX, Entry.PosY));
|
||||||
if (NewNode)
|
if (NewNode)
|
||||||
{
|
{
|
||||||
WingOut::Stdout.Printf(TEXT("Spawned: %s %s\n"), *Entry.ActionName, *WingUtils::FormatName(NewNode));
|
WingOut::Stdout.Printf(TEXT("Spawned: %s %s\n"), *Entry.Type, *WingUtils::FormatName(NewNode));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
WingOut::Stdout.Printf(TEXT("FAILED: %s\n"), *Entry.ActionName);
|
WingOut::Stdout.Printf(TEXT("FAILED: %s\n"), *Entry.Type);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -347,11 +347,10 @@ FString FWingProperty::GetCategory() const
|
|||||||
|
|
||||||
void FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props)
|
void FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props)
|
||||||
{
|
{
|
||||||
TArray<FWingProperty> Result;
|
|
||||||
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
|
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
|
||||||
{
|
{
|
||||||
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
|
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
|
||||||
Result.Add(FWingProperty(*It, Obj.StructPtr));
|
Props.Add(FWingProperty(*It, Obj.StructPtr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,24 +443,16 @@ TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, EPropertyFlags Fla
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json, bool AllOptional, WingOut Errors)
|
bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors)
|
||||||
{
|
{
|
||||||
bool Ok = true;
|
bool Ok = true;
|
||||||
|
|
||||||
// Make sure they passed in a JSON object.
|
|
||||||
TSharedPtr<FJsonObject> Obj = Json.AsObject();
|
|
||||||
if (Obj == nullptr)
|
|
||||||
{
|
|
||||||
Errors.Printf(TEXT("property data should be stored in a json object\n"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a set of known property names for the unknown-field check.
|
// Build a set of known property names for the unknown-field check.
|
||||||
TSet<FName> KnownKeys;
|
TSet<FName> KnownKeys;
|
||||||
for (const FWingProperty& P : Props) KnownKeys.Add(P->GetFName());
|
for (const FWingProperty& P : Props) KnownKeys.Add(P->GetFName());
|
||||||
|
|
||||||
// Check for unknown fields in the JSON
|
// Check for unknown fields in the JSON
|
||||||
for (const auto& KV : Obj->Values)
|
for (const auto& KV : Json.Values)
|
||||||
{
|
{
|
||||||
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
|
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
|
||||||
if (!KnownKeys.Contains(Name))
|
if (!KnownKeys.Contains(Name))
|
||||||
@@ -475,7 +466,7 @@ bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonVa
|
|||||||
for (FWingProperty& P : Props)
|
for (FWingProperty& P : Props)
|
||||||
{
|
{
|
||||||
FString JsonKey = WingUtils::FormatName(P.Prop);
|
FString JsonKey = WingUtils::FormatName(P.Prop);
|
||||||
TSharedPtr<FJsonValue> Value = Obj->TryGetField(JsonKey);
|
TSharedPtr<FJsonValue> Value = Json.TryGetField(JsonKey);
|
||||||
if (!Value)
|
if (!Value)
|
||||||
{
|
{
|
||||||
bool Optional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
|
bool Optional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
|
||||||
@@ -491,6 +482,18 @@ bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonVa
|
|||||||
return Ok;
|
return Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json, bool AllOptional, WingOut Errors)
|
||||||
|
{
|
||||||
|
// Make sure they passed in a JSON object.
|
||||||
|
TSharedPtr<FJsonObject> Obj = Json.AsObject();
|
||||||
|
if (Obj == nullptr)
|
||||||
|
{
|
||||||
|
Errors.Printf(TEXT("property data should be stored in a json object\n"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return PopulateFromJson(Props, *Obj, AllOptional, Errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool FWingProperty::IsPinTypeProperty(FProperty* Prop)
|
bool FWingProperty::IsPinTypeProperty(FProperty* Prop)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "WingServer.h"
|
#include "WingServer.h"
|
||||||
#include "WingHandler.h"
|
#include "WingHandler.h"
|
||||||
#include "WingPropHandle.h"
|
#include "WingProperty.h"
|
||||||
#include "WingManual.h"
|
#include "WingManual.h"
|
||||||
#include "WingLogCapture.h"
|
#include "WingLogCapture.h"
|
||||||
#include "WingUtils.h"
|
#include "WingUtils.h"
|
||||||
@@ -328,9 +328,8 @@ void UWingServer::TryCallHandler(const FString &Line)
|
|||||||
Handler->ConfigurationObject = Found->Config.Get();
|
Handler->ConfigurationObject = Found->Config.Get();
|
||||||
|
|
||||||
// Populate the handler object with the request parameters.
|
// Populate the handler object with the request parameters.
|
||||||
WingPropHandle Props;
|
TArray<FWingProperty> Props = FWingProperty::GetAll(Handler, CPF_Edit);
|
||||||
WingPropHandle::Handles Handles = Props.AllProperties(HandlerObj.Get(), true);
|
if (!FWingProperty::PopulateFromJson(Props, *Request, false, WingOut::Stdout))
|
||||||
if (!WingPropHandle::PopulateFromJson(Handles, *Request, false, WingOut::Stdout))
|
|
||||||
{
|
{
|
||||||
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ struct FWingProperty
|
|||||||
|
|
||||||
// Functions to populate properties from a JSON object.
|
// Functions to populate properties from a JSON object.
|
||||||
//
|
//
|
||||||
|
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject& Json,
|
||||||
|
bool AllOptional, WingOut Errors);
|
||||||
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json,
|
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json,
|
||||||
bool AllOptional, WingOut Errors);
|
bool AllOptional, WingOut Errors);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
Human-friendly MCP test client.
|
Human-friendly MCP test client.
|
||||||
|
|
||||||
Usage: ue-wingman.py <command> [key=value ...]
|
Usage: ue-wingman.py <command> [key=value ...]
|
||||||
ue-wingman.py (reads JSON with "command" from stdin)
|
|
||||||
|
Values starting with '[' or '{' are parsed as JSON.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@@ -18,33 +19,19 @@ TIMEOUT = 120
|
|||||||
def main():
|
def main():
|
||||||
args = sys.argv[1:]
|
args = sys.argv[1:]
|
||||||
if not args:
|
if not args:
|
||||||
# No arguments: read a complete JSON object from stdin.
|
print("Usage: ue-wingman.py <command> [key=value ...]")
|
||||||
# The JSON must already contain a "command" key.
|
sys.exit(1)
|
||||||
decoder = json.JSONDecoder()
|
|
||||||
raw = ""
|
msg = {"command": args[0]}
|
||||||
msg = None
|
for arg in args[1:]:
|
||||||
for line in sys.stdin:
|
key, _, value = arg.partition("=")
|
||||||
raw += line
|
if value and value[0] in ('[', '{'):
|
||||||
stripped = raw.strip()
|
|
||||||
try:
|
try:
|
||||||
msg, _ = decoder.raw_decode(stripped)
|
value = json.loads(value)
|
||||||
break
|
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
if e.pos < len(stripped):
|
print(f"Bad JSON in {key}: {e.msg}")
|
||||||
print(f"Malformed JSON: {e.msg}")
|
sys.exit(1)
|
||||||
sys.exit(1)
|
msg[key] = value
|
||||||
continue
|
|
||||||
if msg is None:
|
|
||||||
print("Could not parse a complete JSON object from stdin")
|
|
||||||
sys.exit(1)
|
|
||||||
if "command" not in msg:
|
|
||||||
print("JSON object must contain a \"command\" key")
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
|
||||||
msg = {"command": args[0]}
|
|
||||||
for arg in args[1:]:
|
|
||||||
key, _, value = arg.partition("=")
|
|
||||||
msg[key] = value
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(TIMEOUT)
|
sock.settimeout(TIMEOUT)
|
||||||
|
|||||||
Reference in New Issue
Block a user