taochangchun пре 1 недеља
родитељ
комит
904b53777a

+ 55 - 0
Editor/AssetBudleConfig.cs

@@ -0,0 +1,55 @@
1
+using System.Collections.Generic;
2
+using UnityEngine;
3
+using System;
4
+
5
+namespace TModule.Editor
6
+{
7
+    [Serializable]
8
+    public class AssetBudleConfig : ScriptableObject
9
+    {
10
+        public List<AssetBundleEntity> m_AB = new List<AssetBundleEntity>();
11
+    }
12
+    [Serializable]
13
+    public class AssetBundleEntity
14
+    {
15
+        /// <summary>
16
+        /// ID
17
+        /// </summary>
18
+        public string m_key;
19
+
20
+        /// <summary>
21
+        /// 名称
22
+        /// </summary>
23
+        public string m_name;
24
+
25
+        /// <summary>
26
+        /// 标签
27
+        /// </summary>
28
+        public string m_tag;
29
+
30
+        /// <summary>
31
+        /// 版本号
32
+        /// </summary>
33
+        public int m_version;
34
+
35
+        /// <summary>
36
+        /// 大小
37
+        /// </summary>
38
+        public long m_size;
39
+
40
+        /// <summary>
41
+        /// 打包路径
42
+        /// </summary>
43
+        public string m_toPath;
44
+
45
+        /// <summary>
46
+        /// 是否是文件夹
47
+        /// </summary>
48
+        public bool m_isFolder;
49
+
50
+        /// <summary>
51
+        /// 是否是初始资源
52
+        /// </summary>
53
+        public bool m_isFirstData;
54
+    }
55
+}

+ 11 - 0
Editor/AssetBudleConfig.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: a2d4bb0157c733647aa12789ecab546e
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 418 - 0
Editor/AssetBudleTool.cs

