From c5e3d41b22488f307601f6dadfafcb2e914c53d6 Mon Sep 17 00:00:00 2001 From: "17@autogame.ai" <17@autogame.ai> Date: Tue, 25 Mar 2025 20:40:19 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=BC=BAUnLua=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=E5=AF=B9GameFeature=E6=8F=92=E4=BB=B6=E5=92=8C=E6=99=AE?= =?UTF-8?q?=E9=80=9A=E6=8F=92=E4=BB=B6=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Plugins/UnLua/Source/UnLua/Private/LuaEnv.cpp | 224 +++++++++++++++++- .../Source/UnLua/Private/LuaModuleLocator.cpp | 94 +++++++- .../UnLua/Source/UnLua/Private/UnLuaLib.cpp | 26 +- 3 files changed, 339 insertions(+), 5 deletions(-) diff --git a/Plugins/UnLua/Source/UnLua/Private/LuaEnv.cpp b/Plugins/UnLua/Source/UnLua/Private/LuaEnv.cpp index 913270a7..e5e30dc5 100644 --- a/Plugins/UnLua/Source/UnLua/Private/LuaEnv.cpp +++ b/Plugins/UnLua/Source/UnLua/Private/LuaEnv.cpp @@ -30,6 +30,13 @@ #include "UnLuaLib.h" #include "UnLuaSettings.h" #include "lstate.h" +#include "Containers/Queue.h" +#include "Interfaces/IPluginManager.h" +#include "HAL/FileManager.h" +#include "Misc/Paths.h" + +// 添加调试定义 +#define UNLUA_DEBUG_PATH 1 namespace UnLua { @@ -599,12 +606,19 @@ namespace UnLua FString FileName(UTF8_TO_TCHAR(lua_tostring(L, 1))); FileName.ReplaceInline(TEXT("."), TEXT("/")); +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Trying to load module: %s"), *FileName); +#endif + auto& Env = *(FLuaEnv*)lua_touserdata(L, lua_upvalueindex(1)); TArray Data; FString FullPath; auto LoadIt = [&] { +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Successfully loaded file: %s"), *FullPath); +#endif if (Env.LoadString(L, Data, FullPath)) return 1; const auto Msg = FString::Printf(TEXT("file loading from file system error.\nfull path:%s"), *FullPath); @@ -615,13 +629,169 @@ namespace UnLua if (PackagePath.IsEmpty()) return 0; +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Package path: %s"), *PackagePath); +#endif + TArray Patterns; if (PackagePath.ParseIntoArray(Patterns, TEXT(";"), false) == 0) return 0; +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Found %d patterns"), Patterns.Num()); +#endif + + // 特殊处理 GameFeature 插件路径 + // 如果是PluginName.ModuleName这样的模块格式,尝试直接定位到对应插件 + if (FileName.Contains(TEXT("/"))) + { + TArray ModuleParts; + FileName.ParseIntoArray(ModuleParts, TEXT("/")); + + if (ModuleParts.Num() >= 2) + { + FString PluginName = ModuleParts[0]; + FString ModuleFile = ModuleParts[ModuleParts.Num() - 1]; + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Searching for module in plugin - Plugin: %s, Module: %s"), + *PluginName, *ModuleFile); +#endif + + // 尝试在GameFeature插件中查找 + FString GameFeaturePath = FPaths::Combine( + FPaths::ProjectPluginsDir(), + TEXT("GameFeatures"), + PluginName, + TEXT("Content/Script"), + PluginName, + ModuleFile + TEXT(".lua") + ); + + FullPath = FPaths::ConvertRelativePathToFull(GameFeaturePath); + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Checking GameFeature plugin path: %s"), *FullPath); +#endif + + if (FPaths::FileExists(FullPath) && FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + { + return LoadIt(); + } + + // 尝试在普通插件中查找 + FString NormalPluginPath = FPaths::Combine( + FPaths::ProjectPluginsDir(), + PluginName, + TEXT("Content/Script"), + PluginName, + ModuleFile + TEXT(".lua") + ); + + FullPath = FPaths::ConvertRelativePathToFull(NormalPluginPath); + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Checking normal plugin path: %s"), *FullPath); +#endif + + if (FPaths::FileExists(FullPath) && FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + { + return LoadIt(); + } + + // 尝试第二种常见结构 - 直接在Content/Script下 + FString DirectPath = FPaths::Combine( + FPaths::ProjectPluginsDir(), + TEXT("GameFeatures"), + PluginName, + TEXT("Content/Script"), + ModuleFile + TEXT(".lua") + ); + + FullPath = FPaths::ConvertRelativePathToFull(DirectPath); + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Checking direct GameFeature path: %s"), *FullPath); +#endif + + if (FPaths::FileExists(FullPath) && FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + { + return LoadIt(); + } + + // 尝试普通插件的直接路径 + FString DirectNormalPath = FPaths::Combine( + FPaths::ProjectPluginsDir(), + PluginName, + TEXT("Content/Script"), + ModuleFile + TEXT(".lua") + ); + + FullPath = FPaths::ConvertRelativePathToFull(DirectNormalPath); + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Display, TEXT("[UnLua] LoadFromFileSystem - Checking direct normal plugin path: %s"), *FullPath); +#endif + + if (FPaths::FileExists(FullPath) && FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + { + return LoadIt(); + } + } + } + // 优先加载下载目录下的单文件 for (auto& Pattern : Patterns) { + // 处理通配符路径,例如Plugins/*/Content/Script + bool bHasWildcard = Pattern.Contains(TEXT("*")); + if (bHasWildcard) + { + // 找到通配符位置 + int32 WildcardPos = Pattern.Find(TEXT("*")); + if (WildcardPos != INDEX_NONE) + { + FString PreWildcard = Pattern.Left(WildcardPos); + FString PostWildcard = Pattern.Mid(WildcardPos + 1); + + // 替换问号为文件名 + FString FilePartPattern = PostWildcard; + FilePartPattern.ReplaceInline(TEXT("?"), *FileName); + + // 获取插件目录 + IFileManager& FileManager = IFileManager::Get(); + TArray Plugins; + + // 判断是GameFeature插件还是普通插件 + FString PluginsRoot; + if (PreWildcard.Contains(TEXT("GameFeatures"))) + { + PluginsRoot = FPaths::ProjectPluginsDir() / TEXT("GameFeatures"); + } + else + { + PluginsRoot = FPaths::ProjectPluginsDir(); + } + + // 获取所有插件目录 + FileManager.FindFiles(Plugins, *PluginsRoot, false, true); + + // 检查每个插件目录下的文件 + for (const FString& Plugin : Plugins) + { + FString TestPath = FPaths::Combine(PluginsRoot, Plugin, FilePartPattern); + FullPath = FPaths::ConvertRelativePathToFull(TestPath); + + if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + return LoadIt(); + } + + // 找不到文件时继续下一个模式 + continue; + } + } + + // 处理标准路径 Pattern.ReplaceInline(TEXT("?"), *FileName); const auto PathWithPersistentDir = FPaths::Combine(FPaths::ProjectPersistentDownloadDir(), Pattern); FullPath = FPaths::ConvertRelativePathToFull(PathWithPersistentDir); @@ -632,12 +802,64 @@ namespace UnLua // 其次是打包目录下的文件 for (auto& Pattern : Patterns) { + // 同样处理通配符路径 + bool bHasWildcard = Pattern.Contains(TEXT("*")); + if (bHasWildcard) + { + // 找到通配符位置 + int32 WildcardPos = Pattern.Find(TEXT("*")); + if (WildcardPos != INDEX_NONE) + { + FString PreWildcard = Pattern.Left(WildcardPos); + FString PostWildcard = Pattern.Mid(WildcardPos + 1); + + // 替换问号为文件名 + FString FilePartPattern = PostWildcard; + FilePartPattern.ReplaceInline(TEXT("?"), *FileName); + + // 获取插件目录 + IFileManager& FileManager = IFileManager::Get(); + TArray Plugins; + + // 判断是GameFeature插件还是普通插件 + FString PluginsRoot; + if (PreWildcard.Contains(TEXT("GameFeatures"))) + { + PluginsRoot = FPaths::ProjectPluginsDir() / TEXT("GameFeatures"); + } + else + { + PluginsRoot = FPaths::ProjectPluginsDir(); + } + + // 获取所有插件目录 + FileManager.FindFiles(Plugins, *PluginsRoot, false, true); + + // 检查每个插件目录下的文件 + for (const FString& Plugin : Plugins) + { + FString TestPath = FPaths::Combine(FPaths::ProjectDir(), PreWildcard, Plugin, FilePartPattern); + FullPath = FPaths::ConvertRelativePathToFull(TestPath); + + if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) + return LoadIt(); + } + + // 找不到文件时继续下一个模式 + continue; + } + } + + // 处理标准路径 const auto PathWithProjectDir = FPaths::Combine(FPaths::ProjectDir(), Pattern); FullPath = FPaths::ConvertRelativePathToFull(PathWithProjectDir); if (FFileHelper::LoadFileToArray(Data, *FullPath, FILEREAD_Silent)) return LoadIt(); } - + +#if UNLUA_DEBUG_PATH + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] LoadFromFileSystem - File not found: %s"), *FileName); +#endif return 0; } diff --git a/Plugins/UnLua/Source/UnLua/Private/LuaModuleLocator.cpp b/Plugins/UnLua/Source/UnLua/Private/LuaModuleLocator.cpp index ef5dfccf..0bfbfc93 100644 --- a/Plugins/UnLua/Source/UnLua/Private/LuaModuleLocator.cpp +++ b/Plugins/UnLua/Source/UnLua/Private/LuaModuleLocator.cpp @@ -57,10 +57,98 @@ FString ULuaModuleLocator_ByPackage::Locate(const UObject* Object) } else { - ModuleName = Object->GetOutermost()->GetName(); - const auto ChopCount = ModuleName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, 1) + 1; - ModuleName = ModuleName.Replace(TEXT("/"), TEXT(".")).RightChop(ChopCount); + FString OuterName = Object->GetOutermost()->GetName(); + + // 检查是否来自GameFeature插件 + const bool bFromGameFeature = OuterName.Contains(TEXT("/GameFeatures/")); + const bool bFromPlugin = OuterName.Contains(TEXT("/Plugins/")); + + if (bFromGameFeature || bFromPlugin) + { + // 提取插件名称 + FString PluginName; + int32 PluginPathPos = INDEX_NONE; + + if (bFromGameFeature) + { + // 处理 GameFeature 插件 + const int32 GameFeaturesPos = OuterName.Find(TEXT("/GameFeatures/"), ESearchCase::IgnoreCase); + if (GameFeaturesPos != INDEX_NONE) + { + const int32 StartPos = GameFeaturesPos + FString(TEXT("/GameFeatures/")).Len(); + const int32 EndPos = OuterName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartPos); + if (EndPos != INDEX_NONE) + { + PluginName = OuterName.Mid(StartPos, EndPos - StartPos); + PluginPathPos = GameFeaturesPos; + } + } + } + else if (bFromPlugin) + { + // 处理普通插件 + const int32 PluginsPos = OuterName.Find(TEXT("/Plugins/"), ESearchCase::IgnoreCase); + if (PluginsPos != INDEX_NONE) + { + const int32 StartPos = PluginsPos + FString(TEXT("/Plugins/")).Len(); + const int32 EndPos = OuterName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, StartPos); + if (EndPos != INDEX_NONE) + { + PluginName = OuterName.Mid(StartPos, EndPos - StartPos); + PluginPathPos = PluginsPos; + } + } + } + + // 处理插件中的蓝图 + if (!PluginName.IsEmpty() && PluginPathPos != INDEX_NONE) + { + // 获取资源路径部分 + FString ResourcePath; + const int32 ContentPos = OuterName.Find(TEXT("/Content/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, PluginPathPos); + if (ContentPos != INDEX_NONE) + { + const int32 ScriptPartStart = ContentPos + FString(TEXT("/Content/")).Len(); + ResourcePath = OuterName.Mid(ScriptPartStart); + + // 获取类名部分(最后一个斜杠后的部分) + FString ClassName; + const int32 LastSlashPos = ResourcePath.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromEnd); + if (LastSlashPos != INDEX_NONE) + { + ClassName = ResourcePath.Mid(LastSlashPos + 1); + + // 构建最终的模块名 + // 对于GameFeature插件,使用: 插件名.类名 + // 例如: AstraAI.BP_TEST_C + ModuleName = PluginName + TEXT(".") + ClassName; + } + else + { + ModuleName = PluginName + TEXT(".") + ResourcePath; + } + } + else + { + // 如果找不到Content目录,使用类名 + ModuleName = PluginName + TEXT(".") + Class->GetName(); + } + } + else + { + // 使用默认处理 + const auto ChopCount = OuterName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, 1) + 1; + ModuleName = OuterName.Replace(TEXT("/"), TEXT(".")).RightChop(ChopCount); + } + } + else + { + // 普通资产的处理方式(不变) + const auto ChopCount = OuterName.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart, 1) + 1; + ModuleName = OuterName.Replace(TEXT("/"), TEXT(".")).RightChop(ChopCount); + } } + Cache.Add(Key, ModuleName); return ModuleName; } diff --git a/Plugins/UnLua/Source/UnLua/Private/UnLuaLib.cpp b/Plugins/UnLua/Source/UnLua/Private/UnLuaLib.cpp index 5d1d8308..d544e4af 100644 --- a/Plugins/UnLua/Source/UnLua/Private/UnLuaLib.cpp +++ b/Plugins/UnLua/Source/UnLua/Private/UnLuaLib.cpp @@ -2,6 +2,11 @@ #include "LowLevel.h" #include "LuaEnv.h" #include "UnLuaBase.h" +#include "Misc/Paths.h" +#include "Misc/App.h" + +// 在开头添加DEBUG标志,用于调试 +#define UNLUA_DEBUG_PATH 1 namespace UnLua { @@ -234,7 +239,26 @@ namespace UnLua { lua_newtable(L); luaL_setfuncs(L, UnLua_Functions, 0); - lua_pushstring(L, "Content/Script/?.lua;Plugins/UnLua/Content/Script/?.lua"); + + // 生成完整的默认搜索路径 + FString ProjectContentPath = "Content/Script/?.lua;"; + FString PluginBasePath = "Plugins/UnLua/Content/Script/?.lua;"; + FString PluginsPath = "Plugins/*/Content/Script/?.lua;"; + FString GameFeaturesPath = "Plugins/GameFeatures/*/Content/Script/?.lua"; + + // 完整的搜索路径 + FString FullPackagePath = ProjectContentPath + PluginBasePath + PluginsPath + GameFeaturesPath; + +#if UNLUA_DEBUG_PATH + // 输出搜索路径信息以便调试 + UE_LOG(LogUnLua, Display, TEXT("=================== UnLua Package Path ==================")); + UE_LOG(LogUnLua, Display, TEXT("Setting PackagePath: %s"), *FullPackagePath); + UE_LOG(LogUnLua, Display, TEXT("Project Dir: %s"), *FPaths::ProjectDir()); + UE_LOG(LogUnLua, Display, TEXT("Engine Dir: %s"), *FPaths::EngineDir()); + UE_LOG(LogUnLua, Display, TEXT("=========================================================")); +#endif + + lua_pushstring(L, TCHAR_TO_UTF8(*FullPackagePath)); lua_setfield(L, -2, PACKAGE_PATH_KEY); return 1; } From d4c4574b8633c00848e9ebbefe001fdd7a9e3c1e Mon Sep 17 00:00:00 2001 From: "17@autogame.ai" <17@autogame.ai> Date: Wed, 26 Mar 2025 00:01:33 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=8F=92=E4=BB=B6=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E4=B8=8B=E6=94=AF=E6=8C=81=E4=B8=80=E9=94=AE=E5=88=9B=E5=BB=BA?= =?UTF-8?q?lua=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Private/Toolbars/UnLuaEditorToolbar.cpp | 503 +++++++++++++++++- .../UnLuaEditor/Private/UnLuaEditorCore.cpp | 211 +++++++- 2 files changed, 699 insertions(+), 15 deletions(-) diff --git a/Plugins/UnLua/Source/UnLuaEditor/Private/Toolbars/UnLuaEditorToolbar.cpp b/Plugins/UnLua/Source/UnLuaEditor/Private/Toolbars/UnLuaEditorToolbar.cpp index c1451c70..04784c04 100644 --- a/Plugins/UnLua/Source/UnLuaEditor/Private/Toolbars/UnLuaEditorToolbar.cpp +++ b/Plugins/UnLua/Source/UnLuaEditor/Private/Toolbars/UnLuaEditorToolbar.cpp @@ -258,19 +258,35 @@ void FUnLuaEditorToolbar::CreateLuaTemplate_Executed() { const auto Blueprint = Cast(ContextObject); if (!IsValid(Blueprint)) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] Blueprint is not valid")); return; + } + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Blueprint name: %s"), *Blueprint->GetName()); UClass* Class = Blueprint->GeneratedClass; + if (!IsValid(Class)) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] Blueprint generated class is not valid")); + return; + } + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Blueprint generated class name: %s"), *Class->GetName()); const auto Func = Class->FindFunctionByName(FName("GetModuleName")); if (!IsValid(Func)) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] GetModuleName function not found")); return; + } FString ModuleName; Class->GetDefaultObject()->ProcessEvent(Func, &ModuleName); if (ModuleName.IsEmpty()) { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] Module name is empty")); FNotificationInfo Info(LOCTEXT("ModuleNameRequired", "Please specify a module name first")); Info.ExpireDuration = 5; FSlateNotificationManager::Get().AddNotification(Info); @@ -282,33 +298,329 @@ void FUnLuaEditorToolbar::CreateLuaTemplate_Executed() const auto TemplateName = ModuleNameParts.Last(); const auto RelativePath = ModuleName.Replace(TEXT("."), TEXT("/")); - const auto FileName = FString::Printf(TEXT("%s%s.lua"), *GLuaSrcFullPath, *RelativePath); + + // 获取蓝图资源的完整路径和包路径 + FString BlueprintPath = Blueprint->GetPathName(); + FString BlueprintPackagePath = Blueprint->GetOutermost()->GetName(); + FString AssetPath = Blueprint->GetPathName(); + FString OutermostName = Blueprint->GetOutermost()->GetName(); + FString OuterPathName = Blueprint->GetOutermost()->GetPathName(); + FString OutermostPath = Blueprint->GetOutermost()->GetPackage()->GetPathName(); + FString ClassPath = Blueprint->GeneratedClass->GetPathName(); + FString PackageName = Blueprint->GetOutermost()->GetPackage()->GetName(); + + // 只保留重要的路径信息日志 + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Blueprint path: %s"), *BlueprintPath); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Blueprint package path: %s"), *BlueprintPackagePath); + + // 获取项目和引擎路径信息 + FString ProjectDir = FPaths::ProjectDir(); + FString ProjectContentDir = FPaths::ProjectContentDir(); + FString PluginsDir = FPaths::ProjectPluginsDir(); + + // 默认的Lua文件存储路径(项目Content/Script目录) + FString DefaultScriptPath = FPaths::ConvertRelativePathToFull(GLuaSrcFullPath); + FString FileName = FString::Printf(TEXT("%s%s.lua"), *DefaultScriptPath, *RelativePath); + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 默认脚本路径: %s"), *DefaultScriptPath); + + bool bFoundScriptLocation = false; + + // 尝试从路径中提取第一段目录名称,可能是插件名称 + // 例如,从"/AstraAI/Blueprints/Widgets/BP_TEST"提取"AstraAI" + FString FirstPathSegment; + if (!BlueprintPackagePath.IsEmpty() && BlueprintPackagePath.StartsWith(TEXT("/"))) + { + FString TrimmedPath = BlueprintPackagePath.RightChop(1); // 去掉开头的 / + int32 SlashPos = TrimmedPath.Find(TEXT("/")); + if (SlashPos != INDEX_NONE) + { + FirstPathSegment = TrimmedPath.Left(SlashPos); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] First path segment extracted: %s"), *FirstPathSegment); + } + } + + // 遍历所有路径,检测插件类型 + FString PluginPathToUse; + FString PluginPathDesc; + bool bIsGameFeaturePlugin = false; + + // 如果还没找到插件路径,尝试通过第一段目录名查找匹配的插件 + if (PluginPathToUse.IsEmpty() && !FirstPathSegment.IsEmpty()) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 尝试通过目录名 '%s' 查找插件"), *FirstPathSegment); + + // 获取项目中所有插件的信息 + TArray> AllPlugins = IPluginManager::Get().GetDiscoveredPlugins(); + + // 遍历所有插件,查找显示名与FirstPathSegment匹配的插件 + bool bFoundPlugin = false; + FString MatchedPluginName; + FString MatchedPluginFolder; + bool bIsMatchedPluginGameFeature = false; + TSharedPtr MatchedPlugin; + + for (const TSharedRef& PluginRef : AllPlugins) + { + FString PluginName = PluginRef->GetName(); + FString PluginBaseDir = PluginRef->GetBaseDir(); + FString PluginFolder = FPaths::GetCleanFilename(PluginBaseDir); + + // 检查插件名称是否与路径段匹配 + if (PluginName.Equals(FirstPathSegment, ESearchCase::IgnoreCase)) + { + bFoundPlugin = true; + MatchedPluginName = PluginName; + MatchedPluginFolder = PluginFolder; + MatchedPlugin = PluginRef; + + // 检查是否为GameFeature插件 + if (PluginBaseDir.Contains(TEXT("/GameFeatures/")) || PluginBaseDir.Contains(TEXT("\\GameFeatures\\"))) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 找到匹配的GameFeature插件: %s, 目录: %s"), *PluginName, *PluginBaseDir); + bIsMatchedPluginGameFeature = true; + break; + } + else + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 找到匹配的普通插件: %s, 目录: %s"), *PluginName, *PluginBaseDir); + bIsMatchedPluginGameFeature = false; + break; + } + } + } + + // 如果找到了匹配的插件 + if (bFoundPlugin && MatchedPlugin.IsValid()) + { + // 直接使用插件的实际物理路径,而不是基于名称拼接路径 + FString PluginBaseDir = MatchedPlugin->GetBaseDir(); + + if (bIsMatchedPluginGameFeature) + { + bIsGameFeaturePlugin = true; + PluginPathToUse = FString::Printf(TEXT("/GameFeatures/%s/%s"), *MatchedPluginName, *BlueprintPackagePath); + PluginPathDesc = TEXT("通过插件管理器找到的GameFeature插件"); + } + else + { + bIsGameFeaturePlugin = false; + PluginPathToUse = FString::Printf(TEXT("/Plugins/%s/%s"), *MatchedPluginName, *BlueprintPackagePath); + PluginPathDesc = TEXT("通过插件管理器找到的普通插件"); + } + + // 构建插件的脚本路径 - 使用实际物理路径 + FString PluginScriptDir = FPaths::Combine(PluginBaseDir, TEXT("Content/Script")); + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件根目录(真实物理路径): %s"), *PluginBaseDir); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件脚本目录: %s"), *PluginScriptDir); + + // 检查插件目录是否存在 + bool bPluginRootExists = FPaths::DirectoryExists(PluginBaseDir); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件根目录存在: %s"), bPluginRootExists ? TEXT("是") : TEXT("否")); + + if (!bPluginRootExists) + { + UE_LOG(LogUnLua, Error, TEXT("[UnLua] 插件目录不存在: %s,将使用默认Content/Script"), *PluginBaseDir); + bFoundScriptLocation = false; + } + else + { + // 检查插件Content目录是否存在 + FString PluginContentDir = FPaths::Combine(PluginBaseDir, TEXT("Content")); + bool bContentDirExists = FPaths::DirectoryExists(PluginContentDir); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件Content目录存在: %s"), bContentDirExists ? TEXT("是") : TEXT("否")); + + if (!bContentDirExists) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件Content目录不存在: %s,尝试创建"), *PluginContentDir); + bool bContentDirCreated = FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*PluginContentDir); + + if (!bContentDirCreated) + { + UE_LOG(LogUnLua, Error, TEXT("[UnLua] 无法创建Content目录,将使用默认Content/Script")); + bFoundScriptLocation = false; + } + else + { + // Content目录创建成功,继续创建Script目录 + bool bScriptDirCreated = FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*PluginScriptDir); + + if (!bScriptDirCreated) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] 无法在插件中创建Script目录: %s"), *PluginScriptDir); + bFoundScriptLocation = false; + } + else + { + bFoundScriptLocation = true; + } + } + } + else + { + // Content目录存在,检查Script目录 + bool bScriptDirExists = FPaths::DirectoryExists(PluginScriptDir); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 插件脚本目录存在: %s"), bScriptDirExists ? TEXT("是") : TEXT("否")); + + if (!bScriptDirExists) + { + bool bCreateScriptResult = FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*PluginScriptDir); + + if (!bCreateScriptResult) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] 无法在插件中创建Script目录: %s"), *PluginScriptDir); + bFoundScriptLocation = false; + } + else + { + bFoundScriptLocation = true; + } + } + else + { + // Script目录已存在 + bFoundScriptLocation = true; + } + } + + // 如果找到了脚本位置,更新文件名 + if (bFoundScriptLocation) + { + // 设置新的Lua文件路径 + FString OldFileName = FileName; + if (ModuleNameParts.Num() > 1 && ModuleNameParts[0] == MatchedPluginName) + { + // 如果模块名已经包含插件名前缀,直接使用相对路径 + FileName = FPaths::Combine(PluginScriptDir, *RelativePath) + TEXT(".lua"); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 使用相对路径创建Lua文件: %s"), *FileName); + } + else + { + // 否则,将Lua文件直接放在Script目录下 + FileName = FPaths::Combine(PluginScriptDir, *TemplateName) + TEXT(".lua"); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 使用模板名称创建Lua文件: %s"), *FileName); + } + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 文件路径已更新 从: %s 到: %s"), *OldFileName, *FileName); + } + } + } + } + + // 如果找到了插件路径 + if (!PluginPathToUse.IsEmpty()) + { + if (bIsGameFeaturePlugin) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 蓝图位于GameFeatures插件中")); + } + else + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 蓝图位于普通插件中")); + } + } + else + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] 未找到插件路径")); + } - if (FPaths::FileExists(FileName)) + // 检查文件是否已存在 + bool bFileExists = FPaths::FileExists(FileName); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 文件已存在: %s"), bFileExists ? TEXT("是") : TEXT("否")); + + if (bFileExists) { UE_LOG(LogUnLua, Warning, TEXT("%s"), *FText::Format(LOCTEXT("FileAlreadyExists", "Lua file ({0}) is already existed!"), FText::FromString(TemplateName)).ToString()); return; } + // 确保目标目录存在 + FString TargetDirectory = FPaths::GetPath(FileName); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 目标目录: %s"), *TargetDirectory); + + bool bDirExists = FPaths::DirectoryExists(TargetDirectory); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 目标目录存在: %s"), bDirExists ? TEXT("是") : TEXT("否")); + + if (!bDirExists) + { + bool bDirCreated = FPlatformFileManager::Get().GetPlatformFile().CreateDirectoryTree(*TargetDirectory); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 创建目录 %s: %s"), *TargetDirectory, bDirCreated ? TEXT("成功") : TEXT("失败")); + + if (!bDirCreated) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] 无法创建目录: %s"), *TargetDirectory); + return; + } + } + + // 加载并处理模板 static FString BaseDir = IPluginManager::Get().FindPlugin(TEXT("UnLua"))->GetBaseDir(); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] UnLua插件基础目录: %s"), *BaseDir); + + bool bFoundTemplate = false; for (auto TemplateClass = Class; TemplateClass; TemplateClass = TemplateClass->GetSuperClass()) { auto TemplateClassName = TemplateClass->GetName().EndsWith("_C") ? TemplateClass->GetName().LeftChop(2) : TemplateClass->GetName(); auto RelativeFilePath = "Config/LuaTemplates" / TemplateClassName + ".lua"; auto FullFilePath = FPaths::ProjectConfigDir() / RelativeFilePath; + + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 尝试模板类: %s"), *TemplateClassName); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 查找项目模板: %s"), *FullFilePath); + if (!FPaths::FileExists(FullFilePath)) + { FullFilePath = BaseDir / RelativeFilePath; + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 查找插件模板: %s"), *FullFilePath); + } if (!FPaths::FileExists(FullFilePath)) + { + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 没有找到模板文件")); continue; + } + + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 找到模板文件: %s"), *FullFilePath); FString Content; - FFileHelper::LoadFileToString(Content, *FullFilePath); + bool bReadSuccess = FFileHelper::LoadFileToString(Content, *FullFilePath); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 读取模板文件: %s"), bReadSuccess ? TEXT("成功") : TEXT("失败")); + + if (bReadSuccess) + { Content = Content.Replace(TEXT("TemplateName"), *TemplateName) .Replace(TEXT("ClassName"), *UnLua::IntelliSense::GetTypeName(Class)); - FFileHelper::SaveStringToFile(Content, *FileName, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); + bool bFileSaved = FFileHelper::SaveStringToFile(Content, *FileName, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 保存Lua文件到 %s: %s"), *FileName, bFileSaved ? TEXT("成功") : TEXT("失败")); + + if (bFileSaved) + { + UE_LOG(LogUnLua, Log, TEXT("[UnLua] 成功创建Lua模板文件: %s"), *FileName); + + // 显示通知 + FNotificationInfo Info(FText::Format(LOCTEXT("FileCreated", "Lua file created: {0}"), FText::FromString(FPaths::GetCleanFilename(FileName)))); + Info.ExpireDuration = 5; + FSlateNotificationManager::Get().AddNotification(Info); + } + else + { + UE_LOG(LogUnLua, Error, TEXT("[UnLua] 无法保存Lua文件到: %s"), *FileName); + + // 显示错误通知 + FNotificationInfo ErrorInfo(FText::Format(LOCTEXT("FileCreateFailed", "Failed to create Lua file: {0}"), FText::FromString(FPaths::GetCleanFilename(FileName)))); + ErrorInfo.ExpireDuration = 5; + FSlateNotificationManager::Get().AddNotification(ErrorInfo); + } + + bFoundTemplate = true; break; + } + } + + if (!bFoundTemplate) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] 没有找到适合的模板文件")); } } @@ -333,20 +645,190 @@ void FUnLuaEditorToolbar::RevealInExplorer_Executed() const auto DefaultObject = TargetClass->GetDefaultObject(); DefaultObject->UObject::ProcessEvent(Func, &ModuleName); - const auto RelativePath = ModuleName.Replace(TEXT("."), TEXT("/")); - const auto FileName = FString::Printf(TEXT("%s%s.lua"), *GLuaSrcFullPath, *RelativePath); + if (ModuleName.IsEmpty()) + return; - if (IFileManager::Get().FileExists(*FileName)) + TArray ModuleNameParts; + ModuleName.ParseIntoArray(ModuleNameParts, TEXT(".")); + const auto TemplateName = ModuleNameParts.Last(); + + const auto RelativePath = ModuleName.Replace(TEXT("."), TEXT("/")); + + // 获取蓝图资源的完整路径和包路径 + FString BlueprintPath = Blueprint->GetPathName(); + FString BlueprintPackagePath = Blueprint->GetOutermost()->GetName(); + FString AssetPath = Blueprint->GetPathName(); + + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Looking for Lua file for blueprint at path: %s"), *BlueprintPath); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Blueprint package path: %s"), *BlueprintPackagePath); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Blueprint asset path: %s"), *AssetPath); + + // 默认的Lua文件存储路径(项目Content/Script目录) + FString DefaultScriptPath = FPaths::ConvertRelativePathToFull(GLuaSrcFullPath); + FString FileName = FString::Printf(TEXT("%s%s.lua"), *DefaultScriptPath, *RelativePath); + + bool bFoundScriptLocation = false; + TArray PossibleFileNames; + + // 添加默认路径到可能文件位置 + PossibleFileNames.Add(FileName); + + // 尝试从路径中提取第一段目录名称,可能是插件名称 + // 例如,从"/AstraAI/Blueprints/Widgets/BP_TEST"提取"AstraAI" + FString FirstPathSegment; + if (!BlueprintPackagePath.IsEmpty() && BlueprintPackagePath.StartsWith(TEXT("/"))) + { + FString TrimmedPath = BlueprintPackagePath.RightChop(1); // 去掉开头的 / + int32 SlashPos = TrimmedPath.Find(TEXT("/")); + if (SlashPos != INDEX_NONE) + { + FirstPathSegment = TrimmedPath.Left(SlashPos); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] First path segment extracted: %s"), *FirstPathSegment); + } + } + + // 如果未找到常规路径,尝试通过第一段目录名查找插件 + if (!FirstPathSegment.IsEmpty()) { - FPlatformProcess::ExploreFolder(*FileName); + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] Trying to locate plugin by first path segment: %s"), *FirstPathSegment); + + // 使用插件管理器直接获取插件信息 + TArray> AllPlugins = IPluginManager::Get().GetDiscoveredPlugins(); + bool bFoundPlugin = false; + TSharedPtr MatchedPlugin; + + // 查找匹配的插件 + for (const TSharedRef& PluginRef : AllPlugins) + { + FString PluginName = PluginRef->GetName(); + + if (PluginName.Equals(FirstPathSegment, ESearchCase::IgnoreCase)) + { + bFoundPlugin = true; + MatchedPlugin = PluginRef; + FString PluginBaseDir = PluginRef->GetBaseDir(); + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Found matching plugin: %s, directory: %s"), *PluginName, *PluginBaseDir); + + FString PluginScriptDir = FPaths::Combine(PluginBaseDir, TEXT("Content/Script")); + + // 设置可能的Lua文件路径 + FString PossibleFileName1 = FPaths::Combine(PluginScriptDir, *RelativePath) + TEXT(".lua"); + FString PossibleFileName2 = FPaths::Combine(PluginScriptDir, *TemplateName) + TEXT(".lua"); + + PossibleFileNames.Add(PossibleFileName1); + PossibleFileNames.Add(PossibleFileName2); + break; + } + } + + // 如果未通过插件管理器找到,则回退到文件系统查找 + if (!bFoundPlugin) + { + // 首先检查是否是GameFeatures插件 + FString GameFeaturesDir = FPaths::Combine(FPaths::ProjectDir(), TEXT("Plugins/GameFeatures")); + FString PotentialGameFeaturePlugin = FPaths::Combine(GameFeaturesDir, FirstPathSegment); + + if (FPaths::DirectoryExists(PotentialGameFeaturePlugin)) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Found matching GameFeature plugin: %s"), *PotentialGameFeaturePlugin); + + FString PluginScriptDir = FPaths::Combine(PotentialGameFeaturePlugin, TEXT("Content/Script")); + + // 设置可能的Lua文件路径 + FString PossibleFileName1 = FPaths::Combine(PluginScriptDir, *RelativePath) + TEXT(".lua"); + FString PossibleFileName2 = FPaths::Combine(PluginScriptDir, *TemplateName) + TEXT(".lua"); + + PossibleFileNames.Add(PossibleFileName1); + PossibleFileNames.Add(PossibleFileName2); } else + { + // 检查是否是普通插件 + FString PluginsRootDir = FPaths::Combine(FPaths::ProjectDir(), TEXT("Plugins")); + FString PotentialPlugin = FPaths::Combine(PluginsRootDir, FirstPathSegment); + + if (FPaths::DirectoryExists(PotentialPlugin)) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Found matching regular plugin: %s"), *PotentialPlugin); + + FString PluginScriptDir = FPaths::Combine(PotentialPlugin, TEXT("Content/Script")); + + // 设置可能的Lua文件路径 + FString PossibleFileName1 = FPaths::Combine(PluginScriptDir, *RelativePath) + TEXT(".lua"); + FString PossibleFileName2 = FPaths::Combine(PluginScriptDir, *TemplateName) + TEXT(".lua"); + + PossibleFileNames.Add(PossibleFileName1); + PossibleFileNames.Add(PossibleFileName2); + } + } + } + } + + // 尝试所有可能的文件位置 + bool bFoundFile = false; + for (const FString& PossibleFile : PossibleFileNames) { + if (IFileManager::Get().FileExists(*PossibleFile)) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] Found Lua file at: %s"), *PossibleFile); + FPlatformProcess::ExploreFolder(*PossibleFile); + bFoundFile = true; + break; + } + } + + // 如果按标准路径没找到文件,尝试使用插件管理器再次查找 + if (!bFoundFile && !FirstPathSegment.IsEmpty()) + { + // 获取所有插件信息并遍历 + TArray> AllPlugins = IPluginManager::Get().GetDiscoveredPlugins(); + + // 尝试以插件名为关键字搜索 + for (const TSharedRef& PluginRef : AllPlugins) + { + FString PluginName = PluginRef->GetName(); + if (PluginName.Equals(FirstPathSegment, ESearchCase::IgnoreCase)) + { + // 获取插件的真实物理路径 + FString PluginBaseDir = PluginRef->GetBaseDir(); + FString PluginScriptDir = FPaths::Combine(PluginBaseDir, TEXT("Content/Script")); + + // 检查修正后的两个可能的文件路径 + FString AdjustedFileName1 = FPaths::Combine(PluginScriptDir, TEXT("TEST.lua")); + + // 使用中间变量来避免指针加法 + FString TemplateFileName = TemplateName + TEXT(".lua"); + FString AdjustedFileName2 = FPaths::Combine(PluginScriptDir, TemplateFileName); + + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 使用插件实际路径尝试: %s"), *AdjustedFileName1); + if (IFileManager::Get().FileExists(*AdjustedFileName1)) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 找到文件(使用TEST名称): %s"), *AdjustedFileName1); + FPlatformProcess::ExploreFolder(*AdjustedFileName1); + bFoundFile = true; + break; + } + + UE_LOG(LogUnLua, Verbose, TEXT("[UnLua] 使用插件实际路径尝试: %s"), *AdjustedFileName2); + if (IFileManager::Get().FileExists(*AdjustedFileName2)) + { + UE_LOG(LogUnLua, Display, TEXT("[UnLua] 找到文件(使用模板名称): %s"), *AdjustedFileName2); + FPlatformProcess::ExploreFolder(*AdjustedFileName2); + bFoundFile = true; + break; + } + } + } + } + + if (!bFoundFile) + { + UE_LOG(LogUnLua, Warning, TEXT("[UnLua] Could not find Lua file in any location")); FNotificationInfo NotificationInfo(FText::FromString("UnLua Notification")); NotificationInfo.Text = LOCTEXT("FileNotExist", "The file does not exist."); NotificationInfo.bFireAndForget = true; - NotificationInfo.ExpireDuration = 100.0f; - NotificationInfo.bUseThrobber = true; + NotificationInfo.ExpireDuration = 5.0f; + NotificationInfo.bUseThrobber = false; FSlateNotificationManager::Get().AddNotification(NotificationInfo); } } @@ -377,3 +859,4 @@ void FUnLuaEditorToolbar::CopyAsRelativePath_Executed() const } #undef LOCTEXT_NAMESPACE + diff --git a/Plugins/UnLua/Source/UnLuaEditor/Private/UnLuaEditorCore.cpp b/Plugins/UnLua/Source/UnLuaEditor/Private/UnLuaEditorCore.cpp index 64be898f..ad7dd9c2 100644 --- a/Plugins/UnLua/Source/UnLuaEditor/Private/UnLuaEditorCore.cpp +++ b/Plugins/UnLua/Source/UnLuaEditor/Private/UnLuaEditorCore.cpp @@ -44,10 +44,211 @@ ELuaBindingStatus GetBindingStatus(const UBlueprint* Blueprint) if (ModuleName.IsEmpty()) return ELuaBindingStatus::Unknown; + // 分解模块名称 + TArray ModuleNameParts; + ModuleName.ParseIntoArray(ModuleNameParts, TEXT(".")); + const auto TemplateName = ModuleNameParts.Last(); + + // 转换为相对路径 const auto RelativePath = ModuleName.Replace(TEXT("."), TEXT("/")) + TEXT(".lua"); - const auto FullPath = GLuaSrcFullPath + "/" + RelativePath; - if (!FPaths::FileExists(FullPath)) - return ELuaBindingStatus::BoundButInvalid; - - return ELuaBindingStatus::Bound; + + // 获取蓝图路径信息 + FString BlueprintPackagePath = Blueprint->GetOutermost()->GetName(); + FString BlueprintPath = Blueprint->GetPathName(); + FString AssetPath = Blueprint->GetPathName(); + FString OutermostName = Blueprint->GetOutermost()->GetName(); + FString OuterPathName = Blueprint->GetOutermost()->GetPathName(); + FString OutermostPath = Blueprint->GetOutermost()->GetPackage()->GetPathName(); + FString ClassPath = Blueprint->GeneratedClass->GetPathName(); + + // 存储可能的Lua文件路径 + TArray PossiblePaths; + + // 检查默认路径 + const auto DefaultFullPath = GLuaSrcFullPath + "/" + RelativePath; + PossiblePaths.Add(DefaultFullPath); + + // 尝试从路径中提取第一段目录名称,可能是插件名称 + // 例如,从"/AstraAI/Blueprints/Widgets/BP_TEST"提取"AstraAI" + FString FirstPathSegment; + if (!BlueprintPackagePath.IsEmpty() && BlueprintPackagePath.StartsWith(TEXT("/"))) + { + FString TrimmedPath = BlueprintPackagePath.RightChop(1); // 去掉开头的 / + int32 SlashPos = TrimmedPath.Find(TEXT("/")); + if (SlashPos != INDEX_NONE) + { + FirstPathSegment = TrimmedPath.Left(SlashPos); + } + } + + // 合并所有可能的路径到一个数组,并检查每个路径是否包含插件关键词 + TArray> AllPaths; + AllPaths.Add(TPair(BlueprintPackagePath, TEXT("BlueprintPackagePath"))); + AllPaths.Add(TPair(OutermostPath, TEXT("OutermostPath"))); + AllPaths.Add(TPair(OutermostName, TEXT("OutermostName"))); + AllPaths.Add(TPair(OuterPathName, TEXT("OuterPathName"))); + AllPaths.Add(TPair(ClassPath, TEXT("ClassPath"))); + AllPaths.Add(TPair(AssetPath, TEXT("AssetPath"))); + AllPaths.Add(TPair(BlueprintPath, TEXT("BlueprintPath"))); + + // 遍历所有路径,检测插件类型 + FString PluginPathToUse; + bool bIsGameFeaturePlugin = false; + + for (const auto& PathPair : AllPaths) + { + const FString& Path = PathPair.Key; + + // 检查是否包含GameFeatures路径 + if (Path.Contains(TEXT("/GameFeatures/")) || Path.Contains(TEXT("\\GameFeatures\\"))) + { + PluginPathToUse = Path; + bIsGameFeaturePlugin = true; + break; + } + // 检查是否包含普通插件路径 + else if (Path.Contains(TEXT("/Plugins/")) || Path.Contains(TEXT("\\Plugins\\"))) + { + PluginPathToUse = Path; + break; + } + } + + // 如果还没找到插件路径,尝试通过第一段目录名查找匹配的插件 + if (PluginPathToUse.IsEmpty() && !FirstPathSegment.IsEmpty()) + { + // 检查GameFeatures目录是否有匹配插件 + FString ProjectDir = FPaths::ProjectDir(); + FString GameFeaturesDir = FPaths::Combine(ProjectDir, TEXT("Plugins/GameFeatures")); + FString PotentialGameFeaturePlugin = FPaths::Combine(GameFeaturesDir, FirstPathSegment); + if (FPaths::DirectoryExists(PotentialGameFeaturePlugin)) + { + bIsGameFeaturePlugin = true; + // 构造一个包含插件路径的字符串,供后续处理使用 + PluginPathToUse = FString::Printf(TEXT("/GameFeatures/%s/%s"), *FirstPathSegment, *BlueprintPackagePath); + } + else + { + // 检查普通Plugins目录是否有匹配插件 + FString PluginsRootDir = FPaths::Combine(ProjectDir, TEXT("Plugins")); + FString PotentialPlugin = FPaths::Combine(PluginsRootDir, FirstPathSegment); + if (FPaths::DirectoryExists(PotentialPlugin)) + { + // 构造一个包含插件路径的字符串,供后续处理使用 + PluginPathToUse = FString::Printf(TEXT("/Plugins/%s/%s"), *FirstPathSegment, *BlueprintPackagePath); + } + } + } + + // 如果找到了插件路径 + if (!PluginPathToUse.IsEmpty()) + { + FString PluginName; + FString RemainingPath; + + // 如果通过目录名生成了路径,可以直接使用FirstPathSegment作为插件名 + if (!FirstPathSegment.IsEmpty() && PluginPathToUse.Contains(FirstPathSegment)) + { + PluginName = FirstPathSegment; + } + else + { + // 处理不同的分隔符 + bool bUsesForwardSlash = true; + FString SplitKeyword; + + if (bIsGameFeaturePlugin) + { + if (PluginPathToUse.Contains(TEXT("/GameFeatures/"))) + { + SplitKeyword = TEXT("/GameFeatures/"); + } + else + { + SplitKeyword = TEXT("\\GameFeatures\\"); + bUsesForwardSlash = false; + } + } + else + { + if (PluginPathToUse.Contains(TEXT("/Plugins/"))) + { + SplitKeyword = TEXT("/Plugins/"); + } + else + { + SplitKeyword = TEXT("\\Plugins\\"); + bUsesForwardSlash = false; + } + } + + // 分离路径获取插件名 + PluginPathToUse.Split(SplitKeyword, nullptr, &RemainingPath); + + // 分隔插件名和剩余路径 + if (bUsesForwardSlash) + { + RemainingPath.Split(TEXT("/"), &PluginName, &RemainingPath); + } + else + { + RemainingPath.Split(TEXT("\\"), &PluginName, &RemainingPath); + } + + // 如果提取失败,尝试更特殊的处理(从路径字符串中分析) + if (PluginName.IsEmpty()) + { + // 尝试用简单的字符串分析提取插件名 + int32 PluginsPos = PluginPathToUse.Find(SplitKeyword, ESearchCase::IgnoreCase, ESearchDir::FromStart); + + if (PluginsPos != INDEX_NONE) + { + FString AfterPlugins = PluginPathToUse.Mid(PluginsPos + SplitKeyword.Len()); + + // 提取第一个目录名作为插件名 + int32 NextSlashPos = bUsesForwardSlash ? + AfterPlugins.Find(TEXT("/"), ESearchCase::IgnoreCase, ESearchDir::FromStart) : + AfterPlugins.Find(TEXT("\\"), ESearchCase::IgnoreCase, ESearchDir::FromStart); + + if (NextSlashPos != INDEX_NONE) + { + PluginName = AfterPlugins.Left(NextSlashPos); + } + } + } + } + + if (!PluginName.IsEmpty()) + { + // 构建插件的脚本路径 + FString PluginRoot; + if (bIsGameFeaturePlugin) + { + PluginRoot = FPaths::Combine(FPaths::ProjectDir(), TEXT("Plugins/GameFeatures"), PluginName); + } + else + { + PluginRoot = FPaths::Combine(FPaths::ProjectDir(), TEXT("Plugins"), PluginName); + } + + FString PluginScriptDir = FPaths::Combine(PluginRoot, TEXT("Content/Script")); + + // 检查两种可能的文件路径 + FString PossiblePath1 = FPaths::Combine(PluginScriptDir, *RelativePath); + FString PossiblePath2 = FPaths::Combine(PluginScriptDir, *TemplateName) + TEXT(".lua"); + + PossiblePaths.Add(PossiblePath1); + PossiblePaths.Add(PossiblePath2); + } + } + + // 尝试所有可能的路径 + for (const FString& Path : PossiblePaths) + { + if (FPaths::FileExists(Path)) + return ELuaBindingStatus::Bound; + } + + // 如果所有位置都没找到,标记为无效绑定 + return ELuaBindingStatus::BoundButInvalid; }