少年修仙传客户端基础资源
hch
2024-04-11 4c71d74b77c9eb62a0323698c9a0db3b641a917e
Assets/Editor/AssetBundleBrowser/AssetBundleBuildTab.cs
@@ -6,8 +6,16 @@
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]
@@ -322,7 +330,10 @@
                    EditorGUI.indentLevel = indent;
                }
            }
            if (GUILayout.Button("HybridclrBuild(不能热更)"))
            {
                EditorApplication.delayCall += ExcuteBuildHybridclrBuild;
            }
            // build.
            EditorGUILayout.Space();
            if (GUILayout.Button("Build All Assets"))
@@ -335,11 +346,6 @@
            if (GUILayout.Button("Config"))
            {
                EditorApplication.delayCall += ExcuteBuildConfig;
            }
            if (GUILayout.Button("Lua"))
            {
                EditorApplication.delayCall += ExcuteBuildLua;
            }
            if (GUILayout.Button("UI"))
@@ -370,13 +376,12 @@
            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();
@@ -389,6 +394,11 @@
            if (GUILayout.Button("Make VersionFile"))
            {
                EditorApplication.delayCall += MakeAssetsVersionFile;
            }
            if (GUILayout.Button("Copy to StreamingAssets"))
            {
                EditorApplication.delayCall += CopyToStreamingAssets;
            }
            EditorGUILayout.EndHorizontal();
@@ -440,6 +450,13 @@
            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();
@@ -509,6 +526,13 @@
            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()
        {
@@ -561,8 +585,7 @@
            ExcuteBuildLevels();
            ExcuteBuildUI();
            ExcuteBuildBuiltIn();
            ExcuteBuildLua();
            ExcuteBuildPatch();
            ExcuteBuildHybridclrUpdate();
            AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
@@ -576,12 +599,12 @@
            }
        }
        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;
                }
@@ -640,38 +663,198 @@
            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()
@@ -727,9 +910,60 @@
        {
            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); }