@@ -0,0 +1,418 @@
1
+using HybridCLR.Editor;
2
+using HybridCLR.Editor.Commands;
3
+using Newtonsoft.Json;
4
+using System;
5
+using System.Collections;
6
+using System.Collections.Generic;
7
+using System.IO;
8
+using System.Security.Cryptography;
9
+using System.Text;
10
+using TFramework;
11
+using TModule.Runtime;
12
+using UnityEditor;
13
+using UnityEngine;
14
+
15
+namespace TModule.Editor
16
+{
17
+    public class AssetBudleTool : EditorWindow
18
+    {
19
+        private string AssetBudleConfigName= "AssetBundleConfig.asset";
20
+        private string AssetBudleConfigPath;
21
+        private string AssetbudleToolConfigName = "BudleToolConfig.asset";
22
+        private  string AssetbudleToolConfigPath;
23
+        private AssetBudleConfig _list;
24
+        private AssetBudleConfig BudleConfig
25
+        {
26
+            get
27
+            {
28
+                _list = _list ?? AssetDatabase.LoadAssetAtPath<AssetBudleConfig>(AssetBudleConfigPath);
29
+                if(_list==null)
30
+                {
31
+                    _list = CreateInstance<AssetBudleConfig>();
32
+                    AssetDatabase.CreateAsset(_list, AssetBudleConfigPath);
33
+                }
34
+                return _list;
35
+            }
36
+            set => _list = value;
37
+        }
38
+
39
+        private AssetBudleToolConfig _toolConfig;
40
+
41
+        private AssetBudleToolConfig BudleToolConfig
42
+        {
43
+            get
44
+            {
45
+                _toolConfig = _toolConfig ?? AssetDatabase.LoadAssetAtPath<AssetBudleToolConfig>(AssetbudleToolConfigPath);
46
+                if(_toolConfig==null)
47
+                {
48
+                    _toolConfig = CreateInstance<AssetBudleToolConfig>();
49
+                    AssetDatabase.CreateAsset(_toolConfig, AssetbudleToolConfigPath);
50
+                }
51
+                return _toolConfig;
52
+            }
53
+            set => _toolConfig = value;
54
+        }
55
+
56
+        private static List<VersionDataEntity> versionDatas = new List<VersionDataEntity>();
57
+
58
+        private Vector2 _scorllView = Vector2.zero;
59
+
60
+#if UNITY_STANDALONE_WIN
61
+        private BuildTarget _buildTarget = BuildTarget.StandaloneWindows64;
62
+#elif UNITY_ANDROID
63
+        private BuildTarget _buildTarget = BuildTarget.Android;
64
+#elif UNITY_IPHON
65
+        private BuildTarget _buildTarget = BuildTarget.iOS;
66
+#endif
67
+
68
+        [MenuItem("TModule/热更/AssetBudleTool")]
69
+        public static void OpenWin()
70
+        {
71
+            AssetBudleTool win = GetWindow<AssetBudleTool>();
72
+            win.maxSize = new Vector2(800, 400);
73
+            win.titleContent.text = "ABTool";
74
+            win.titleContent.image = EditorGUIUtility.IconContent("PrefabModel Icon").image;
75
+            win.Show();
76
+        }
77
+
78
+        private void OnEnable()
79
+        {
80
+            var ms = MonoScript.FromScriptableObject(this);
81
+            var path = AssetDatabase.GetAssetPath(ms);
82
+            path = Path.GetDirectoryName(path);
83
+            AssetBudleConfigPath = Path.Combine(path, AssetBudleConfigName);
84
+            AssetbudleToolConfigPath = Path.Combine(path, AssetbudleToolConfigName);
85
+        }
86
+
87
+        private void OnGUI()
88
+        {
89
+            using (new EditorGUILayout.HorizontalScope("Box"))
90
+            {
91
+                EditorGUILayout.LabelField("打包平台:", GUILayout.Width(60));
92
+                _buildTarget = (BuildTarget)EditorGUILayout.EnumPopup(_buildTarget);
93
+                EditorGUILayout.LabelField("输出路径:", GUILayout.Width(60));
94
+                BudleToolConfig.m_savePath = EditorGUILayout.TextField(BudleToolConfig.m_savePath);
95
+                if (GUILayout.Button(EditorGUIUtility.IconContent("Project@2x").image, GUILayout.Height(20), GUILayout.Width(40)))
96
+                {
97
+                    try
98
+                    {
99
+                        BudleToolConfig.m_savePath = EditorUtility.OpenFolderPanel("AB包输出路径", "Assets", "Assets");
100
+                    }
101
+                    catch (Exception e)
102
+                    {
103
+                        Debug.Log(e.Message);
104
+                    }
105
+                }
106
+            }
107
+            float _width = position.width / 6;
108
+            using (new EditorGUILayout.HorizontalScope("Box"))
109
+            {
110
+                EditorGUILayout.LabelField("包名", GUILayout.Width(_width * 4));
111
+                EditorGUILayout.LabelField("文件夹", GUILayout.Width(_width));
112
+                EditorGUILayout.LabelField("操作");
113
+            }
114
+
115
+            _scorllView = EditorGUILayout.BeginScrollView(_scorllView);
116
+            for (int i = 0; i < BudleConfig.m_AB.Count; i++)
117
+            {
118
+                using (new EditorGUILayout.HorizontalScope())
119
+                {
120
+                    BudleConfig.m_AB[i].m_name = EditorGUILayout.TextField(BudleConfig.m_AB[i].m_name, GUILayout.Width(_width * 4));
121
+                    BudleConfig.m_AB[i].m_isFolder = EditorGUILayout.Toggle(BudleConfig.m_AB[i].m_isFolder, GUILayout.Width(_width));
122
+                    if (GUILayout.Button(EditorGUIUtility.IconContent("TreeEditor.Trash").image, GUILayout.Height(20)))
123
+                    {
124
+                        BudleConfig.m_AB.Remove(BudleConfig.m_AB[i]);
125
+                    }
126
+                }
127
+            }
128
+            if (GUILayout.Button(EditorGUIUtility.IconContent("CreateAddNew@2x").image, GUILayout.Height(30), GUILayout.Width(30)))
129
+            {
130
+                BudleConfig.m_AB.Add(new AssetBundleEntity());
131
+            }
132
+            EditorGUILayout.EndScrollView();
133
+
134
+            if (GUILayoutUtility.GetLastRect().Contains(Event.current.mousePosition))
135
+            {
136
+                if (Event.current.type == EventType.DragUpdated)
137
+                    DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
138
+                else if (Event.current.type == EventType.DragExited)
139
+                {
140
+                    if (DragAndDrop.paths != null && DragAndDrop.paths.Length > 0)
141
+                    {
142
+                        for (int i = 0; i < DragAndDrop.paths.Length; i++)
143
+                        {
144
+                            if (BudleConfig.m_AB.Find(p => p.m_name == DragAndDrop.paths[i]) == null)
145
+                                BudleConfig.m_AB.Add(new AssetBundleEntity()
146
+                                { m_name = DragAndDrop.paths[i] });
147
+                        }
148
+                    }
149
+                }
150
+            }
151
+
152
+            using (new EditorGUILayout.HorizontalScope())
153
+            {
154
+                EditorGUILayout.LabelField("将打包资源拷贝到StreamingAssets文件夹", GUILayout.Width(230));
155
+                BudleToolConfig.m_copyToStreamingAssets = EditorGUILayout.Toggle(BudleToolConfig.m_copyToStreamingAssets);
156
+            }
157
+
158
+            if (GUILayout.Button("构建AssetBundle"))
159
+            {
160
+                OnAssetBundleCallBack();
161
+            }
162
+        }
163
+
164
+        /// <summary>
165
+        /// 打包回调
166
+        /// </summary>
167
+        private void OnAssetBundleCallBack()
168
+        {
169
+            string tartgetPath = BudleToolConfig.m_savePath + "/" + _buildTarget.ToString();
170
+            if (!Directory.Exists(tartgetPath))
171
+                Directory.CreateDirectory(tartgetPath);
172
+            SetABName();
173
+            BuildPipeline.BuildAssetBundles(tartgetPath, BuildAssetBundleOptions.None, _buildTarget);
174
+
175
+            CompileDllCommand.CompileDll(_buildTarget);
176
+            CopyAOTAssembliesToPath(tartgetPath);
177
+            CopyHotUpdateAssembliesToPath(tartgetPath);
178
+            if (BudleToolConfig.m_copyToStreamingAssets)
179
+                CopyToStreamingAssets();
180
+
181
+            OnCreateVersionText();
182
+            AssetDatabase.Refresh();
183
+        }
184
+
185
+        private void SetABName()
186
+        {
187
+            for (int i = 0; i < BudleConfig.m_AB.Count; i++)
188
+            {
189
+                AssetBundleEntity assetBudle = BudleConfig.m_AB[i];
190
+                if (assetBudle.m_isFolder)
191
+                {
192
+                    string[] floderArr = new string[] {GetAbsolutePath(assetBudle.m_name) };
193
+                    SaveFolderSettings(floderArr);
194
+                }
195
+                else
196
+                {
197
+                    SaveFileSettings(Application.dataPath + "/" + assetBudle.m_name);
198
+                }
199
+            }
200
+
201
+        }
202
+
203
+        private void SaveFolderSettings(string[] folderArr)
204
+        {
205
+            foreach (string floderPath in folderArr)
206
+            {
207
+                string[] arrFile = Directory.GetFiles(floderPath);
208
+
209
+                foreach (string filePah in arrFile)
210
+                {
211
+                    SaveFileSettings(filePah);
212
+                }
213
+
214
+                string[] arrFolder = Directory.GetDirectories(floderPath);
215
+                SaveFolderSettings(arrFolder);
216
+            }
217
+        }
218
+
219
+        private void SaveFileSettings(string filePath)
220
+        {
221
+            FileInfo file = new FileInfo(filePath);
222
+            if (!file.Extension.Equals(".meta", StringComparison.CurrentCultureIgnoreCase))
223
+            {
224
+                int index = filePath.IndexOf("Assets/", StringComparison.CurrentCultureIgnoreCase);
225
+
226
+                string newPath = filePath.Substring(index);
227
+
228
+                string fileName = newPath.Substring(newPath.IndexOf("Assets/")).Replace(file.Extension, "");
229
+
230
+                string variant = file.Extension.Equals(".unity", StringComparison.CurrentCultureIgnoreCase) ? "unity3d" : "assetbundle";
231
+
232
+                AssetImporter importer = AssetImporter.GetAtPath(newPath);
233
+                if(importer.assetBundleName=="None" || string.IsNullOrEmpty(importer.assetBundleName))
234
+                {
235
+                    importer.SetAssetBundleNameAndVariant(fileName, variant);
236
+                    importer.SaveAndReimport();
237
+                }
238
+            }
239
+        }
240
+
241
+        private void CopyToStreamingAssets()
242
+        {
243
+            string toPath = Application.streamingAssetsPath;
244
+            string tartgetPath = BudleToolConfig.m_savePath + "/" + _buildTarget.ToString();
245
+            CopyDirectory(tartgetPath, toPath,true);
246
+            AssetDatabase.Refresh();
247
+        }
248
+
249
+        /// <summary>
250
+        /// 创建版本文件
251
+        /// </summary>
252
+        private void OnCreateVersionText()
253
+        {
254
+            string strVersionJsonPath = $"{BudleToolConfig.m_savePath}/{_buildTarget.ToString()}/VersionFile.json";
255
+            versionDatas.Clear();
256
+            if (File.Exists(strVersionJsonPath))
257
+                File.Delete(strVersionJsonPath);
258
+
259
+            StringBuilder sbContent = new StringBuilder();
260
+
261
+            DirectoryInfo directory = new DirectoryInfo(BudleToolConfig.m_savePath);
262
+            FileInfo[] arrfiles = directory.GetFiles("*", SearchOption.AllDirectories);
263
+            for (int i = 0; i < arrfiles.Length; i++)
264
+            {
265
+                FileInfo file = arrfiles[i];
266
+
267
+                string fullName = file.FullName;
268
+                string md5 = GetFileMD5(fullName);
269
+                if (md5 == null) continue;
270
+
271
+                string subPath = $"{BudleToolConfig.m_savePath}/{_buildTarget.ToString()}";
272
+                versionDatas.Add(new VersionDataEntity()
273
+                {
274
+                    m_fullName = fullName.Substring(fullName.IndexOf(subPath) + subPath.Length + 2),
275
+                    m_md5 = md5,
276
+                    m_size = Mathf.Ceil(file.Length / 1024.0f)
277
+                });
278
+            }
279
+
280
+            WriteJsonData(versionDatas, strVersionJsonPath);
281
+        }
282
+
283
+        private void OnDisable()
284
+        {
285
+            EditorUtility.SetDirty(_list);
286
+            EditorUtility.SetDirty(BudleToolConfig);
287
+            AssetDatabase.SaveAssets();
288
+        }
289
+
290
+        public string GetAbsolutePath(string path)
291
+        {
292
+            string _path = Application.dataPath.Remove(Application.dataPath.LastIndexOf('/') + 1);
293
+            return _path + path;
294
+        }
295
+
296
+        /// <summary>
297
+        /// 拷贝文件夹
298
+        /// </summary>
299
+        /// <param name="sourceDirName"></param>
300
+        /// <param name="destDirName"></param>
301
+        /// <param name="isOverlay">是否覆盖已有同名文件</param>
302
+        public void CopyDirectory(string sourceDirName, string destDirName, bool isOverlay = false)
303
+        {
304
+            try
305
+            {
306
+                if (!Directory.Exists(destDirName))
307
+                {
308
+                    Directory.CreateDirectory(destDirName);
309
+                    File.SetAttributes(destDirName, File.GetAttributes(sourceDirName));
310
+                }
311
+
312
+                if (destDirName[destDirName.Length - 1] != Path.DirectorySeparatorChar)
313
+                    destDirName = destDirName + Path.DirectorySeparatorChar;
314
+
315
+                string[] files = Directory.GetFiles(sourceDirName);
316
+                foreach (string file in files)
317
+                {
318
+                    if (File.Exists(destDirName + Path.GetFileName(file)) && !isOverlay)
319
+                        continue;
320
+                    FileInfo fileInfo = new FileInfo(file);
321
+                    if (fileInfo.Extension.Equals(".meta", StringComparison.CurrentCultureIgnoreCase))
322
+                        continue;
323
+
324
+                    File.Copy(file, destDirName + Path.GetFileName(file), true);
325
+                    File.SetAttributes(destDirName + Path.GetFileName(file), FileAttributes.Normal);
326
+                }
327
+
328
+                string[] dirs = Directory.GetDirectories(sourceDirName);
329
+                foreach (string dir in dirs)
330
+                {
331
+                    CopyDirectory(dir, destDirName + Path.GetFileName(dir), isOverlay);
332
+                }
333
+            }
334
+            catch (Exception ex)
335
+            {
336
+                throw ex;
337
+            }
338
+        }
339
+
340
+        /// <summary>
341
+        /// 写入数据
342
+        /// </summary>
343
+        /// <param name="data">数据</param>
344
+        /// <param name="path">文件完整路径</param>
345
+        public static void WriteJsonData(object data, string path)
346
+        {
347
+            string json = JsonConvert.SerializeObject(data);
348
+            using (StreamWriter sw = new StreamWriter(path))
349
+            {
350
+                //保存数据
351
+                sw.WriteLine(json);
352
+                //关闭文档
353
+                sw.Close();
354
+                sw.Dispose();
355
+            }
356
+        }
357
+
358
+        /// <summary>
359
+        /// 获取文件MD5
360
+        /// </summary>
361
+        /// <param name="filePath"></param>
362
+        /// <returns></returns>
363
+        public static string GetFileMD5(string filePath)
364
+        {
365
+            if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
366
+                return null;
367
+
368
+            try
369
+            {
370
+                FileStream file = new FileStream(filePath, FileMode.Open);
371
+                MD5 mD5 = new MD5CryptoServiceProvider();
372
+                byte[] byteResult = mD5.ComputeHash(file);
373
+                string strResult = BitConverter.ToString(byteResult);
374
+                strResult = strResult.Replace("-", "");
375
+                return strResult;
376
+            }
377
+            catch
378
+            {
379
+                return null;
380
+            }
381
+        }
382
+
383
+        public void CopyAOTAssembliesToPath(string path)
384
+        {
385
+            string aotAssembliesSrcDir = SettingsUtil.GetAssembliesPostIl2CppStripDir(_buildTarget);
386
+            List<string> aotMetaAssemblyFiles = Resources.Load<HotfixConfig>("HotfixUpdate/热更配置表").m_hotfixUpdateAotDlls;
387
+            foreach (var dll in aotMetaAssemblyFiles)
388
+            {
389
+                string srcDllPath = $"{aotAssembliesSrcDir}/{dll}";
390
+                if (!File.Exists(srcDllPath))
391
+                {
392
+                    Log.Error($"ab中添加AOT补充元数据dll:{srcDllPath} 时发生错误,文件不存在。裁剪后的AOT dll在BuildPlayer时才能生成,因此需要你先构建一次游戏App后再打包。");
393
+                    continue;
394
+                }
395
+                string dllBytesPath = $"{path}/{dll}.bytes";
396
+                File.Copy(srcDllPath, dllBytesPath, true);
397
+                Log.Info($"【CopyAOTAssemblies】 copy AOT dll {srcDllPath} -> {dllBytesPath}");
398
+            }
399
+        }
400
+
401
+        public static void CopyHotUpdateAssembliesToPath(string path)
402
+        {
403
+            var target = EditorUserBuildSettings.activeBuildTarget;
404
+
405
+            string hotfixDllSrcDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target);
406
+            string hotfixAssembliesDstDir = Application.streamingAssetsPath;
407
+
408
+            foreach (var dll in SettingsUtil.HotUpdateAssemblyFilesExcludePreserved)
409
+            {
410
+                string dllPath = $"{hotfixDllSrcDir}/{dll}";
411
+                string dllBytesPath = $"{path}/{dll}.bytes";
412
+                File.Copy(dllPath, dllBytesPath, true);
413
+                Log.Info($"【CopyHotUpdateAssemblies】 copy hotfix dll {dllPath} -> {dllBytesPath}");
414
+            }
415
+            AssetDatabase.Refresh();
416
+        }
417
+    }
418
+}

