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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check that the proposed name is valid
FName InternalID = WingUtils::CheckProposedName(Component);
FName InternalID = WingUtils::CheckProposedName(Component, WingOut::Stdout);
if (InternalID.IsNone()) return;
TSet<FName> 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
UWingTypes::Requirements Req;
@@ -62,18 +62,18 @@ public:
Req.Blueprintable = false;
Req.AllowContainer = false;
Req.IsChildOf = UActorComponent::StaticClass();
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req);
UClass* ComponentClass = UWingTypes::TextToOneObjectType(Class, Req, WingOut::Stdout);
if (!ComponentClass) return;
if (!UWingComponentReference::CheckValidComponentClass(ComponentClass)) return;
if (!UWingComponentReference::CheckValidComponentClass(ComponentClass, WingOut::Stdout)) return;
// Find the specified parent component
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;
// 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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>();
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UWingComponentReference* CompRef = F.Walk(Component).Cast<UWingComponentReference>();
if (!CompRef) return;
// Find the new parent among all components (if specified)
UBlueprint *BP = CompRef->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 (!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))
{
UWingServer::Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename);
WingOut::Stdout.Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename);
return;
}
@@ -43,10 +43,10 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*BackupFilename, *Filename, true);
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;
}
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();
for (const FString &Result : Results) UWingServer::Print(Result);
for (const FString &Result : Results) WingOut::Stdout.Print(Result);
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;
}
}

View File

@@ -44,7 +44,7 @@ public:
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;
}
@@ -60,22 +60,22 @@ public:
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)
{
FString RefStr = Ref.ToString();
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)"));
}
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;
}
// Force delete: unload the package from memory first
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
@@ -109,7 +109,7 @@ public:
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;
}
@@ -122,6 +122,6 @@ public:
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));
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;
}
@@ -44,7 +44,7 @@ public:
if (Referencers.Num() == 0)
{
UWingServer::Print(TEXT("No referencers found.\n"));
WingOut::Stdout.Print(TEXT("No referencers found.\n"));
return;
}
@@ -56,13 +56,13 @@ public:
Registry.GetAssetsByPackageName(Ref, RefAssets);
if (RefAssets.Num() > 0)
{
UWingServer::Printf(TEXT("%s %s\n"),
WingOut::Stdout.Printf(TEXT("%s %s\n"),
*WingUtils::FormatName(RefAssets[0].GetClass()),
*RefStr);
}
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
{
// Load the asset
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UObject* AssetObj = F.Asset(Asset).GetObj();
if (!AssetObj) return;
@@ -48,7 +48,7 @@ public:
NewAssetName = NewPath;
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;
}
}
@@ -62,10 +62,10 @@ public:
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;
}
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))
{
UWingServer::Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename);
WingOut::Stdout.Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename);
return;
}
@@ -52,7 +52,7 @@ public:
uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true);
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;
}
@@ -64,12 +64,12 @@ public:
UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive);
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());
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())
{
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;
}
@@ -55,7 +55,7 @@ public:
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req);
UClass* TypeClass = UWingTypes::TextToOneObjectType(Type, Req, WingOut::Stdout);
if (!TypeClass) return;
Filter.ClassPaths.Add(TypeClass->GetClassPathName());
}
@@ -81,18 +81,18 @@ public:
for (const FAssetData& Data : Results)
{
UWingServer::Printf(TEXT("%s %s\n"),
WingOut::Stdout.Printf(TEXT("%s %s\n"),
*WingUtils::FormatName(Data.GetClass()),
*Data.PackageName.ToString());
}
if (Results.Num() == 0)
{
UWingServer::Print(TEXT("No assets found.\n"));
WingOut::Stdout.Print(TEXT("No assets found.\n"));
}
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"))
{
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;
}
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check that this graph type is valid for this blueprint type
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;
}
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;
}
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;
}
// Check graph name uniqueness and legality
FName InternalID = WingUtils::CheckProposedName(Graph);
FName InternalID = WingUtils::CheckProposedName(Graph, WingOut::Stdout);
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;
// Parse and validate variables before making changes
WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables, WingOut::Stdout)) return;
// Create the Graph
UEdGraph* NewGraph = FBlueprintEditorUtils::CreateNewGraph(BP, InternalID,
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
if (!NewGraph)
{
UWingServer::Print(TEXT("ERROR: Failed to create graph\n"));
WingOut::Stdout.Print(TEXT("ERROR: Failed to create graph\n"));
return;
}
@@ -103,10 +103,10 @@ public:
}
// Create the variables on the new graph
if (!Vars.SetBackingStore(NewGraph)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
if (!Vars.SetBackingStore(NewGraph, WingOut::Stdout)) return;
if (!Vars.Check(WingOut::Stdout)) 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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* FoundGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!FoundGraph) return;
UBlueprint* BP = FBlueprintEditorUtils::FindBlueprintForGraph(FoundGraph);
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;
}
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;
}
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return;
@@ -43,7 +43,7 @@ public:
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req);
UClass* InterfaceClass = UWingTypes::TextToOneInterfaceType(Interface, Req, WingOut::Stdout);
if (!InterfaceClass) return;
// Check for duplicates
@@ -51,7 +51,7 @@ public:
{
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));
return;
}
@@ -61,19 +61,19 @@ public:
bool bAdded = FBlueprintEditorUtils::ImplementNewInterface(BP, InterfacePath);
if (!bAdded)
{
UWingServer::Printf(TEXT("ERROR: ImplementNewInterface failed for '%s'.\n"),
WingOut::Stdout.Printf(TEXT("ERROR: ImplementNewInterface failed for '%s'.\n"),
*WingUtils::FormatName(InterfaceClass));
return;
}
// 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)
{
if (IfaceDesc.Interface != InterfaceClass) continue;
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;
}

View File

@@ -38,7 +38,7 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return;
@@ -47,7 +47,7 @@ public:
Req.BlueprintType = false;
Req.Blueprintable = false;
Req.AllowContainer = false;
UClass* FoundInterface = UWingTypes::TextToOneInterfaceType(Interface, Req);
UClass* FoundInterface = UWingTypes::TextToOneInterfaceType(Interface, Req, WingOut::Stdout);
if (!FoundInterface) return;
// Verify this blueprint actually implements it
@@ -58,7 +58,7 @@ public:
}
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));
return;
}
@@ -66,8 +66,8 @@ public:
FTopLevelAssetPath InterfacePath = FoundInterface->GetClassPathName();
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)
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return;
@@ -48,11 +48,11 @@ public:
{
if (!Node->bHasCompilerMessage) continue;
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()),
*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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Header
UWingServer::Printf(TEXT("Blueprint: %s\n"), *WingUtils::FormatName(BP));
UWingServer::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("Blueprint: %s\n"), *WingUtils::FormatName(BP));
WingOut::Stdout.Printf(TEXT("Parent: %s\n"), BP->ParentClass ? *WingUtils::FormatName(BP->ParentClass) : TEXT("None"));
WingOut::Stdout.Printf(TEXT("Type: %s\n"), *WingUtils::EnumToString(BP->BlueprintType));
// Animation Blueprint
if (UAnimBlueprint* AnimBP = Cast<UAnimBlueprint>(BP))
{
if (AnimBP->TargetSkeleton)
UWingServer::Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName());
WingOut::Stdout.Printf(TEXT("TargetSkeleton: %s\n"), *AnimBP->TargetSkeleton->GetPathName());
}
// Interfaces
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
WingVariables BlueprintVars;
BlueprintVars.SetBackingStore(BP);
BlueprintVars.Load();
BlueprintVars.Print(UWingServer::GetPrintBuffer());
BlueprintVars.SetBackingStore(BP, WingOut::Stdout);
BlueprintVars.Load(WingOut::Stdout);
BlueprintVars.Print(WingOut::StdoutBuffer);
// Components
TArray<UWingComponentReference*> Components3 = UWingComponentReference::GetAll(BP);
if (!Components3.IsEmpty())
{
UWingServer::Print(TEXT("\nComponents:\n"));
WingOut::Stdout.Print(TEXT("\nComponents:\n"));
for (const UWingComponentReference* Ref : Components3)
{
UWingServer::Printf(TEXT(" %s %s"),
WingOut::Stdout.Printf(TEXT(" %s %s"),
*Ref->TypeName,
*WingUtils::FormatName(Ref));
if (!Ref->ParentName.IsEmpty())
UWingServer::Printf(TEXT(" [parent: %s]"), *Ref->ParentName);
WingOut::Stdout.Printf(TEXT(" [parent: %s]"), *Ref->ParentName);
if (Ref->Inherited)
UWingServer::Print(TEXT(" [inherited]"));
UWingServer::Print(TEXT("\n"));
WingOut::Stdout.Print(TEXT(" [inherited]"));
WingOut::Stdout.Print(TEXT("\n"));
}
}
// Widget Tree
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);
}
// Event Dispatchers
if (!BP->DelegateSignatureGraphs.IsEmpty())
{
UWingServer::Print(TEXT("\nEvent Dispatchers:\n"));
WingOut::Stdout.Print(TEXT("\nEvent Dispatchers:\n"));
for (UEdGraph* Graph : BP->DelegateSignatureGraphs)
PrintEventDispatcher(Graph);
}
@@ -106,10 +106,10 @@ public:
// Graphs
TSet<UEdGraph*> Printed;
UWingServer::Print(TEXT("\nGraphs:\n"));
WingOut::Stdout.Print(TEXT("\nGraphs:\n"));
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);
}
for (UEdGraph* Graph : BP->FunctionGraphs)
@@ -147,7 +147,7 @@ public:
Printed.Add(Graph);
}
if (!AnimGraphNames.IsEmpty())
UWingServer::Printf(TEXT("\nAnimation Graphs: %s\n"), *AnimGraphNames);
WingOut::Stdout.Printf(TEXT("\nAnimation Graphs: %s\n"), *AnimGraphNames);
FString StateMachineNames;
for (UEdGraph* Graph : AllGraphs)
@@ -159,13 +159,13 @@ public:
Printed.Add(Graph);
}
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.
for (UEdGraph* Graph : AllGraphs)
{
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->GetSchema()->GetClass()));
}
@@ -175,22 +175,21 @@ private:
void PrintEventDispatcher(UEdGraph* Graph)
{
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(WingOut::Stdout);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer();
Out.Appendf(TEXT(" %s("), *WingUtils::FormatName(Graph));
Vars.InputVariables.PrintCompact(Out);
Out.Append(TEXT(")\n"));
WingOut::Stdout.Printf(TEXT(" %s("), *WingUtils::FormatName(Graph));
Vars.InputVariables.PrintCompact(WingOut::Stdout);
WingOut::Stdout.Printf(TEXT(")\n"));
}
void PrintGraph(UEdGraph* Graph, const TCHAR* Type, UClass* Interface = nullptr)
{
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(WingOut::Stdout);
FStringBuilderBase &Out = UWingServer::GetPrintBuffer();
FStringBuilderBase &Out = WingOut::StdoutBuffer;
Out.Appendf(TEXT(" %s %s"), Type, *WingUtils::FormatName(Graph));
Out.AppendChar('(');
Vars.InputVariables.PrintCompact(Out);

View File

@@ -36,7 +36,7 @@ public:
virtual void Handle() override
{
// Load Blueprint
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Asset(Blueprint).Cast<UBlueprint>();
if (!BP) return;
@@ -45,13 +45,13 @@ public:
Req.BlueprintType = false;
Req.Blueprintable = true;
Req.AllowContainer = false;
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req);
UClass* NewParentClassObj = UWingTypes::TextToOneObjectType(Parent, Req, WingOut::Stdout);
if (!NewParentClassObj) return;
// Validate reparent
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));
return;
}
@@ -61,7 +61,7 @@ public:
FBlueprintEditorUtils::RefreshAllNodes(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));
}
};

View File

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

View File

@@ -18,9 +18,6 @@ public:
UPROPERTY(EditAnywhere, meta=(Description="Target object"))
FString Object;
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
FString Query;
virtual void Register() override
{
UWingServer::AddHandler(this,
@@ -29,12 +26,11 @@ public:
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UObject* Target = F.Walk(Object).Cast<UObject>();
if (!Target) return;
TArray<FWingProperty> Props = FWingProperty::GetDetailsImmutable(Target, CPF_Edit);
Props = FWingProperty::FindAllSubstring(Props, Query);
TArray<FWingProperty> Props = FWingProperty::GetDetails(Target, CPF_Edit, false);
// Group by category, preserving within-category order.
TSortedMap<FString, TArray<FWingProperty>> Categories;
@@ -45,14 +41,13 @@ public:
Categories.FindOrAdd(Category).Add(P);
}
FStringBuilderBase& Out = UWingServer::GetPrintBuffer();
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)
{
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"),
*UWingTypes::TypeToText(P.Prop),
*WingUtils::FormatName(P.Prop),

View File

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

View File

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

View File

@@ -28,22 +28,22 @@ public:
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
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;
}
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.
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;
}
@@ -51,10 +51,10 @@ public:
int SuccessCount = 0;
for (const auto& Pair : Properties.Json->Values)
{
FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property"));
if (P->SetJson(Pair.Value)) SuccessCount++;
FWingProperty* P = WingUtils::FindOneWithExternalID(Pair.Key, Props, TEXT("Property"), WingOut::Stdout);
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>();
if (!Sub)
{
UWingServer::Print(TEXT("Error: AssetEditorSubsystem not available\n"));
WingOut::Stdout.Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return;
}
TArray<UObject*> EditedAssets = Sub->GetAllEditedAssets();
if (EditedAssets.IsEmpty())
{
UWingServer::Print(TEXT("No asset editors are open.\n"));
WingOut::Stdout.Print(TEXT("No asset editors are open.\n"));
return;
}
@@ -43,7 +43,7 @@ public:
{
bool bDirty = Asset->GetOutermost()->IsDirty();
UWingServer::Printf(TEXT(" %s%s\n"),
WingOut::Stdout.Printf(TEXT(" %s%s\n"),
bDirty ? TEXT("[unsaved] ") : TEXT(""),
*Asset->GetPathName());
}

View File

@@ -29,20 +29,20 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Asset).Cast<UObject>();
if (!Obj) return;
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
if (!Sub)
{
UWingServer::Print(TEXT("Error: AssetEditorSubsystem not available\n"));
WingOut::Stdout.Print(TEXT("Error: AssetEditorSubsystem not available\n"));
return;
}
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
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
if (!BP) return;
// Check for valid proposed name
FName InternalID = WingUtils::CheckProposedName(Dispatcher);
FName InternalID = WingUtils::CheckProposedName(Dispatcher, WingOut::Stdout);
if (InternalID.IsNone()) return;
TSet<FName> 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.
WingVariables Vars;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
// Add the delegate variable
FEdGraphPinType DelegateType;
DelegateType.PinCategory = UEdGraphSchema_K2::PC_MCDelegate;
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;
}
@@ -67,7 +67,7 @@ public:
UEdGraph::StaticClass(), UEdGraphSchema_K2::StaticClass());
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;
}
@@ -79,9 +79,9 @@ public:
BP->DelegateSignatureGraphs.Add(SigGraph);
// Store the function arguments
if (!Vars.SetBackingStore(SigGraph)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Created event dispatcher %s in %s\n"), *Dispatcher, *WingUtils::FormatName(BP));
if (!Vars.SetBackingStore(SigGraph, WingOut::Stdout)) return;
if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Create(WingOut::Stdout)) return;
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
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;
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;
UEdGraph* Graph = *SigGraph;
@@ -51,6 +51,6 @@ public:
// Remove the signature graph
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:
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return;
@@ -49,15 +49,15 @@ private:
if (WingToolMenu::Execute(Entry, Context))
{
UWingServer::Printf(TEXT("Executed: %s\n"), *LabelText);
WingOut::Stdout.Printf(TEXT("Executed: %s\n"), *LabelText);
}
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;
}
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()
int32 PosY = 0;
FWingGraphAction *Action;
};
@@ -53,50 +55,40 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return;
int32 SuccessCount = 0;
int32 TotalCount = Nodes.Array.Num();
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(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
continue;
// Find the action by exact full name
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) return;
TArray<FWingGraphAction*> Results = GraphActions.Search(Entry.ActionName, 2, true);
if (Results.Num() == 0)
{
UWingServer::Printf(TEXT("ERROR: No action found matching '%s'. Use GraphNode_SearchTypes to find available actions.\n"),
*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++;
if (!WingUtils::CheckExactlyOneNamed(Results.Num(), TEXT("node type"), Entry.ActionName, WingOut::Stdout)) return;
Entry.Action = Results[0];
Entries.Add(Entry);
}
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return;
@@ -44,7 +44,7 @@ public:
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);
return;
}
@@ -63,6 +63,6 @@ public:
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:
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return;
WingGraphExport Exporter(NodeObj);
UWingServer::Print(*Exporter.GetOutput());
UWingServer::Print(Exporter.GetDetails());
WingOut::Stdout.Print(*Exporter.GetOutput());
WingOut::Stdout.Print(Exporter.GetDetails());
}
};

View File

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

View File

@@ -33,13 +33,13 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return;
if (!WingUtils::CheckCanRename(FoundNode, Name)) return;
if (!WingUtils::CheckCanRename(FoundNode, Name, WingOut::Stdout)) return;
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return;
@@ -47,16 +47,16 @@ public:
TArray<FWingGraphAction*> Results = GraphActions.Search(Query, MaxResults, false);
for (const FWingGraphAction* Action : Results)
{
UWingServer::Printf(TEXT("%s\n"), *Action->Name);
WingOut::Stdout.Printf(TEXT("%s\n"), *Action->Name);
}
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)
{
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
if (!FoundNode) return;
@@ -41,6 +41,6 @@ public:
FoundNode->bCommentBubbleVisible = !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)
{
WingFetcher F(GraphObj);
WingFetcher F(GraphObj, WingOut::Stdout);
UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
if (!Pin) return;
@@ -63,7 +63,7 @@ public:
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;
}
@@ -74,7 +74,7 @@ public:
FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
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;
}
UWingServer::AddTouchedObject(Node);
@@ -87,17 +87,17 @@ public:
void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj)
{
WingFetcher F(GraphObj);
WingFetcher F(GraphObj, WingOut::Stdout);
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
if (!Node) return;
TArray<FWingProperty> All = FWingProperty::GetDetailsMutable(Node, CPF_Edit);
FWingProperty *P = WingUtils::FindOneWithExternalID(Entry.Name, All, TEXT("Property"));
TArray<FWingProperty> All = FWingProperty::GetDetails(Node, CPF_Edit, true);
FWingProperty *P = WingUtils::FindOneWithExternalID(Entry.Name, All, TEXT("Property"), WingOut::Stdout);
if (!P) return;
UWingServer::AddTouchedObject(Node);
if (!P->SetText(Entry.Value))
if (!P->SetText(Entry.Value, WingOut::Stdout))
return;
}
@@ -106,7 +106,7 @@ public:
virtual void Handle() override
{
// Fetch the graph once.
WingFetcher GraphFetcher;
WingFetcher GraphFetcher(WingOut::Stdout);
UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
if (!GraphObj) return;
@@ -116,22 +116,19 @@ public:
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;
}
FSetNodeDefaultEntry Entry;
TArray<FWingProperty> Props = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
{
FSetNodeDefaultEntry Entry;
if (!FWingProperty::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
continue;
if (K2Schema)
HandleK2Entry(Entry, GraphObj, K2Schema);
else if (MGSchema)
HandleMaterialEntry(Entry, GraphObj);
if (!FWingProperty::PopulateFromJson(Props, *PinVal, false, WingOut::Stdout)) continue;
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 "WingHandler.h"
#include "WingFetcher.h"
#include "WingProperty.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "EdGraph/EdGraphNode.h"
@@ -49,26 +50,25 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
if (!TargetGraph) return;
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(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue;
WingFetcher FN(TargetGraph);
if (!FWingProperty::PopulateFromJson(Props, *Elt, false, WingOut::Stdout)) continue;
WingFetcher FN(TargetGraph, WingOut::Stdout);
UEdGraphNode* Node = FN.Node(Entry.Node).Cast<UEdGraphNode>();
if (!Node) continue;
Node->NodePosX = Entry.X;
Node->NodePosY = Entry.Y;
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:
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
if (!NodeObj) return;
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;
}
FToolMenuContext Context;
@@ -45,8 +45,8 @@ private:
for (FToolMenuEntry &Entry : Entries)
{
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
int32 SuccessCount = 0;
int32 TotalCount = Connections.Array.Num();
FConnectPinsEntry Entry;
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
{
FConnectPinsEntry Entry;
if (!FWingProperty::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
if (!FWingProperty::PopulateFromJson(EntryProps, *ConnVal, false, WingOut::Stdout))
continue;
WingFetcher FS(G);
WingFetcher FS(G, WingOut::Stdout);
UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast<UEdGraphPin>();
if (!SourcePin) continue;
WingFetcher FT(G);
WingFetcher FT(G, WingOut::Stdout);
UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
if (!TargetPin) continue;
@@ -74,7 +75,7 @@ public:
const FPinConnectionResponse Response = Schema->CanCreateConnection(SourcePin, TargetPin);
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(TargetPin->GetOwningNode()), *WingUtils::FormatName(TargetPin),
*Response.Message.ToString());
@@ -85,6 +86,6 @@ public:
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
int32 SuccessCount = 0;
int32 TotalDisconnected = 0;
FDisconnectPinEntry Entry;
TArray<FWingProperty> EntryProps = FWingProperty::GetAll(&Entry, CPF_None);
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
{
FDisconnectPinEntry Entry;
if (!FWingProperty::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
if (!FWingProperty::PopulateFromJson(EntryProps, *DiscVal, false, WingOut::Stdout)) continue;
WingFetcher FP(G);
WingFetcher FP(G, WingOut::Stdout);
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
if (!Pin) continue;
@@ -69,13 +70,13 @@ public:
if (!Entry.TargetPin.IsEmpty())
{
WingFetcher FT(G);
WingFetcher FT(G, WingOut::Stdout);
UEdGraphPin* Target = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
if (!Target) continue;
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(Target->GetOwningNode()), *WingUtils::FormatName(Target));
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,
*WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin));
SuccessCount++;
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);
}
};

View File

@@ -36,19 +36,19 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UEdGraph *G = F.Walk(Graph).Cast<UEdGraph>();
if (!G) return;
WingGraphExport Exporter(G);
UWingServer::Print(*Exporter.GetOutput());
WingOut::Stdout.Print(*Exporter.GetOutput());
if (IncludeDetails)
{
UWingServer::Print(Exporter.GetDetails());
WingOut::Stdout.Print(Exporter.GetDetails());
}
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return;
// Parse the association string.
EMaterialParameterAssociation Association;
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout))
return;
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
@@ -68,12 +68,12 @@ public:
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));
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));
}
};

View File

@@ -31,7 +31,7 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return;
@@ -42,7 +42,7 @@ public:
for (auto& [Info, Meta] : AllParams)
{
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);
}
@@ -51,7 +51,7 @@ public:
for (auto& [Info, Meta] : AllParams)
{
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);
}
}

View File

@@ -44,13 +44,13 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
if (!MI) return;
// Parse the association string.
EMaterialParameterAssociation Association;
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association, WingOut::Stdout))
return;
// Build the parameter ID to look up.
@@ -61,13 +61,13 @@ public:
FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
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);
return;
}
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;
}
@@ -80,7 +80,7 @@ public:
float 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;
}
MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue);
@@ -91,17 +91,17 @@ public:
FLinearColor Color;
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;
}
MI->SetVectorParameterValueEditorOnly(ParamID, Color);
break;
}
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;
}
UWingServer::Printf(TEXT("Set '%s' = %s on %s\n"),
WingOut::Stdout.Printf(TEXT("Set '%s' = %s on %s\n"),
*Parameter, *Value, *WingUtils::FormatName(MI));
}
};

View File

@@ -30,7 +30,7 @@ public:
virtual void Handle() override
{
// Load material
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UMaterial* MaterialObj = F.Asset(Material).Cast<UMaterial>();
if (!MaterialObj) return;
@@ -48,14 +48,14 @@ public:
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
{
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)
{
UWingServer::Printf(TEXT(" %s\n"), *Err);
WingOut::Stdout.Printf(TEXT(" %s\n"), *Err);
}
}
}

View File

@@ -30,7 +30,7 @@ public:
}
virtual void Handle() override
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UMaterial* Mat = F.Asset(Material).Cast<UMaterial>();
if (!Mat) return;
@@ -40,6 +40,6 @@ public:
{
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 (!PrevGroup.IsEmpty())
UWingServer::Print(TEXT("\n"));
WingOut::Stdout.Print(TEXT("\n"));
PrevGroup = Group;
}
}
@@ -62,7 +62,7 @@ public:
}
if (Kind == EWingHandlerKind::Normal)
{
UWingServer::Printf(TEXT(
WingOut::Stdout.Printf(TEXT(
"\n"
"You can also use ShowCommands with Kind=Create to see commands that create new assets.\n"
"\n"));

View File

@@ -51,7 +51,7 @@ public:
Results.Sort();
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
{
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
{
WingTokenizer T(Input);
T.PrintEverything(UWingServer::GetPrintBuffer());
T.PrintEverything(WingOut::StdoutBuffer);
}
};

View File

@@ -33,22 +33,22 @@ public:
Require.Blueprintable = false;
Require.AllowContainer = true;
bool OK = UWingTypes::TextToType(Input, PinType, Require);
bool OK = UWingTypes::TextToType(Input, PinType, Require, WingOut::Stdout);
auto& Out = UWingServer::GetPrintBuffer();
Out.Appendf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED"));
Out.Appendf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString());
Out.Appendf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString());
Out.Appendf(TEXT("PinSubCategoryObject: %s\n"),
auto& Out = WingOut::Stdout;
Out.Printf(TEXT("ParseResult: %s\n"), OK ? TEXT("OK") : TEXT("FAILED"));
Out.Printf(TEXT("PinCategory: %s\n"), *PinType.PinCategory.ToString());
Out.Printf(TEXT("PinSubCategory: %s\n"), *PinType.PinSubCategory.ToString());
Out.Printf(TEXT("PinSubCategoryObject: %s\n"),
PinType.PinSubCategoryObject.IsValid()
? *PinType.PinSubCategoryObject->GetPathName()
: TEXT("(none)"));
Out.Appendf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType);
Out.Printf(TEXT("ContainerType: %d\n"), (int32)PinType.ContainerType);
if (PinType.IsMap())
{
Out.Appendf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString());
Out.Appendf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString());
Out.Appendf(TEXT("ValueTerminalSubCategoryObject: %s\n"),
Out.Printf(TEXT("ValueTerminalCategory: %s\n"), *PinType.PinValueType.TerminalCategory.ToString());
Out.Printf(TEXT("ValueTerminalSubCategory: %s\n"), *PinType.PinValueType.TerminalSubCategory.ToString());
Out.Printf(TEXT("ValueTerminalSubCategoryObject: %s\n"),
PinType.PinValueType.TerminalSubCategoryObject.IsValid()
? *PinType.PinValueType.TerminalSubCategoryObject->GetPathName()
: TEXT("(none)"));
@@ -57,7 +57,7 @@ public:
if (OK)
{
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);
if (!Error.IsEmpty())
{
UWingServer::Printf(TEXT("Error: %s\n"), *Error);
WingOut::Stdout.Printf(TEXT("Error: %s\n"), *Error);
}
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];
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
UWingServer::Printf(TEXT("%s (%s)\n"), *Info.Short, *BroadCategory(Info));
WingOut::Stdout.Printf(TEXT("%s (%s)\n"), *Info.Short, *BroadCategory(Info));
}
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
{
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UObject* Obj = F.Walk(Object).Cast<UObject>();
if (!Obj) return;
WingVariables Vars;
if (!Vars.SetBackingStore(Obj)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables)) return;
if (!Vars.InputVariables.ParseString(InputVariables)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables)) return;
if (!Vars.Check()) return;
if (!Vars.Create()) return;
UWingServer::Printf(TEXT("Success.\n"));
if (!Vars.SetBackingStore(Obj, WingOut::Stdout)) return;
if (!Vars.BlueprintVariables.ParseString(BlueprintVariables, WingOut::Stdout)) return;
if (!Vars.InputVariables.ParseString(InputVariables, WingOut::Stdout)) return;
if (!Vars.OutputVariables.ParseString(OutputVariables, WingOut::Stdout)) return;
if (!Vars.LocalVariables.ParseString(LocalVariables, WingOut::Stdout)) return;
if (!Vars.Check(WingOut::Stdout)) return;
if (!Vars.Create(WingOut::Stdout)) return;
WingOut::Stdout.Printf(TEXT("Success.\n"));
}
};

View File

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

View File

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

View File

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

View File

@@ -48,7 +48,7 @@ public:
virtual void Handle() override
{
// Fetch the widget blueprint.
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UWidgetBlueprint* BP = F.Walk(Blueprint).Cast<UWidgetBlueprint>();
if (!BP) return;
UWidgetTree* Tree = BP->WidgetTree;
@@ -56,18 +56,18 @@ public:
// Resolve the widget type.
WingWidgets WidgetMenu;
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.
UClass* WidgetClass = TypeResults[0].Class.LoadSynchronous();
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;
}
// Validate the proposed name.
FName InternalID = WingUtils::CheckProposedName(Name);
FName InternalID = WingUtils::CheckProposedName(Name, WingOut::Stdout);
if (InternalID.IsNone()) return;
// Check that the name is unique among existing widgets.
@@ -75,23 +75,23 @@ public:
Tree->GetAllWidgets(AllWidgets);
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (!WingUtils::FindNoDuplicateNames(Names, AllWidgets, TEXT("widget or variable"))) return;
if (!WingUtils::FindNoDuplicateName(Names, InternalID, 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"), WingOut::Stdout)) return;
// If a parent is specified, find it and verify it's a panel.
UPanelWidget* ParentPanel = nullptr;
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 (!WingWidgets::CheckCanBeParent(ParentWidget)) return;
if (!WingWidgets::CheckCanBeParent(ParentWidget, WingOut::Stdout)) return;
ParentPanel = Cast<UPanelWidget>(ParentWidget);
}
else
{
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;
}
}
@@ -105,7 +105,7 @@ public:
if (!NewWidget)
{
UWingServer::Printf(TEXT("ERROR: Failed to create widget\n"));
WingOut::Stdout.Printf(TEXT("ERROR: Failed to create widget\n"));
return;
}
@@ -123,6 +123,6 @@ public:
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
{
// Walk to the widget.
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>();
if (!TargetWidget) return;
@@ -42,7 +42,7 @@ public:
UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>();
if (!BP)
{
UWingServer::Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
WingOut::Stdout.Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
return;
}
UWidgetTree* Tree = BP->WidgetTree;
@@ -52,7 +52,7 @@ public:
{
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());
return;
}
@@ -80,6 +80,6 @@ public:
// Rename to transient package to avoid name conflicts with future widgets.
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
{
// Walk to the widget.
WingFetcher F;
WingFetcher F(WingOut::Stdout);
UWidget* TargetWidget = F.Walk(Widget).Cast<UWidget>();
if (!TargetWidget) return;
@@ -44,7 +44,7 @@ public:
UWidgetBlueprint* BP = TargetWidget->GetTypedOuter<UWidgetBlueprint>();
if (!BP)
{
UWingServer::Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
WingOut::Stdout.Printf(TEXT("ERROR: Could not find owning WidgetBlueprint\n"));
return;
}
UWidgetTree* Tree = BP->WidgetTree;
@@ -56,9 +56,9 @@ public:
FString WidgetName = WingUtils::FormatName(TargetWidget);
// 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 (!WingWidgets::CheckCanBeParent(ParentWidget)) return;
if (!WingWidgets::CheckCanBeParent(ParentWidget, WingOut::Stdout)) return;
UPanelWidget* ParentPanel = Cast<UPanelWidget>(ParentWidget);
// Check for circular reparenting.
@@ -66,7 +66,7 @@ public:
{
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;
}
}
@@ -77,6 +77,6 @@ public:
// Add to new parent.
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);
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)
{
UWingServer::Print(TEXT("No matching widget types found.\n"));
WingOut::Stdout.Print(TEXT("No matching widget types found.\n"));
}
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;
}
bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponent FC)
bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponent FC, WingOut Errors)
{
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 true;
}
bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundComponent FC)
bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundComponent FC, WingOut Errors)
{
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));
return false;
}
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));
return false;
}
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));
return false;
}
return true;
}
bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC)
bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC, WingOut Errors)
{
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));
return false;
}
return true;
}
bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Action)
bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Action, WingOut Errors)
{
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);
return false;
}
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))
{
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()));
return false;
}
return true;
}
bool UWingComponentReference::CheckValidComponentClass(UClass *Class)
bool UWingComponentReference::CheckValidComponentClass(UClass *Class, WingOut Errors)
{
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));
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;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
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));
return false;
}
FoundComponent ExistingComponent = FindComponent(BP, Name);
if (!CheckNoSuchComponent(ExistingComponent)) return false;
if (!CheckNoSuchComponent(ExistingComponent, Errors)) return false;
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false;
if (!CheckValidComponentClass(Class)) return false;
if (!CheckValidParent(ParentComponent, Errors)) return false;
if (!CheckValidComponentClass(Class, Errors)) return false;
USCS_Node *NewNode = BP->SimpleConstructionScript->CreateNode(Class, Name);
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));
return false;
}
@@ -167,18 +167,18 @@ bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingC
return true;
}
bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent)
bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent, WingOut Errors)
{
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false;
if (!CheckValidParent(ParentComponent, Errors)) return false;
FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false;
if (!CheckNotNative(ThisComponent, TEXT("reparent"))) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false;
if (!CheckExists(ThisComponent, Errors)) return false;
if (!CheckNotNative(ThisComponent, TEXT("reparent"), Errors)) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP, Errors)) return false;
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;
}
@@ -187,12 +187,12 @@ bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent)
return true;
}
bool UWingComponentReference::DeleteComponent()
bool UWingComponentReference::DeleteComponent(WingOut Errors)
{
FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false;
if (!CheckNotNative(ThisComponent, TEXT("delete"))) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false;
if (!CheckExists(ThisComponent, Errors)) return false;
if (!CheckNotNative(ThisComponent, TEXT("delete"), Errors)) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP, Errors)) return false;
BP->SimpleConstructionScript->RemoveNodeAndPromoteChildren(ThisComponent.SCS);
return true;
}

View File

@@ -5,26 +5,26 @@
#include "AssetRegistry/AssetRegistryModule.h"
#include "Kismet2/EnumEditorUtils.h"
bool WingFactories::CheckNewAssetPath(const FString& Path)
bool WingFactories::CheckNewAssetPath(const FString& Path, WingOut Errors)
{
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;
}
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;
}
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;
}
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 true;
@@ -38,24 +38,24 @@ bool WingFactories::IsBlacklisted(UClass* FactoryClass)
return false;
}
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory)
UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory, WingOut Errors)
{
// Check the blacklist.
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;
}
// 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));
// Create the package.
UPackage *Package = CreatePackage(*Path);
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;
}
@@ -82,7 +82,7 @@ UObject* WingFactories::CreateAsset(const FString& Path, UFactory* Factory)
}
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->MarkAsGarbage();
return nullptr;
@@ -109,7 +109,7 @@ UObject* UEnumFactoryWing::FactoryCreateNew(UClass* Class, UObject* InParent, FN
{
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 FEnumEditorUtils::CreateUserDefinedEnum(InParent, Name, Flags);

View File

@@ -59,11 +59,11 @@ WingFetcher& WingFetcher::SetError()
void WingFetcher::PathFailed(const TCHAR* Expected)
{
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)
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
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);
SetError();
}
@@ -71,11 +71,11 @@ void WingFetcher::PathFailed(const TCHAR* Expected)
WingFetcher& WingFetcher::TypeMismatch(const TCHAR* Walker, const TCHAR* Expected)
{
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)
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
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);
SetError();
return *this;
@@ -87,7 +87,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
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::EscapeSequences);
return SetError();
@@ -96,7 +96,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
Path.ParseIntoArray(Segments, TEXT(","));
if (Segments.Num() == 0)
{
UWingServer::Print(TEXT("ERROR: Empty path\n"));
Errors.Print(TEXT("ERROR: Empty path\n"));
UWingServer::SuggestManual(WingManual::Section::Paths);
return SetError();
}
@@ -117,7 +117,7 @@ WingFetcher& WingFetcher::Walk(const FString& Path)
WalkFunc Func = GetWalker(Key);
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);
return SetError();
}
@@ -134,7 +134,7 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
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);
return SetError();
}
@@ -144,13 +144,13 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
FString PackageName = FPackageName::ObjectPathToPackageName(PackagePath);
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();
}
SetObj(LoadObject<UObject>(nullptr, *PackagePath));
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();
}
@@ -160,13 +160,13 @@ WingFetcher& WingFetcher::Asset(const FString& PackagePath)
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
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();
}
Editor = Sub->FindEditorForAsset(OriginalAsset, false);
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();
}
@@ -184,7 +184,7 @@ bool WingFetcher::CheckAssetIsA(UClass* StaticClass)
if (bError) return false;
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"),
*StaticClass->GetName());
SetError();
@@ -202,14 +202,14 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
{
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);
return SetError();
}
WingUtils::EnsureMaterialGraph(Mat);
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();
}
SetObj(Mat->MaterialGraph);
@@ -224,13 +224,13 @@ WingFetcher& WingFetcher::Graph(const FString& Value)
}
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)
{
UWingServer::Printf(TEXT("Graphs that exist in blueprint:\n"));
Errors.Printf(TEXT("Graphs that exist in blueprint:\n"));
for (const UEdGraph *G : Graphs)
{
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(G));
Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(G));
}
return SetError();
}
@@ -253,13 +253,13 @@ WingFetcher& WingFetcher::Node(const FString& Value)
// Get the nodes from the 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)
{
UWingServer::Printf(TEXT("Nodes that exist in graph:\n"));
Errors.Printf(TEXT("Nodes that exist in graph:\n"));
for (const UEdGraphNode *N : AllNodes)
{
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(N));
Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(N));
}
return SetError();
}
@@ -277,13 +277,13 @@ WingFetcher& WingFetcher::Pin(const FString& Value)
TypeMismatch(TEXT("pin"), TEXT("node"));
return SetError();
}
UEdGraphPin *Found = WingUtils::FindOneWithExternalID(Value, N->Pins, TEXT("pin"));
UEdGraphPin *Found = WingUtils::FindOneWithExternalID(Value, N->Pins, TEXT("pin"), Errors);
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)
{
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(P));
Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(P));
}
return SetError();
}
@@ -303,13 +303,13 @@ WingFetcher& WingFetcher::Component(const FString& Value)
}
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)
{
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)
{
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(C));
Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(C));
}
return SetError();
}
@@ -331,13 +331,13 @@ WingFetcher& WingFetcher::Widget(const FString& Value)
TArray<UWidget*> AllWidgets;
WidgetBP->WidgetTree->GetAllWidgets(AllWidgets);
UWidget* Found = WingUtils::FindOneWithExternalID(Value, AllWidgets, TEXT("widget"));
UWidget* Found = WingUtils::FindOneWithExternalID(Value, AllWidgets, TEXT("widget"), Errors);
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)
{
UWingServer::Printf(TEXT(" %s\n"), *WingUtils::FormatName(W));
Errors.Printf(TEXT(" %s\n"), *WingUtils::FormatName(W));
}
return SetError();
}
@@ -358,18 +358,17 @@ WingFetcher& WingFetcher::LevelBlueprint(const FString& Value)
if (!World->PersistentLevel)
{
UWingServer::Print(TEXT("ERROR: World has no PersistentLevel\n"));
Errors.Print(TEXT("ERROR: World has no PersistentLevel\n"));
return SetError();
}
ULevelScriptBlueprint* LevelBP = World->PersistentLevel->GetLevelScriptBlueprint(true);
if (!LevelBP)
{
UWingServer::Print(TEXT("ERROR: World has no level blueprint\n"));
Errors.Print(TEXT("ERROR: World has no level blueprint\n"));
return SetError();
}
SetObj(LevelBP);
return *this;
}

View File

@@ -254,7 +254,7 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
FString PrimaryCategory;
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();
for (const TSharedPtr<IPropertyHandle>& H : Handles)
{
@@ -265,7 +265,7 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
}
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)
WingPropHandle::Print(*H, Out);
}
@@ -274,8 +274,8 @@ void WingGraphExport::EmitNodeProperties(UEdGraphNode* Node, FStringBuilderBase&
void WingGraphExport::EmitLocalVariables()
{
WingVariables Vars;
Vars.SetBackingStore(Graph);
Vars.Load();
Vars.SetBackingStore(Graph, WingOut::Stdout);
Vars.Load(WingOut::Stdout);
Vars.Print(Output);
}

View File

@@ -5,17 +5,17 @@
void WingManual::PrintHandlerPrototype(const FWingHandlerConfig& Handler)
{
UWingServer::Print(Handler.Name);
UWingServer::Print(TEXT("("));
WingOut::Stdout.Print(Handler.Name);
WingOut::Stdout.Print(TEXT("("));
bool bFirst = true;
for (TFieldIterator<FProperty> PropIt(Handler.Class.Get(), EFieldIterationFlags::None); PropIt; ++PropIt)
{
if (!bFirst) UWingServer::Print(TEXT(","));
if (!bFirst) WingOut::Stdout.Print(TEXT(","));
bFirst = false;
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?"));
UWingServer::Print(PropIt->GetName());
if (PropIt->HasMetaData(TEXT("Optional"))) WingOut::Stdout.Print(TEXT("?"));
WingOut::Stdout.Print(PropIt->GetName());
}
UWingServer::Print(TEXT(")\n"));
WingOut::Stdout.Print(TEXT(")\n"));
}
void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler)
@@ -31,30 +31,30 @@ void WingManual::PrintHandlerArguments(const FWingHandlerConfig& Handler)
if (bOptional)
{
UWingServer::Printf(TEXT(" %s (optional %s)"), *Name, *Type);
WingOut::Stdout.Printf(TEXT(" %s (optional %s)"), *Name, *Type);
}
else
{
UWingServer::Printf(TEXT(" %s (%s)"), *Name, *Type);
WingOut::Stdout.Printf(TEXT(" %s (%s)"), *Name, *Type);
}
if (!Desc.IsEmpty()) UWingServer::Printf(TEXT(" — %s"), *Desc);
UWingServer::Print(TEXT("\n"));
if (!Desc.IsEmpty()) WingOut::Stdout.Printf(TEXT(" — %s"), *Desc);
WingOut::Stdout.Print(TEXT("\n"));
}
}
void WingManual::PrintHandlerDescription(const FWingHandlerConfig& Handler)
{
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)
{
UWingServer::Print(TEXT("\n"));
WingOut::Stdout.Print(TEXT("\n"));
PrintHandlerPrototype(Handler);
PrintHandlerArguments(Handler);
PrintHandlerDescription(Handler);
UWingServer::Print(TEXT("\n"));
WingOut::Stdout.Print(TEXT("\n"));
}
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)
{
UWingServer::Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n"));
WingOut::Stdout.Printf(TEXT("\n--- AUTOMATIC DOCUMENTATION ---\n"));
}
if (Handler && (Sections.Contains(Section::HandlerHelp) || bPrintAll))
@@ -77,7 +77,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
{
if (Abridged)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n PATHS: Here are some example paths:"
"\n /Game/Widgets/WB_Hotkeys,widget:Canvas·122"
"\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
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n PATHS:"
"\n"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n TYPES: Here are some examples of valid types:"
"\n Bool, String, Vector, Rotator, HitResult, Actor, Character,"
"\n PlayerController, EBlendMode, EMovementMode, BP_Manny, BP_Quinn,"
@@ -134,7 +134,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
}
else
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n TYPES:"
"\n"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n VARIABLE DECLARATIONS: example variable declarations:"
"\n Array<Actor> Actors"
"\n Float F (InstanceEditable)"
@@ -166,7 +166,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
}
else
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n VARIABLE DECLARATIONS:"
"\n"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n USING HTML ESCAPE SEQUENCES:"
"\n When we output FNames, we use HTML escape sequences for the"
"\n following marks: \\\"'(),.:;<=>& We also escape control"
@@ -218,7 +218,7 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
}
else
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n USING HTML ESCAPE SEQUENCES:"
"\n When we output FNames, we use HTML escape sequences for the"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n ABOUT WHITESPACE:"
"\n Do not put excess whitespace into paths, typenames, or"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n MATERIAL EDITING:"
"\n We do not expose material expressions directly. Instead, use"
"\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
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n MATERIAL EDITING:"
"\n"
"\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)
{
UWingServer::Print(TEXT(
WingOut::Stdout.Print(TEXT(
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
"\n UserManual: this explanation"
"\n ShowCommands: a full list of all the commands"
@@ -288,6 +288,6 @@ void WingManual::PrintManual(TSet<Section> Sections, const FWingHandlerConfig* H
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;
}
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))
OutAssociation = GlobalParameter;
@@ -25,7 +25,7 @@ bool WingMaterialParameter::ParseMaterialParameterAssociation(const FString& Str
OutAssociation = BlendParameter;
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 true;
@@ -43,35 +43,35 @@ void WingMaterialParameter::FormatMaterialParameter(const FMaterialParameterInfo
switch (Meta.Value.Type)
{
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;
case EMaterialParameterType::Vector:
{
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);
break;
}
case EMaterialParameterType::DoubleVector:
{
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);
break;
}
case EMaterialParameterType::Texture:
{
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"));
break;
}
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"));
break;
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;
}
}

View File

@@ -186,19 +186,19 @@ TSharedPtr<IPropertyHandle> WingPropHandle::TryNamedProperty(const UStruct* Scri
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);
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;
}
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);
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;
}
@@ -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;
@@ -219,7 +219,7 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
{
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 {};
}
Obj = BP->GeneratedClass->GetDefaultObject();
@@ -235,7 +235,7 @@ WingPropHandle::Handles WingPropHandle::GetDetails(UObject* Obj, bool Mutable)
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
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 {};
}
}
@@ -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();
TSet<FName> DuplicateNames;
@@ -288,12 +288,12 @@ bool WingPropHandle::OrganizeByName(const Handles &HList, TMap<FName, TSharedPtr
else Result.Add(Name, H);
}
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)
{
UWingServer::Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName));
Errors.Printf(TEXT(" %s"), *WingUtils::ExternalizeID(DupName));
}
UWingServer::Print(TEXT("\n"));
Errors.Print(TEXT("\n"));
return false;
}
@@ -339,7 +339,7 @@ FString WingPropHandle::GetText(IPropertyHandle& Handle)
return Result;
}
bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text, WingOut Errors)
{
FProperty* Prop = Handle.GetProperty();
@@ -351,11 +351,11 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.AllowContainer = true;
if (!UWingTypes::TextToType(Text, PinType, Req)) return false;
if (!UWingTypes::TextToType(Text, PinType, Req, Errors)) return false;
void* Data = nullptr;
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));
return false;
}
@@ -375,12 +375,12 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
Req.BlueprintType = true;
Req.Blueprintable = false;
Req.IsChildOf = ClassProp->MetaClass;
Class = UWingTypes::TextToOneObjectType(Text, Req);
Class = UWingTypes::TextToOneObjectType(Text, Req, Errors);
if (!Class) return false;
}
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));
return false;
}
@@ -397,14 +397,14 @@ bool WingPropHandle::SetText(IPropertyHandle& Handle, const FString& Text)
if (Enum != nullptr)
{
int64 EnumValue;
if (!WingUtils::StringToEnum(Enum, Value, EnumValue)) return false;
if (!WingUtils::StringToEnum(Enum, Value, EnumValue, Errors)) return false;
Value = Enum->GetNameStringByValue(EnumValue);
}
FPropertyAccess::Result Result = Handle.SetValueFromFormattedString(Value);
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());
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;
switch (JsonValue->Type)
{
case EJson::String:
return SetText(Handle, JsonValue->AsString());
return SetText(Handle, JsonValue->AsString(), Errors);
case EJson::Boolean:
Result = Handle.SetValue(JsonValue->AsBool());
@@ -441,34 +441,34 @@ bool WingPropHandle::SetJson(IPropertyHandle& Handle, const TSharedPtr<FJsonValu
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()));
return false;
}
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;
// Organize the properties by name.
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
// that they match the names of actual properties, and that there are no dups.
TSet<FName> Specified;
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 (!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;
}
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.
@@ -480,7 +480,7 @@ bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props
FName Name = H->GetProperty()->GetFName();
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));
Ok = false;
}
@@ -494,8 +494,8 @@ bool WingPropHandle::PopulateFromJson(TArray<TSharedPtr<IPropertyHandle>>& Props
// point, we're committed.
for (const auto& KV : Json.Values)
{
FName Name = WingUtils::CheckInternalizeID(KV.Key);
if (!SetJson(*OrganizedProps[Name], KV.Value)) Ok = false;
FName Name = WingUtils::CheckInternalizeID(KV.Key, Errors);
if (!SetJson(*OrganizedProps[Name], KV.Value, Errors)) Ok = false;
}
return Ok;
}

View File

@@ -14,32 +14,313 @@
#include "Dom/JsonValue.h"
static bool IsPinTypeProperty(FProperty* Prop)
bool FWingProperty::SetObject(UObject *Obj, WingOut Errors) const
{
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
return StructProp && StructProp->Struct == FEdGraphPinType::StaticStruct();
FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop);
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)
: Prop(InProp), Container(InContainer) {}
FWingProperty::FWingProperty(FProperty* InProp, UObject* InContainer)
: Prop(InProp), Container(static_cast<void*>(InContainer)) {}
FString FWingProperty::GetCategory()
bool FWingProperty::SetDouble(double D, WingOut Errors) const
{
FString Result = Prop->GetMetaData(TEXT("Category"));
if (Result.IsEmpty()) Result = "Unclassified";
return Result;
FNumericProperty *NProp = CastField<FNumericProperty>(Prop);
if (!NProp)
{
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
{
if (IsPinTypeProperty(Prop))
{
FEdGraphPinType PinType;
Prop->GetValue_InContainer(Container, &PinType);
return UWingTypes::TypeToText(PinType);
FEdGraphPinType *PinType = Prop->ContainerPtrToValuePtr<FEdGraphPinType>(Container);
return UWingTypes::TypeToText(*PinType);
}
FString Result;
Prop->ExportTextItem_InContainer(Result, Container, nullptr, nullptr, PPF_None);
@@ -56,269 +337,35 @@ FString FWingProperty::GetTruncatedText(int32 MaxLen) const
return Result;
}
void FWingProperty::PrintExpectsReceived(const TCHAR *Type)
FString FWingProperty::GetCategory() const
{
UWingServer::Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"),
*WingUtils::FormatName(Prop), Type, *Prop->GetCPPType());
FString Result = Prop->GetMetaData(TEXT("Category"));
if (Result.IsEmpty()) Result = "Unclassified";
return Result;
}
bool FWingProperty::CheckImportTextResult(const FString &Value)
void FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props)
{
uint8 *VP = Prop->ContainerPtrToValuePtr<uint8>(Container);
if (FObjectPropertyBase *OProp = CastField<FObjectPropertyBase>(Prop))
{
UObject *Obj = OProp->GetObjectPropertyValue(VP);
if (Obj == nullptr && Value.TrimStartAndEnd().Compare(TEXT("None"), ESearchCase::IgnoreCase) != 0)
{
UWingServer::Printf(TEXT("ERROR: Failed to parse '%s' for property '%s'\n"),
*Value, *WingUtils::FormatName(Prop));
return false;
}
}
return true;
}
bool FWingProperty::SetText(FString Value)
{
// 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)
TArray<FWingProperty> Result;
for (TFieldIterator<FProperty> It(Obj.UStructPtr); It; ++It)
{
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
FString SortCat = *It->GetMetaData(TEXT("Category"));
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]);
Result.Add(FWingProperty(*It, Obj.StructPtr));
}
}
TArray<FWingProperty> FWingProperty::GetAll(UObject* Object, EPropertyFlags Flags)
{
return GetAll(Object->GetClass(), Object, Flags);
}
TArray<FWingProperty> FWingProperty::GetAll(UStruct* StructType, void* Container, EPropertyFlags Flags)
TArray<FWingProperty> FWingProperty::GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags)
{
TArray<FWingProperty> Result;
Collect(StructType, Container, Result, Flags);
GetAll(Obj, Flags, Result);
return Result;
}
TArray<FWingProperty> FWingProperty::GetNamed(UStruct* StructType, void* Container, const TArray<FName> &Names)
{
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> FWingProperty::GetNames(UStruct *US, EPropertyFlags Flags)
{
TArray<FName> Result;
for (TFieldIterator<FProperty> It(StructType); It; ++It)
for (TFieldIterator<FProperty> It(US); It; ++It)
{
if (Flags != 0 && !It->HasAnyPropertyFlags(Flags)) continue;
Result.Add(It->GetFName());
@@ -326,29 +373,23 @@ TArray<FName> FWingProperty::GetNames(UStruct *StructType, EPropertyFlags Flags)
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; });
}
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;
for (int i = 0; i < In.Num(); i++)
{
if (In[i]->GetName() == Name)
{
Out.Add(In[i]);
}
else
{
In[Dst++] = In[i];
}
if (In[i]->GetFName() == Name) Out.Add(In[i]);
else In[Dst++] = In[i];
}
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 {};
@@ -358,7 +399,7 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
{
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 {};
}
Obj = BP->GeneratedClass->GetDefaultObject();
@@ -370,13 +411,12 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
Obj = Mutable ? Ref->GetMutableTemplate() : Ref->GetImmutableTemplate();
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 {};
}
}
TArray<FWingProperty> Result;
Collect(Obj->GetClass(), Obj, Result, Flags);
TArray<FWingProperty> Result = GetAll(Obj, Flags);
// If it's a Material Graph node, also collect properties from
// the associated material expression.
@@ -385,7 +425,7 @@ TArray<FWingProperty> FWingProperty::GetDetailsGeneral(UObject* Obj, EPropertyFl
{
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"));
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)
{
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 FWingProperty::PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json, bool AllOptional, WingOut Errors)
{
bool Ok = true;
// Build a set of known property names for the unknown-field check.
TSet<FString> KnownKeys;
for (const FWingProperty& P : Props)
KnownKeys.Add(WingUtils::FormatName(P.Prop));
// Check for unknown fields in the JSON
for (const auto& KV : Json->Values)
// Make sure they passed in a JSON object.
TSharedPtr<FJsonObject> Obj = Json.AsObject();
if (Obj == nullptr)
{
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;
}
}
@@ -455,23 +474,51 @@ bool FWingProperty::PopulateFromJson(
// Populate each property from JSON
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;
}
bool FWingProperty::PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object)
bool FWingProperty::IsPinTypeProperty(FProperty* Prop)
{
TArray<FWingProperty> Props = FWingProperty::GetAll(StructType, Container, (EPropertyFlags)0);
return PopulateFromJson(Props, Object);
FStructProperty* StructProp = CastField<FStructProperty>(Prop);
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))
{
UWingServer::Print(TEXT("ERROR: Expected a JSON object\n"));
return false;
}
return PopulateFromJson(StructType, Container, Object->AsObject().Get());
Errors.Printf(TEXT("ERROR: '%s' received a %s, but expects %s\n"),
*WingUtils::FormatName(Prop), Type, *Prop->GetCPPType());
}
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.bEnabled = true;
HandlerOutput.Reset();
WingOut::StdoutBuffer.Reset();
SuggestedManualSections.Empty();
LastHandler = nullptr;
@@ -273,7 +273,7 @@ FString UWingServer::HandleRequest(const FString& Line)
LogCapture.bEnabled = false;
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();
if (!SuggestedManualSections.IsEmpty())
@@ -281,8 +281,8 @@ FString UWingServer::HandleRequest(const FString& Line)
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
WingManual::PrintManual(SuggestedManualSections, LastHandler, true);
}
FString Result = HandlerOutput.ToString();
HandlerOutput.Reset();
FString Result = WingOut::StdoutBuffer.ToString();
WingOut::StdoutBuffer.Reset();
for (int32 i = 0; i < Result.Len(); ++i)
{
if (Result[i] == TEXT('\0')) Result[i] = TEXT(' ');
@@ -298,7 +298,7 @@ void UWingServer::TryCallHandler(const FString &Line)
FJsonSerializer::Deserialize(Reader, Request);
if (!Request.IsValid())
{
UWingServer::Printf(TEXT("Request is not valid JSON"));
WingOut::Stdout.Printf(TEXT("Request is not valid JSON"));
return;
}
@@ -306,8 +306,8 @@ void UWingServer::TryCallHandler(const FString &Line)
FString Command;
if (!Request->TryGetStringField(TEXT("command"), Command))
{
UWingServer::Printf(TEXT("Request does not contain 'command' parameter"));
UWingServer::Printf(TEXT("We recommend sending command='UserManual'."));
WingOut::Stdout.Printf(TEXT("Request does not contain 'command' parameter"));
WingOut::Stdout.Printf(TEXT("We recommend sending command='UserManual'."));
return;
}
Request->RemoveField(TEXT("command"));
@@ -316,7 +316,7 @@ void UWingServer::TryCallHandler(const FString &Line)
FWingHandlerConfig* Found = FindHandler(Command);
if (!Found)
{
UWingServer::Printf(TEXT("Unknown command: %s"), *Command);
WingOut::Stdout.Printf(TEXT("Unknown command: %s"), *Command);
UWingServer::SuggestManual(WingManual::Section::ImportantCommands);
return;
}
@@ -330,7 +330,7 @@ void UWingServer::TryCallHandler(const FString &Line)
// Populate the handler object with the request parameters.
WingPropHandle Props;
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);
return;
@@ -490,3 +490,7 @@ FWingHandlerConfig* UWingServer::FindHandler(const FString& Name)
}
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");
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));
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);
}
bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c)
bool UWingTypes::ParseChar(WingTokenizer& Tok, TCHAR c, WingOut Errors)
{
if (!Tok.TokenIs(c))
{
PrintParseError(Tok, *FString::Printf(TEXT("expected '%c'"), c));
PrintParseError(Tok, *FString::Printf(TEXT("expected '%c'"), c), Errors);
return false;
}
Tok.Advance();
return true;
}
bool UWingTypes::ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType)
bool UWingTypes::ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{
if (!Tok.TokenIs(Tok.Identifier))
{
PrintParseError(Tok, TEXT("expected identifier"));
PrintParseError(Tok, TEXT("expected identifier"), Errors);
return false;
}
FString Name = Tok.NextName().ToString();
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();
if (!ParseChar(Tok, '<')) return false;
if (!ParsePlainIdentifier(Tok, OutType)) return false;
if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParsePlainIdentifier(Tok, OutType, Errors)) return false;
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;
}
if (!ParseChar(Tok, '>')) return false;
if (!ParseChar(Tok, '>', Errors)) return false;
OutType.PinCategory = Wrapper;
return true;
}
bool UWingTypes::ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType)
bool UWingTypes::ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{
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, '<'))
{
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_Class, OutType);
return ParseWrapped(Tok, UEdGraphSchema_K2::PC_Class, OutType, Errors);
}
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();
if (!ParseChar(Tok, '<')) return false;
if (!ParseMaybeWrapped(Tok, OutType)) return false;
if (!ParseChar(Tok, '>')) return false;
if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParseMaybeWrapped(Tok, OutType, Errors)) return false;
if (!ParseChar(Tok, '>', Errors)) return false;
return true;
}
bool UWingTypes::ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType)
bool UWingTypes::ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{
Tok.Advance();
if (!ParseChar(Tok, '<')) return false;
if (!ParsePlainIdentifier(Tok, OutType)) return false;
if (!ParseChar(Tok, ',')) return false;
if (!ParseChar(Tok, '<', Errors)) return false;
if (!ParsePlainIdentifier(Tok, OutType, Errors)) return false;
if (!ParseChar(Tok, ',', Errors)) return false;
FEdGraphPinType ValueType;
if (!ParseMaybeWrapped(Tok, ValueType)) return false;
if (!ParseMaybeWrapped(Tok, ValueType, Errors)) return false;
OutType.PinValueType.TerminalCategory = ValueType.PinCategory;
OutType.PinValueType.TerminalSubCategory = ValueType.PinSubCategory;
OutType.PinValueType.TerminalSubCategoryObject = ValueType.PinSubCategoryObject;
if (!ParseChar(Tok, '>')) return false;
if (!ParseChar(Tok, '>', Errors)) return false;
return true;
}
bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType)
bool UWingTypes::ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors)
{
if (Tok.TokenIs(NAME_TypeArray, '<'))
{
OutType.ContainerType = EPinContainerType::Array;
if (!ParseArrayOrSet(Tok, OutType)) return false;
if (!ParseArrayOrSet(Tok, OutType, Errors)) return false;
}
else if (Tok.TokenIs(NAME_TypeSet, '<'))
{
OutType.ContainerType = EPinContainerType::Set;
if (!ParseArrayOrSet(Tok, OutType)) return false;
if (!ParseArrayOrSet(Tok, OutType, Errors)) return false;
}
else if (Tok.TokenIs(NAME_TypeMap, '<'))
{
OutType.ContainerType = EPinContainerType::Map;
if (!ParseMap(Tok, OutType)) return false;
if (!ParseMap(Tok, OutType, Errors)) return false;
}
else
{
if (!ParseMaybeWrapped(Tok, OutType)) return false;
if (!ParseMaybeWrapped(Tok, OutType, Errors)) return false;
}
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());
if (!TypeInfo)
{
PrintParseError(Tok, *FString::Printf(TEXT("unrecognized type '%s'"), *Name));
PrintParseError(Tok, *FString::Printf(TEXT("unrecognized type '%s'"), *Name), Errors);
return false;
}
@@ -566,7 +566,7 @@ bool UWingTypes::ResolveShortName(WingTokenizer& Tok, const FString &Name, FEdGr
UObject* Obj = LoadObject<UObject>(nullptr, *TypeInfo->PinSubCategoryObject);
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;
}
@@ -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>();
check(Types);
@@ -615,19 +615,19 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
!Require.Blueprintable.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;
}
OutPinType = FEdGraphPinType();
if (!Types->ParseType(Tok, OutPinType))
if (!Types->ParseType(Tok, OutPinType, Errors))
{
OutPinType = FEdGraphPinType(); return false;
}
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;
}
@@ -636,7 +636,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (OutPinType.IsContainer())
{
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);
OutPinType = FEdGraphPinType(); return false;
}
@@ -646,7 +646,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
{
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());
UWingServer::SuggestManual(WingManual::Section::HandlerHelp);
OutPinType = FEdGraphPinType(); return false;
@@ -657,7 +657,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
{
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);
OutPinType = FEdGraphPinType(); return false;
}
@@ -668,7 +668,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (!IsBlueprintType(OutPinType))
{
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);
OutPinType = FEdGraphPinType(); return false;
}
@@ -679,7 +679,7 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
if (!IsBlueprintable(OutPinType))
{
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);
OutPinType = FEdGraphPinType(); return false;
}
@@ -688,36 +688,36 @@ bool UWingTypes::TextToType(WingTokenizer &Tok, FEdGraphPinType& OutPinType, con
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);
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;
if (!TextToType(Text, PinType, Require)) return nullptr;
if (!TextToType(Text, PinType, Require, Errors)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Object) ||
(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);
return nullptr;
}
return Class;
}
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require)
UClass* UWingTypes::TextToOneInterfaceType(const FString& Text, const Requirements &Require, WingOut Errors)
{
FEdGraphPinType PinType;
if (!TextToType(Text, PinType, Require)) return nullptr;
if (!TextToType(Text, PinType, Require, Errors)) return nullptr;
UClass* Class = Cast<UClass>(PinType.PinSubCategoryObject.Get());
if ((!Class) || (PinType.PinCategory != UEdGraphSchema_K2::PC_Interface) ||
(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);
return nullptr;
}

View File

@@ -61,24 +61,24 @@ FString WingUtils::ExternalizeID(FName Name)
return WingTokenizer::ExternalizeID(Name);
}
FName WingUtils::CheckInternalizeID(const FString &ExternalID)
FName WingUtils::CheckInternalizeID(const FString &ExternalID, WingOut Errors)
{
FString Error;
FName InternalID = WingTokenizer::TryInternalizeID(ExternalID, Error);
if (!Error.IsEmpty())
{
UWingServer::Printf(TEXT("%s\n"), *Error);
Errors.Printf(TEXT("%s\n"), *Error);
UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
}
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))
{
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);
UWingServer::SuggestManual(WingManual::Section::EscapeSequences);
return FName();
@@ -341,7 +341,7 @@ FString WingUtils::EnumToString(UEnum* Enum, int64 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);
if (Index == INDEX_NONE)
@@ -352,7 +352,7 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue)
}
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;
return false;
}
@@ -365,53 +365,53 @@ bool WingUtils::StringToEnum(UEnum* Enum, const FString& Str, int64& OutValue)
// 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)
{
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;
}
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 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)
{
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 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)
{
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;
}
TSharedPtr<INameValidatorInterface> Validator = FNameValidatorFactory::MakeValidator(Node);
EValidatorResult Result = Validator->IsValid(Name, false);
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 true;
@@ -573,7 +573,7 @@ UAnimationStateMachineGraph* WingUtils::FindStateMachineGraph(UBlueprint* BP, co
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)
{
@@ -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;
}

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

