Enormouse overhaul

This commit is contained in:
2026-04-04 01:45:25 -04:00
parent b1a2813f05
commit e9fff6599a
86 changed files with 1324 additions and 1225 deletions

View File

@@ -45,16 +45,16 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
// Check that the proposed name is valid // Check that the proposed name is valid
FName InternalID = WingUtils::CheckProposedName(Component); FName InternalID = WingUtils::CheckProposedName(Component, WingOut::Stdout);
if (InternalID.IsNone()) return; if (InternalID.IsNone()) return;
TSet<FName> Names; TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names); FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"))) return; if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
// Resolve the component class by name // Resolve the component class by name
UWingTypes::Requirements Req; UWingTypes::Requirements Req;
@@ -62,18 +62,18 @@ public:
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = false; Req.AllowContainer = false;
Req.IsChildOf = UActorComponent::StaticClass(); Req.IsChildOf = UActorComponent::StaticClass();
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req); UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req, WingOut::Stdout);
if (!ComponentClass) return; if (!ComponentClass) return;
if (!UWingComponentReference::CheckValidComponentClass(ComponentClass)) return; if (!UWingComponentReference::CheckValidComponentClass(ComponentClass, WingOut::Stdout)) return;
// Find the specified parent component // Find the specified parent component
TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP); TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP);
UWingComponentReference* ParentComp = WingUtils::FindOneWithExternalID(Parent, AllComponents, TEXT("Component")); UWingComponentReference* ParentComp = WingUtils::FindOneWithExternalID(Parent, AllComponents, TEXT("Component"), WingOut::Stdout);
if (!ParentComp) return; if (!ParentComp) return;
// Create the SCS node // Create the SCS node
if (!UWingComponentReference::AddComponent(BP, ComponentClass, ParentComp, InternalID)) return; if (!UWingComponentReference::AddComponent(BP, ComponentClass, ParentComp, InternalID, WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Component Added.\n")); WingOut::Stdout.Printf(TEXT("Component Added.\n"));
} }
}; };

View File

@@ -28,12 +28,12 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>(); UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>();
if (!CompRef) return; if (!CompRef) return;
if (!CompRef->DeleteComponent()) return; if (!CompRef->DeleteComponent(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Removed component.\n")); WingOut::Stdout.Printf(TEXT("Removed component.\n"));
} }
}; };

View File

@@ -35,18 +35,18 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>(); UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>();
if (!CompRef) return; if (!CompRef) return;
// Find the new parent among all components (if specified) // Find the new parent among all components (if specified)
UBlueprint *BP = CompRef->BP; UBlueprint *BP = CompRef->BP;
TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP); TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP);
UWingComponentReference* NewParent = WingUtils::FindOneWithExternalID(Parent, AllComponents, TEXT("Component")); UWingComponentReference* NewParent = WingUtils::FindOneWithExternalID(Parent, AllComponents, TEXT("Component"), WingOut::Stdout);
if (!NewParent) return; if (!NewParent) return;
if (!CompRef->ReparentComponent(NewParent)) return; if (!CompRef->ReparentComponent(NewParent, WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Reparented component.")); WingOut::Stdout.Printf(TEXT("Reparented component."));
} }
}; };

View File

@@ -35,7 +35,7 @@ public:
if (!IFileManager::Get().FileExists(*Filename)) if (!IFileManager::Get().FileExists(*Filename))
{ {
UWingServer::Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename); WingOut::Stdout.Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename);
return; return;
} }
@@ -43,10 +43,10 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true); uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true);
if (CopyResult != COPY_OK) if (CopyResult != COPY_OK)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to copy %s to %s\n"), *Filename, *BackupFilename); WingOut::Stdout.Printf(TEXT("ERROR: Failed to copy %s to %s\n"), *Filename, *BackupFilename);
return; return;
} }
UWingServer::Printf(TEXT("Backed up to %s\n"), *BackupFilename); WingOut::Stdout.Printf(TEXT("Backed up to %s\n"), *BackupFilename);
} }
}; };

View File

@@ -93,11 +93,11 @@ public:
} }
Results.Sort(); Results.Sort();
for (const FString &Result : Results) UWingServer::Print(Result); for (const FString &Result : Results) WingOut::Stdout.Print(Result);
if (Results.IsEmpty()) if (Results.IsEmpty())
{ {
UWingServer::Printf(TEXT("No contents found at '%s'.\n"), *Path); WingOut::Stdout.Printf(TEXT("No contents found at '%s'.\n"), *Path);
return; return;
} }
} }

View File

@@ -44,7 +44,7 @@ public:
if (!IFileManager::Get().FileExists(*PackageFilename)) if (!IFileManager::Get().FileExists(*PackageFilename))
{ {
UWingServer::Printf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename); WingOut::Stdout.Printf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename);
return; return;
} }
@@ -60,22 +60,22 @@ public:
if (Referencers.Num() > 0 && !Force) if (Referencers.Num() > 0 && !Force)
{ {
UWingServer::Printf(TEXT("ERROR: Asset is still referenced by %d package(s):\n"), Referencers.Num()); WingOut::Stdout.Printf(TEXT("ERROR: Asset is still referenced by %d package(s):\n"), Referencers.Num());
for (const FName& Ref : Referencers) for (const FName& Ref : Referencers)
{ {
FString RefStr = Ref.ToString(); FString RefStr = Ref.ToString();
UPackage* RefPackage = FindPackage(nullptr, *RefStr); UPackage* RefPackage = FindPackage(nullptr, *RefStr);
UWingServer::Printf(TEXT(" %s%s\n"), *RefStr, WingOut::Stdout.Printf(TEXT(" %s%s\n"), *RefStr,
RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)")); RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)"));
} }
UWingServer::Print(TEXT("Use force=true to skip the reference check.\n")); WingOut::Stdout.Print(TEXT("Use force=true to skip the reference check.\n"));
return; return;
} }
// Force delete: unload the package from memory first // Force delete: unload the package from memory first
if (Force && Referencers.Num() > 0) if (Force && Referencers.Num() > 0)
{ {
UWingServer::Printf(TEXT("WARNING: Force-deleting despite %d referencer(s).\n"), Referencers.Num()); WingOut::Stdout.Printf(TEXT("WARNING: Force-deleting despite %d referencer(s).\n"), Referencers.Num());
} }
// Mark the package, and all the objects in it, as NOT // Mark the package, and all the objects in it, as NOT
@@ -109,7 +109,7 @@ public:
if (!bDeleted) if (!bDeleted)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename); WingOut::Stdout.Printf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename);
return; return;
} }
@@ -122,6 +122,6 @@ public:
Registry.ScanPathsSynchronous({PackageDir}, true); Registry.ScanPathsSynchronous({PackageDir}, true);
} }
UWingServer::Printf(TEXT("Deleted %s\n"), *Asset); WingOut::Stdout.Printf(TEXT("Deleted %s\n"), *Asset);
} }
}; };

View File

@@ -35,7 +35,7 @@ public:
FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(Asset)); FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(Asset));
if (!AssetData.IsValid()) if (!AssetData.IsValid())
{ {
UWingServer::Printf(TEXT("ERROR: Asset not found: %s\n"), *Asset); WingOut::Stdout.Printf(TEXT("ERROR: Asset not found: %s\n"), *Asset);
return; return;
} }
@@ -44,7 +44,7 @@ public:
if (Referencers.Num() == 0) if (Referencers.Num() == 0)
{ {
UWingServer::Print(TEXT("No referencers found.\n")); WingOut::Stdout.Print(TEXT("No referencers found.\n"));
return; return;
} }
@@ -56,13 +56,13 @@ public:
Registry.GetAssetsByPackageName(Ref, RefAssets); Registry.GetAssetsByPackageName(Ref, RefAssets);
if (RefAssets.Num() > 0) if (RefAssets.Num() > 0)
{ {
UWingServer::Printf(TEXT("%s %s\n"), WingOut::Stdout.Printf(TEXT("%s %s\n"),
*WingUtils::FormatName(RefAssets[0].GetClass()), *WingUtils::FormatName(RefAssets[0].GetClass()),
*RefStr); *RefStr);
} }
else else
{ {
UWingServer::Printf(TEXT("Unknown %s\n"), *RefStr); WingOut::Stdout.Printf(TEXT("Unknown %s\n"), *RefStr);
} }
} }
} }

View File

@@ -34,7 +34,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Load the asset // Load the asset
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* AssetObj = F.Asset(Asset).GetObj(); UObject* AssetObj = F.Asset(Asset).GetObj();
if (!AssetObj) return; if (!AssetObj) return;
@@ -48,7 +48,7 @@ public:
NewAssetName = NewPath; NewAssetName = NewPath;
if (NewPackagePath.IsEmpty()) if (NewPackagePath.IsEmpty())
{ {
UWingServer::Printf(TEXT("ERROR: Cannot determine directory from Asset '%s'\n"), *Asset); WingOut::Stdout.Printf(TEXT("ERROR: Cannot determine directory from Asset '%s'\n"), *Asset);
return; return;
} }
} }
@@ -62,10 +62,10 @@ public:
if (!AssetTools.RenameAssets(RenameData)) if (!AssetTools.RenameAssets(RenameData))
{ {
UWingServer::Print(TEXT("ERROR: Rename failed. The target path may be invalid or a conflicting asset may exist.\n")); WingOut::Stdout.Print(TEXT("ERROR: Rename failed. The target path may be invalid or a conflicting asset may exist.\n"));
return; return;
} }
UWingServer::Printf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName); WingOut::Stdout.Printf(TEXT("Renamed to %s/%s\n"), *NewPackagePath, *NewAssetName);
} }
}; };

View File

@@ -37,7 +37,7 @@ public:
if (!IFileManager::Get().FileExists(*BackupFilename)) if (!IFileManager::Get().FileExists(*BackupFilename))
{ {
UWingServer::Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename); WingOut::Stdout.Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename);
return; return;
} }
@@ -52,7 +52,7 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true); uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true);
if (CopyResult != COPY_OK) if (CopyResult != COPY_OK)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to copy backup over %s\n"), *Asset); WingOut::Stdout.Printf(TEXT("ERROR: Failed to copy backup over %s\n"), *Asset);
return; return;
} }
@@ -64,12 +64,12 @@ public:
UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive); UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive);
if (!bReloaded) if (!bReloaded)
{ {
UWingServer::Printf(TEXT("WARNING: Restored %s but reload failed: %s\n"), WingOut::Stdout.Printf(TEXT("WARNING: Restored %s but reload failed: %s\n"),
*Asset, *ErrorMessage.ToString()); *Asset, *ErrorMessage.ToString());
return; return;
} }
} }
UWingServer::Printf(TEXT("Restored %s from backup\n"), *Asset); WingOut::Stdout.Printf(TEXT("Restored %s from backup\n"), *Asset);
} }
}; };

View File

@@ -39,7 +39,7 @@ public:
{ {
if (Query.IsEmpty() && Type.IsEmpty()) if (Query.IsEmpty() && Type.IsEmpty())
{ {
UWingServer::Print(TEXT("ERROR: At least one of Query or Type must be specified\n")); WingOut::Stdout.Print(TEXT("ERROR: At least one of Query or Type must be specified\n"));
return; return;
} }
@@ -55,7 +55,7 @@ public:
Req.BlueprintType = false; Req.BlueprintType = false;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = false; Req.AllowContainer = false;
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req); UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req, WingOut::Stdout);
if (!TypeClass) return; if (!TypeClass) return;
Filter.ClassPaths.Add(TypeClass->GetClassPathName()); Filter.ClassPaths.Add(TypeClass->GetClassPathName());
} }
@@ -81,18 +81,18 @@ public:
for (const FAssetData& Data : Results) for (const FAssetData& Data : Results)
{ {
UWingServer::Printf(TEXT("%s %s\n"), WingOut::Stdout.Printf(TEXT("%s %s\n"),
*WingUtils::FormatName(Data.GetClass()), *WingUtils::FormatName(Data.GetClass()),
*Data.PackageName.ToString()); *Data.PackageName.ToString());
} }
if (Results.Num() == 0) if (Results.Num() == 0)
{ {
UWingServer::Print(TEXT("No assets found.\n")); WingOut::Stdout.Print(TEXT("No assets found.\n"));
} }
else if (Results.Num() >= Limit) else if (Results.Num() >= Limit)
{ {
UWingServer::Printf(TEXT("WARNING: You reached the limit of %d, to raise it, specify the Limit parameter.\n"), Limit); WingOut::Stdout.Printf(TEXT("WARNING: You reached the limit of %d, to raise it, specify the Limit parameter.\n"), Limit);
} }
} }
}; };

View File

@@ -47,48 +47,48 @@ public:
{ {
if (GraphType != TEXT("function") && GraphType != TEXT("macro")) if (GraphType != TEXT("function") && GraphType != TEXT("macro"))
{ {
UWingServer::Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro\n"), *GraphType); WingOut::Stdout.Printf(TEXT("ERROR: Invalid GraphType '%s'. Valid values: function, macro\n"), *GraphType);
return; return;
} }
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
// Check that this graph type is valid for this blueprint type // Check that this graph type is valid for this blueprint type
if (BP->BlueprintType == BPTYPE_Interface) if (BP->BlueprintType == BPTYPE_Interface)
{ {
UWingServer::Print(TEXT("ERROR: Cannot add graphs to interface blueprints.\n")); WingOut::Stdout.Print(TEXT("ERROR: Cannot add graphs to interface blueprints.\n"));
return; return;
} }
if (BP->BlueprintType == BPTYPE_MacroLibrary && GraphType == TEXT("function")) if (BP->BlueprintType == BPTYPE_MacroLibrary && GraphType == TEXT("function"))
{ {
UWingServer::Print(TEXT("ERROR: Macro libraries cannot contain functions.\n")); WingOut::Stdout.Print(TEXT("ERROR: Macro libraries cannot contain functions.\n"));
return; return;
} }
if (BP->BlueprintType == BPTYPE_FunctionLibrary && GraphType == TEXT("macro")) if (BP->BlueprintType == BPTYPE_FunctionLibrary && GraphType == TEXT("macro"))
{ {
UWingServer::Print(TEXT("ERROR: Function libraries cannot contain macros.\n")); WingOut::Stdout.Print(TEXT("ERROR: Function libraries cannot contain macros.\n"));
return; return;
} }
// Check graph name uniqueness and legality // Check graph name uniqueness and legality
FName InternalID = WingUtils::CheckProposedName(Graph); FName InternalID = WingUtils::CheckProposedName(Graph, WingOut::Stdout);
if (InternalID.IsNone()) return; if (InternalID.IsNone()) return;
if (!WingUtils::FindNoneWithInternalID(InternalID, WingUtils::AllGraphs(BP), TEXT("Graph"))) if (!WingUtils::FindNoneWithInternalID(InternalID, WingUtils::AllGraphs(BP), TEXT("Graph"), WingOut::Stdout))
return; return;
// Parse and validate variables before making changes // Parse and validate variables before making changes
WingVariables Vars; WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return; if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return; if (!Vars.OutputVariables.ParseString(OutputVariables, WingOut::Stdout)) return;
// Create the Graph // Create the Graph
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, InternalID, UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, InternalID,
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph) if (!NewGraph)
{ {
UWingServer::Print(TEXT("ERROR: Failed to create graph\n")); WingOut::Stdout.Print(TEXT("ERROR: Failed to create graph\n"));
return; return;
} }
@@ -103,10 +103,10 @@ public:
} }
// Create the variables on the new graph // Create the variables on the new graph
if (!Vars.SetBackingStore(NewGraph)) return; if (!Vars.SetBackingStore(NewGraph, WingOut::Stdout)) return;
if (!Vars.Check()) return; if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Create()) return; if (!Vars.Create(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Created %s graph: %s\n"), *GraphType, *WingUtils::FormatName(NewGraph)); WingOut::Stdout.Printf(TEXT("Created %s graph: %s\n"), *GraphType, *WingUtils::FormatName(NewGraph));
} }
}; };

View File

@@ -31,25 +31,25 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* FoundGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* FoundGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!FoundGraph) return; if (!FoundGraph) return;
UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(FoundGraph); UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(FoundGraph);
if (!BP) if (!BP)
{ {
UWingServer::Print(TEXT("ERROR: Could not find owning blueprint for this graph.\n")); WingOut::Stdout.Print(TEXT("ERROR: Could not find owning blueprint for this graph.\n"));
return; return;
} }
if (!BP->FunctionGraphs.Contains(FoundGraph) && !BP->MacroGraphs.Contains(FoundGraph)) if (!BP->FunctionGraphs.Contains(FoundGraph) && !BP->MacroGraphs.Contains(FoundGraph))
{ {
UWingServer::Printf(TEXT("ERROR: %s is not a function or macro graph.\n"), *WingUtils::FormatName(FoundGraph)); WingOut::Stdout.Printf(TEXT("ERROR: %s is not a function or macro graph.\n"), *WingUtils::FormatName(FoundGraph));
return; return;
} }
FBlueprintEditorUtils::RemoveGraph(BP, FoundGraph, EGraphRemoveFlags::Recompile); FBlueprintEditorUtils::RemoveGraph(BP, FoundGraph, EGraphRemoveFlags::Recompile);
UWingServer::Printf(TEXT("Deleted graph from %s\n"), *WingUtils::FormatName(BP)); WingOut::Stdout.Printf(TEXT("Deleted graph from %s\n"), *WingUtils::FormatName(BP));
} }
}; };

View File

@@ -34,7 +34,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
@@ -43,7 +43,7 @@ public:
Req.BlueprintType = false; Req.BlueprintType = false;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = false; Req.AllowContainer = false;
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req); UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req, WingOut::Stdout);
if (!InterfaceClass) return; if (!InterfaceClass) return;
// Check for duplicates // Check for duplicates
@@ -51,7 +51,7 @@ public:
{ {
if (IfaceDesc.Interface == InterfaceClass) if (IfaceDesc.Interface == InterfaceClass)
{ {
UWingServer::Printf(TEXT("ERROR: Interface '%s' is already implemented by this Blueprint.\n"), WingOut::Stdout.Printf(TEXT("ERROR: Interface '%s' is already implemented by this Blueprint.\n"),
*WingUtils::FormatName(InterfaceClass)); *WingUtils::FormatName(InterfaceClass));
return; return;
} }
@@ -61,19 +61,19 @@ public:
bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath); bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath);
if (!bAdded) if (!bAdded)
{ {
UWingServer::Printf(TEXT("ERROR: ImplementNewInterface failed for '%s'.\n"), WingOut::Stdout.Printf(TEXT("ERROR: ImplementNewInterface failed for '%s'.\n"),
*WingUtils::FormatName(InterfaceClass)); *WingUtils::FormatName(InterfaceClass));
return; return;
} }
// Collect stub function graph names from the newly added interface entry // Collect stub function graph names from the newly added interface entry
UWingServer::Printf(TEXT("Added interface %s\n"), *WingUtils::FormatName(InterfaceClass)); WingOut::Stdout.Printf(TEXT("Added interface %s\n"), *WingUtils::FormatName(InterfaceClass));
for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces) for (const FBPInterfaceDescription& IfaceDesc : BP->ImplementedInterfaces)
{ {
if (IfaceDesc.Interface != InterfaceClass) continue; if (IfaceDesc.Interface != InterfaceClass) continue;
for (const UEdGraph* Graph : IfaceDesc.Graphs) for (const UEdGraph* Graph : IfaceDesc.Graphs)
{ {
UWingServer::Printf(TEXT("New Graph: %s\n"), *WingUtils::FormatName(Graph)); WingOut::Stdout.Printf(TEXT("New Graph: %s\n"), *WingUtils::FormatName(Graph));
} }
break; break;
} }

View File

@@ -38,7 +38,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
@@ -47,7 +47,7 @@ public:
Req.BlueprintType = false; Req.BlueprintType = false;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = false; Req.AllowContainer = false;
UClass* FoundInterface = UWingTypes::TextToOneInterfaceType(Interface, Req); UClass* FoundInterface = UWingTypes::TextToOneInterfaceType(Interface, Req, WingOut::Stdout);
if (!FoundInterface) return; if (!FoundInterface) return;
// Verify this blueprint actually implements it // Verify this blueprint actually implements it
@@ -58,7 +58,7 @@ public:
} }
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("ERROR: Blueprint %s does not implement interface %s\n"), WingOut::Stdout.Printf(TEXT("ERROR: Blueprint %s does not implement interface %s\n"),
*WingUtils::FormatName(BP), *WingUtils::FormatName(FoundInterface)); *WingUtils::FormatName(BP), *WingUtils::FormatName(FoundInterface));
return; return;
} }
@@ -66,8 +66,8 @@ public:
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName(); FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions); FBlueprintEditorUtils::RemoveInterface(BP, InterfacePath, PreserveFunctions);
UWingServer::Printf(TEXT("Removed interface %s\n"), *WingUtils::FormatName(FoundInterface)); WingOut::Stdout.Printf(TEXT("Removed interface %s\n"), *WingUtils::FormatName(FoundInterface));
if (PreserveFunctions) if (PreserveFunctions)
UWingServer::Print(TEXT("Function graphs preserved as regular functions.\n")); WingOut::Stdout.Print(TEXT("Function graphs preserved as regular functions.\n"));
} }
}; };

View File

@@ -32,7 +32,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
@@ -48,11 +48,11 @@ public:
{ {
if (!Node->bHasCompilerMessage) continue; if (!Node->bHasCompilerMessage) continue;
const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING"); const TCHAR* Prefix = (Node->ErrorType == EMessageSeverity::Error) ? TEXT("ERROR") : TEXT("WARNING");
UWingServer::Printf(TEXT("%s: [%s] %s: %s\n"), WingOut::Stdout.Printf(TEXT("%s: [%s] %s: %s\n"),
Prefix, *WingUtils::FormatName(Node->GetGraph()), Prefix, *WingUtils::FormatName(Node->GetGraph()),
*WingUtils::FormatName(Node), *Node->ErrorMsg); *WingUtils::FormatName(Node), *Node->ErrorMsg);
} }
UWingServer::Printf(TEXT("Compilation Done.\n")); WingOut::Stdout.Printf(TEXT("Compilation Done.\n"));
} }
}; };

View File

@@ -42,63 +42,63 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
// Header // Header
UWingServer::Printf(TEXT("Blueprint: %s\n"), *WingUtils::FormatName(BP)); WingOut::Stdout.Printf(TEXT("Blueprint: %s\n"), *WingUtils::FormatName(BP));
UWingServer::Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *WingUtils::FormatName(BP->ParentClass) : TEXT("None")); WingOut::Stdout.Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *WingUtils::FormatName(BP->ParentClass) : TEXT("None"));
UWingServer::Printf(TEXT("Type: %s\n"), *WingUtils::EnumToString(BP->BlueprintType)); WingOut::Stdout.Printf(TEXT("Type: %s\n"), *WingUtils::EnumToString(BP->BlueprintType));
// Animation Blueprint // Animation Blueprint
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP)) if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
{ {
if (AnimBP->TargetSkeleton) if (AnimBP->TargetSkeleton)
UWingServer::Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName()); WingOut::Stdout.Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName());
} }
// Interfaces // Interfaces
for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces) for (const FBPInterfaceDescription& I : BP->ImplementedInterfaces)
{ {
if (I.Interface) UWingServer::Printf(TEXT("Interface: %s\n"), *WingUtils::FormatName(I)); if (I.Interface) WingOut::Stdout.Printf(TEXT("Interface: %s\n"), *WingUtils::FormatName(I));
} }
// Variables // Variables
WingVariables BlueprintVars; WingVariables BlueprintVars;
BlueprintVars.SetBackingStore(BP); BlueprintVars.SetBackingStore(BP, WingOut::Stdout);
BlueprintVars.Load(); BlueprintVars.Load(WingOut::Stdout);
BlueprintVars.Print(UWingServer::GetPrintBuffer()); BlueprintVars.Print(WingOut::StdoutBuffer);
// Components // Components
TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP); TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP);
if (!Components3.IsEmpty()) if (!Components3.IsEmpty())
{ {
UWingServer::Print(TEXT("\nComponents:\n")); WingOut::Stdout.Print(TEXT("\nComponents:\n"));
for (const UWingComponentReference* Ref : Components3) for (const UWingComponentReference* Ref : Components3)
{ {
UWingServer::Printf(TEXT(" %s %s"), WingOut::Stdout.Printf(TEXT(" %s %s"),
*Ref->TypeName, *Ref->TypeName,
*WingUtils::FormatName(Ref)); *WingUtils::FormatName(Ref));
if (!Ref->ParentName.IsEmpty()) if (!Ref->ParentName.IsEmpty())
UWingServer::Printf(TEXT(" [parent: %s]"), *Ref->ParentName); WingOut::Stdout.Printf(TEXT(" [parent: %s]"), *Ref->ParentName);
if (Ref->Inherited) if (Ref->Inherited)
UWingServer::Print(TEXT(" [inherited]")); WingOut::Stdout.Print(TEXT(" [inherited]"));
UWingServer::Print(TEXT("\n")); WingOut::Stdout.Print(TEXT("\n"));
} }
} }
// Widget Tree // Widget Tree
if (UWidgetBlueprint* WidgetBP = Cast<UWidgetBlueprint>(BP)) if (UWidgetBlueprint* WidgetBP = Cast<UWidgetBlueprint>(BP))
{ {
UWingServer::Print(TEXT("\nWidget Tree:\n")); WingOut::Stdout.Print(TEXT("\nWidget Tree:\n"));
WingWidgets::PrintWidgetTree(WidgetBP->WidgetTree->RootWidget, 1); WingWidgets::PrintWidgetTree(WidgetBP->WidgetTree->RootWidget, 1);
} }
// Event Dispatchers // Event Dispatchers
if (!BP->DelegateSignatureGraphs.IsEmpty()) if (!BP->DelegateSignatureGraphs.IsEmpty())
{ {
UWingServer::Print(TEXT("\nEvent Dispatchers:\n")); WingOut::Stdout.Print(TEXT("\nEvent Dispatchers:\n"));
for (UEdGraph* Graph : BP->DelegateSignatureGraphs) for (UEdGraph* Graph : BP->DelegateSignatureGraphs)
PrintEventDispatcher(Graph); PrintEventDispatcher(Graph);
} }
@@ -106,10 +106,10 @@ public:
// Graphs // Graphs
TSet<UEdGraph*> Printed; TSet<UEdGraph*> Printed;
UWingServer::Print(TEXT("\nGraphs:\n")); WingOut::Stdout.Print(TEXT("\nGraphs:\n"));
for (UEdGraph* Graph : BP->UbergraphPages) for (UEdGraph* Graph : BP->UbergraphPages)
{ {
UWingServer::Printf(TEXT(" EventGraph %s\n"), *WingUtils::FormatName(Graph)); WingOut::Stdout.Printf(TEXT(" EventGraph %s\n"), *WingUtils::FormatName(Graph));
Printed.Add(Graph); Printed.Add(Graph);
} }
for (UEdGraph* Graph : BP->FunctionGraphs) for (UEdGraph* Graph : BP->FunctionGraphs)
@@ -147,7 +147,7 @@ public:
Printed.Add(Graph); Printed.Add(Graph);
} }
if (!AnimGraphNames.IsEmpty()) if (!AnimGraphNames.IsEmpty())
UWingServer::Printf(TEXT("\nAnimation Graphs: %s\n"), *AnimGraphNames); WingOut::Stdout.Printf(TEXT("\nAnimation Graphs: %s\n"), *AnimGraphNames);
FString StateMachineNames; FString StateMachineNames;
for (UEdGraph* Graph : AllGraphs) for (UEdGraph* Graph : AllGraphs)
@@ -159,13 +159,13 @@ public:
Printed.Add(Graph); Printed.Add(Graph);
} }
if (!StateMachineNames.IsEmpty()) if (!StateMachineNames.IsEmpty())
UWingServer::Printf(TEXT("\nAnimation State Machines: %s\n"), *StateMachineNames); WingOut::Stdout.Printf(TEXT("\nAnimation State Machines: %s\n"), *StateMachineNames);
// Catch any graphs we missed. // Catch any graphs we missed.
for (UEdGraph* Graph : AllGraphs) for (UEdGraph* Graph : AllGraphs)
{ {
if (Printed.Contains(Graph)) continue; if (Printed.Contains(Graph)) continue;
UWingServer::Printf(TEXT("WARNING: unlisted graph: %s (%s)\n"), WingOut::Stdout.Printf(TEXT("WARNING: unlisted graph: %s (%s)\n"),
*WingUtils::FormatName(Graph), *WingUtils::FormatName(Graph),
*WingUtils::FormatName(Graph->GetSchema()->GetClass())); *WingUtils::FormatName(Graph->GetSchema()->GetClass()));
} }
@@ -175,22 +175,21 @@ private:
void PrintEventDispatcher(UEdGraph* Graph) void PrintEventDispatcher(UEdGraph* Graph)
{ {
WingVariables Vars; WingVariables Vars;
Vars.SetBackingStore(Graph); Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(); Vars.Load(WingOut::Stdout);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer(); WingOut::Stdout.Printf(TEXT(" %s("), *WingUtils::FormatName(Graph));
Out.Appendf(TEXT(" %s("), *WingUtils::FormatName(Graph)); Vars.InputVariables.PrintCompact(WingOut::Stdout);
Vars.InputVariables.PrintCompact(Out); WingOut::Stdout.Printf(TEXT(")\n"));
Out.Append(TEXT(")\n"));
} }
void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr) void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr)
{ {
WingVariables Vars; WingVariables Vars;
Vars.SetBackingStore(Graph); Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(); Vars.Load(WingOut::Stdout);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer(); FStringBuilderBase &Out = WingOut::StdoutBuffer;
Out.Appendf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph)); Out.Appendf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph));
Out.AppendChar('('); Out.AppendChar('(');
Vars.InputVariables.PrintCompact(Out); Vars.InputVariables.PrintCompact(Out);

View File

@@ -36,7 +36,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Load Blueprint // Load Blueprint
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
@@ -45,13 +45,13 @@ public:
Req.BlueprintType = false; Req.BlueprintType = false;
Req.Blueprintable = true; Req.Blueprintable = true;
Req.AllowContainer = false; Req.AllowContainer = false;
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req); UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req, WingOut::Stdout);
if (!NewParentClassObj) return; if (!NewParentClassObj) return;
// Validate reparent // Validate reparent
if (!WingUtils::CanReparentBlueprint(BP->GeneratedClass, NewParentClassObj)) if (!WingUtils::CanReparentBlueprint(BP->GeneratedClass, NewParentClassObj))
{ {
UWingServer::Printf(TEXT("Error: Cannot reparent %s to %s — incompatible class hierarchy.\n"), WingOut::Stdout.Printf(TEXT("Error: Cannot reparent %s to %s — incompatible class hierarchy.\n"),
*WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj)); *WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj));
return; return;
} }
@@ -61,7 +61,7 @@ public:
FBlueprintEditorUtils::RefreshAllNodes(BP); FBlueprintEditorUtils::RefreshAllNodes(BP);
FKismetEditorUtilities::CompileBlueprint(BP); FKismetEditorUtilities::CompileBlueprint(BP);
UWingServer::Printf(TEXT("Reparented %s -> %s\n"), WingOut::Stdout.Printf(TEXT("Reparented %s -> %s\n"),
*WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj)); *WingUtils::FormatName(BP), *WingUtils::FormatName(NewParentClassObj));
} }
}; };

