123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- namespace assetBundleUtility
- {
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Text;
- using UnityEngine;
-
- public class AssetBundleUtility : MonoBehaviour
- {
- #region Config
-
- public static AssetBundleUtility Instance
- {
- get
- {
- if (instance == null)
- {
- GameObject go = new GameObject("AssetBundleUtility");
- DontDestroyOnLoad(go);
- instance = go.AddComponent<AssetBundleUtility>();
- }
- return instance;
- }
- }
- public static AssetBundleUtility instance;
- private static string FTPPrefix = "file://";
- #if UNITY_EDITOR
- public List<AssetBundleGroup> AssetBundleGroups;
- #endif
- #endregion
- public static string GetMD5OfAssetBundleSets(List<AssetBundleSet> assetBundleSets)
- {
- string md5Dictionary = CreateMD5Dictionary(assetBundleSets, false);
- byte[] bytes = MD5Utility.GetMD5(md5Dictionary);
- string md5 = MD5Utility.BytesToString(bytes);
- return md5;
- }
- public static string CreateMD5Dictionary(List<AssetBundleSet> assetBundleSets, bool includeURL)
- {
- StringBuilder stringBuilder = new StringBuilder();
- for (int i = 0; i < assetBundleSets.Count; i++)
- {
- stringBuilder.Append($"{assetBundleSets[i].Name}|{assetBundleSets[i].MD5}");
- if (includeURL)
- {
- stringBuilder.Append($"|{assetBundleSets[i].URL}");
- }
- if (i < assetBundleSets.Count - 1)
- {
- stringBuilder.Append("\r\n");
- }
- }
- return stringBuilder.ToString();
- }
- private static List<AssetBundleSet> ParseMD5Dictionary(string md5Dictionary)
- {
- List<AssetBundleSet> assetBundleSets = new List<AssetBundleSet>();
- List<string> items = md5Dictionary.Trim().Split(new[] { "\r\n" }, StringSplitOptions.None).ToList();
- for (int i = 0; i < items.Count; i++)
- {
- AssetBundleSet assetBundleSet = new AssetBundleSet();
- string[] strings = items[i].Split('|');
- assetBundleSet.Name = strings[0];
- assetBundleSet.MD5 = strings[1];
- assetBundleSet.URL = strings[2];
- assetBundleSets.Add(assetBundleSet);
- }
- return assetBundleSets;
- }
- /// <summary>
- ///加载AssetBundle
- /// </summary>
- /// <param name="allSucceedCallback">所有AssetBundle都加载成功后的回调</param>
- /// <param name="haveFailedCallback">任意一个AssetBundle加载失败后的回调 如果有多个AssetBundle加载失败 也只回调一次</param>
- /// <param name="getSucceedCallback">每个AssetBundle加载成功后的回调</param>
- /// <param name="getFailedCallback">每个AssetBundle加载失败后的回调</param>
- public static void LoadAllAssetBundle(List<AssetBundleSet> assetBundleSets, Action allSucceedCallback, Action haveFailedCallback, Action<AssetBundleSet> getSucceedCallback, Action<AssetBundleSet, string> getFailedCallback)
- {
- List<AssetBundleSet> staleAssetBundleSets = new List<AssetBundleSet>();
- foreach (var assetBundleSet in assetBundleSets)
- {
- assetBundleSet.GetSucceedCallback = getSucceedCallback;
- assetBundleSet.GetFailedCallback = getFailedCallback;
- //Debug.Log($"UpToDateAssetBundleSet : {assetBundleSet.Name}");
- }
- LoadAndDownloadAssetBundles(staleAssetBundleSets, assetBundleSets, allSucceedCallback, haveFailedCallback);
- }
- /// <summary>
- ///加载/下载AssetBundle
- /// </summary>
- /// <param name="persistentFolder">下载后AssetBundle存放的目录</param>
- /// <param name="allSucceedCallback">所有AssetBundle都加载/下载成功后的回调</param>
- /// <param name="haveFailedCallback">任意一个AssetBundle加载/下载失败后的回调 如果有多个AssetBundle加载/下载失败 也只回调一次</param>
- /// <param name="getSucceedCallback">每个AssetBundle加载/下载成功后的回调</param>
- /// <param name="getFailedCallback">每个AssetBundle加载/下载失败后的回调</param>
- public static void UpdateAllAssetBundle(string md5Dictionary, string persistentFolder, List<AssetBundleSet> assetBundleSets, Action allSucceedCallback, Action haveFailedCallback, Action<AssetBundleSet> getSucceedCallback, Action<AssetBundleSet, string> getFailedCallback)
- {
- List<AssetBundleSet> staleAssetBundleSets = new List<AssetBundleSet>();
- List<AssetBundleSet> upToDateAssetBundleSets = new List<AssetBundleSet>();
- GetStaleAndUpToDateAssetBundleSets(md5Dictionary, persistentFolder, assetBundleSets, staleAssetBundleSets, upToDateAssetBundleSets);
- foreach (var staleAssetBundleSet in staleAssetBundleSets)
- {
- staleAssetBundleSet.GetSucceedCallback = getSucceedCallback;
- staleAssetBundleSet.GetFailedCallback = getFailedCallback;
- //Debug.Log($"StaleAssetBundleSet : {staleAssetBundleSet.Name}");
- }
- foreach (var upToDateAssetBundleSet in upToDateAssetBundleSets)
- {
- upToDateAssetBundleSet.GetSucceedCallback = getSucceedCallback;
- upToDateAssetBundleSet.GetFailedCallback = getFailedCallback;
- //Debug.Log($"UpToDateAssetBundleSet : {upToDateAssetBundleSet.Name}");
- }
- LoadAndDownloadAssetBundles(staleAssetBundleSets, upToDateAssetBundleSets, allSucceedCallback, haveFailedCallback);
- }
- private static void GetStaleAndUpToDateAssetBundleSets(string md5Dictionary, string persistentFolder, List<AssetBundleSet> assetBundleSets, List<AssetBundleSet> staleAssetBundleSets, List<AssetBundleSet> upToDateAssetBundleSets)
- {
- List<AssetBundleSet> serverAssetBundleSets = ParseMD5Dictionary(md5Dictionary);
- foreach (var serverAssetBundleSet in serverAssetBundleSets)
- {
- bool missingFlag = true;
- foreach (var assetBundleSet in assetBundleSets)
- {
- if (assetBundleSet.Name != serverAssetBundleSet.Name) continue;
- if (assetBundleSet.MD5 == serverAssetBundleSet.MD5)
- {
- missingFlag = false;
- upToDateAssetBundleSets.Add(assetBundleSet);
- break;
- }
- else
- {
- assetBundleSet.URL = serverAssetBundleSet.URL;
- staleAssetBundleSets.Add(assetBundleSet);
- missingFlag = false;
- break;
- }
- }
- if (missingFlag)
- {
- serverAssetBundleSet.PersistentPath = $"{persistentFolder}{Path.DirectorySeparatorChar}{serverAssetBundleSet.Name}";
- staleAssetBundleSets.Add(serverAssetBundleSet);
- }
- }
- }
- private static void LoadAndDownloadAssetBundles(List<AssetBundleSet> staleAssetBundleSets, List<AssetBundleSet> upToDateAssetBundleSets, Action allSucceedCallback, Action haveFailedCallback)
- {
- int remainAmount = upToDateAssetBundleSets.Count + staleAssetBundleSets.Count;
- bool failedFlag = false;
- foreach (var assetBundleSets in upToDateAssetBundleSets)
- {
- LoadBundle
- (
- assetBundleSets,
- () => LoadAndDownloadAssetBundleSucceedCallback(ref remainAmount, allSucceedCallback),
- () => LoadAndDownloadAssetBundleFailedCallback(ref failedFlag, haveFailedCallback)
- );
- }
- foreach (var assetBundleSets in staleAssetBundleSets)
- {
- DownloadBundle
- (
- assetBundleSets,
- () => LoadAndDownloadAssetBundleSucceedCallback(ref remainAmount, allSucceedCallback),
- () => LoadAndDownloadAssetBundleFailedCallback(ref failedFlag, haveFailedCallback)
- );
- }
- }
- private static void LoadAndDownloadAssetBundleSucceedCallback(ref int remainAmount, Action allSucceedCallback)
- {
- if (--remainAmount == 0) allSucceedCallback.Invoke();
- }
- private static void LoadAndDownloadAssetBundleFailedCallback(ref bool failedFlag, Action haveFailedCallback)
- {
- if (failedFlag) return;
- failedFlag = true;
- haveFailedCallback.Invoke();
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="persistentFolder">更新后AssetBundle存放的目录</param>
- /// <param name="streamingFolder">源AssetBundle存放的目录</param>
- /// <returns></returns>
- public static List<AssetBundleSet> GetAssetBundleSetsFromFolders(string persistentFolder/*, string streamingFolder*/)
- {
- List<AssetBundleSet> persistentAssetBundleSets = new List<AssetBundleSet>();
- if (Directory.Exists(persistentFolder))
- {
- persistentAssetBundleSets.AddRange(GetAllAssetBundleSetFromFolder(persistentFolder, persistentFolder));
- }
- //List<AssetBundleSet> streamingAssetBundleSets = new List<AssetBundleSet>();
- //streamingAssetBundleSets.AddRange(GetAllAssetBundleSetFromFolder(streamingFolder, persistentFolder));
- // for (int i = 0; i < streamingAssetBundleSets.Count; i++)
- //{
- // foreach (var persistentAssetBundleSet in persistentAssetBundleSets)
- // {
- // if (streamingAssetBundleSets[i].Name != persistentAssetBundleSet.Name) continue;
- // streamingAssetBundleSets.RemoveAt(i--);
- // break;
- // }
- //}
- List<AssetBundleSet> assetBundleSets = new List<AssetBundleSet>();
- //assetBundleSets.AddRange(streamingAssetBundleSets);
- assetBundleSets.AddRange(persistentAssetBundleSets);
- for (int i = 0; i < assetBundleSets.Count; i++)
- {
- assetBundleSets[i].StreamingPath = assetBundleSets[i].StreamingPath.Replace("\\", Path.DirectorySeparatorChar.ToString()).Replace("/", Path.DirectorySeparatorChar.ToString());
- }
- assetBundleSets.MySort((bundleSet0, bundleSet1) => SortExtension.CompareASCII(bundleSet1.Name, bundleSet0.Name));
- return assetBundleSets;
- }
- /// <summary>
- ///
- /// </summary>
- /// <param name="folder">源AssetBundle存放的目录</param>
- /// <param name="persistentFolder">更新后AssetBundle存放的目录</param>
- /// <returns></returns>
- private static List<AssetBundleSet> GetAllAssetBundleSetFromFolder(string folder, string persistentFolder)
- {
- List<string> pathes = new List<string>();
- List<string> names = new List<string>();
- List<string> md5s = new List<string>();
- List<string> savePathes = new List<string>();
- pathes.AddRange(Directory.GetFiles(folder, "*", SearchOption.AllDirectories));
- RemoveMetaFilePath(pathes);
- RemoveManifestFilePath(pathes);
- names = pathes.GetAllFileNameFromPath();
- md5s = MD5Utility.GetAllMD5StringFromPath(pathes);
- savePathes = pathes.ReplaceAll(folder, persistentFolder);
-
- for (int i = 0; i < pathes.Count; i++)
- {
- pathes[i] = pathes[i].Replace("\\", Path.DirectorySeparatorChar.ToString()).Replace("/", Path.DirectorySeparatorChar.ToString());
- }
-
- List<AssetBundleSet> results = new List<AssetBundleSet>();
- for (int i = 0; i < names.Count; i++)
- {
- AssetBundleSet assetBundleSet = new AssetBundleSet();
- assetBundleSet.StreamingPath = pathes[i];
- assetBundleSet.Name = names[i];
- assetBundleSet.MD5 = md5s[i];
- assetBundleSet.PersistentPath = savePathes[i];
- results.Add(assetBundleSet);
- }
- return results;
- }
-
- private static void RemoveMetaFilePath(List<string> pathes)
- {
- for (int i = 0; i < pathes.Count; i++)
- {
- if (pathes[i].EndsWith(".meta"))
- {
- pathes.RemoveAt(i--);
- }
- }
- }
- private static void RemoveManifestFilePath(List<string> pathes)
- {
- for (int i = 0; i < pathes.Count; i++)
- {
- if (pathes[i].EndsWith(".manifest"))
- {
- pathes.RemoveAt(i--);
- }
- }
- }
- public static void LoadBundle(AssetBundleSet assetBundleSet, Action succeedCallback, Action failedCallback)
- {
- Instance.StartCoroutine(loadBundle(assetBundleSet, succeedCallback, failedCallback));
- }
- public static void DownloadBundle(AssetBundleSet assetBundleSet, Action succeedCallback, Action failedCallback)
- {
- Instance.StartCoroutine(downloadBundle(assetBundleSet, succeedCallback, failedCallback));
- }
- private static IEnumerator loadBundle(AssetBundleSet assetBundleSet, Action succeedCallback, Action failedCallback)
- {
- WWW www = new WWW(FTPPrefix + assetBundleSet.StreamingPath);
- yield return www;
- if (!string.IsNullOrEmpty(www.error))
- {
- assetBundleSet.GetFailedCallback.Invoke(assetBundleSet, www.error);
- failedCallback.Invoke();
- yield break;
- }
- assetBundleSet.AssetBundle = www.assetBundle;
- assetBundleSet.GetSucceedCallback.Invoke(assetBundleSet);
- succeedCallback.Invoke();
- }
-
- private static IEnumerator downloadBundle(AssetBundleSet assetBundleSet, Action succeedCallback, Action failedCallback)
- {
- WWW www = new WWW(assetBundleSet.URL);
- yield return www;
- if (!string.IsNullOrEmpty(www.error))
- {
- assetBundleSet.GetFailedCallback.Invoke(assetBundleSet, www.error);
- failedCallback.Invoke();
- yield break;
- }
- if (!Directory.Exists(assetBundleSet.PersistentPath))
- {
- Directory.CreateDirectory(Path.GetDirectoryName(assetBundleSet.PersistentPath));
- }
- File.WriteAllBytes(assetBundleSet.PersistentPath, www.bytes);
- assetBundleSet.AssetBundle = www.assetBundle;
- assetBundleSet.GetSucceedCallback.Invoke(assetBundleSet);
- succeedCallback.Invoke();
- }
- }
- }
|