+ 11 - 0
Editor/AssetBudleTool.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: 43731dda4c05c934e8c230876a8c428d
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 15 - 0
Editor/AssetBudleToolConfig.cs

@@ -0,0 +1,15 @@
1
+using System.Collections;
2
+using System.Collections.Generic;
3
+using UnityEngine;
4
+using System;
5
+
6
+namespace TModule.Editor
7
+{
8
+    [Serializable]
9
+    public class AssetBudleToolConfig : ScriptableObject
10
+    {
11
+        public string m_savePath;
12
+
13
+        public bool m_copyToStreamingAssets;
14
+    }
15
+}

+ 11 - 0
Editor/AssetBudleToolConfig.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: 026905a26e66dda4ea85e5b8fbd10bc8
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 63 - 0
Editor/AssetBundleConfig.asset

@@ -0,0 +1,63 @@
1
+%YAML 1.1
2
+%TAG !u! tag:unity3d.com,2011:
3
+--- !u!114 &11400000
4
+MonoBehaviour:
5
+  m_ObjectHideFlags: 0
6
+  m_CorrespondingSourceObject: {fileID: 0}
7
+  m_PrefabInstance: {fileID: 0}
8
+  m_PrefabAsset: {fileID: 0}
9
+  m_GameObject: {fileID: 0}
10
+  m_Enabled: 1
11
+  m_EditorHideFlags: 0
12
+  m_Script: {fileID: 11500000, guid: a2d4bb0157c733647aa12789ecab546e, type: 3}
13
+  m_Name: AssetBundleConfig
14
+  m_EditorClassIdentifier: 
15
+  m_AB:
16
+  - m_key: 
17
+    m_name: Assets/Code/Env1/HotUpdateResources/Audios
18
+    m_tag: 
19
+    m_version: 0
20
+    m_size: 0
21
+    m_toPath: 
22
+    m_isFolder: 1
23
+    m_isFirstData: 0
24
+  - m_key: 
25
+    m_name: Assets/Code/Env1/HotUpdateResources/Prefabs
26
+    m_tag: 
27
+    m_version: 0
28
+    m_size: 0
29
+    m_toPath: 
30
+    m_isFolder: 1
31
+    m_isFirstData: 0
32
+  - m_key: 
33
+    m_name: Assets/Code/Env1/HotUpdateResources/Resources
34
+    m_tag: 
35
+    m_version: 0
36
+    m_size: 0
37
+    m_toPath: 
38
+    m_isFolder: 1
39
+    m_isFirstData: 0
40
+  - m_key: 
41
+    m_name: Assets/Code/Env1/HotUpdateResources/Sprites
42
+    m_tag: 
43
+    m_version: 0
44
+    m_size: 0
45
+    m_toPath: 
46
+    m_isFolder: 1
47
+    m_isFirstData: 0
48
+  - m_key: 
49
+    m_name: Assets/Code/Env1/HotUpdateResources/Scenes
50
+    m_tag: 
51
+    m_version: 0
52
+    m_size: 0
53
+    m_toPath: 
54
+    m_isFolder: 1
55
+    m_isFirstData: 0
56
+  - m_key: 
57
+    m_name: Assets/Code/Env1/HotUpdateResources/Fonts
58
+    m_tag: 
59
+    m_version: 0
60
+    m_size: 0
61
+    m_toPath: 
62
+    m_isFolder: 1
63
+    m_isFirstData: 0