View File

@@ -57,7 +57,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
if (!WingFactories::CheckNewAssetPath(Path)) return; if (!WingFactories::CheckNewAssetPath(Path, WingOut::Stdout)) return;
// Resolve parent class, if specified. // Resolve parent class, if specified.
UClass *ParentClassObj = nullptr; UClass *ParentClassObj = nullptr;
@@ -67,7 +67,7 @@ public:
Req.BlueprintType = false; Req.BlueprintType = false;
Req.Blueprintable = true; Req.Blueprintable = true;
Req.AllowContainer = false; Req.AllowContainer = false;
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req); ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass, Req, WingOut::Stdout);
if (!ParentClassObj) return; if (!ParentClassObj) return;
} }
@@ -76,26 +76,26 @@ public:
UFactory *Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass); UFactory *Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
if (Factory == nullptr) if (Factory == nullptr)
{ {
UWingServer::Printf(TEXT("ERROR: factory creation failed (bug)\n")); WingOut::Stdout.Printf(TEXT("ERROR: factory creation failed (bug)\n"));
return; return;
} }
// Get the 'ParentClass' property. // Get the 'ParentClass' property.
WingPropHandle Props; WingPropHandle Props;
TSharedPtr<IPropertyHandle> PCProp = Props.NamedProperty(Factory, TEXT("ParentClass"), true); TSharedPtr<IPropertyHandle> PCProp = Props.NamedProperty(Factory, TEXT("ParentClass"), true, WingOut::Stdout);
if (!PCProp) return; if (!PCProp) return;
// Store the parent class. // Store the parent class.
FPropertyAccess::Result SetResult = PCProp->SetValue(ParentClassObj); FPropertyAccess::Result SetResult = PCProp->SetValue(ParentClassObj);
if (SetResult != FPropertyAccess::Result::Success) if (SetResult != FPropertyAccess::Result::Success)
{ {
UWingServer::Printf(TEXT("ERROR: property does not allow value: %s\n"), *ParentClass); WingOut::Stdout.Printf(TEXT("ERROR: property does not allow value: %s\n"), *ParentClass);
return; return;
} }
// Create the asset using the factory. // Create the asset using the factory.
UObject *Blueprint = WingFactories::CreateAsset(Path, Factory); UObject *Blueprint = WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
if (Blueprint == nullptr) return; if (Blueprint == nullptr) return;
UWingServer::Printf(TEXT("Created.\n")); WingOut::Stdout.Printf(TEXT("Created.\n"));
} }
}; };

View File

@@ -49,7 +49,7 @@ public:
{ {
UClass* FactoryClass = Cast<UClass>(ConfigurationObject); UClass* FactoryClass = Cast<UClass>(ConfigurationObject);
UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass); UFactory* Factory = NewObject<UFactory>(GetTransientPackage(), FactoryClass);
WingFactories::CreateAsset(Path, Factory); WingFactories::CreateAsset(Path, Factory, WingOut::Stdout);
UWingServer::Printf(TEXT("Created.\n")); WingOut::Stdout.Printf(TEXT("Created.\n"));
} }
}; };

View File

@@ -18,9 +18,6 @@ public:
UPROPERTY(EditAnywhere, meta=(Description="Target object")) UPROPERTY(EditAnywhere, meta=(Description="Target object"))
FString Object; FString Object;
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
FString Query;
virtual void Register() override virtual void Register() override
{ {
UWingServer::AddHandler(this, UWingServer::AddHandler(this,
@@ -29,12 +26,11 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Target = F.Walk(Object).Cast<UObject>(); UObject* Target = F.Walk(Object).Cast<UObject>();
if (!Target) return; if (!Target) return;
TArray<FWingProperty> Props = FWingProperty::GetDetailsImmutable(Target, CPF_Edit); TArray<FWingProperty> Props = FWingProperty::GetDetails(Target, CPF_Edit, false);
Props = FWingProperty::FindAllSubstring(Props, Query);
// Group by category, preserving within-category order. // Group by category, preserving within-category order.
TSortedMap<FString, TArray<FWingProperty>> Categories; TSortedMap<FString, TArray<FWingProperty>> Categories;
@@ -45,14 +41,13 @@ public:
Categories.FindOrAdd(Category).Add(P); Categories.FindOrAdd(Category).Add(P);
} }
FStringBuilderBase& Out = UWingServer::GetPrintBuffer();
for (const auto& Pair : Categories) for (const auto& Pair : Categories)
{ {
Out.Appendf(TEXT("\n%s:\n"), *Pair.Key); WingOut::Stdout.Printf(TEXT("\n%s:\n"), *Pair.Key);
for (const FWingProperty& P : Pair.Value) for (const FWingProperty& P : Pair.Value)
{ {
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst); bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
Out.Appendf(TEXT(" %s %s %s = %s\n"), WingOut::Stdout.Printf(TEXT(" %s %s %s = %s\n"),
bEditable ? TEXT("editable") : TEXT("readonly"), bEditable ? TEXT("editable") : TEXT("readonly"),
*UWingTypes::TypeToText(P.Prop), *UWingTypes::TypeToText(P.Prop),
*WingUtils::FormatName(P.Prop), *WingUtils::FormatName(P.Prop),

View File

@@ -28,14 +28,14 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
TArray<FWingProperty> Props = FWingProperty::GetDetailsImmutable(Obj, CPF_Edit); TArray<FWingProperty> Props = FWingProperty::GetDetails(Obj, CPF_Edit, false);
FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property")); FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property"), WingOut::Stdout);
if (!P) return; if (!P) return;
UWingServer::Print(P->GetText()); WingOut::Stdout.Print(P->GetText());
} }
}; };

View File

@@ -31,15 +31,15 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
TArray<FWingProperty> Props = FWingProperty::GetDetailsMutable(Obj, CPF_Edit); TArray<FWingProperty> Props = FWingProperty::GetDetails(Obj, CPF_Edit, true);
FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property")); FWingProperty* P = WingUtils::FindOneWithExternalID(Property, Props, TEXT("Property"), WingOut::Stdout);
if (!P) return; if (!P) return;
if (P->SetText(Value)) if (P->SetText(Value, WingOut::Stdout))
UWingServer::Print(TEXT("OK\n")); WingOut::Stdout.Print(TEXT("OK\n"));
} }
}; };

View File

@@ -28,22 +28,22 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
if (!Properties.Json || Properties.Json->Values.Num() == 0) if (!Properties.Json || Properties.Json->Values.Num() == 0)
{ {
UWingServer::Print(TEXT("Error: No properties specified\n")); WingOut::Stdout.Print(TEXT("Error: No properties specified\n"));
return; return;
} }
TArray<FWingProperty> Props = FWingProperty::GetDetailsMutable(Obj, CPF_Edit); TArray<FWingProperty> Props = FWingProperty::GetDetails(Obj, CPF_Edit, true);
// Validation pass — resolve all properties before modifying anything. // Validation pass — resolve all properties before modifying anything.
for (const auto& Pair : Properties.Json->Values) for (const auto& Pair : Properties.Json->Values)
{ {
FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property")); FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property"), WingOut::Stdout);
if (!P) return; if (!P) return;
} }
@@ -51,10 +51,10 @@ public:
int SuccessCount = 0; int SuccessCount = 0;
for (const auto& Pair : Properties.Json->Values) for (const auto& Pair : Properties.Json->Values)
{ {
FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property")); FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property"), WingOut::Stdout);
if (P->SetJson(Pair.Value)) SuccessCount++; if (P->SetJson(*Pair.Value, WingOut::Stdout)) SuccessCount++;
} }
UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num()); WingOut::Stdout.Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
} }
}; };

View File

@@ -28,14 +28,14 @@ public:
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>(); UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub) if (!Sub)
{ {
UWingServer::Print(TEXT("Error: AssetEditorSubsystem not available\n")); WingOut::Stdout.Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return; return;
} }
TArray<UObject*> EditedAssets = Sub->GetAllEditedAssets(); TArray<UObject*> EditedAssets = Sub->GetAllEditedAssets();
if (EditedAssets.IsEmpty()) if (EditedAssets.IsEmpty())
{ {
UWingServer::Print(TEXT("No asset editors are open.\n")); WingOut::Stdout.Print(TEXT("No asset editors are open.\n"));
return; return;
} }
@@ -43,7 +43,7 @@ public:
{ {
bool bDirty = Asset->GetOutermost()->IsDirty(); bool bDirty = Asset->GetOutermost()->IsDirty();
UWingServer::Printf(TEXT(" %s%s\n"), WingOut::Stdout.Printf(TEXT(" %s%s\n"),
bDirty ? TEXT("[unsaved] ") : TEXT(""), bDirty ? TEXT("[unsaved] ") : TEXT(""),
*Asset->GetPathName()); *Asset->GetPathName());
} }

View File

@@ -29,20 +29,20 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Asset).Cast<UObject>(); UObject* Obj = F.Walk(Asset).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>(); UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub) if (!Sub)
{ {
UWingServer::Print(TEXT("Error: AssetEditorSubsystem not available\n")); WingOut::Stdout.Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return; return;
} }
if (Sub->OpenEditorForAsset(Obj)) if (Sub->OpenEditorForAsset(Obj))
UWingServer::Printf(TEXT("Opened editor for %s\n"), *Obj->GetPathName()); WingOut::Stdout.Printf(TEXT("Opened editor for %s\n"), *Obj->GetPathName());
else else
UWingServer::Printf(TEXT("Error: Could not open editor for %s\n"), *Obj->GetPathName()); WingOut::Stdout.Printf(TEXT("Error: Could not open editor for %s\n"), *Obj->GetPathName());
} }
}; };

View File

@@ -37,27 +37,27 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
// Check for valid proposed name // Check for valid proposed name
FName InternalID = WingUtils::CheckProposedName(Dispatcher); FName InternalID = WingUtils::CheckProposedName(Dispatcher, WingOut::Stdout);
if (InternalID.IsNone()) return; if (InternalID.IsNone()) return;
TSet<FName> Names; TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names); FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"))) return; if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("variable or component"), WingOut::Stdout)) return;
// Parse the arguments. // Parse the arguments.
WingVariables Vars; WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return; if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
// Add the delegate variable // Add the delegate variable
FEdGraphPinType DelegateType; FEdGraphPinType DelegateType;
DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate; DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
if (!FBlueprintEditorUtils::AddMemberVariable(BP, InternalID, DelegateType)) if (!FBlueprintEditorUtils::AddMemberVariable(BP, InternalID, DelegateType))
{ {
UWingServer::Printf(TEXT("ERROR: Failed to add event dispatcher '%s' to %s\n"), *Dispatcher, *WingUtils::FormatName(BP)); WingOut::Stdout.Printf(TEXT("ERROR: Failed to add event dispatcher '%s' to %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
return; return;
} }
@@ -67,7 +67,7 @@ public:
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass()); UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!SigGraph) if (!SigGraph)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to create signature graph for '%s'\n"), *Dispatcher); WingOut::Stdout.Printf(TEXT("ERROR: Failed to create signature graph for '%s'\n"), *Dispatcher);
return; return;
} }
@@ -79,9 +79,9 @@ public:
BP->DelegateSignatureGraphs.Add(SigGraph); BP->DelegateSignatureGraphs.Add(SigGraph);
// Store the function arguments // Store the function arguments
if (!Vars.SetBackingStore(SigGraph)) return; if (!Vars.SetBackingStore(SigGraph, WingOut::Stdout)) return;
if (!Vars.Check()) return; if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Create()) return; if (!Vars.Create(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Created event dispatcher %s in %s\n"), *Dispatcher, *WingUtils::FormatName(BP)); WingOut::Stdout.Printf(TEXT("Created event dispatcher %s in %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
} }
}; };

View File

@@ -33,13 +33,13 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>(); UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return; if (!BP) return;
FBPVariableDescription* Var = WingUtils::FindOneWithExternalID(Dispatcher, BP->NewVariables, TEXT("Dispatcher")); FBPVariableDescription* Var = WingUtils::FindOneWithExternalID(Dispatcher, BP->NewVariables, TEXT("Dispatcher"), WingOut::Stdout);
if (!Var) return; if (!Var) return;
TObjectPtr<UEdGraph>* SigGraph = WingUtils::FindOneWithExternalID(Dispatcher, BP->DelegateSignatureGraphs, TEXT("Dispatcher Signature Graph")); TObjectPtr<UEdGraph>* SigGraph = WingUtils::FindOneWithExternalID(Dispatcher, BP->DelegateSignatureGraphs, TEXT("Dispatcher Signature Graph"), WingOut::Stdout);
if (!SigGraph) return; if (!SigGraph) return;
UEdGraph* Graph = *SigGraph; UEdGraph* Graph = *SigGraph;
@@ -51,6 +51,6 @@ public:
// Remove the signature graph // Remove the signature graph
FBlueprintEditorUtils::RemoveGraph(BP, Graph, EGraphRemoveFlags::Recompile); FBlueprintEditorUtils::RemoveGraph(BP, Graph, EGraphRemoveFlags::Recompile);
UWingServer::Printf(TEXT("Deleted event dispatcher %s from %s\n"), *Dispatcher, *WingUtils::FormatName(BP)); WingOut::Stdout.Printf(TEXT("Deleted event dispatcher %s from %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
} }
}; };

View File

@@ -35,7 +35,7 @@ public:
private: private:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return; if (!NodeObj) return;
@@ -49,15 +49,15 @@ private:
if (WingToolMenu::Execute(Entry, Context)) if (WingToolMenu::Execute(Entry, Context))
{ {
UWingServer::Printf(TEXT("Executed: %s\n"), *LabelText); WingOut::Stdout.Printf(TEXT("Executed: %s\n"), *LabelText);
} }
else else
{ {
UWingServer::Printf(TEXT("ERROR: Action '%s' cannot execute (greyed out)\n"), *LabelText); WingOut::Stdout.Printf(TEXT("ERROR: Action '%s' cannot execute (greyed out)\n"), *LabelText);
} }
return; return;
} }
UWingServer::Printf(TEXT("ERROR: Menu item '%s' not found. Use GraphNode_ShowMenu to see available items.\n"), *Item); WingOut::Stdout.Printf(TEXT("ERROR: Menu item '%s' not found. Use GraphNode_ShowMenu to see available items.\n"), *Item);
} }
}; };

View File

@@ -30,6 +30,8 @@ struct FSpawnNodeEntry
UPROPERTY() UPROPERTY()
int32 PosY = 0; int32 PosY = 0;
FWingGraphAction *Action;
}; };
@@ -53,50 +55,40 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return; if (!TargetGraph) return;
int32 SuccessCount = 0; int32 SuccessCount = 0;
int32 TotalCount = Nodes.Array.Num(); int32 TotalCount = Nodes.Array.Num();
FWingGraphActions GraphActions(TargetGraph); FWingGraphActions GraphActions(TargetGraph);
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array) // Parse the json array, turning it into an array of spawn node entries.
TArray<FSpawnNodeEntry> Entries;
FSpawnNodeEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
{ {
FSpawnNodeEntry Entry; if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) return;
if (!FWingProperty::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
continue;
// Find the action by exact full name
TArray<FWingGraphAction*> Results = GraphActions.Search(Entry.ActionName, 2, true); TArray<FWingGraphAction*> Results = GraphActions.Search(Entry.ActionName, 2, true);
if (Results.Num() == 0) if (!WingUtils::CheckExactlyOneNamed(Results.Num(), TEXT("node type"), Entry.ActionName, WingOut::Stdout)) return;
{ Entry.Action = Results[0];
UWingServer::Printf(TEXT("ERROR: No action found matching '%s'. Use GraphNode_SearchTypes to find available actions.\n"), Entries.Add(Entry);
*Entry.ActionName);
continue;
}
if (Results.Num() > 1)
{
UWingServer::Printf(TEXT("ERROR: More than one action matches '%s'.\n"), *Entry.ActionName);
continue;
}
// Perform the action
UEdGraphNode* NewNode = Results[0]->Execute(FVector2D(Entry.PosX, Entry.PosY));
if (!NewNode)
{
UWingServer::Printf(TEXT("ERROR: Execute returned null for '%s'.\n"), *Entry.ActionName);
continue;
}
if (!NewNode->NodeGuid.IsValid())
NewNode->CreateNewGuid();
UWingServer::Printf(TEXT("Spawned: %s (%s)\n"),
*WingUtils::FormatName(NewNode), *WingUtils::FormatName(NewNode->GetClass()));
SuccessCount++;
} }
UWingServer::Printf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount); // Execute all.
for (const FSpawnNodeEntry &Entry : Entries)
{
UEdGraphNode* NewNode = Entry.Action->Execute(FVector2D(Entry.PosX, Entry.PosY));
if (NewNode)
{
WingOut::Stdout.Printf(TEXT("Spawned: %s %s\n"), *Entry.ActionName, *WingUtils::FormatName(NewNode));
}
else
{
WingOut::Stdout.Printf(TEXT("FAILED: %s\n"), *Entry.ActionName);
continue;
}
}
} }
}; };

View File

@@ -34,7 +34,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return; if (!FoundNode) return;
@@ -44,7 +44,7 @@ public:
if (!FoundNode->CanUserDeleteNode()) if (!FoundNode->CanUserDeleteNode())
{ {
UWingServer::Printf(TEXT("ERROR: Cannot delete node '%s' in graph '%s' — it is not deletable.\n"), WingOut::Stdout.Printf(TEXT("ERROR: Cannot delete node '%s' in graph '%s' — it is not deletable.\n"),
*NodeTitle, *GraphName); *NodeTitle, *GraphName);
return; return;
} }
@@ -63,6 +63,6 @@ public:
Graph->RemoveNode(FoundNode); Graph->RemoveNode(FoundNode);
} }
UWingServer::Printf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName); WingOut::Stdout.Printf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName);
} }
}; };

View File

@@ -29,12 +29,12 @@ public:
private: private:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return; if (!NodeObj) return;
WingGraphExport Exporter(NodeObj); WingGraphExport Exporter(NodeObj);
UWingServer::Print(*Exporter.GetOutput()); WingOut::Stdout.Print(*Exporter.GetOutput());
UWingServer::Print(Exporter.GetDetails()); WingOut::Stdout.Print(Exporter.GetDetails());
} }
}; };

View File

@@ -29,12 +29,12 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return; if (!FoundNode) return;
UWingServer::Printf(TEXT("Node: %s\n"), *WingUtils::FormatName(FoundNode)); WingOut::Stdout.Printf(TEXT("Node: %s\n"), *WingUtils::FormatName(FoundNode));
UWingServer::Printf(TEXT("Comment: %s\n"), *FoundNode->NodeComment); WingOut::Stdout.Printf(TEXT("Comment: %s\n"), *FoundNode->NodeComment);
UWingServer::Printf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false")); WingOut::Stdout.Printf(TEXT("BubbleVisible: %s\n"), FoundNode->bCommentBubbleVisible ? TEXT("true") : TEXT("false"));
} }
}; };

View File

@@ -33,13 +33,13 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return; if (!FoundNode) return;
if (!WingUtils::CheckCanRename(FoundNode, Name)) return; if (!WingUtils::CheckCanRename(FoundNode, Name, WingOut::Stdout)) return;
FoundNode->OnRenameNode(Name); FoundNode->OnRenameNode(Name);
UWingServer::Printf(TEXT("Renamed node to %s\n"), *Name); WingOut::Stdout.Printf(TEXT("Renamed node to %s\n"), *Name);
} }
}; };

View File

@@ -39,7 +39,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return; if (!TargetGraph) return;
@@ -47,16 +47,16 @@ public:
TArray<FWingGraphAction*> Results = GraphActions.Search(Query, MaxResults, false); TArray<FWingGraphAction*> Results = GraphActions.Search(Query, MaxResults, false);
for (const FWingGraphAction* Action : Results) for (const FWingGraphAction* Action : Results)
{ {
UWingServer::Printf(TEXT("%s\n"), *Action->Name); WingOut::Stdout.Printf(TEXT("%s\n"), *Action->Name);
} }
if (Results.Num() == 0) if (Results.Num() == 0)
{ {
UWingServer::Print(TEXT("No matching node types found.\n")); WingOut::Stdout.Print(TEXT("No matching node types found.\n"));
} }
else if (Results.Num() >= MaxResults) else if (Results.Num() >= MaxResults)
{ {
UWingServer::Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults); WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
} }
} }
}; };

View File

@@ -33,7 +33,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return; if (!FoundNode) return;
@@ -41,6 +41,6 @@ public:
FoundNode->bCommentBubbleVisible = !Comment.IsEmpty(); FoundNode->bCommentBubbleVisible = !Comment.IsEmpty();
FoundNode->bCommentBubblePinned = !Comment.IsEmpty(); FoundNode->bCommentBubblePinned = !Comment.IsEmpty();
UWingServer::Printf(TEXT("Comment set on %s\n"), *WingUtils::FormatName(FoundNode)); WingOut::Stdout.Printf(TEXT("Comment set on %s\n"), *WingUtils::FormatName(FoundNode));
} }
}; };

View File

@@ -55,7 +55,7 @@ public:
void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema) void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema)
{ {
WingFetcher F(GraphObj); WingFetcher F(GraphObj, WingOut::Stdout);
UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>(); UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
if (!Pin) return; if (!Pin) return;
@@ -63,7 +63,7 @@ public:
if (Pin->Direction != EGPD_Input) if (Pin->Direction != EGPD_Input)
{ {
UWingServer::Printf(TEXT("error: %s is an output pin\n"), *WingUtils::FormatName(Pin)); WingOut::Stdout.Printf(TEXT("error: %s is an output pin\n"), *WingUtils::FormatName(Pin));
return; return;
} }
@@ -74,7 +74,7 @@ public:
FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText); FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
if (!Error.IsEmpty()) if (!Error.IsEmpty())
{ {
UWingServer::Printf(TEXT("error: %s: %s\n"), *WingUtils::FormatName(Pin), *Error); WingOut::Stdout.Printf(TEXT("error: %s: %s\n"), *WingUtils::FormatName(Pin), *Error);
return; return;
} }
UWingServer::AddTouchedObject(Node); UWingServer::AddTouchedObject(Node);
@@ -87,17 +87,17 @@ public:
void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj) void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj)
{ {
WingFetcher F(GraphObj); WingFetcher F(GraphObj, WingOut::Stdout);
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>(); UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
if (!Node) return; if (!Node) return;
TArray<FWingProperty> All = FWingProperty::GetDetailsMutable(Node, CPF_Edit); TArray<FWingProperty> All = FWingProperty::GetDetails(Node, CPF_Edit, true);
FWingProperty *P = WingUtils::FindOneWithExternalID(Entry.Name, All, TEXT("Property")); FWingProperty *P = WingUtils::FindOneWithExternalID(Entry.Name, All, TEXT("Property"), WingOut::Stdout);
if (!P) return; if (!P) return;
UWingServer::AddTouchedObject(Node); UWingServer::AddTouchedObject(Node);
if (!P->SetText(Entry.Value)) if (!P->SetText(Entry.Value, WingOut::Stdout))
return; return;
} }
@@ -106,7 +106,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Fetch the graph once. // Fetch the graph once.
WingFetcher GraphFetcher; WingFetcher GraphFetcher(WingOut::Stdout);
UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>(); UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
if (!GraphObj) return; if (!GraphObj) return;
@@ -116,22 +116,19 @@ public:
if (!K2Schema && !MGSchema) if (!K2Schema && !MGSchema)
{ {
UWingServer::Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName()); WingOut::Stdout.Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName());
return; return;
} }
FSetNodeDefaultEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array) for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
{ {
FSetNodeDefaultEntry Entry; if (!FWingProperty::PopulateFromJson(Props, *PinVal, false, WingOut::Stdout)) continue;
if (!FWingProperty::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal)) if (K2Schema) HandleK2Entry(Entry, GraphObj, K2Schema);
continue; else if (MGSchema) HandleMaterialEntry(Entry, GraphObj);
if (K2Schema)
HandleK2Entry(Entry, GraphObj, K2Schema);
else if (MGSchema)
HandleMaterialEntry(Entry, GraphObj);
} }
UWingServer::Printf(TEXT("Done.\n")); WingOut::Stdout.Printf(TEXT("Done.\n"));
} }
}; };

View File

@@ -4,6 +4,7 @@
#include "WingServer.h" #include "WingServer.h"
#include "WingHandler.h" #include "WingHandler.h"
#include "WingFetcher.h" #include "WingFetcher.h"
#include "WingProperty.h"
#include "WingUtils.h" #include "WingUtils.h"
#include "Engine/Blueprint.h" #include "Engine/Blueprint.h"
#include "EdGraph/EdGraphNode.h" #include "EdGraph/EdGraphNode.h"
@@ -49,26 +50,25 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return; if (!TargetGraph) return;
int32 SuccessCount = 0; int32 SuccessCount = 0;
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array) FMoveNodeEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& Elt : Nodes.Array)
{ {
FMoveNodeEntry Entry; if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) continue;
if (!FWingProperty::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue; WingFetcher FN(TargetGraph, WingOut::Stdout);
WingFetcher FN(TargetGraph);
UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>(); UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>();
if (!Node) continue; if (!Node) continue;
Node->NodePosX = Entry.X; Node->NodePosX = Entry.X;
Node->NodePosY = Entry.Y; Node->NodePosY = Entry.Y;
SuccessCount++; SuccessCount++;
} }
UWingServer::Printf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num()); WingOut::Stdout.Printf(TEXT("Moved %d/%d nodes.\n"), SuccessCount, Nodes.Array.Num());
} }
}; };

View File

@@ -31,13 +31,13 @@ public:
private: private:
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>(); UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return; if (!NodeObj) return;
if (Cast<UMaterialGraphNode>(NodeObj)) if (Cast<UMaterialGraphNode>(NodeObj))
{ {
UWingServer::Printf(TEXT("Material graph nodes do not have usable context menus.")); WingOut::Stdout.Printf(TEXT("Material graph nodes do not have usable context menus."));
return; return;
} }
FToolMenuContext Context; FToolMenuContext Context;
@@ -45,8 +45,8 @@ private:
for (FToolMenuEntry &Entry : Entries) for (FToolMenuEntry &Entry : Entries)
{ {
FString LabelText = Entry.Label.Get().ToString(); FString LabelText = Entry.Label.Get().ToString();
UWingServer::Printf(TEXT("%s\n"), *LabelText); WingOut::Stdout.Printf(TEXT("%s\n"), *LabelText);
} }
if (Entries.IsEmpty()) UWingServer::Printf(TEXT("No selectable menu entries right now.\n")); if (Entries.IsEmpty()) WingOut::Stdout.Printf(TEXT("No selectable menu entries right now.\n"));
} }
}; };

View File

@@ -49,24 +49,25 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return; if (!G) return;
int32 SuccessCount = 0; int32 SuccessCount = 0;
int32 TotalCount = Connections.Array.Num(); int32 TotalCount = Connections.Array.Num();
FConnectPinsEntry Entry;
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array) for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
{ {
FConnectPinsEntry Entry; if (!FWingProperty::PopulateFromJson(EntryProps, *ConnVal, false, WingOut::Stdout))
if (!FWingProperty::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
continue; continue;
WingFetcher FS(G); WingFetcher FS(G, WingOut::Stdout);
UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast<UEdGraphPin>(); UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast<UEdGraphPin>();
if (!SourcePin) continue; if (!SourcePin) continue;
WingFetcher FT(G); WingFetcher FT(G, WingOut::Stdout);
UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>(); UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
if (!TargetPin) continue; if (!TargetPin) continue;
@@ -74,7 +75,7 @@ public:
const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin); const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
if (Response.Response == CONNECT_RESPONSE_DISALLOW) if (Response.Response == CONNECT_RESPONSE_DISALLOW)
{ {
UWingServer::Printf(TEXT("error: Cannot connect %s.%s to %s.%s: %s\n"), WingOut::Stdout.Printf(TEXT("error: Cannot connect %s.%s to %s.%s: %s\n"),
*WingUtils::FormatName(SourcePin->GetOwningNode()), *WingUtils::FormatName(SourcePin), *WingUtils::FormatName(SourcePin->GetOwningNode()), *WingUtils::FormatName(SourcePin),
*WingUtils::FormatName(TargetPin->GetOwningNode()), *WingUtils::FormatName(TargetPin), *WingUtils::FormatName(TargetPin->GetOwningNode()), *WingUtils::FormatName(TargetPin),
*Response.Message.ToString()); *Response.Message.ToString());
@@ -85,6 +86,6 @@ public:
SuccessCount++; SuccessCount++;
} }
UWingServer::Printf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount); WingOut::Stdout.Printf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount);
} }
}; };

View File

@@ -49,19 +49,20 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return; if (!G) return;
int32 SuccessCount = 0; int32 SuccessCount = 0;
int32 TotalDisconnected = 0; int32 TotalDisconnected = 0;
FDisconnectPinEntry Entry;
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array) for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
{ {
FDisconnectPinEntry Entry; if (!FWingProperty::PopulateFromJson(EntryProps, *DiscVal, false, WingOut::Stdout)) continue;
if (!FWingProperty::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
WingFetcher FP(G); WingFetcher FP(G, WingOut::Stdout);
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>(); UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
if (!Pin) continue; if (!Pin) continue;
@@ -69,13 +70,13 @@ public:
if (!Entry.TargetPin.IsEmpty()) if (!Entry.TargetPin.IsEmpty())
{ {
WingFetcher FT(G); WingFetcher FT(G, WingOut::Stdout);
UEdGraphPin* Target = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>(); UEdGraphPin* Target = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
if (!Target) continue; if (!Target) continue;
if (!Pin->LinkedTo.Contains(Target)) if (!Pin->LinkedTo.Contains(Target))
{ {
UWingServer::Printf(TEXT("Error: %s.%s is not connected to %s.%s\n"), WingOut::Stdout.Printf(TEXT("Error: %s.%s is not connected to %s.%s\n"),
*WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin), *WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin),
*WingUtils::FormatName(Target->GetOwningNode()), *WingUtils::FormatName(Target)); *WingUtils::FormatName(Target->GetOwningNode()), *WingUtils::FormatName(Target));
continue; continue;
@@ -93,14 +94,14 @@ public:
} }
} }
UWingServer::Printf(TEXT("Disconnected %d link(s) from %s.%s\n"), WingOut::Stdout.Printf(TEXT("Disconnected %d link(s) from %s.%s\n"),
DisconnectedCount, DisconnectedCount,
*WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin)); *WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin));
SuccessCount++; SuccessCount++;
TotalDisconnected += DisconnectedCount; TotalDisconnected += DisconnectedCount;
} }
UWingServer::Printf(TEXT("Done: %d/%d succeeded, %d links broken.\n"), WingOut::Stdout.Printf(TEXT("Done: %d/%d succeeded, %d links broken.\n"),
SuccessCount, Disconnections.Array.Num(), TotalDisconnected); SuccessCount, Disconnections.Array.Num(), TotalDisconnected);
} }
}; };

