using System.Linq; using System; using System.Collections.Generic; using UnityEditor; using System.IO; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Build; using addressables.helper; using UnityEngine; using UnityEditor.Build.Player; using System.Diagnostics; using Debug = UnityEngine.Debug; public class BuildTool { public enum EBundleType : byte { None, FirstPack, ALL } public class BuildParams { public string outPath; public string outName; public string defineSymbol; public string buildType; public bool aab = false; public EBundleType bundleType = EBundleType.FirstPack; public bool split = false; } private const string Param_Out_Path = "--out"; private const string Param_Define_Symbol = "--define-symbol"; private const string Param_Split = "--split"; private const string Param_Aab = "--aab"; private const string Param_Bundle_Type = "--bundle-type"; private const string Param_Build_Type = "--build-type"; private const string Param_Ru = "--rustore"; private const string default_iOS_Out_Name = "ft_xcodeproj"; public static void CIBuildAndroid() { //- Debug.Log("CIBuildAndroid-> "); var addrSettings = AddressableAssetSettingsDefaultObject.Settings; List args = new List(Environment.GetCommandLineArgs()); BuildParams bparams = new BuildParams() { outPath = args[args.IndexOf(Param_Out_Path) + 1], defineSymbol = args.IndexOf(Param_Define_Symbol) == -1 ? string.Empty : args[args.IndexOf(Param_Define_Symbol) + 1], buildType = args.IndexOf(Param_Build_Type) == -1 ? "New" : args[args.IndexOf(Param_Build_Type) + 1], split = args.Contains(Param_Split), aab = args.Contains(Param_Aab), bundleType = Enum.Parse(args[args.IndexOf(Param_Bundle_Type) + 1]) }; //- Rustore 相关 if (args.Contains(Param_Ru,StringComparer.OrdinalIgnoreCase)) { DoRuSpecial(); var profile = addrSettings.profileSettings.GetProfileId("arksgameru"); addrSettings.activeProfileId = profile; } else { var profile = addrSettings.profileSettings.GetProfileId("arksgamecos"); addrSettings.activeProfileId = profile; } switch (bparams.buildType) { case "New": BuildAndroid(bparams); break; case "Hotupdate": if (!bparams.aab) BuildHotUpdate(bparams); break; } } private static void DoRuSpecial() { Debug.Log("DoRuSpecial -> Start"); CopyRuAddressablesBin(); CopyRuAndroidBuildReources(); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); Debug.Log("DoRuSpecial -> Finished"); } public static void CIBuildIOS() { List args = new List(Environment.GetCommandLineArgs()); BuildParams bparams = new BuildParams() { outPath = args[args.IndexOf(Param_Out_Path) + 1], defineSymbol = args.IndexOf(Param_Define_Symbol) == -1 ? string.Empty : args[args.IndexOf(Param_Define_Symbol) + 1], buildType = args.IndexOf(Param_Build_Type) == -1 ? "New" : args[args.IndexOf(Param_Build_Type) + 1], split = false, aab = false, bundleType = Enum.Parse(args[args.IndexOf(Param_Bundle_Type) + 1]) }; switch (bparams.buildType) { case "New": BuildIOS(bparams); break; case "Hotupdate": if (!bparams.aab) BuildHotUpdate(bparams); break; } } public static void CIUpdateAddressable() { RemoveAssertbundles(); UpdateAddressable(); } //[MenuItem("Tools/Compile/HotUpdateAssembly")] //static void CompileHotUpdateDllNoDefine() //{ //CompileHotUpdateDll(); //} /* static void CompileHotUpdateDll(params string[] defineSymbol) { var buildTarget = EditorUserBuildSettings.activeBuildTarget; var hotUpdateDir = HybridCLR.Editor.SettingsUtil.GetHotUpdateDllsOutputDirByTarget(buildTarget); var hotUpdateAssetDir = Path.Combine(Application.dataPath, "HotUpdateAssembly").Replace("\\", "/"); var hotUpdateDllFiles = HybridCLR.Editor.SettingsUtil.HotUpdateAssemblyFilesExcludePreserved; var assembliesPostIl2CppStripDir = HybridCLR.Editor.SettingsUtil.GetAssembliesPostIl2CppStripDir(buildTarget); UnityEngine.Debug.Log($"hotUpdateDir: {hotUpdateDir}"); if (Directory.Exists(hotUpdateDir)) { UnityEngine.Debug.Log("Delete hotUpdateDir"); Directory.Delete(hotUpdateDir, true); } CompileDll(hotUpdateDir, buildTarget, false, defineSymbol); foreach (var file in hotUpdateDllFiles) { var sourcePath = Path.Combine(hotUpdateDir, file); var destinationPath = Path.Combine(hotUpdateAssetDir, $"{file}.bytes"); UnityEngine.Debug.Log($"Copy hotupdate dll from [{sourcePath}] to [{destinationPath}]"); File.Copy(sourcePath, destinationPath, true); } //Copy aot dlls from assembliesPostIl2CppStripDir to hotUpdateAssetDir foreach (var file in GConstant.AOT_Dlls) { var sourcePath = Path.Combine(assembliesPostIl2CppStripDir, file); var destinationPath = Path.Combine(hotUpdateAssetDir, $"{file}.bytes"); UnityEngine.Debug.Log($"Copy striped dll from [{sourcePath}] to [{destinationPath}]"); File.Copy(sourcePath, destinationPath, true); } AssetDatabase.Refresh(); } */ private static void CompileDll(string buildDir, BuildTarget target, bool developmentBuild, params string[] defineSymbol) { var group = BuildPipeline.GetBuildTargetGroup(target); ScriptCompilationSettings scriptCompilationSettings = new ScriptCompilationSettings(); scriptCompilationSettings.group = group; scriptCompilationSettings.target = target; scriptCompilationSettings.options = developmentBuild ? ScriptCompilationOptions.DevelopmentBuild : ScriptCompilationOptions.None; if (defineSymbol != null && defineSymbol.Length > 0) scriptCompilationSettings.extraScriptingDefines = defineSymbol; Directory.CreateDirectory(buildDir); ScriptCompilationResult scriptCompilationResult = PlayerBuildInterface.CompilePlayerScripts(scriptCompilationSettings, buildDir); #if UNITY_2022 UnityEditor.EditorUtility.ClearProgressBar(); #endif UnityEngine.Debug.Log("compile finish!!!"); } private static void BuildHotUpdate(BuildParams buildParams) { WriteRawConfig(buildParams.outPath); RemoveAssertbundles(); AddressableHelper.CleanStreamingAssets(); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); //if (string.IsNullOrEmpty(buildParams.defineSymbol)) //CompileHotUpdateDll(); //else //CompileHotUpdateDll(buildParams.defineSymbol); if (!string.IsNullOrEmpty(buildParams.defineSymbol)) { AddScriptingDefineSymbols(buildParams.defineSymbol); } AddressableLabelHelper.SyncGroups(); UpdateAddressable(); WriteAssetsFile(buildParams.outPath); } private static void WriteBuildVersion() { var buildVersionPath = Path.Combine(Application.streamingAssetsPath, VersionTool.Build_Ver_File_Name); var commitHash = GetCommitHash(); File.WriteAllText(buildVersionPath, commitHash); AssetDatabase.Refresh(); } private static void WriteRawConfig(string path) { if(string.IsNullOrEmpty(path)) path = Path.Combine(Directory.GetParent(Application.dataPath).FullName,"bin"); var rawConfigPath = Path.Combine(path, "config_raw.json"); var configTmpPath = Path.Combine(Directory.GetParent(Application.dataPath).Parent.FullName,"AssetsServer/config.json"); var configTmp = Newtonsoft.Json.Linq.JObject.Parse(File.ReadAllText(configTmpPath)); var splitVersion = Application.version.Split('.'); var version = $"{splitVersion[0]}.{splitVersion[1]}.{GetCommitHash()}"; configTmp["Ver"] = version; File.WriteAllText(rawConfigPath, configTmp.ToString(Newtonsoft.Json.Formatting.Indented)); } private static void WriteAssetsFile(string path) { if(string.IsNullOrEmpty(path)) path = Path.Combine(Directory.GetParent(Application.dataPath).FullName,"bin"); var savePath = Path.Combine(path, "assets.json"); if(File.Exists(savePath)) File.Delete(savePath); if (!string.IsNullOrEmpty(path) && !Directory.Exists(path)) { Directory.CreateDirectory(path); } UnityEngine.Debug.Log($"savePath: {savePath}"); File.WriteAllText(savePath, AddressableHelper.ExportAssetsFile()); } private static string GetCommitHash() { var gitPath = Directory.GetParent(Application.dataPath).Parent; ProcessStartInfo startInfo = new ProcessStartInfo { FileName = "git", Arguments = $"--git-dir={gitPath}/.git --work-tree={gitPath} rev-parse --short HEAD", RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; using (Process process = Process.Start(startInfo)) { process.WaitForExit(); string output = process.StandardOutput.ReadToEnd().Trim(); return output; } } private static void DeleteBuildVersion() { var buildVersionPath = Path.Combine(Application.streamingAssetsPath, VersionTool.Build_Ver_File_Name); if (File.Exists(buildVersionPath)) { File.Delete(buildVersionPath); AssetDatabase.Refresh(); } } private static void BuildIOS(BuildParams buildParams) { UpdateAplovinInternalSettings(); var outputFullPath = string.IsNullOrEmpty(buildParams.outName) ? $"{buildParams.outPath}/{default_iOS_Out_Name}" : $"{buildParams.outPath}/{buildParams.outName}"; var buildPlayerOptions = new BuildPlayerOptions { scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(), locationPathName = outputFullPath, target = BuildTarget.iOS, options = BuildOptions.None, }; DoBuild(buildParams, buildPlayerOptions); WriteRawConfig(buildParams.outPath); AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } private static void BuildAndroid(BuildParams buildParams) { UpdateAplovinInternalSettings(); var is_aab = EditorUserBuildSettings.buildAppBundle; EditorUserBuildSettings.buildAppBundle = buildParams.aab; EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Public; PlayerSettings.Android.useAPKExpansionFiles = buildParams.split; var default_pack_file_name = buildParams.aab ? "ft.aab" : "ft.apk"; var outputFullPath = string.IsNullOrEmpty(buildParams.outName) ? $"{buildParams.outPath}/{default_pack_file_name}" : $"{buildParams.outPath}/{buildParams.outName}"; var buildPlayerOptions = new BuildPlayerOptions { scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(), locationPathName = outputFullPath, target = BuildTarget.Android, options = BuildOptions.None, }; DoBuild(buildParams, buildPlayerOptions); WriteRawConfig(buildParams.outPath); EditorUserBuildSettings.buildAppBundle = false; AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } private static void DoBuild(BuildParams buildParams, BuildPlayerOptions buildPlayerOptions) { PlayerSettings.SplashScreen.showUnityLogo = false; RemoveAssertbundles(); DeleteBuildVersion(); AddressableHelper.CleanStreamingAssets(); if (!string.IsNullOrEmpty(buildParams.defineSymbol)) { AddScriptingDefineSymbols(buildParams.defineSymbol); } AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); WriteBuildVersion(); //if (string.IsNullOrEmpty(buildParams.defineSymbol)) //CompileHotUpdateDll(); //else //CompileHotUpdateDll(buildParams.defineSymbol); AddressableLabelHelper.SyncGroups(); UpdateAddressable(); switch (buildParams.bundleType) { case EBundleType.FirstPack: AddressableHelper.CopyToStreamingAssets(); break; case EBundleType.ALL: AddressableHelper.CopyALLToStreamingAssets(); break; } WriteAssetsFile(buildParams.outPath); CopyCfgData(); var build_report = BuildPipeline.BuildPlayer(buildPlayerOptions); if (build_report?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded) { UnityEngine.Debug.Log($"[Build Player Success] {System.IO.Path.GetFullPath(buildParams.outPath)}"); AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport); } else { UnityEngine.Debug.LogError($"Build Player Failed: {build_report?.summary}"); } if (!string.IsNullOrEmpty(buildParams.defineSymbol)) { RemoveScriptingDefineSymbols(buildParams.defineSymbol); } AddressableHelper.CleanStreamingAssets(); DeleteBuildVersion(); } [MenuItem("Tools/Build/APK")] private static void DoBuildAPK() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.apk", }; BuildAndroid(bparams); } [MenuItem("Tools/Build/APK Full")] private static void DoBuildAPKFull() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.apk", bundleType = EBundleType.ALL }; BuildAndroid(bparams); } [MenuItem("Tools/Build/APK Empty")] private static void DoBuildAPKEmpty() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.apk", bundleType = EBundleType.None }; BuildAndroid(bparams); } [MenuItem("Tools/Build/APK Agg")] private static void DoBuildAPKAgg() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_agg_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.apk", defineSymbol = "AGG" }; BuildAndroid(bparams); } [MenuItem("Tools/Build/APK Agg Full")] private static void DoBuildAPKAggFull() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_agg_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.apk", defineSymbol = "AGG", bundleType = EBundleType.ALL }; BuildAndroid(bparams); } [MenuItem("Tools/Build/AAB")] private static void DoBuildAAB() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.aab", aab = true, split = true }; BuildAndroid(bparams); } [MenuItem("Tools/Build/AAB Agg")] private static void DoBuildAABAgg() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_agg_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.aab", defineSymbol = "AGG", aab = true, split = true }; BuildAndroid(bparams); } [MenuItem("Tools/Build/AAB Agg Full")] private static void DoBuildAABAggFull() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_agg_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}.aab", bundleType = EBundleType.ALL, defineSymbol = "AGG", aab = true, split = true }; BuildAndroid(bparams); } [MenuItem("Tools/Hotupdate/APK")] private static void DoBuildHotupdate() { BuildParams bparams = new BuildParams(); BuildHotUpdate(bparams); } [MenuItem("Tools/Hotupdate/APK agg")] private static void DoBuildHotupdateAgg() { BuildParams bparams = new BuildParams() { defineSymbol = "AGG" }; BuildHotUpdate(bparams); } [MenuItem("Tools/Build/IPA Full")] private static void DoBuildIPAFull() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_xcode_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}", bundleType = EBundleType.ALL, }; BuildIOS(bparams); } [MenuItem("Tools/Build/IPA Full Agg")] private static void DoBuildIPAFullAgg() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_xcode_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}", bundleType = EBundleType.ALL, defineSymbol = "AGG" }; BuildIOS(bparams); } [MenuItem("Tools/Build/IPA Agg")] private static void DoBuildIPAAgg() { string outPath = Directory.GetParent(UnityEngine.Application.dataPath).FullName.Replace("\\", "/"); DateTime now = DateTime.Now; BuildParams bparams = new BuildParams() { outPath = $"{outPath}/bin", outName = $"ft_xcode_{UnityEngine.Application.version.Replace('.', '_')}_{now.ToString("yyyyMMddHHmmss")}", bundleType = EBundleType.FirstPack, defineSymbol = "AGG" }; BuildIOS(bparams); } [MenuItem("Tools/Hotupdate/IPA")] private static void HotUpdateIOS() { DoBuildHotupdate(); } [MenuItem("Tools/Hotupdate/IPA Agg")] private static void HotUpdateIOSAgg() { DoBuildHotupdateAgg(); } [MenuItem("Tools/Build/RemoveAssertbundles")] private static void RemoveAssertbundles() { var addrSettings = AddressableAssetSettingsDefaultObject.Settings; var bundleBuildPath = addrSettings.RemoteCatalogBuildPath.GetValue(addrSettings); var rootBundleBuildPath = Directory.GetParent(bundleBuildPath).Parent; if (rootBundleBuildPath.Name == "assetbundles") { UnityEngine.Debug.Log($"RemoveAssertbundles {rootBundleBuildPath.FullName}"); if(Directory.Exists(rootBundleBuildPath.FullName)) Directory.Delete(rootBundleBuildPath.FullName, true); } } private static void UpdateAddressable() { var settings = AddressableAssetSettingsDefaultObject.Settings; var stateBinPath = ContentUpdateScript.GetContentStateDataPath(false); if (File.Exists(stateBinPath)) { UnityEditor.AddressableAssets.Settings.AddressableAssetSettings.CleanPlayerContent(null); UnityEditor.Build.Pipeline.Utilities.BuildCache.PurgeCache(false); ContentUpdateScript.BuildContentUpdate(settings, stateBinPath); } else { throw new Exception($"state.bin file not found at {stateBinPath}"); } } private static bool CheckStaticAssetUpdate() { var settings = AddressableAssetSettingsDefaultObject.Settings; var stateBinPath = ContentUpdateScript.GetContentStateDataPath(false); if (!File.Exists(stateBinPath)) { throw new Exception($"state.bin file not found at {stateBinPath}"); } var modifiedEntries = ContentUpdateScript.GatherModifiedEntriesWithDependencies(settings, stateBinPath); return modifiedEntries.Count == 0; } private static void AddScriptingDefineSymbols(string dfs) { var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup, out string[] symbols); if (symbols.Contains(dfs)) return; var newDefines = new List(symbols); newDefines.Add(dfs); PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", newDefines.ToArray())); } private static void RemoveScriptingDefineSymbols(string dfs) { var buildTargetGroup = EditorUserBuildSettings.selectedBuildTargetGroup; PlayerSettings.GetScriptingDefineSymbolsForGroup(buildTargetGroup, out string[] symbols); if (!symbols.Contains(dfs)) return; var newDefines = new List(symbols); newDefines.Remove(dfs); PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, string.Join(";", newDefines.ToArray())); } [MenuItem("Tools/Build/CopyCfgData")] private static void CopyCfgData() { var targetDir = Path.Combine(Application.streamingAssetsPath, "cfgData"); var sourceDir = "../cfgPack"; if(Directory.Exists(targetDir)) { Directory.Delete(targetDir, true); } Directory.CreateDirectory(targetDir); var files = Directory.GetFiles(sourceDir); foreach (var file in files) { var savePath = Path.Combine(targetDir, Path.GetFileName(file)); if (File.Exists(savePath)) File.Delete(savePath); File.Copy(file, savePath); } AssetDatabase.Refresh(); AssetDatabase.SaveAssets(); } [MenuItem("Tools/Build/applovinSetting")] private static void UpdateAplovinInternalSettings() { var projPath = Directory.GetParent(Application.dataPath).FullName; var settingsFilePath = Path.Combine(projPath, "ProjectSettings/AppLovinInternalSettings.json"); var settingJson = File.ReadAllText(settingsFilePath); var settings = Newtonsoft.Json.Linq.JObject.Parse(settingJson); if(EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) { settings["consentFlowPrivacyPolicyUrl"] = GConstant.V_Android_Privacy_Policy_URL; } else { settings["consentFlowPrivacyPolicyUrl"] = GConstant.V_IOS_Privacy_Policy_URL; } settings["consentFlowTermsOfServiceUrl"] = "https://arksgame.com/terms.html"; File.WriteAllText(settingsFilePath, settings.ToString()); } [MenuItem("Tools/Build/SwitchProfile")] public static void SwitchProfile() { var addrSettings = AddressableAssetSettingsDefaultObject.Settings; var profile = addrSettings.profileSettings.GetProfileId("arksgamecos"); addrSettings.activeProfileId = profile; // AddressableLabelHelper.SyncGroups(); // UpdateAddressable(); } [MenuItem("Tools/Build/CopyRuTest")] public static void CopyRuTest() { DoRuSpecial(); AddressableLabelHelper.SyncGroups(); UpdateAddressable(); //-COpy AddressableHelper.CopyToStreamingAssets(); } // #define ELOG private static void CopyRuAddressablesBin() { Debug.LogWarning("Copy Addressables Bin !!! 注意 拷贝 生成的bin 文件到 {Source} 目录"); var sourceDir = "../ExtraPluginCfgs/AddressableAssetsData/Android"; var targetDir = "../Client/Assets/AddressableAssetsData/Android"; if(!Directory.Exists(sourceDir)) { Debug.Log($"sourcdDir: {sourceDir} 不存在,请检查"); } if(!Directory.Exists(targetDir)) { Debug.Log($"targetDir: {targetDir} 不存在,请检查"); } // DoCopy var fileName = "addressables_content_state.bin"; var sourceFile = Path.Combine(sourceDir, fileName); var savePath = Path.Combine(targetDir, fileName); if (File.Exists(savePath)) File.Delete(savePath); File.Copy(sourceFile, savePath); Debug.Log($"Copy {sourceFile}-> {savePath}"); } //- 执行特殊操作-Rustore //- 拷贝特殊文件 private static void CopyRuAndroidBuildReources() { var sourceDir = "../ExtraPluginCfgs/Android/rustore"; var targetDir = "../Client/Assets/Plugins/Android"; if(!Directory.Exists(sourceDir)) { UnityEngine.Debug.Log($"sourcdDir: {sourceDir} 不存在,请检查"); } if(!Directory.Exists(targetDir)) { UnityEngine.Debug.Log($"targetDir: {targetDir} 不存在,请检查"); } var files = Directory.GetFiles(sourceDir); foreach (var file in files) { var savePath = Path.Combine(targetDir, Path.GetFileName(file)); if (File.Exists(savePath)) File.Delete(savePath); File.Copy(file, savePath); Debug.Log($"Copy {file}-> {savePath}"); } } }