| | |
| | | |
| | | using UnityEngine.AssetBundles.AssetBundleDataSource; |
| | | using Assets.Editor.Tool; |
| | | using IFix.Editor; |
| | | |
| | | using HybridCLR.Editor.Commands; |
| | | using HybridCLR.Editor.Settings; |
| | | //using Mono.Cecil; |
| | | using DG.Tweening.Plugins.Core.PathCore; |
| | | using System; |
| | | using Path = System.IO.Path; |
| | | using HybridCLR.Editor.HotUpdate; |
| | | using HybridCLR.Editor; |
| | | using System.Text;
|
| | |
|
| | | namespace UnityEngine.AssetBundles |
| | | { |
| | | [System.Serializable] |
| | |
| | | EditorGUI.indentLevel = indent; |
| | | } |
| | | } |
| | | |
| | | if (GUILayout.Button("HybridclrBuild(不能热更)")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildHybridclrBuild; |
| | | } |
| | | // build. |
| | | EditorGUILayout.Space(); |
| | | if (GUILayout.Button("Build All Assets")) |
| | |
| | | if (GUILayout.Button("Config")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildConfig; |
| | | } |
| | | |
| | | if (GUILayout.Button("Lua")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildLua; |
| | | } |
| | | |
| | | if (GUILayout.Button("UI")) |
| | |
| | | EditorApplication.delayCall += ExcuteBuildAudio; |
| | | } |
| | | |
| | | if (GUILayout.Button("Video")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildVideo; |
| | | } |
| | | |
| | | if (GUILayout.Button("Levels")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildLevels; |
| | |
| | | if (GUILayout.Button("MobEffectShader")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildMobEffectShader; |
| | | } |
| | | |
| | | if (GUILayout.Button("patch")) |
| | | }
|
| | |
|
| | | if (GUILayout.Button("HybridclrUpdate")) |
| | | { |
| | | EditorApplication.delayCall += ExcuteBuildPatch; |
| | | EditorApplication.delayCall += ExcuteBuildHybridclrUpdate; |
| | | } |
| | | |
| | | EditorGUILayout.EndHorizontal(); |
| | | |
| | | EditorGUILayout.Space(); |
| | |
| | | if (GUILayout.Button("Make VersionFile")) |
| | | { |
| | | EditorApplication.delayCall += MakeAssetsVersionFile; |
| | | } |
| | | |
| | | if (GUILayout.Button("Copy to StreamingAssets")) |
| | | { |
| | | EditorApplication.delayCall += CopyToStreamingAssets; |
| | | } |
| | | |
| | | EditorGUILayout.EndHorizontal(); |
| | |
| | | publishers = EditorGUILayout.TextField("Publishers", publishers, GUILayout.Height(50)); |
| | | GUILayout.EndHorizontal(); |
| | | |
| | | EditorGUILayout.Space(); |
| | | GUILayout.BeginHorizontal(); |
| | | |
| | | ClientPackage.includeConfig = EditorGUILayout.Toggle("Include Config ", ClientPackage.includeConfig, GUILayout.Width(250)); |
| | | ClientPackage.includeUI = EditorGUILayout.Toggle("Include UI ", ClientPackage.includeUI, GUILayout.Width(250)); |
| | | |
| | | GUILayout.EndHorizontal(); |
| | | EditorGUILayout.Space(); |
| | | GUILayout.BeginHorizontal(); |
| | | |
| | |
| | | EditorGUILayout.EndScrollView(); |
| | | } |
| | | |
| | | private void CopyToStreamingAssets() |
| | | { |
| | | if (Directory.Exists(m_streamingPath)) |
| | | Directory.Delete(m_streamingPath, true); |
| | | DirectoryCopy(m_UserData.m_OutputPath, m_streamingPath); |
| | | } |
| | | |
| | | private void ExecuteBuildAll() |
| | | { |
| | | |
| | |
| | | } |
| | | |
| | | ExcuteBuildAudio(); |
| | | ExcuteBuildVideo(); |
| | | ExcuteBuildMobEffectShader(); |
| | | ExcuteBuildConfig(); |
| | | ExcuteBuildLevels(); |
| | | ExcuteBuildUI(); |
| | | ExcuteBuildBuiltIn(); |
| | | ExcuteBuildLua(); |
| | | ExcuteBuildPatch(); |
| | | ExcuteBuildHybridclrUpdate(); |
| | | |
| | | AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate); |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private void ExcuteBuildAsset(string _category) |
| | | private void ExcuteBuildAsset(string _category, bool uncompressed = false) |
| | | { |
| | | BuildAssetBundleOptions opt = BuildAssetBundleOptions.None; |
| | | if (AssetBundleModel.Model.DataSource.CanSpecifyBuildOptions) |
| | | { |
| | | if (m_UserData.m_Compression == CompressOptions.Uncompressed) |
| | | if (m_UserData.m_Compression == CompressOptions.Uncompressed || uncompressed) |
| | | { |
| | | opt |= BuildAssetBundleOptions.UncompressedAssetBundle; |
| | | } |
| | |
| | | { |
| | | UpdateAudioSetting.SetAllAudioAssetBundleName(); |
| | | ExcuteBuildAsset("audio"); |
| | | } |
| | | |
| | | private void ExcuteBuildVideo() |
| | | { |
| | | UpdateVideoSetting.SetAllVideoAssetBundleName(); |
| | | ExcuteBuildAsset("video"); |
| | | } |
| | |
| | | ExcuteBuildAsset("config"); |
| | | TableTool.CopyConfigsToOutPutPath(StringUtility.Contact(m_UserData.m_OutputPath, "/config")); |
| | | } |
| | | |
| | | private void ExcuteBuildLua() |
| | | //发包时获取热更dll和裁剪AOT |
| | | private void ExcuteBuildHybridclrBuild() |
| | | { |
| | | LuaBuildHelper.OnPreBuild(); |
| | | UpdateLuaSetting.SetAllLuaAssetBundleName(); |
| | | ExcuteBuildAsset("lua"); |
| | | LuaBuildHelper.OnPostBuild(); |
| | | } |
| | | |
| | | private void ExcuteBuildPatch() |
| | | { |
| | | string sourcePath; |
| | | if (m_UserData.m_BuildTarget == ValidBuildTarget.Android) |
| | | { |
| | | IFixEditor.CompileToAndroid(); |
| | | sourcePath = ResourcesPath.PATCH_ANDROID; |
| | | } |
| | | else if (m_UserData.m_BuildTarget == ValidBuildTarget.iOS) |
| | | { |
| | | IFixEditor.CompileToIOS(); |
| | | sourcePath = ResourcesPath.PATCH_IOS; |
| | | } |
| | | else |
| | | { |
| | | IFixEditor.Patch(); |
| | | sourcePath = ResourcesPath.PATCH_EDITOR; |
| | | } |
| | | //重新生成热更dll和裁剪AOT |
| | | PrebuildCommand.GenerateAll(); |
| | | // 输出到AssetBundles目录 |
| | | var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath); |
| | | outputPath = StringUtility.Contact(outputPath, "/patch"); |
| | | outputPath = StringUtility.Contact(outputPath, "/logicbytes"); |
| | | if (Directory.Exists(outputPath)) |
| | | Directory.Delete(outputPath, true); |
| | | DirectoryCopy(sourcePath, outputPath, ".meta"); |
| | | //复制新生成的AOT dll到指定路径 |
| | | CopyAOTDataDll(outputPath); |
| | | //复制热更dll到指定路径 |
| | | CopyHotUpdateDll(outputPath); |
| | | AssetDatabase.Refresh(); |
| | | } |
| | | //热更新时获取热更dll |
| | | private void ExcuteBuildHybridclrUpdate() |
| | | { |
| | | //生成热更dll |
| | | CompileDllCommand.CompileDll(EditorUserBuildSettings.activeBuildTarget); |
| | | //判断当前的dll有没有使用了被裁剪的aot dll |
| | | if (IsAccessMissingMetadata()) |
| | | { |
| | | DebugEx.LogError("当前热更代码使用了被裁剪的aot dll,请使用HybridclrBuild重新生成,重新出包"); |
| | | return; |
| | | } |
| | | // 输出到AssetBundles目录 |
| | | var outputPath = Application.dataPath.Replace("Assets", m_UserData.m_OutputPath); |
| | | outputPath = StringUtility.Contact(outputPath, "/logicbytes"); |
| | | CopyHotUpdateDll(outputPath); |
| | | MakeBytesVersionFile(); |
| | | AssetDatabase.Refresh(); |
| | | } |
| | | |
| | | private void MakeBytesVersionFile() |
| | | { |
| | | ChangeBytes(); |
| | | var fileInfos = new List<FileInfo>(); |
| | | FileExtersion.GetAllDirectoryFileInfos(StringUtility.Contact(m_UserData.m_OutputPath, "/logicbytes"), fileInfos); |
| | | BytesVersionMaker.WriteAssetsVersionFile(Path.Combine(Directory.GetParent(Application.dataPath).FullName, m_UserData.m_OutputPath), fileInfos); |
| | | Debug.Log("热更新代码更新完毕,生成md5文件"); |
| | | } |
| | | |
| | | //复制裁剪AOT到指定目录 |
| | | void CopyAOTDataDll(string outputPath) |
| | | { |
| | | //项目根目录 |
| | | string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/"); |
| | | //当前构建的平台名称 |
| | | string platformName = "/" + EditorUserBuildSettings.activeBuildTarget + "/"; |
| | | //上次出包时使用的AOT所在路径 |
| | | string checkAotDir = rootDir + "/HybridCLRData/CheckAssembliesPostIl2CppStrip" + platformName; |
| | | //HybridCLR Settings面板上设置的裁减后AOT dll输出根目录 |
| | | string strippedAOTDllOutputRootDir = "/" + HybridCLRSettings.Instance.strippedAOTDllOutputRootDir; |
| | | //重新生成的AOT所在路径 |
| | | string aotDir = StringUtility.Contact(rootDir, strippedAOTDllOutputRootDir, platformName); |
| | | |
| | | if (Directory.Exists(checkAotDir)) |
| | | { |
| | | Directory.Delete(checkAotDir, true); |
| | | Directory.CreateDirectory(checkAotDir); |
| | | } |
| | | //复制一份出包时使用的AOT |
| | | CopyDirectoryToPath(aotDir, checkAotDir); |
| | | |
| | | for (int i = 0; i < AOTGenericReferences.PatchedAOTAssemblyList.Count; i++) |
| | | { |
| | | string dllFile = StringUtility.Contact(aotDir, AOTGenericReferences.PatchedAOTAssemblyList[i]); |
| | | var outDllFile = outputPath + "/" + AOTGenericReferences.PatchedAOTAssemblyList[i] + ".bytes"; |
| | | if (!File.Exists(dllFile)) |
| | | { |
| | | DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile); |
| | | return; |
| | | } |
| | | |
| | | FileExtersion.MakeSureDirectory(outDllFile); |
| | | File.Copy(dllFile, outDllFile, true); |
| | | } |
| | | } |
| | | |
| | | //复制热更dll到指定目录 |
| | | void CopyHotUpdateDll(string outputPath) |
| | | { |
| | | //项目根目录 |
| | | string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/"); |
| | | //当前构建的平台名称 |
| | | string platformName = "/" + EditorUserBuildSettings.activeBuildTarget + "/"; |
| | | //HybridCLR Settings面板上设置的热更新dll编译输出根目录 |
| | | string hotUpdateDllCompileOutputRootDir = "/" + HybridCLRSettings.Instance.hotUpdateDllCompileOutputRootDir; |
| | | //热更DLL生成目录 |
| | | string hotDllDir = StringUtility.Contact(rootDir, hotUpdateDllCompileOutputRootDir, platformName); |
| | | //HybridCLR的Settings面板上设置的热更新Assembly Definitions 资源 |
| | | var assemblyDefinitionAsset = HybridCLRSettings.Instance.hotUpdateAssemblyDefinitions; |
| | | for (int i = 0; i < assemblyDefinitionAsset.Length; i++) |
| | | { |
| | | string assemblyPath = AssetDatabase.GetAssetPath(assemblyDefinitionAsset[i]); |
| | | string assemblyName = Path.GetFileNameWithoutExtension(assemblyPath); |
| | | |
| | | string dllFile = StringUtility.Contact(hotDllDir, assemblyName, ".dll"); |
| | | var outDllFile = outputPath + "/" + assemblyName + ".dll.bytes"; |
| | | if (!File.Exists(dllFile)) |
| | | { |
| | | DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile); |
| | | return; |
| | | } |
| | | FileExtersion.MakeSureDirectory(outDllFile); |
| | | File.Copy(dllFile, outDllFile, true); |
| | | } |
| | | |
| | | //HybridCLR的Settings面板上设置的要热更的DLL资源 |
| | | string[] hotUpdateAssemblies = HybridCLRSettings.Instance.hotUpdateAssemblies; |
| | | for (int i = 0; i < hotUpdateAssemblies.Length; i++) |
| | | { |
| | | string dllFile = StringUtility.Contact(hotDllDir, hotUpdateAssemblies[i], ".dll"); |
| | | var outDllFile = outputPath + "/" + hotUpdateAssemblies[i] + ".dll.bytes"; |
| | | if (!File.Exists(dllFile)) |
| | | { |
| | | DebugEx.LogErrorFormat("错误:{0} 不存在", dllFile); |
| | | return; |
| | | } |
| | | FileExtersion.MakeSureDirectory(outDllFile); |
| | | File.Copy(dllFile, outDllFile, true); |
| | | } |
| | | |
| | | //HybridCLR的Settings面板上设置的预留的热更新dlls |
| | | string[] preserveHotUpdateAssemblies = HybridCLRSettings.Instance.preserveHotUpdateAssemblies; |
| | | for (int i = 0; i < preserveHotUpdateAssemblies.Length; i++) |
| | | { |
| | | string dllFile = StringUtility.Contact(hotDllDir, preserveHotUpdateAssemblies[i], ".dll"); |
| | | var outDllFile = outputPath + "/" + preserveHotUpdateAssemblies[i] + ".dll.bytes"; |
| | | if (!File.Exists(dllFile)) |
| | | { |
| | | DebugEx.LogWarningFormat("警告:预留的热更新dll: {0} 不存在", dllFile); |
| | | continue; |
| | | } |
| | | FileExtersion.MakeSureDirectory(outDllFile); |
| | | File.Copy(dllFile, outDllFile, true); |
| | | } |
| | | } |
| | | |
| | | //检查目标文件夹下是否有文件 |
| | | bool IsFolderNotEmpty(string dir) |
| | | { |
| | | if (Directory.Exists(dir)) |
| | | { |
| | | string[] files = Directory.GetFiles(dir); |
| | | if (files.Length > 0) |
| | | return true; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | //检查热更新代码中是否引用了被裁剪的类型或函数 |
| | | bool IsAccessMissingMetadata() |
| | | { |
| | | //项目根目录 |
| | | string rootDir = Directory.GetParent(Application.dataPath).FullName.Replace(@"\", @"/"); |
| | | //当前构建的平台名称 |
| | | BuildTarget target = EditorUserBuildSettings.activeBuildTarget; |
| | | string platformName = "/" + target + "/"; |
| | | //上次出包时使用的AOT所在路径 |
| | | string checkAotDir = rootDir + "/HybridCLRData/CheckAssembliesPostIl2CppStrip" + platformName; |
| | | |
| | | //上次打包时保存的AOT所在文件夹不存在或没有dll文件 |
| | | if (!IsFolderNotEmpty(checkAotDir)) |
| | | return true; |
| | | var checker = new MissingMetadataChecker(checkAotDir, new List<string>()); |
| | | string hotUpdateDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); |
| | | for (int i = 0; i < SettingsUtil.HotUpdateAssemblyFilesExcludePreserved.Count; i++) |
| | | { |
| | | string dllPath = hotUpdateDir + "/" + SettingsUtil.HotUpdateAssemblyFilesExcludePreserved[i]; |
| | | bool notAnyMissing = checker.Check(dllPath); |
| | | if (!notAnyMissing) |
| | | { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | private void CopyDirectoryToPath(string sourceDir, string destinationDir) |
| | | { |
| | | // 如果目标路径不存在,则创建它 |
| | | if (!Directory.Exists(destinationDir)) |
| | | Directory.CreateDirectory(destinationDir); |
| | | // 获取所有文件并复制 |
| | | string[] files = Directory.GetFiles(sourceDir); |
| | | for (int i = 0; i < files.Length; i++) |
| | | { |
| | | string dest = Path.Combine(destinationDir, Path.GetFileName(files[i])); |
| | | File.Copy(files[i], dest, true); // true表示如果目标存在,则覆盖 |
| | | } |
| | | } |
| | | |
| | | private void ExcuteBuildLevels() |
| | |
| | | { |
| | | var fileInfos = new List<FileInfo>(); |
| | | FileExtersion.GetAllDirectoryFileInfos(m_UserData.m_OutputPath, fileInfos); |
| | | AssetsVersionMaker.WriteAssetsVersionFile(Path.Combine(Directory.GetParent(Application.dataPath).FullName, m_UserData.m_OutputPath), fileInfos); |
| | | AssetsVersionMaker.WriteAssetsVersionFile(Path.Combine(Directory.GetParent(Application.dataPath).FullName, m_UserData.m_OutputPath), fileInfos);
|
| | | |
| | | Debug.Log("生成AssetsVersion.txt文件完毕"); |
| | | } |
| | | |
| | | void ChangeBytes()
|
| | | {
|
| | | string path = StringUtility.Contact(m_UserData.m_OutputPath, "/logicbytes/");
|
| | | // 原始DLL字节内容文件的路径 |
| | | string originalDllBytesFilePath = path + "Assembly-CSharp.dll.bytes";
|
| | |
|
| | | // 临时文件的路径,用于写入"abc"后再追加原始DLL字节内容 |
| | | string tempFilePath = path + "temp_Assembly-CSharp.dll.bytes";
|
| | |
|
| | | // 要写入的字符串"abc" |
| | | string contentToWrite = "abc";
|
| | |
|
| | | // 将"abc"转换为字节数组 |
| | | byte[] abcBytes = Encoding.ASCII.GetBytes(contentToWrite);
|
| | |
|
| | | // 读取原始DLL字节内容到字节数组 |
| | | byte[] originalDllBytes = File.ReadAllBytes(originalDllBytesFilePath);
|
| | |
|
| | | // 将"abc"写入临时文件 |
| | | using (FileStream fs = new FileStream(tempFilePath, FileMode.Create, FileAccess.Write))
|
| | | {
|
| | | fs.Write(abcBytes, 0, abcBytes.Length);
|
| | |
|
| | | // 追加原始DLL字节内容到临时文件 |
| | | fs.Write(originalDllBytes, 0, originalDllBytes.Length);
|
| | | }
|
| | |
|
| | | ReplaceFile(tempFilePath, originalDllBytesFilePath);
|
| | | }
|
| | |
|
| | | public void ReplaceFile(string sourceFile, string targetFile)
|
| | | {
|
| | | // 确保源文件存在 |
| | | if (!File.Exists(sourceFile))
|
| | | {
|
| | | throw new FileNotFoundException("源文件未找到:" + sourceFile);
|
| | | }
|
| | |
|
| | | // 如果目标文件存在,则删除它 |
| | | if (File.Exists(targetFile))
|
| | | {
|
| | | File.Delete(targetFile);
|
| | | }
|
| | |
|
| | | // 将源文件重命名为目标文件 |
| | | File.Move(sourceFile, targetFile);
|
| | | }
|
| | |
|
| | | |
| | | static int packageIndex |
| | | { |
| | | get { return LocalSave.GetInt("ClientPackageIndex", 1); } |