View File

@@ -36,19 +36,19 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UEdGraph *G = F.Walk(Graph).Cast<UEdGraph>(); UEdGraph *G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return; if (!G) return;
WingGraphExport Exporter(G); WingGraphExport Exporter(G);
UWingServer::Print(*Exporter.GetOutput()); WingOut::Stdout.Print(*Exporter.GetOutput());
if (IncludeDetails) if (IncludeDetails)
{ {
UWingServer::Print(Exporter.GetDetails()); WingOut::Stdout.Print(Exporter.GetDetails());
} }
else else
{ {
UWingServer::Printf(TEXT("\nNote: use bDetails=true to see suppressed details.")); WingOut::Stdout.Printf(TEXT("\nNote: use bDetails=true to see suppressed details."));
} }
} }
}; };

View File

@@ -40,13 +40,13 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return; if (!MI) return;
// Parse the association string. // Parse the association string.
EMaterialParameterAssociation Association; EMaterialParameterAssociation Association;
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association)) if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout))
return; return;
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer); FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
@@ -68,12 +68,12 @@ public:
if (Removed == 0) if (Removed == 0)
{ {
UWingServer::Printf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"), WingOut::Stdout.Printf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"),
*Parameter, *ParameterAssociation, ParameterLayer, *WingUtils::FormatName(MI)); *Parameter, *ParameterAssociation, ParameterLayer, *WingUtils::FormatName(MI));
return; return;
} }
UWingServer::Printf(TEXT("Cleared override for '%s' on %s\n"), WingOut::Stdout.Printf(TEXT("Cleared override for '%s' on %s\n"),
*Parameter, *WingUtils::FormatName(MI)); *Parameter, *WingUtils::FormatName(MI));
} }
}; };

View File

@@ -31,7 +31,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return; if (!MI) return;
@@ -42,7 +42,7 @@ public:
for (auto& [Info, Meta] : AllParams) for (auto& [Info, Meta] : AllParams)
{ {
if (!Meta.bOverride) continue; if (!Meta.bOverride) continue;
if (!bHasOverrides) { UWingServer::Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; } if (!bHasOverrides) { WingOut::Stdout.Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; }
WingMaterialParameter::FormatMaterialParameter(Info, Meta); WingMaterialParameter::FormatMaterialParameter(Info, Meta);
} }
@@ -51,7 +51,7 @@ public:
for (auto& [Info, Meta] : AllParams) for (auto& [Info, Meta] : AllParams)
{ {
if (Meta.bOverride) continue; if (Meta.bOverride) continue;
if (!bHasInherited) { UWingServer::Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; } if (!bHasInherited) { WingOut::Stdout.Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
WingMaterialParameter::FormatMaterialParameter(Info, Meta); WingMaterialParameter::FormatMaterialParameter(Info, Meta);
} }
} }

View File

@@ -44,13 +44,13 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>(); UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return; if (!MI) return;
// Parse the association string. // Parse the association string.
EMaterialParameterAssociation Association; EMaterialParameterAssociation Association;
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association)) if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout))
return; return;
// Build the parameter ID to look up. // Build the parameter ID to look up.
@@ -61,13 +61,13 @@ public:
FMaterialParameterMetadata* Found = AllParams.Find(ParamID); FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("No parameter named '%s' with association=%s layer=%d"), WingOut::Stdout.Printf(TEXT("No parameter named '%s' with association=%s layer=%d"),
*Parameter, *ParameterAssociation, ParameterLayer); *Parameter, *ParameterAssociation, ParameterLayer);
return; return;
} }
if (Found->PrimitiveDataIndex != INDEX_NONE) if (Found->PrimitiveDataIndex != INDEX_NONE)
{ {
UWingServer::Printf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter); WingOut::Stdout.Printf(TEXT("Parameter '%s' uses custom primitive data and cannot be set on a material instance"), *Parameter);
return; return;
} }
@@ -80,7 +80,7 @@ public:
float ScalarValue; float ScalarValue;
if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue)) if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue))
{ {
UWingServer::Printf(TEXT("Failed to parse '%s' as a float"), *Value); WingOut::Stdout.Printf(TEXT("Failed to parse '%s' as a float"), *Value);
return; return;
} }
MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue); MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue);
@@ -91,17 +91,17 @@ public:
FLinearColor Color; FLinearColor Color;
if (!Color.InitFromString(Value)) if (!Color.InitFromString(Value))
{ {
UWingServer::Printf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value); WingOut::Stdout.Printf(TEXT("Failed to parse '%s' as a color/vector (expected format: '(R=1,G=0,B=0,A=1)')"), *Value);
return; return;
} }
MI->SetVectorParameterValueEditorOnly(ParamID, Color); MI->SetVectorParameterValueEditorOnly(ParamID, Color);
break; break;
} }
default: default:
UWingServer::Printf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type); WingOut::Stdout.Printf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type);
return; return;
} }
UWingServer::Printf(TEXT("Set '%s' = %s on %s\n"), WingOut::Stdout.Printf(TEXT("Set '%s' = %s on %s\n"),
*Parameter, *Value, *WingUtils::FormatName(MI)); *Parameter, *Value, *WingUtils::FormatName(MI));
} }
}; };

View File

@@ -30,7 +30,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Load material // Load material
WingFetcher F; WingFetcher F(WingOut::Stdout);
UMaterial* MaterialObj = F.Asset(Material).Cast<UMaterial>(); UMaterial* MaterialObj = F.Asset(Material).Cast<UMaterial>();
if (!MaterialObj) return; if (!MaterialObj) return;
@@ -48,14 +48,14 @@ public:
if (Errors.IsEmpty()) if (Errors.IsEmpty())
{ {
UWingServer::Printf(TEXT("%s compiled successfully.\n"), *WingUtils::FormatName(MaterialObj)); WingOut::Stdout.Printf(TEXT("%s compiled successfully.\n"), *WingUtils::FormatName(MaterialObj));
} }
else else
{ {
UWingServer::Printf(TEXT("%s compiled with %d error(s):\n"), *WingUtils::FormatName(MaterialObj), Errors.Num()); WingOut::Stdout.Printf(TEXT("%s compiled with %d error(s):\n"), *WingUtils::FormatName(MaterialObj), Errors.Num());
for (const FString& Err : Errors) for (const FString& Err : Errors)
{ {
UWingServer::Printf(TEXT(" %s\n"), *Err); WingOut::Stdout.Printf(TEXT(" %s\n"), *Err);
} }
} }
} }

View File

@@ -30,7 +30,7 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UMaterial* Mat = F.Asset(Material).Cast<UMaterial>(); UMaterial* Mat = F.Asset(Material).Cast<UMaterial>();
if (!Mat) return; if (!Mat) return;
@@ -40,6 +40,6 @@ public:
{ {
WingMaterialParameter::FormatMaterialParameter(Info, Meta); WingMaterialParameter::FormatMaterialParameter(Info, Meta);
} }
if (AllParams.IsEmpty()) UWingServer::Printf(TEXT("No material parameters.\n")); if (AllParams.IsEmpty()) WingOut::Stdout.Printf(TEXT("No material parameters.\n"));
} }
}; };

View File

@@ -46,7 +46,7 @@ public:
if (Group != PrevGroup) if (Group != PrevGroup)
{ {
if (!PrevGroup.IsEmpty()) if (!PrevGroup.IsEmpty())
UWingServer::Print(TEXT("\n")); WingOut::Stdout.Print(TEXT("\n"));
PrevGroup = Group; PrevGroup = Group;
} }
} }
@@ -62,7 +62,7 @@ public:
} }
if (Kind == EWingHandlerKind::Normal) if (Kind == EWingHandlerKind::Normal)
{ {
UWingServer::Printf(TEXT( WingOut::Stdout.Printf(TEXT(
"\n" "\n"
"You can also use ShowCommands with Kind=Create to see commands that create new assets.\n" "You can also use ShowCommands with Kind=Create to see commands that create new assets.\n"
"\n")); "\n"));

View File

@@ -51,7 +51,7 @@ public:
Results.Sort(); Results.Sort();
for (const FString &Line : Results) for (const FString &Line : Results)
{ {
UWingServer::Printf(TEXT("%s\n"), *Line); WingOut::Stdout.Printf(TEXT("%s\n"), *Line);
} }
} }
}; };

View File

@@ -27,6 +27,6 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
UWingServer::Printf(TEXT("%s\n"), *WingTokenizer::ExternalizeID(FName(Input))); WingOut::Stdout.Printf(TEXT("%s\n"), *WingTokenizer::ExternalizeID(FName(Input)));
} }
}; };

View File

@@ -28,6 +28,6 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
WingTokenizer T(Input); WingTokenizer T(Input);
T.PrintEverything(UWingServer::GetPrintBuffer()); T.PrintEverything(WingOut::StdoutBuffer);
} }
}; };

View File

@@ -33,22 +33,22 @@ public:
Require.Blueprintable = false; Require.Blueprintable = false;
Require.AllowContainer = true; Require.AllowContainer = true;
bool OK = UWingTypes::TextToType(Input, PinType, Require); bool OK = UWingTypes::TextToType(Input, PinType, Require, WingOut::Stdout);
auto& Out = UWingServer::GetPrintBuffer(); auto& Out = WingOut::Stdout;
Out.Appendf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED")); Out.Printf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED"));
Out.Appendf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString()); Out.Printf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString());
Out.Appendf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString()); Out.Printf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString());
Out.Appendf(TEXT("PinSubCategoryObject: %s\n"), Out.Printf(TEXT("PinSubCategoryObject: %s\n"),
PinType.PinSubCategoryObject.IsValid() PinType.PinSubCategoryObject.IsValid()
? *PinType.PinSubCategoryObject->GetPathName() ? *PinType.PinSubCategoryObject->GetPathName()
: TEXT("(none)")); : TEXT("(none)"));
Out.Appendf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType); Out.Printf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType);
if (PinType.IsMap()) if (PinType.IsMap())
{ {
Out.Appendf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString()); Out.Printf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString());
Out.Appendf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString()); Out.Printf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString());
Out.Appendf(TEXT("ValueTerminalSubCategoryObject: %s\n"), Out.Printf(TEXT("ValueTerminalSubCategoryObject: %s\n"),
PinType.PinValueType.TerminalSubCategoryObject.IsValid() PinType.PinValueType.TerminalSubCategoryObject.IsValid()
? *PinType.PinValueType.TerminalSubCategoryObject->GetPathName() ? *PinType.PinValueType.TerminalSubCategoryObject->GetPathName()
: TEXT("(none)")); : TEXT("(none)"));
@@ -57,7 +57,7 @@ public:
if (OK) if (OK)
{ {
FString RoundTrip = UWingTypes::TypeToText(PinType); FString RoundTrip = UWingTypes::TypeToText(PinType);
Out.Appendf(TEXT("TypeToText: %s\n"), RoundTrip.IsEmpty() ? TEXT("(empty)") : *RoundTrip); Out.Printf(TEXT("TypeToText: %s\n"), RoundTrip.IsEmpty() ? TEXT("(empty)") : *RoundTrip);
} }
} }
}; };

View File

@@ -31,11 +31,11 @@ public:
FName Result = WingTokenizer::TryInternalizeID(Input, Error); FName Result = WingTokenizer::TryInternalizeID(Input, Error);
if (!Error.IsEmpty()) if (!Error.IsEmpty())
{ {
UWingServer::Printf(TEXT("Error: %s\n"), *Error); WingOut::Stdout.Printf(TEXT("Error: %s\n"), *Error);
} }
if (!Result.IsNone()) if (!Result.IsNone())
{ {
UWingServer::Printf(TEXT("Result: %s\n"), *Result.ToString()); WingOut::Stdout.Printf(TEXT("Result: %s\n"), *Result.ToString());
} }
} }
}; };

View File

@@ -94,13 +94,13 @@ public:
{ {
const UWingTypes::Info& Info = *Matches[i]; const UWingTypes::Info& Info = *Matches[i];
if (IsProjectDefined(Info)) if (IsProjectDefined(Info))
UWingServer::Printf(TEXT("%s (%s, User-Defined)\n"), *Info.Short, *BroadCategory(Info)); WingOut::Stdout.Printf(TEXT("%s (%s, User-Defined)\n"), *Info.Short, *BroadCategory(Info));
else else
UWingServer::Printf(TEXT("%s (%s)\n"), *Info.Short, *BroadCategory(Info)); WingOut::Stdout.Printf(TEXT("%s (%s)\n"), *Info.Short, *BroadCategory(Info));
} }
if (Count >= Limit) if (Count >= Limit)
{ {
UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n")); WingOut::Stdout.Printf(TEXT("Search limit reached, raise it with Limit=\n"));
} }
} }
}; };

View File

@@ -40,18 +40,18 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
WingVariables Vars; WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return; if (!Vars.SetBackingStore(Obj, WingOut::Stdout)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables)) return; if (!Vars.BlueprintVariables.ParseString(BlueprintVariables, WingOut::Stdout)) return;
if (!Vars.InputVariables.ParseString(InputVariables)) return; if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return; if (!Vars.OutputVariables.ParseString(OutputVariables, WingOut::Stdout)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return; if (!Vars.LocalVariables.ParseString(LocalVariables, WingOut::Stdout)) return;
if (!Vars.Check()) return; if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Create()) return; if (!Vars.Create(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Success.\n")); WingOut::Stdout.Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -29,13 +29,13 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
WingVariables Vars; WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return; if (!Vars.SetBackingStore(Obj, WingOut::Stdout)) return;
Vars.Load(); Vars.Load(WingOut::Stdout);
Vars.Print(UWingServer::GetPrintBuffer()); Vars.Print(WingOut::StdoutBuffer);
} }
}; };

View File

@@ -41,18 +41,18 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
WingVariables Vars; WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return; if (!Vars.SetBackingStore(Obj, WingOut::Stdout)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables)) return; if (!Vars.BlueprintVariables.ParseString(BlueprintVariables, WingOut::Stdout)) return;
if (!Vars.InputVariables.ParseString(InputVariables)) return; if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return; if (!Vars.OutputVariables.ParseString(OutputVariables, WingOut::Stdout)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return; if (!Vars.LocalVariables.ParseString(LocalVariables, WingOut::Stdout)) return;
if (!Vars.Check()) return; if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Modify()) return; if (!Vars.Modify(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Success.\n")); WingOut::Stdout.Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -40,17 +40,17 @@ public:
} }
virtual void Handle() override virtual void Handle() override
{ {
WingFetcher F; WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>(); UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return; if (!Obj) return;
WingVariables Vars; WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return; if (!Vars.SetBackingStore(Obj, WingOut::Stdout)) return;
if (!Vars.BlueprintVariables.ParseNamesString(BlueprintVariables)) return; if (!Vars.BlueprintVariables.ParseNamesString(BlueprintVariables, WingOut::Stdout)) return;
if (!Vars.InputVariables.ParseNamesString(InputVariables)) return; if (!Vars.InputVariables.ParseNamesString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseNamesString(OutputVariables)) return; if (!Vars.OutputVariables.ParseNamesString(OutputVariables, WingOut::Stdout)) return;
if (!Vars.LocalVariables.ParseNamesString(LocalVariables)) return; if (!Vars.LocalVariables.ParseNamesString(LocalVariables, WingOut::Stdout)) return;
if (!Vars.Remove()) return; if (!Vars.Remove(WingOut::Stdout)) return;
UWingServer::Printf(TEXT("Success.\n")); WingOut::Stdout.Printf(TEXT("Success.\n"));
} }
}; };

View File

@@ -48,7 +48,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Fetch the widget blueprint. // Fetch the widget blueprint.
WingFetcher F; WingFetcher F(WingOut::Stdout);
UWidgetBlueprint* BP = F.Walk(Blueprint).Cast<UWidgetBlueprint>(); UWidgetBlueprint* BP = F.Walk(Blueprint).Cast<UWidgetBlueprint>();
if (!BP) return; if (!BP) return;
UWidgetTree* Tree = BP->WidgetTree; UWidgetTree* Tree = BP->WidgetTree;
@@ -56,18 +56,18 @@ public:
// Resolve the widget type. // Resolve the widget type.
WingWidgets WidgetMenu; WingWidgets WidgetMenu;
TArray<WingWidgets::Type> TypeResults = WidgetMenu.Search(Type, 2, true); TArray<WingWidgets::Type> TypeResults = WidgetMenu.Search(Type, 2, true);
if (!WingUtils::CheckExactlyOneNamed(TypeResults.Num(), TEXT("Widget type"), Type)) return; if (!WingUtils::CheckExactlyOneNamed(TypeResults.Num(), TEXT("Widget type"), Type, WingOut::Stdout)) return;
// Load the widget's class. // Load the widget's class.
UClass* WidgetClass = TypeResults[0].Class.LoadSynchronous(); UClass* WidgetClass = TypeResults[0].Class.LoadSynchronous();
if (!WidgetClass) if (!WidgetClass)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to load widget class for '%s'\n"), *Type); WingOut::Stdout.Printf(TEXT("ERROR: Failed to load widget class for '%s'\n"), *Type);
return; return;
} }
// Validate the proposed name. // Validate the proposed name.
FName InternalID = WingUtils::CheckProposedName(Name); FName InternalID = WingUtils::CheckProposedName(Name, WingOut::Stdout);
if (InternalID.IsNone()) return; if (InternalID.IsNone()) return;
// Check that the name is unique among existing widgets. // Check that the name is unique among existing widgets.
@@ -75,23 +75,23 @@ public:
Tree->GetAllWidgets(AllWidgets); Tree->GetAllWidgets(AllWidgets);
TSet<FName> Names; TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names); FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateNames(Names, AllWidgets, TEXT("widget or variable"))) return; if (!WingUtils::FindNoDuplicateNames(Names, AllWidgets, TEXT("widget or variable"), WingOut::Stdout)) return;
if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("widget or variable"))) return; if (!WingUtils::FindNoDuplicateName(Names, InternalID, TEXT("widget or variable"), WingOut::Stdout)) return;
// If a parent is specified, find it and verify it's a panel. // If a parent is specified, find it and verify it's a panel.
UPanelWidget* ParentPanel = nullptr; UPanelWidget* ParentPanel = nullptr;
if (!Parent.IsEmpty()) if (!Parent.IsEmpty())
{ {
UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget")); UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget"), WingOut::Stdout);
if (!ParentWidget) return; if (!ParentWidget) return;
if (!WingWidgets::CheckCanBeParent(ParentWidget)) return; if (!WingWidgets::CheckCanBeParent(ParentWidget, WingOut::Stdout)) return;
ParentPanel = Cast<UPanelWidget>(ParentWidget); ParentPanel = Cast<UPanelWidget>(ParentWidget);
} }
else else
{ {
if (Tree->RootWidget != nullptr) if (Tree->RootWidget != nullptr)
{ {
UWingServer::Printf(TEXT("ERROR: Widget tree already has a root widget. Specify a Parent.\n")); WingOut::Stdout.Printf(TEXT("ERROR: Widget tree already has a root widget. Specify a Parent.\n"));
return; return;
} }
} }
@@ -105,7 +105,7 @@ public:
if (!NewWidget) if (!NewWidget)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to create widget\n")); WingOut::Stdout.Printf(TEXT("ERROR: Failed to create widget\n"));
return; return;
} }
@@ -123,6 +123,6 @@ public:
Tree->RootWidget = NewWidget; Tree->RootWidget = NewWidget;
} }
UWingServer::Printf(TEXT("Created widget '%s' of type '%s'\n"), *Name, *Type); WingOut::Stdout.Printf(TEXT("Created widget '%s' of type '%s'\n"), *Name, *Type);
} }
}; };

View File

@@ -34,7 +34,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Walk to the widget. // Walk to the widget.
WingFetcher F; WingFetcher F(WingOut::Stdout);
UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>(); UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>();
if (!TargetWidget) return; if (!TargetWidget) return;
@@ -42,7 +42,7 @@ public:
UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>(); UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>();
if (!BP) if (!BP)
{ {
UWingServer::Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n")); WingOut::Stdout.Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
return; return;
} }
UWidgetTree* Tree = BP->WidgetTree; UWidgetTree* Tree = BP->WidgetTree;
@@ -52,7 +52,7 @@ public:
{ {
if (Panel->GetChildrenCount() > 0) if (Panel->GetChildrenCount() > 0)
{ {
UWingServer::Printf(TEXT("ERROR: Widget '%s' has %d children. Remove them first.\n"), WingOut::Stdout.Printf(TEXT("ERROR: Widget '%s' has %d children. Remove them first.\n"),
*WingUtils::FormatName(TargetWidget), Panel->GetChildrenCount()); *WingUtils::FormatName(TargetWidget), Panel->GetChildrenCount());
return; return;
} }
@@ -80,6 +80,6 @@ public:
// Rename to transient package to avoid name conflicts with future widgets. // Rename to transient package to avoid name conflicts with future widgets.
TargetWidget->Rename(nullptr, GetTransientPackage()); TargetWidget->Rename(nullptr, GetTransientPackage());
UWingServer::Printf(TEXT("Deleted widget '%s'\n"), *WidgetName); WingOut::Stdout.Printf(TEXT("Deleted widget '%s'\n"), *WidgetName);
} }
}; };

View File

@@ -36,7 +36,7 @@ public:
virtual void Handle() override virtual void Handle() override
{ {
// Walk to the widget. // Walk to the widget.
WingFetcher F; WingFetcher F(WingOut::Stdout);
UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>(); UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>();
if (!TargetWidget) return; if (!TargetWidget) return;
@@ -44,7 +44,7 @@ public:
UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>(); UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>();
if (!BP) if (!BP)
{ {
UWingServer::Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n")); WingOut::Stdout.Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
return; return;
} }
UWidgetTree* Tree = BP->WidgetTree; UWidgetTree* Tree = BP->WidgetTree;
@@ -56,9 +56,9 @@ public:
FString WidgetName = WingUtils::FormatName(TargetWidget); FString WidgetName = WingUtils::FormatName(TargetWidget);
// Find the new parent and verify it's a panel with room. // Find the new parent and verify it's a panel with room.
UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget")); UWidget* ParentWidget = WingUtils::FindOneWithExternalID(Parent, AllWidgets, TEXT("Widget"), WingOut::Stdout);
if (!ParentWidget) return; if (!ParentWidget) return;
if (!WingWidgets::CheckCanBeParent(ParentWidget)) return; if (!WingWidgets::CheckCanBeParent(ParentWidget, WingOut::Stdout)) return;
UPanelWidget* ParentPanel = Cast<UPanelWidget>(ParentWidget); UPanelWidget* ParentPanel = Cast<UPanelWidget>(ParentWidget);
// Check for circular reparenting. // Check for circular reparenting.
@@ -66,7 +66,7 @@ public:
{ {
if (Ancestor == TargetWidget) if (Ancestor == TargetWidget)
{ {
UWingServer::Printf(TEXT("ERROR: Cannot reparent '%s' under itself\n"), *WidgetName); WingOut::Stdout.Printf(TEXT("ERROR: Cannot reparent '%s' under itself\n"), *WidgetName);
return; return;
} }
} }
@@ -77,6 +77,6 @@ public:
// Add to new parent. // Add to new parent.
ParentPanel->AddChild(TargetWidget); ParentPanel->AddChild(TargetWidget);
UWingServer::Printf(TEXT("Reparented '%s' under '%s'\n"), *WidgetName, *Parent); WingOut::Stdout.Printf(TEXT("Reparented '%s' under '%s'\n"), *WidgetName, *Parent);
} }
}; };

View File

@@ -35,16 +35,16 @@ public:
TArray<WingWidgets::Type> Results = Widgets.Search(Query, MaxResults, false); TArray<WingWidgets::Type> Results = Widgets.Search(Query, MaxResults, false);
for (const WingWidgets::Type& Entry : Results) for (const WingWidgets::Type& Entry : Results)
{ {
UWingServer::Printf(TEXT("%s\n"), *Entry.MenuName); WingOut::Stdout.Printf(TEXT("%s\n"), *Entry.MenuName);
} }
if (Results.Num() == 0) if (Results.Num() == 0)
{ {
UWingServer::Print(TEXT("No matching widget types found.\n")); WingOut::Stdout.Print(TEXT("No matching widget types found.\n"));
} }
else if (Results.Num() >= MaxResults) else if (Results.Num() >= MaxResults)
{ {
UWingServer::Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults); WingOut::Stdout.Printf(TEXT("WARNING: Reached limit of %d results. You may specify MaxResults.\n"), MaxResults);
} }
} }
}; };

View File

@@ -40,77 +40,77 @@ UWingComponentReference::FoundComponent UWingComponentReference::FindComponent(U
return Result; return Result;
} }
bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponent FC) bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponent FC, WingOut Errors)
{ {
if ((FC.SCS == nullptr) && (FC.Native == nullptr)) if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{ {
UWingServer::Printf(TEXT("Cannot find component: %s\n"), *WingUtils::ExternalizeID(FC.Name)); Errors.Printf(TEXT("Cannot find component: %s\n"), *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
} }
bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundComponent FC) bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundComponent FC, WingOut Errors)
{ {
if (FC.SCS && FC.Native) if (FC.SCS && FC.Native)
{ {
UWingServer::Printf(TEXT("Weirdness, two components named %s\n"), Errors.Printf(TEXT("Weirdness, two components named %s\n"),
*WingUtils::ExternalizeID(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
if ((FC.SCS == nullptr) && (FC.Native == nullptr)) if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{ {
UWingServer::Printf(TEXT("No such parent component: %s\n"), Errors.Printf(TEXT("No such parent component: %s\n"),
*WingUtils::ExternalizeID(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
if (FC.Native && (!Cast<USceneComponent>(FC.Native))) if (FC.Native && (!Cast<USceneComponent>(FC.Native)))
{ {
UWingServer::Printf(TEXT("Not a SceneComponent, so cannot be parent: %s\n"), Errors.Printf(TEXT("Not a SceneComponent, so cannot be parent: %s\n"),
*WingUtils::ExternalizeID(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
} }
bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC) bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC, WingOut Errors)
{ {
if (FC.SCS || FC.Native) if (FC.SCS || FC.Native)
{ {
UWingServer::Printf(TEXT("A component named %s already exists"), Errors.Printf(TEXT("A component named %s already exists"),
*WingUtils::ExternalizeID(FC.Name)); *WingUtils::ExternalizeID(FC.Name));
return false; return false;
} }
return true; return true;
} }
bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Action) bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Action, WingOut Errors)
{ {
if (FC.Native != nullptr) if (FC.Native != nullptr)
{ {
UWingServer::Printf(TEXT("Component %s is native, cannot %s native components"), Errors.Printf(TEXT("Component %s is native, cannot %s native components"),
*WingUtils::ExternalizeID(FC.Name), Action); *WingUtils::ExternalizeID(FC.Name), Action);
return false; return false;
} }
return true; return true;
} }
bool UWingComponentReference::CheckOwnedByBlueprint(FoundComponent FC, UBlueprint *BP) bool UWingComponentReference::CheckOwnedByBlueprint(FoundComponent FC, UBlueprint *BP, WingOut Errors)
{ {
if ((FC.SCS == nullptr) || (FC.SCS->GetSCS() != BP->SimpleConstructionScript)) if ((FC.SCS == nullptr) || (FC.SCS->GetSCS() != BP->SimpleConstructionScript))
{ {
UWingServer::Printf(TEXT("Component %s belongs to blueprint %s, edit that blueprint instead"), Errors.Printf(TEXT("Component %s belongs to blueprint %s, edit that blueprint instead"),
*WingUtils::ExternalizeID(FC.Name), *WingUtils::FormatName(FC.SCS->GetSCS()->GetBlueprint())); *WingUtils::ExternalizeID(FC.Name), *WingUtils::FormatName(FC.SCS->GetSCS()->GetBlueprint()));
return false; return false;
} }
return true; return true;
} }
bool UWingComponentReference::CheckValidComponentClass(UClass *Class) bool UWingComponentReference::CheckValidComponentClass(UClass *Class, WingOut Errors)
{ {
if (!Class->IsChildOf(UActorComponent::StaticClass())) if (!Class->IsChildOf(UActorComponent::StaticClass()))
{ {
UWingServer::Printf(TEXT("Class does not derive from ActorComponent: %s\n"), Errors.Printf(TEXT("Class does not derive from ActorComponent: %s\n"),
*WingUtils::FormatName(Class)); *WingUtils::FormatName(Class));
return false; return false;
} }
@@ -139,26 +139,26 @@ void UWingComponentReference::AddChildNode(UBlueprint *BP, USCS_Node *NewNode, F
} }
} }
bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name) bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name, WingOut Errors)
{ {
TSet<FName> Names; TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names); FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (Names.Contains(Name)) if (Names.Contains(Name))
{ {
UWingServer::Printf(TEXT("There is already a variable or component named %s in %s.\n"), Errors.Printf(TEXT("There is already a variable or component named %s in %s.\n"),
*WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class)); *WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false; return false;
} }
FoundComponent ExistingComponent = FindComponent(BP, Name); FoundComponent ExistingComponent = FindComponent(BP, Name);
if (!CheckNoSuchComponent(ExistingComponent)) return false; if (!CheckNoSuchComponent(ExistingComponent, Errors)) return false;
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName); FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false; if (!CheckValidParent(ParentComponent, Errors)) return false;
if (!CheckValidComponentClass(Class)) return false; if (!CheckValidComponentClass(Class, Errors)) return false;
USCS_Node *NewNode = BP->SimpleConstructionScript->CreateNode(Class, Name); USCS_Node *NewNode = BP->SimpleConstructionScript->CreateNode(Class, Name);
if (NewNode == nullptr) if (NewNode == nullptr)
{ {
UWingServer::Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"), Errors.Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"),
*WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class)); *WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false; return false;
} }
@@ -167,18 +167,18 @@ bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingC
return true; return true;
} }
bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent) bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent, WingOut Errors)
{ {
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName); FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false; if (!CheckValidParent(ParentComponent, Errors)) return false;
FoundComponent ThisComponent = FindComponent(BP, VariableName); FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false; if (!CheckExists(ThisComponent, Errors)) return false;
if (!CheckNotNative(ThisComponent, TEXT("reparent"))) return false; if (!CheckNotNative(ThisComponent, TEXT("reparent"), Errors)) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false; if (!CheckOwnedByBlueprint(ThisComponent, BP, Errors)) return false;
if (ParentComponent.SCS && ParentComponent.SCS->IsChildOf(ThisComponent.SCS)) if (ParentComponent.SCS && ParentComponent.SCS->IsChildOf(ThisComponent.SCS))
{ {
UWingServer::Printf(TEXT("Cannot parent a component to itself or its own child.")); Errors.Printf(TEXT("Cannot parent a component to itself or its own child."));
return false; return false;
} }
@@ -187,12 +187,12 @@ bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent)
return true; return true;
} }
bool UWingComponentReference::DeleteComponent() bool UWingComponentReference::DeleteComponent(WingOut Errors)
{ {
FoundComponent ThisComponent = FindComponent(BP, VariableName); FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false; if (!CheckExists(ThisComponent, Errors)) return false;
if (!CheckNotNative(ThisComponent, TEXT("delete"))) return false; if (!CheckNotNative(ThisComponent, TEXT("delete"), Errors)) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false; if (!CheckOwnedByBlueprint(ThisComponent, BP, Errors)) return false;
BP->SimpleConstructionScript->RemoveNodeAndPromoteChildren(ThisComponent.SCS); BP->SimpleConstructionScript->RemoveNodeAndPromoteChildren(ThisComponent.SCS);
return true; return true;
} }

