Lua向C#逻辑迁移 一期 #13

将整个插件代码上传
This commit is contained in:
2025-10-26 21:48:39 +08:00
parent 56994b3927
commit 648386cd73
785 changed files with 53683 additions and 2 deletions

View File

@ -0,0 +1,365 @@
#include "CSProcHelper.h"
#include "UnrealSharpProcHelper.h"
#include "XmlFile.h"
#include "XmlNode.h"
#include "Misc/App.h"
#include "Misc/Paths.h"
#include "Interfaces/IPluginManager.h"
#include "Misc/MessageDialog.h"
bool FCSProcHelper::InvokeCommand(const FString& ProgramPath, const FString& Arguments, int32& OutReturnCode, FString& Output, const FString* InWorkingDirectory)
{
double StartTime = FPlatformTime::Seconds();
FString ProgramName = FPaths::GetBaseFilename(ProgramPath);
FString WorkingDirectory = InWorkingDirectory ? *InWorkingDirectory : FPaths::GetPath(ProgramPath);
FString ErrorMessage;
FPlatformProcess::ExecProcess(*ProgramPath, *Arguments, &OutReturnCode, &Output, &ErrorMessage, *WorkingDirectory);
if (OutReturnCode != 0)
{
UE_LOG(LogUnrealSharpProcHelper, Error, TEXT("%s task failed (Args: %s) with return code %d. Error: %s"), *ProgramName, *Arguments, OutReturnCode, *Output)
FText DialogText = FText::FromString(FString::Printf(TEXT("%s task failed: \n %s"), *ProgramName, *Output));
FMessageDialog::Open(EAppMsgType::Ok, DialogText);
return false;
}
double EndTime = FPlatformTime::Seconds();
double ElapsedTime = EndTime - StartTime;
UE_LOG(LogUnrealSharpProcHelper, Log, TEXT("%s with args (%s) took %f seconds to execute."), *ProgramName, *Arguments, ElapsedTime);
return true;
}
bool FCSProcHelper::InvokeUnrealSharpBuildTool(const FString& BuildAction, const TMap<FString, FString>& AdditionalArguments)
{
FString PluginFolder = FPaths::ConvertRelativePathToFull(IPluginManager::Get().FindPlugin(UE_PLUGIN_NAME)->GetBaseDir());
FString DotNetPath = GetDotNetExecutablePath();
FString Args;
Args += FString::Printf(TEXT(" --Action %s"), *BuildAction);
Args += FString::Printf(TEXT(" --EngineDirectory \"%s\""), *FPaths::ConvertRelativePathToFull(FPaths::EngineDir()));
Args += FString::Printf(TEXT(" --ProjectDirectory \"%s\""), *FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()));
Args += FString::Printf(TEXT(" --ProjectName %s"), FApp::GetProjectName());
Args += FString::Printf(TEXT(" --PluginDirectory \"%s\""), *PluginFolder);
Args += FString::Printf(TEXT(" --DotNetPath \"%s\""), *DotNetPath);
if (AdditionalArguments.Num())
{
Args += TEXT(" --AdditionalArgs");
for (const TPair<FString, FString>& Argument : AdditionalArguments)
{
Args += FString::Printf(TEXT(" %s=%s"), *Argument.Key, *Argument.Value);
}
}
int32 ReturnCode = 0;
FString Output;
FString WorkingDirectory = GetPluginAssembliesPath();
return InvokeCommand(GetUnrealSharpBuildToolPath(), Args, ReturnCode, Output, &WorkingDirectory);
}
FString FCSProcHelper::GetLatestHostFxrPath()
{
FString DotNetRoot = GetDotNetDirectory();
FString HostFxrRoot = FPaths::Combine(DotNetRoot, "host", "fxr");
TArray<FString> Folders;
IFileManager::Get().FindFiles(Folders, *(HostFxrRoot / "*"), true, true);
FString HighestVersion = "0.0.0";
for (const FString &Folder : Folders)
{
if (Folder > HighestVersion)
{
HighestVersion = Folder;
}
}
if (HighestVersion == "0.0.0")
{
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to find hostfxr version in %s"), *HostFxrRoot);
return "";
}
if (HighestVersion < DOTNET_MAJOR_VERSION)
{
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Hostfxr version %s is less than the required version %s"), *HighestVersion, TEXT(DOTNET_MAJOR_VERSION));
return "";
}
#ifdef _WIN32
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_WINDOWS);
#elif defined(__APPLE__)
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_MAC);
#else
return FPaths::Combine(HostFxrRoot, HighestVersion, HOSTFXR_LINUX);
#endif
}
FString FCSProcHelper::GetRuntimeHostPath()
{
#if WITH_EDITOR
return GetLatestHostFxrPath();
#else
#ifdef _WIN32
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_WINDOWS);
#elif defined(__APPLE__)
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_MAC);
#else
return FPaths::Combine(GetPluginAssembliesPath(), HOSTFXR_LINUX);
#endif
#endif
}
FString FCSProcHelper::GetPathToSolution()
{
static FString SolutionPath = GetScriptFolderDirectory() / GetUserManagedProjectName() + ".sln";
return SolutionPath;
}
FString FCSProcHelper::GetPluginAssembliesPath()
{
#if WITH_EDITOR
return FPaths::Combine(GetPluginDirectory(), "Binaries", "Managed");
#else
return GetUserAssemblyDirectory();
#endif
}
FString FCSProcHelper::GetUnrealSharpPluginsPath()
{
return GetPluginAssembliesPath() / "UnrealSharp.Plugins.dll";
}
FString FCSProcHelper::GetRuntimeConfigPath()
{
return GetPluginAssembliesPath() / "UnrealSharp.runtimeconfig.json";
}
FString FCSProcHelper::GetUserAssemblyDirectory()
{
return FPaths::ConvertRelativePathToFull(FPaths::Combine(FPaths::ProjectDir(), "Binaries", "Managed"));
}
FString FCSProcHelper::GetUnrealSharpMetadataPath()
{
return FPaths::Combine(GetUserAssemblyDirectory(), "UnrealSharp.assemblyloadorder.json");
}
void FCSProcHelper::GetProjectNamesByLoadOrder(TArray<FString>& UserProjectNames, const bool bIncludeGlue)
{
const FString ProjectMetadataPath = GetUnrealSharpMetadataPath();
if (!FPaths::FileExists(ProjectMetadataPath))
{
// Can be null at the start of the project.
return;
}
FString JsonString;
if (!FFileHelper::LoadFileToString(JsonString, *ProjectMetadataPath))
{
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to load UnrealSharp metadata file at: %s"), *ProjectMetadataPath);
return;
}
TSharedPtr<FJsonObject> JsonObject;
if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(JsonString), JsonObject) || !JsonObject.IsValid())
{
UE_LOG(LogUnrealSharpProcHelper, Fatal, TEXT("Failed to parse UnrealSharp metadata at: %s"), *ProjectMetadataPath);
return;
}
for (const TSharedPtr<FJsonValue>& OrderEntry : JsonObject->GetArrayField(TEXT("AssemblyLoadingOrder")))
{
FString ProjectName = OrderEntry->AsString();
if (!bIncludeGlue && ProjectName.EndsWith(TEXT("Glue")))
{
continue;
}
UserProjectNames.Add(OrderEntry->AsString());
}
}
void FCSProcHelper::GetAssemblyPathsByLoadOrder(TArray<FString>& AssemblyPaths, const bool bIncludeGlue)
{
FString AbsoluteFolderPath = GetUserAssemblyDirectory();
TArray<FString> ProjectNames;
GetProjectNamesByLoadOrder(ProjectNames, bIncludeGlue);
for (const FString& ProjectName : ProjectNames)
{
const FString AssemblyPath = FPaths::Combine(AbsoluteFolderPath, ProjectName + TEXT(".dll"));
AssemblyPaths.Add(AssemblyPath);
}
}
void FCSProcHelper::GetAllProjectPaths(TArray<FString>& ProjectPaths, bool bIncludeProjectGlue)
{
// Use the FileManager to find files matching the pattern
IFileManager::Get().FindFilesRecursive(ProjectPaths,
*GetScriptFolderDirectory(),
TEXT("*.csproj"),
true,
false,
false);
TArray<FString> PluginFilePaths;
IPluginManager::Get().FindPluginsUnderDirectory(FPaths::ProjectPluginsDir(), PluginFilePaths);
for (const FString& PluginFilePath : PluginFilePaths)
{
FString ScriptDirectory = FPaths::GetPath(PluginFilePath) / "Script";
IFileManager::Get().FindFilesRecursive(ProjectPaths,
*ScriptDirectory,
TEXT("*.csproj"),
true,
false,
false);
}
for (int32 i = ProjectPaths.Num() - 1; i >= 0; i--)
{
if (bIncludeProjectGlue || !ProjectPaths[i].EndsWith("Glue.csproj"))
{
continue;
}
ProjectPaths.RemoveAt(i);
}
}
bool FCSProcHelper::IsProjectReloadable(FStringView ProjectPath)
{
FXmlFile ProjectFile(ProjectPath.GetData());
if (!ProjectFile.IsValid())
{
UE_LOG(LogUnrealSharpProcHelper, Warning, TEXT("Failed to parse project file as XML: %s"),
ProjectPath.GetData());
return true;
}
const FXmlNode* RootNode = ProjectFile.GetRootNode();
if (!RootNode)
{
return true;
}
// Look through all PropertyGroup elements
for (const TArray<FXmlNode*>& ProjectNodes = RootNode->GetChildrenNodes();
const FXmlNode* Node : ProjectNodes)
{
if (Node->GetTag() == TEXT("PropertyGroup"))
{
if (const FXmlNode* RoslynComponentNode = Node->FindChildNode(TEXT("ExcludeFromWeaver"));
RoslynComponentNode &&
RoslynComponentNode->GetContent().Equals(TEXT("true"), ESearchCase::IgnoreCase))
{
return false;
}
}
}
return true;
}
FString FCSProcHelper::GetUnrealSharpBuildToolPath()
{
#if PLATFORM_WINDOWS
return FPaths::ConvertRelativePathToFull(GetPluginAssembliesPath() / "UnrealSharpBuildTool.exe");
#else
return FPaths::ConvertRelativePathToFull(GetPluginAssembliesPath() / "UnrealSharpBuildTool");
#endif
}
FString FCSProcHelper::GetDotNetDirectory()
{
const FString PathVariable = FPlatformMisc::GetEnvironmentVariable(TEXT("PATH"));
TArray<FString> Paths;
PathVariable.ParseIntoArray(Paths, FPlatformMisc::GetPathVarDelimiter());
#if defined(_WIN32)
FString PathDotnet = "Program Files\\dotnet\\";
#elif defined(__APPLE__)
FString PathDotnet = "/usr/local/share/dotnet/";
return PathDotnet;
#endif
for (FString &Path : Paths)
{
if (!Path.Contains(PathDotnet))
{
continue;
}
if (!FPaths::DirectoryExists(Path))
{
UE_LOG(LogUnrealSharpProcHelper, Warning, TEXT("Found path to DotNet, but the directory doesn't exist: %s"), *Path);
break;
}
return Path;
}
return "";
}
FString FCSProcHelper::GetDotNetExecutablePath()
{
#if defined(_WIN32)
return GetDotNetDirectory() + "dotnet.exe";
#else
return GetDotNetDirectory() + "dotnet";
#endif
}
FString& FCSProcHelper::GetPluginDirectory()
{
static FString PluginDirectory;
if (PluginDirectory.IsEmpty())
{
TSharedPtr<IPlugin> Plugin = IPluginManager::Get().FindPlugin(UE_PLUGIN_NAME);
check(Plugin);
PluginDirectory = Plugin->GetBaseDir();
}
return PluginDirectory;
}
FString FCSProcHelper::GetUnrealSharpDirectory()
{
return FPaths::Combine(GetPluginDirectory(), "Managed", "UnrealSharp");
}
FString FCSProcHelper::GetGeneratedClassesDirectory()
{
return FPaths::Combine(GetUnrealSharpDirectory(), "UnrealSharp", "Generated");
}
const FString& FCSProcHelper::GetScriptFolderDirectory()
{
static FString ScriptFolderDirectory = FPaths::ProjectDir() / "Script";
return ScriptFolderDirectory;
}
const FString& FCSProcHelper::GetPluginsDirectory()
{
static FString PluginsDirectory = FPaths::ProjectDir() / "Plugins";
return PluginsDirectory;
}
const FString& FCSProcHelper::GetProjectGlueFolderPath()
{
static FString ProjectGlueFolderPath = GetScriptFolderDirectory() / FApp::GetProjectName() + TEXT(".Glue");
return ProjectGlueFolderPath;
}
FString FCSProcHelper::GetUserManagedProjectName()
{
return FString::Printf(TEXT("Managed%s"), FApp::GetProjectName());
}