+ 8 - 0
Editor/AssetBundleConfig.asset.meta

@@ -0,0 +1,8 @@
1
+fileFormatVersion: 2
2
+guid: b5192e0885e6c944cb70dcf5a396c58f
3
+NativeFormatImporter:
4
+  externalObjects: {}
5
+  mainObjectFileID: 11400000
6
+  userData: 
7
+  assetBundleName: 
8
+  assetBundleVariant: 

+ 16 - 0
Editor/BudleToolConfig.asset

@@ -0,0 +1,16 @@
1
+%YAML 1.1
2
+%TAG !u! tag:unity3d.com,2011:
3
+--- !u!114 &11400000
4
+MonoBehaviour:
5
+  m_ObjectHideFlags: 0
6
+  m_CorrespondingSourceObject: {fileID: 0}
7
+  m_PrefabInstance: {fileID: 0}
8
+  m_PrefabAsset: {fileID: 0}
9
+  m_GameObject: {fileID: 0}
10
+  m_Enabled: 1
11
+  m_EditorHideFlags: 0
12
+  m_Script: {fileID: 11500000, guid: 026905a26e66dda4ea85e5b8fbd10bc8, type: 3}
13
+  m_Name: BudleToolConfig
14
+  m_EditorClassIdentifier: 
15
+  m_savePath: E:/Project/Unity/qian_yong_mahjong_client/DLC
16
+  m_copyToStreamingAssets: 1

+ 8 - 0
Editor/BudleToolConfig.asset.meta

@@ -0,0 +1,8 @@
1
+fileFormatVersion: 2
2
+guid: c3f55918776855743bf3148f2267d819
3
+NativeFormatImporter:
4
+  externalObjects: {}
5
+  mainObjectFileID: 11400000
6
+  userData: 
7
+  assetBundleName: 
8
+  assetBundleVariant: 

+ 50 - 0
Editor/HotfixManagerInsoector.cs

@@ -0,0 +1,50 @@
1
+using System;
2
+using System.Collections.Generic;
3
+using System.IO;
4
+using TFramework;
5
+using TModule.Runtime;
6
+using UnityEditor;
7
+using UnityEngine;
8
+
9
+namespace Tmodule.Editor
10
+{
11
+    [CustomEditor(typeof(HotfixManager))]
12
+    public class HotfixManagerInsoector : UnityEditor.Editor
13
+    {
14
+        private SerializedProperty isHotfix;
15
+        private GUIContent isHotfixGC = new GUIContent("启用热更");
16
+
17
+        private SerializedProperty resServerUrl;
18
+        private GUIContent resServerUrlGC = new GUIContent("热更服务器地址");
19
+
20
+        private SerializedProperty loadMode;
21
+        private GUIContent loadModeGC = new GUIContent("加载模式", "Editor:编辑器模式(不加载AB包)\n Loacl:本地热更模式(加载StreamingAssets下AB包)\n Bulid:打包热更模式(从服务器下载并加载AB包)\n 打包版本在启用热更情况下强制Build模式");
22
+
23
+        private SerializedProperty hotfixDllName;
24
+        private GUIContent hotfixDllNameGC = new GUIContent("热更程序集");
25
+
26
+        private SerializedProperty hotfixClass;
27
+        private GUIContent hotfixClassGC = new GUIContent("热更启动类","注意!应填入类的完全限定名(带命名空间)");
28
+
29
+        private void OnEnable()
30
+        {
31
+            isHotfix = serializedObject.FindProperty("isHotfix");
32
+            resServerUrl = serializedObject.FindProperty("resServerUrl");
33
+            loadMode = serializedObject.FindProperty("loadMode");
34
+            hotfixDllName = serializedObject.FindProperty("hotfixDllName");
35
+            hotfixClass = serializedObject.FindProperty("hotfixClass");
36
+        }
37
+
38
+        public override void OnInspectorGUI()
39
+        {
40
+            //base.OnInspectorGUI();
41
+            serializedObject.Update();
42
+            EditorGUILayout.PropertyField(isHotfix, isHotfixGC);
43
+            EditorGUILayout.PropertyField(resServerUrl, resServerUrlGC);
44
+            EditorGUILayout.PropertyField(loadMode, loadModeGC);
45
+            EditorGUILayout.PropertyField(hotfixDllName, hotfixDllNameGC);
46
+            EditorGUILayout.PropertyField(hotfixClass, hotfixClassGC);
47
+            serializedObject.ApplyModifiedProperties();
48
+        }
49
+    }
50
+}

+ 11 - 0
Editor/HotfixManagerInsoector.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: 1031a9b15e0ff9a4fbd39a2d1aa993ad
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 1 - 1
Editor/Prepare.cs

@@ -9,7 +9,7 @@ using UnityEditor.PackageManager.Requests;
9 9
 public class Prepare