View File

@@ -5,26 +5,26 @@
#include "AssetRegistry/AssetRegistryModule.h" #include "AssetRegistry/AssetRegistryModule.h"
#include "Kismet2/EnumEditorUtils.h" #include "Kismet2/EnumEditorUtils.h"
bool WingFactories::CheckNewAssetPath(const FString& Path) bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
{ {
if (UPackageTools::SanitizePackageName(Path) != Path) if (UPackageTools::SanitizePackageName(Path) != Path)
{ {
UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path); Errors.Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
return false; return false;
} }
if (!FPackageName::IsValidTextForLongPackageName(Path)) if (!FPackageName::IsValidTextForLongPackageName(Path))
{ {
UWingServer::Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path); Errors.Printf(TEXT("ERROR: Package path '%s' is not a valid package name\n"), *Path);
return false; return false;
} }
if (!Path.StartsWith(TEXT("/Game"))) if (!Path.StartsWith(TEXT("/Game")))
{ {
UWingServer::Printf(TEXT("ERROR: Package path '%s' must start with '/Game'\n"), *Path); Errors.Printf(TEXT("ERROR: Package path '%s' must start with '/Game'\n"), *Path);
return false; return false;
} }
if (FindObject<UPackage>(nullptr, *Path)) if (FindObject<UPackage>(nullptr, *Path))
{ {
UWingServer::Printf(TEXT("ERROR: An asset already exists at '%s'\n"), *Path); Errors.Printf(TEXT("ERROR: An asset already exists at '%s'\n"), *Path);
return false; return false;
} }
return true; return true;
@@ -38,24 +38,24 @@ bool WingFactories::IsBlacklisted(UClass* FactoryClass)
return false; return false;
} }
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory) UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, WingOut Errors)
{ {
// Check the blacklist. // Check the blacklist.
if (IsBlacklisted(Factory->GetClass())) if (IsBlacklisted(Factory->GetClass()))
{ {
UWingServer::Printf(TEXT("ERROR: Factory '%s' is blacklisted\n"), *Factory->GetClass()->GetName()); Errors.Printf(TEXT("ERROR: Factory '%s' is blacklisted\n"), *Factory->GetClass()->GetName());
return nullptr; return nullptr;
} }
// Validate the path, and that there's not already something there. // Validate the path, and that there's not already something there.
if (!CheckNewAssetPath(Path)) return nullptr; if (!CheckNewAssetPath(Path, Errors)) return nullptr;
FName Name = FName(FPackageName::GetShortName(Path)); FName Name = FName(FPackageName::GetShortName(Path));
// Create the package. // Create the package.
UPackage *Package = CreatePackage(*Path); UPackage *Package = CreatePackage(*Path);
if (!Package) if (!Package)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *Path); Errors.Printf(TEXT("ERROR: Failed to create package at '%s'\n"), *Path);
return nullptr; return nullptr;
} }
@@ -82,7 +82,7 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory)
} }
else else
{ {
UWingServer::Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName()); Errors.Printf(TEXT("ERROR: Factory '%s' failed to create an object\n"), *Factory->GetClass()->GetName());
Package->ClearDirtyFlag(); Package->ClearDirtyFlag();
Package->MarkAsGarbage(); Package->MarkAsGarbage();
return nullptr; return nullptr;
@@ -109,7 +109,7 @@ UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FN
{ {
if (!FEnumEditorUtils::IsNameAvailebleForUserDefinedEnum(Name)) if (!FEnumEditorUtils::IsNameAvailebleForUserDefinedEnum(Name))
{ {
UWingServer::Printf(TEXT("ERROR: Enum name is already taken: %s\n"), *Name.ToString()); WingOut::Stdout.Printf(TEXT("ERROR: Enum name is already taken: %s\n"), *Name.ToString());
return nullptr; return nullptr;
} }
return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags); return FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);

View File

@@ -59,11 +59,11 @@ WingFetcher& WingFetcher::SetError()
void WingFetcher::PathFailed(const TCHAR* Expected) void WingFetcher::PathFailed(const TCHAR* Expected)
{ {
if (ResultPin) if (ResultPin)
UWingServer::Printf(TEXT("ERROR: Path specifies a pin, but expected %s\n"), Expected); Errors.Printf(TEXT("ERROR: Path specifies a pin, but expected %s\n"), Expected);
else if (Obj) else if (Obj)
UWingServer::Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj->GetClass()->GetName(), Expected); Errors.Printf(TEXT("ERROR: Path specifies a %s, but expected %s\n"), *Obj->GetClass()->GetName(), Expected);
else else
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n")); Errors.Printf(TEXT("ERROR: Path led to a null pointer\n"));
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
SetError(); SetError();
} }
@@ -71,11 +71,11 @@ void WingFetcher::PathFailed(const TCHAR* Expected)
WingFetcher& WingFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expected) WingFetcher& WingFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expected)
{ {
if (ResultPin) if (ResultPin)
UWingServer::Printf(TEXT("ERROR: Input to '%s' is a pin, but expected %s\n"), Walker, Expected); Errors.Printf(TEXT("ERROR: Input to '%s' is a pin, but expected %s\n"), Walker, Expected);
else if (Obj) else if (Obj)
UWingServer::Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected); Errors.Printf(TEXT("ERROR: Input to '%s' is %s, but expected %s\n"), Walker, *Obj->GetClass()->GetName(), Expected);
else else
UWingServer::Printf(TEXT("ERROR: Path led to a null pointer\n")); Errors.Printf(TEXT("ERROR: Path led to a null pointer\n"));
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
SetError(); SetError();
return *this; return *this;
@@ -87,7 +87,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
if (Path.Contains(TEXT(" "))) if (Path.Contains(TEXT(" ")))
{ {
UWingServer::Printf(TEXT("ERROR: Paths may not contain whitespace.")); Errors.Printf(TEXT("ERROR: Paths may not contain whitespace."));
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
UWingServer::SuggestManual(WingManual::Section::EscapeSequences); UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
return SetError(); return SetError();
@@ -96,7 +96,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
Path.ParseIntoArray(Segments, TEXT(",")); Path.ParseIntoArray(Segments, TEXT(","));
if (Segments.Num() == 0) if (Segments.Num() == 0)
{ {
UWingServer::Print(TEXT("ERROR: Empty path\n")); Errors.Print(TEXT("ERROR: Empty path\n"));
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
return SetError(); return SetError();
} }
@@ -117,7 +117,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
WalkFunc Func = GetWalker(Key); WalkFunc Func = GetWalker(Key);
if (!Func) if (!Func)
{ {
UWingServer::Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key); Errors.Printf(TEXT("ERROR: Unknown path step '%s'\n"), *Key);
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
return SetError(); return SetError();
} }
@@ -134,7 +134,7 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
if (!PackagePath.StartsWith(TEXT("/"))) if (!PackagePath.StartsWith(TEXT("/")))
{ {
UWingServer::Printf(TEXT("ERROR: Path must start with '/', got '%s'\n"), *PackagePath); Errors.Printf(TEXT("ERROR: Path must start with '/', got '%s'\n"), *PackagePath);
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
return SetError(); return SetError();
} }
@@ -144,13 +144,13 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
FString PackageName = FPackageName::ObjectPathToPackageName(PackagePath); FString PackageName = FPackageName::ObjectPathToPackageName(PackagePath);
if (!FPackageName::DoesPackageExist(PackageName)) if (!FPackageName::DoesPackageExist(PackageName))
{ {
UWingServer::Printf(TEXT("ERROR: Asset '%s' does not exist.\n"), *PackagePath); Errors.Printf(TEXT("ERROR: Asset '%s' does not exist.\n"), *PackagePath);
return SetError(); return SetError();
} }
SetObj(LoadObject<UObject>(nullptr, *PackagePath)); SetObj(LoadObject<UObject>(nullptr, *PackagePath));
if (!Obj) if (!Obj)
{ {
UWingServer::Printf(TEXT("ERROR: Could not load asset '%s'\n"), *PackagePath); Errors.Printf(TEXT("ERROR: Could not load asset '%s'\n"), *PackagePath);
return SetError(); return SetError();
} }
@@ -160,13 +160,13 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>(); UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub || !Sub->OpenEditorForAsset(Obj)) if (!Sub || !Sub->OpenEditorForAsset(Obj))
{ {
UWingServer::Printf(TEXT("ERROR: Could not open editor for '%s'\n"), *PackagePath); Errors.Printf(TEXT("ERROR: Could not open editor for '%s'\n"), *PackagePath);
return SetError(); return SetError();
} }
Editor = Sub->FindEditorForAsset(OriginalAsset, false); Editor = Sub->FindEditorForAsset(OriginalAsset, false);
if (!Editor) if (!Editor)
{ {
UWingServer::Printf(TEXT("ERROR: Could not find editor instance for '%s'\n"), *PackagePath); Errors.Printf(TEXT("ERROR: Could not find editor instance for '%s'\n"), *PackagePath);
return SetError(); return SetError();
} }
@@ -184,7 +184,7 @@ bool WingFetcher::CheckAssetIsA(UClass* StaticClass)
if (bError) return false; if (bError) return false;
if (!OriginalAsset || !OriginalAsset->IsA(StaticClass)) if (!OriginalAsset || !OriginalAsset->IsA(StaticClass))
{ {
UWingServer::Printf(TEXT("ERROR: Asset is %s, expected %s\n"), Errors.Printf(TEXT("ERROR: Asset is %s, expected %s\n"),
OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"), OriginalAsset ? *OriginalAsset->GetClass()->GetName() : TEXT("null"),
*StaticClass->GetName()); *StaticClass->GetName());
SetError(); SetError();
@@ -202,14 +202,14 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
{ {
if (!Value.IsEmpty()) if (!Value.IsEmpty())
{ {
UWingServer::Printf(TEXT("ERROR: Materials have only one graph, with a blank name.\n\n")); Errors.Printf(TEXT("ERROR: Materials have only one graph, with a blank name.\n\n"));
UWingServer::SuggestManual(WingManual::Section::Paths); UWingServer::SuggestManual(WingManual::Section::Paths);
return SetError(); return SetError();
} }
WingUtils::EnsureMaterialGraph(Mat); WingUtils::EnsureMaterialGraph(Mat);
if (!Mat->MaterialGraph) if (!Mat->MaterialGraph)
{ {
UWingServer::Printf(TEXT("ERROR: Material '%s' has no material graph\n"), *Mat->GetName()); Errors.Printf(TEXT("ERROR: Material '%s' has no material graph\n"), *Mat->GetName());
return SetError(); return SetError();
} }
SetObj(Mat->MaterialGraph); SetObj(Mat->MaterialGraph);
@@ -224,13 +224,13 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
} }
TArray<UEdGraph*> Graphs = WingUtils::AllGraphs(BP); TArray<UEdGraph*> Graphs = WingUtils::AllGraphs(BP);
UEdGraph* Found = WingUtils::FindOneWithExternalID(Value, Graphs, TEXT("graph")); UEdGraph* Found = WingUtils::FindOneWithExternalID(Value, Graphs, TEXT("graph"), Errors);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("Graphs that exist in blueprint:\n")); Errors.Printf(TEXT("Graphs that exist in blueprint:\n"));
for (const UEdGraph *G : Graphs) for (const UEdGraph *G : Graphs)
{ {
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(G)); Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(G));
} }
return SetError(); return SetError();
} }
@@ -253,13 +253,13 @@ WingFetcher& WingFetcher::Node(const FString& Value)
// Get the nodes from the graph. // Get the nodes from the graph.
TArray<UEdGraphNode *> AllNodes = WingUtils::AllNodes(Graph); TArray<UEdGraphNode *> AllNodes = WingUtils::AllNodes(Graph);
UEdGraphNode *Node = WingUtils::FindOneWithExternalID(Value, AllNodes, TEXT("node")); UEdGraphNode *Node = WingUtils::FindOneWithExternalID(Value, AllNodes, TEXT("node"), Errors);
if (Node == nullptr) if (Node == nullptr)
{ {
UWingServer::Printf(TEXT("Nodes that exist in graph:\n")); Errors.Printf(TEXT("Nodes that exist in graph:\n"));
for (const UEdGraphNode *N : AllNodes) for (const UEdGraphNode *N : AllNodes)
{ {
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(N)); Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(N));
} }
return SetError(); return SetError();
} }
@@ -277,13 +277,13 @@ WingFetcher& WingFetcher::Pin(const FString& Value)
TypeMismatch(TEXT("pin"), TEXT("node")); TypeMismatch(TEXT("pin"), TEXT("node"));
return SetError(); return SetError();
} }
UEdGraphPin *Found = WingUtils::FindOneWithExternalID(Value, N->Pins, TEXT("pin")); UEdGraphPin *Found = WingUtils::FindOneWithExternalID(Value, N->Pins, TEXT("pin"), Errors);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("Pins that exist in the node:\n")); Errors.Printf(TEXT("Pins that exist in the node:\n"));
for (const UEdGraphPin *P : N->Pins) for (const UEdGraphPin *P : N->Pins)
{ {
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(P)); Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(P));
} }
return SetError(); return SetError();
} }
@@ -303,13 +303,13 @@ WingFetcher& WingFetcher::Component(const FString& Value)
} }
TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP); TArray<UWingComponentReference*> AllComponents = UWingComponentReference::GetAll(BP);
UWingComponentReference* Found = WingUtils::FindOneWithExternalID(Value, AllComponents, TEXT("component")); UWingComponentReference* Found = WingUtils::FindOneWithExternalID(Value, AllComponents, TEXT("component"), Errors);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("Components that exist in the blueprint:\n")); Errors.Printf(TEXT("Components that exist in the blueprint:\n"));
for (const UWingComponentReference* C : AllComponents) for (const UWingComponentReference* C : AllComponents)
{ {
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(C)); Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(C));
} }
return SetError(); return SetError();
} }
@@ -331,13 +331,13 @@ WingFetcher& WingFetcher::Widget(const FString& Value)
TArray<UWidget*> AllWidgets; TArray<UWidget*> AllWidgets;
WidgetBP->WidgetTree->GetAllWidgets(AllWidgets); WidgetBP->WidgetTree->GetAllWidgets(AllWidgets);
UWidget* Found = WingUtils::FindOneWithExternalID(Value, AllWidgets, TEXT("widget")); UWidget* Found = WingUtils::FindOneWithExternalID(Value, AllWidgets, TEXT("widget"), Errors);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("Widgets that exist in the blueprint:\n")); Errors.Printf(TEXT("Widgets that exist in the blueprint:\n"));
for (const UWidget *W : AllWidgets) for (const UWidget *W : AllWidgets)
{ {
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(W)); Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(W));
} }
return SetError(); return SetError();
} }
@@ -358,18 +358,17 @@ WingFetcher& WingFetcher::LevelBlueprint(const FString& Value)
if (!World->PersistentLevel) if (!World->PersistentLevel)
{ {
UWingServer::Print(TEXT("ERROR: World has no PersistentLevel\n")); Errors.Print(TEXT("ERROR: World has no PersistentLevel\n"));
return SetError(); return SetError();
} }
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true); ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
if (!LevelBP) if (!LevelBP)
{ {
UWingServer::Print(TEXT("ERROR: World has no level blueprint\n")); Errors.Print(TEXT("ERROR: World has no level blueprint\n"));
return SetError(); return SetError();
} }
SetObj(LevelBP); SetObj(LevelBP);
return *this; return *this;
} }

View File

@@ -254,7 +254,7 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
FString PrimaryCategory; FString PrimaryCategory;
if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node)) if (UMaterialGraphNode* MatNode = Cast<UMaterialGraphNode>(Node))
{ {
WingPropHandle::Handles Handles = Props.GetDetails(Node, false); WingPropHandle::Handles Handles = Props.GetDetails(Node, false, WingOut::Stdout);
PrimaryCategory = MatNode->MaterialExpression->GetClass()->GetName(); PrimaryCategory = MatNode->MaterialExpression->GetClass()->GetName();
for (const TSharedPtr<IPropertyHandle>& H : Handles) for (const TSharedPtr<IPropertyHandle>& H : Handles)
{ {
@@ -265,7 +265,7 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
} }
else if (bPrimary) else if (bPrimary)
{ {
WingPropHandle::Handles Handles = Props.GetDetails(Node, false); WingPropHandle::Handles Handles = Props.GetDetails(Node, false, WingOut::Stdout);
for (const TSharedPtr<IPropertyHandle>& H : Handles) for (const TSharedPtr<IPropertyHandle>& H : Handles)
WingPropHandle::Print(*H, Out); WingPropHandle::Print(*H, Out);
} }
@@ -274,8 +274,8 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
void WingGraphExport::EmitLocalVariables() void WingGraphExport::EmitLocalVariables()
{ {
WingVariables Vars; WingVariables Vars;
Vars.SetBackingStore(Graph); Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(); Vars.Load(WingOut::Stdout);
Vars.Print(Output); Vars.Print(Output);
} }

View File

@@ -5,17 +5,17 @@
void WingManual::PrintHandlerPrototype(const FWingHandlerConfig& Handler) void WingManual::PrintHandlerPrototype(const FWingHandlerConfig& Handler)
{ {
UWingServer::Print(Handler.Name); WingOut::Stdout.Print(Handler.Name);
UWingServer::Print(TEXT("(")); WingOut::Stdout.Print(TEXT("("));
bool bFirst = true; bool bFirst = true;
for (TFieldIterator<FProperty> PropIt(Handler.Class.Get(), EFieldIterationFlags::None); PropIt; ++PropIt) for (TFieldIterator<FProperty> PropIt(Handler.Class.Get(), EFieldIterationFlags::None); PropIt; ++PropIt)
{ {
if (!bFirst) UWingServer::Print(TEXT(",")); if (!bFirst) WingOut::Stdout.Print(TEXT(","));
bFirst = false; bFirst = false;
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?")); if (PropIt->HasMetaData(TEXT("Optional"))) WingOut::Stdout.Print(TEXT("?"));
UWingServer::Print(PropIt->GetName()); WingOut::Stdout.Print(PropIt->GetName());
} }
UWingServer::Print(TEXT(")\n")); WingOut::Stdout.Print(TEXT(")\n"));
} }
void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler) void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler)
@@ -31,30 +31,30 @@ void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler)
if (bOptional) if (bOptional)
{ {
UWingServer::Printf(TEXT(" %s (optional %s)"), *Name, *Type); WingOut::Stdout.Printf(TEXT(" %s (optional %s)"), *Name, *Type);
} }
else else
{ {
UWingServer::Printf(TEXT(" %s (%s)"), *Name, *Type); WingOut::Stdout.Printf(TEXT(" %s (%s)"), *Name, *Type);
} }
if (!Desc.IsEmpty()) UWingServer::Printf(TEXT(" — %s"), *Desc); if (!Desc.IsEmpty()) WingOut::Stdout.Printf(TEXT(" — %s"), *Desc);
UWingServer::Print(TEXT("\n")); WingOut::Stdout.Print(TEXT("\n"));
} }
} }
void WingManual::PrintHandlerDescription(const FWingHandlerConfig& Handler) void WingManual::PrintHandlerDescription(const FWingHandlerConfig& Handler)
{ {
if (Handler.Documentation.IsEmpty()) return; if (Handler.Documentation.IsEmpty()) return;
UWingServer::Print(WingUtils::WrapText(Handler.Documentation, 80, TEXT(" // "))); WingOut::Stdout.Print(WingUtils::WrapText(Handler.Documentation, 80, TEXT(" // ")));
} }
void WingManual::PrintHandlerHelp(const FWingHandlerConfig& Handler) void WingManual::PrintHandlerHelp(const FWingHandlerConfig& Handler)
{ {
UWingServer::Print(TEXT("\n")); WingOut::Stdout.Print(TEXT("\n"));
PrintHandlerPrototype(Handler); PrintHandlerPrototype(Handler);
PrintHandlerArguments(Handler); PrintHandlerArguments(Handler);
PrintHandlerDescription(Handler); PrintHandlerDescription(Handler);
UWingServer::Print(TEXT("\n")); WingOut::Stdout.Print(TEXT("\n"));
} }
void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* Handler, bool Abridged) void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* Handler, bool Abridged)
@@ -65,7 +65,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
if (Abridged) if (Abridged)
{ {
UWingServer::Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n")); WingOut::Stdout.Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n"));
} }
if (Handler && (Sections.Contains(Section::HandlerHelp) || bPrintAll)) if (Handler && (Sections.Contains(Section::HandlerHelp) || bPrintAll))
@@ -77,7 +77,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{ {
if (Abridged) if (Abridged)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n PATHS: Here are some example paths:" "\n PATHS: Here are some example paths:"
"\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122" "\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122"
"\n /Game/Testing/BP_Test,graph:Rescale·Actor,node:K2Node_CallFunction_0,pin:Scale" "\n /Game/Testing/BP_Test,graph:Rescale·Actor,node:K2Node_CallFunction_0,pin:Scale"
@@ -87,7 +87,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
} }
else else
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n PATHS:" "\n PATHS:"
"\n" "\n"
"\n Most commands require you to specify a path. A path starts" "\n Most commands require you to specify a path. A path starts"
@@ -123,7 +123,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{ {
if (Abridged) if (Abridged)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n TYPES: Here are some examples of valid types:" "\n TYPES: Here are some examples of valid types:"
"\n Bool, String, Vector, Rotator, HitResult, Actor, Character," "\n Bool, String, Vector, Rotator, HitResult, Actor, Character,"
"\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn," "\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn,"
@@ -134,7 +134,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
} }
else else
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n TYPES:" "\n TYPES:"
"\n" "\n"
"\n To change variable types, or to express function prototypes, you will" "\n To change variable types, or to express function prototypes, you will"
@@ -157,7 +157,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{ {
if (Abridged) if (Abridged)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n VARIABLE DECLARATIONS: example variable declarations:" "\n VARIABLE DECLARATIONS: example variable declarations:"
"\n Array<Actor> Actors" "\n Array<Actor> Actors"
"\n Float F (InstanceEditable)" "\n Float F (InstanceEditable)"
@@ -166,7 +166,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
} }
else else
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n VARIABLE DECLARATIONS:" "\n VARIABLE DECLARATIONS:"
"\n" "\n"
"\n We have our own syntax for variable declarations: a type," "\n We have our own syntax for variable declarations: a type,"
@@ -192,7 +192,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{ {
if (Abridged) if (Abridged)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n USING HTML ESCAPE SEQUENCES:" "\n USING HTML ESCAPE SEQUENCES:"
"\n When we output FNames, we use HTML escape sequences for the" "\n When we output FNames, we use HTML escape sequences for the"
"\n following marks: \\\"'(),.:;<=>& We also escape control" "\n following marks: \\\"'(),.:;<=>& We also escape control"
@@ -218,7 +218,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
} }
else else
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n USING HTML ESCAPE SEQUENCES:" "\n USING HTML ESCAPE SEQUENCES:"
"\n When we output FNames, we use HTML escape sequences for the" "\n When we output FNames, we use HTML escape sequences for the"
"\n following marks: \\\"'(),.:;<=>&, and for certain other characters." "\n following marks: \\\"'(),.:;<=>&, and for certain other characters."
@@ -234,7 +234,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
if (Sections.Contains(Section::Whitespace) || bPrintAll) if (Sections.Contains(Section::Whitespace) || bPrintAll)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n ABOUT WHITESPACE:" "\n ABOUT WHITESPACE:"
"\n Do not put excess whitespace into paths, typenames, or" "\n Do not put excess whitespace into paths, typenames, or"
"\n function prototypes, only use whitespace where it is required" "\n function prototypes, only use whitespace where it is required"
@@ -247,7 +247,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{ {
if (Abridged) if (Abridged)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n MATERIAL EDITING:" "\n MATERIAL EDITING:"
"\n We do not expose material expressions directly. Instead, use" "\n We do not expose material expressions directly. Instead, use"
"\n Property_Dump and Property_Set on the material graph nodes to" "\n Property_Dump and Property_Set on the material graph nodes to"
@@ -257,7 +257,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
} }
else else
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n MATERIAL EDITING:" "\n MATERIAL EDITING:"
"\n" "\n"
"\n We do not expose material expressions directly. Instead, you" "\n We do not expose material expressions directly. Instead, you"
@@ -272,7 +272,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
if (Sections.Contains(Section::ImportantCommands) || bPrintAll) if (Sections.Contains(Section::ImportantCommands) || bPrintAll)
{ {
UWingServer::Print(TEXT( WingOut::Stdout.Print(TEXT(
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:" "\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
"\n UserManual: this explanation" "\n UserManual: this explanation"
"\n ShowCommands: a full list of all the commands" "\n ShowCommands: a full list of all the commands"
@@ -288,6 +288,6 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
if (Abridged) if (Abridged)
{ {
UWingServer::Printf(TEXT("\nUse command 'UserManual' to see the full manual.\n")); WingOut::Stdout.Printf(TEXT("\nUse command 'UserManual' to see the full manual.\n"));
} }
} }

View File

@@ -15,7 +15,7 @@ TMap<FMaterialParameterInfo, FMaterialParameterMetadata> WingMaterialParameter::
return Result; return Result;
} }
bool WingMaterialParameter::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation) bool WingMaterialParameter::ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, WingOut Errors)
{ {
if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase)) if (Str.Equals(TEXT("Global"), ESearchCase::IgnoreCase))
OutAssociation = GlobalParameter; OutAssociation = GlobalParameter;
@@ -25,7 +25,7 @@ bool WingMaterialParameter::ParseMaterialParameterAssociation(const FString& Str
OutAssociation = BlendParameter; OutAssociation = BlendParameter;
else else
{ {
UWingServer::Printf(TEXT("ERROR: Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')\n"), *Str); Errors.Printf(TEXT("ERROR: Invalid ParameterAssociation '%s' (expected 'Global', 'Layer', or 'Blend')\n"), *Str);
return false; return false;
} }
return true; return true;
@@ -43,35 +43,35 @@ void WingMaterialParameter::FormatMaterialParameter(const FMaterialParameterInfo
switch (Meta.Value.Type) switch (Meta.Value.Type)
{ {
case EMaterialParameterType::Scalar: case EMaterialParameterType::Scalar:
UWingServer::Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar()); WingOut::Stdout.Printf(TEXT(" %sScalar \"%s\" = %g\n"), *Prefix, *Info.Name.ToString(), Meta.Value.AsScalar());
break; break;
case EMaterialParameterType::Vector: case EMaterialParameterType::Vector:
{ {
FLinearColor C = Meta.Value.AsLinearColor(); FLinearColor C = Meta.Value.AsLinearColor();
UWingServer::Printf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"), WingOut::Stdout.Printf(TEXT(" %sVector \"%s\" = (R=%.3f, G=%.3f, B=%.3f, A=%.3f)\n"),
*Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A); *Prefix, *Info.Name.ToString(), C.R, C.G, C.B, C.A);
break; break;
} }
case EMaterialParameterType::DoubleVector: case EMaterialParameterType::DoubleVector:
{ {
FVector4d V = Meta.Value.AsVector4d(); FVector4d V = Meta.Value.AsVector4d();
UWingServer::Printf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"), WingOut::Stdout.Printf(TEXT(" %sDoubleVector \"%s\" = (%.3f, %.3f, %.3f, %.3f)\n"),
*Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W); *Prefix, *Info.Name.ToString(), V.X, V.Y, V.Z, V.W);
break; break;
} }
case EMaterialParameterType::Texture: case EMaterialParameterType::Texture:
{ {
UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject()); UTexture* Tex = Cast<UTexture>(Meta.Value.AsTextureObject());
UWingServer::Printf(TEXT(" %sTexture \"%s\" = %s\n"), WingOut::Stdout.Printf(TEXT(" %sTexture \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Tex ? *WingUtils::FormatName(Tex) : TEXT("None")); *Prefix, *Info.Name.ToString(), Tex ? *WingUtils::FormatName(Tex) : TEXT("None"));
break; break;
} }
case EMaterialParameterType::StaticSwitch: case EMaterialParameterType::StaticSwitch:
UWingServer::Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"), WingOut::Stdout.Printf(TEXT(" %sStaticSwitch \"%s\" = %s\n"),
*Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false")); *Prefix, *Info.Name.ToString(), Meta.Value.AsStaticSwitch() ? TEXT("true") : TEXT("false"));
break; break;
default: default:
UWingServer::Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString()); WingOut::Stdout.Printf(TEXT(" %sType%d \"%s\"\n"), *Prefix, (int)Meta.Value.Type, *Info.Name.ToString());
break; break;
} }
} }

View File