View File

@ -0,0 +1,93 @@
#pragma once
const FString BUILD_ACTION_BUILD = TEXT("Build");
const FString BUILD_ACTION_CLEAN = TEXT("Clean");
const FString BUILD_ACTION_GENERATE_PROJECT = TEXT("GenerateProject");
const FString BUILD_ACTION_GENERATE_SOLUTION = TEXT("GenerateSolution");
const FString BUILD_ACTION_REBUILD = TEXT("Rebuild");
const FString BUILD_ACTION_WEAVE = TEXT("Weave");
const FString BUILD_ACTION_BUILD_WEAVE = TEXT("BuildWeave");
const FString BUILD_ACTION_PACKAGE_PROJECT = TEXT("PackageProject");
UENUM()
enum class EDotNetBuildConfiguration : uint64
{
Debug,
Release,
Publish,
};
#define HOSTFXR_WINDOWS "hostfxr.dll"
#define HOSTFXR_MAC "libhostfxr.dylib"
#define HOSTFXR_LINUX "libhostfxr.so"
#define DOTNET_MAJOR_VERSION "9.0.0"
class UNREALSHARPPROCHELPER_API FCSProcHelper final
{
public:
static bool InvokeCommand(const FString& ProgramPath, const FString& Arguments, int32& OutReturnCode, FString& Output, const FString* InWorkingDirectory = nullptr);
static bool InvokeUnrealSharpBuildTool(const FString& BuildAction, const TMap<FString, FString>& AdditionalArguments = TMap<FString, FString>());
static FString GetRuntimeConfigPath();
static FString GetPluginAssembliesPath();
static FString GetUnrealSharpPluginsPath();
static FString GetUnrealSharpBuildToolPath();
// Path to the directory where we store the user's assembly after it has been processed by the weaver.
static FString GetUserAssemblyDirectory();
// Path to file with UnrealSharp metadata
static FString GetUnrealSharpMetadataPath();
// Gets the project names in the order they should be loaded.
static void GetProjectNamesByLoadOrder(TArray<FString>& UserProjectNames, bool bIncludeGlue = false);
// Same as GetProjectNamesByLoadOrder, but returns the paths to the assemblies instead.
static void GetAssemblyPathsByLoadOrder(TArray<FString>& AssemblyPaths, bool bIncludeGlue = false);
// Gets all the project paths in the /Scripts directory.
static void GetAllProjectPaths(TArray<FString>& ProjectPaths, bool bIncludeProjectGlue = false);
// Checks if the project at this path can actually be reloaded. This is mainly used to skip of Roslyn analyzers since we don't want to reload them.
static bool IsProjectReloadable(FStringView ProjectPath);
// Path to the .NET runtime root. Only really works in editor, since players don't have the .NET runtime.
static FString GetDotNetDirectory();
// Path to the .NET executable. Only really works in editor, since players don't have the .NET runtime.
static FString GetDotNetExecutablePath();
// Path to the UnrealSharp plugin directory.
static FString& GetPluginDirectory();
// Path to the UnrealSharp bindings directory.
static FString GetUnrealSharpDirectory();
// Path to the directory where we store classes we have generated from C# > C++.
static FString GetGeneratedClassesDirectory();
// Path to the current project's script directory
static const FString& GetScriptFolderDirectory();
static const FString& GetPluginsDirectory();
// Path to the current project's glue directory
static const FString& GetProjectGlueFolderPath();
// Get the name of the current managed version of the project
static FString GetUserManagedProjectName();
// Path to the latest installed hostfxr version
static FString GetLatestHostFxrPath();
// Path to the runtime host. This is different in editor/builds.
static FString GetRuntimeHostPath();
// Path to the C# solution file.
static FString GetPathToSolution();
};

View File

@ -0,0 +1,31 @@
using UnrealBuildTool;
public class UnrealSharpProcHelper : ModuleRules
{
public UnrealSharpProcHelper(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"Slate",
"SlateCore",
"Projects",
"Json",
"XmlParser",
}
);
PublicDefinitions.Add("SkipGlueGeneration");
}
}

View File

@ -0,0 +1,21 @@
#include "UnrealSharpProcHelper.h"
#include "CSProcHelper.h"
#define LOCTEXT_NAMESPACE "FUnrealSharpProcHelperModule"
DEFINE_LOG_CATEGORY(LogUnrealSharpProcHelper);
void FUnrealSharpProcHelperModule::StartupModule()
{
}
void FUnrealSharpProcHelperModule::ShutdownModule()
{
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FUnrealSharpProcHelperModule, UnrealSharpProcHelper)

View File

@ -0,0 +1,18 @@
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
DECLARE_LOG_CATEGORY_EXTERN(LogUnrealSharpProcHelper, Log, All);
class FUnrealSharpProcHelperModule : public IModuleInterface
{
public:
// IModuleInterface interface
virtual void StartupModule() override;
virtual void ShutdownModule() override;
// End of IModuleInterface interface
};