From cb936a477da07e8e70efb471f1d687d0717506f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?91=E7=84=A6=E5=85=88=E7=94=9F?= <1292366529@qq.com> Date: Tue, 24 Jan 2023 08:40:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=9B=B4=E6=96=B0=E5=86=85?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BundleMasterRuntime/AssetComponent.cs | 70 +--- BundleMasterRuntime/AssetComponentLife.cs | 13 + BundleMasterRuntime/AssetComponentTools.cs | 279 +-------------- BundleMasterRuntime/AssetComponentUpdate.cs | 144 ++------ .../Base/LoadBaseRuntimeLoad.cs | 85 ++--- BundleMasterRuntime/DownLoadTask.cs | 162 +++++++++ .../Helper/DownloadBundleHelper.cs | 163 ++++++++- .../Helper/PathUnifiedHelper.cs | 25 ++ BundleMasterRuntime/Helper/VerifyHelper.cs | 14 + BundleMasterRuntime/LoadHandler.cs | 93 +++-- BundleMasterRuntime/LoadHandlerBase.cs | 12 +- BundleMasterRuntime/LoadSceneHandler.cs | 88 ++--- BundleMasterRuntime/UpdateBundleDataInfo.cs | 320 ++++++++++++++++++ CoroutineLock/CoroutineLock.cs | 47 +++ CoroutineLock/CoroutineLockComponent.cs | 37 ++ CoroutineLock/CoroutineLockQueue.cs | 63 ++++ CoroutineLock/CoroutineLockType.cs | 11 + CoroutineLock/LoadPathConvertHelper.cs | 29 ++ Editor/BundleMasterEditor/BuildAssets.cs | 80 ----- Editor/BundleMasterEditor/BundleUsefulTool.cs | 120 +++++++ LiteMultiThreadDownLoad/ILiteThreadAction.cs | 7 + LiteMultiThreadDownLoad/LMTDProxy.cs | 21 ++ LiteMultiThreadDownLoad/LMTDownLoad.cs | 214 ++++++++++++ LiteMultiThreadDownLoad/LiteThread.cs | 53 +++ LiteMultiThreadDownLoad/LmtdTable.cs | 46 +++ LiteMultiThreadDownLoad/ThreadFactory.cs | 137 ++++++++ 26 files changed, 1658 insertions(+), 675 deletions(-) create mode 100644 BundleMasterRuntime/DownLoadTask.cs create mode 100644 BundleMasterRuntime/Helper/PathUnifiedHelper.cs create mode 100644 BundleMasterRuntime/UpdateBundleDataInfo.cs create mode 100644 CoroutineLock/CoroutineLock.cs create mode 100644 CoroutineLock/CoroutineLockComponent.cs create mode 100644 CoroutineLock/CoroutineLockQueue.cs create mode 100644 CoroutineLock/CoroutineLockType.cs create mode 100644 CoroutineLock/LoadPathConvertHelper.cs create mode 100644 Editor/BundleMasterEditor/BundleUsefulTool.cs create mode 100644 LiteMultiThreadDownLoad/ILiteThreadAction.cs create mode 100644 LiteMultiThreadDownLoad/LMTDProxy.cs create mode 100644 LiteMultiThreadDownLoad/LMTDownLoad.cs create mode 100644 LiteMultiThreadDownLoad/LiteThread.cs create mode 100644 LiteMultiThreadDownLoad/LmtdTable.cs create mode 100644 LiteMultiThreadDownLoad/ThreadFactory.cs diff --git a/BundleMasterRuntime/AssetComponent.cs b/BundleMasterRuntime/AssetComponent.cs index 9841f63..5952845 100644 --- a/BundleMasterRuntime/AssetComponent.cs +++ b/BundleMasterRuntime/AssetComponent.cs @@ -190,60 +190,42 @@ public static async ETTask LoadAsync(string assetPath, string bundlePackag { bundlePackageName = AssetComponentConfig.DefaultBundlePackageName; } - - T asset = null; if (AssetComponentConfig.AssetLoadMode == AssetLoadMode.Develop) { #if UNITY_EDITOR - asset = AssetDatabase.LoadAssetAtPath(assetPath); + return AssetDatabase.LoadAssetAtPath(assetPath); #else AssetLogHelper.LogError("加载资源: " + assetPath + " 失败(资源加载Develop模式只能在编辑器下运行)"); + return null; #endif - return asset; } - LoadHandler loadHandler = null; if (!BundleNameToRuntimeInfo.TryGetValue(bundlePackageName, out BundleRuntimeInfo bundleRuntimeInfo)) { AssetLogHelper.LogError(bundlePackageName + "分包没有初始化"); return null; } - if (!bundleRuntimeInfo.AllAssetLoadHandler.TryGetValue(assetPath, out loadHandler)) + if (!bundleRuntimeInfo.AllAssetLoadHandler.TryGetValue(assetPath, out LoadHandler loadHandler)) { loadHandler = LoadHandlerFactory.GetLoadHandler(assetPath, bundlePackageName, true, true); bundleRuntimeInfo.AllAssetLoadHandler.Add(assetPath, loadHandler); bundleRuntimeInfo.UnLoadHandler.Add(loadHandler.UniqueId, loadHandler); } + //加载资源 + CoroutineLock coroutineLock = await CoroutineLockComponent.Wait(CoroutineLockType.BundleMaster, LoadPathConvertHelper.LoadPathConvert("T|" + assetPath)); if (loadHandler.LoadState == LoadState.NoLoad) { - ETTask tcs = ETTask.Create(true); - loadHandler.AwaitEtTasks.Add(tcs); await loadHandler.LoadAsync(); AssetBundleRequest loadAssetAsync = loadHandler.FileAssetBundle.LoadAssetAsync(assetPath); + ETTask tcs = ETTask.Create(true); loadAssetAsync.completed += operation => { loadHandler.Asset = loadAssetAsync.asset; - asset = loadHandler.Asset as T; - for (int i = 0; i < loadHandler.AwaitEtTasks.Count; i++) - { - ETTask etTask = loadHandler.AwaitEtTasks[i]; - etTask.SetResult(); - } - loadHandler.AwaitEtTasks.Clear(); + tcs.SetResult(); }; await tcs; - return asset; - } - else if (loadHandler.LoadState == LoadState.Loading) - { - ETTask tcs = ETTask.Create(true); - loadHandler.AwaitEtTasks.Add(tcs); - await tcs; - return (T)loadHandler.Asset; - } - else - { - return (T)loadHandler.Asset; } + coroutineLock.Dispose(); + return (T)loadHandler.Asset; } /// @@ -295,55 +277,39 @@ public static ETTask LoadAsync(out LoadHandler handler, string assetPath, if (AssetComponentConfig.AssetLoadMode == AssetLoadMode.Develop) { #if UNITY_EDITOR - UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); + return AssetDatabase.LoadAssetAtPath(assetPath, typeof(UnityEngine.Object)); #else - UnityEngine.Object asset = null; AssetLogHelper.LogError("加载资源: " + assetPath + " 失败(资源加载Develop模式只能在编辑器下运行)"); + return null; #endif - return asset; } - LoadHandler loadHandler = null; if (!BundleNameToRuntimeInfo.TryGetValue(bundlePackageName, out BundleRuntimeInfo bundleRuntimeInfo)) { AssetLogHelper.LogError(bundlePackageName + "分包没有初始化"); return null; } - if (!bundleRuntimeInfo.AllAssetLoadHandler.TryGetValue(assetPath, out loadHandler)) + if (!bundleRuntimeInfo.AllAssetLoadHandler.TryGetValue(assetPath, out LoadHandler loadHandler)) { loadHandler = LoadHandlerFactory.GetLoadHandler(assetPath, bundlePackageName, true, true); bundleRuntimeInfo.AllAssetLoadHandler.Add(assetPath, loadHandler); bundleRuntimeInfo.UnLoadHandler.Add(loadHandler.UniqueId, loadHandler); } + //加载资源 + CoroutineLock coroutineLock = await CoroutineLockComponent.Wait(CoroutineLockType.BundleMaster, LoadPathConvertHelper.LoadPathConvert("UnityObject|" + assetPath)); if (loadHandler.LoadState == LoadState.NoLoad) { - ETTask tcs = ETTask.Create(true); - loadHandler.AwaitEtTasks.Add(tcs); await loadHandler.LoadAsync(); AssetBundleRequest loadAssetAsync = loadHandler.FileAssetBundle.LoadAssetAsync(assetPath); + ETTask tcs = ETTask.Create(true); loadAssetAsync.completed += operation => { loadHandler.Asset = loadAssetAsync.asset; - for (int i = 0; i < loadHandler.AwaitEtTasks.Count; i++) - { - ETTask etTask = loadHandler.AwaitEtTasks[i]; - etTask.SetResult(); - } - loadHandler.AwaitEtTasks.Clear(); + tcs.SetResult(); }; await tcs; - return loadHandler.Asset; - } - else if (loadHandler.LoadState == LoadState.Loading) - { - ETTask tcs = ETTask.Create(true); - loadHandler.AwaitEtTasks.Add(tcs); - await tcs; - return loadHandler.Asset; - } - else - { - return loadHandler.Asset; } + coroutineLock.Dispose(); + return loadHandler.Asset; } /// diff --git a/BundleMasterRuntime/AssetComponentLife.cs b/BundleMasterRuntime/AssetComponentLife.cs index a3edadf..11b49c2 100644 --- a/BundleMasterRuntime/AssetComponentLife.cs +++ b/BundleMasterRuntime/AssetComponentLife.cs @@ -42,7 +42,20 @@ public static void Update() _timer = 0; AutoAddToTrueUnLoadPool(); } + //更新下载完成任务 + lock (DownloadBundleHelper.DownLoadFinishQueue) + { + if (DownloadBundleHelper.DownLoadFinishQueue.Count > 0) + { + int downLoadFinishCount = DownloadBundleHelper.DownLoadFinishQueue.Count; + for (int i = 0; i < downLoadFinishCount; i++) + { + DownloadBundleHelper.DownLoadFinishQueue.Dequeue().SetResult(); + } + } + } DownLoadAction?.Invoke(nowTime); + } } } \ No newline at end of file diff --git a/BundleMasterRuntime/AssetComponentTools.cs b/BundleMasterRuntime/AssetComponentTools.cs index db7427b..382cfe6 100644 --- a/BundleMasterRuntime/AssetComponentTools.cs +++ b/BundleMasterRuntime/AssetComponentTools.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.IO; using System.Text; using ET; @@ -98,279 +96,4 @@ private static void CreateUpdateLogFile(string filePath, string fileData) } - public class UpdateBundleDataInfo - { - /// - /// 是否需要更新 - /// - public bool NeedUpdate = false; - - /// - /// 需要更新的总大小 - /// - public long NeedUpdateSize = 0; - - /// - /// 是否取消 - /// - internal bool Cancel = false; - - /// - /// 需要更新的Bundle的信息 - /// - internal readonly Dictionary> PackageNeedUpdateBundlesInfos = new Dictionary>(); - - /// - /// 分包对应的版本号 int[本地版本, 远程版本] 仅Build模式可用 - /// - internal readonly Dictionary PackageToVersion = new Dictionary(); - - /// - /// 分包以及对于的类型 - /// - internal readonly Dictionary PackageToType = new Dictionary(); - - /// - /// 客户端更新时间 - /// - internal string UpdateTime = ""; - - /// - /// CRC信息字典 - /// - internal readonly Dictionary> PackageCRCDictionary = new Dictionary>(); - - /// - /// CRC对应的写入流 - /// - internal readonly Dictionary PackageCRCFile = new Dictionary(); - - /// - /// 更新完成的大小 - /// - public long FinishUpdateSize = 0; - - /// - /// 是否更新完成 - /// - public bool FinishUpdate = false; - - /// - /// 更新进度(1 - 100) - /// - public float Progress - { - get - { - float progress = ((float)FinishUpdateSize / NeedUpdateSize) * 100.0f; - return progress; - } - } - - /// - /// 总共需要下载的Bundle的数量 - /// - public int NeedDownLoadBundleCount = 0; - - /// - /// 下载完成的Bundle的数量 - /// - public int FinishDownLoadBundleCount = 0; - - /// - /// 平滑进度 - /// - internal float SmoothProgress = 0.0f; - private Action progressCallback; - /// - /// 下载进度回调,每帧刷新,有插值 - /// - public event Action ProgressCallback - { - add => progressCallback += value; - remove => this.progressCallback -= value; - } - - internal void UpdateProgress(float deltaTime) - { - if (FinishUpdate) - { - SmoothProgress = 100; - } - else - { - SmoothProgress += (Progress - SmoothProgress) * ValueHelper.GetMinValue(deltaTime / 0.1f, 1.0f); - } - progressCallback?.Invoke(SmoothProgress); - } - - internal Action FinishCallback; - - /// - /// 下载更新完成回调 - /// - public event Action DownLoadFinishCallback - { - add => FinishCallback += value; - remove => this.FinishCallback -= value; - } - - /// - /// 下载速度平均个数 - /// - private int speedAvgCount = 10; - /// - /// 下载速度缓存队列 - /// - private readonly Queue downLoadSpeedQueue = new Queue(); - /// - /// 下载速度 - /// - public int DownLoadSpeed - { - get - { - int addSpeed = 0; - int speedQueueCount = downLoadSpeedQueue.Count; - if (speedQueueCount == 0) - { - return 0; - } - for (int i = 0; i < speedQueueCount; i++) - { - int tempSpeed = downLoadSpeedQueue.Dequeue(); - addSpeed += tempSpeed; - downLoadSpeedQueue.Enqueue(tempSpeed); - } - return addSpeed / speedQueueCount * AssetComponentConfig.MaxDownLoadCount; - } - } - - private Action downLoadSpeedCallback; - - /// - /// 下载速度改变回调,非每帧都刷新 单位byte每秒 - /// - public event Action DownLoadSpeedCallback - { - add => downLoadSpeedCallback += value; - remove => this.downLoadSpeedCallback -= value; - } - - public void AddSpeedQueue(int speed) - { - if (downLoadSpeedQueue.Count >= speedAvgCount) - { - downLoadSpeedQueue.Dequeue(); - AddSpeedQueue(speed); - return; - } - downLoadSpeedQueue.Enqueue(speed); - downLoadSpeedCallback?.Invoke(DownLoadSpeed); - } - - /// - /// 获取更新的分包的版本索引 int[本地版本, 远程版本] - /// - public int[] GetVersion(string bundlePackageName) - { - if (AssetComponentConfig.AssetLoadMode != AssetLoadMode.Build) - { - AssetLogHelper.LogError("仅Build模式可用获取版本索引"); - return null; - } - if (!PackageToVersion.TryGetValue(bundlePackageName, out int[] versionData)) - { - AssetLogHelper.LogError("获取索引号没有找到分包: " + bundlePackageName); - return null; - } - return versionData; - } - - /// - /// 添加一个更新好的CRC文件信息 - /// - internal void AddCRCFileInfo(string bundlePackageName, string fileName, uint crc) - { - if (AssetComponentConfig.AssetLoadMode != AssetLoadMode.Build) - { - AssetLogHelper.LogError("AssetLoadMode != Build 不涉及更新"); - return; - } - if (!PackageCRCDictionary.TryGetValue(bundlePackageName, out Dictionary crcDictionary)) - { - AssetLogHelper.LogError("获取索引号没有找到分包: " + bundlePackageName); - return; - } - if (Cancel) - { - return; - } - if (!crcDictionary.ContainsKey(fileName)) - { - crcDictionary.Add(fileName, crc); - } - PackageCRCFile[bundlePackageName].WriteLine(fileName + "|" + crc.ToString() + "|" + UpdateTime); - PackageCRCFile[bundlePackageName].Flush(); - } - - private Action errorCancelCallback; - - /// - /// 下载失败回调 - /// - public event Action ErrorCancelCallback - { - add => errorCancelCallback += value; - remove => this.errorCancelCallback -= value; - } - - /// - /// 取消更新 - /// - public void CancelUpdate() - { - if (Cancel) - { - return; - } - Cancel = true; - errorCancelCallback?.Invoke(); - DestroySelf(); - } - - private void DestroySelf() - { - foreach (StreamWriter sw in PackageCRCFile.Values) - { - sw.Close(); - sw.Dispose(); - } - PackageCRCFile.Clear(); - AssetComponent.DownLoadAction -= UpdateProgress; - progressCallback = null; - FinishCallback = null; - downLoadSpeedCallback = null; - errorCancelCallback = null; - } - - /// - /// 分包类型 - /// - internal enum PackageType - { - /// - /// 未加密 - /// - Normal, - /// - /// 加密 - /// - Encrypt, - /// - /// 原始资源 - /// - Origin, - } - } } \ No newline at end of file diff --git a/BundleMasterRuntime/AssetComponentUpdate.cs b/BundleMasterRuntime/AssetComponentUpdate.cs index ed4dfe2..6ccd07a 100644 --- a/BundleMasterRuntime/AssetComponentUpdate.cs +++ b/BundleMasterRuntime/AssetComponentUpdate.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using ET; -using UnityEngine; using UnityEngine.Networking; namespace BM @@ -120,16 +119,18 @@ public static async ETTask CheckAllBundlePackageUpdate(Dic } } + string[] remoteVersionData = remoteVersionLog.Split('\n'); + string[] localVersionData = localVersionLog.Split('\n'); if (bundlePackageInfo.Value) { - await CalcNeedUpdateBundleFileCRC(updateBundleDataInfo, bundlePackageName, remoteVersionLog, localVersionLog); + await CalcNeedUpdateBundleFileCRC(updateBundleDataInfo, bundlePackageName, remoteVersionData, localVersionData); } else { - await CalcNeedUpdateBundle(updateBundleDataInfo, bundlePackageName, remoteVersionLog, localVersionLog); + await CalcNeedUpdateBundle(updateBundleDataInfo, bundlePackageName, remoteVersionData, localVersionData); } } - if (updateBundleDataInfo.NeedUpdateSize > 0) + if (updateBundleDataInfo.PackageNeedUpdateBundlesInfos.Count > 0) { updateBundleDataInfo.NeedUpdate = true; } @@ -149,10 +150,8 @@ public static async ETTask CheckAllBundlePackageUpdate(Dic /// /// 获取所哟需要更新的Bundle的文件(不检查文件CRC) /// - private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBundleDataInfo, string bundlePackageName, string remoteVersionLog, string localVersionLog) + private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBundleDataInfo, string bundlePackageName, string[] remoteVersionData, string[] localVersionData) { - string[] remoteVersionData = remoteVersionLog.Split('\n'); - string[] localVersionData = localVersionLog.Split('\n'); string[] remoteVersionDataSplits = remoteVersionData[0].Split('|'); int remoteVersion = int.Parse(remoteVersionDataSplits[1]); int localVersion = int.Parse(localVersionData[0].Split('|')[1]); @@ -258,7 +257,7 @@ private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBund { if (crc != streamingCrc) { - if (!File.Exists(Path.Combine(AssetComponentConfig.HotfixPath, bundlePackageName, info[0]))) + if (!File.Exists(PathUnifiedHelper.UnifiedPath(Path.Combine(AssetComponentConfig.HotfixPath, bundlePackageName, info[0])))) { needUpdateBundles.Add(info[0], long.Parse(info[1])); } @@ -266,7 +265,7 @@ private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBund } else { - if (!File.Exists(Path.Combine(AssetComponentConfig.HotfixPath, bundlePackageName, info[0]))) + if (!File.Exists(PathUnifiedHelper.UnifiedPath(Path.Combine(AssetComponentConfig.HotfixPath, bundlePackageName, info[0])))) { needUpdateBundles.Add(info[0], long.Parse(info[1])); } @@ -277,6 +276,11 @@ private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBund needUpdateBundles.Add(info[0], long.Parse(info[1])); } } + if (!(needUpdateBundles.Count > 0 || remoteVersionData[0] != localVersionData[0])) + { + //说明这个分包不需要更新 + return; + } updateBundleDataInfo.PackageNeedUpdateBundlesInfos.Add(bundlePackageName, needUpdateBundles); foreach (long needUpdateBundleSize in needUpdateBundles.Values) { @@ -288,10 +292,8 @@ private static async ETTask CalcNeedUpdateBundle(UpdateBundleDataInfo updateBund /// /// 获取所有需要更新的Bundle的文件(计算文件CRC) /// - private static async ETTask CalcNeedUpdateBundleFileCRC(UpdateBundleDataInfo updateBundleDataInfo, string bundlePackageName, string remoteVersionLog, string localVersionLog) + private static async ETTask CalcNeedUpdateBundleFileCRC(UpdateBundleDataInfo updateBundleDataInfo, string bundlePackageName, string[] remoteVersionData, string[] localVersionData) { - string[] remoteVersionData = remoteVersionLog.Split('\n'); - string[] localVersionData = localVersionLog.Split('\n'); string[] remoteVersionDataSplits = remoteVersionData[0].Split('|'); int remoteVersion = int.Parse(remoteVersionDataSplits[1]); int localVersion = int.Parse(localVersionData[0].Split('|')[1]); @@ -345,6 +347,12 @@ private static async ETTask CalcNeedUpdateBundleFileCRC(UpdateBundleDataInfo upd await finishTcs; _checkCount = 0; } + + if (!(needUpdateBundles.Count > 0 || remoteVersionData[0] != localVersionData[0])) + { + //说明这个分包不需要更新 + return; + } updateBundleDataInfo.PackageNeedUpdateBundlesInfos.Add(bundlePackageName, needUpdateBundles); foreach (long needUpdateBundleSize in needUpdateBundles.Values) { @@ -412,6 +420,8 @@ public static async ETTask DownLoadUpdate(UpdateBundleDataInfo updateBundleDataI downLoadTaskQueue.Enqueue(downLoadTask); } } + //最初开始的下载任务数量 + int firstDownLoadTask = 0; //开启下载 for (int i = 0; i < AssetComponentConfig.MaxDownLoadCount; i++) { @@ -419,15 +429,21 @@ public static async ETTask DownLoadUpdate(UpdateBundleDataInfo updateBundleDataI { if (downLoadTaskQueue.Count > 0) { - downLoadTaskQueue.Dequeue().DownLoad().Coroutine(); + //downLoadTaskQueue.Dequeue().DownLoad().Coroutine(); //webGL 不可以用多线程下载器 + downLoadTaskQueue.Dequeue().ThreadDownLoad().Coroutine(); + firstDownLoadTask++; break; } } } //将下载进度更新添加到帧循环 - DownLoadAction += updateBundleDataInfo.UpdateProgress; + DownLoadAction += updateBundleDataInfo.UpdateProgressAndSpeedCallBack; + //说明没有ab需要更新 + if (firstDownLoadTask == 0) + { + downLoading.SetResult(); + } await downLoading; - //下载完成关闭CRCLog文件 foreach (StreamWriter sw in updateBundleDataInfo.PackageCRCFile.Values) { @@ -472,105 +488,11 @@ public static async ETTask DownLoadUpdate(UpdateBundleDataInfo updateBundleDataI } updateBundleDataInfo.SmoothProgress = 100; - DownLoadAction -= updateBundleDataInfo.UpdateProgress; + DownLoadAction -= updateBundleDataInfo.UpdateProgressAndSpeedCallBack; updateBundleDataInfo.FinishCallback?.Invoke(); AssetLogHelper.LogError("下载完成"); } } - - - public class DownLoadTask - { - public UpdateBundleDataInfo UpdateBundleDataInfo; - - public ETTask DownLoadingKey; - - public Dictionary> PackageDownLoadTask; - - /// - /// 下载的资源分包名称 - /// - public string PackegName; - - /// - /// 分包所在路径 - /// - public string DownLoadPackagePath; - - /// - /// 下载的文件的名称 - /// - public string FileName; - - /// - /// 下载的文件的大小 - /// - public long FileSize; - - public async ETTask DownLoad() - { - string url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, UnityWebRequest.EscapeURL(FileName)); - if (FileName.Contains("\\")) - { - string[] pathSplits = FileName.Split('\\'); - string filePath = ""; - string fileUrls = ""; - for (int i = 0; i < pathSplits.Length - 1; i++) - { - filePath += (pathSplits[i] + "/"); - fileUrls += (UnityWebRequest.EscapeURL(pathSplits[i]) + "/"); - } - fileUrls += (UnityWebRequest.EscapeURL(pathSplits[pathSplits.Length - 1])); - Directory.CreateDirectory(Path.Combine(AssetComponentConfig.HotfixPath, PackegName, filePath)); - url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, fileUrls); - } - float startDownLoadTime = Time.realtimeSinceStartup; - byte[] data = await DownloadBundleHelper.DownloadDataByUrl(url); - if (data == null) - { - UpdateBundleDataInfo.CancelUpdate(); - } - //说明下载更新已经被取消 - if (UpdateBundleDataInfo.Cancel) - { - return; - } - int dataLength = data.Length; - UpdateBundleDataInfo.AddSpeedQueue((int)(dataLength / (Time.realtimeSinceStartup - startDownLoadTime))); - string fileCreatePath = Path.Combine(DownLoadPackagePath, FileName); - fileCreatePath = fileCreatePath.Replace("\\", "/"); - using (FileStream fs = new FileStream(fileCreatePath, FileMode.Create)) - { - //大于2M用异步 - if (dataLength > 2097152) - { - await fs.WriteAsync(data, 0, data.Length); - } - else - { - fs.Write(data, 0, data.Length); - } - fs.Close(); - } - UpdateBundleDataInfo.AddCRCFileInfo(PackegName, FileName, VerifyHelper.GetCRC32(data)); - UpdateBundleDataInfo.FinishUpdateSize += data.Length; - UpdateBundleDataInfo.FinishDownLoadBundleCount++; - foreach (Queue downLoadTaskQueue in PackageDownLoadTask.Values) - { - if (downLoadTaskQueue.Count > 0) - { - downLoadTaskQueue.Dequeue().DownLoad().Coroutine(); - return; - } - } - //说明下载完成了 - if (UpdateBundleDataInfo.FinishDownLoadBundleCount < UpdateBundleDataInfo.NeedDownLoadBundleCount) - { - return; - } - UpdateBundleDataInfo.FinishUpdate = true; - DownLoadingKey.SetResult(); - } - } + } \ No newline at end of file diff --git a/BundleMasterRuntime/Base/LoadBaseRuntimeLoad.cs b/BundleMasterRuntime/Base/LoadBaseRuntimeLoad.cs index c91043a..3d0fe3b 100644 --- a/BundleMasterRuntime/Base/LoadBaseRuntimeLoad.cs +++ b/BundleMasterRuntime/Base/LoadBaseRuntimeLoad.cs @@ -24,12 +24,7 @@ public partial class LoadBase /// /// AssetBundle的引用 /// - public AssetBundle AssetBundle; - - /// - /// 加载完成后需要执行的Task - /// - private List _loadFinishTasks = new List(); + public AssetBundle AssetBundle = null; /// /// 需要统计进度 @@ -82,7 +77,7 @@ internal void LoadAssetBundle(string bundlePackageName) return; } } - + //资源没有加载过也没有正在加载就同步加载出来 if (AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].Encrypt) { string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, true); @@ -95,95 +90,86 @@ internal void LoadAssetBundle(string bundlePackageName) AssetBundle = AssetBundle.LoadFromFile(assetBundlePath); } _loadState = LoadState.Finish; - for (int i = 0; i < _loadFinishTasks.Count; i++) - { - _loadFinishTasks[i].SetResult(); - } - _loadFinishTasks.Clear(); } - internal async ETTask LoadAssetBundleAsync(ETTask tcs, string bundlePackageName) + /// + /// 异步加载LoadBase的AssetBundle + /// + internal async ETTask LoadAssetBundleAsync(string bundlePackageName) { AddRefCount(); if (_loadState == LoadState.Finish) { - tcs.SetResult(); return; } - if (_loadState == LoadState.Loading) - { - _loadFinishTasks.Add(tcs); - return; - } - _loadFinishTasks.Add(tcs); - _loadState = LoadState.Loading; - - if (AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].Encrypt) - { - string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, true); - await LoadDataFinish(assetBundlePath, bundlePackageName); - } - else + BundleRuntimeInfo bundleRuntimeInfo = AssetComponent.BundleNameToRuntimeInfo[bundlePackageName]; + string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, bundleRuntimeInfo.Encrypt); + //获取一个协程锁 + CoroutineLock coroutineLock = await CoroutineLockComponent.Wait(CoroutineLockType.BundleMaster, LoadPathConvertHelper.LoadPathConvert(assetBundlePath)); + if (_loadState == LoadState.NoLoad) { - string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, false); - LoadBundleFinish(assetBundlePath); + _loadState = LoadState.Loading; + if (bundleRuntimeInfo.Encrypt) + { + await LoadDataFinish(assetBundlePath, bundleRuntimeInfo.SecretKey); + } + else + { + await LoadBundleFinish(assetBundlePath); + } + _loadState = LoadState.Finish; } + //协程锁解锁 + coroutineLock.Dispose(); } /// /// 通过Byte加载完成(只有启用了异或加密才使用此加载方式) /// - private async ETTask LoadDataFinish(string assetBundlePath, string bundlePackageName) + private async ETTask LoadDataFinish(string assetBundlePath, char[] bundlePackageSecretKey) { - byte[] data = await VerifyHelper.GetDecryptDataAsync(assetBundlePath, _loadProgress, AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].SecretKey); + byte[] data = await VerifyHelper.GetDecryptDataAsync(assetBundlePath, _loadProgress, bundlePackageSecretKey); if (_loadState == LoadState.Finish) { return; } + ETTask tcs = ETTask.Create(true); _assetBundleCreateRequest = AssetBundle.LoadFromMemoryAsync(data); _assetBundleCreateRequest.completed += operation => { AssetBundle = _assetBundleCreateRequest.assetBundle; - for (int i = 0; i < _loadFinishTasks.Count; i++) - { - _loadFinishTasks[i].SetResult(); - } - _loadFinishTasks.Clear(); - _loadState = LoadState.Finish; + tcs.SetResult(); //判断是否还需要 if (_refCount <= 0) { AssetComponent.AddPreUnLoadPool(this); } }; + await tcs; } /// /// 通过路径直接加载硬盘上的AssetBundle /// - /// - private void LoadBundleFinish(string assetBundlePath) + private async ETTask LoadBundleFinish(string assetBundlePath) { if (_loadState == LoadState.Finish) { return; } + ETTask tcs = ETTask.Create(true); _assetBundleCreateRequest = AssetBundle.LoadFromFileAsync(assetBundlePath); _assetBundleCreateRequest.completed += operation => { AssetBundle = _assetBundleCreateRequest.assetBundle; - for (int i = 0; i < _loadFinishTasks.Count; i++) - { - _loadFinishTasks[i].SetResult(); - } - _loadFinishTasks.Clear(); - _loadState = LoadState.Finish; + tcs.SetResult(); //判断是否还需要 if (_refCount <= 0) { AssetComponent.AddPreUnLoadPool(this); } }; + await tcs; } /// @@ -205,7 +191,7 @@ internal void ForceLoadFinish(string bundlePackageName) if (AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].Encrypt) { - string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, true); + string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].Encrypt); byte[] data = VerifyHelper.GetDecryptData(assetBundlePath, AssetComponent.BundleNameToRuntimeInfo[bundlePackageName].SecretKey); AssetBundle = AssetBundle.LoadFromMemory(data); } @@ -214,11 +200,6 @@ internal void ForceLoadFinish(string bundlePackageName) string assetBundlePath = AssetComponent.BundleFileExistPath(bundlePackageName, AssetBundleName, false); AssetBundle = AssetBundle.LoadFromFile(assetBundlePath); } - for (int i = 0; i < _loadFinishTasks.Count; i++) - { - _loadFinishTasks[i].SetResult(); - } - _loadFinishTasks.Clear(); _loadState = LoadState.Finish; //判断是否还需要 if (_refCount <= 0) diff --git a/BundleMasterRuntime/DownLoadTask.cs b/BundleMasterRuntime/DownLoadTask.cs new file mode 100644 index 0000000..01f4af8 --- /dev/null +++ b/BundleMasterRuntime/DownLoadTask.cs @@ -0,0 +1,162 @@ +using System; +using ET; +using System.Collections.Generic; +using System.IO; +using LMTD; +using UnityEngine.Networking; +using UnityEngine; + +namespace BM +{ + public class DownLoadTask + { + public UpdateBundleDataInfo UpdateBundleDataInfo; + + /// + /// 资源更新下载完成回调位 + /// + public ETTask DownLoadingKey; + + public Dictionary> PackageDownLoadTask; + + /// + /// 下载的资源分包名称 + /// + public string PackegName; + + /// + /// 分包所在路径 + /// + public string DownLoadPackagePath; + + /// + /// 下载的文件的名称 + /// + public string FileName; + + /// + /// 下载的文件的大小 + /// + public long FileSize; + + public async ETTask DownLoad() + { + string url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, UnityWebRequest.EscapeURL(FileName)); + if (FileName.Contains("\\")) + { + string[] pathSplits = FileName.Split('\\'); + string filePath = ""; + string fileUrls = ""; + for (int i = 0; i < pathSplits.Length - 1; i++) + { + filePath += (pathSplits[i] + "/"); + fileUrls += (UnityWebRequest.EscapeURL(pathSplits[i]) + "/"); + } + fileUrls += (UnityWebRequest.EscapeURL(pathSplits[pathSplits.Length - 1])); + Directory.CreateDirectory(Path.Combine(AssetComponentConfig.HotfixPath, PackegName, filePath)); + url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, fileUrls); + } + float startDownLoadTime = Time.realtimeSinceStartup; + DownLoadData downLoadData = await DownloadBundleHelper.DownloadRefDataByUrl(url); + if (downLoadData.Data == null) + { + UpdateBundleDataInfo.CancelUpdate(); + } + //说明下载更新已经被取消 + if (UpdateBundleDataInfo.Cancel) + { + DownLoadData.Recovery(downLoadData); + return; + } + Debug.Assert(downLoadData.Data != null, "downLoadData.Data != null"); + int dataLength = downLoadData.Data.Length; + + string fileCreatePath = PathUnifiedHelper.UnifiedPath(Path.Combine(DownLoadPackagePath, FileName)); + using (FileStream fs = new FileStream(fileCreatePath, FileMode.Create)) + { + //大于2M用异步 + if (dataLength > 2097152) + { + await fs.WriteAsync(downLoadData.Data, 0, downLoadData.Data.Length); + } + else + { + fs.Write(downLoadData.Data, 0, downLoadData.Data.Length); + } + fs.Close(); + } + UpdateBundleDataInfo.AddCRCFileInfo(PackegName, FileName, VerifyHelper.GetCRC32(downLoadData)); + UpdateBundleDataInfo.FinishUpdateSize += downLoadData.Data.Length; + UpdateBundleDataInfo.FinishDownLoadBundleCount++; + DownLoadData.Recovery(downLoadData); + foreach (Queue downLoadTaskQueue in PackageDownLoadTask.Values) + { + if (downLoadTaskQueue.Count > 0) + { + downLoadTaskQueue.Dequeue().DownLoad().Coroutine(); + return; + } + } + //说明下载完成了 + if (UpdateBundleDataInfo.FinishDownLoadBundleCount < UpdateBundleDataInfo.NeedDownLoadBundleCount) + { + DownLoadData.ClearPool(); + return; + } + UpdateBundleDataInfo.FinishUpdate = true; + DownLoadingKey.SetResult(); + } + + + public async ETTask ThreadDownLoad() + { + //计算URL + string url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, UnityWebRequest.EscapeURL(FileName)); + if (FileName.Contains("\\")) + { + string[] pathSplits = FileName.Split('\\'); + string filePath = ""; + string fileUrls = ""; + for (int i = 0; i < pathSplits.Length - 1; i++) + { + filePath += (pathSplits[i] + "/"); + fileUrls += (UnityWebRequest.EscapeURL(pathSplits[i]) + "/"); + } + fileUrls += (UnityWebRequest.EscapeURL(pathSplits[pathSplits.Length - 1])); + Directory.CreateDirectory(Path.Combine(AssetComponentConfig.HotfixPath, PackegName, filePath)); + url = Path.Combine(AssetComponentConfig.BundleServerUrl, PackegName, fileUrls); + } + //计算文件存储路径 + string fileCreatePath = PathUnifiedHelper.UnifiedPath(Path.Combine(DownLoadPackagePath, FileName)); + //开始下载 + LmtDownloadInfo lmtDownloadInfo = await DownloadBundleHelper.DownloadData(url, fileCreatePath, UpdateBundleDataInfo); + //说明下载更新已经被取消 + if (UpdateBundleDataInfo.Cancel) + { + return; + } + UpdateBundleDataInfo.AddCRCFileInfo(PackegName, FileName, lmtDownloadInfo.DownLoadFileCRC); + UpdateBundleDataInfo.FinishDownLoadBundleCount++; + //检查新的需要下载的资源 + foreach (Queue downLoadTaskQueue in PackageDownLoadTask.Values) + { + if (downLoadTaskQueue.Count > 0) + { + downLoadTaskQueue.Dequeue().ThreadDownLoad().Coroutine(); + return; + } + } + //说明下载完成了 + if (UpdateBundleDataInfo.FinishDownLoadBundleCount < UpdateBundleDataInfo.NeedDownLoadBundleCount) + { + DownLoadData.ClearPool(); + return; + } + UpdateBundleDataInfo.FinishUpdate = true; + DownLoadingKey.SetResult(); + } + + + + } +} \ No newline at end of file diff --git a/BundleMasterRuntime/Helper/DownloadBundleHelper.cs b/BundleMasterRuntime/Helper/DownloadBundleHelper.cs index b6314ce..d0c4b36 100644 --- a/BundleMasterRuntime/Helper/DownloadBundleHelper.cs +++ b/BundleMasterRuntime/Helper/DownloadBundleHelper.cs @@ -1,5 +1,8 @@ -using UnityEngine.Networking; +using System.Collections.Generic; +using System.Threading.Tasks; +using UnityEngine.Networking; using ET; +using LMTD; namespace BM { @@ -18,7 +21,7 @@ public static async ETTask DownloadDataByUrl(string url) AssetLogHelper.LogError("下载资源失败: " + url); return null; } - + private static async ETTask DownloadData(string url) { using (UnityWebRequest webRequest = UnityWebRequest.Get(url)) @@ -43,6 +46,162 @@ private static async ETTask DownloadData(string url) } } + public static async ETTask DownloadRefDataByUrl(string url) + { + DownLoadData downLoadData = DownLoadData.Get(); + for (int i = 0; i < AssetComponentConfig.ReDownLoadCount; i++) + { + await DownloadData(url, downLoadData); + if (downLoadData.Data != null) + { + return downLoadData; + } + } + AssetLogHelper.LogError("下载资源失败: " + url); + return null; + } + + private static async ETTask DownloadData(string url, DownLoadData downLoadData) + { + using (UnityWebRequest webRequest = UnityWebRequest.Get(url)) + { + UnityWebRequestAsyncOperation webRequestAsync = webRequest.SendWebRequest(); + ETTask waitDown = ETTask.Create(true); + webRequestAsync.completed += (asyncOperation) => + { + waitDown.SetResult(); + }; + await waitDown; +#if UNITY_2020_1_OR_NEWER + if (webRequest.result != UnityWebRequest.Result.Success) +#else + if (!string.IsNullOrEmpty(webRequest.error)) +#endif + { + AssetLogHelper.Log("下载Bundle失败 重试\n" + webRequest.error + "\nURL:" + url); + return; + } + downLoadData.Data = webRequest.downloadHandler.data; + webRequest.downloadHandler.Dispose(); + } + } + + /// + /// 下载完成回调位 + /// + internal static readonly Queue DownLoadFinishQueue = new Queue(); + + /// + /// 多线程下载资源直存 + /// + public static async ETTask DownloadData(string url, string filePath, UpdateBundleDataInfo updateBundleDataInfo) + { + LmtDownloadInfo lmtDownloadInfo = new LmtDownloadInfo(); + for (int i = 0; i < AssetComponentConfig.ReDownLoadCount; i++) + { + //说明下载更新已经被取消 + if (updateBundleDataInfo.Cancel) + { + return lmtDownloadInfo; + } + + lmtDownloadInfo = await DownloadOneData(url, filePath, updateBundleDataInfo); + if (lmtDownloadInfo.LmtDownloadResult == LmtDownloadResult.Success) + { + return lmtDownloadInfo; + } + } + //多次下载失败直接取消下载 + updateBundleDataInfo.CancelUpdate(); + return lmtDownloadInfo; + } + + private static async ETTask DownloadOneData(string url, string filePath, UpdateBundleDataInfo updateBundleDataInfo) + { + ETTask tcs = ETTask.Create(true); + LMTDownLoad lmtDownLoad = LMTDownLoad.Create(url, filePath); + long lastDownSize = 0; + lmtDownLoad.UpDateInfo += () => + { + // ReSharper disable AccessToDisposedClosure + updateBundleDataInfo.FinishUpdateSize += lmtDownLoad.LmtDownloadInfo.DownLoadSize - lastDownSize; + lastDownSize = lmtDownLoad.LmtDownloadInfo.DownLoadSize; + // ReSharper restore AccessToDisposedClosure + lock (updateBundleDataInfo) + { + if (updateBundleDataInfo.Cancel) + { + // ReSharper disable once AccessToDisposedClosure + lmtDownLoad.CancelLock = true; + } + } + }; + lmtDownLoad.Completed += (info) => + { + lock (DownLoadFinishQueue) + { + DownLoadFinishQueue.Enqueue(tcs); + } + }; + ThreadFactory.ThreadAction(lmtDownLoad); + await tcs; + LmtDownloadInfo lmtDownloadInfo = lmtDownLoad.LmtDownloadInfo; + if (lmtDownloadInfo.LmtDownloadResult != LmtDownloadResult.Success) + { + updateBundleDataInfo.FinishUpdateSize -= lmtDownLoad.LmtDownloadInfo.DownLoadSize; + //主动取消下载就没必要再输出log了 + if (lmtDownloadInfo.LmtDownloadResult != LmtDownloadResult.CancelDownLoad) + { + AssetLogHelper.LogError("下载资源失败: " + lmtDownloadInfo.LmtDownloadResult); + } + } + lmtDownLoad.Dispose(); + return lmtDownloadInfo; + } + + /// + /// 另一种多线程下载的方式,不建议使用 + /// + public static async Task DownloadDataTask(string url, string filePath) + { + LMTDownLoad lmtDownLoad = LMTDownLoad.Create(url, filePath); + Task tcs = new Task(lmtDownLoad.DownLoad); + tcs.Start(); + await tcs; + lmtDownLoad.Dispose(); + return lmtDownLoad.LmtDownloadInfo; + } } + + public class DownLoadData + { + public byte[] Data; + + private static readonly Queue DownLoadDataPool = new Queue(); + + public static DownLoadData Get() + { + if (DownLoadDataPool.Count > 0) + { + return DownLoadDataPool.Dequeue(); + } + else + { + return new DownLoadData(); + } + } + + public static void Recovery(DownLoadData downLoadData) + { + downLoadData.Data = null; + DownLoadDataPool.Enqueue(downLoadData); + } + + public static void ClearPool() + { + DownLoadDataPool.Clear(); + } + } + } \ No newline at end of file diff --git a/BundleMasterRuntime/Helper/PathUnifiedHelper.cs b/BundleMasterRuntime/Helper/PathUnifiedHelper.cs new file mode 100644 index 0000000..0f2f659 --- /dev/null +++ b/BundleMasterRuntime/Helper/PathUnifiedHelper.cs @@ -0,0 +1,25 @@ +namespace BM +{ + public static class PathUnifiedHelper + { + /// + /// 解决路径资源是否存在的判定问题 + /// + public static string UnifiedPath(string path) + { + +#if UNITY_ANDROID && !UNITY_EDITOR + path = path.Replace("\\", "/"); +#elif UNITY_IOS && !UNITY_EDITOR + path = path.Replace("\\", "/"); +#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX + +#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN + +#endif + return path; + } + + + } +} \ No newline at end of file diff --git a/BundleMasterRuntime/Helper/VerifyHelper.cs b/BundleMasterRuntime/Helper/VerifyHelper.cs index 09ca086..d4d9dbe 100644 --- a/BundleMasterRuntime/Helper/VerifyHelper.cs +++ b/BundleMasterRuntime/Helper/VerifyHelper.cs @@ -67,6 +67,17 @@ public static uint GetCRC32(byte[] bytes) return crc; } + internal static uint GetCRC32(DownLoadData downLoadData) + { + uint iCount = (uint)downLoadData.Data.Length; + uint crc = 0xFFFFFFFF; + for (uint i = 0; i < iCount; i++) + { + crc = (crc << 8) ^ CRCTable[(crc >> 24) ^ downLoadData.Data[i]]; + } + return crc; + } + /// /// 创建加密过的数据 /// @@ -174,6 +185,9 @@ public static long GetFileLength(string filePath) return fileLength; } + /// + /// 注意此Table和多线程下载的Table以及打AssetBundle加密用的Table需要一样,其中在多线程下载的Table里还有一份拷贝 + /// private static readonly UInt32[] CRCTable = { 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, diff --git a/BundleMasterRuntime/LoadHandler.cs b/BundleMasterRuntime/LoadHandler.cs index 3006b2c..b890042 100644 --- a/BundleMasterRuntime/LoadHandler.cs +++ b/BundleMasterRuntime/LoadHandler.cs @@ -37,12 +37,6 @@ public event Action Completed /// 加载的状态 /// internal LoadState LoadState = LoadState.NoLoad; - - /// - /// 异步等待加载的Task - /// - internal List AwaitEtTasks = new List(); - internal LoadHandler(bool isPool) { @@ -72,24 +66,24 @@ internal void Init(string assetPath, string bundlePackageName, bool haveHandler) AssetLogHelper.LogError("没有找到资源组: " + groupPath); return; } - _loadBase = loadGroup; + LoadBase = loadGroup; //需要记录loadGroup的依赖 for (int i = 0; i < loadGroup.DependFileName.Count; i++) { string dependFile = loadGroup.DependFileName[i]; if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadDependDic.TryGetValue(dependFile, out LoadDepend loadDepend)) { - _loadDepends.Add(loadDepend); + LoadDepends.Add(loadDepend); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadFileDic.TryGetValue(dependFile, out LoadFile loadDependFile)) { - _loadDependFiles.Add(loadDependFile); + LoadDependFiles.Add(loadDependFile); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadGroupDic.TryGetValue(dependFile, out LoadGroup loadDependGroup)) { - _loadDependGroups.Add(loadDependGroup); + LoadDependGroups.Add(loadDependGroup); continue; } AssetLogHelper.LogError("依赖的资源没有找到对应的类: " + dependFile); @@ -102,24 +96,24 @@ internal void Init(string assetPath, string bundlePackageName, bool haveHandler) AssetLogHelper.LogError("没有找到资源: " + AssetPath); return; } - _loadBase = loadFile; + LoadBase = loadFile; //需要记录loadFile的依赖 for (int i = 0; i < loadFile.DependFileName.Length; i++) { string dependFile = loadFile.DependFileName[i]; if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadDependDic.TryGetValue(dependFile, out LoadDepend loadDepend)) { - _loadDepends.Add(loadDepend); + LoadDepends.Add(loadDepend); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadFileDic.TryGetValue(dependFile, out LoadFile loadDependFile)) { - _loadDependFiles.Add(loadDependFile); + LoadDependFiles.Add(loadDependFile); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadGroupDic.TryGetValue(dependFile, out LoadGroup loadDependGroup)) { - _loadDependGroups.Add(loadDependGroup); + LoadDependGroups.Add(loadDependGroup); continue; } AssetLogHelper.LogError("依赖的资源没有找到对应的类: " + dependFile); @@ -131,20 +125,20 @@ internal void Init(string assetPath, string bundlePackageName, bool haveHandler) /// internal void Load() { - _loadBase.LoadAssetBundle(BundlePackageName); - for (int i = 0; i < _loadDepends.Count; i++) + LoadBase.LoadAssetBundle(BundlePackageName); + for (int i = 0; i < LoadDepends.Count; i++) { - _loadDepends[i].LoadAssetBundle(BundlePackageName); + LoadDepends[i].LoadAssetBundle(BundlePackageName); } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - _loadDependFiles[i].LoadAssetBundle(BundlePackageName); + LoadDependFiles[i].LoadAssetBundle(BundlePackageName); } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - _loadDependGroups[i].LoadAssetBundle(BundlePackageName); + LoadDependGroups[i].LoadAssetBundle(BundlePackageName); } - FileAssetBundle = _loadBase.AssetBundle; + FileAssetBundle = LoadBase.AssetBundle; LoadState = LoadState.Finish; } @@ -155,27 +149,28 @@ internal async ETTask LoadAsync() { LoadState = LoadState.Loading; //计算出所有需要加载的Bundle包的总数 - RefLoadFinishCount = _loadDepends.Count + _loadDependFiles.Count + _loadDependGroups.Count + 1; + RefLoadFinishCount = LoadDepends.Count + LoadDependFiles.Count + LoadDependGroups.Count + 1; ETTask tcs = ETTask.Create(true); - LoadAsyncLoader(_loadBase, tcs).Coroutine(); - for (int i = 0; i < _loadDepends.Count; i++) + LoadAsyncLoader(LoadBase, tcs).Coroutine(); + for (int i = 0; i < LoadDepends.Count; i++) { - LoadAsyncLoader(_loadDepends[i], tcs).Coroutine(); + LoadAsyncLoader(LoadDepends[i], tcs).Coroutine(); } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - LoadAsyncLoader(_loadDependFiles[i], tcs).Coroutine(); + LoadAsyncLoader(LoadDependFiles[i], tcs).Coroutine(); } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - LoadAsyncLoader(_loadDependGroups[i], tcs).Coroutine(); + LoadAsyncLoader(LoadDependGroups[i], tcs).Coroutine(); } await tcs; - if (LoadState != LoadState.Finish) + if (LoadState == LoadState.Finish) { - LoadState = LoadState.Finish; - FileAssetBundle = _loadBase.AssetBundle; + AssetLogHelper.Log("此资源异步加载时触发了强制加载: " + AssetPath); } + LoadState = LoadState.Finish; + FileAssetBundle = LoadBase.AssetBundle; } /// @@ -183,20 +178,20 @@ internal async ETTask LoadAsync() /// internal void ForceAsyncLoadFinish() { - _loadBase.ForceLoadFinish(BundlePackageName); - for (int i = 0; i < _loadDepends.Count; i++) + LoadBase.ForceLoadFinish(BundlePackageName); + for (int i = 0; i < LoadDepends.Count; i++) { - _loadDepends[i].ForceLoadFinish(BundlePackageName); + LoadDepends[i].ForceLoadFinish(BundlePackageName); } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - _loadDependFiles[i].ForceLoadFinish(BundlePackageName); + LoadDependFiles[i].ForceLoadFinish(BundlePackageName); } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - _loadDependGroups[i].ForceLoadFinish(BundlePackageName); + LoadDependGroups[i].ForceLoadFinish(BundlePackageName); } - FileAssetBundle = _loadBase.AssetBundle; + FileAssetBundle = LoadBase.AssetBundle; LoadState = LoadState.Finish; } @@ -208,23 +203,23 @@ protected override void ClearAsset() Asset = null; FileAssetBundle = null; LoadState = LoadState.NoLoad; - foreach (LoadDepend loadDepends in _loadDepends) + foreach (LoadDepend loadDepends in LoadDepends) { loadDepends.SubRefCount(); } - _loadDepends.Clear(); - foreach (LoadFile loadDependFiles in _loadDependFiles) + LoadDepends.Clear(); + foreach (LoadFile loadDependFiles in LoadDependFiles) { loadDependFiles.SubRefCount(); } - _loadDependFiles.Clear(); - foreach (LoadGroup loadDependGroups in _loadDependGroups) + LoadDependFiles.Clear(); + foreach (LoadGroup loadDependGroups in LoadDependGroups) { loadDependGroups.SubRefCount(); } - _loadDependGroups.Clear(); - _loadBase.SubRefCount(); - _loadBase = null; + LoadDependGroups.Clear(); + LoadBase.SubRefCount(); + LoadBase = null; //从缓存里取出进池 if (!AssetComponent.BundleNameToRuntimeInfo.TryGetValue(BundlePackageName, out BundleRuntimeInfo bundleRuntimeInfo)) { diff --git a/BundleMasterRuntime/LoadHandlerBase.cs b/BundleMasterRuntime/LoadHandlerBase.cs index 7206d8c..5cdd8dc 100644 --- a/BundleMasterRuntime/LoadHandlerBase.cs +++ b/BundleMasterRuntime/LoadHandlerBase.cs @@ -44,31 +44,29 @@ public abstract class LoadHandlerBase /// /// 资源所在的LoadBase包 /// - protected LoadBase _loadBase = null; + protected LoadBase LoadBase = null; /// /// 依赖的Bundle包 /// - protected List _loadDepends = new List(); + protected readonly List LoadDepends = new List(); /// /// 依赖的其它File包 /// - protected List _loadDependFiles = new List(); + protected readonly List LoadDependFiles = new List(); /// /// 依赖的其它Group包 /// - protected List _loadDependGroups = new List(); + protected readonly List LoadDependGroups = new List(); /// /// 加载计数器(负责完成所有依赖的Bundle加载完成) /// protected async ETTask LoadAsyncLoader(LoadBase loadBase, ETTask baseTcs) { - ETTask tcs = ETTask.Create(true); - loadBase.LoadAssetBundleAsync(tcs, BundlePackageName).Coroutine(); - await tcs; + await loadBase.LoadAssetBundleAsync(BundlePackageName); RefLoadFinishCount--; if (RefLoadFinishCount == 0) { diff --git a/BundleMasterRuntime/LoadSceneHandler.cs b/BundleMasterRuntime/LoadSceneHandler.cs index 0c86c75..77598a4 100644 --- a/BundleMasterRuntime/LoadSceneHandler.cs +++ b/BundleMasterRuntime/LoadSceneHandler.cs @@ -26,24 +26,24 @@ public LoadSceneHandler(string scenePath, string bundlePackageName) AssetLogHelper.LogError("没有找到资源组: " + groupPath); return; } - _loadBase = loadGroup; + LoadBase = loadGroup; //需要记录loadGroup的依赖 for (int i = 0; i < loadGroup.DependFileName.Count; i++) { string dependFile = loadGroup.DependFileName[i]; if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadDependDic.TryGetValue(dependFile, out LoadDepend loadDepend)) { - _loadDepends.Add(loadDepend); + LoadDepends.Add(loadDepend); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadFileDic.TryGetValue(dependFile, out LoadFile loadDependFile)) { - _loadDependFiles.Add(loadDependFile); + LoadDependFiles.Add(loadDependFile); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadGroupDic.TryGetValue(dependFile, out LoadGroup loadDependGroup)) { - _loadDependGroups.Add(loadDependGroup); + LoadDependGroups.Add(loadDependGroup); continue; } AssetLogHelper.LogError("场景依赖的资源没有找到对应的类: " + dependFile); @@ -56,24 +56,24 @@ public LoadSceneHandler(string scenePath, string bundlePackageName) AssetLogHelper.LogError("没有找到资源: " + AssetPath); return; } - _loadBase = loadFile; + LoadBase = loadFile; //需要记录loadFile的依赖 for (int i = 0; i < loadFile.DependFileName.Length; i++) { string dependFile = loadFile.DependFileName[i]; if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadDependDic.TryGetValue(dependFile, out LoadDepend loadDepend)) { - _loadDepends.Add(loadDepend); + LoadDepends.Add(loadDepend); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadFileDic.TryGetValue(dependFile, out LoadFile loadDependFile)) { - _loadDependFiles.Add(loadDependFile); + LoadDependFiles.Add(loadDependFile); continue; } if (AssetComponent.BundleNameToRuntimeInfo[BundlePackageName].LoadGroupDic.TryGetValue(dependFile, out LoadGroup loadDependGroup)) { - _loadDependGroups.Add(loadDependGroup); + LoadDependGroups.Add(loadDependGroup); continue; } AssetLogHelper.LogError("场景依赖的资源没有找到对应的类: " + dependFile); @@ -86,20 +86,20 @@ public LoadSceneHandler(string scenePath, string bundlePackageName) /// public void LoadSceneBundle() { - _loadBase.LoadAssetBundle(BundlePackageName); - for (int i = 0; i < _loadDepends.Count; i++) + LoadBase.LoadAssetBundle(BundlePackageName); + for (int i = 0; i < LoadDepends.Count; i++) { - _loadDepends[i].LoadAssetBundle(BundlePackageName); + LoadDepends[i].LoadAssetBundle(BundlePackageName); } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - _loadDependFiles[i].LoadAssetBundle(BundlePackageName); + LoadDependFiles[i].LoadAssetBundle(BundlePackageName); } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - _loadDependGroups[i].LoadAssetBundle(BundlePackageName); + LoadDependGroups[i].LoadAssetBundle(BundlePackageName); } - FileAssetBundle = _loadBase.AssetBundle; + FileAssetBundle = LoadBase.AssetBundle; } /// @@ -108,26 +108,26 @@ public void LoadSceneBundle() public async ETTask LoadSceneBundleAsync(ETTask finishTask) { //计算出所有需要加载的Bundle包的总数 - RefLoadFinishCount = _loadDepends.Count + _loadDependFiles.Count + _loadDependGroups.Count + 1; - _loadBase.OpenProgress(); - LoadAsyncLoader(_loadBase, finishTask).Coroutine(); - for (int i = 0; i < _loadDepends.Count; i++) + RefLoadFinishCount = LoadDepends.Count + LoadDependFiles.Count + LoadDependGroups.Count + 1; + LoadBase.OpenProgress(); + LoadAsyncLoader(LoadBase, finishTask).Coroutine(); + for (int i = 0; i < LoadDepends.Count; i++) { - _loadDepends[i].OpenProgress(); - LoadAsyncLoader(_loadDepends[i], finishTask).Coroutine(); + LoadDepends[i].OpenProgress(); + LoadAsyncLoader(LoadDepends[i], finishTask).Coroutine(); } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - _loadDependFiles[i].OpenProgress(); - LoadAsyncLoader(_loadDependFiles[i], finishTask).Coroutine(); + LoadDependFiles[i].OpenProgress(); + LoadAsyncLoader(LoadDependFiles[i], finishTask).Coroutine(); } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - _loadDependGroups[i].OpenProgress(); - LoadAsyncLoader(_loadDependGroups[i], finishTask).Coroutine(); + LoadDependGroups[i].OpenProgress(); + LoadAsyncLoader(LoadDependGroups[i], finishTask).Coroutine(); } await finishTask; - FileAssetBundle = _loadBase.AssetBundle; + FileAssetBundle = LoadBase.AssetBundle; } /// @@ -146,20 +146,20 @@ public float GetProgress() } float progress = 0; int loadCount = 1; - progress += _loadBase.GetProgress(); - for (int i = 0; i < _loadDepends.Count; i++) + progress += LoadBase.GetProgress(); + for (int i = 0; i < LoadDepends.Count; i++) { - progress += _loadDepends[i].GetProgress(); + progress += LoadDepends[i].GetProgress(); loadCount++; } - for (int i = 0; i < _loadDependFiles.Count; i++) + for (int i = 0; i < LoadDependFiles.Count; i++) { - progress += _loadDependFiles[i].GetProgress(); + progress += LoadDependFiles[i].GetProgress(); loadCount++; } - for (int i = 0; i < _loadDependGroups.Count; i++) + for (int i = 0; i < LoadDependGroups.Count; i++) { - progress += _loadDependGroups[i].GetProgress(); + progress += LoadDependGroups[i].GetProgress(); loadCount++; } return progress / loadCount; @@ -171,23 +171,23 @@ public float GetProgress() protected override void ClearAsset() { FileAssetBundle = null; - foreach (LoadDepend loadDepends in _loadDepends) + foreach (LoadDepend loadDepends in LoadDepends) { loadDepends.SubRefCount(); } - _loadDepends.Clear(); - foreach (LoadFile loadDependFiles in _loadDependFiles) + LoadDepends.Clear(); + foreach (LoadFile loadDependFiles in LoadDependFiles) { loadDependFiles.SubRefCount(); } - _loadDependFiles.Clear(); - foreach (LoadGroup loadDependGroups in _loadDependGroups) + LoadDependFiles.Clear(); + foreach (LoadGroup loadDependGroups in LoadDependGroups) { loadDependGroups.SubRefCount(); } - _loadDependGroups.Clear(); - _loadBase.SubRefCount(); - _loadBase = null; + LoadDependGroups.Clear(); + LoadBase.SubRefCount(); + LoadBase = null; } } } \ No newline at end of file diff --git a/BundleMasterRuntime/UpdateBundleDataInfo.cs b/BundleMasterRuntime/UpdateBundleDataInfo.cs new file mode 100644 index 0000000..f94d7a2 --- /dev/null +++ b/BundleMasterRuntime/UpdateBundleDataInfo.cs @@ -0,0 +1,320 @@ +using System; +using System.Collections.Generic; +using System.IO; +using UnityEngine; + +namespace BM +{ + public class UpdateBundleDataInfo + { + /// + /// 是否需要更新 + /// + public bool NeedUpdate = false; + + /// + /// 需要更新的总大小 + /// + public long NeedUpdateSize = 0; + + /// + /// 是否取消 + /// + internal bool Cancel = false; + + /// + /// 需要更新的Bundle的信息 + /// + internal readonly Dictionary> PackageNeedUpdateBundlesInfos = new Dictionary>(); + + /// + /// 分包对应的版本号 int[本地版本, 远程版本] 仅Build模式可用 + /// + internal readonly Dictionary PackageToVersion = new Dictionary(); + + /// + /// 分包以及对于的类型 + /// + internal readonly Dictionary PackageToType = new Dictionary(); + + /// + /// 客户端更新时间 + /// + internal string UpdateTime = ""; + + /// + /// CRC信息字典 + /// + internal readonly Dictionary> PackageCRCDictionary = new Dictionary>(); + + /// + /// CRC对应的写入流 + /// + internal readonly Dictionary PackageCRCFile = new Dictionary(); + + /// + /// 更新完成的大小 + /// + public long FinishUpdateSize = 0; + + /// + /// 是否更新完成 + /// + public bool FinishUpdate = false; + + /// + /// 更新进度(1 - 100) + /// + public float Progress + { + get + { + float progress = ((float)FinishUpdateSize / NeedUpdateSize) * 100.0f; + return progress; + } + } + + /// + /// 总共需要下载的Bundle的数量 + /// + public int NeedDownLoadBundleCount = 0; + + /// + /// 下载完成的Bundle的数量 + /// + public int FinishDownLoadBundleCount = 0; + + /// + /// 平滑进度 + /// + internal float SmoothProgress = 0.0f; + private Action progressCallback; + /// + /// 下载进度回调,每帧刷新,有插值 + /// + public event Action ProgressCallback + { + add => progressCallback += value; + remove => this.progressCallback -= value; + } + + internal Action FinishCallback; + + /// + /// 下载更新完成回调 + /// + public event Action DownLoadFinishCallback + { + add => FinishCallback += value; + remove => this.FinishCallback -= value; + } + + /// + /// 下载速度平均个数 + /// + private int speedAvgCount = 5; + /// + /// 下载速度更新频率 + /// + private long speedRate = 1000; + /// + /// 下载速度缓存队列 + /// + private readonly Queue downLoadSpeedQueue = new Queue(); + + /// + /// 下载速度 + /// + public int DownLoadSpeed + { + get + { + int addSpeed = 0; + int speedQueueCount = downLoadSpeedQueue.Count; + if (speedQueueCount == 0) + { + return 0; + } + for (int i = 0; i < speedQueueCount; i++) + { + int tempSpeed = downLoadSpeedQueue.Dequeue(); + addSpeed += tempSpeed; + downLoadSpeedQueue.Enqueue(tempSpeed); + } + return addSpeed / speedQueueCount; + } + } + + private Action downLoadSpeedCallback; + + /// + /// 下载速度改变回调,非每帧都刷新 单位byte每秒 + /// + public event Action DownLoadSpeedCallback + { + add => downLoadSpeedCallback += value; + remove => this.downLoadSpeedCallback -= value; + } + + private void AddSpeedQueue(int speed) + { + if (downLoadSpeedQueue.Count >= speedAvgCount) + { + downLoadSpeedQueue.Dequeue(); + AddSpeedQueue(speed); + return; + } + downLoadSpeedQueue.Enqueue(speed); + } + + /// + /// 上一帧下载大小 + /// + private long lastDownSize = 0; + + /// + /// 上一次下载速度变化时间 + /// + private long lastTime = 0; + + /// + /// 更新下载进度和下载速度以及执行回调 + /// + internal void UpdateProgressAndSpeedCallBack(float deltaTime) + { + //计算下载进度 + if (FinishUpdate) + { + SmoothProgress = 100; + } + else + { + SmoothProgress += (Progress - SmoothProgress) * ValueHelper.GetMinValue(deltaTime / 0.1f, 1.0f); + } + progressCallback?.Invoke(SmoothProgress); + + long nowTime = DateTime.Now.Ticks; + long continueTime = nowTime - lastTime; + //下载速度一秒才更新一次 + if (continueTime > 10000 * speedRate) + { + //计算下载速度(非UnityWebReq下载), 下载量有改变 + if (FinishUpdateSize != lastDownSize) + { + long blockTimeDownSize = FinishUpdateSize - lastDownSize; + lastDownSize = FinishUpdateSize; + if (lastTime != 0) + { + AddSpeedQueue((int)(blockTimeDownSize / ((nowTime - lastTime) / 10000000.0f))); + } + lastTime = nowTime; + } + downLoadSpeedCallback?.Invoke(DownLoadSpeed); + } + } + + /// + /// 获取更新的分包的版本索引 int[本地版本, 远程版本] + /// + public int[] GetVersion(string bundlePackageName) + { + if (AssetComponentConfig.AssetLoadMode != AssetLoadMode.Build) + { + AssetLogHelper.LogError("仅Build模式可用获取版本索引"); + return null; + } + if (!PackageToVersion.TryGetValue(bundlePackageName, out int[] versionData)) + { + AssetLogHelper.LogError("获取索引号没有找到分包: " + bundlePackageName); + return null; + } + return versionData; + } + + /// + /// 添加一个更新好的CRC文件信息 + /// + internal void AddCRCFileInfo(string bundlePackageName, string fileName, uint crc) + { + if (AssetComponentConfig.AssetLoadMode != AssetLoadMode.Build) + { + AssetLogHelper.LogError("AssetLoadMode != Build 不涉及更新"); + return; + } + if (!PackageCRCDictionary.TryGetValue(bundlePackageName, out Dictionary crcDictionary)) + { + AssetLogHelper.LogError("获取索引号没有找到分包: " + bundlePackageName); + return; + } + if (Cancel) + { + return; + } + if (!crcDictionary.ContainsKey(fileName)) + { + crcDictionary.Add(fileName, crc); + } + PackageCRCFile[bundlePackageName].WriteLine(fileName + "|" + crc.ToString() + "|" + UpdateTime); + PackageCRCFile[bundlePackageName].Flush(); + } + + private Action errorCancelCallback; + + /// + /// 下载失败回调 + /// + public event Action ErrorCancelCallback + { + add => errorCancelCallback += value; + remove => this.errorCancelCallback -= value; + } + + /// + /// 取消更新 + /// + public void CancelUpdate() + { + if (Cancel) + { + return; + } + Cancel = true; + errorCancelCallback?.Invoke(); + DestroySelf(); + } + + private void DestroySelf() + { + foreach (StreamWriter sw in PackageCRCFile.Values) + { + sw.Close(); + sw.Dispose(); + } + PackageCRCFile.Clear(); + AssetComponent.DownLoadAction -= UpdateProgressAndSpeedCallBack; + progressCallback = null; + FinishCallback = null; + downLoadSpeedCallback = null; + errorCancelCallback = null; + } + + /// + /// 分包类型 + /// + internal enum PackageType + { + /// + /// 未加密 + /// + Normal, + /// + /// 加密 + /// + Encrypt, + /// + /// 原始资源 + /// + Origin, + } + } +} \ No newline at end of file diff --git a/CoroutineLock/CoroutineLock.cs b/CoroutineLock/CoroutineLock.cs new file mode 100644 index 0000000..67c027c --- /dev/null +++ b/CoroutineLock/CoroutineLock.cs @@ -0,0 +1,47 @@ +using System; +using ET; + +namespace BM +{ + public class CoroutineLock : IDisposable + { + private bool _isDispose = false; + internal long Key; + private CoroutineLockQueue _coroutineLockQueue; + private ETTask _waitTask; + + internal void Init(long key, CoroutineLockQueue coroutineLockQueue) + { + _isDispose = false; + this.Key = key; + this._coroutineLockQueue = coroutineLockQueue; + _waitTask = ETTask.Create(true); + } + + internal void Enable() + { + _waitTask.SetResult(); + } + + internal ETTask Wait() + { + return _waitTask; + } + + + public void Dispose() + { + if (_isDispose) + { + //Debug.LogError("协程锁重复释放"); + return; + } + _waitTask = null; + _isDispose = true; + _coroutineLockQueue.CoroutineLockDispose(this); + _coroutineLockQueue = null; + CoroutineLockComponent.CoroutineLockQueue.Enqueue(this); + + } + } +} \ No newline at end of file diff --git a/CoroutineLock/CoroutineLockComponent.cs b/CoroutineLock/CoroutineLockComponent.cs new file mode 100644 index 0000000..1384161 --- /dev/null +++ b/CoroutineLock/CoroutineLockComponent.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using ET; + +namespace BM +{ + public static class CoroutineLockComponent + { + /// + /// 协程锁类型以及对应的协程锁队列 + /// + private static readonly Dictionary CoroutineLockTypeToQueue = new Dictionary(); + + /// + /// 没有用到的CoroutineLock池 + /// + internal static readonly Queue CoroutineLockQueue = new Queue(); + + /// + /// 缓存池的池 + /// + internal static readonly Queue> CoroutineLockQueuePool = new Queue>(); + + public static async ETTask Wait(CoroutineLockType coroutineLockType, long key) + { + if (!CoroutineLockTypeToQueue.TryGetValue(coroutineLockType, out CoroutineLockQueue coroutineLockQueue)) + { + coroutineLockQueue = new CoroutineLockQueue(coroutineLockType); + CoroutineLockTypeToQueue.Add(coroutineLockType, coroutineLockQueue); + } + //取一个 CoroutineLock + CoroutineLock coroutineLock = coroutineLockQueue.GetCoroutineLock(key); + await coroutineLock.Wait(); + return coroutineLock; + } + } +} + diff --git a/CoroutineLock/CoroutineLockQueue.cs b/CoroutineLock/CoroutineLockQueue.cs new file mode 100644 index 0000000..84f920e --- /dev/null +++ b/CoroutineLock/CoroutineLockQueue.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; + +namespace BM +{ + internal class CoroutineLockQueue + { + private CoroutineLockType _coroutineLockType; + private readonly Dictionary> _coroutineLockKeyToQueue = new Dictionary>(); + + internal CoroutineLockQueue(CoroutineLockType coroutineLockType) + { + this._coroutineLockType = coroutineLockType; + } + + internal void CoroutineLockDispose(CoroutineLock coroutineLock) + { + Queue keyToQueue = _coroutineLockKeyToQueue[coroutineLock.Key]; + if (keyToQueue.Count > 0) + { + CoroutineLock nextCoroutineLock = keyToQueue.Dequeue(); + nextCoroutineLock.Enable(); + return; + } + keyToQueue.Clear(); + CoroutineLockComponent.CoroutineLockQueuePool.Enqueue(keyToQueue); + _coroutineLockKeyToQueue.Remove(coroutineLock.Key); + } + + internal CoroutineLock GetCoroutineLock(long key) + { + CoroutineLock coroutineLock; + if (CoroutineLockComponent.CoroutineLockQueue.Count > 0) + { + coroutineLock = CoroutineLockComponent.CoroutineLockQueue.Dequeue(); + } + else + { + coroutineLock = new CoroutineLock(); + } + coroutineLock.Init(key, this); + if (!_coroutineLockKeyToQueue.ContainsKey(key)) + { + Queue coroutineLockQueue; + if (CoroutineLockComponent.CoroutineLockQueuePool.Count > 0) + { + coroutineLockQueue = CoroutineLockComponent.CoroutineLockQueuePool.Dequeue(); + } + else + { + coroutineLockQueue = new Queue(); + } + _coroutineLockKeyToQueue.Add(key, coroutineLockQueue); + coroutineLock.Enable(); + } + else + { + _coroutineLockKeyToQueue[key].Enqueue(coroutineLock); + } + return coroutineLock; + } + + } +} \ No newline at end of file diff --git a/CoroutineLock/CoroutineLockType.cs b/CoroutineLock/CoroutineLockType.cs new file mode 100644 index 0000000..8b47ce4 --- /dev/null +++ b/CoroutineLock/CoroutineLockType.cs @@ -0,0 +1,11 @@ +namespace BM +{ + public enum CoroutineLockType + { + /// + /// BundleMaster资源加载用的标识符 + /// + BundleMaster = 0, + + } +} \ No newline at end of file diff --git a/CoroutineLock/LoadPathConvertHelper.cs b/CoroutineLock/LoadPathConvertHelper.cs new file mode 100644 index 0000000..d404464 --- /dev/null +++ b/CoroutineLock/LoadPathConvertHelper.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; + +namespace BM +{ + /// + /// 负责将加载路径转化为唯一的long key + /// + public class LoadPathConvertHelper + { + /// + /// 初始值 + /// + private static long _originKey = 0; + + private static readonly Dictionary LoadPathToKey = new Dictionary(); + + public static long LoadPathConvert(string loadPath) + { + if (LoadPathToKey.TryGetValue(loadPath, out long key)) + { + return key; + } + _originKey++; + key = _originKey; + LoadPathToKey.Add(loadPath, key); + return key; + } + } +} \ No newline at end of file diff --git a/Editor/BundleMasterEditor/BuildAssets.cs b/Editor/BundleMasterEditor/BuildAssets.cs index fdf6287..bb52905 100644 --- a/Editor/BundleMasterEditor/BuildAssets.cs +++ b/Editor/BundleMasterEditor/BuildAssets.cs @@ -6,10 +6,7 @@ using System.Text.RegularExpressions; using UnityEngine; using UnityEditor; -using UnityEditor.U2D; -using Debug = UnityEngine.Debug; using UnityEngine.Rendering; -using UnityEngine.U2D; namespace BM { @@ -110,83 +107,6 @@ public static void BuildAllBundle() AssetLogHelper.Log("打包结束, 耗时" + sw.Elapsed.TotalMilliseconds + " ms \n" + assetLoadTable.BuildBundlePath); } - [MenuItem("Tools/BuildAsset/Copy资源到StreamingAssets")] - public static void CopyToStreamingAssets() - { - if (!Directory.Exists(Application.streamingAssetsPath)) - { - Directory.CreateDirectory(Application.streamingAssetsPath); - } - DeleteHelper.DeleteDir(Application.streamingAssetsPath); - AssetLoadTable assetLoadTable = AssetDatabase.LoadAssetAtPath(BundleMasterWindow.AssetLoadTablePath); - foreach (AssetsSetting assetsSetting in assetLoadTable.AssetsSettings) - { - if (!(assetsSetting is AssetsLoadSetting assetsLoadSetting)) - { - continue; - } - string assetPathFolder; - if (assetsLoadSetting.EncryptAssets) - { - assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath + "/../", assetLoadTable.EncryptPathFolder, assetsLoadSetting.BuildName); - } - else - { - assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath, assetsLoadSetting.BuildName); - } - string directoryPath = Path.Combine(Application.streamingAssetsPath, assetsLoadSetting.BuildName); - if (!Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - DirectoryInfo subBundlePath = new DirectoryInfo(assetPathFolder); - FileInfo[] fileInfos = subBundlePath.GetFiles(); - foreach (FileInfo fileInfo in fileInfos) - { - if (fileInfo.DirectoryName == null) - { - AssetLogHelper.LogError("找不到文件的路径: " + fileInfo.Name); - continue; - } - string filePath = Path.Combine(fileInfo.DirectoryName, fileInfo.Name); - string suffix = Path.GetExtension(filePath); - if ((!fileInfo.Name.StartsWith("shader_") && string.IsNullOrWhiteSpace(suffix)) || suffix == ".manifest") - { - continue; - } - File.Copy(filePath, Path.Combine(directoryPath, fileInfo.Name)); - } - } - foreach (AssetsSetting assetsSetting in assetLoadTable.AssetsSettings) - { - if (!(assetsSetting is AssetsOriginSetting assetsOriginSetting)) - { - continue; - } - string assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath, assetsOriginSetting.BuildName); - string directoryPath = Path.Combine(Application.streamingAssetsPath, assetsOriginSetting.BuildName); - if (!Directory.Exists(directoryPath)) - { - Directory.CreateDirectory(directoryPath); - } - //获取所有资源目录 - HashSet files = new HashSet(); - HashSet dirs = new HashSet(); - BuildAssetsTools.GetOriginsPath(assetPathFolder, files, dirs); - //Copy资源 - foreach (string dir in dirs) - { - Directory.CreateDirectory(dir.Replace(assetPathFolder, directoryPath)); - } - foreach (string file in files) - { - File.Copy(file, file.Replace(assetPathFolder, directoryPath), true); - } - } - AssetDatabase.Refresh(); - AssetLogHelper.Log("已将资源复制到StreamingAssets"); - } - private static void Build(AssetLoadTable assetLoadTable, AssetsLoadSetting assetsLoadSetting, HashSet assetLoadPath, HashSet alwaysIncludedShaders) { Dictionary loadFileDic = new Dictionary(); diff --git a/Editor/BundleMasterEditor/BundleUsefulTool.cs b/Editor/BundleMasterEditor/BundleUsefulTool.cs new file mode 100644 index 0000000..198485f --- /dev/null +++ b/Editor/BundleMasterEditor/BundleUsefulTool.cs @@ -0,0 +1,120 @@ +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; + +namespace BM +{ + /// + /// 一些打包会用到的实用的功能 + /// + public class BundleUsefulTool : EditorWindow + { + [MenuItem("Tools/BuildAsset/Copy资源到StreamingAssets")] + public static void CopyToStreamingAssets() + { + if (!Directory.Exists(Application.streamingAssetsPath)) + { + Directory.CreateDirectory(Application.streamingAssetsPath); + } + DeleteHelper.DeleteDir(Application.streamingAssetsPath); + AssetLoadTable assetLoadTable = AssetDatabase.LoadAssetAtPath(BundleMasterWindow.AssetLoadTablePath); + foreach (AssetsSetting assetsSetting in assetLoadTable.AssetsSettings) + { + if (!(assetsSetting is AssetsLoadSetting assetsLoadSetting)) + { + continue; + } + string assetPathFolder; + if (assetsLoadSetting.EncryptAssets) + { + assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath + "/../", assetLoadTable.EncryptPathFolder, assetsLoadSetting.BuildName); + } + else + { + assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath, assetsLoadSetting.BuildName); + } + string directoryPath = Path.Combine(Application.streamingAssetsPath, assetsLoadSetting.BuildName); + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + DirectoryInfo subBundlePath = new DirectoryInfo(assetPathFolder); + FileInfo[] fileInfos = subBundlePath.GetFiles(); + foreach (FileInfo fileInfo in fileInfos) + { + if (fileInfo.DirectoryName == null) + { + AssetLogHelper.LogError("找不到文件的路径: " + fileInfo.Name); + continue; + } + string filePath = Path.Combine(fileInfo.DirectoryName, fileInfo.Name); + string suffix = Path.GetExtension(filePath); + if ((!fileInfo.Name.StartsWith("shader_") && string.IsNullOrWhiteSpace(suffix)) || suffix == ".manifest") + { + continue; + } + File.Copy(filePath, Path.Combine(directoryPath, fileInfo.Name)); + } + } + foreach (AssetsSetting assetsSetting in assetLoadTable.AssetsSettings) + { + if (!(assetsSetting is AssetsOriginSetting assetsOriginSetting)) + { + continue; + } + string assetPathFolder = Path.Combine(assetLoadTable.BuildBundlePath, assetsOriginSetting.BuildName); + string directoryPath = Path.Combine(Application.streamingAssetsPath, assetsOriginSetting.BuildName); + if (!Directory.Exists(directoryPath)) + { + Directory.CreateDirectory(directoryPath); + } + //获取所有资源目录 + HashSet files = new HashSet(); + HashSet dirs = new HashSet(); + BuildAssetsTools.GetOriginsPath(assetPathFolder, files, dirs); + //Copy资源 + foreach (string dir in dirs) + { + Directory.CreateDirectory(dir.Replace(assetPathFolder, directoryPath)); + } + foreach (string file in files) + { + File.Copy(file, file.Replace(assetPathFolder, directoryPath), true); + } + } + AssetDatabase.Refresh(); + AssetLogHelper.Log("已将资源复制到StreamingAssets"); + } + + [MenuItem("Tools/BuildAsset/实用工具/清空热更目录下的文件")] + public static void ClearHotfixPathPath() + { + DeleteHelper.DeleteDir(AssetComponentConfig.HotfixPath); + AssetDatabase.Refresh(); + } + + [MenuItem("Tools/BuildAsset/实用工具/清空本地目录下的文件")] + public static void ClearLocalBundlePath() + { + DeleteHelper.DeleteDir(AssetComponentConfig.LocalBundlePath); + AssetDatabase.Refresh(); + } + + [MenuItem("Tools/BuildAsset/实用工具/清空打包目录下的文件")] + public static void ClearLocalBuildPath() + { + AssetLoadTable assetLoadTable = AssetDatabase.LoadAssetAtPath(BundleMasterWindow.AssetLoadTablePath); + DeleteHelper.DeleteDir(assetLoadTable.BundlePath); + } + + [MenuItem("Tools/BuildAsset/实用工具/清空加密目录下的文件")] + public static void ClearLocalEncryptPath() + { + AssetLoadTable assetLoadTable = AssetDatabase.LoadAssetAtPath(BundleMasterWindow.AssetLoadTablePath); + DeleteHelper.DeleteDir(assetLoadTable.EncryptPathFolder); + } + } + + +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/ILiteThreadAction.cs b/LiteMultiThreadDownLoad/ILiteThreadAction.cs new file mode 100644 index 0000000..e54fc29 --- /dev/null +++ b/LiteMultiThreadDownLoad/ILiteThreadAction.cs @@ -0,0 +1,7 @@ +namespace LMTD +{ + public interface ILiteThreadAction + { + public void Logic(); + } +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/LMTDProxy.cs b/LiteMultiThreadDownLoad/LMTDProxy.cs new file mode 100644 index 0000000..7bac72b --- /dev/null +++ b/LiteMultiThreadDownLoad/LMTDProxy.cs @@ -0,0 +1,21 @@ +using System; +using System.Net; + +namespace LMTD +{ + public class LMTDProxy + { + private static readonly object LockObj = new object(); + private static WebProxy _proxy = null; + + public static WebProxy GetProxy() + { + lock (LockObj) + { + _proxy = new WebProxy(); + return _proxy; + } + } + + } +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/LMTDownLoad.cs b/LiteMultiThreadDownLoad/LMTDownLoad.cs new file mode 100644 index 0000000..214f1db --- /dev/null +++ b/LiteMultiThreadDownLoad/LMTDownLoad.cs @@ -0,0 +1,214 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; + +namespace LMTD +{ + public class LMTDownLoad : ILiteThreadAction, IDisposable + { + private static readonly Queue LmtDownLoadQueue = new Queue(); + + private Action _completeCallback; + + /// + /// 下载完成后的回调 + /// + public event Action Completed + { + add => _completeCallback += value; + remove => this._completeCallback -= value; + } + + private Action _upDateInfoCallback; + + /// + /// 下载更新循环 + /// + public event Action UpDateInfo + { + add => _upDateInfoCallback += value; + remove => this._upDateInfoCallback -= value; + } + + /// + /// 创建一个下载器 + /// + public static LMTDownLoad Create(string url, string filePath) + { + LMTDownLoad lmtDownLoad; + lock (LmtDownLoadQueue) + { + if (LmtDownLoadQueue.Count > 0) + { + lmtDownLoad = LmtDownLoadQueue.Dequeue(); + lmtDownLoad.url = url; + lmtDownLoad.filePath = filePath; + } + else + { + lmtDownLoad = new LMTDownLoad(url, filePath); + } + } + lmtDownLoad.CancelLock = false; + return lmtDownLoad; + } + + private LMTDownLoad(string url, string filePath) + { + this.url = url; + this.filePath = filePath; + } + + public bool CancelLock = false; + + /// + /// 下载地址 + /// + private string url = null; + + /// + /// 文件存储路径 + /// + private string filePath = null; + + /// + /// 下载信息 + /// + public LmtDownloadInfo LmtDownloadInfo; + + /// + /// 返回下载文件的信息 + /// + public LmtDownloadInfo DownLoad() + { + //需要返回的数据 + LmtDownloadInfo.DownLoadFileCRC = 0xFFFFFFFF; + LmtDownloadInfo.downLoadSizeValue = 0; + //创建下载请求 + HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); + httpWebRequest.Proxy = LMTDProxy.GetProxy(); + HttpWebResponse httpWebResponse; + try + { + httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); + } + catch + { +#if UNITY_5_3_OR_NEWER + UnityEngine.Debug.LogError("下载资源失败\n" + url + "\n"); +#else + Console.WriteLine("下载失败资源失败\n" + url + "\n"); +#endif + LmtDownloadInfo.LmtDownloadResult = LmtDownloadResult.ResponseFail; + return LmtDownloadInfo; + } + //创建一块存储的大小 1mb + byte[] blockBytes = new byte[1048576]; + using FileStream fileStream = new FileStream(filePath, FileMode.Create); + try + { + //获取文件流 + using Stream receiveStream = httpWebResponse.GetResponseStream(); + // ReSharper disable once PossibleNullReferenceException + int blockSize = receiveStream.Read(blockBytes, 0, blockBytes.Length); + while (blockSize > 0) + { + if (CancelLock) + { + LmtDownloadInfo.LmtDownloadResult = LmtDownloadResult.CancelDownLoad; + return LmtDownloadInfo; + } + //计算CRC + for (uint i = 0; i < blockSize; i++) + { + LmtDownloadInfo.DownLoadFileCRC = (LmtDownloadInfo.DownLoadFileCRC << 8) ^ LmtdTable.CRCTable[(LmtDownloadInfo.DownLoadFileCRC >> 24) ^ blockBytes[i]]; + } + LmtDownloadInfo.downLoadSizeValue += blockSize; + _upDateInfoCallback?.Invoke(); + //循环写入读取数据 + fileStream.Write(blockBytes, 0, blockSize); + blockSize = receiveStream.Read(blockBytes, 0, blockBytes.Length); + } + receiveStream.Close(); + } + catch + { +#if UNITY_5_3_OR_NEWER + UnityEngine.Debug.LogError("下载资源中断\n" + url + "\n"); +#else + Console.WriteLine("下载资源中断\n" + url + "\n"); +#endif + LmtDownloadInfo.LmtDownloadResult = LmtDownloadResult.DownLoadFail; + return LmtDownloadInfo; + } + finally + { + fileStream.Close(); + httpWebResponse.Close(); + httpWebResponse.Dispose(); + } + LmtDownloadInfo.LmtDownloadResult = LmtDownloadResult.Success; + return LmtDownloadInfo; + } + + public void Logic() + { + LmtDownloadInfo lmtDownloadInfo = DownLoad(); + _completeCallback?.Invoke(lmtDownloadInfo); + } + + public void Dispose() + { + CancelLock = false; + url = null; + filePath = null; + _completeCallback = null; + _upDateInfoCallback = null; + lock (LmtDownLoadQueue) + { + LmtDownLoadQueue.Enqueue(this); + } + } + + } + + /// + /// 下载完成后的回调信息 + /// + public struct LmtDownloadInfo + { + public LmtDownloadResult LmtDownloadResult; + public uint DownLoadFileCRC; + + internal long downLoadSizeValue; + /// + /// 已经下载了多少 + /// + public long DownLoadSize + { + get { return downLoadSizeValue; } + } + + } + + public enum LmtDownloadResult + { + /// + /// 成功 + /// + Success = 0, + /// + /// 请求连接失败 + /// + ResponseFail = 1, + /// + /// 下载失败 + /// + DownLoadFail = 2, + /// + /// 取消下载 + /// + CancelDownLoad = 3 + } +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/LiteThread.cs b/LiteMultiThreadDownLoad/LiteThread.cs new file mode 100644 index 0000000..b3401be --- /dev/null +++ b/LiteMultiThreadDownLoad/LiteThread.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace LMTD +{ + internal class LiteThread + { + private readonly Thread thread; + private ILiteThreadAction _liteThreadAction = null; + + internal LiteThread() + { + ThreadFactory.ThreadCount++; + thread = new Thread(Run); + thread.Start(); + } + + internal void Action(ILiteThreadAction liteThreadAction) + { + this._liteThreadAction = liteThreadAction; + } + + private void Run() + { + while (!ThreadFactory.RecoverKey) + { + Thread.Sleep(1); + if (_liteThreadAction != null) + { + _liteThreadAction.Logic(); + _liteThreadAction = null; + lock (ThreadFactory.ThreadPool) + { + //执行完逻辑后自己进池 + ThreadFactory.ThreadPool.Enqueue(this); + } + } + + } + Recovery(); + } + + /// + /// 回收这个线程 + /// + internal void Recovery() + { + ThreadFactory.ThreadCount--; + thread.Abort(); + } + } +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/LmtdTable.cs b/LiteMultiThreadDownLoad/LmtdTable.cs new file mode 100644 index 0000000..475ed38 --- /dev/null +++ b/LiteMultiThreadDownLoad/LmtdTable.cs @@ -0,0 +1,46 @@ +using System; + +namespace LMTD +{ + internal static class LmtdTable + { + /// + /// 注意此Table和多线程下载的Table以及打AssetBundle加密用的Table需要一样,其中在BM的代码里还有一份拷贝 + /// + internal static readonly UInt32[] CRCTable = + { + 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, + 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75, + 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd, + 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, + 0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d, + 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, + 0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072, + 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, + 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, + 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692, + 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, + 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, + 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a, + 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53, + 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b, + 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, + 0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b, + 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, + 0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3, + 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, + 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, + 0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, + 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, + 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, + 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c, + 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 + }; + } +} \ No newline at end of file diff --git a/LiteMultiThreadDownLoad/ThreadFactory.cs b/LiteMultiThreadDownLoad/ThreadFactory.cs new file mode 100644 index 0000000..7d6cccc --- /dev/null +++ b/LiteMultiThreadDownLoad/ThreadFactory.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace LMTD +{ + public static class ThreadFactory + { + /// + /// 所有线程的数量 + /// + internal static uint ThreadCount = 0; + + /// + /// 所有线程的池 + /// + internal static readonly Queue ThreadPool = new Queue(); + + private static readonly HashSet AllLiteThread = new HashSet(); + + /// + /// 是否开启回收进程,默认不开启 + /// + internal static bool RecoverKey + { + set + { + if (value == false) + { + //说明所有进程都已经被回收 + } + } + get => recoverKey; + } + private static bool recoverKey = false; + + /// + /// 生命周期 + /// + private static Thread _threadFactoryLife = null; + + /// + /// 执行一个逻辑 + /// + public static void ThreadAction(ILiteThreadAction liteThreadAction) + { + if (_threadFactoryLife == null) + { + _threadFactoryLife = new Thread(ThreadUpdate); + _threadFactoryLife.Start(); + } + LiteThread liteThread; + lock (ThreadPool) + { + if (ThreadPool.Count > 0) + { + liteThread = ThreadPool.Dequeue(); + } + else + { + liteThread = new LiteThread(); + AllLiteThread.Add(liteThread); + } + } + AllLiteThread.Add(liteThread); + liteThread.Action(liteThreadAction); + } + + + private static long _lastTime = 0; + /// + /// 线程池清空标志位(如果5-10秒内池有多余的线程线程就清空) + /// + private static bool _poolClear = false; + /// + /// 线程自动回收机制 + /// + private static void ThreadUpdate() + { + _lastTime = DateTime.Now.Ticks; + while (true) + { + long nowTime = DateTime.Now.Ticks; + if (nowTime - _lastTime > 50000000) + { + _lastTime = nowTime; + //每隔5s一次循环 + lock (ThreadPool) + { + if (ThreadPool.Count == 0) + { + continue; + } + if (!_poolClear) + { + _poolClear = true; + continue; + } + LiteThread liteThread = ThreadPool.Dequeue(); + AllLiteThread.Remove(liteThread); + liteThread.Recovery(); + _poolClear = false; + if (ThreadCount == 0) + { + _lastTime = 0; + _threadFactoryLife?.Abort(); + _threadFactoryLife = null; + } + } + } + Thread.Sleep(1); + } + } + + /// + /// 线程池销毁 + /// + public static void Destroy() + { + foreach (LiteThread liteThread in AllLiteThread) + { + liteThread.Recovery(); + } + AllLiteThread.Clear(); + lock (ThreadPool) + { + ThreadPool.Clear(); + } + ThreadCount = 0; + _lastTime = 0; + _poolClear = false; + _threadFactoryLife?.Abort(); + _threadFactoryLife = null; + } + + } +} \ No newline at end of file