@@ -186,19 +186,19 @@ TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(const UStruct* Scri
return TryNamedProperty(GetRootForStruct(ScriptStruct, Data), Name, RootFilter); return TryNamedProperty(GetRootForStruct(ScriptStruct, Data), Name, RootFilter);
} }
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name, bool RootFilter) TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(UObject* Obj, FName Name, bool RootFilter, WingOut Errors)
{ {
TSharedPtr<IPropertyHandle> Result = TryNamedProperty(Obj, Name, RootFilter); TSharedPtr<IPropertyHandle> Result = TryNamedProperty(Obj, Name, RootFilter);
if (!Result) if (!Result)
UWingServer::Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString()); Errors.Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString());
return Result; return Result;
} }
TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter) TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter, WingOut Errors)
{ {
TSharedPtr<IPropertyHandle> Result = TryNamedProperty(ScriptStruct, Data, Name, RootFilter); TSharedPtr<IPropertyHandle> Result = TryNamedProperty(ScriptStruct, Data, Name, RootFilter);
if (!Result) if (!Result)
UWingServer::Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString()); Errors.Printf(TEXT("ERROR: Property '%s' not found\n"), *Name.ToString());
return Result; return Result;
} }
@@ -208,7 +208,7 @@ TSharedPtr<IPropertyHandle> WingPropHandle::NamedProperty(const UStruct* ScriptS
// //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable) WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable, WingOut Errors)
{ {
bool RootFilter = false; bool RootFilter = false;
@@ -219,7 +219,7 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
{ {
if (!BP->GeneratedClass) if (!BP->GeneratedClass)
{ {
UWingServer::Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName()); Errors.Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
return {}; return {};
} }
Obj = BP->GeneratedClass->GetDefaultObject(); Obj = BP->GeneratedClass->GetDefaultObject();
@@ -235,7 +235,7 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate(); Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
if (!Obj) if (!Obj)
{ {
UWingServer::Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString()); Errors.Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString());
return {}; return {};
} }
} }
@@ -277,7 +277,7 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
// //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr<IPropertyHandle>> &Result) bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr<IPropertyHandle>> &Result, WingOut Errors)
{ {
Result.Empty(); Result.Empty();
TSet<FName> DuplicateNames; TSet<FName> DuplicateNames;
@@ -288,12 +288,12 @@ bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr
else Result.Add(Name, H); else Result.Add(Name, H);
} }
if (DuplicateNames.IsEmpty()) return true; if (DuplicateNames.IsEmpty()) return true;
UWingServer::Print(TEXT("More than one property with name:")); Errors.Print(TEXT("More than one property with name:"));
for (FName DupName : DuplicateNames) for (FName DupName : DuplicateNames)
{ {
UWingServer::Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName)); Errors.Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName));
} }
UWingServer::Print(TEXT("\n")); Errors.Print(TEXT("\n"));
return false; return false;
} }
@@ -339,7 +339,7 @@ FString WingPropHandle::GetText(IPropertyHandle& Handle)
return Result; return Result;
} }
bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text) bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text, WingOut Errors)
{ {
FProperty* Prop = Handle.GetProperty(); FProperty* Prop = Handle.GetProperty();
@@ -351,11 +351,11 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
Req.BlueprintType = true; Req.BlueprintType = true;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = true; Req.AllowContainer = true;
if (!UWingTypes::TextToType(Text, PinType, Req)) return false; if (!UWingTypes::TextToType(Text, PinType, Req, Errors)) return false;
void* Data = nullptr; void* Data = nullptr;
if (Handle.GetValueData(Data) != FPropertyAccess::Success || !Data) if (Handle.GetValueData(Data) != FPropertyAccess::Success || !Data)
{ {
UWingServer::Printf(TEXT("ERROR: Cannot access data for property '%s'\n"), Errors.Printf(TEXT("ERROR: Cannot access data for property '%s'\n"),
*WingUtils::FormatName(Prop)); *WingUtils::FormatName(Prop));
return false; return false;
} }
@@ -375,12 +375,12 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
Req.BlueprintType = true; Req.BlueprintType = true;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.IsChildOf = ClassProp->MetaClass; Req.IsChildOf = ClassProp->MetaClass;
Class = UWingTypes::TextToOneObjectType(Text, Req); Class = UWingTypes::TextToOneObjectType(Text, Req, Errors);
if (!Class) return false; if (!Class) return false;
} }
if (Handle.SetValue(Class) != FPropertyAccess::Success) if (Handle.SetValue(Class) != FPropertyAccess::Success)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to set class property '%s'\n"), Errors.Printf(TEXT("ERROR: Failed to set class property '%s'\n"),
*WingUtils::FormatName(Prop)); *WingUtils::FormatName(Prop));
return false; return false;
} }
@@ -397,14 +397,14 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
if (Enum != nullptr) if (Enum != nullptr)
{ {
int64 EnumValue; int64 EnumValue;
if (!WingUtils::StringToEnum(Enum, Value, EnumValue)) return false; if (!WingUtils::StringToEnum(Enum, Value, EnumValue, Errors)) return false;
Value = Enum->GetNameStringByValue(EnumValue); Value = Enum->GetNameStringByValue(EnumValue);
} }
FPropertyAccess::Result Result = Handle.SetValueFromFormattedString(Value); FPropertyAccess::Result Result = Handle.SetValueFromFormattedString(Value);
if (Result != FPropertyAccess::Success) if (Result != FPropertyAccess::Success)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"), Errors.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());
return false; return false;
} }
@@ -417,14 +417,14 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
// //
///////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////
bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue) bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue, WingOut Errors)
{ {
FPropertyAccess::Result Result; FPropertyAccess::Result Result;
switch (JsonValue->Type) switch (JsonValue->Type)
{ {
case EJson::String: case EJson::String:
return SetText(Handle, JsonValue->AsString()); return SetText(Handle, JsonValue->AsString(), Errors);
case EJson::Boolean: case EJson::Boolean:
Result = Handle.SetValue(JsonValue->AsBool()); Result = Handle.SetValue(JsonValue->AsBool());
@@ -441,34 +441,34 @@ bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValu
if (Result != FPropertyAccess::Success) if (Result != FPropertyAccess::Success)
{ {
UWingServer::Printf(TEXT("ERROR: Failed to set property '%s'\n"), Errors.Printf(TEXT("ERROR: Failed to set property '%s'\n"),
*WingUtils::FormatName(Handle.GetProperty())); *WingUtils::FormatName(Handle.GetProperty()));
return false; return false;
} }
return true; return true;
} }
bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional) bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors)
{ {
bool Ok = true; bool Ok = true;
// Organize the properties by name. // Organize the properties by name.
TMap<FName, TSharedPtr<IPropertyHandle>> OrganizedProps; TMap<FName, TSharedPtr<IPropertyHandle>> OrganizedProps;
if (!OrganizeByName(Props, OrganizedProps)) Ok = false; if (!OrganizeByName(Props, OrganizedProps, Errors)) Ok = false;
// Parse the keys in the json, make sure they're syntactically valid and // Parse the keys in the json, make sure they're syntactically valid and
// that they match the names of actual properties, and that there are no dups. // that they match the names of actual properties, and that there are no dups.
TSet<FName> Specified; TSet<FName> Specified;
for (const auto& KV : Json.Values) for (const auto& KV : Json.Values)
{ {
FName Name = WingUtils::CheckInternalizeID(KV.Key); FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
if (Name.IsNone()) { Ok = false; continue; } if (Name.IsNone()) { Ok = false; continue; }
if (!OrganizedProps.Contains(Name)) if (!OrganizedProps.Contains(Name))
{ {
UWingServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key); Errors.Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
Ok = false; Ok = false;
} }
if (!WingUtils::FindNoDuplicateName(Specified, Name, TEXT("parameter"))) Ok = false; if (!WingUtils::FindNoDuplicateName(Specified, Name, TEXT("parameter"), Errors)) Ok = false;
} }
// Make sure that all required properties have been specified. // Make sure that all required properties have been specified.
@@ -480,7 +480,7 @@ bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props
FName Name = H->GetProperty()->GetFName(); FName Name = H->GetProperty()->GetFName();
if (!Specified.Contains(Name)) if (!Specified.Contains(Name))
{ {
UWingServer::Printf(TEXT("Required parameter %s not specified\n"), Errors.Printf(TEXT("Required parameter %s not specified\n"),
*WingUtils::ExternalizeID(Name)); *WingUtils::ExternalizeID(Name));
Ok = false; Ok = false;
} }
@@ -494,8 +494,8 @@ bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props
// point, we're committed. // point, we're committed.
for (const auto& KV : Json.Values) for (const auto& KV : Json.Values)
{ {
FName Name = WingUtils::CheckInternalizeID(KV.Key); FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
if (!SetJson(*OrganizedProps[Name], KV.Value)) Ok = false; if (!SetJson(*OrganizedProps[Name], KV.Value, Errors)) Ok = false;
} }
return Ok; return Ok;
} }

View File

@@ -14,32 +14,313 @@
#include "Dom/JsonValue.h" #include "Dom/JsonValue.h"
static bool IsPinTypeProperty(FProperty* Prop) bool FWingProperty::SetObject(UObject *Obj, WingOut Errors) const
{ {
FStructProperty* StructProp = CastField<FStructProperty>(Prop); FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop);
return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct(); if (!OProp)
{
PrintExpectsReceived(TEXT("object"), Errors);
return false;
}
if (Obj)
{
if (!Obj->IsA(OProp->PropertyClass))
{
Errors.Printf(TEXT("ERROR: Property '%s' expects %s, but received %s\n"),
*WingUtils::FormatName(Prop), *OProp->PropertyClass->GetName(), *Obj->GetClass()->GetName());
return false;
}
if (FClassProperty *CProp = CastField<FClassProperty>(Prop))
{
UClass *Class = CastChecked<UClass>(Obj);
if (!Class->IsChildOf(CProp->MetaClass))
{
Errors.Printf(TEXT("ERROR: Property '%s' expects a subclass of %s, but received %s\n"),
*WingUtils::FormatName(Prop), *CProp->MetaClass->GetName(), *Class->GetName());
return false;
}
}
}
OProp->SetObjectPropertyValue_InContainer(Container, Obj);
return true;
} }
FWingProperty::FWingProperty(FProperty* InProp, void* InContainer) bool FWingProperty::SetDouble(double D, WingOut Errors) const
: Prop(InProp), Container(InContainer) {}
FWingProperty::FWingProperty(FProperty* InProp, UObject* InContainer)
: Prop(InProp), Container(static_cast<void*>(InContainer)) {}
FString FWingProperty::GetCategory()
{ {
FString Result = Prop->GetMetaData(TEXT("Category")); FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (Result.IsEmpty()) Result = "Unclassified"; if (!NProp)
return Result; {
PrintExpectsReceived(TEXT("double"), Errors);
return false;
}
if (NProp->IsFloatingPoint())
{
uint8 buffer[16];
NProp->SetFloatingPointPropertyValue(buffer, D);
if (!FMath::IsFinite(NProp->GetFloatingPointPropertyValue(buffer)))
{
Errors.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;
}
else
{
uint8 buffer[16];
if (FMath::Floor(D) != D)
{
PrintExpectsReceived(TEXT("double"), Errors);
return false;
}
if (FMath::Abs(D) > (double)((1LL)<<53))
{
Errors.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)
{
Errors.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, WingOut Errors) const
{
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
{
PrintExpectsReceived(TEXT("int"), Errors);
return false;
}
if (NProp->IsFloatingPoint())
{
uint8 buffer[16];
double D = I;
NProp->SetFloatingPointPropertyValue(buffer, D);
int64 RT = (int64)NProp->GetFloatingPointPropertyValue(buffer);
if (RT != I)
{
Errors.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)
{
Errors.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, WingOut Errors) const
{
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
{
Prop->SetValue_InContainer(Container, &B);
return true;
}
PrintExpectsReceived(TEXT("boolean"), Errors);
return false;
}
bool FWingProperty::SetText(FString Value, WingOut Errors) const
{
// Pin types get parsed by UWingTypes.
if (IsPinTypeProperty(Prop))
{
FEdGraphPinType PinType;
UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(Value, PinType, Req, Errors)) return false;
Prop->SetValue_InContainer(Container, &PinType);
return true;
}
// 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))
Enum = ByteProp->Enum;
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
Enum = EnumProp->GetEnum();
if (Enum != nullptr)
{
int64 EnumValue;
if (!WingUtils::StringToEnum(Enum, Value, EnumValue, Errors)) return false;
Value = Enum->GetNameStringByValue(EnumValue);
}
// Now Use ImportText
const TCHAR* Result = Prop->ImportText_InContainer(*Value, Container, nullptr, PPF_None);
if ((!Result) || (*Result != 0))
{
Errors.Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
return false;
}
if (!CheckImportTextResult(Value, Errors)) return false;
return true;
}
bool FWingProperty::SetJson(const FJsonValue &JsonValue, WingOut Errors) const
{
if (JsonValue.Type == EJson::String)
{
return SetText(JsonValue.AsString(), Errors);
}
if (JsonValue.Type == EJson::Number)
{
return SetDouble(JsonValue.AsNumber(), Errors);
}
if (JsonValue.Type == EJson::Boolean)
{
return SetBool(JsonValue.AsBool(), Errors);
}
if (JsonValue.Type == EJson::Object)
{
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
if (StructProp && (StructProp->Struct == FWingJsonObject::StaticStruct()))
{
FWingJsonObject Val;
Val.Json = JsonValue.AsObject();
Prop->SetValue_InContainer(Container, &Val);
return true;
}
PrintExpectsReceived(TEXT("json object"), Errors);
return false;
}
if (JsonValue.Type == EJson::Array)
{
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
if (StructProp && (StructProp->Struct == FWingJsonArray::StaticStruct()))
{
FWingJsonArray Val;
Val.Array = JsonValue.AsArray();
Prop->SetValue_InContainer(Container, &Val);
return true;
}
PrintExpectsReceived(TEXT("json array"), Errors);
return false;
}
PrintExpectsReceived(TEXT("Unrecognized Json Data"), Errors);
return false;
}
TOptional<UObject*> FWingProperty::GetObject(WingOut Errors) const
{
FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop);
if (!OProp)
{
PrintExpectsReceived(TEXT("object"), Errors);
return {};
}
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
return OProp->GetObjectPropertyValue(VP);
}
TOptional<double> FWingProperty::GetDouble(WingOut Errors) const
{
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
{
PrintExpectsReceived(TEXT("double"), Errors);
return {};
}
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
if (NProp->IsFloatingPoint())
{
return NProp->GetFloatingPointPropertyValue(VP);
}
else
{
int64 I = NProp->GetSignedIntPropertyValue(VP);
double D = (double)I;
if ((int64)D != I)
{
Errors.Printf(TEXT("ERROR: Property '%s' value %lld cannot be represented losslessly as double\n"),
*WingUtils::FormatName(Prop), I);
return {};
}
return D;
}
}
TOptional<int64> FWingProperty::GetInt64(WingOut Errors) const
{
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
{
PrintExpectsReceived(TEXT("int"), Errors);
return {};
}
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
if (NProp->IsFloatingPoint())
{
double D = NProp->GetFloatingPointPropertyValue(VP);
if (FMath::Floor(D) != D)
{
Errors.Printf(TEXT("ERROR: Property '%s' value %lf is not an integer\n"),
*WingUtils::FormatName(Prop), D);
return {};
}
if (FMath::Abs(D) > (double)((1LL)<<53))
{
Errors.Printf(TEXT("ERROR: Property '%s' value %lf is too large to convert to int64 losslessly\n"),
*WingUtils::FormatName(Prop), D);
return {};
}
return (int64)D;
}
else
{
return NProp->GetSignedIntPropertyValue(VP);
}
}
TOptional<bool> FWingProperty::GetBool(WingOut Errors) const
{
if (FBoolProperty* BoolProp = CastField<FBoolProperty>(Prop))
{
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
return BoolProp->GetPropertyValue(VP);
}
PrintExpectsReceived(TEXT("boolean"), Errors);
return {};
} }
FString FWingProperty::GetText() const FString FWingProperty::GetText() const
{ {
if (IsPinTypeProperty(Prop)) if (IsPinTypeProperty(Prop))
{ {
FEdGraphPinType PinType; FEdGraphPinType *PinType = Prop->ContainerPtrToValuePtr<FEdGraphPinType>(Container);
Prop->GetValue_InContainer(Container, &PinType); return UWingTypes::TypeToText(*PinType);
return UWingTypes::TypeToText(PinType);
} }
FString Result; FString Result;
Prop->ExportTextItem_InContainer(Result, Container, nullptr, nullptr, PPF_None); Prop->ExportTextItem_InContainer(Result, Container, nullptr, nullptr, PPF_None);
@@ -56,269 +337,35 @@ FString FWingProperty::GetTruncatedText(int32 MaxLen) const
return Result; return Result;
} }
void FWingProperty::PrintExpectsReceived(const TCHAR *Type)
FString FWingProperty::GetCategory() const
{ {
UWingServer::Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"), FString Result = Prop->GetMetaData(TEXT("Category"));
*WingUtils::FormatName(Prop), Type, *Prop->GetCPPType()); if (Result.IsEmpty()) Result = "Unclassified";
return Result;
} }
void FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props)
bool FWingProperty::CheckImportTextResult(const FString &Value)
{ {
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container); TArray<FWingProperty> Result;
if (FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop)) for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
{
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)
{
// Pin types get parsed by UWingTypes.
if (IsPinTypeProperty(Prop))
{
FEdGraphPinType PinType;
UWingTypes::Requirements Req;
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(Value, PinType, Req)) return false;
Prop->SetValue_InContainer(Container, &PinType);
return true;
}
// 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))
Enum = ByteProp->Enum;
if (FEnumProperty* EnumProp = CastField<FEnumProperty>(Prop))
Enum = EnumProp->GetEnum();
if (Enum != nullptr)
{
int64 EnumValue;
if (!WingUtils::StringToEnum(Enum, Value, EnumValue)) return false;
Value = Enum->GetNameStringByValue(EnumValue);
}
// Now Use ImportText
const TCHAR* Result = Prop->ImportText_InContainer(*Value, Container, nullptr, PPF_None);
if ((!Result) || (*Result != 0))
{
UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s' (type: %s)\n"),
*Value, *WingUtils::FormatName(Prop), *Prop->GetCPPType());
return false;
}
if (!CheckImportTextResult(Value)) return false;
return true;
}
bool FWingProperty::SetDouble(double D)
{
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
{
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: Property '%s' of type %s cannot hold %lf\n"),
*WingUtils::FormatName(Prop), *Prop->GetCPPType(), D);
return false;
}
Prop->SetValue_InContainer(Container, buffer);
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<FNumericProperty>(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<FBoolProperty>(Prop))
{
Prop->SetValue_InContainer(Container, &B);
return true;
}
PrintExpectsReceived(TEXT("boolean"));
return false;
}
bool FWingProperty::SetJson(const TSharedPtr<FJsonValue> &JsonValue)
{
if (JsonValue->Type == EJson::String)
{
return SetText(JsonValue->AsString());
}
if (JsonValue->Type == EJson::Number)
{
return SetDouble(JsonValue->AsNumber());
}
if (JsonValue->Type == EJson::Boolean)
{
return SetBool(JsonValue->AsBool());
}
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)
{
TMap<FString, TArray<FWingProperty>> Grouped;
for (TFieldIterator<FProperty> It(StructType); It; ++It)
{ {
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue; if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
FString SortCat = *It->GetMetaData(TEXT("Category")); Result.Add(FWingProperty(*It, Obj.StructPtr));
Grouped.FindOrAdd(SortCat).Add(FWingProperty(*It, Container));
}
TArray<FString> Categories;
Grouped.GetKeys(Categories);
Categories.Sort([](const FString& A, const FString& B) {
if (A.IsEmpty()) return false;
if (B.IsEmpty()) return true;
return A < B;
});
for (const FString& Category : Categories)
{
Props.Append(Grouped[Category]);
} }
} }
TArray<FWingProperty> FWingProperty::GetAll(UObject* Object, EPropertyFlags Flags) TArray<FWingProperty> FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags)
{
return GetAll(Object->GetClass(), Object, Flags);
}
TArray<FWingProperty> FWingProperty::GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags)
{ {
TArray<FWingProperty> Result; TArray<FWingProperty> Result;
Collect(StructType, Container, Result, Flags); GetAll(Obj, Flags, Result);
return Result; return Result;
} }
TArray<FWingProperty> FWingProperty::GetNamed(UStruct* StructType, void* Container, const TArray<FName> &Names) TArray<FName> FWingProperty::GetNames(UStruct *US, EPropertyFlags Flags)
{
TArray<FWingProperty> Result;
for (FName Name : Names)
{
FProperty *Prop = StructType->FindPropertyByName(Name);
if (Prop != nullptr) Result.Emplace(Prop, Container);
}
return Result;
}
TArray<FName> FWingProperty::GetNames(UStruct *StructType, EPropertyFlags Flags)
{ {
TArray<FName> Result; TArray<FName> Result;
for (TFieldIterator<FProperty> It(StructType); It; ++It) for (TFieldIterator<FProperty> It(US); It; ++It)
{ {
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue; if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
Result.Add(It->GetFName()); Result.Add(It->GetFName());
@@ -326,29 +373,23 @@ TArray<FName> FWingProperty::GetNames(UStruct *StructType, EPropertyFlags Flags)
return Result; return Result;
} }
void FWingProperty::Remove(TArray<FWingProperty>& Props, const FString& Name) void FWingProperty::Remove(TArray<FWingProperty>& Props, FName Name)
{ {
Props.RemoveAll([&](const FWingProperty& P) { return P.Prop->GetName() == Name; }); Props.RemoveAll([&](const FWingProperty& P) { return P.Prop->GetName() == Name; });
} }
void FWingProperty::Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, const FString &Name) void FWingProperty::Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, FName Name)
{ {
int Dst = 0; int Dst = 0;
for (int i = 0; i < In.Num(); i++) for (int i = 0; i < In.Num(); i++)
{ {
if (In[i]->GetName() == Name) if (In[i]->GetFName() == Name) Out.Add(In[i]);
{ else In[Dst++] = In[i];
Out.Add(In[i]);
}
else
{
In[Dst++] = In[i];
}
} }
In.SetNum(Dst); In.SetNum(Dst);
} }
TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFlags Flags, bool Mutable) TArray<FWingProperty> FWingProperty::GetDetails(UObject* Obj, EPropertyFlags Flags, bool Mutable)
{ {
if (!Obj) return {}; if (!Obj) return {};
@@ -358,7 +399,7 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
{ {
if (BP->GeneratedClass == nullptr) if (BP->GeneratedClass == nullptr)
{ {
UWingServer::Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName()); WingOut::Stdout.Printf(TEXT("ERROR: Blueprint '%s' has no GeneratedClass\n"), *Obj->GetName());
return {}; return {};
} }
Obj = BP->GeneratedClass->GetDefaultObject(); Obj = BP->GeneratedClass->GetDefaultObject();
@@ -370,13 +411,12 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate(); Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
if (!Obj) if (!Obj)
{ {
UWingServer::Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString()); WingOut::Stdout.Printf(TEXT("ERROR: Component '%s' has no template\n"), *Ref->VariableName.ToString());
return {}; return {};
} }
} }
TArray<FWingProperty> Result; TArray<FWingProperty> Result = GetAll(Obj, Flags);
Collect(Obj->GetClass(), Obj, Result, Flags);
// If it's a Material Graph node, also collect properties from // If it's a Material Graph node, also collect properties from
// the associated material expression. // the associated material expression.
@@ -385,7 +425,7 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
{ {
if (UMaterialExpression* Expr = MatNode->MaterialExpression) if (UMaterialExpression* Expr = MatNode->MaterialExpression)
{ {
Collect(Expr->GetClass(), Expr, Result, Flags); GetAll(Expr, Flags, Result);
} }
} }
@@ -396,7 +436,7 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
FWingProperty::Remove(Result, TEXT("Slot")); FWingProperty::Remove(Result, TEXT("Slot"));
if (UPanelSlot* Slot = Widget->Slot) if (UPanelSlot* Slot = Widget->Slot)
{ {
Collect(Slot->GetClass(), Slot, Result, Flags); GetAll(Slot, Flags, Result);
} }
} }
@@ -404,50 +444,29 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
} }
TArray<FWingProperty> FWingProperty::FindAllSubstring(const TArray<FWingProperty>& Props, const FString& Substring) bool FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json, bool AllOptional, WingOut Errors)
{
if (Substring.IsEmpty()) return Props;
TArray<FWingProperty> Result;
for (const FWingProperty& P : Props)
{
if (WingUtils::FormatName(P.Prop).Contains(Substring, ESearchCase::IgnoreCase))
Result.Add(P);
}
return Result;
}
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; bool Ok = true;
// Build a set of known property names for the unknown-field check. // Make sure they passed in a JSON object.
TSet<FString> KnownKeys; TSharedPtr<FJsonObject> Obj = Json.AsObject();
for (const FWingProperty& P : Props) if (Obj == nullptr)
KnownKeys.Add(WingUtils::FormatName(P.Prop));
// Check for unknown fields in the JSON
for (const auto& KV : Json->Values)
{ {
if (!KnownKeys.Contains(KV.Key)) 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.
TSet<FName> KnownKeys;
for (const FWingProperty& P : Props) KnownKeys.Add(P->GetFName());
// Check for unknown fields in the JSON
for (const auto& KV : Obj->Values)
{
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
if (!KnownKeys.Contains(Name))
{ {
UWingServer::Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key); Errors.Printf(TEXT("ERROR: Unknown parameter '%s'\n"), *KV.Key);
Ok = false; Ok = false;
} }
} }
@@ -455,23 +474,51 @@ bool FWingProperty::PopulateFromJson(
// Populate each property from JSON // Populate each property from JSON
for (FWingProperty& P : Props) for (FWingProperty& P : Props)
{ {
if (!PopulateFromJson(P, Json, AllOptional)) Ok = false; FString JsonKey = WingUtils::FormatName(P.Prop);
TSharedPtr<FJsonValue> Value = Obj->TryGetField(JsonKey);
if (!Value)
{
bool Optional = AllOptional || P.Prop->HasMetaData(TEXT("Optional"));
if (!Optional)
{
Errors.Printf(TEXT("ERROR: Missing required parameter '%s'\n"), *JsonKey);
Ok = false;
}
continue;
}
if (!P.SetJson(*Value, Errors)) Ok = false;
} }
return Ok; return Ok;
} }
bool FWingProperty::PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object)
bool FWingProperty::IsPinTypeProperty(FProperty* Prop)
{ {
TArray<FWingProperty> Props = FWingProperty::GetAll(StructType, Container, (EPropertyFlags)0); FStructProperty* StructProp = CastField<FStructProperty>(Prop);
return PopulateFromJson(Props, Object); return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct();
} }
bool FWingProperty::PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object)
void FWingProperty::PrintExpectsReceived(const TCHAR *Type, WingOut Errors) const
{ {
if (!Object.IsValid() || (Object->Type != EJson::Object)) Errors.Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"),
{ *WingUtils::FormatName(Prop), Type, *Prop->GetCPPType());
UWingServer::Print(TEXT("ERROR: Expected a JSON object\n"));
return false;
}
return PopulateFromJson(StructType, Container, Object->AsObject().Get());
} }
bool FWingProperty::CheckImportTextResult(const FString &Value, WingOut Errors) const
{
if (FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop))
{
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
UObject *Obj = OProp->GetObjectPropertyValue(VP);
if (Obj == nullptr && Value.TrimStartAndEnd().Compare(TEXT("None"), ESearchCase::IgnoreCase) != 0)
{
Errors.Printf(TEXT("ERROR: Failed to parse '%s' for property '%s'\n"),
*Value, *WingUtils::FormatName(Prop));
return false;
}
}
return true;
}

View File

@@ -263,7 +263,7 @@ FString UWingServer::HandleRequest(const FString& Line)
{ {
LogCapture.CapturedErrors.Empty(); LogCapture.CapturedErrors.Empty();
LogCapture.bEnabled = true; LogCapture.bEnabled = true;
HandlerOutput.Reset(); WingOut::StdoutBuffer.Reset();
SuggestedManualSections.Empty(); SuggestedManualSections.Empty();
LastHandler = nullptr; LastHandler = nullptr;
@@ -273,7 +273,7 @@ FString UWingServer::HandleRequest(const FString& Line)
LogCapture.bEnabled = false; LogCapture.bEnabled = false;
for (const FString& Msg : LogCapture.CapturedErrors) for (const FString& Msg : LogCapture.CapturedErrors)
{ {
UWingServer::Printf(TEXT("UE_LOG: %s\n"), *Msg); WingOut::Stdout.Printf(TEXT("UE_LOG: %s\n"), *Msg);
} }
LogCapture.CapturedErrors.Empty(); LogCapture.CapturedErrors.Empty();
if (!SuggestedManualSections.IsEmpty()) if (!SuggestedManualSections.IsEmpty())
@@ -281,8 +281,8 @@ FString UWingServer::HandleRequest(const FString& Line)
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
WingManual::PrintManual(SuggestedManualSections, LastHandler, true); WingManual::PrintManual(SuggestedManualSections, LastHandler, true);
} }
FString Result = HandlerOutput.ToString(); FString Result = WingOut::StdoutBuffer.ToString();
HandlerOutput.Reset(); WingOut::StdoutBuffer.Reset();
for (int32 i = 0; i < Result.Len(); ++i) for (int32 i = 0; i < Result.Len(); ++i)
{ {
if (Result[i] == TEXT('\0')) Result[i] = TEXT(' '); if (Result[i] == TEXT('\0')) Result[i] = TEXT(' ');
@@ -298,7 +298,7 @@ void UWingServer::TryCallHandler(const FString &Line)
FJsonSerializer::Deserialize(Reader, Request); FJsonSerializer::Deserialize(Reader, Request);
if (!Request.IsValid()) if (!Request.IsValid())
{ {
UWingServer::Printf(TEXT("Request is not valid JSON")); WingOut::Stdout.Printf(TEXT("Request is not valid JSON"));
return; return;
} }
@@ -306,8 +306,8 @@ void UWingServer::TryCallHandler(const FString &Line)
FString Command; FString Command;
if (!Request->TryGetStringField(TEXT("command"), Command)) if (!Request->TryGetStringField(TEXT("command"), Command))
{ {
UWingServer::Printf(TEXT("Request does not contain 'command' parameter")); WingOut::Stdout.Printf(TEXT("Request does not contain 'command' parameter"));
UWingServer::Printf(TEXT("We recommend sending command='UserManual'.")); WingOut::Stdout.Printf(TEXT("We recommend sending command='UserManual'."));
return; return;
} }
Request->RemoveField(TEXT("command")); Request->RemoveField(TEXT("command"));
@@ -316,7 +316,7 @@ void UWingServer::TryCallHandler(const FString &Line)
FWingHandlerConfig* Found = FindHandler(Command); FWingHandlerConfig* Found = FindHandler(Command);
if (!Found) if (!Found)
{ {
UWingServer::Printf(TEXT("Unknown command: %s"), *Command); WingOut::Stdout.Printf(TEXT("Unknown command: %s"), *Command);
UWingServer::SuggestManual(WingManual::Section::ImportantCommands); UWingServer::SuggestManual(WingManual::Section::ImportantCommands);
return; return;
} }
@@ -330,7 +330,7 @@ void UWingServer::TryCallHandler(const FString &Line)
// Populate the handler object with the request parameters. // Populate the handler object with the request parameters.
WingPropHandle Props; WingPropHandle Props;
WingPropHandle::Handles Handles = Props.AllProperties(HandlerObj.Get(), true); WingPropHandle::Handles Handles = Props.AllProperties(HandlerObj.Get(), true);
if (!WingPropHandle::PopulateFromJson(Handles, *Request, false)) if (!WingPropHandle::PopulateFromJson(Handles, *Request, false, WingOut::Stdout))
{ {
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
return; return;
@@ -490,3 +490,7 @@ FWingHandlerConfig* UWingServer::FindHandler(const FString& Name)
} }
return nullptr; return nullptr;
} }
TStringBuilder<65536> WingOut::StdoutBuffer;
WingOut WingOut::Stdout(&WingOut::StdoutBuffer);
WingOut WingOut::None(nullptr);

View File