10 10
 {
11 11
     private const string PACKAGENAME = "com.code-philosophy.hybridclr";
12
-    private const string PACKAGEGITPATH = "https://github.com/focus-creative-games/hybridclr_unity.git";
12
+    private const string PACKAGEGITPATH = "https://gitee.com/focus-creative-games/hybridclr_unity.git";
13 13
     static ListRequest _listRequest;
14 14
 
15 15
     [InitializeOnLoadMethod]

+ 0 - 236
HotfixManager.cs

@@ -1,236 +0,0 @@
1
-using TFramework;
2
-using System.Collections;
3
-using System.Collections.Generic;
4
-using UnityEngine;
5
-using System;
6
-using UnityEngine.Networking;
7
-#if HybridCLR
8
-using HybridCLR;
9
-#endif
10
-
11
-namespace TModule.Runtime {
12
-    public class HotfixManager : MonoBehaviour
13
-    {
14
-        public bool m_isEnableHotfix = true;
15
-
16
-        public string m_resServerUrl;
17
-
18
-        public string m_updateHandle;
19
-
20
-        public string m_versionFileName;
21
-
22
-        /// <summary>
23
-        ///本地资源存放路径
24
-        /// </summary>
25
-        public string LocalResFilePath { get; private set; }
26
-
27
-        /// <summary>
28
-        /// 本地版本文件存放路径
29
-        /// </summary>
30
-        private string _loaclVersionPath;
31
-
32
-        /// <summary>
33
-        /// 待下载资源列表
34
-        /// </summary>
35
-        private List<VersionDataEntity> _NeedDownloadResList = new List<VersionDataEntity>();
36
-
37
-        /// <summary>
38
-        /// 本地资源列表
39
-        /// </summary>
40
-        private List<VersionDataEntity> _LocalResList = new List<VersionDataEntity>();
41
-
42
-        /// <summary>
43
-        /// 检查版本文件进行事件
44
-        /// </summary>
45
-        private Action<float> OnCheckVersion;
46
-        /// <summary>
47
-        /// 版本文件检查完成事件
48
-        /// </summary>
49
-        private Action<float> OnCheckVersionOver;
50
-
51
-        /// <summary>
52
-        /// 版本文件检查出错事件
53
-        /// </summary>
54
-        private Action<string> OnCheckVersionError;
55
-        /// <summary>
56
-        /// 检查进度
57
-        /// </summary>
58
-        private float _checkProgress;
59
-
60
-        /// <summary>
61
-        /// 超时时间
62
-        /// </summary>
63
-        public const int DOWNLOADTIMEOUT = 5;
64
-
65
-        public float TotalSize
66
-        {
67
-            get
68
-            {
69
-                float szie = 0;
70
-                _NeedDownloadResList.ForEach(p => szie += (float)p.m_size);
71
-                return szie;
72
-            }
73
-        }
74
-
75
-
76
-        void Start()
77
-        {
78
-            if (m_isEnableHotfix)
79
-            {
80
-
81
-            }
82
-        }
83
-
84
-        /// <summary>
85
-        /// 检查版本文件
86
-        /// </summary>
87
-        public void InitCheckVersion(Action<float> checkVersion, Action<float> checkVersionOver, Action<string> checkVersionError = null)
88
-        {
89
-            if (string.IsNullOrEmpty(AssetConfig.ResUrl))
90
-            {
91
-                Log.Error("未指定资源服务器地址");
92
-                return;
93
-            }
94
-            OnCheckVersion = checkVersion;
95
-            OnCheckVersionOver = checkVersionOver;
96
-            OnCheckVersionError = checkVersionError;
97
-            _checkProgress = 0;
98
-            string strVersionPath = AssetConfig.ResUrl + m_versionFileName;
99
-            StartCoroutine(DowloadVersion(strVersionPath));
100
-        }
101
-
102
-        /// <summary>
103
-        /// 对比版本文件
104
-        /// </summary>
105
-        /// <param name="arg0"></param>
106
-        private void OnInitVersionCallBack(List<VersionDataEntity> arg0)
107
-        {
108
-            _loaclVersionPath = LocalResFilePath + m_versionFileName;
109
-
110
-            if (FileOperations.FileExists(_loaclVersionPath))
111
-            {
112
-                List<VersionDataEntity> clienData = FileOperations.ReadJsonData<List<VersionDataEntity>>(_loaclVersionPath);
113
-
114
-                foreach (var item in arg0)
115
-                {
116
-                    _checkProgress += 1.0f / arg0.Count / 2;
117
-                    _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
118
-                    OnCheckVersion?.Invoke(_checkProgress);
119
-                    VersionDataEntity dataEntity = clienData.Find(p => p.m_fullName == item.m_fullName);
120
-                    //已有资源
121
-                    if (dataEntity != null)
122
-                    {
123
-                        //对比MD5
124
-                        if (dataEntity.m_md5 != item.m_md5)
125
-                            _NeedDownloadResList.Add(item);
126
-                    }
127
-                    else
128
-                        _NeedDownloadResList.Add(item);//新资源
129
-                }
130
-            }
131
-            else
132
-                arg0.ForEach(p =>
133
-                {
134
-                    _NeedDownloadResList.Add(p);
135
-                    _checkProgress += 1.0f / arg0.Count / 2;
136
-                    _checkProgress = _checkProgress >= 1 ? 1 : _checkProgress;
137
-                    OnCheckVersion?.Invoke(_checkProgress);
138
-                });
139
-
140
-            if (_NeedDownloadResList.Count > 0)
141
-                OnCheckVersionOver?.Invoke(TotalSize);
142
-            else
143
-                OnCheckVersionOver?.Invoke(0);
144
-        }
145
-
146
-        /// <summary>
147
-        /// 下载版本文件
148
-        /// </summary>
149
-        /// <param name="url"></param>
150
-        /// <param name="OnDownLoadOver"></param>
151
-        /// <returns></returns>
152
-        private IEnumerator DowloadVersion(string url)
153
-        {
154
-            UnityWebRequest www = UnityWebRequest.Get(url);
155
-
156
-            www.timeout = DOWNLOADTIMEOUT;
157
-            yield return www.SendWebRequest();
158
-            while (!www.isDone)
159
-            {
160
-                _checkProgress = www.downloadProgress / 2;
161
-                OnCheckVersion?.Invoke(_checkProgress);
162
-            }
163
-
164
-            if (www.result != UnityWebRequest.Result.ProtocolError && www.result != UnityWebRequest.Result.ConnectionError)
165
-            {
166
-                string content = www.downloadHandler.text;
167
-                _checkProgress = 0.5f;
168
-                OnInitVersionCallBack(content.JsonStrToObject<List<VersionDataEntity>>());
169
-                Debug.Log(content);
170
-            }
171
-            else
172
-            {
173
-                Debug.Log("下载失败:" + www.error);
174
-                OnCheckVersionError?.Invoke(www.error);
175
-            }
176
-        }
177
-
178
-        /// <summary>
179
-        /// 保存版本文件
180
-        /// </summary>
181
-        private void SaveLoaclVersion() => FileOperations.WriteJsonData(_LocalResList, _loaclVersionPath);
182
-
183
-        /// <summary>
184
-        /// 更新版本文件
185
-        /// </summary>
186
-        /// <param name="entity"></param>
187
-        public void ModifyLocaData(VersionDataEntity entity)
188
-        {
189
-            if (_LocalResList == null) return;
190
-            bool isExists = false;
191
-            for (int i = 0; i < _LocalResList.Count; i++)
192
-            {
193
-                if (_LocalResList[i].m_fullName.Equals(entity.m_fullName, StringComparison.CurrentCultureIgnoreCase))
194
-                {
195
-                    _LocalResList[i].m_md5 = entity.m_md5;
196
-                    _LocalResList[i].m_fullName = entity.m_fullName;
197
-                    _LocalResList[i].m_size = entity.m_size;
198
-                    isExists = true;
199
-                    break;
200
-                }
201
-            }
202
-
203
-            if (!isExists)
204
-            {
205
-                _LocalResList.Add(entity);
206
-            }
207
-            SaveLoaclVersion();
208
-        }
209
-
210
-        private static void LoadMetadataForAOTAssemblies()
211
-        {
212
-            List<string> aotMetaAssemblyFiles = new List<string>()
213
-        {
214
-            "mscorlib.dll",
215
-            "System.dll",
216
-            "System.Core.dll",
217
-        };
218
-            /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
219
-            /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
220
-            /// 
221
-#if HybridCLR
222
-            HomologousImageMode mode = HomologousImageMode.SuperSet;
223
-            foreach (var aotDllName in aotMetaAssemblyFiles)
224
-            {
225
-                byte[] dllBytes = FileOperations.SafeReadAllBytes(AssetConfig.AssetBundleRootPath + aotDllName + ".bytes");
226
-                // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
227
-                LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
228
-                Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
229
-            }
230
-#else
231
-            Log.Error("当前非热更环境!请先安装HybridCLR");
232
-#endif
233
-        }
234
-    }
235
-}
236
-

+ 8 - 0
Runtime.meta

@@ -0,0 +1,8 @@
1
+fileFormatVersion: 2
2
+guid: b81b2fc96a685a84b8bc8c359dfd5817
3
+folderAsset: yes
4
+DefaultImporter:
5
+  externalObjects: {}
6
+  userData: 
7
+  assetBundleName: 
8
+  assetBundleVariant: 

+ 11 - 0
Runtime/HotfixConfig.cs

@@ -0,0 +1,11 @@
1
+using System.Collections.Generic;
2
+using TFramework;
3
+using UnityEngine;
4
+namespace TModule.Runtime
5
+{
6
+    [CreateAssetMenu(fileName = "ÈȸüÅäÖñí", menuName = "TModule/ÈȸüÅäÖñí")]
7
+    public class HotfixConfig : UnityEngine.ScriptableObject
8
+    {
9
+        public List<string> m_hotfixUpdateAotDlls;
10
+    }
11
+}

+ 11 - 0
Runtime/HotfixConfig.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: fa97b141ea0b89b49abc978ed7934735
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 388 - 0
Runtime/HotfixManager.cs

@@ -0,0 +1,388 @@
1
+using TFramework;
2
+using System.Collections;
3
+using System.Collections.Generic;
4
+using UnityEngine;
5
+using System;
6
+using UnityEngine.Networking;
7
+using Cysharp.Threading.Tasks;
8
+using Newtonsoft.Json.Serialization;
9
+using System.Threading.Tasks;
10
+using System.IO;
11
+using System.Reflection;
12
+
13
+
14
+
15
+#if HybridCLR
16
+using HybridCLR;
17
+#endif
18
+
19
+namespace TModule.Runtime {
20
+    public class HotfixManager : BaseManager
21
+    {
22
+        /// <summary>
23
+        /// 是否开启热更
24
+        /// </summary>
25
+        [SerializeField] bool isHotfix;
26
+        /// <summary>
27
+        /// 资源下载地址
28
+        /// </summary>
29
+        [SerializeField] string resServerUrl;
30
+
31
+        /// <summary>
32
+        /// 资源加载模式
33
+        /// </summary>
34
+        [SerializeField] UpdateMode loadMode;
35
+
36
+        /// <summary>
37
+        /// 热更新dll名称
38
+        /// </summary>
39
+        [SerializeField] string hotfixDllName;
40
+
41
+        /// <summary>
42
+        /// 热更新启动类
43
+        /// </summary>
44
+        [SerializeField] string hotfixClass;
45
+
46
+
47
+        /// <summary>
48
+        /// 超时时间
49
+        /// </summary>
50
+        private const int DOWNLOADTIMEOUT = 5;
51
+
52
+        /// <summary>
53
+        /// 获取平台
54
+        /// </summary>
55
+        public string GetPlatform
56
+        {
57
+            get
58
+            {
59
+#if UNITY_ANDROID
60
+                return "Android";
61
+#elif UNITY_IOS
62
+                return "IOS";
63
+#elif UNITY_STANDALONE_OSX
64
+                return "Mac";
65
+#elif UNITY_STANDALONE_WIN
66
+                return "Windows";
67
+#elif UNITY_WEBGL
68
+                return "WebGL";
69
+#else
70
+                return Application.platform.ToString();
71
+#endif
72
+            }
73
+        }
74
+
75
+        public long TotalSize
76
+        {
77
+            get
78
+            {
79
+                long szie = 0;
80
+                _NeedDownloadResList.ForEach(p => szie += (long)p.m_size);
81
+                return szie;
82
+            }
83
+        }
84
+
85
+        TAction<float,long,long> onCheckUpdataEvent;
86
+        TAction<TAction, long> onCheckOverEvent;
87
+        TAction<string> onErrorEvent;
88
+
89
+        /// <summary>
90
+        /// 检查进度
91
+        /// </summary>
92
+        private float _checkProgress;
93
+        private string _loaclVersionPath;
94
+        private List<VersionDataEntity> _NeedDownloadResList = new List<VersionDataEntity>();
95
+        private List<VersionDataEntity> _LocalResList = new List<VersionDataEntity>();
96
+
97
+        private long completeTotalCount = 0;
98
+        /// <summary>
99
+        ///本地资源存放路径
100
+        /// </summary>
101
+        string localResFilePath;
102
+        /// <summary>
103
+        /// 版本文件名称
104
+        /// </summary>
105
+        string versionFileName = "VersionFile.json";
106
+
107
+        /// <summary>
108
+        /// 热更入口
109
+        /// </summary>
110
+        /// <param name="onCheckUpdata">更新事件 return 返回检查更新进度(仅进度)/更新进度(进度,总大小,已下载大小)</param>
111
+        /// <param name="onCheckOver">检查完成 return 是否有更新 更新资源大小</param>
112
+        /// <param name="onError">热更出错事件</param>
113
+        public void Startup(TAction<float,long,long> onUpdata, TAction<TAction, long> onCheckOver,TAction<string> onError=null)
114
+        {
115
+            localResFilePath = Application.persistentDataPath + "/";
116
+            resServerUrl += '/' + GetPlatform;
117
+            AssetConfig.AssetBundleManifestName = GetPlatform;
118
+#if !UNITY_EDITOR
119
+            AssetConfig.RuntimeMode = UpdateMode.Bulid;
120
+#else
121
+            AssetConfig.RuntimeMode = loadMode;
122
+#endif
123
+            onCheckUpdataEvent = onUpdata;
124
+            onCheckOverEvent = onCheckOver;
125
+            onErrorEvent = onError;
126
+            
127
+            if (isHotfix)
128
+            {
129
+                if (AssetConfig.RuntimeMode == UpdateMode.Bulid)
130
+                {
131
+                    CheckUpdata();
132
+                }
133
+                else
134
+                {
135
+                    EnterAPP();
136
+                    onCheckOverEvent(null, 0);
137
+                }
138
+            }
139
+            else
140
+            {
141
+                EnterAPP();
142
+                onCheckOverEvent(null, 0);
143
+            }
144
+        }
145
+
146
+        /// <summary>
147
+        /// 检查更新
148
+        /// </summary>
149
+        private async UniTask CheckUpdata()
150
+        {
151
+            if (string.IsNullOrEmpty(resServerUrl))
152
+            {
153
+                Log.Error("未指定资源服务器地址");
154
+                return;
155
+            }
156
+            string strVersionPath = resServerUrl + "/" + versionFileName;
157
+            List<VersionDataEntity> versionDatas = await DowloadVersion(strVersionPath);
158
+            if(versionDatas!=null)
159
+            {
160
+                OnInitVersionCallBack(versionDatas);
161
+                if (_NeedDownloadResList.Count > 0)
162
+                {
163
+                    onCheckOverEvent?.Invoke(DownloadFiles, TotalSize);
164
+                }
165
+                else
166
+                {
167
+                    EnterAPP();
168
+                    onCheckOverEvent?.Invoke(null, 0);
169
+                }
170
+            }
171
+        }
172
+
173
+        public async void EnterAPP()
174
+        {
175
+            await UniTask.DelayFrame(2);
176
+            Assembly Csharp;
177
+            if(string.IsNullOrEmpty(hotfixDllName))
178
+            {
179
+                Log.Error("开启了热更新,但未指定热更程序集,程序终止");
180
+#if UNITY_EDITOR
181
+                UnityEditor.EditorApplication.isPlaying = false;
182
+#endif
183
+                return;
184
+            }
185
+            if(loadMode==UpdateMode.Editor||!isHotfix)
186
+            {
187
+                Csharp = AppDomain.CurrentDomain.Load(hotfixDllName);
188
+            }
189
+            else
190
+            {
191
+                LoadMetadataForAOTAssemblies();
192
+                byte[] assemblyDll = FileOperations.SafeReadAllBytes(AssetConfig.AssetBundleRootPath + $"{hotfixDllName}.dll.bytes");
193
+                if (assemblyDll == null)
194
+                {
195
+                    Log.Error($"未加载到{hotfixDllName}.dll.bytes文件,请检查文件是否存在");
196
+                    return;
197
+                }
198
+
199
+                Csharp = Assembly.Load(assemblyDll);
200
+            }
201
+            if (string.IsNullOrEmpty(hotfixClass))
202
+            {
203
+                Log.Error("开启了热更新,但未指定热更入口程序");
204
+                return;
205
+            }
206
+            object hclass = Csharp.CreateInstance(hotfixClass);
207
+            if (hclass != null && (hclass as IHotfixStartup) != null)
208
+            {
209
+                (hclass as IHotfixStartup).Run();
210
+            }
211
+            else
212
+                Log.Error($"热更入口程序{hotfixClass}未找到,{hotfixClass}需要继承自IHotfixStartup,请检查脚本");
213
+        }
214
+
215
+        /// <summary>
216
+        /// 为aot assembly加载原始metadata, 这个代码放aot或者热更新都行。
217
+        /// 一旦加载后,如果AOT泛型函数对应native实现不存在,则自动替换为解释模式执行
218
+        /// </summary>
219
+        private static void LoadMetadataForAOTAssemblies()
220
+        {
221
+            List<string> aotMetaAssemblyFiles = Resources.Load<HotfixConfig>("HotfixUpdate/热更配置表").m_hotfixUpdateAotDlls; 
222
+            /// 注意,补充元数据是给AOT dll补充元数据,而不是给热更新dll补充元数据。
223
+            /// 热更新dll不缺元数据,不需要补充,如果调用LoadMetadataForAOTAssembly会返回错误
224
+            /// 
225
+#if HybridCLR
226
+            HomologousImageMode mode = HomologousImageMode.SuperSet;
227
+            foreach (var aotDllName in aotMetaAssemblyFiles)
228
+            {
229
+                byte[] dllBytes = FileOperations.SafeReadAllBytes(AssetConfig.AssetBundleRootPath + aotDllName + ".bytes");
230
+                Debug.Log($"加载{aotDllName}******{dllBytes}");
231
+               // 加载assembly对应的dll,会自动为它hook。一旦aot泛型函数的native函数不存在,用解释器版本代码
232
+               LoadImageErrorCode err = RuntimeApi.LoadMetadataForAOTAssembly(dllBytes, mode);
233
+                if (err != LoadImageErrorCode.OK)
234
+                {
235
+                    Debug.LogError($"Error occurs when loadMetadataForAOT: {err}");
236
+                }
237
+                else
238
+                    Debug.Log($"LoadMetadataForAOTAssembly:{aotDllName}. mode:{mode} ret:{err}");
239
+            }
240
+#else
241
+            Log.Error("当前非热更环境!请先安装HybridCLR");
242
+#endif
243
+        }
244
+
245
+
246
+        /// <summary>
247
+        /// 下载版本文件
248
+        /// </summary>
249
+        /// <param name="url"></param>
250
+        /// <param name="OnDownLoadOver"></param>
251
+        /// <returns></returns>
252
+        private async UniTask<List<VersionDataEntity>> DowloadVersion(string url)
253
+        {
254
+            UnityWebRequest www = UnityWebRequest.Get(url);
255
+            www.timeout = DOWNLOADTIMEOUT;
256
+
257
+            await www.SendWebRequest();
258
+            while (!www.isDone)
259
+            {
260
+                _checkProgress = www.downloadProgress / 2 * 0.1f;
261
+                onCheckUpdataEvent?.Invoke(_checkProgress,0,0);
262
+            }
263
+
264
+            if (www.result != UnityWebRequest.Result.ProtocolError && www.result != UnityWebRequest.Result.ConnectionError)
265
+            {
266
+                string content = www.downloadHandler.text;
267
+                return content.JsonStrToObject<List<VersionDataEntity>>();
268
+            }
269
+            else
270
+            {
271
+                Debug.Log("下载失败:" + www.error);
272
+                onErrorEvent?.Invoke(www.error);
273
+                return null;
274
+            }
275
+        }
276
+
277
+
278
+        /// <summary>
279
+        /// 对比版本文件
280
+        /// </summary>
281
+        /// <param name="arg0"></param>
282
+        private void OnInitVersionCallBack(List<VersionDataEntity> arg0)
283
+        {
284
+            _loaclVersionPath = localResFilePath + versionFileName;
285
+
286
+            if (FileOperations.FileExists(_loaclVersionPath))
287
+            {
288
+                List<VersionDataEntity> clienData = FileOperations.ReadJsonData<List<VersionDataEntity>>(_loaclVersionPath);
289
+                _LocalResList.AddRange(clienData);
290
+                foreach (var item in arg0)
291
+                {
292
+                    var progress = 1.0f / arg0.Count * 0.9f + _checkProgress;
293
+
294
+                    progress = progress >= 1 ? 1 : progress;
295
+                    onCheckUpdataEvent?.Invoke(progress,0,0);
296
+                    VersionDataEntity dataEntity = clienData.Find(p => p.m_fullName == item.m_fullName);
297
+                    //已有资源
298
+                    if (dataEntity != null)
299
+                    {
300
+                        //对比MD5
301
+                        if (dataEntity.m_md5 != item.m_md5)
302
+                            _NeedDownloadResList.Add(item);
303
+                    }
304
+                    else
305
+                        _NeedDownloadResList.Add(item);//新资源
306
+                }
307
+            }
308
+            else
309
+                arg0.ForEach(p =>
310
+                {
311
+                    _NeedDownloadResList.Add(p);
312
+                    var progress = 1.0f / arg0.Count * 0.9f + _checkProgress;
313
+
314
+                    progress = progress >= 1 ? 1 : progress;
315
+                    onCheckUpdataEvent?.Invoke(progress,0,0);
316
+                });
317
+        }
318
+
319
+        /// <summary>
320
+        /// 更新版本文件
321
+        /// </summary>
322
+        /// <param name="entity"></param>
323
+        private void ModifyLocaData(VersionDataEntity entity)
324
+        {
325
+            if (_LocalResList == null) return;
326
+            bool isExists = false;
327
+            for (int i = 0; i < _LocalResList.Count; i++)
328
+            {
329
+                if (_LocalResList[i].m_fullName.Equals(entity.m_fullName, StringComparison.CurrentCultureIgnoreCase))
330
+                {
331
+                    _LocalResList[i].m_md5 = entity.m_md5;
332
+                    _LocalResList[i].m_fullName = entity.m_fullName;
333
+                    _LocalResList[i].m_size = entity.m_size;
334
+                    isExists = true;
335
+                    break;
336
+                }
337
+            }
338
+
339
+            if (!isExists)
340
+            {
341
+                _LocalResList.Add(entity);
342
+            }
343
+            SaveLoaclVersion();
344
+        }
345
+
346
+        /// <summary>
347
+        /// 保存版本文件
348
+        /// </summary>
349
+        private void SaveLoaclVersion() => FileOperations.WriteJsonData(_LocalResList, _loaclVersionPath);
350
+
351
+        /// <summary>
352
+        /// 分配下载任务并开始下载
353
+        /// </summary>
354
+        /// <param name="downloadList"></param>
355
+        private void DownloadFiles()
356
+        {
357
+            _checkProgress = 0;
358
+            foreach (var versionData in _NeedDownloadResList)
359
+            {
360
+                string savePath = Path.Combine(localResFilePath, versionData.m_fullName);
361
+                savePath = savePath.Replace("\\", "/");
362
+                string url = resServerUrl+"/"+ versionData.m_fullName;
363
+                if (File.Exists(savePath))
364
+                    File.Delete(savePath);
365
+                Main.GetMagr<DownloadManager>().StartDownload(url, savePath, OnDownloading, () =>
366
+                {
367
+                    completeTotalCount += (long)versionData.m_size;
368
+                    ModifyLocaData(versionData);
369
+                    if (completeTotalCount >= TotalSize)
370
+                    {
371
+                        EnterAPP();
372
+                        _checkProgress = 1;
373
+                        onCheckUpdataEvent?.Invoke(_checkProgress, TotalSize, completeTotalCount);
374
+                    }
375
+                }, err => onErrorEvent?.Invoke(err));
376
+            }
377
+        }
378
+
379
+        long t = 0;
380
+        private void OnDownloading(float prossage,long totaSize,long size)
381
+        {
382
+            _checkProgress = (completeTotalCount*1.0f)/TotalSize + 1/TotalSize*(size*1.0f / totaSize);
383
+            
384
+            onCheckUpdataEvent?.Invoke(_checkProgress, TotalSize, completeTotalCount);
385
+        }
386
+    }
387
+}
388
+

HotfixManager.cs.meta → Runtime/HotfixManager.cs.meta


+ 8 - 0
Runtime/IHotfixStartup.cs

@@ -0,0 +1,8 @@
1
+using TFramework;
2
+namespace TModule.Runtime
3
+{
4
+    public interface IHotfixStartup 
5
+    {
6
+        public void Run();
7
+    }
8
+}

+ 11 - 0
Runtime/IHotfixStartup.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: 4835afafd0cbade4b9b5e89ac348af1d
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: 

+ 20 - 0
Runtime/TModule.Hotfix.asmdef

@@ -0,0 +1,20 @@
1
+{
2
+    "name": "TModule.Hotfix",
3
+    "rootNamespace": "",
4
+    "references": [
5
+        "GUID:898501583428e8f489660e21e1ae83fe",
6
+        "GUID:13ba8ce62aa80c74598530029cb2d649",
7
+        "GUID:f51ebe6a0ceec4240a699833d6309b23",
8
+        "GUID:2373f786d14518f44b0f475db77ba4de",
9
+        "GUID:bdbd4945b56ace4478e1dfa9f119cbea"
10
+    ],
11
+    "includePlatforms": [],
12
+    "excludePlatforms": [],
13
+    "allowUnsafeCode": false,
14
+    "overrideReferences": false,
15
+    "precompiledReferences": [],
16
+    "autoReferenced": true,
17
+    "defineConstraints": [],
18
+    "versionDefines": [],
19
+    "noEngineReferences": false
20
+}

+ 7 - 0
Runtime/TModule.Hotfix.asmdef.meta

@@ -0,0 +1,7 @@
1
+fileFormatVersion: 2
2
+guid: 7f2fc0ba02ac13a43a6ece3893e805dc
3
+AssemblyDefinitionImporter:
4
+  externalObjects: {}
5
+  userData: 
6
+  assetBundleName: 
7
+  assetBundleVariant: 

+ 23 - 0
Runtime/VersionDataEntity.cs

@@ -0,0 +1,23 @@
1
+using System;
2
+
3
+namespace TModule.Runtime
4
+{
5
+    [Serializable]
6
+    public class VersionDataEntity
7
+    {
8
+        /// <summary>
9
+        /// ×ÊÔ´Ãû³Æ
10
+        /// </summary>
11
+        public string m_fullName {  get; set; }
12
+
13
+        /// <summary>
14
+        /// MD5
15
+        /// </summary>
16
+        public string m_md5 {  get; set; }
17
+
18
+        /// <summary>
19
+        /// ×ÊÔ´´óС£¨K£©
20
+        /// </summary>
21
+        public double m_size {  get; set; }
22
+    }
23
+}

+ 11 - 0
Runtime/VersionDataEntity.cs.meta

@@ -0,0 +1,11 @@
1
+fileFormatVersion: 2
2
+guid: c26349e8638a4224cbe622331274aa42
3
+MonoImporter:
4
+  externalObjects: {}
5
+  serializedVersion: 2
6
+  defaultReferences: []
7
+  executionOrder: 0
8
+  icon: {instanceID: 0}
9
+  userData: 
10
+  assetBundleName: 
11
+  assetBundleVariant: