UE Wingman renaming complete.
This commit is contained in:
52
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Backup.h
Normal file
52
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Backup.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Misc/Paths.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "Asset_Backup.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_Backup : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to back up"))
|
||||
FString Asset;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Copy an asset's .uasset file to a .uasset.bak backup.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
FString Filename = FPaths::ConvertRelativePathToFull(
|
||||
FPackageName::LongPackageNameToFilename(Asset, FPackageName::GetAssetPackageExtension()));
|
||||
|
||||
if (!IFileManager::Get().FileExists(*Filename))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Asset file not found: %s\n"), *Filename);
|
||||
return;
|
||||
}
|
||||
|
||||
FString BackupFilename = Filename + TEXT(".bak");
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Backed up to %s\n"), *BackupFilename);
|
||||
}
|
||||
};
|
||||
127
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Delete.h
Normal file
127
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Delete.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "UObject/LinkerLoad.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "Asset_Delete.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_Delete : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to delete"))
|
||||
FString Asset;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, skip reference check and force delete"))
|
||||
bool Force = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Delete a .uasset after verifying no references. "
|
||||
"Use force=true to skip the reference check.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Verify the asset file exists on disk
|
||||
FString PackageFilename = FPackageName::LongPackageNameToFilename(
|
||||
Asset, FPackageName::GetAssetPackageExtension());
|
||||
PackageFilename = FPaths::ConvertRelativePathToFull(PackageFilename);
|
||||
|
||||
if (!IFileManager::Get().FileExists(*PackageFilename))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Asset file not found on disk: %s\n"), *PackageFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check references
|
||||
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||
TArray<FName> Referencers;
|
||||
Registry.GetReferencers(FName(*Asset), Referencers);
|
||||
|
||||
// Filter out self-references
|
||||
Referencers.RemoveAll([this](const FName& Ref) {
|
||||
return Ref.ToString() == Asset;
|
||||
});
|
||||
|
||||
if (Referencers.Num() > 0 && !Force)
|
||||
{
|
||||
UWingServer::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,
|
||||
RefPackage ? TEXT(" (loaded)") : TEXT(" (on-disk only)"));
|
||||
}
|
||||
UWingServer::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());
|
||||
}
|
||||
|
||||
// Mark the package, and all the objects in it, as NOT
|
||||
// GC Roots. Also, make them undiscoverable.
|
||||
UPackage* Package = FindPackage(nullptr, *Asset);
|
||||
if (Package)
|
||||
{
|
||||
// Collect all objects in this package
|
||||
TArray<UObject*> ObjectsInPackage;
|
||||
GetObjectsWithPackage(Package, ObjectsInPackage);
|
||||
|
||||
// Clear flags and remove from root to allow GC
|
||||
for (UObject* Obj : ObjectsInPackage)
|
||||
{
|
||||
if (Obj)
|
||||
{
|
||||
Obj->ClearFlags(RF_Standalone | RF_Public);
|
||||
Obj->RemoveFromRoot();
|
||||
}
|
||||
}
|
||||
Package->ClearFlags(RF_Standalone | RF_Public);
|
||||
Package->RemoveFromRoot();
|
||||
}
|
||||
|
||||
// The loader that loaded the package might still
|
||||
// have a file lock on it. Unlock the file.
|
||||
ResetLoaders(Package);
|
||||
|
||||
// Delete the file on disk
|
||||
bool bDeleted = IFileManager::Get().Delete(*PackageFilename, false, true);
|
||||
|
||||
if (!bDeleted)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to delete file from disk: %s\n"), *PackageFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger an asset registry rescan so it notices the deletion
|
||||
FString PackageDir;
|
||||
int32 LastSlash;
|
||||
if (Asset.FindLastChar(TEXT('/'), LastSlash))
|
||||
{
|
||||
PackageDir = Asset.Left(LastSlash);
|
||||
Registry.ScanPathsSynchronous({PackageDir}, true);
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Deleted %s\n"), *Asset);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "Asset_FindReferences.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_FindReferences : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to find references for"))
|
||||
FString Asset;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Find all assets that reference a given asset.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||
|
||||
// Verify the asset exists
|
||||
FAssetData AssetData = Registry.GetAssetByObjectPath(FSoftObjectPath(Asset));
|
||||
if (!AssetData.IsValid())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Asset not found: %s\n"), *Asset);
|
||||
return;
|
||||
}
|
||||
|
||||
TArray<FName> Referencers;
|
||||
Registry.GetReferencers(FName(*Asset), Referencers);
|
||||
|
||||
if (Referencers.Num() == 0)
|
||||
{
|
||||
UWingServer::Print(TEXT("No referencers found.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Classify referencers by looking up their asset class
|
||||
for (const FName& Ref : Referencers)
|
||||
{
|
||||
FString RefStr = Ref.ToString();
|
||||
TArray<FAssetData> RefAssets;
|
||||
Registry.GetAssetsByPackageName(Ref, RefAssets);
|
||||
if (RefAssets.Num() > 0)
|
||||
{
|
||||
UWingServer::Printf(TEXT("%s %s\n"),
|
||||
*WingUtils::FormatName(RefAssets[0].GetClass()),
|
||||
*RefStr);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWingServer::Printf(TEXT("Unknown %s\n"), *RefStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
71
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Rename.h
Normal file
71
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Rename.h
Normal file
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "AssetToolsModule.h"
|
||||
#include "IAssetTools.h"
|
||||
#include "Asset_Rename.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_Rename : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to rename"))
|
||||
FString Asset;
|
||||
|
||||
UPROPERTY(meta=(Description="New package path or just a new name"))
|
||||
FString NewPath;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Rename or move an asset with reference fixup.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Load the asset
|
||||
WingFetcher F;
|
||||
UObject* AssetObj = F.Asset(Asset).GetObj();
|
||||
if (!AssetObj) return;
|
||||
|
||||
// Parse new path into package path and asset name
|
||||
FString NewPackagePath = FPackageName::GetLongPackagePath(NewPath);
|
||||
FString NewAssetName = FPackageName::GetShortName(NewPath);
|
||||
if (NewPackagePath.IsEmpty())
|
||||
{
|
||||
// No slash — just a new name, keep the same directory
|
||||
NewPackagePath = FPackageName::GetLongPackagePath(Asset);
|
||||
NewAssetName = NewPath;
|
||||
if (NewPackagePath.IsEmpty())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Cannot determine directory from Asset '%s'\n"), *Asset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the rename with reference fixup
|
||||
FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
|
||||
IAssetTools& AssetTools = AssetToolsModule.Get();
|
||||
|
||||
TArray<FAssetRenameData> RenameData;
|
||||
RenameData.Add(FAssetRenameData(AssetObj, NewPackagePath, NewAssetName));
|
||||
|
||||
if (!AssetTools.RenameAssets(RenameData))
|
||||
{
|
||||
UWingServer::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);
|
||||
}
|
||||
};
|
||||
75
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Restore.h
Normal file
75
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Restore.h
Normal file
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "FileHelpers.h"
|
||||
#include "HAL/FileManager.h"
|
||||
#include "UObject/LinkerLoad.h"
|
||||
#include "Asset_Restore.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_Restore : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to restore"))
|
||||
FString Asset;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Restore a .uasset file from its .uasset.bak backup, reloading it in the editor.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
FString Filename = FPaths::ConvertRelativePathToFull(
|
||||
FPackageName::LongPackageNameToFilename(Asset, FPackageName::GetAssetPackageExtension()));
|
||||
FString BackupFilename = Filename + TEXT(".bak");
|
||||
|
||||
if (!IFileManager::Get().FileExists(*BackupFilename))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Backup file not found: %s\n"), *BackupFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
// Release file handles if the package is loaded
|
||||
UPackage* Package = FindPackage(nullptr, *Asset);
|
||||
if (Package)
|
||||
{
|
||||
ResetLoaders(Package);
|
||||
}
|
||||
|
||||
// Copy backup over the original
|
||||
uint32 CopyResult = IFileManager::Get().Copy(*Filename, *BackupFilename, true);
|
||||
if (CopyResult != COPY_OK)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to copy backup over %s\n"), *Asset);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload the package if it was loaded
|
||||
if (Package)
|
||||
{
|
||||
bool bReloaded = false;
|
||||
FText ErrorMessage;
|
||||
UEditorLoadingAndSavingUtils::ReloadPackages({Package}, bReloaded, ErrorMessage, EReloadPackagesInteractionMode::AssumePositive);
|
||||
if (!bReloaded)
|
||||
{
|
||||
UWingServer::Printf(TEXT("WARNING: Restored %s but reload failed: %s\n"),
|
||||
*Asset, *ErrorMessage.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Restored %s from backup\n"), *Asset);
|
||||
}
|
||||
};
|
||||
96
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h
Normal file
96
Plugins/UEWingman/Source/UEWingman/Handlers/Asset_Search.h
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "Asset_Search.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Asset_Search : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Substring to match against asset package paths"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Asset class name to filter by, e.g. Blueprint, Material, StaticMesh"))
|
||||
FString Type;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results (default 50)"))
|
||||
int32 Limit = 50;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search for assets by name and/or type. At least one of Query or Type must be specified.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
if (Query.IsEmpty() && Type.IsEmpty())
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: At least one of Query or Type must be specified\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the asset registry filter
|
||||
FARFilter Filter;
|
||||
Filter.bRecursiveClasses = true;
|
||||
Filter.bRecursivePaths = true;
|
||||
Filter.PackagePaths.Add(FName(TEXT("/Game")));
|
||||
|
||||
if (!Type.IsEmpty())
|
||||
{
|
||||
UClass* TypeClass = WingUtils::FindClassByName(Type);
|
||||
if (!TypeClass)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Unknown asset type '%s'\n"), *Type);
|
||||
return;
|
||||
}
|
||||
Filter.ClassPaths.Add(TypeClass->GetClassPathName());
|
||||
}
|
||||
|
||||
// Query the asset registry
|
||||
IAssetRegistry& AR = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry").Get();
|
||||
TArray<FAssetData> Candidates;
|
||||
AR.GetAssets(Filter, Candidates);
|
||||
|
||||
// Filter by query substring and collect results
|
||||
TArray<FAssetData> Results;
|
||||
for (const FAssetData& Data : Candidates)
|
||||
{
|
||||
if (Results.Num() >= Limit) break;
|
||||
if (!Query.IsEmpty())
|
||||
{
|
||||
if (!Data.AssetName.ToString().Contains(Query, ESearchCase::IgnoreCase) &&
|
||||
!Data.PackageName.ToString().Contains(Query, ESearchCase::IgnoreCase))
|
||||
continue;
|
||||
}
|
||||
Results.Add(Data);
|
||||
}
|
||||
|
||||
for (const FAssetData& Data : Results)
|
||||
{
|
||||
UWingServer::Printf(TEXT("%s %s\n"),
|
||||
*WingUtils::FormatName(Data.GetClass()),
|
||||
*Data.PackageName.ToString());
|
||||
}
|
||||
|
||||
if (Results.Num() == 0)
|
||||
{
|
||||
UWingServer::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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingBlueprintVar.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingTypes.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "BlueprintVariable_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_BlueprintVariable_Create : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
|
||||
UPROPERTY(meta=(Description="Name of the new variable"))
|
||||
FString Name;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Variable configuration: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc."))
|
||||
FWingJsonObject Config;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Add a new member variable to a Blueprint. Pass Config to set type, category, flags, etc.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
// Check for duplicate variable name
|
||||
FName VarFName(*Name);
|
||||
if (FBlueprintEditorUtils::FindNewVariableIndex(BP, VarFName) != INDEX_NONE)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Variable '%s' already exists in %s\n"), *Name, *WingUtils::FormatName(BP));
|
||||
return;
|
||||
}
|
||||
|
||||
// Add the variable with a default type
|
||||
FEdGraphPinType DefaultType;
|
||||
DefaultType.PinCategory = UEdGraphSchema_K2::PC_Int;
|
||||
if (!FBlueprintEditorUtils::AddMemberVariable(BP, VarFName, DefaultType))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to add variable '%s' to %s\n"), *Name, *WingUtils::FormatName(BP));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the newly created variable description
|
||||
FBlueprintVar Editor(BP, Name);
|
||||
if (Editor.NotFound()) return;
|
||||
|
||||
// Apply config if provided
|
||||
if (Config.Json && Config.Json->Values.Num() > 0)
|
||||
{
|
||||
if (!Editor.ApplyJson(Config.Json.Get()))
|
||||
return;
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Created variable %s (%s) in %s\n"),
|
||||
*Name, *UWingTypes::TypeToText(Editor.Desc->VarType), *WingUtils::FormatName(BP));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingBlueprintVar.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "BlueprintVariable_Delete.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_BlueprintVariable_Delete : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
|
||||
UPROPERTY(meta=(Description="Name of the variable to delete"))
|
||||
FString Variable;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Remove a member variable from a Blueprint.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBlueprintVar Editor(BP, Variable);
|
||||
if (Editor.NotFound()) return;
|
||||
|
||||
FBlueprintEditorUtils::RemoveMemberVariable(BP, Editor.Desc->VarName);
|
||||
|
||||
UWingServer::Printf(TEXT("Removed variable %s from %s\n"),
|
||||
*Variable, *WingUtils::FormatName(BP));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingBlueprintVar.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "BlueprintVariable_Dump.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_BlueprintVariable_Dump : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
|
||||
UPROPERTY(meta=(Description="Name of the variable to inspect"))
|
||||
FString Variable;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Show all editable properties of a Blueprint variable.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBlueprintVar Editor(BP, Variable);
|
||||
if (Editor.NotFound()) return;
|
||||
|
||||
UWingServer::Printf(TEXT("Variable %s in %s:\n"), *Variable, *WingUtils::FormatName(BP));
|
||||
Editor.Dump();
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingBlueprintVar.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingTypes.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "BlueprintVariable_Modify.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_BlueprintVariable_Modify : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
||||
FString Blueprint;
|
||||
|
||||
UPROPERTY(meta=(Description="Name of the variable to modify"))
|
||||
FString Variable;
|
||||
|
||||
UPROPERTY(meta=(Description="Properties to change: VarType, Category, DefaultValue, InstanceEditable, BlueprintReadOnly, ExposeOnSpawn, Private, ExposeToCinematics, etc."))
|
||||
FWingJsonObject Properties;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Modify properties of an existing Blueprint variable.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UBlueprint* BP = F.Walk(Blueprint).Cast<UBlueprint>();
|
||||
if (!BP) return;
|
||||
|
||||
FBlueprintVar Editor(BP, Variable);
|
||||
if (Editor.NotFound()) return;
|
||||
|
||||
if (!Properties.Json || Properties.Json->Values.Num() == 0)
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: No properties specified\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Editor.ApplyJson(Properties.Json.Get()))
|
||||
return;
|
||||
|
||||
UWingServer::Printf(TEXT("Modified variable %s (%s) in %s\n"),
|
||||
*Variable, *UWingTypes::TypeToText(Editor.Desc->VarType), *WingUtils::FormatName(BP));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
#include "Blueprint_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Blueprint_Create : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new Blueprint"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(meta=(Description="Parent class, expressed as a type"))
|
||||
FString ParentClass;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Normal, Interface, FunctionLibrary, or MacroLibrary"))
|
||||
TEnumAsByte<EBlueprintType> BlueprintType = BPTYPE_Normal;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create a new Blueprint asset with a specified parent class and type.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(AssetPath);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Resolve parent class based on blueprint type
|
||||
UClass* ParentClassObj = nullptr;
|
||||
switch (BlueprintType)
|
||||
{
|
||||
case BPTYPE_Normal:
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_MacroLibrary:
|
||||
ParentClassObj = UWingTypes::TextToOneObjectType(ParentClass);
|
||||
if (!ParentClassObj) return;
|
||||
break;
|
||||
case BPTYPE_Interface:
|
||||
ParentClassObj = UInterface::StaticClass();
|
||||
break;
|
||||
case BPTYPE_FunctionLibrary:
|
||||
ParentClassObj = UBlueprintFunctionLibrary::StaticClass();
|
||||
break;
|
||||
default:
|
||||
UWingServer::Print(TEXT("ERROR: BlueprintType must be Normal, Interface, FunctionLibrary, or MacroLibrary\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the package and Blueprint
|
||||
if (!Maker.Make()) return;
|
||||
|
||||
UBlueprint* NewBP = FKismetEditorUtilities::CreateBlueprint(
|
||||
ParentClassObj,
|
||||
Maker.Package(),
|
||||
FName(*Maker.Name()),
|
||||
BlueprintType,
|
||||
UBlueprint::StaticClass(),
|
||||
UBlueprintGeneratedClass::StaticClass()
|
||||
);
|
||||
|
||||
if (!NewBP)
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: FKismetEditorUtilities::CreateBlueprint returned null\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Compile and save
|
||||
FKismetEditorUtilities::CompileBlueprint(NewBP);
|
||||
bool bSaved = WingUtils::SaveBlueprintPackage(NewBP);
|
||||
|
||||
// Report result
|
||||
UWingServer::Printf(TEXT("Created: %s\n"), *WingUtils::FormatName(NewBP));
|
||||
if (!bSaved) UWingServer::Print(TEXT("Warning: save failed\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "Editor.h"
|
||||
#include "Subsystems/AssetEditorSubsystem.h"
|
||||
#include "Editor_ListOpenAssets.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Editor_ListOpenAssets : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all currently open asset editors, showing which has focus and whether they have unsaved changes.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
UAssetEditorSubsystem* Sub = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>();
|
||||
if (!Sub)
|
||||
{
|
||||
UWingServer::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"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (UObject* Asset : EditedAssets)
|
||||
{
|
||||
bool bDirty = Asset->GetOutermost()->IsDirty();
|
||||
|
||||
UWingServer::Printf(TEXT(" %s%s\n"),
|
||||
bDirty ? TEXT("[unsaved] ") : TEXT(""),
|
||||
*Asset->GetPathName());
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "Editor.h"
|
||||
#include "Subsystems/AssetEditorSubsystem.h"
|
||||
#include "Editor_OpenAsset.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Editor_OpenAsset : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Asset to open"))
|
||||
FString Asset;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Open an asset in its editor and bring it to focus.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
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"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Sub->OpenEditorForAsset(Obj))
|
||||
UWingServer::Printf(TEXT("Opened editor for %s\n"), *Obj->GetPathName());
|
||||
else
|
||||
UWingServer::Printf(TEXT("Error: Could not open editor for %s\n"), *Obj->GetPathName());
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingToolMenu.h"
|
||||
#include "WingServer.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "GraphNode_ChooseMenu.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_ChooseMenu : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target node"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Description="Menu item as shown by GraphNode_ShowMenu"))
|
||||
FString Item;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Execute a context menu action on a node or pin. "
|
||||
"Supports SplitStructPin, AddPin, AddArrayElementPin, etc. "
|
||||
"Use GraphNode_ShowMenu to see available actions. ");
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!NodeObj) return;
|
||||
|
||||
FToolMenuContext Context;
|
||||
TArray<FToolMenuEntry> Entries = WingToolMenu::GetMenuItems(NodeObj, Context);
|
||||
for (FToolMenuEntry &Entry : Entries)
|
||||
{
|
||||
FString LabelText = Entry.Label.Get().ToString();
|
||||
if (!LabelText.Equals(Item, ESearchCase::IgnoreCase))
|
||||
continue;
|
||||
|
||||
if (WingToolMenu::Execute(Entry, Context))
|
||||
{
|
||||
UWingServer::Printf(TEXT("Executed: %s\n"), *LabelText);
|
||||
}
|
||||
else
|
||||
{
|
||||
UWingServer::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);
|
||||
}
|
||||
};
|
||||
102
Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h
Normal file
102
Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Create.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "GraphNode_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FSpawnNodeEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString ActionName;
|
||||
|
||||
UPROPERTY()
|
||||
int32 PosX = 0;
|
||||
|
||||
UPROPERTY()
|
||||
int32 PosY = 0;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_Create : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {Type, posX, posY} objects. Use GraphNode_SearchTypes to find types."))
|
||||
FWingJsonArray Nodes;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create nodes using the editor's action database. "
|
||||
"Use GraphNode_SearchTypes to find types.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!TargetGraph) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
int32 TotalCount = Nodes.Array.Num();
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
||||
{
|
||||
FSpawnNodeEntry Entry;
|
||||
if (!WingJson::PopulateFromJson(FSpawnNodeEntry::StaticStruct(), &Entry, NodeVal))
|
||||
continue;
|
||||
|
||||
// Find the action by exact full name
|
||||
TArray<TSharedPtr<FEdGraphSchemaAction>> Matches = WingUtils::SearchGraphActions(TargetGraph, Entry.ActionName, 0, /*ExactMatch=*/true);
|
||||
if (Matches.Num() == 0)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: No action found matching '%s'. Use GraphNodeSearchTypes to find available actions.\n"),
|
||||
*Entry.ActionName);
|
||||
continue;
|
||||
}
|
||||
if (Matches.Num() > 1)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Ambiguous: %d actions match '%s'.\n"),
|
||||
Matches.Num(), *Entry.ActionName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Perform the action
|
||||
FVector2D Location(Entry.PosX, Entry.PosY);
|
||||
UEdGraphNode* NewNode = Matches[0]->PerformAction(TargetGraph, nullptr, Location, /*bSelectNewNode=*/false);
|
||||
if (!NewNode)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: PerformAction returned null for '%s'.\n"), *Entry.ActionName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!NewNode->NodeGuid.IsValid())
|
||||
NewNode->CreateNewGuid();
|
||||
|
||||
UWingServer::Printf(TEXT("Spawned: %s (%s)\n"),
|
||||
*WingUtils::FormatName(NewNode), *WingUtils::FormatName(NewNode->GetClass()));
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Spawned %d/%d nodes.\n"), SuccessCount, TotalCount);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingServer.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "IMaterialEditor.h"
|
||||
#include "GraphNode_Delete.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_Delete : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Node to delete"))
|
||||
FString Node;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Delete a node from a graph. "
|
||||
"Cannot delete undeletable nodes (entry points, root nodes, etc).");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!FoundNode) return;
|
||||
|
||||
UEdGraph* Graph = FoundNode->GetGraph();
|
||||
FString NodeTitle = WingUtils::FormatName(FoundNode);
|
||||
FString GraphName = WingUtils::FormatName(Graph);
|
||||
|
||||
if (!FoundNode->CanUserDeleteNode())
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Cannot delete node '%s' in graph '%s' — it is not deletable.\n"),
|
||||
*NodeTitle, *GraphName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Cast<UMaterialGraphNode>(FoundNode))
|
||||
{
|
||||
// Use the material editor's DeleteNodes to properly remove
|
||||
// both the graph node and the underlying material expression.
|
||||
IMaterialEditor* MatEditor = F.CastEditor<UMaterial, IMaterialEditor>();
|
||||
if (!MatEditor) return;
|
||||
MatEditor->DeleteNodes({FoundNode});
|
||||
}
|
||||
else
|
||||
{
|
||||
FoundNode->BreakAllNodeLinks();
|
||||
Graph->RemoveNode(FoundNode);
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Deleted node '%s' from graph '%s'.\n"), *NodeTitle, *GraphName);
|
||||
}
|
||||
};
|
||||
40
Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Dump.h
Normal file
40
Plugins/UEWingman/Source/UEWingman/Handlers/GraphNode_Dump.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingGraphExport.h"
|
||||
#include "GraphNode_Dump.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_Dump : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target node"))
|
||||
FString Node;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Dump a single node as readable text, including all pins and connections.");
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!NodeObj) return;
|
||||
|
||||
WingGraphExport Exporter(NodeObj);
|
||||
UWingServer::Print(*Exporter.GetOutput());
|
||||
UWingServer::Print(Exporter.GetDetails());
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "GraphNode_Duplicate.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_Duplicate : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of node names to duplicate (as returned by FormatName)"))
|
||||
FWingJsonArray Nodes;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="X offset for duplicated nodes"))
|
||||
int32 OffsetX = 50;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Y offset for duplicated nodes"))
|
||||
int32 OffsetY = 50;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Duplicate one or more nodes in a Blueprint graph. "
|
||||
"Creates copies offset from the originals with new GUIDs. "
|
||||
"Connections are not preserved on the duplicates.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!TargetGraph) return;
|
||||
|
||||
if (Nodes.Array.Num() == 0)
|
||||
{
|
||||
UWingServer::Print(TEXT("ERROR: Nodes array is empty\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all source nodes by name
|
||||
TArray<UEdGraphNode*> SourceNodes;
|
||||
for (const TSharedPtr<FJsonValue>& IdVal : Nodes.Array)
|
||||
{
|
||||
FString Name = IdVal->AsString();
|
||||
UEdGraphNode* Found = nullptr;
|
||||
for (UEdGraphNode* Node : TargetGraph->Nodes)
|
||||
{
|
||||
if (WingUtils::Identifies(Name, Node))
|
||||
{
|
||||
Found = Node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!Found)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Node '%s' not found in graph\n"), *Name);
|
||||
continue;
|
||||
}
|
||||
SourceNodes.Add(Found);
|
||||
}
|
||||
|
||||
if (SourceNodes.Num() == 0) return;
|
||||
|
||||
// Duplicate each node
|
||||
for (UEdGraphNode* SourceNode : SourceNodes)
|
||||
{
|
||||
UEdGraphNode* NewNode = DuplicateObject<UEdGraphNode>(SourceNode, TargetGraph);
|
||||
if (!NewNode)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Failed to duplicate %s\n"), *WingUtils::FormatName(SourceNode));
|
||||
continue;
|
||||
}
|
||||
|
||||
NewNode->CreateNewGuid();
|
||||
NewNode->NodePosX += OffsetX;
|
||||
NewNode->NodePosY += OffsetY;
|
||||
|
||||
for (UEdGraphPin* Pin : NewNode->Pins)
|
||||
{
|
||||
if (Pin)
|
||||
Pin->LinkedTo.Empty();
|
||||
}
|
||||
|
||||
TargetGraph->AddNode(NewNode, false, false);
|
||||
UWingServer::Printf(TEXT("Duplicated: %s -> %s\n"), *WingUtils::FormatName(SourceNode), *WingUtils::FormatName(NewNode));
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "GraphNode_GetComment.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_GetComment : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target node"))
|
||||
FString Node;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Get the comment text and bubble visibility of a node.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
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"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphSchema.h"
|
||||
#include "GraphNode_SearchTypes.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_SearchTypes : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Search query string"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results (default 50, max 500)"))
|
||||
int32 MaxResults = 50;
|
||||
|
||||
UPROPERTY(meta=(Description="Target graph (needed for context-sensitive results)"))
|
||||
FString Graph;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search the action database for node types that can be spawned in a graph. "
|
||||
"Works with any graph type (Blueprint, Material, etc.). "
|
||||
"Returns full action names for use with GraphNodeCreate.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
int32 ClampedMax = FMath::Clamp(MaxResults, 1, 500);
|
||||
|
||||
WingFetcher F;
|
||||
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!TargetGraph) return;
|
||||
|
||||
TArray<TSharedPtr<FEdGraphSchemaAction>> Actions = WingUtils::SearchGraphActions(TargetGraph, Query, ClampedMax, /*ExactMatch=*/false);
|
||||
|
||||
for (const TSharedPtr<FEdGraphSchemaAction>& Action : Actions)
|
||||
{
|
||||
UWingServer::Printf(TEXT("%s\n"), *WingUtils::ActionFullName(Action));
|
||||
}
|
||||
|
||||
if (Actions.Num() == 0)
|
||||
{
|
||||
UWingServer::Print(TEXT("No matching node types found.\n"));
|
||||
}
|
||||
else if (Actions.Num() >= ClampedMax)
|
||||
{
|
||||
UWingServer::Printf(TEXT("WARNING: Reached limit of %d results. Refine your query or increase MaxResults.\n"), ClampedMax);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingFunctionArgs.h"
|
||||
#include "GraphNode_SetArgs.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_SetArgs : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Path to a graph node (function entry, function result, custom event, or tunnel)"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Description="Comma-separated args, e.g. 'int x, float y'"))
|
||||
FString Args;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set the user-defined pins (arguments or return values) on a function entry, result, custom event, or tunnel node.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraphNode* NodeObj = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!NodeObj) return;
|
||||
|
||||
if (!WingFunctionArgs::HasArgs(NodeObj))
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Node does not support editable args\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!WingFunctionArgs::SetArgs(NodeObj, Args)) return;
|
||||
|
||||
UWingServer::Printf(TEXT("Args set to: %s\n"), *WingFunctionArgs::GetArgs(NodeObj));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "GraphNode_SetComment.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_SetComment : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target node"))
|
||||
FString Node;
|
||||
|
||||
UPROPERTY(meta=(Description="Comment text to set"))
|
||||
FString Comment;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set a node's comment text, and make the comment visible. "
|
||||
"Setting empty text will cause the comment to vanish.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraphNode* FoundNode = F.Walk(Node).Cast<UEdGraphNode>();
|
||||
if (!FoundNode) return;
|
||||
|
||||
FoundNode->NodeComment = Comment;
|
||||
FoundNode->bCommentBubbleVisible = !Comment.IsEmpty();
|
||||
FoundNode->bCommentBubblePinned = !Comment.IsEmpty();
|
||||
|
||||
UWingServer::Printf(TEXT("Comment set on %s\n"), *WingUtils::FormatName(FoundNode));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "MaterialGraph/MaterialGraphSchema.h"
|
||||
#include "GraphNode_SetDefaults.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FSetNodeDefaultEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString Node;
|
||||
|
||||
UPROPERTY()
|
||||
FString Name;
|
||||
|
||||
UPROPERTY()
|
||||
FString Value;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_SetDefaults : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {node, name, value} objects"))
|
||||
FWingJsonArray Pins;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set the default value of input pins or material expression properties on nodes.");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// K2 graphs: set pin default values.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void HandleK2Entry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj, const UEdGraphSchema_K2* K2Schema)
|
||||
{
|
||||
WingFetcher F(GraphObj);
|
||||
UEdGraphPin* Pin = F.Node(Entry.Node).Pin(Entry.Name).Cast<UEdGraphPin>();
|
||||
if (!Pin) return;
|
||||
|
||||
UEdGraphNode* Node = Pin->GetOwningNode();
|
||||
|
||||
if (Pin->Direction != EGPD_Input)
|
||||
{
|
||||
UWingServer::Printf(TEXT("error: %s is an output pin\n"), *WingUtils::FormatName(Pin));
|
||||
return;
|
||||
}
|
||||
|
||||
Pin->Modify();
|
||||
|
||||
FString UseDefaultValue;
|
||||
TObjectPtr<UObject> UseDefaultObject = nullptr;
|
||||
FText UseDefaultText;
|
||||
K2Schema->GetPinDefaultValuesFromString(Pin->PinType, Node, Entry.Value, UseDefaultValue, UseDefaultObject, UseDefaultText, false);
|
||||
FString Error = K2Schema->IsPinDefaultValid(Pin, UseDefaultValue, UseDefaultObject, UseDefaultText);
|
||||
if (!Error.IsEmpty())
|
||||
{
|
||||
UWingServer::Printf(TEXT("error: %s: %s\n"), *WingUtils::FormatName(Pin), *Error);
|
||||
return;
|
||||
}
|
||||
UWingServer::AddTouchedObject(Node);
|
||||
K2Schema->TrySetDefaultValue(*Pin, Entry.Value);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Material graphs: set material expression properties.
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
void HandleMaterialEntry(const FSetNodeDefaultEntry& Entry, UEdGraph* GraphObj)
|
||||
{
|
||||
WingFetcher F(GraphObj);
|
||||
UEdGraphNode* Node = F.Node(Entry.Node).Cast<UEdGraphNode>();
|
||||
if (!Node) return;
|
||||
|
||||
TArray<WingProperty> All = WingProperty::GetAll(Node, CPF_Edit);
|
||||
WingProperty P = WingProperty::FindOneExactMatch(All, Entry.Name);
|
||||
if (!P) return;
|
||||
|
||||
UWingServer::AddTouchedObject(Node);
|
||||
|
||||
if (!P.SetText(Entry.Value))
|
||||
return;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Fetch the graph once.
|
||||
WingFetcher GraphFetcher;
|
||||
UEdGraph* GraphObj = GraphFetcher.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!GraphObj) return;
|
||||
|
||||
const UEdGraphSchema* Schema = GraphObj->GetSchema();
|
||||
const UEdGraphSchema_K2* K2Schema = Cast<UEdGraphSchema_K2>(Schema);
|
||||
const UMaterialGraphSchema* MGSchema = Cast<UMaterialGraphSchema>(Schema);
|
||||
|
||||
if (!K2Schema && !MGSchema)
|
||||
{
|
||||
UWingServer::Printf(TEXT("error: unsupported graph schema %s\n"), *Schema->GetClass()->GetName());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& PinVal : Pins.Array)
|
||||
{
|
||||
FSetNodeDefaultEntry Entry;
|
||||
if (!WingJson::PopulateFromJson(FSetNodeDefaultEntry::StaticStruct(), &Entry, PinVal))
|
||||
continue;
|
||||
|
||||
if (K2Schema)
|
||||
HandleK2Entry(Entry, GraphObj, K2Schema);
|
||||
else if (MGSchema)
|
||||
HandleMaterialEntry(Entry, GraphObj);
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Done.\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraphNode.h"
|
||||
#include "GraphNode_SetPositions.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FMoveNodeEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString Node;
|
||||
|
||||
UPROPERTY()
|
||||
int32 X = 0;
|
||||
|
||||
UPROPERTY()
|
||||
int32 Y = 0;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_SetPositions : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {node, x, y} objects"))
|
||||
FWingJsonArray Nodes;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Reposition one or more nodes in a Blueprint graph.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph* TargetGraph = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!TargetGraph) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& NodeVal : Nodes.Array)
|
||||
{
|
||||
FMoveNodeEntry Entry;
|
||||
if (!WingJson::PopulateFromJson(FMoveNodeEntry::StaticStruct(), &Entry, NodeVal)) continue;
|
||||
|
||||
WingFetcher FN(TargetGraph);
|
||||
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());
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingToolMenu.h"
|
||||
#include "WingServer.h"
|
||||
#include "ToolMenus.h"
|
||||
#include "MaterialGraph/MaterialGraphNode.h"
|
||||
#include "GraphNode_ShowMenu.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphNode_ShowMenu : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target node"))
|
||||
FString Node;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Show context menu actions available for a node and its pins.");
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
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."));
|
||||
return;
|
||||
}
|
||||
FToolMenuContext Context;
|
||||
TArray<FToolMenuEntry> Entries = WingToolMenu::GetMenuItems(NodeObj, Context);
|
||||
for (FToolMenuEntry &Entry : Entries)
|
||||
{
|
||||
FString LabelText = Entry.Label.Get().ToString();
|
||||
UWingServer::Printf(TEXT("%s\n"), *LabelText);
|
||||
}
|
||||
if (Entries.IsEmpty()) UWingServer::Printf(TEXT("No selectable menu entries right now.\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "GraphPin_Connect.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FConnectPinsEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString SourcePin;
|
||||
|
||||
UPROPERTY()
|
||||
FString TargetPin;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphPin_Connect : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {sourcePin, targetPin} objects"))
|
||||
FWingJsonArray Connections;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Connect pins between nodes in a graph (Blueprint or Material).");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
int32 TotalCount = Connections.Array.Num();
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& ConnVal : Connections.Array)
|
||||
{
|
||||
FConnectPinsEntry Entry;
|
||||
if (!WingJson::PopulateFromJson(FConnectPinsEntry::StaticStruct(), &Entry, ConnVal))
|
||||
continue;
|
||||
|
||||
WingFetcher FS(G);
|
||||
UEdGraphPin* SourcePin = FS.Walk(Entry.SourcePin).Cast<UEdGraphPin>();
|
||||
if (!SourcePin) continue;
|
||||
|
||||
WingFetcher FT(G);
|
||||
UEdGraphPin* TargetPin = FT.Walk(Entry.TargetPin).Cast<UEdGraphPin>();
|
||||
if (!TargetPin) continue;
|
||||
|
||||
const UEdGraphSchema* Schema = G->GetSchema();
|
||||
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"),
|
||||
*WingUtils::FormatName(SourcePin->GetOwningNode()), *WingUtils::FormatName(SourcePin),
|
||||
*WingUtils::FormatName(TargetPin->GetOwningNode()), *WingUtils::FormatName(TargetPin),
|
||||
*Response.Message.ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
Schema->TryCreateConnection(SourcePin, TargetPin);
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
UWingServer::Printf(TEXT("Connected %d/%d pins.\n"), SuccessCount, TotalCount);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "GraphPin_Disconnect.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
USTRUCT()
|
||||
struct FDisconnectPinEntry
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
UPROPERTY()
|
||||
FString Pin;
|
||||
|
||||
UPROPERTY(meta=(Optional))
|
||||
FString TargetPin;
|
||||
};
|
||||
|
||||
|
||||
UCLASS()
|
||||
class UWing_GraphPin_Disconnect : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Description="Array of {pin, targetPin?} objects. If targetPin is omitted, all connections on the pin are broken."))
|
||||
FWingJsonArray Disconnections;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Disconnect pins in a graph (Blueprint or Material). "
|
||||
"Can disconnect a specific link or all links on a pin.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph* G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
int32 SuccessCount = 0;
|
||||
int32 TotalDisconnected = 0;
|
||||
|
||||
for (const TSharedPtr<FJsonValue>& DiscVal : Disconnections.Array)
|
||||
{
|
||||
FDisconnectPinEntry Entry;
|
||||
if (!WingJson::PopulateFromJson(FDisconnectPinEntry::StaticStruct(), &Entry, DiscVal)) continue;
|
||||
|
||||
WingFetcher FP(G);
|
||||
UEdGraphPin* Pin = FP.Walk(Entry.Pin).Cast<UEdGraphPin>();
|
||||
if (!Pin) continue;
|
||||
|
||||
int32 DisconnectedCount = 0;
|
||||
|
||||
if (!Entry.TargetPin.IsEmpty())
|
||||
{
|
||||
WingFetcher FT(G);
|
||||
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"),
|
||||
*WingUtils::FormatName(Pin->GetOwningNode()), *WingUtils::FormatName(Pin),
|
||||
*WingUtils::FormatName(Target->GetOwningNode()), *WingUtils::FormatName(Target));
|
||||
continue;
|
||||
}
|
||||
|
||||
Pin->BreakLinkTo(Target);
|
||||
DisconnectedCount = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
DisconnectedCount = Pin->LinkedTo.Num();
|
||||
if (DisconnectedCount > 0)
|
||||
{
|
||||
Pin->BreakAllPinLinks(true);
|
||||
}
|
||||
}
|
||||
|
||||
UWingServer::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"),
|
||||
SuccessCount, Disconnections.Array.Num(), TotalDisconnected);
|
||||
}
|
||||
};
|
||||
54
Plugins/UEWingman/Source/UEWingman/Handlers/Graph_Dump.h
Normal file
54
Plugins/UEWingman/Source/UEWingman/Handlers/Graph_Dump.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingGraphExport.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialGraph/MaterialGraph.h"
|
||||
#include "Graph_Dump.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Graph_Dump : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Path to graph"))
|
||||
FString Graph;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="True to include less-significant details"))
|
||||
bool bDetails;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Dump blueprint or material graphs as readable text. ");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UEdGraph *G = F.Walk(Graph).Cast<UEdGraph>();
|
||||
if (!G) return;
|
||||
|
||||
WingGraphExport Exporter(G);
|
||||
UWingServer::Print(*Exporter.GetOutput());
|
||||
if (bDetails)
|
||||
{
|
||||
UWingServer::Print(Exporter.GetDetails());
|
||||
}
|
||||
else
|
||||
{
|
||||
UWingServer::Printf(TEXT("Note: use bDetails=true to see suppressed details."));
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingMaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "MaterialInstance_ClearParameter.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_MaterialInstance_ClearParameter : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target material instance"))
|
||||
FString MaterialInstance;
|
||||
|
||||
UPROPERTY(meta=(Description="Parameter name to clear"))
|
||||
FString Parameter;
|
||||
|
||||
UPROPERTY(meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional))
|
||||
FString ParameterAssociation = TEXT("Global");
|
||||
|
||||
UPROPERTY(meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional))
|
||||
int32 ParameterLayer = INDEX_NONE;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Remove a parameter override from a Material Instance, reverting it to the parent material's value.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
|
||||
if (!MI) return;
|
||||
|
||||
// Parse the association string.
|
||||
EMaterialParameterAssociation Association;
|
||||
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
return;
|
||||
|
||||
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
|
||||
|
||||
// Remove the override from all parameter arrays.
|
||||
auto RemoveFrom = [&](auto& Arr) {
|
||||
return Arr.RemoveAll([&](auto& Entry) { return Entry.ParameterInfo == ParamID; });
|
||||
};
|
||||
|
||||
int32 Removed = 0;
|
||||
Removed += RemoveFrom(MI->ScalarParameterValues);
|
||||
Removed += RemoveFrom(MI->VectorParameterValues);
|
||||
Removed += RemoveFrom(MI->DoubleVectorParameterValues);
|
||||
Removed += RemoveFrom(MI->TextureParameterValues);
|
||||
Removed += RemoveFrom(MI->TextureCollectionParameterValues);
|
||||
Removed += RemoveFrom(MI->RuntimeVirtualTextureParameterValues);
|
||||
Removed += RemoveFrom(MI->SparseVolumeTextureParameterValues);
|
||||
Removed += RemoveFrom(MI->FontParameterValues);
|
||||
|
||||
if (Removed == 0)
|
||||
{
|
||||
UWingServer::Printf(TEXT("No override found for parameter '%s' (association=%s layer=%d) on %s"),
|
||||
*Parameter, *ParameterAssociation, ParameterLayer, *WingUtils::FormatName(MI));
|
||||
return;
|
||||
}
|
||||
|
||||
WingUtils::SaveGenericPackage(MI);
|
||||
UWingServer::Printf(TEXT("Cleared override for '%s' on %s\n"),
|
||||
*Parameter, *WingUtils::FormatName(MI));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "MaterialInstance_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_MaterialInstance_Create : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new Material Instance (e.g. '/Game/Materials/MI_GoldShiny')"))
|
||||
FString AssetPath;
|
||||
|
||||
UPROPERTY(meta=(Description="Parent material package path (Material or Material Instance)"))
|
||||
FString ParentMaterial;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create a new Material Instance Constant asset with a specified parent material.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(AssetPath);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Load parent material by package path.
|
||||
WingFetcher F;
|
||||
UObject* ParentObj = F.Asset(ParentMaterial).GetObj();
|
||||
UMaterialInterface* ParentMaterialObj = ParentObj ? Cast<UMaterialInterface>(ParentObj) : nullptr;
|
||||
if (!ParentMaterialObj)
|
||||
{
|
||||
UWingServer::Printf(TEXT("ERROR: Parent material '%s' not found or not a material\n"), *ParentMaterial);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create via factory + AssetTools.
|
||||
UMaterialInstanceConstant* MI = Maker.CreateAsset<UMaterialInstanceConstant, UMaterialInstanceConstantFactoryNew>();
|
||||
if (!MI) return;
|
||||
|
||||
// Set parent.
|
||||
MI->Parent = ParentMaterialObj;
|
||||
|
||||
// Save.
|
||||
bool bSaved = WingUtils::SaveGenericPackage(MI);
|
||||
|
||||
UWingServer::Printf(TEXT("Created %s\n"), *MI->GetPathName());
|
||||
if (UMaterialInstance* ParentMI = Cast<UMaterialInstance>(ParentMaterialObj))
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *WingUtils::FormatName(ParentMI));
|
||||
else if (UMaterial* ParentMat = Cast<UMaterial>(ParentMaterialObj))
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *WingUtils::FormatName(ParentMat));
|
||||
else
|
||||
UWingServer::Printf(TEXT("Parent: %s\n"), *ParentMaterialObj->GetPathName());
|
||||
if (!bSaved)
|
||||
UWingServer::Print(TEXT("WARNING: Package save failed\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingMaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "MaterialInstance_DumpParameters.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_MaterialInstance_DumpParameters : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target material instance"))
|
||||
FString MaterialInstance;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all parameters on a Material Instance, showing current values and which are overridden.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
|
||||
if (!MI) return;
|
||||
|
||||
auto AllParams = WingMaterialParameter::GetMaterialParameters(MI);
|
||||
|
||||
// Overridden parameters first.
|
||||
bool bHasOverrides = false;
|
||||
for (auto& [Info, Meta] : AllParams)
|
||||
{
|
||||
if (!Meta.bOverride) continue;
|
||||
if (!bHasOverrides) { UWingServer::Print(TEXT("\nOverridden Parameters:\n")); bHasOverrides = true; }
|
||||
WingMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
|
||||
// Inherited (non-overridden) parameters.
|
||||
bool bHasInherited = false;
|
||||
for (auto& [Info, Meta] : AllParams)
|
||||
{
|
||||
if (Meta.bOverride) continue;
|
||||
if (!bHasInherited) { UWingServer::Print(TEXT("\nInherited Parameters (not overridden):\n")); bHasInherited = true; }
|
||||
WingMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingMaterialParameter.h"
|
||||
#include "Materials/MaterialInstanceConstant.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "Misc/DefaultValueHelper.h"
|
||||
#include "MaterialInstance_SetParameter.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_MaterialInstance_SetParameter : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target material instance"))
|
||||
FString MaterialInstance;
|
||||
|
||||
UPROPERTY(meta=(Description="Parameter name to set"))
|
||||
FString Parameter;
|
||||
|
||||
UPROPERTY(meta=(Description="Parameter association: 'Global', 'Layer', or 'Blend'. Default: 'Global'", Optional))
|
||||
FString ParameterAssociation = TEXT("Global");
|
||||
|
||||
UPROPERTY(meta=(Description="Layer/blend index (0-based). Only used when ParameterAssociation is 'Layer' or 'Blend'", Optional))
|
||||
int32 ParameterLayer = INDEX_NONE;
|
||||
|
||||
UPROPERTY(meta=(Description="Value to set (uses Unreal text format, e.g. '0.5' for scalar, '(R=1,G=0,B=0,A=1)' for vector)"))
|
||||
FString Value;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set a parameter override on a Material Instance.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UMaterialInstanceConstant* MI = F.Asset(MaterialInstance).Cast<UMaterialInstanceConstant>();
|
||||
if (!MI) return;
|
||||
|
||||
// Parse the association string.
|
||||
EMaterialParameterAssociation Association;
|
||||
if (!WingMaterialParameter::ParseMaterialParameterAssociation(ParameterAssociation, Association))
|
||||
return;
|
||||
|
||||
// Build the parameter ID to look up.
|
||||
FMaterialParameterInfo ParamID(*Parameter, Association, ParameterLayer);
|
||||
|
||||
// Find it in the material's parameter map.
|
||||
auto AllParams = WingMaterialParameter::GetMaterialParameters(MI);
|
||||
FMaterialParameterMetadata* Found = AllParams.Find(ParamID);
|
||||
if (!Found)
|
||||
{
|
||||
UWingServer::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);
|
||||
return;
|
||||
}
|
||||
|
||||
EMaterialParameterType Type = Found->Value.Type;
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case EMaterialParameterType::Scalar:
|
||||
{
|
||||
float ScalarValue;
|
||||
if (!FDefaultValueHelper::ParseFloat(Value, ScalarValue))
|
||||
{
|
||||
UWingServer::Printf(TEXT("Failed to parse '%s' as a float"), *Value);
|
||||
return;
|
||||
}
|
||||
MI->SetScalarParameterValueEditorOnly(ParamID, ScalarValue);
|
||||
break;
|
||||
}
|
||||
case EMaterialParameterType::Vector:
|
||||
{
|
||||
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);
|
||||
return;
|
||||
}
|
||||
MI->SetVectorParameterValueEditorOnly(ParamID, Color);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UWingServer::Printf(TEXT("Parameters of type %d (see EMaterialParameterType) are not implemented"), (int)Type);
|
||||
return;
|
||||
}
|
||||
WingUtils::SaveGenericPackage(MI);
|
||||
|
||||
UWingServer::Printf(TEXT("Set '%s' = %s on %s\n"),
|
||||
*Parameter, *Value, *WingUtils::FormatName(MI));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Material_Compile.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Material_Compile : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Material name or package path"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Force recompile a material and check for compilation errors.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Load material
|
||||
WingFetcher F;
|
||||
UMaterial* MaterialObj = F.Asset(Material).Cast<UMaterial>();
|
||||
if (!MaterialObj) return;
|
||||
|
||||
// Force recompile
|
||||
MaterialObj->ForceRecompileForRendering();
|
||||
|
||||
// Wait for compilation to finish, then check for errors
|
||||
FMaterialResource* Resource = MaterialObj->GetMaterialResource(GMaxRHIFeatureLevel);
|
||||
TArray<FString> Errors;
|
||||
if (Resource)
|
||||
{
|
||||
Resource->FinishCompilation();
|
||||
Errors = Resource->GetCompileErrors();
|
||||
}
|
||||
|
||||
if (Errors.IsEmpty())
|
||||
{
|
||||
UWingServer::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());
|
||||
for (const FString& Err : Errors)
|
||||
{
|
||||
UWingServer::Printf(TEXT(" %s\n"), *Err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "MaterialDomain.h"
|
||||
#include "Factories/MaterialFactoryNew.h"
|
||||
#include "WingPackageMaker.h"
|
||||
#include "Material_Create.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Material_Create : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Full asset path for the new material"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Create a new UMaterial asset");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingPackageMaker Maker(Material);
|
||||
if (!Maker.Ok()) return;
|
||||
|
||||
// Create via IAssetTools + factory.
|
||||
UMaterial* MaterialObj = Maker.CreateAsset<UMaterial, UMaterialFactoryNew>();
|
||||
if (!MaterialObj) return;
|
||||
|
||||
bool bSaved = WingUtils::SaveGenericPackage(MaterialObj);
|
||||
UWingServer::Printf(TEXT("Created %s\n"), *MaterialObj->GetPathName());
|
||||
if (!bSaved) UWingServer::Print(TEXT("WARNING: Package save failed\n"));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingUtils.h"
|
||||
#include "WingMaterialParameter.h"
|
||||
#include "MaterialTypes.h"
|
||||
#include "Material_DumpParameters.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Material_DumpParameters : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Material path"))
|
||||
FString Material;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all parameters on a Material, showing their default values.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UMaterial* Mat = F.Asset(Material).Cast<UMaterial>();
|
||||
if (!Mat) return;
|
||||
|
||||
auto AllParams = WingMaterialParameter::GetMaterialParameters(Mat);
|
||||
|
||||
for (auto& [Info, Meta] : AllParams)
|
||||
{
|
||||
WingMaterialParameter::FormatMaterialParameter(Info, Meta);
|
||||
}
|
||||
if (AllParams.IsEmpty()) UWingServer::Printf(TEXT("No material parameters.\n"));
|
||||
}
|
||||
};
|
||||
98
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h
Normal file
98
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Dump.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Property_Dump.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Dump : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Substring filter for property names"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Truncate values to 80 characters (default true)"))
|
||||
bool Truncate = true;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Only show properties declared on the object's own class, not inherited ones"))
|
||||
bool Local = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all blueprint-visible properties, showing current values and which are editable.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Resolve the path to an object and get its editable template.
|
||||
WingFetcher F;
|
||||
UObject* Template = F.Walk(Object).Cast<UObject>();
|
||||
if (!Template) return;
|
||||
TArray<WingProperty> AllProps = WingProperty::GetAll(Template, CPF_Edit);
|
||||
TArray<WingProperty> Props = WingProperty::FindAllSubstring(AllProps, Query);
|
||||
if (Local)
|
||||
{
|
||||
UClass* ObjClass = Template->GetClass();
|
||||
Props.RemoveAll([ObjClass](const WingProperty& P) { return P->GetOwnerStruct() != ObjClass; });
|
||||
}
|
||||
|
||||
// Group properties by category.
|
||||
TMap<FString, TArray<WingProperty>> ByCategory;
|
||||
for (WingProperty& P : Props)
|
||||
{
|
||||
FString Category = P->HasMetaData(TEXT("Category")) ? P->GetMetaData(TEXT("Category")) : FString();
|
||||
ByCategory.FindOrAdd(Category).Add(P);
|
||||
}
|
||||
|
||||
// Sort category names, putting empty category last.
|
||||
TArray<FString> Categories;
|
||||
ByCategory.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)
|
||||
{
|
||||
if (Category.IsEmpty())
|
||||
UWingServer::Print(TEXT("\nUncategorized:\n"));
|
||||
else
|
||||
UWingServer::Printf(TEXT("\n%s:\n"), *Category);
|
||||
|
||||
for (WingProperty& P : ByCategory[Category])
|
||||
{
|
||||
FString PropName = WingUtils::FormatName(P.Prop);
|
||||
FString ValueStr = P.GetText();
|
||||
|
||||
if (Truncate && (ValueStr.Len() > 80))
|
||||
ValueStr = ValueStr.Left(80) + TEXT("...");
|
||||
|
||||
bool bEditable = !P->HasAnyPropertyFlags(CPF_EditConst);
|
||||
UWingServer::Printf(TEXT(" %s %s %s = %s\n"),
|
||||
bEditable ? TEXT("editable") : TEXT("readonly"),
|
||||
*UWingTypes::TypeToText(P.Prop),
|
||||
*PropName,
|
||||
*ValueStr);
|
||||
}
|
||||
}
|
||||
|
||||
if (Props.IsEmpty())
|
||||
UWingServer::Print(TEXT(" (no blueprint-visible properties found)\n"));
|
||||
}
|
||||
};
|
||||
46
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h
Normal file
46
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Get.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Property_Get.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Get : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Description="Property name"))
|
||||
FString Property;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Get the value of a single property.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
WingFetcher F;
|
||||
UObject* Obj = F.Walk(Object).Cast<UObject>();
|
||||
if (!Obj) return;
|
||||
|
||||
TArray<WingProperty> All = WingProperty::GetAll(Obj, CPF_Edit);
|
||||
WingProperty P = WingProperty::FindOneExactMatch(All, Property);
|
||||
if (!P) return;
|
||||
|
||||
UWingServer::Print(P.GetText());
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
79
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h
Normal file
79
Plugins/UEWingman/Source/UEWingman/Handlers/Property_Set.h
Normal file
@@ -0,0 +1,79 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingProperty.h"
|
||||
#include "WingUtils.h"
|
||||
#include "Property_Set.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_Property_Set : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Target object"))
|
||||
FString Object;
|
||||
|
||||
UPROPERTY(meta=(Description="Object mapping property names to new values in Unreal text format"))
|
||||
FWingJsonObject Properties;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Set one or more editable properties. Values use Unreal text format.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
// Resolve the path to an object and get its editable template.
|
||||
WingFetcher F;
|
||||
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"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Validation pass — resolve all properties and values before modifying anything.
|
||||
TArray<WingProperty> All = WingProperty::GetAll(Obj, CPF_Edit);
|
||||
TArray<TPair<WingProperty, FString>> Resolved;
|
||||
for (const auto& Pair : Properties.Json->Values)
|
||||
{
|
||||
WingProperty P = WingProperty::FindOneExactMatch(All, Pair.Key);
|
||||
if (!P) return;
|
||||
|
||||
FString ValueStr;
|
||||
if (!Pair.Value->TryGetString(ValueStr))
|
||||
{
|
||||
UWingServer::Printf(TEXT("Error: Value for '%s' must be a string\n"), *Pair.Key);
|
||||
return;
|
||||
}
|
||||
Resolved.Emplace(P, ValueStr);
|
||||
}
|
||||
|
||||
// Apply all changes.
|
||||
int32 SuccessCount = 0;
|
||||
for (auto& [P, ValueStr] : Resolved)
|
||||
{
|
||||
if (!P.SetText(ValueStr))
|
||||
continue;
|
||||
SuccessCount++;
|
||||
}
|
||||
|
||||
// Save.
|
||||
bool bSaved = WingUtils::SaveGenericPackage(Obj);
|
||||
|
||||
UWingServer::Printf(TEXT("Set %d/%d properties.\n"), SuccessCount, Properties.Json->Values.Num());
|
||||
if (!bSaved)
|
||||
UWingServer::Print(TEXT("Warning: Save failed\n"));
|
||||
}
|
||||
};
|
||||
85
Plugins/UEWingman/Source/UEWingman/Handlers/ShowCommands.h
Normal file
85
Plugins/UEWingman/Source/UEWingman/Handlers/ShowCommands.h
Normal file
@@ -0,0 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingFetcher.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingJson.h"
|
||||
#include "WingUtils.h"
|
||||
#include "ShowCommands.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_ShowCommands : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Optional, Description="Substring filter for command names"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="If true, return full details including parameter types and descriptions"))
|
||||
bool Verbose = false;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("List all available commands with their descriptions.");
|
||||
}
|
||||
|
||||
void EmitCommand(UClass* Class)
|
||||
{
|
||||
if (Verbose)
|
||||
{
|
||||
WingUtils::FormatCommandHelp(Class);
|
||||
return;
|
||||
}
|
||||
UWingServer::Print(WingUtils::GetHandlerName(Class));
|
||||
UWingServer::Print(TEXT("("));
|
||||
bool bFirst = true;
|
||||
for (TFieldIterator<FProperty> PropIt(Class, EFieldIterationFlags::None); PropIt; ++PropIt)
|
||||
{
|
||||
if (!bFirst) UWingServer::Print(TEXT(","));
|
||||
bFirst = false;
|
||||
if (PropIt->HasMetaData(TEXT("Optional"))) UWingServer::Print(TEXT("?"));
|
||||
UWingServer::Print(PropIt->GetName());
|
||||
}
|
||||
UWingServer::Print(TEXT(")\n"));
|
||||
}
|
||||
|
||||
void EmitCommandList(bool bHalfBaked)
|
||||
{
|
||||
FString QueryLower = Query.ToLower();
|
||||
FString PrevGroup;
|
||||
|
||||
for (UClass* Class : WingUtils::CollectHandlerClasses())
|
||||
{
|
||||
bool bIsHalfBaked = Class->GetMetaData(TEXT("ModuleRelativePath")).StartsWith(TEXT("HalfBaked/"));
|
||||
if (bIsHalfBaked != bHalfBaked)
|
||||
continue;
|
||||
|
||||
FString ToolName = WingUtils::GetHandlerName(Class);
|
||||
if (!ToolName.ToLower().Contains(QueryLower))
|
||||
continue;
|
||||
|
||||
// Blank line between groups
|
||||
FString Group = WingUtils::GetHandlerGroup(Class);
|
||||
if (Group != PrevGroup)
|
||||
{
|
||||
if (!PrevGroup.IsEmpty())
|
||||
UWingServer::Print(TEXT("\n"));
|
||||
PrevGroup = Group;
|
||||
}
|
||||
|
||||
EmitCommand(Class);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
UWingServer::Printf(TEXT("\n"));
|
||||
EmitCommandList(false);
|
||||
// UWingServer::Print(TEXT("\n--- Half-Baked (may have issues) ---\n\n"));
|
||||
// EmitCommandList(true);
|
||||
UWingServer::Printf(TEXT("\n"));
|
||||
}
|
||||
};
|
||||
104
Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h
Normal file
104
Plugins/UEWingman/Source/UEWingman/Handlers/TypeName_Search.h
Normal file
@@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingServer.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingTypes.h"
|
||||
#include "WingUtils.h"
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetRegistry/IAssetRegistry.h"
|
||||
#include "UObject/UObjectIterator.h"
|
||||
#include "StructUtils/UserDefinedStruct.h"
|
||||
#include "Engine/UserDefinedEnum.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "TypeName_Search.generated.h"
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
UCLASS()
|
||||
class UWing_TypeName_Search : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(meta=(Description="Substring filter for type names"))
|
||||
FString Query;
|
||||
|
||||
UPROPERTY(meta=(Optional, Description="Maximum number of results"))
|
||||
int32 Limit = 100;
|
||||
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Search for type names usable in pin type specifications. "
|
||||
"Returns short names that can be used with commands like Blueprint_ChangeVariableType.");
|
||||
}
|
||||
|
||||
void TryMatchObject(TSet<UObject*> &Matches, UObject *Obj)
|
||||
{
|
||||
if (!Obj) return;
|
||||
FString Name = Obj->GetName();
|
||||
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) return;
|
||||
Matches.Add(Obj);
|
||||
}
|
||||
|
||||
void TryMatchObjects(TSet<UObject*> &Matches, UClass *Class)
|
||||
{
|
||||
ForEachObjectOfClass(Class, [&](UObject *Obj){
|
||||
if (Matches.Num() == Limit) return;
|
||||
TryMatchObject(Matches, Obj);
|
||||
}, true);
|
||||
}
|
||||
|
||||
void TryMatchAssets(TSet<UObject*> &Matches, UClass *Class)
|
||||
{
|
||||
IAssetRegistry& Registry = *IAssetRegistry::Get();
|
||||
TArray<FAssetData> AssetResults;
|
||||
Registry.GetAssetsByClass(Class->GetClassPathName(), AssetResults, true);
|
||||
for (const FAssetData& Data : AssetResults)
|
||||
{
|
||||
if (Matches.Num() == Limit) return;
|
||||
FString Name = Data.AssetName.ToString();
|
||||
if (!Name.Contains(Query, ESearchCase::IgnoreCase)) continue;
|
||||
UObject *Obj = Data.GetAsset();
|
||||
if (UBlueprint* BP = Cast<UBlueprint>(Obj))
|
||||
Obj = BP->GeneratedClass;
|
||||
TryMatchObject(Matches, Obj);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
TSet<UObject*> Matches;
|
||||
TryMatchObjects(Matches, UScriptStruct::StaticClass());
|
||||
TryMatchObjects(Matches, UClass::StaticClass());
|
||||
TryMatchObjects(Matches, UEnum::StaticClass());
|
||||
TryMatchAssets(Matches, UBlueprint::StaticClass());
|
||||
TryMatchAssets(Matches, UUserDefinedStruct::StaticClass());
|
||||
TryMatchAssets(Matches, UUserDefinedEnum::StaticClass());
|
||||
|
||||
TArray<FString> Results;
|
||||
for (const UObject *Obj : Matches)
|
||||
{
|
||||
const TCHAR *Kind = TEXT("Unknown");
|
||||
if (Cast<UEnum>(Obj))
|
||||
Kind = TEXT("Enum");
|
||||
else if (Cast<UScriptStruct>(Obj))
|
||||
Kind = TEXT("Struct");
|
||||
else if (const UClass* Class = Cast<UClass>(Obj))
|
||||
Kind = Class->IsChildOf(UInterface::StaticClass()) ? TEXT("Interface") : TEXT("Class");
|
||||
Results.Add(FString::Printf(TEXT("%s %s\n"), Kind, *UWingTypes::TypeToText(Obj)));
|
||||
}
|
||||
Results.Sort();
|
||||
for (const auto &Result : Results)
|
||||
{
|
||||
UWingServer::Print(Result);
|
||||
}
|
||||
if (Results.Num() == Limit)
|
||||
{
|
||||
UWingServer::Printf(TEXT("Search limit reached, raise it with Limit=\n"));
|
||||
}
|
||||
}
|
||||
};
|
||||
90
Plugins/UEWingman/Source/UEWingman/Handlers/UserManual.h
Normal file
90
Plugins/UEWingman/Source/UEWingman/Handlers/UserManual.h
Normal file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "WingHandler.h"
|
||||
#include "WingServer.h"
|
||||
#include "UserManual.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class UWing_UserManual : public UObject, public IWingHandler
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
virtual FString GetDescription() const override
|
||||
{
|
||||
return TEXT("Print the user manual.");
|
||||
}
|
||||
|
||||
virtual void Handle() override
|
||||
{
|
||||
UWingServer::Print(TEXT(
|
||||
"\n PATHS:"
|
||||
"\n"
|
||||
"\n Most commands require you to specify a path. A path starts"
|
||||
"\n with an asset name, followed by comma-separated steps that"
|
||||
"\n navigate into the asset. Example:"
|
||||
"\n"
|
||||
"\n /Game/Widgets/WB_Hotkeys,graph:EventGraph,node:Self03,pin:Result"
|
||||
"\n"
|
||||
"\n The navigation steps supported are:"
|
||||
"\n"
|
||||
"\n graph — move from a blueprint or material to a graph."
|
||||
"\n node — move from a graph to a graph node"
|
||||
"\n pin — move from a graph node to a pin"
|
||||
"\n component — move from a blueprint to a component"
|
||||
"\n levelblueprint — move from a world to a blueprint"
|
||||
"\n"
|
||||
"\n Steps do not always require a parameter. For example, materials"
|
||||
"\n only have one graph, so you can just say:"
|
||||
"\n"
|
||||
"\n /Game/Materials/MyMaterial,graph"
|
||||
"\n"
|
||||
"\n TYPES:"
|
||||
"\n"
|
||||
"\n To change variable types, or function prototypes, you will"
|
||||
"\n use our syntax for types. Here are some simple examples:"
|
||||
"\n"
|
||||
"\n boolean, int64, double, string, etc"
|
||||
"\n vector, rotator, hitresult, etc"
|
||||
"\n actor, character, playercontroller, etc"
|
||||
"\n eblendmode, emovementmode, etc"
|
||||
"\n"
|
||||
"\n Notice that it's 'actor', not 'AActor'."
|
||||
"\n You can use the following notations for complex types:"
|
||||
"\n"
|
||||
"\n soft<abp_manny>, class<pawn>, softclass<pawn>"
|
||||
"\n array<int>, set<string>, map<int, string>"
|
||||
"\n"
|
||||
"\n FUNCTION ARGUMENTS AND RETURN VALUES:"
|
||||
"\n"
|
||||
"\n Function argument lists are expressed as comma-separated"
|
||||
"\n lists containing type and variable name:"
|
||||
"\n"
|
||||
"\n double D, PlayerController P, array<int> A"
|
||||
"\n"
|
||||
"\n To change the arguments or return values of a function, edit the"
|
||||
"\n entry or exit node of the graph using GraphNode_SetArgs."
|
||||
"\n You can view the arguments using GraphNode_Dump. If a return "
|
||||
"\n node doesn't exist, you may have to create it using GraphNode_Create"
|
||||
"\n before you can set return values. Custom event nodes also have"
|
||||
"\n editable arguments."
|
||||
"\n"
|
||||
"\n MATERIAL EDITING:"
|
||||
"\n"
|
||||
"\n We do not expose material expressions directly. Instead, you"
|
||||
"\n will be editing the material graph. However, if you Graph_Dump"
|
||||
"\n a material graph, you will see that the nodes contain mxprop"
|
||||
"\n properties, which actually come from the material expressions."
|
||||
"\n You can edit these using Property_Set on the node."
|
||||
"\n"
|
||||
"\n COMMANDS YOU SHOULD KNOW ABOUT AND REMEMBER:"
|
||||
"\n"
|
||||
"\n UserManual: this explanation"
|
||||
"\n ShowCommands: a full list of all the commands"
|
||||
"\n Graph_Dump: a detailed listing of any UEdGraph"
|
||||
"\n Property_Dump: show information on many objects"
|
||||
"\n"
|
||||
));
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user