@@ -433,124 +433,124 @@ static const FName NAME_TypeSoftClass(TEXT("SoftClass"));
static const FName NAME_StartOfType("Start-of-Type"); static const FName NAME_StartOfType("Start-of-Type");
void UWingTypes::PrintParseError(WingTokenizer& Tok, const TCHAR* Message) void UWingTypes::PrintParseError(WingTokenizer& Tok, const TCHAR* Message, WingOut Errors)
{ {
FString TypeText(Tok.GetRange(NAME_StartOfType, 1)); FString TypeText(Tok.GetRange(NAME_StartOfType, 1));
UWingServer::Printf(TEXT("ERROR parsing type '%s' — %s\n"), *TypeText, Message); Errors.Printf(TEXT("ERROR parsing type '%s' — %s\n"), *TypeText, Message);
UWingServer::SuggestManual(WingManual::Section::Types); UWingServer::SuggestManual(WingManual::Section::Types);
} }
bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c) bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c, WingOut Errors)
{ {
if (!Tok.TokenIs(c)) if (!Tok.TokenIs(c))
{ {
PrintParseError(Tok, *FString::Printf(TEXT("expected '%c'"), c)); PrintParseError(Tok, *FString::Printf(TEXT("expected '%c'"), c), Errors);
return false; return false;
} }
Tok.Advance(); Tok.Advance();
return true; return true;
} }
bool UWingTypes::ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType) bool UWingTypes::ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{ {
if (!Tok.TokenIs(Tok.Identifier)) if (!Tok.TokenIs(Tok.Identifier))
{ {
PrintParseError(Tok, TEXT("expected identifier")); PrintParseError(Tok, TEXT("expected identifier"), Errors);
return false; return false;
} }
FString Name = Tok.NextName().ToString(); FString Name = Tok.NextName().ToString();
Tok.Advance(); Tok.Advance();
return ResolveShortName(Tok, Name, OutType); return ResolveShortName(Tok, Name, OutType, Errors);
} }
bool UWingTypes::ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType) bool UWingTypes::ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType, WingOut Errors)
{ {
Tok.Advance(); Tok.Advance();
if (!ParseChar(Tok, '<')) return false; if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParsePlainIdentifier(Tok, OutType)) return false; if (!ParsePlainIdentifier(Tok, OutType, Errors)) return false;
if (OutType.PinCategory != UEdGraphSchema_K2::PC_Object) if (OutType.PinCategory != UEdGraphSchema_K2::PC_Object)
{ {
PrintParseError(Tok, *FString::Printf(TEXT("'%s' is not an object type"), *OutType.PinSubCategoryObject->GetName())); PrintParseError(Tok, *FString::Printf(TEXT("'%s' is not an object type"), *OutType.PinSubCategoryObject->GetName()), Errors);
return false; return false;
} }
if (!ParseChar(Tok, '>')) return false; if (!ParseChar(Tok, '>', Errors)) return false;
OutType.PinCategory = Wrapper; OutType.PinCategory = Wrapper;
return true; return true;
} }
bool UWingTypes::ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType) bool UWingTypes::ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{ {
if (Tok.TokenIs(NAME_TypeSoft, '<')) if (Tok.TokenIs(NAME_TypeSoft, '<'))
{ {
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftObject, OutType); return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftObject, OutType, Errors);
} }
else if (Tok.TokenIs(NAME_TypeClass, '<')) else if (Tok.TokenIs(NAME_TypeClass, '<'))
{ {
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_Class, OutType); return ParseWrapped(Tok, UEdGraphSchema_K2::PC_Class, OutType, Errors);
} }
else if (Tok.TokenIs(NAME_TypeSoftClass, '<')) else if (Tok.TokenIs(NAME_TypeSoftClass, '<'))
{ {
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftClass, OutType); return ParseWrapped(Tok, UEdGraphSchema_K2::PC_SoftClass, OutType, Errors);
} }
else return ParsePlainIdentifier(Tok, OutType); else return ParsePlainIdentifier(Tok, OutType, Errors);
} }
bool UWingTypes::ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType) bool UWingTypes::ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{ {
Tok.Advance(); Tok.Advance();
if (!ParseChar(Tok, '<')) return false; if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParseMaybeWrapped(Tok, OutType)) return false; if (!ParseMaybeWrapped(Tok, OutType, Errors)) return false;
if (!ParseChar(Tok, '>')) return false; if (!ParseChar(Tok, '>', Errors)) return false;
return true; return true;
} }
bool UWingTypes::ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType) bool UWingTypes::ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{ {
Tok.Advance(); Tok.Advance();
if (!ParseChar(Tok, '<')) return false; if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParsePlainIdentifier(Tok, OutType)) return false; if (!ParsePlainIdentifier(Tok, OutType, Errors)) return false;
if (!ParseChar(Tok, ',')) return false; if (!ParseChar(Tok, ',', Errors)) return false;
FEdGraphPinType ValueType; FEdGraphPinType ValueType;
if (!ParseMaybeWrapped(Tok, ValueType)) return false; if (!ParseMaybeWrapped(Tok, ValueType, Errors)) return false;
OutType.PinValueType.TerminalCategory = ValueType.PinCategory; OutType.PinValueType.TerminalCategory = ValueType.PinCategory;
OutType.PinValueType.TerminalSubCategory = ValueType.PinSubCategory; OutType.PinValueType.TerminalSubCategory = ValueType.PinSubCategory;
OutType.PinValueType.TerminalSubCategoryObject = ValueType.PinSubCategoryObject; OutType.PinValueType.TerminalSubCategoryObject = ValueType.PinSubCategoryObject;
if (!ParseChar(Tok, '>')) return false; if (!ParseChar(Tok, '>', Errors)) return false;
return true; return true;
} }
bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType) bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{ {
if (Tok.TokenIs(NAME_TypeArray, '<')) if (Tok.TokenIs(NAME_TypeArray, '<'))
{ {
OutType.ContainerType = EPinContainerType::Array; OutType.ContainerType = EPinContainerType::Array;
if (!ParseArrayOrSet(Tok, OutType)) return false; if (!ParseArrayOrSet(Tok, OutType, Errors)) return false;
} }
else if (Tok.TokenIs(NAME_TypeSet, '<')) else if (Tok.TokenIs(NAME_TypeSet, '<'))
{ {
OutType.ContainerType = EPinContainerType::Set; OutType.ContainerType = EPinContainerType::Set;
if (!ParseArrayOrSet(Tok, OutType)) return false; if (!ParseArrayOrSet(Tok, OutType, Errors)) return false;
} }
else if (Tok.TokenIs(NAME_TypeMap, '<')) else if (Tok.TokenIs(NAME_TypeMap, '<'))
{ {
OutType.ContainerType = EPinContainerType::Map; OutType.ContainerType = EPinContainerType::Map;
if (!ParseMap(Tok, OutType)) return false; if (!ParseMap(Tok, OutType, Errors)) return false;
} }
else else
{ {
if (!ParseMaybeWrapped(Tok, OutType)) return false; if (!ParseMaybeWrapped(Tok, OutType, Errors)) return false;
} }
return true; return true;
} }
bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType) bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType, WingOut Errors)
{ {
Info* TypeInfo = ShortToInfo.Find(Name.ToLower()); Info* TypeInfo = ShortToInfo.Find(Name.ToLower());
if (!TypeInfo) if (!TypeInfo)
{ {
PrintParseError(Tok, *FString::Printf(TEXT("unrecognized type '%s'"), *Name)); PrintParseError(Tok, *FString::Printf(TEXT("unrecognized type '%s'"), *Name), Errors);
return false; return false;
} }
@@ -566,7 +566,7 @@ bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGr
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject); UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject);
if (!Obj) if (!Obj)
{ {
PrintParseError(Tok, *FString::Printf(TEXT("failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject)); PrintParseError(Tok, *FString::Printf(TEXT("failed to load type '%s' at path '%s'"), *Name, *TypeInfo->PinSubCategoryObject), Errors);
return false; return false;
} }
@@ -605,7 +605,7 @@ bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGr
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF) bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF, WingOut Errors)
{ {
UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>(); UWingTypes* Types = GEditor->GetEditorSubsystem<UWingTypes>();
check(Types); check(Types);
@@ -615,19 +615,19 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
!Require.Blueprintable.IsSet() || !Require.Blueprintable.IsSet() ||
!Require.AllowContainer.IsSet()) !Require.AllowContainer.IsSet())
{ {
UWingServer::Printf(TEXT("ERROR: TextToType called with underspecified Requirements list.\n")); Errors.Printf(TEXT("ERROR: TextToType called with underspecified Requirements list.\n"));
return false; return false;
} }
OutPinType = FEdGraphPinType(); OutPinType = FEdGraphPinType();
if (!Types->ParseType(Tok, OutPinType)) if (!Types->ParseType(Tok, OutPinType, Errors))
{ {
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
if (CheckEOF && (Tok.NextType() != 0)) if (CheckEOF && (Tok.NextType() != 0))
{ {
PrintParseError(Tok, TEXT("Extra tokens at end of Type name")); PrintParseError(Tok, TEXT("Extra tokens at end of Type name"), Errors);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
@@ -636,7 +636,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (OutPinType.IsContainer()) if (OutPinType.IsContainer())
{ {
FString Text(Tok.GetRange(NAME_StartOfType, 0)); FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Type '%s' is a container, not allowed here\n"), *Text); Errors.Printf(TEXT("ERROR: Type '%s' is a container, not allowed here\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
@@ -646,7 +646,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
{ {
if (OutPinType.PinCategory != Require.PinCategory) if (OutPinType.PinCategory != Require.PinCategory)
{ {
UWingServer::Printf(TEXT("ERROR: Need a type which is an %s, got a %s instead.\n"), Errors.Printf(TEXT("ERROR: Need a type which is an %s, got a %s instead.\n"),
*Require.PinCategory.ToString(), *OutPinType.PinCategory.ToString()); *Require.PinCategory.ToString(), *OutPinType.PinCategory.ToString());
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
@@ -657,7 +657,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
{ {
if (!IsChildOf(OutPinType, Require.IsChildOf)) if (!IsChildOf(OutPinType, Require.IsChildOf))
{ {
UWingServer::Printf(TEXT("ERROR: Type must derive from %s\n"), *WingUtils::FormatName(Require.IsChildOf)); Errors.Printf(TEXT("ERROR: Type must derive from %s\n"), *WingUtils::FormatName(Require.IsChildOf));
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
@@ -668,7 +668,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (!IsBlueprintType(OutPinType)) if (!IsBlueprintType(OutPinType))
{ {
FString Text(Tok.GetRange(NAME_StartOfType, 0)); FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text); Errors.Printf(TEXT("ERROR: Not a blueprint type: %s\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
@@ -679,7 +679,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (!IsBlueprintable(OutPinType)) if (!IsBlueprintable(OutPinType))
{ {
FString Text(Tok.GetRange(NAME_StartOfType, 0)); FString Text(Tok.GetRange(NAME_StartOfType, 0));
UWingServer::Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text); Errors.Printf(TEXT("ERROR: Not a blueprintable type: %s\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::HandlerHelp); UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false; OutPinType = FEdGraphPinType(); return false;
} }
@@ -688,36 +688,36 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
return true; return true;
} }
bool UWingTypes::TextToType(const FString &Text, FEdGraphPinType& OutPinType, const Requirements &Require) bool UWingTypes::TextToType(const FString &Text, FEdGraphPinType& OutPinType, const Requirements &Require, WingOut Errors)
{ {
WingTokenizer Tok(Text); WingTokenizer Tok(Text);
return TextToType(Tok, OutPinType, Require, true); return TextToType(Tok, OutPinType, Require, true, Errors);
} }
UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require) UClass* UWingTypes::TextToOneObjectType(const FString& Text, const Requirements &Require, WingOut Errors)
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!TextToType(Text, PinType, Require)) return nullptr; if (!TextToType(Text, PinType, Require, Errors)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get()); UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) || if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) ||
(PinType.IsContainer())) (PinType.IsContainer()))
{ {
UWingServer::Printf(TEXT("ERROR: '%s' is not an object class\n"), *Text); Errors.Printf(TEXT("ERROR: '%s' is not an object class\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::Types); UWingServer::SuggestManual(WingManual::Section::Types);
return nullptr; return nullptr;
} }
return Class; return Class;
} }
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require) UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require, WingOut Errors)
{ {
FEdGraphPinType PinType; FEdGraphPinType PinType;
if (!TextToType(Text, PinType, Require)) return nullptr; if (!TextToType(Text, PinType, Require, Errors)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get()); UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) || if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) ||
(PinType.IsContainer())) (PinType.IsContainer()))
{ {
UWingServer::Printf(TEXT("ERROR: '%s' is not an interface class\n"), *Text); Errors.Printf(TEXT("ERROR: '%s' is not an interface class\n"), *Text);
UWingServer::SuggestManual(WingManual::Section::Types); UWingServer::SuggestManual(WingManual::Section::Types);
return nullptr; return nullptr;
} }

View File

@@ -61,24 +61,24 @@ FString WingUtils::ExternalizeID(FName Name)
return WingTokenizer::ExternalizeID(Name); return WingTokenizer::ExternalizeID(Name);
} }
FName WingUtils::CheckInternalizeID(const FString &ExternalID) FName WingUtils::CheckInternalizeID(const FString &ExternalID, WingOut Errors)
{ {
FString Error; FString Error;
FName InternalID = WingTokenizer::TryInternalizeID(ExternalID, Error); FName InternalID = WingTokenizer::TryInternalizeID(ExternalID, Error);
if (!Error.IsEmpty()) if (!Error.IsEmpty())
{ {
UWingServer::Printf(TEXT("%s\n"), *Error); Errors.Printf(TEXT("%s\n"), *Error);
UWingServer::SuggestManual(WingManual::Section::EscapeSequences); UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
} }
return InternalID; return InternalID;
} }
FName WingUtils::CheckProposedName(const FString &ExternalID) FName WingUtils::CheckProposedName(const FString &ExternalID, WingOut Errors)
{ {
FName InternalID = CheckInternalizeID(ExternalID); FName InternalID = CheckInternalizeID(ExternalID, Errors);
if (!InternalID.IsNone() && !WingTokenizer::WouldExternalizeReadably(InternalID)) if (!InternalID.IsNone() && !WingTokenizer::WouldExternalizeReadably(InternalID))
{ {
UWingServer::Printf(TEXT("ERROR: id %s would not be a readable id, may not create item with this name"), Errors.Printf(TEXT("ERROR: id %s would not be a readable id, may not create item with this name"),
*ExternalID); *ExternalID);
UWingServer::SuggestManual(WingManual::Section::EscapeSequences); UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
return FName(); return FName();
@@ -341,7 +341,7 @@ FString WingUtils::EnumToString(UEnum* Enum, int64 Value)
return Enum->GetNameStringByValue(Value); return Enum->GetNameStringByValue(Value);
} }
bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue) bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, WingOut Errors)
{ {
int32 Index = Enum->GetIndexByNameString(Str); int32 Index = Enum->GetIndexByNameString(Str);
if (Index == INDEX_NONE) if (Index == INDEX_NONE)
@@ -352,7 +352,7 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue)
} }
if (Index == INDEX_NONE) if (Index == INDEX_NONE)
{ {
UWingServer::Printf(TEXT("ERROR: '%s' is not a valid value for %s\n"), *Str, *Enum->GetName()); Errors.Printf(TEXT("ERROR: '%s' is not a valid value for %s\n"), *Str, *Enum->GetName());
OutValue = 0; OutValue = 0;
return false; return false;
} }
@@ -365,53 +365,53 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue)
// Common Error Reporting // Common Error Reporting
// ============================================================ // ============================================================
bool WingUtils::CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name) bool WingUtils::CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors)
{ {
if (Count == 0) if (Count == 0)
{ {
UWingServer::Printf(TEXT("Could not find a %s named %s.\n"), Kind, *Name); Errors.Printf(TEXT("Could not find a %s named %s.\n"), Kind, *Name);
return false; return false;
} }
if (Count > 1) if (Count > 1)
{ {
UWingServer::Printf(TEXT("More than one %s named %s\n"), Kind, *Name); Errors.Printf(TEXT("More than one %s named %s\n"), Kind, *Name);
return false; return false;
} }
return true; return true;
} }
bool WingUtils::CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name) bool WingUtils::CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors)
{ {
return CheckExactlyOneNamed(Count, Kind, ExternalizeID(Name)); return CheckExactlyOneNamed(Count, Kind, ExternalizeID(Name), Errors);
} }
bool WingUtils::CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name) bool WingUtils::CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors)
{ {
if (Count > 0) if (Count > 0)
{ {
UWingServer::Printf(TEXT("A %s named %s already exists."), Kind, *Name); Errors.Printf(TEXT("A %s named %s already exists."), Kind, *Name);
return false; return false;
} }
return true; return true;
} }
bool WingUtils::CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name) bool WingUtils::CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors)
{ {
return CheckExactlyNoneNamed(Count, Kind, ExternalizeID(Name)); return CheckExactlyNoneNamed(Count, Kind, ExternalizeID(Name), Errors);
} }
bool WingUtils::CheckCanRename(UEdGraphNode* Node, const FString &Name) bool WingUtils::CheckCanRename(UEdGraphNode* Node, const FString &Name, WingOut Errors)
{ {
if (!Node->bCanRenameNode) if (!Node->bCanRenameNode)
{ {
UWingServer::Printf(TEXT("ERROR: Node %s does not support renaming.\n"), *FormatName(Node)); Errors.Printf(TEXT("ERROR: Node %s does not support renaming.\n"), *FormatName(Node));
return false; return false;
} }
TSharedPtr<INameValidatorInterface> Validator = FNameValidatorFactory::MakeValidator(Node); TSharedPtr<INameValidatorInterface> Validator = FNameValidatorFactory::MakeValidator(Node);
EValidatorResult Result = Validator->IsValid(Name, false); EValidatorResult Result = Validator->IsValid(Name, false);
if (Result != EValidatorResult::Ok && Result != EValidatorResult::ExistingName) if (Result != EValidatorResult::Ok && Result != EValidatorResult::ExistingName)
{ {
UWingServer::Printf(TEXT("ERROR: %s\n"), *INameValidatorInterface::GetErrorString(Name, Result)); Errors.Printf(TEXT("ERROR: %s\n"), *INameValidatorInterface::GetErrorString(Name, Result));
return false; return false;
} }
return true; return true;
@@ -573,7 +573,7 @@ UAnimationStateMachineGraph* WingUtils::FindStateMachineGraph(UBlueprint* BP, co
return nullptr; return nullptr;
} }
UAnimStateNode* WingUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName) UAnimStateNode* WingUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName, WingOut Errors)
{ {
for (UEdGraphNode* Node : SMGraph->Nodes) for (UEdGraphNode* Node : SMGraph->Nodes)
{ {
@@ -585,7 +585,7 @@ UAnimStateNode* WingUtils::FindStateByName(UAnimationStateMachineGraph* SMGraph,
} }
} }
} }
UWingServer::Printf(TEXT("ERROR: State '%s' not found in graph '%s'\n"), *StateName, *SMGraph->GetName()); Errors.Printf(TEXT("ERROR: State '%s' not found in graph '%s'\n"), *StateName, *SMGraph->GetName());
return nullptr; return nullptr;
} }

View File