View File

@@ -90,17 +90,17 @@ TArray<WingWidgets::Type> WingWidgets::Search(const FString& Query, int32 MaxRes
return Results;
}
bool WingWidgets::CheckCanBeParent(UWidget* Widget)
bool WingWidgets::CheckCanBeParent(UWidget* Widget, WingOut Errors)
{
UPanelWidget* Panel = Cast<UPanelWidget>(Widget);
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;
}
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 true;
@@ -111,12 +111,12 @@ void WingWidgets::PrintWidgetTree(UWidget* Widget, int32 Depth)
FString Indent = FString::ChrN(Depth * 2, TEXT(' '));
if (Widget == nullptr)
{
UWingServer::Printf(TEXT("%s[null]\n"), *Indent);
WingOut::Stdout.Printf(TEXT("%s[null]\n"), *Indent);
return;
}
FString TypeName = WingUtils::FormatName(Widget->GetClass());
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))
{

View File

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

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "Factories/Factory.h"
#include "Factories/EnumFactory.h"
#include "WingHandler.h"
#include "WingFactories.generated.h"
@@ -13,7 +14,7 @@ public:
// Create an asset on disk, using a factory instance.
// Returns the main object. If there are problems,
// 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
// 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
// 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.

View File

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

View File

@@ -8,6 +8,7 @@
// Marker struct for handler parameters that accept a JSON object.
// PopulateFromJson stashes the actual JSON object into the Json field.
//
USTRUCT()
struct FWingJsonObject
{
@@ -17,6 +18,7 @@ struct FWingJsonObject
// Marker struct for handler parameters that accept a JSON array.
// PopulateFromJson stashes the actual JSON array into the Array field.
//
USTRUCT()
struct FWingJsonArray
{
@@ -56,4 +58,48 @@ public:
// The configuration object.
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 "Materials/Material.h"
#include "WingHandler.h"
class WingMaterialParameter
{
public:
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);
};

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "IPropertyRowGenerator.h"
#include "PropertyHandle.h"
#include "WingHandler.h"
class FJsonObject;
class FJsonValue;
@@ -44,35 +45,35 @@ public:
TSharedPtr<IPropertyHandle> TryNamedProperty(const UStruct* ScriptStruct, uint8* Data, FName Name, bool RootFilter);
// Like TryNamedProperty, but prints an error if the property is not found.
TSharedPtr<IPropertyHandle> NamedProperty(UObject* Obj, FName Name, bool RootFilter);
TSharedPtr<IPropertyHandle> NamedProperty(const UStruct* ScriptStruct, uint8* Data, 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, WingOut Errors);
// Get "details panel" properties for an object. Handles special
// cases: blueprints (redirects to CDO), component references
// (redirects to template), material graph nodes (includes
// 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
// of a given name, print an error and returns false, and returns a map
// 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
// text, these support more concise enum values, FEdGraphPinType, and
// more concise Class properties.
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
// 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
// AllOptional is true, the Json may supply a subset of the properties.
// If not, the Json must supply all of them, excepting properties that
// 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:
// editable|readonly Type Name = Value

View File

@@ -1,38 +1,88 @@
#pragma once
#include "CoreMinimal.h"
#include "WingHandler.h"
#include "WingUtils.h"
// 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
{
FProperty* Prop = 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(FProperty* InProp, void* InContainer);
FWingProperty(FProperty* InProp, UObject* InContainer);
bool SetDouble(double D);
bool SetInt64(int64 I);
bool SetBool(bool B);
// Operator -> forwards to the FProperty.
//
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;
bool SetText(FString Value);
// Store data from a json object.
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.
// Get the text, replace newlines with whitespace, and truncate to the specified maximum length.
//
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; }
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.
//
@@ -46,49 +96,19 @@ struct FWingProperty
//
// When editing an inherited ActorComponent, you're actually editing
// properties that *override* the original properties of the ActorComponent.
// The mutable version, 'GetDetailsMutable', tells this function to first
// create the overrides, and then edit those.
//
// For a more direct "just give me the properties of this object,"
// use the GetAll function.
// The 'mutable' flag tells it whether to create overrides for these
// properties.
//
static TArray<FWingProperty> GetDetailsMutable(UObject* Obj, EPropertyFlags Flags)
{ 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);
static TArray<FWingProperty> GetDetails(UObject* Obj, EPropertyFlags Flags, bool Mutable);
// 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 FJsonObject* Json, bool AllOptional = false);
static bool PopulateFromJson(UStruct* StructType, void* Container, const FJsonObject* Object);
static bool PopulateFromJson(UStruct* StructType, void* Container, const TSharedPtr<FJsonValue>& Object);
static bool PopulateFromJson(TArray<FWingProperty>& Props, const FJsonValue& Json,
bool AllOptional, WingOut Errors);
private:
static TArray<FWingProperty> GetDetailsGeneral(UObject* Obj, EPropertyFlags Flags, bool Mutable);
void PrintExpectsReceived(const TCHAR *Type);
static void Collect(UStruct* Struct, void* Container, TArray<FWingProperty> &Props, EPropertyFlags Flags);
bool CheckImportTextResult(const FString &Value);
static void GetAll(FWingStructAndUStruct Obj, EPropertyFlags Flags, TArray<FWingProperty> &Props);
static bool IsPinTypeProperty(FProperty *Prop);
void PrintExpectsReceived(const TCHAR *Type, WingOut Errors) const;
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. */
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. */
static void SuggestManual(WingManual::Section Section) { GWingServer->SuggestedManualSections.Add(Section); }
@@ -84,7 +70,6 @@ private:
UPROPERTY()
FWingNotifier Notifier;
FWingHandlerConfig* LastHandler;
TStringBuilder<16384> HandlerOutput;
TSet<WingManual::Section> SuggestedManualSections;
FLogCaptureOutputDevice LogCapture; // installed once at startup, enabled per-request
TArray<FWingHandlerConfig> WingHandlerRegistry; // sorted by Name

View File

@@ -3,6 +3,7 @@
#include "CoreMinimal.h"
#include "EditorSubsystem.h"
#include "EdGraph/EdGraphPin.h"
#include "WingHandler.h"
#include "WingTypes.generated.h"
struct WingTokenizer;
@@ -117,16 +118,16 @@ private:
// Parser and ResolveShortName
// ---------------------------------------------------------------------------
static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message);
static bool ParseChar(WingTokenizer& Tok, TCHAR c);
bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType);
bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType);
bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType);
bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType);
bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType);
bool ParseType(WingTokenizer& Tok, FEdGraphPinType& OutType);
static void PrintParseError(WingTokenizer& Tok, const TCHAR* Message, WingOut Errors);
static bool ParseChar(WingTokenizer& Tok, TCHAR c, WingOut Errors);
bool ParsePlainIdentifier(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseWrapped(WingTokenizer& Tok, FName Wrapper, FEdGraphPinType& OutType, WingOut Errors);
bool ParseMaybeWrapped(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseArrayOrSet(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
bool ParseMap(WingTokenizer& Tok, FEdGraphPinType& OutType, WingOut Errors);
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:
// ---------------------------------------------------------------------------
@@ -146,21 +147,21 @@ public:
// 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,
// 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
// requirements, prints an error and returns false.
static bool TextToType(const FString& Text, FEdGraphPinType& OutPinType, const Requirements &Require);
// requirements, prints an error and returns false.
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
// 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).
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
// 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).
static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require);
static UClass* TextToOneInterfaceType(const FString& Text, const Requirements &Require, WingOut Errors);
private:
// ---------------------------------------------------------------------------

View File

@@ -15,6 +15,7 @@
#include "Components/Widget.h"
#include "WingActorComponent.h"
#include "WingVariables.h"
#include "WingHandler.h"
struct FEdGraphSchemaAction;
class UAnimationStateMachineGraph;
@@ -104,53 +105,53 @@ public:
////////////////////////////////////////////////////////
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;
int Count = 0;
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;
}
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;
FName InternalID = CheckInternalizeID(ExternalID);
if (!InternalID.IsNone()) Result = FindOneWithInternalID(InternalID, Array, Kind);
FName InternalID = CheckInternalizeID(ExternalID, Errors);
if (!InternalID.IsNone()) Result = FindOneWithInternalID(InternalID, Array, Kind, Errors);
return Result;
}
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)
return CheckExactlyNoneNamed(1, Kind, InternalID);
for (auto &Elt : Array) if (GetFName(Elt) == InternalID)
return CheckExactlyNoneNamed(1, Kind, InternalID, Errors);
return true;
}
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;
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))
{ CheckExactlyOneNamed(2, Kind, InternalID); return false; }
{ CheckExactlyOneNamed(2, Kind, InternalID, Errors); return false; }
Collection.Add(InternalID);
return true;
}
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)
if (!FindNoDuplicateName(Collection, GetFName(Elt), Kind)) return false;
if (!FindNoDuplicateName(Collection, GetFName(Elt), Kind, Errors)) return false;
return true;
}
@@ -200,7 +201,7 @@ public:
// static FString ExternalizeID(const FString& InternalID);
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
@@ -221,13 +222,13 @@ public:
// empty string.
////////////////////////////////////////////////////////
static FName CheckProposedName(const FString &Name);
static FName CheckProposedName(const FString &Name, WingOut Errors);
static FString FormatNodeTitle(const UEdGraphNode *Node);
// ----- Enum helpers -----
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>
static FString EnumToString(TEnumAsByte<T> Value)
@@ -238,8 +239,8 @@ public:
{ return EnumToString(StaticEnum<T>(), (int64)Value); }
template<typename T>
static bool StringToEnum(const FString& Str, T& OutValue)
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V)) return false; OutValue = (T)V; return true; }
static bool StringToEnum(const FString& Str, T& OutValue, WingOut Errors)
{ int64 V; if (!StringToEnum(StaticEnum<T>(), Str, V, Errors)) return false; OutValue = (T)V; return true; }
// ----- Blueprint helpers -----
static TArray<UEdGraph*> AllGraphs(UBlueprint* BP);
@@ -258,7 +259,7 @@ public:
// ----- Anim blueprint helpers -----
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);
// ----- Text formatting -----
@@ -273,11 +274,11 @@ public:
static bool CanReparentBlueprint(UClass* CurrentGenerated, UClass* Proposed);
// ----- Common Error Reporting -----
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name);
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, FName Name);
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
static bool CheckExactlyOneNamed(int Count, const TCHAR *Kind, FName Name, WingOut Errors);
static bool CheckExactlyNoneNamed(int Count, const TCHAR *Kind, const FString &Name, WingOut Errors);
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 "EdGraph/EdGraphPin.h"
#include "Engine/Blueprint.h"
#include "WingHandler.h"
struct WingTokenizer;
class UK2Node_CustomEvent;
@@ -54,27 +55,27 @@ public:
void Empty() { Variables.Empty(); }
// Print the variables to a string builder.
void Print(FStringBuilderBase &Out);
void Print(WingOut Out);
// Print compact: "type name,type name,..."
void PrintCompact(FStringBuilderBase &Out);
void PrintCompact(WingOut Out);
// Clear the BPVar and Pin fields.
void ClearLinks();
// Check the sanity of the vars in the array. If allow
// 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.
bool ParseString(const FString &Input);
bool ParseString(const FString &Input, WingOut Errors);
// Parse variable names only from a string.
bool ParseNamesString(const FString &Input);
bool ParseNamesString(const FString &Input, WingOut Errors);
private:
bool ParseOneVariable(WingTokenizer &Tok, Var &V);
bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out);
bool ParseOneVariable(WingTokenizer &Tok, Var &V, WingOut Errors);
bool ParseVariableFlags(WingTokenizer &Tok, TSet<FName> &Out, WingOut Errors);
};
class WingVariables
@@ -100,7 +101,7 @@ public:
// Configure the backing store. On failure,
// 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.
@@ -112,29 +113,29 @@ public:
// Print the contents of the workspace.
void Print(FStringBuilderBase &Out);
void Print(WingOut Out);
// Load: clear the workspace, then
// 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
// given the type of backing store.
bool Check();
bool Check(WingOut Errors);
// 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.
bool Create();
bool Create(WingOut Errors);
// Remove every variable mentioned in the workspace from the backing store.
bool Remove();
bool Remove(WingOut Errors);
private:
void LoadBlueprint();
@@ -144,37 +145,38 @@ private:
Var LoadLocalVariableDescription(FBPVariableDescription &Desc);
void LoadEditablePinBase(UK2Node_EditablePinBase *Node, WingVariableList &List);
bool CheckBlueprint();
bool CheckGraph();
bool CheckBlueprint(WingOut Errors);
bool CheckGraph(WingOut Errors);
bool ModifyBlueprint();
bool LinkBlueprintVariables();
bool ModifyBlueprint(WingOut Errors);
bool LinkBlueprintVariables(WingOut Errors);
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 RemoveGraph();
bool RemoveBlueprint(WingOut Errors);
bool RemoveGraph(WingOut Errors);
void LoadCustomEvent();
bool CheckCustomEvent();
bool ModifyCustomEvent();
bool CreateCustomEvent();
bool RemoveCustomEvent();
bool CheckCustomEvent(WingOut Errors);
bool ModifyCustomEvent(WingOut Errors);
bool CreateCustomEvent(WingOut Errors);
bool RemoveCustomEvent(WingOut Errors);
bool GetGraphNodes(
UK2Node_EditablePinBase *&InputNode,
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);
bool ErrorNoBackingStore();
bool ErrorNoBackingStore(WingOut Errors);
};

View File

@@ -2,6 +2,7 @@
#include "CoreMinimal.h"
#include "Components/Widget.h"
#include "WingHandler.h"
class UWidgetTree;
struct FAssetData;
@@ -24,7 +25,7 @@ public:
TArray<Type> Search(const FString& Query, int32 MaxResults, bool Exact);
// 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.
static void PrintWidgetTree(UWidget* Widget, int32 Depth);