@@ -26,12 +26,12 @@ static TSet<FName> Flags_BlueprintVariables = { Flag_InstanceEditable, Flag_Blue
void WingVariableList::Print(FStringBuilderBase &Out) void WingVariableList::Print(WingOut Out)
{ {
if (Variables.IsEmpty()) return; if (Variables.IsEmpty()) return;
Out.Append(ListName); Out.Print(ListName);
Out.AppendChar(':'); Out.PrintChar(':');
Out.AppendChar('\n'); Out.PrintChar('\n');
for (const Var& V : Variables) for (const Var& V : Variables)
{ {
FString TypeStr = UWingTypes::TypeToText(V.Type); FString TypeStr = UWingTypes::TypeToText(V.Type);
@@ -51,23 +51,23 @@ void WingVariableList::Print(FStringBuilderBase &Out)
} }
// Print: type name (flags) = defaultvalue // Print: type name (flags) = defaultvalue
Out.Appendf(TEXT(" %s %s"), *TypeStr, *NameStr); Out.Printf(TEXT(" %s %s"), *TypeStr, *NameStr);
if (!FlagsStr.IsEmpty()) if (!FlagsStr.IsEmpty())
Out.Appendf(TEXT(" (%s)"), *FlagsStr); Out.Printf(TEXT(" (%s)"), *FlagsStr);
if (!V.DefaultValue.IsEmpty()) if (!V.DefaultValue.IsEmpty())
Out.Appendf(TEXT(" = %s"), *V.DefaultValue); Out.Printf(TEXT(" = %s"), *V.DefaultValue);
Out.Append(TEXT("\n")); Out.Print(TEXT("\n"));
} }
} }
void WingVariableList::PrintCompact(FStringBuilderBase &Out) void WingVariableList::PrintCompact(WingOut Out)
{ {
bool First = true; bool First = true;
for (const Var& V : Variables) for (const Var& V : Variables)
{ {
if (!First) Out << TEXT(","); if (!First) Out.PrintChar(',');
First = false; First = false;
Out << UWingTypes::TypeToText(V.Type) << TEXT(" ") << WingUtils::ExternalizeID(V.Name); Out.Printf(TEXT("%s %s"), *UWingTypes::TypeToText(V.Type), *WingUtils::ExternalizeID(V.Name));
} }
} }
@@ -80,11 +80,11 @@ void WingVariableList::ClearLinks()
} }
} }
bool WingVariableList::CheckSanity(const TSet<FName> &GoodFlags, bool Allow) bool WingVariableList::CheckSanity(const TSet<FName> &GoodFlags, bool Allow, WingOut Errors)
{ {
if ((!Allow) && (!Variables.IsEmpty())) if ((!Allow) && (!Variables.IsEmpty()))
{ {
UWingServer::Printf(TEXT("In this context, %s must be empty."), ListName); Errors.Printf(TEXT("In this context, %s must be empty."), ListName);
return false; return false;
} }
for (const Var &Variable : Variables) for (const Var &Variable : Variables)
@@ -93,20 +93,20 @@ bool WingVariableList::CheckSanity(const TSet<FName> &GoodFlags, bool Allow)
FString TypeText = UWingTypes::TypeToText(Variable.Type); FString TypeText = UWingTypes::TypeToText(Variable.Type);
if (TypeText.IsEmpty()) if (TypeText.IsEmpty())
{ {
UWingServer::Printf(TEXT("Type of variable %s is not valid for unknown reasons\n"), *VarName); Errors.Printf(TEXT("Type of variable %s is not valid for unknown reasons\n"), *VarName);
return false; return false;
} }
if (!UWingTypes::IsBlueprintType(Variable.Type)) if (!UWingTypes::IsBlueprintType(Variable.Type))
{ {
UWingServer::Printf(TEXT("Type is not a valid BlueprintType: %s %s\n"), *TypeText, *VarName); Errors.Printf(TEXT("Type is not a valid BlueprintType: %s %s\n"), *TypeText, *VarName);
return false; return false;
} }
for (FName Flag : Variable.Flags) for (FName Flag : Variable.Flags)
{ {
if (!GoodFlags.Contains(Flag)) if (!GoodFlags.Contains(Flag))
{ {
UWingServer::Printf(TEXT("Flag %s is not valid here. Valid flags are:"), *Flag.ToString()); Errors.Printf(TEXT("Flag %s is not valid here. Valid flags are:"), *Flag.ToString());
for (FName Rel : GoodFlags) UWingServer::Printf(TEXT(" %s\n"), *Rel.ToString()); for (FName Rel : GoodFlags) Errors.Printf(TEXT(" %s\n"), *Rel.ToString());
return false; return false;
} }
} }
@@ -114,7 +114,7 @@ bool WingVariableList::CheckSanity(const TSet<FName> &GoodFlags, bool Allow)
return true; return true;
} }
bool WingVariableList::ParseString(const FString &Input) bool WingVariableList::ParseString(const FString &Input, WingOut Errors)
{ {
Variables.Empty(); Variables.Empty();
@@ -127,14 +127,14 @@ bool WingVariableList::ParseString(const FString &Input)
if (Tok.NextType() == 0) continue; if (Tok.NextType() == 0) continue;
Var V; Var V;
V.DefaultSpecified = false; V.DefaultSpecified = false;
if (!ParseOneVariable(Tok, V)) return false; if (!ParseOneVariable(Tok, V, Errors)) return false;
Variables.Add(MoveTemp(V)); Variables.Add(MoveTemp(V));
} }
return true; return true;
} }
bool WingVariableList::ParseNamesString(const FString &Input) bool WingVariableList::ParseNamesString(const FString &Input, WingOut Errors)
{ {
Variables.Empty(); Variables.Empty();
WingTokenizer Tok(Input); WingTokenizer Tok(Input);
@@ -151,27 +151,27 @@ bool WingVariableList::ParseNamesString(const FString &Input)
if (!Tok.TokenIs(0)) if (!Tok.TokenIs(0))
{ {
Tok.SaveCursor(NAME_None); Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("Unexpected token %s in variable list"), Errors.Printf(TEXT("Unexpected token %s in variable list"),
*FString(Tok.GetRange(NAME_None, 1))); *FString(Tok.GetRange(NAME_None, 1)));
return false; return false;
} }
return true; return true;
} }
bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V) bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V, WingOut Errors)
{ {
// Parse type. // Parse type.
UWingTypes::Requirements Req; UWingTypes::Requirements Req;
Req.BlueprintType = true; Req.BlueprintType = true;
Req.Blueprintable = false; Req.Blueprintable = false;
Req.AllowContainer = true; Req.AllowContainer = true;
if (!UWingTypes::TextToType(Tok, V.Type, Req, false)) if (!UWingTypes::TextToType(Tok, V.Type, Req, false, Errors))
return false; return false;
// Parse name. // Parse name.
if (Tok.NextType() != Tok.Identifier) if (Tok.NextType() != Tok.Identifier)
{ {
UWingServer::Print(TEXT("ERROR: Expected variable name after type\n")); Errors.Print(TEXT("ERROR: Expected variable name after type\n"));
return false; return false;
} }
V.Name = Tok.NextName(); V.Name = Tok.NextName();
@@ -180,7 +180,7 @@ bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V)
// Parse optional flags: (flag1, flag2, ...) // Parse optional flags: (flag1, flag2, ...)
if (Tok.TokenIs('(')) if (Tok.TokenIs('('))
{ {
if (!ParseVariableFlags(Tok, V.Flags)) return false; if (!ParseVariableFlags(Tok, V.Flags, Errors)) return false;
} }
// Parse optional default value: = rest-of-line // Parse optional default value: = rest-of-line
@@ -195,7 +195,7 @@ bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V)
if (Tok.NextType() != 0) if (Tok.NextType() != 0)
{ {
Tok.SaveCursor(NAME_None); Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("ERROR: Unexpected token after variable declaration: '%s'\n"), Errors.Printf(TEXT("ERROR: Unexpected token after variable declaration: '%s'\n"),
*FString(Tok.GetRange(NAME_None, 1))); *FString(Tok.GetRange(NAME_None, 1)));
return false; return false;
} }
@@ -203,7 +203,7 @@ bool WingVariableList::ParseOneVariable(WingTokenizer &Tok, Var &V)
return true; return true;
} }
bool WingVariableList::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out) bool WingVariableList::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out, WingOut Errors)
{ {
Tok.Advance(); // Step over open-paren Tok.Advance(); // Step over open-paren
while (Tok.TokenIs(Tok.Identifier)) while (Tok.TokenIs(Tok.Identifier))
@@ -216,7 +216,7 @@ bool WingVariableList::ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out)
if (!Tok.TokenIs(')')) if (!Tok.TokenIs(')'))
{ {
Tok.SaveCursor(NAME_None); Tok.SaveCursor(NAME_None);
UWingServer::Printf(TEXT("ERROR: flag list contains invalid token '%s'\n"), Errors.Printf(TEXT("ERROR: flag list contains invalid token '%s'\n"),
*FString(Tok.GetRange(NAME_None, 1))); *FString(Tok.GetRange(NAME_None, 1)));
return false; return false;
} }
@@ -240,7 +240,7 @@ void WingVariables::ClearLinks()
OutputVariables.ClearLinks(); OutputVariables.ClearLinks();
} }
void WingVariables::Print(FStringBuilderBase &Out) void WingVariables::Print(WingOut Out)
{ {
BlueprintVariables.Print(Out); BlueprintVariables.Print(Out);
LocalVariables.Print(Out); LocalVariables.Print(Out);
@@ -248,13 +248,13 @@ void WingVariables::Print(FStringBuilderBase &Out)
OutputVariables.Print(Out); OutputVariables.Print(Out);
} }
void WingVariables::Load() void WingVariables::Load(WingOut Errors)
{ {
Empty(); Empty();
if (Blueprint != nullptr) return LoadBlueprint(); if (Blueprint != nullptr) return LoadBlueprint();
if (Graph != nullptr) return LoadGraph(); if (Graph != nullptr) return LoadGraph();
if (CustomEvent != nullptr) return LoadCustomEvent(); if (CustomEvent != nullptr) return LoadCustomEvent();
ErrorNoBackingStore(); ErrorNoBackingStore(Errors);
} }
void WingVariables::LoadBlueprint() void WingVariables::LoadBlueprint()
@@ -356,25 +356,25 @@ void WingVariables::LoadCustomEvent()
LoadEditablePinBase(CustomEvent, InputVariables); LoadEditablePinBase(CustomEvent, InputVariables);
} }
bool WingVariables::Check() bool WingVariables::Check(WingOut Errors)
{ {
if (Blueprint) return CheckBlueprint(); if (Blueprint) return CheckBlueprint(Errors);
if (Graph) return CheckGraph(); if (Graph) return CheckGraph(Errors);
if (CustomEvent) return CheckCustomEvent(); if (CustomEvent) return CheckCustomEvent(Errors);
return ErrorNoBackingStore(); return ErrorNoBackingStore(Errors);
} }
bool WingVariables::CheckBlueprint() bool WingVariables::CheckBlueprint(WingOut Errors)
{ {
bool OK = true; bool OK = true;
if (!BlueprintVariables.CheckSanity(Flags_BlueprintVariables, true)) OK = false; if (!BlueprintVariables.CheckSanity(Flags_BlueprintVariables, true, Errors)) OK = false;
if (!LocalVariables.CheckSanity(Flags_None, false)) OK = false; if (!LocalVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
if (!InputVariables.CheckSanity(Flags_None, false)) OK = false; if (!InputVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, false)) OK = false; if (!OutputVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
return OK; return OK;
} }
bool WingVariables::CheckGraph() bool WingVariables::CheckGraph(WingOut Errors)
{ {
EGraphType T = Graph->GetSchema()->GetGraphType(Graph); EGraphType T = Graph->GetSchema()->GetGraphType(Graph);
bool AllowLocal = (T == EGraphType::GT_Function); bool AllowLocal = (T == EGraphType::GT_Function);
@@ -382,56 +382,56 @@ bool WingVariables::CheckGraph()
bool AllowOutput = (T == EGraphType::GT_Function) || (T == EGraphType::GT_Macro); bool AllowOutput = (T == EGraphType::GT_Function) || (T == EGraphType::GT_Macro);
if ((!AllowLocal) && (!AllowInput) && (!AllowOutput)) if ((!AllowLocal) && (!AllowInput) && (!AllowOutput))
{ {
UWingServer::Printf(TEXT("This graph type has no variables.")); Errors.Printf(TEXT("This graph type has no variables."));
return false; return false;
} }
bool OK = true; bool OK = true;
if (!BlueprintVariables.CheckSanity(Flags_None, false)) OK = false; if (!BlueprintVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
if (!LocalVariables.CheckSanity(Flags_None, AllowLocal)) OK = false; if (!LocalVariables.CheckSanity(Flags_None, AllowLocal, Errors)) OK = false;
if (!InputVariables.CheckSanity(Flags_None, AllowInput)) OK = false; if (!InputVariables.CheckSanity(Flags_None, AllowInput, Errors)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, AllowOutput)) OK = false; if (!OutputVariables.CheckSanity(Flags_None, AllowOutput, Errors)) OK = false;
return OK; return OK;
} }
bool WingVariables::CheckCustomEvent() bool WingVariables::CheckCustomEvent(WingOut Errors)
{ {
bool OK = true; bool OK = true;
if (!BlueprintVariables.CheckSanity(Flags_None, false)) OK = false; if (!BlueprintVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
if (!LocalVariables.CheckSanity(Flags_None, false)) OK = false; if (!LocalVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
if (!InputVariables.CheckSanity(Flags_None, true)) OK = false; if (!InputVariables.CheckSanity(Flags_None, true, Errors)) OK = false;
if (!OutputVariables.CheckSanity(Flags_None, false)) OK = false; if (!OutputVariables.CheckSanity(Flags_None, false, Errors)) OK = false;
return OK; return OK;
} }
bool WingVariables::Modify() bool WingVariables::Modify(WingOut Errors)
{ {
if (Blueprint) return ModifyBlueprint(); if (Blueprint) return ModifyBlueprint(Errors);
if (Graph) return ModifyGraph(); if (Graph) return ModifyGraph(Errors);
if (CustomEvent) return ModifyCustomEvent(); if (CustomEvent) return ModifyCustomEvent(Errors);
return ErrorNoBackingStore(); return ErrorNoBackingStore(Errors);
} }
bool WingVariables::ModifyBlueprint() bool WingVariables::ModifyBlueprint(WingOut Errors)
{ {
if (!CheckBlueprint()) return false; if (!CheckBlueprint(Errors)) return false;
if (LinkBlueprintVariables()) return false; if (LinkBlueprintVariables(Errors)) return false;
for (Var &V : BlueprintVariables.Variables) for (Var &V : BlueprintVariables.Variables)
{ {
V.BPVar->VarType = V.Type; V.BPVar->VarType = V.Type;
ModifyBlueprintVariableFlags(V); ModifyBlueprintVariableFlags(V);
} }
if (!ModifyBlueprintDefaults()) return false; if (!ModifyBlueprintDefaults(Errors)) return false;
return true; return true;
} }
bool WingVariables::LinkBlueprintVariables() bool WingVariables::LinkBlueprintVariables(WingOut Errors)
{ {
ClearLinks(); ClearLinks();
for (Var &V : BlueprintVariables.Variables) for (Var &V : BlueprintVariables.Variables)
{ {
V.BPVar = WingUtils::FindOneWithInternalID( V.BPVar = WingUtils::FindOneWithInternalID(
V.Name, Blueprint->NewVariables, TEXT("non-inherited variable")); V.Name, Blueprint->NewVariables, TEXT("non-inherited variable"), Errors);
if (V.BPVar == nullptr) return false; if (V.BPVar == nullptr) return false;
} }
return true; return true;
@@ -468,7 +468,7 @@ void WingVariables::ModifyBlueprintVariableFlags(Var &Input)
Out.RemoveMetaData(FBlueprintMetadata::MD_Private); Out.RemoveMetaData(FBlueprintMetadata::MD_Private);
} }
bool WingVariables::ModifyBlueprintDefaults() bool WingVariables::ModifyBlueprintDefaults(WingOut Errors)
{ {
bool AnySpecified = false; bool AnySpecified = false;
for (Var &Input : BlueprintVariables.Variables) for (Var &Input : BlueprintVariables.Variables)
@@ -479,7 +479,7 @@ bool WingVariables::ModifyBlueprintDefaults()
UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint); UObject *CDO = WingUtils::GetGeneratedCDO(Blueprint);
if (!CDO) if (!CDO)
{ {
UWingServer::Printf(TEXT("Blueprint didn't compile, cannot store default values")); Errors.Printf(TEXT("Blueprint didn't compile, cannot store default values"));
return false; return false;
} }
@@ -490,56 +490,56 @@ bool WingVariables::ModifyBlueprintDefaults()
FProperty* Prop = CDO->GetClass()->FindPropertyByName(Input.Name); FProperty* Prop = CDO->GetClass()->FindPropertyByName(Input.Name);
if (!Prop) if (!Prop)
{ {
UWingServer::Printf(TEXT("Variable exists in blueprint, but not the generated class, which is weird: %s."), Errors.Printf(TEXT("Variable exists in blueprint, but not the generated class, which is weird: %s."),
*WingTokenizer::ExternalizeID(Input.Name)); *WingTokenizer::ExternalizeID(Input.Name));
return false; return false;
} }
if (!FWingProperty(Prop, CDO).SetText(Input.DefaultValue)) return false; if (!FWingProperty(Prop, CDO).SetText(Input.DefaultValue, Errors)) return false;
} }
} }
return true; return true;
} }
bool WingVariables::ModifyGraph() bool WingVariables::ModifyGraph(WingOut Errors)
{ {
if (!CheckGraph()) return false; if (!CheckGraph(Errors)) return false;
ClearLinks(); ClearLinks();
UK2Node_EditablePinBase *InputNode, *OutputNode; UK2Node_EditablePinBase *InputNode, *OutputNode;
UK2Node_FunctionEntry *LocalNode; UK2Node_FunctionEntry *LocalNode;
if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false; if (!GetGraphNodes(InputNode, OutputNode, LocalNode, Errors)) return false;
for (Var &V : LocalVariables.Variables) for (Var &V : LocalVariables.Variables)
{ {
FBPVariableDescription *Desc = FBPVariableDescription *Desc =
WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable")); WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable"), Errors);
if (Desc == nullptr) return false; if (Desc == nullptr) return false;
Desc->VarType = V.Type; Desc->VarType = V.Type;
if (V.DefaultSpecified) Desc->DefaultValue = V.DefaultValue; if (V.DefaultSpecified) Desc->DefaultValue = V.DefaultValue;
} }
if (!ModifyEditablePinBase(InputVariables, InputNode)) return false; if (!ModifyEditablePinBase(InputVariables, InputNode, Errors)) return false;
if (!ModifyEditablePinBase(OutputVariables, OutputNode)) return false; if (!ModifyEditablePinBase(OutputVariables, OutputNode, Errors)) return false;
if (InputNode) InputNode->ReconstructNode(); if (InputNode) InputNode->ReconstructNode();
if (OutputNode) OutputNode->ReconstructNode(); if (OutputNode) OutputNode->ReconstructNode();
return true; return true;
} }
bool WingVariables::ModifyCustomEvent() bool WingVariables::ModifyCustomEvent(WingOut Errors)
{ {
if (!CheckCustomEvent()) return false; if (!CheckCustomEvent(Errors)) return false;
ClearLinks(); ClearLinks();
if (!ModifyEditablePinBase(InputVariables, CustomEvent)) return false; if (!ModifyEditablePinBase(InputVariables, CustomEvent, Errors)) return false;
CustomEvent->ReconstructNode(); CustomEvent->ReconstructNode();
return true; return true;
} }
bool WingVariables::ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node) bool WingVariables::ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node, WingOut Errors)
{ {
for (Var &V : List.Variables) for (Var &V : List.Variables)
{ {
TSharedPtr<FUserPinInfo> *Found = TSharedPtr<FUserPinInfo> *Found =
WingUtils::FindOneWithInternalID(V.Name, Node->UserDefinedPins, List.ListName); WingUtils::FindOneWithInternalID(V.Name, Node->UserDefinedPins, List.ListName, Errors);
if (!Found) return false; if (!Found) return false;
(*Found)->PinType = V.Type; (*Found)->PinType = V.Type;
if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue; if (V.DefaultSpecified) (*Found)->PinDefaultValue = V.DefaultValue;
@@ -550,7 +550,8 @@ bool WingVariables::ModifyEditablePinBase(WingVariableList &List, UK2Node_Editab
bool WingVariables::GetGraphNodes( bool WingVariables::GetGraphNodes(
UK2Node_EditablePinBase *&InputNode, UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode, UK2Node_EditablePinBase *&OutputNode,
UK2Node_FunctionEntry *&LocalNode) UK2Node_FunctionEntry *&LocalNode,
WingOut Errors)
{ {
// In theory, none of these errors should trigger, because // In theory, none of these errors should trigger, because
// we call CheckSanity before calling this function. But // we call CheckSanity before calling this function. But
@@ -563,22 +564,22 @@ bool WingVariables::GetGraphNodes(
FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, Inputs, Outputs); FBlueprintEditorUtils::GetEntryAndResultNodes(Graph, Inputs, Outputs);
if (!Inputs.IsValid()) if (!Inputs.IsValid())
{ {
UWingServer::Printf(TEXT("ERROR: no function entry node for graph.")); Errors.Printf(TEXT("ERROR: no function entry node for graph."));
return false; return false;
} }
if (!Outputs.IsValid() && (!OutputVariables.Variables.IsEmpty())) if (!Outputs.IsValid() && (!OutputVariables.Variables.IsEmpty()))
{ {
Outputs = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(Inputs.Get()); Outputs = FBlueprintEditorUtils::FindOrCreateFunctionResultNode(Inputs.Get());
if (!Outputs.IsValid()) if (!Outputs.IsValid())
{ {
UWingServer::Printf(TEXT("ERROR: couldn't create result node for graph.")); Errors.Printf(TEXT("ERROR: couldn't create result node for graph."));
return false; return false;
} }
} }
UK2Node_FunctionEntry *Locals = Cast<UK2Node_FunctionEntry>(Inputs.Get()); UK2Node_FunctionEntry *Locals = Cast<UK2Node_FunctionEntry>(Inputs.Get());
if (!Locals && (!LocalVariables.Variables.IsEmpty())) if (!Locals && (!LocalVariables.Variables.IsEmpty()))
{ {
UWingServer::Printf(TEXT("ERROR: function doesn't have a proper entry node for local variables")); Errors.Printf(TEXT("ERROR: function doesn't have a proper entry node for local variables"));
return false; return false;
} }
InputNode = Inputs.Get(); InputNode = Inputs.Get();
@@ -587,65 +588,65 @@ bool WingVariables::GetGraphNodes(
return true; return true;
} }
bool WingVariables::Create() bool WingVariables::Create(WingOut Errors)
{ {
if (Blueprint) return CreateBlueprint(); if (Blueprint) return CreateBlueprint(Errors);
if (Graph) return CreateGraph(); if (Graph) return CreateGraph(Errors);
if (CustomEvent) return CreateCustomEvent(); if (CustomEvent) return CreateCustomEvent(Errors);
return ErrorNoBackingStore(); return ErrorNoBackingStore(Errors);
} }
bool WingVariables::CreateBlueprint() bool WingVariables::CreateBlueprint(WingOut Errors)
{ {
if (!CheckBlueprint()) return false; if (!CheckBlueprint(Errors)) return false;
ClearLinks(); ClearLinks();
// Check for name collisions against existing variables, components, and the like. // Check for name collisions against existing variables, components, and the like.
TSet<FName> Names; TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(Blueprint, Names); FBlueprintEditorUtils::GetClassVariableList(Blueprint, Names);
if (!WingUtils::FindNoDuplicateNames(Names, BlueprintVariables.Variables, TEXT("variable or component"))) return false; if (!WingUtils::FindNoDuplicateNames(Names, BlueprintVariables.Variables, TEXT("variable or component"), Errors)) return false;
// Create the variables. // Create the variables.
for (const WingVariables::Var& V : BlueprintVariables.Variables) for (const WingVariables::Var& V : BlueprintVariables.Variables)
{ {
if (!FBlueprintEditorUtils::AddMemberVariable(Blueprint, V.Name, V.Type)) if (!FBlueprintEditorUtils::AddMemberVariable(Blueprint, V.Name, V.Type))
{ {
UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s'\n"), Errors.Printf(TEXT("ERROR: Failed to add variable '%s'\n"),
*WingUtils::ExternalizeID(V.Name)); *WingUtils::ExternalizeID(V.Name));
return false; return false;
} }
} }
if (LinkBlueprintVariables()) return false; if (LinkBlueprintVariables(Errors)) return false;
for (Var &V : BlueprintVariables.Variables) ModifyBlueprintVariableFlags(V); for (Var &V : BlueprintVariables.Variables) ModifyBlueprintVariableFlags(V);
if (!ModifyBlueprintDefaults()) return false; if (!ModifyBlueprintDefaults(Errors)) return false;
return false; return false;
} }
bool WingVariables::CreateGraph() bool WingVariables::CreateGraph(WingOut Errors)
{ {
if (!CheckGraph()) return false; if (!CheckGraph(Errors)) return false;
ClearLinks(); ClearLinks();
UK2Node_EditablePinBase *InputNode, *OutputNode; UK2Node_EditablePinBase *InputNode, *OutputNode;
UK2Node_FunctionEntry *LocalNode; UK2Node_FunctionEntry *LocalNode;
if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false; if (!GetGraphNodes(InputNode, OutputNode, LocalNode, Errors)) return false;
// Check for name collisions against existing local variables. // Check for name collisions against existing local variables.
const TCHAR *ctx = TEXT("local, function input, and function output variables"); const TCHAR *ctx = TEXT("local, function input, and function output variables");
TSet<FName> Names; TSet<FName> Names;
if (InputNode && !WingUtils::FindNoDuplicateNames( if (InputNode && !WingUtils::FindNoDuplicateNames(
Names, InputNode->UserDefinedPins, ctx)) return false; Names, InputNode->UserDefinedPins, ctx, Errors)) return false;
if (OutputNode && !WingUtils::FindNoDuplicateNames( if (OutputNode && !WingUtils::FindNoDuplicateNames(
Names, OutputNode->UserDefinedPins, ctx)) return false; Names, OutputNode->UserDefinedPins, ctx, Errors)) return false;
if (LocalNode && !WingUtils::FindNoDuplicateNames( if (LocalNode && !WingUtils::FindNoDuplicateNames(
Names, LocalNode->LocalVariables, ctx)) return false; Names, LocalNode->LocalVariables, ctx, Errors)) return false;
if (!WingUtils::FindNoDuplicateNames( if (!WingUtils::FindNoDuplicateNames(
Names, InputVariables.Variables, ctx)) return false; Names, InputVariables.Variables, ctx, Errors)) return false;
if (!WingUtils::FindNoDuplicateNames( if (!WingUtils::FindNoDuplicateNames(
Names, OutputVariables.Variables, ctx)) return false; Names, OutputVariables.Variables, ctx, Errors)) return false;
if (!WingUtils::FindNoDuplicateNames( if (!WingUtils::FindNoDuplicateNames(
Names, LocalVariables.Variables, ctx)) return false; Names, LocalVariables.Variables, ctx, Errors)) return false;
// Create input pins on the input node. // Create input pins on the input node.
for (const Var& V : InputVariables.Variables) for (const Var& V : InputVariables.Variables)
@@ -661,7 +662,7 @@ bool WingVariables::CreateGraph()
{ {
if (!FBlueprintEditorUtils::AddLocalVariable(BP, Graph, V.Name, V.Type, V.DefaultValue)) if (!FBlueprintEditorUtils::AddLocalVariable(BP, Graph, V.Name, V.Type, V.DefaultValue))
{ {
UWingServer::Printf(TEXT("ERROR: Failed to create local variable '%s'\n"), Errors.Printf(TEXT("ERROR: Failed to create local variable '%s'\n"),
*WingUtils::ExternalizeID(V.Name)); *WingUtils::ExternalizeID(V.Name));
return false; return false;
} }
@@ -672,17 +673,17 @@ bool WingVariables::CreateGraph()
return true; return true;
} }
bool WingVariables::CreateCustomEvent() bool WingVariables::CreateCustomEvent(WingOut Errors)
{ {
if (!CheckCustomEvent()) return false; if (!CheckCustomEvent(Errors)) return false;
ClearLinks(); ClearLinks();
// Check for name collisions against existing pins. // Check for name collisions against existing pins.
TSet<FName> Names; TSet<FName> Names;
if (!WingUtils::FindNoDuplicateNames( if (!WingUtils::FindNoDuplicateNames(
Names, CustomEvent->UserDefinedPins, TEXT("event parameter"))) return false; Names, CustomEvent->UserDefinedPins, TEXT("event parameter"), Errors)) return false;
if (!WingUtils::FindNoDuplicateNames( if (!WingUtils::FindNoDuplicateNames(
Names, InputVariables.Variables, TEXT("event parameter"))) return false; Names, InputVariables.Variables, TEXT("event parameter"), Errors)) return false;
for (const Var& V : InputVariables.Variables) for (const Var& V : InputVariables.Variables)
AddUserPinInfo(V, EGPD_Output, CustomEvent); AddUserPinInfo(V, EGPD_Output, CustomEvent);
@@ -701,22 +702,22 @@ void WingVariables::AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2No
Node->UserDefinedPins.Add(Result); Node->UserDefinedPins.Add(Result);
} }
bool WingVariables::Remove() bool WingVariables::Remove(WingOut Errors)
{ {
if (Blueprint) return RemoveBlueprint(); if (Blueprint) return RemoveBlueprint(Errors);
if (Graph) return RemoveGraph(); if (Graph) return RemoveGraph(Errors);
if (CustomEvent) return RemoveCustomEvent(); if (CustomEvent) return RemoveCustomEvent(Errors);
return ErrorNoBackingStore(); return ErrorNoBackingStore(Errors);
} }
bool WingVariables::RemoveBlueprint() bool WingVariables::RemoveBlueprint(WingOut Errors)
{ {
// Verify that all named variables exist. // Verify that all named variables exist.
TArray<FName> Names; TArray<FName> Names;
for (const Var& V : BlueprintVariables.Variables) for (const Var& V : BlueprintVariables.Variables)
{ {
FBPVariableDescription* Found = FBPVariableDescription* Found =
WingUtils::FindOneWithInternalID(V.Name, Blueprint->NewVariables, TEXT("non-inherited variable")); WingUtils::FindOneWithInternalID(V.Name, Blueprint->NewVariables, TEXT("non-inherited variable"), Errors);
if (!Found) return false; if (!Found) return false;
Names.Add(V.Name); Names.Add(V.Name);
} }
@@ -726,29 +727,29 @@ bool WingVariables::RemoveBlueprint()
return true; return true;
} }
bool WingVariables::RemoveGraph() bool WingVariables::RemoveGraph(WingOut Errors)
{ {
UK2Node_EditablePinBase *InputNode, *OutputNode; UK2Node_EditablePinBase *InputNode, *OutputNode;
UK2Node_FunctionEntry *LocalNode; UK2Node_FunctionEntry *LocalNode;
if (!GetGraphNodes(InputNode, OutputNode, LocalNode)) return false; if (!GetGraphNodes(InputNode, OutputNode, LocalNode, Errors)) return false;
// Verify that all named variables exist before removing anything. // Verify that all named variables exist before removing anything.
for (const Var& V : InputVariables.Variables) for (const Var& V : InputVariables.Variables)
{ {
TSharedPtr<FUserPinInfo>* Found = TSharedPtr<FUserPinInfo>* Found =
WingUtils::FindOneWithInternalID(V.Name, InputNode->UserDefinedPins, TEXT("input variable")); WingUtils::FindOneWithInternalID(V.Name, InputNode->UserDefinedPins, TEXT("input variable"), Errors);
if (!Found) return false; if (!Found) return false;
} }
for (const Var& V : OutputVariables.Variables) for (const Var& V : OutputVariables.Variables)
{ {
TSharedPtr<FUserPinInfo>* Found = TSharedPtr<FUserPinInfo>* Found =
WingUtils::FindOneWithInternalID(V.Name, OutputNode->UserDefinedPins, TEXT("output variable")); WingUtils::FindOneWithInternalID(V.Name, OutputNode->UserDefinedPins, TEXT("output variable"), Errors);
if (!Found) return false; if (!Found) return false;
} }
for (const Var& V : LocalVariables.Variables) for (const Var& V : LocalVariables.Variables)
{ {
FBPVariableDescription* Found = FBPVariableDescription* Found =
WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable")); WingUtils::FindOneWithInternalID(V.Name, LocalNode->LocalVariables, TEXT("local variable"), Errors);
if (!Found) return false; if (!Found) return false;
} }
@@ -772,13 +773,13 @@ bool WingVariables::RemoveGraph()
return true; return true;
} }
bool WingVariables::RemoveCustomEvent() bool WingVariables::RemoveCustomEvent(WingOut Errors)
{ {
// Verify that all named parameters exist before removing anything. // Verify that all named parameters exist before removing anything.
for (const Var& V : InputVariables.Variables) for (const Var& V : InputVariables.Variables)
{ {
TSharedPtr<FUserPinInfo>* Found = TSharedPtr<FUserPinInfo>* Found =
WingUtils::FindOneWithInternalID(V.Name, CustomEvent->UserDefinedPins, TEXT("event parameter")); WingUtils::FindOneWithInternalID(V.Name, CustomEvent->UserDefinedPins, TEXT("event parameter"), Errors);
if (!Found) return false; if (!Found) return false;
} }
@@ -789,7 +790,7 @@ bool WingVariables::RemoveCustomEvent()
return true; return true;
} }
bool WingVariables::SetBackingStore(UObject *Obj) bool WingVariables::SetBackingStore(UObject *Obj, WingOut Errors)
{ {
Blueprint = nullptr; Blueprint = nullptr;
Graph = nullptr; Graph = nullptr;
@@ -809,16 +810,16 @@ bool WingVariables::SetBackingStore(UObject *Obj)
CustomEvent = E; CustomEvent = E;
return true; return true;
} }
UWingServer::Printf(TEXT( Errors.Printf(TEXT(
"ERROR: The variable editor can only edit blueprints, " "ERROR: The variable editor can only edit blueprints, "
"graphs, and custom event nodes. Passed in: %s"), "graphs, and custom event nodes. Passed in: %s"),
*WingUtils::FormatName(Obj->GetClass())); *WingUtils::FormatName(Obj->GetClass()));
return false; return false;
} }
bool WingVariables::ErrorNoBackingStore() bool WingVariables::ErrorNoBackingStore(WingOut Errors)
{ {
UWingServer::Printf(TEXT( Errors.Printf(TEXT(
"ERROR: The variable editor was not successfully " "ERROR: The variable editor was not successfully "
"set up with an object to edit.")); "set up with an object to edit."));
return false; return false;

View File

@@ -90,17 +90,17 @@ TArray<WingWidgets::Type> WingWidgets::Search(const FString& Query, int32 MaxRes
return Results; return Results;
} }
bool WingWidgets::CheckCanBeParent(UWidget* Widget) bool WingWidgets::CheckCanBeParent(UWidget* Widget, WingOut Errors)
{ {
UPanelWidget* Panel = Cast<UPanelWidget>(Widget); UPanelWidget* Panel = Cast<UPanelWidget>(Widget);
if (!Panel) if (!Panel)
{ {
UWingServer::Printf(TEXT("ERROR: '%s' is not a panel widget and cannot have children\n"), *WingUtils::FormatName(Widget)); Errors.Printf(TEXT("ERROR: '%s' is not a panel widget and cannot have children\n"), *WingUtils::FormatName(Widget));
return false; return false;
} }
if (!Panel->CanAddMoreChildren()) if (!Panel->CanAddMoreChildren())
{ {
UWingServer::Printf(TEXT("ERROR: '%s' already has a child and cannot accept more\n"), *WingUtils::FormatName(Widget)); Errors.Printf(TEXT("ERROR: '%s' already has a child and cannot accept more\n"), *WingUtils::FormatName(Widget));
return false; return false;
} }
return true; return true;
@@ -111,12 +111,12 @@ void WingWidgets::PrintWidgetTree(UWidget* Widget, int32 Depth)
FString Indent = FString::ChrN(Depth * 2, TEXT(' ')); FString Indent = FString::ChrN(Depth * 2, TEXT(' '));
if (Widget == nullptr) if (Widget == nullptr)
{ {
UWingServer::Printf(TEXT("%s[null]\n"), *Indent); WingOut::Stdout.Printf(TEXT("%s[null]\n"), *Indent);
return; return;
} }
FString TypeName = WingUtils::FormatName(Widget->GetClass()); FString TypeName = WingUtils::FormatName(Widget->GetClass());
FString WidgetName = WingUtils::FormatName(Widget); FString WidgetName = WingUtils::FormatName(Widget);
UWingServer::Printf(TEXT("%s%s %s\n"), *Indent, *TypeName, *WidgetName); WingOut::Stdout.Printf(TEXT("%s%s %s\n"), *Indent, *TypeName, *WidgetName);
if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget)) if (UPanelWidget* Panel = Cast<UPanelWidget>(Widget))
{ {

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingActorComponent.generated.h" #include "WingActorComponent.generated.h"
class UBlueprint; class UBlueprint;
@@ -41,13 +42,13 @@ public:
UActorComponent* GetImmutableTemplate() const; UActorComponent* GetImmutableTemplate() const;
UActorComponent* GetMutableTemplate() const; UActorComponent* GetMutableTemplate() const;
bool ReparentComponent(UWingComponentReference *Parent); bool ReparentComponent(UWingComponentReference *Parent, WingOut Errors);
bool DeleteComponent(); bool DeleteComponent(WingOut Errors);
static bool AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name); static bool AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name, WingOut Errors);
static TArray<UWingComponentReference*> GetAll(UBlueprint* BP); static TArray<UWingComponentReference*> GetAll(UBlueprint* BP);
static bool CheckValidComponentClass(UClass *Class); static bool CheckValidComponentClass(UClass *Class, WingOut Errors);
private: private:
struct FoundComponent struct FoundComponent
{ {
@@ -59,11 +60,11 @@ private:
static UActorComponent *FindComponentInCDO(UClass *Class, FName Name); static UActorComponent *FindComponentInCDO(UClass *Class, FName Name);
static USCS_Node* FindSCSNodeByName(UBlueprint *BP, FName VariableName); static USCS_Node* FindSCSNodeByName(UBlueprint *BP, FName VariableName);
static FoundComponent FindComponent(UBlueprint *BP, FName Name); static FoundComponent FindComponent(UBlueprint *BP, FName Name);
static bool CheckValidParent(FoundComponent FC); static bool CheckValidParent(FoundComponent FC, WingOut Errors);
static bool CheckExists(FoundComponent FC); static bool CheckExists(FoundComponent FC, WingOut Errors);
static bool CheckNoSuchComponent(FoundComponent FC); static bool CheckNoSuchComponent(FoundComponent FC, WingOut Errors);
static bool CheckNotNative(FoundComponent FC, const TCHAR *Action); static bool CheckNotNative(FoundComponent FC, const TCHAR *Action, WingOut Errors);
static bool CheckOwnedByBlueprint(FoundComponent FC, UBlueprint *BP); static bool CheckOwnedByBlueprint(FoundComponent FC, UBlueprint *BP, WingOut Errors);
static void AddChildNode(UBlueprint *BP, USCS_Node *Node, FoundComponent Parent); static void AddChildNode(UBlueprint *BP, USCS_Node *Node, FoundComponent Parent);
static TMap<FName, FName> CalculateParentNames(USimpleConstructionScript *Script); static TMap<FName, FName> CalculateParentNames(USimpleConstructionScript *Script);
}; };

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Factories/Factory.h" #include "Factories/Factory.h"
#include "Factories/EnumFactory.h" #include "Factories/EnumFactory.h"
#include "WingHandler.h"
#include "WingFactories.generated.h" #include "WingFactories.generated.h"
@@ -13,7 +14,7 @@ public:
// Create an asset on disk, using a factory instance. // Create an asset on disk, using a factory instance.
// Returns the main object. If there are problems, // Returns the main object. If there are problems,
// prints error messages and returns nullptr. // prints error messages and returns nullptr.
static UObject *CreateAsset(const FString &Path, UFactory *Factory); static UObject *CreateAsset(const FString &Path, UFactory *Factory, WingOut Errors);
// Some factories are blacklisted, mainly because they // Some factories are blacklisted, mainly because they
// pop up dialog boxes. In those cases, we deal with it // pop up dialog boxes. In those cases, we deal with it
@@ -28,7 +29,7 @@ public:
// Verifies that the asset path is a valid path, and also // Verifies that the asset path is a valid path, and also
// that there's not something already there at that path. // that there's not something already there at that path.
static bool CheckNewAssetPath(const FString &Path); static bool CheckNewAssetPath(const FString &Path, WingOut Errors);
}; };
// The original UEnumFactory may pop a dialog. We made a better one. // The original UEnumFactory may pop a dialog. We made a better one.

View File

@@ -60,7 +60,7 @@ public:
// output to WingServer::Printf. // output to WingServer::Printf.
// //
bool Ok() const { return !bError; } bool Ok() const { return !bError; }
// Try to fetch the current object as a UObject of // Try to fetch the current object as a UObject of
// the specified type. If it isn't one, generates an // the specified type. If it isn't one, generates an
// error and returns nullptr. // error and returns nullptr.
@@ -75,19 +75,19 @@ public:
// Get the current object as a UObject if it is one, // Get the current object as a UObject if it is one,
// otherwise nullptr. Does not generate errors. // otherwise nullptr. Does not generate errors.
// //
UObject* GetObj() const { return Obj; } UObject* GetObj() const { return Obj; }
// Get the asset from where it all began: the first // Get the asset from where it all began: the first
// step in the walk path. If the asset couldn't be // step in the walk path. If the asset couldn't be
// loaded, returns nullptr. Does not generate errors. // loaded, returns nullptr. Does not generate errors.
// //
UObject* GetAsset() const { return OriginalAsset; } UObject* GetAsset() const { return OriginalAsset; }
// Get the asset from where it all began: the first // Get the asset from where it all began: the first
// step in the walk path, as a specified type. Errors // step in the walk path, as a specified type. Errors
// if it cannot cast to the specified type. // if it cannot cast to the specified type.
// //
template<class T> T* CastAsset() template<class T> T* CastAsset()
{ {
if (!CheckAssetIsA(T::StaticClass())) return nullptr; if (!CheckAssetIsA(T::StaticClass())) return nullptr;
@@ -98,7 +98,7 @@ public:
// opened. Get the editor. You must specify the type // opened. Get the editor. You must specify the type
// that you expect the asset to be, and the type to cast // that you expect the asset to be, and the type to cast
// the editor to. Does not generate errors. // the editor to. Does not generate errors.
// //
template<class AssetType, class EditorType> template<class AssetType, class EditorType>
EditorType* CastEditor() EditorType* CastEditor()
{ {
@@ -115,16 +115,16 @@ public:
// Initialize empty. You need to call Asset, or walk // Initialize empty. You need to call Asset, or walk
// a path that starts with an asset. // a path that starts with an asset.
// //
WingFetcher() {} WingFetcher(WingOut InErrors) : Errors(InErrors) {}
// Initialize with an object. From there, you can walk // Initialize with an object. From there, you can walk
// to sub-objects. // to sub-objects.
// //
WingFetcher(UObject* O) : Obj(O) {} WingFetcher(UObject* O, WingOut InErrors) : Obj(O), Errors(InErrors) {}
private: private:
// The Current Object. Only one of these can be non-null. // The Current Object. Only one of these can be non-null.
UObject* Obj = nullptr; UObject* Obj = nullptr;
UEdGraphPin* ResultPin = nullptr; UEdGraphPin* ResultPin = nullptr;
@@ -137,6 +137,9 @@ private:
bool bError = false; bool bError = false;
bool bSkipNotify = false; bool bSkipNotify = false;
// Error reporting destination.
WingOut Errors;
// Internal methods. // Internal methods.
using WalkFunc = WingFetcher& (WingFetcher::*)(const FString&); using WalkFunc = WingFetcher& (WingFetcher::*)(const FString&);
void SetObj(UObject* InObj); void SetObj(UObject* InObj);

View File

@@ -8,6 +8,7 @@
// Marker struct for handler parameters that accept a JSON object. // Marker struct for handler parameters that accept a JSON object.
// PopulateFromJson stashes the actual JSON object into the Json field. // PopulateFromJson stashes the actual JSON object into the Json field.
//
USTRUCT() USTRUCT()
struct FWingJsonObject struct FWingJsonObject
{ {
@@ -17,6 +18,7 @@ struct FWingJsonObject
// Marker struct for handler parameters that accept a JSON array. // Marker struct for handler parameters that accept a JSON array.
// PopulateFromJson stashes the actual JSON array into the Array field. // PopulateFromJson stashes the actual JSON array into the Array field.
//
USTRUCT() USTRUCT()
struct FWingJsonArray struct FWingJsonArray
{ {
@@ -56,4 +58,48 @@ public:
// The configuration object. // The configuration object.
UObject *ConfigurationObject; UObject *ConfigurationObject;
public:
}; };
// A 'WingOut' is our version of an output stream. It always
// contains a pointer to a string builder, or nullptr.
//
class WingOut
{
public:
WingOut(FStringBuilderBase *O) : Buffer(O) {}
WingOut(FStringBuilderBase &O) : Buffer(&O) {}
static WingOut Stdout;
static WingOut None;
void PrintChar(TCHAR Ch) { if (Buffer) Buffer->AppendChar(Ch); }
void Print(const TCHAR* Text) { if (Buffer) Buffer->Append(Text); }
void Print(const FString& Text) { if (Buffer) Buffer->Append(Text); }
template <typename FmtType, typename... ArgTypes>
void Printf(const FmtType& Fmt, ArgTypes&&... Args)
{ if (Buffer) Buffer->Appendf(Fmt, Forward<ArgTypes>(Args)...); }
static TStringBuilder<65536> StdoutBuffer;
private:
FStringBuilderBase *Buffer;
};
// A FWingStructAndUStruct is a pointer to a struct and its associated ustruct.
//
struct FWingStructAndUStruct
{
void *StructPtr;
UStruct *UStructPtr;
// Copy constructor.
FWingStructAndUStruct(FWingStructAndUStruct &Src) : StructPtr(Src.StructPtr), UStructPtr(Src.UStructPtr) {}
// Construct from a UObject.
FWingStructAndUStruct(UObject *Obj) : StructPtr(Obj), UStructPtr(Obj->GetClass()) {}
// Construct from a UStruct pointer.
template<class T, typename = std::enable_if_t<!std::is_base_of_v<UObject, T>>>
FWingStructAndUStruct(T *Struct) : StructPtr(Struct), UStructPtr(Struct->StaticStruct()) {}
};

View File

@@ -2,12 +2,13 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Materials/Material.h" #include "Materials/Material.h"
#include "WingHandler.h"
class WingMaterialParameter class WingMaterialParameter
{ {
public: public:
static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material); static TMap<FMaterialParameterInfo, FMaterialParameterMetadata> GetMaterialParameters(UMaterialInterface* Material);
static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation); static bool ParseMaterialParameterAssociation(const FString& Str, EMaterialParameterAssociation& OutAssociation, WingOut Errors);
static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta); static void FormatMaterialParameter(const FMaterialParameterInfo& Info, const FMaterialParameterMetadata& Meta);
}; };

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "IPropertyRowGenerator.h" #include "IPropertyRowGenerator.h"
#include "PropertyHandle.h" #include "PropertyHandle.h"
#include "WingHandler.h"
class FJsonObject; class FJsonObject;
class FJsonValue; class FJsonValue;
@@ -44,35 +45,35 @@ public:
TSharedPtr<IPropertyHandle> TryNamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter); TSharedPtr<IPropertyHandle> TryNamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter);
// Like TryNamedProperty, but prints an error if the property is not found. // Like TryNamedProperty, but prints an error if the property is not found.
TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name, bool RootFilter); TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name, bool RootFilter, WingOut Errors);
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter); TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter, WingOut Errors);
// Get "details panel" properties for an object. Handles special // Get "details panel" properties for an object. Handles special
// cases: blueprints (redirects to CDO), component references // cases: blueprints (redirects to CDO), component references
// (redirects to template), material graph nodes (includes // (redirects to template), material graph nodes (includes
// expression properties), widgets (includes slot properties). // expression properties), widgets (includes slot properties).
Handles GetDetails(UObject* Obj, bool Mutable); Handles GetDetails(UObject* Obj, bool Mutable, WingOut Errors);
// Organize properties by name. If there's more than one property // Organize properties by name. If there's more than one property
// of a given name, print an error and returns false, and returns a map // of a given name, print an error and returns false, and returns a map
// that contains one of the two duplicates. // that contains one of the two duplicates.
static bool OrganizeByName(const Handles &H, TMap<FName, TSharedPtr<IPropertyHandle>> &Result); static bool OrganizeByName(const Handles &H, TMap<FName, TSharedPtr<IPropertyHandle>> &Result, WingOut Errors);
// Get/set text. Compared to the built in methods for getting and setting // Get/set text. Compared to the built in methods for getting and setting
// text, these support more concise enum values, FEdGraphPinType, and // text, these support more concise enum values, FEdGraphPinType, and
// more concise Class properties. // more concise Class properties.
static FString GetText(IPropertyHandle& Handle); static FString GetText(IPropertyHandle& Handle);
static bool SetText(IPropertyHandle& Handle, const FString& Text); static bool SetText(IPropertyHandle& Handle, const FString& Text, WingOut Errors);
// Store a Json value into a property handle. The Json value must be // Store a Json value into a property handle. The Json value must be
// a string, number, or boolean. // a string, number, or boolean.
static bool SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue); static bool SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValue>& JsonValue, WingOut Errors);
// Populate a whole bunch of properties from a Json object. If // Populate a whole bunch of properties from a Json object. If
// AllOptional is true, the Json may supply a subset of the properties. // AllOptional is true, the Json may supply a subset of the properties.
// If not, the Json must supply all of them, excepting properties that // If not, the Json must supply all of them, excepting properties that
// are explicitly marked Optional. // are explicitly marked Optional.
static bool PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional); static bool PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props, const FJsonObject& Json, bool AllOptional, WingOut Errors);
// Print a single property in a standardized format: // Print a single property in a standardized format:
// editable|readonly Type Name = Value // editable|readonly Type Name = Value

View File

@@ -1,38 +1,88 @@
#pragma once #pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingUtils.h" #include "WingUtils.h"
// A resolved property: the FProperty descriptor plus a pointer to // A resolved property: the FProperty descriptor plus a pointer to
// the value's storage. operator-> forwards to the FProperty. // the value's storage.
//
struct FWingProperty struct FWingProperty
{ {
FProperty* Prop = nullptr; FProperty* Prop = nullptr;
void* Container = nullptr; void* Container = nullptr;
// Construct a property reference.
//
FWingProperty(FProperty* InProp, void* InContainer)
: Prop(InProp), Container(InContainer) {}
// Construct a null property reference.
//
FWingProperty() = default; FWingProperty() = default;
FWingProperty(FProperty* InProp, void* InContainer);
FWingProperty(FProperty* InProp, UObject* InContainer);
bool SetDouble(double D); // Operator -> forwards to the FProperty.
bool SetInt64(int64 I); //
bool SetBool(bool B); FProperty* operator->() const { return Prop; }
// Store a value. On failure, return false, and print an
// error. These will do automatic conversion of numeric
// types to other numeric types, as long as the value
// fits. They will also do text to any type, as long as
// the value parses as a value of the desired type. If
// you
//
bool SetObject(UObject *Obj, WingOut Errors) const;
bool SetDouble(double D, WingOut Errors) const;
bool SetInt64(int64 I, WingOut Errors) const;
bool SetBool(bool B, WingOut Errors) const;
bool SetText(FString Value, WingOut Errors) const;
bool SetJson(const FJsonValue &Value, WingOut Errors) const;
// Fetch a value. If an error occurs such as a type
// mismatch, returns an empty optional and prints an
// error. These will do automatic conversion of numeric
// types to other numeric types, as long as the value
// fits. They will also do any type to text, which is
// why the GetText version needs no error reporting.
//
TOptional<UObject*> GetObject(WingOut Errors) const;
TOptional<double> GetDouble(WingOut Errors) const;
TOptional<int64> GetInt64(WingOut Errors) const;
TOptional<bool> GetBool(WingOut Errors) const;
FString GetText() const; FString GetText() const;
bool SetText(FString Value);
// Store data from a json object. // Get the text, replace newlines with whitespace, and truncate to the specified maximum length.
bool SetJson(const TSharedPtr<FJsonValue> &Value); //
// Get the Category metadata.
FString GetCategory();
// Get the text, replace newlines with whitespace, and
// truncate to the specified maximum length.
FString GetTruncatedText(int32 MaxLen) const; FString GetTruncatedText(int32 MaxLen) const;
// Get the Category metadata.
//
FString GetCategory() const;
// Return true if the property is not the null property reference.
//
explicit operator bool() const { return Prop != nullptr; } explicit operator bool() const { return Prop != nullptr; }
FProperty* operator->() const { return Prop; }
// Get the raw properties of the specified object or struct.
//
// This gets the properties that are literally present in the
// specified object or struct. No special interpretation is done.
//
static TArray<FWingProperty> GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags);
// Get just the names of the properties of the specified struct/class.
//
static TArray<FName> GetNames(UStruct *US, EPropertyFlags Flags);
// Remove any properties with the specified name.
//
static void Remove(TArray<FWingProperty>& Props, FName Name);
// Move any properties with the specified name to the out array.
//
static void Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, FName Name);
// Return the "Details Panel" properties for the specified object. // Return the "Details Panel" properties for the specified object.
// //
@@ -46,49 +96,19 @@ struct FWingProperty
// //
// When editing an inherited ActorComponent, you're actually editing // When editing an inherited ActorComponent, you're actually editing
// properties that *override* the original properties of the ActorComponent. // properties that *override* the original properties of the ActorComponent.
// The mutable version, 'GetDetailsMutable', tells this function to first // The 'mutable' flag tells it whether to create overrides for these
// create the overrides, and then edit those. // properties.
//
// For a more direct "just give me the properties of this object,"
// use the GetAll function.
// //
static TArray<FWingProperty> GetDetailsMutable(UObject* Obj, EPropertyFlags Flags) static TArray<FWingProperty> GetDetails(UObject* Obj, EPropertyFlags Flags, bool Mutable);
{ return GetDetailsGeneral(Obj, Flags, true); }
static TArray<FWingProperty> GetDetailsImmutable(UObject *Obj, EPropertyFlags Flags)
{ return GetDetailsGeneral(Obj, Flags, false); }
// Get the raw properties of the specified object or struct.
//
// This gets the properties that are literally present in the
// specified object or struct. No special interpretation is done.
//
static TArray<FWingProperty> GetAll(UObject* Object, EPropertyFlags Flags);
static TArray<FWingProperty> GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags);
// Get the names of the properties in the specified class.
//
static TArray<FName> GetNames(UStruct *StructType, EPropertyFlags Flags);
// Get the named properties.
//
static TArray<FWingProperty> GetNamed(UStruct* StructType, void* Container, const TArray<FName> &Names);
// Functions to find items by name in an array of properties.
//
static TArray<FWingProperty> FindAllSubstring(const TArray<FWingProperty>& Props, const FString& Substring);
static void Remove(TArray<FWingProperty>& Props, const FString& Name);
static void Move(TArray<FWingProperty> &Out, TArray<FWingProperty> &In, const FString &Name);
// Functions to populate properties from a JSON object. // Functions to populate properties from a JSON object.
// //
static bool PopulateFromJson(FWingProperty& Prop, const FJsonObject* Json, bool AllOptional = false); static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json,
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonObject* Json, bool AllOptional = false); bool AllOptional, WingOut Errors);
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object);
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object);
private: private:
static TArray<FWingProperty> GetDetailsGeneral(UObject* Obj, EPropertyFlags Flags, bool Mutable); static void GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props);
void PrintExpectsReceived(const TCHAR *Type); static bool IsPinTypeProperty(FProperty *Prop);
static void Collect(UStruct* Struct, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags); void PrintExpectsReceived(const TCHAR *Type, WingOut Errors) const;
bool CheckImportTextResult(const FString &Value); bool CheckImportTextResult(const FString &Value, WingOut Errors) const;
}; };

View File

@@ -50,20 +50,6 @@ public:
/** Track an object that has been modified by the current handler. */ /** Track an object that has been modified by the current handler. */
static void AddTouchedObject(UObject* Obj) { GWingServer->Notifier.AddTouchedObject(Obj); } static void AddTouchedObject(UObject* Obj) { GWingServer->Notifier.AddTouchedObject(Obj); }
/** Print text to the handler output buffer. */
static void Print(const TCHAR* Text) { GWingServer->HandlerOutput.Append(Text); }
static void Print(const FString& Text) { GWingServer->HandlerOutput.Append(Text); }
/** Print formatted text to the handler output buffer. */
template <typename FmtType, typename... ArgTypes>
static void Printf(const FmtType& Fmt, ArgTypes&&... Args)
{
GWingServer->HandlerOutput.Appendf(Fmt, Forward<ArgTypes>(Args)...);
}
/** Get the handler output buffer directly. */
static FStringBuilderBase& GetPrintBuffer() { return GWingServer->HandlerOutput; }
/** Suggest that a manual section be printed after the handler finishes. */ /** Suggest that a manual section be printed after the handler finishes. */
static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); } static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); }
@@ -84,7 +70,6 @@ private:
UPROPERTY() UPROPERTY()
FWingNotifier Notifier; FWingNotifier Notifier;
FWingHandlerConfig* LastHandler; FWingHandlerConfig* LastHandler;
TStringBuilder<16384> HandlerOutput;
TSet<WingManual::Section> SuggestedManualSections; TSet<WingManual::Section> SuggestedManualSections;
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
TArray<FWingHandlerConfig> WingHandlerRegistry; // sorted by Name TArray<FWingHandlerConfig> WingHandlerRegistry; // sorted by Name

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "EditorSubsystem.h" #include "EditorSubsystem.h"
#include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphPin.h"
#include "WingHandler.h"
#include "WingTypes.generated.h" #include "WingTypes.generated.h"
struct WingTokenizer; struct WingTokenizer;
@@ -117,16 +118,16 @@ private:
// Parser and ResolveShortName // Parser and ResolveShortName
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message); static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message, WingOut Errors);
static bool ParseChar(WingTokenizer& Tok, TCHAR c); static bool ParseChar(WingTokenizer& Tok, TCHAR c, WingOut Errors);
bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType); bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType); bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType, WingOut Errors);
bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType); bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType); bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType); bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType); bool ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType); bool ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGraphPinType &OutType, WingOut Errors);
public: public:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -146,21 +147,21 @@ public:
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the // Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. If CheckEOF is true, // requirements, prints an error and returns false. If CheckEOF is true,
// makes sure that the input is *just* a type and nothing else after it. // makes sure that the input is *just* a type and nothing else after it.
static bool TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF); static bool TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, const Requirements &Require, bool CheckEOF, WingOut Errors);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the // Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. // requirements, prints an error and returns false.
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require); static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require, WingOut Errors);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the // Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. Requires that the type // requirements, prints an error and returns false. Requires that the type
// is an object type (even if that's not specified in the requirements struct). // is an object type (even if that's not specified in the requirements struct).
static UClass* TextToOneObjectType(const FString& Text, const Requirements &Require); static UClass* TextToOneObjectType(const FString& Text, const Requirements &Require, WingOut Errors);
// Parse a type. If it doesn't parse, or if the type doesn't satisfy the // Parse a type. If it doesn't parse, or if the type doesn't satisfy the
// requirements, prints an error and returns false. Requires that the type // requirements, prints an error and returns false. Requires that the type
// is an interface type (even if that's not specified in the requirements struct). // is an interface type (even if that's not specified in the requirements struct).
static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require); static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require, WingOut Errors);
private: private:
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@@ -15,6 +15,7 @@
#include "Components/Widget.h" #include "Components/Widget.h"
#include "WingActorComponent.h" #include "WingActorComponent.h"
#include "WingVariables.h" #include "WingVariables.h"
#include "WingHandler.h"
struct FEdGraphSchemaAction; struct FEdGraphSchemaAction;
class UAnimationStateMachineGraph; class UAnimationStateMachineGraph;
@@ -104,53 +105,53 @@ public:
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
template<typename ArrayType> template<typename ArrayType>
static auto FindOneWithInternalID(FName InternalID, ArrayType &&Array, const TCHAR *Kind) static auto FindOneWithInternalID(FName InternalID, ArrayType &&Array, const TCHAR *Kind, WingOut Errors)
{ {
decltype(EltAsPtr(Array, Array[0])) Result = nullptr; decltype(EltAsPtr(Array, Array[0])) Result = nullptr;
int Count = 0; int Count = 0;
for (auto &Elt : Array) if (GetFName(Elt) == InternalID) { Count++; Result = EltAsPtr(Array, Elt); } for (auto &Elt : Array) if (GetFName(Elt) == InternalID) { Count++; Result = EltAsPtr(Array, Elt); }
if (!CheckExactlyOneNamed(Count, Kind, InternalID)) Result = nullptr; if (!CheckExactlyOneNamed(Count, Kind, InternalID, Errors)) Result = nullptr;
return Result; return Result;
} }
template<typename ArrayType> template<typename ArrayType>
static auto FindOneWithExternalID(const FString &ExternalID, ArrayType &&Array, const TCHAR *Kind) static auto FindOneWithExternalID(const FString &ExternalID, ArrayType &&Array, const TCHAR *Kind, WingOut Errors)
{ {
decltype(EltAsPtr(Array, Array[0])) Result = nullptr; decltype(EltAsPtr(Array, Array[0])) Result = nullptr;
FName InternalID = CheckInternalizeID(ExternalID); FName InternalID = CheckInternalizeID(ExternalID, Errors);
if (!InternalID.IsNone()) Result = FindOneWithInternalID(InternalID, Array, Kind); if (!InternalID.IsNone()) Result = FindOneWithInternalID(InternalID, Array, Kind, Errors);
return Result; return Result;
} }
template<typename ArrayType> template<typename ArrayType>
static bool FindNoneWithInternalID(FName InternalID, ArrayType &&Array, const TCHAR *Kind) static bool FindNoneWithInternalID(FName InternalID, ArrayType &&Array, const TCHAR *Kind, WingOut Errors)
{ {
for (auto &Elt : Array) if (GetFName(Elt) == InternalID) for (auto &Elt : Array) if (GetFName(Elt) == InternalID)
return CheckExactlyNoneNamed(1, Kind, InternalID); return CheckExactlyNoneNamed(1, Kind, InternalID, Errors);
return true; return true;
} }
template<typename ArrayType> template<typename ArrayType>
static bool FindNoneWithExternalID(const FString &ExternalID, ArrayType &&Array, const TCHAR *Kind) static bool FindNoneWithExternalID(const FString &ExternalID, ArrayType &&Array, const TCHAR *Kind, WingOut Errors)
{ {
FName InternalID = CheckInternalizeID(ExternalID); FName InternalID = CheckInternalizeID(ExternalID, Errors);
if (InternalID.IsNone()) return false; if (InternalID.IsNone()) return false;
return FindNoneWithInternalID(InternalID, Array, Kind); return FindNoneWithInternalID(InternalID, Array, Kind, Errors);
} }
static bool FindNoDuplicateName(TSet<FName> &Collection, FName InternalID, const TCHAR *Kind) static bool FindNoDuplicateName(TSet<FName> &Collection, FName InternalID, const TCHAR *Kind, WingOut Errors)
{ {
if (Collection.Contains(InternalID)) if (Collection.Contains(InternalID))
{ CheckExactlyOneNamed(2, Kind, InternalID); return false; } { CheckExactlyOneNamed(2, Kind, InternalID, Errors); return false; }
Collection.Add(InternalID); Collection.Add(InternalID);
return true; return true;
} }
template<typename ArrayType> template<typename ArrayType>
static bool FindNoDuplicateNames(TSet<FName> &Collection, ArrayType &&Array, const TCHAR *Kind) static bool FindNoDuplicateNames(TSet<FName> &Collection, ArrayType &&Array, const TCHAR *Kind, WingOut Errors)
{ {
for (auto &Elt : Array) for (auto &Elt : Array)
if (!FindNoDuplicateName(Collection, GetFName(Elt), Kind)) return false; if (!FindNoDuplicateName(Collection, GetFName(Elt), Kind, Errors)) return false;
return true; return true;
} }
@@ -200,7 +201,7 @@ public:
// static FString ExternalizeID(const FString& InternalID); // static FString ExternalizeID(const FString& InternalID);
static FString ExternalizeID(FName Name); static FString ExternalizeID(FName Name);
static FName CheckInternalizeID(const FString &ExternalID); static FName CheckInternalizeID(const FString &ExternalID, WingOut Errors);
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
// In Unreal, Menu items tend to be an unpredictable // In Unreal, Menu items tend to be an unpredictable
@@ -221,13 +222,13 @@ public:
// empty string. // empty string.
//////////////////////////////////////////////////////// ////////////////////////////////////////////////////////
static FName CheckProposedName(const FString &Name); static FName CheckProposedName(const FString &Name, WingOut Errors);
static FString FormatNodeTitle(const UEdGraphNode *Node); static FString FormatNodeTitle(const UEdGraphNode *Node);
// ----- Enum helpers ----- // ----- Enum helpers -----
static FString EnumToString(UEnum* Enum, int64 Value); static FString EnumToString(UEnum* Enum, int64 Value);
static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue); static bool StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue, WingOut Errors);
template<typename T> template<typename T>
static FString EnumToString(TEnumAsByte<T> Value) static FString EnumToString(TEnumAsByte<T> Value)
@@ -238,8 +239,8 @@ public:
{ return EnumToString(StaticEnum<T>(), (int64)Value); } { return EnumToString(StaticEnum<T>(), (int64)Value); }
template<typename T> template<typename T>
static bool StringToEnum(const FString& Str, T& OutValue) static bool StringToEnum(const FString& Str, T& OutValue, WingOut Errors)
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V)) return false; OutValue = (T)V; return true; } { int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Errors)) return false; OutValue = (T)V; return true; }
// ----- Blueprint helpers ----- // ----- Blueprint helpers -----
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP); static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
@@ -258,7 +259,7 @@ public:
// ----- Anim blueprint helpers ----- // ----- Anim blueprint helpers -----
static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName); static UAnimationStateMachineGraph* FindStateMachineGraph(UBlueprint* BP, const FString& GraphName);
static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName); static UAnimStateNode* FindStateByName(UAnimationStateMachineGraph* SMGraph, const FString& StateName, WingOut Errors);
static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName); static UAnimStateTransitionNode* FindTransition(UAnimationStateMachineGraph* SMGraph, const FString& FromStateName, const FString& ToStateName);
// ----- Text formatting ----- // ----- Text formatting -----
@@ -273,11 +274,11 @@ public:
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed); static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
// ----- Common Error Reporting ----- // ----- Common Error Reporting -----
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name); static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name); static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors);
static bool CheckCanRename(UEdGraphNode* Node, const FString &Name); static bool CheckCanRename(UEdGraphNode* Node, const FString &Name, WingOut Errors);
}; };

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "EdGraph/EdGraphPin.h" #include "EdGraph/EdGraphPin.h"
#include "Engine/Blueprint.h" #include "Engine/Blueprint.h"
#include "WingHandler.h"
struct WingTokenizer; struct WingTokenizer;
class UK2Node_CustomEvent; class UK2Node_CustomEvent;
@@ -54,27 +55,27 @@ public:
void Empty() { Variables.Empty(); } void Empty() { Variables.Empty(); }
// Print the variables to a string builder. // Print the variables to a string builder.
void Print(FStringBuilderBase &Out); void Print(WingOut Out);
// Print compact: "type name,type name,..." // Print compact: "type name,type name,..."
void PrintCompact(FStringBuilderBase &Out); void PrintCompact(WingOut Out);
// Clear the BPVar and Pin fields. // Clear the BPVar and Pin fields.
void ClearLinks(); void ClearLinks();
// Check the sanity of the vars in the array. If allow // Check the sanity of the vars in the array. If allow
// is false, then no variables are allowed in the array. // is false, then no variables are allowed in the array.
bool CheckSanity(const TSet<FName> &GoodFlags, bool Allow); bool CheckSanity(const TSet<FName> &GoodFlags, bool Allow, WingOut Errors);
// Parse variables from a string. // Parse variables from a string.
bool ParseString(const FString &Input); bool ParseString(const FString &Input, WingOut Errors);
// Parse variable names only from a string. // Parse variable names only from a string.
bool ParseNamesString(const FString &Input); bool ParseNamesString(const FString &Input, WingOut Errors);
private: private:
bool ParseOneVariable(WingTokenizer &Tok, Var &V); bool ParseOneVariable(WingTokenizer &Tok, Var &V, WingOut Errors);
bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out); bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out, WingOut Errors);
}; };
class WingVariables class WingVariables
@@ -100,7 +101,7 @@ public:
// Configure the backing store. On failure, // Configure the backing store. On failure,
// prints a message and returns false. // prints a message and returns false.
bool SetBackingStore(UObject *Obj); bool SetBackingStore(UObject *Obj, WingOut Errors);
// Clear the workspace. Doesn't affect the backing store. // Clear the workspace. Doesn't affect the backing store.
@@ -112,29 +113,29 @@ public:
// Print the contents of the workspace. // Print the contents of the workspace.
void Print(FStringBuilderBase &Out); void Print(WingOut Out);
// Load: clear the workspace, then // Load: clear the workspace, then
// copy everything from the backing store into the workspace. // copy everything from the backing store into the workspace.
void Load(); void Load(WingOut Errors);
// Check: make sure the contents of the workspace makes sense // Check: make sure the contents of the workspace makes sense
// given the type of backing store. // given the type of backing store.
bool Check(); bool Check(WingOut Errors);
// Use the variables in the workspace to modify the backing store. // Use the variables in the workspace to modify the backing store.
bool Modify(); bool Modify(WingOut Errors);
// Create every variable in the workspace in the backing store. // Create every variable in the workspace in the backing store.
bool Create(); bool Create(WingOut Errors);
// Remove every variable mentioned in the workspace from the backing store. // Remove every variable mentioned in the workspace from the backing store.
bool Remove(); bool Remove(WingOut Errors);
private: private:
void LoadBlueprint(); void LoadBlueprint();
@@ -144,37 +145,38 @@ private:
Var LoadLocalVariableDescription(FBPVariableDescription &Desc); Var LoadLocalVariableDescription(FBPVariableDescription &Desc);
void LoadEditablePinBase(UK2Node_EditablePinBase *Node, WingVariableList &List); void LoadEditablePinBase(UK2Node_EditablePinBase *Node, WingVariableList &List);
bool CheckBlueprint(); bool CheckBlueprint(WingOut Errors);
bool CheckGraph(); bool CheckGraph(WingOut Errors);
bool ModifyBlueprint(); bool ModifyBlueprint(WingOut Errors);
bool LinkBlueprintVariables(); bool LinkBlueprintVariables(WingOut Errors);
void ModifyBlueprintVariableFlags(Var &Input); void ModifyBlueprintVariableFlags(Var &Input);
bool ModifyBlueprintDefaults(); bool ModifyBlueprintDefaults(WingOut Errors);
bool ModifyGraph(); bool ModifyGraph(WingOut Errors);
bool CreateBlueprint(); bool CreateBlueprint(WingOut Errors);
bool CreateGraph(); bool CreateGraph(WingOut Errors);
bool RemoveBlueprint(); bool RemoveBlueprint(WingOut Errors);
bool RemoveGraph(); bool RemoveGraph(WingOut Errors);
void LoadCustomEvent(); void LoadCustomEvent();
bool CheckCustomEvent(); bool CheckCustomEvent(WingOut Errors);
bool ModifyCustomEvent(); bool ModifyCustomEvent(WingOut Errors);
bool CreateCustomEvent(); bool CreateCustomEvent(WingOut Errors);
bool RemoveCustomEvent(); bool RemoveCustomEvent(WingOut Errors);
bool GetGraphNodes( bool GetGraphNodes(
UK2Node_EditablePinBase *&InputNode, UK2Node_EditablePinBase *&InputNode,
UK2Node_EditablePinBase *&OutputNode, UK2Node_EditablePinBase *&OutputNode,
UK2Node_FunctionEntry *&LocalNode); UK2Node_FunctionEntry *&LocalNode,
WingOut Errors);
bool ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node); bool ModifyEditablePinBase(WingVariableList &List, UK2Node_EditablePinBase *Node, WingOut Errors);
void AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node); void AddUserPinInfo(const Var &V, EEdGraphPinDirection Dir, UK2Node_EditablePinBase *Node);
bool ErrorNoBackingStore(); bool ErrorNoBackingStore(WingOut Errors);
}; };

View File

@@ -2,6 +2,7 @@
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Components/Widget.h" #include "Components/Widget.h"
#include "WingHandler.h"
class UWidgetTree; class UWidgetTree;
struct FAssetData; struct FAssetData;
@@ -24,7 +25,7 @@ public:
TArray<Type> Search(const FString& Query, int32 MaxResults, bool Exact); TArray<Type> Search(const FString& Query, int32 MaxResults, bool Exact);
// Check if a widget can accept children. Prints error if not. // Check if a widget can accept children. Prints error if not.
static bool CheckCanBeParent(UWidget* Widget); static bool CheckCanBeParent(UWidget* Widget, WingOut Errors);
// Print out a tree of widgets, just showing the names and types. // Print out a tree of widgets, just showing the names and types.
static void PrintWidgetTree(UWidget* Widget, int32 Depth); static void PrintWidgetTree(UWidget* Widget, int32 Depth);