using Game.Config; using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text; using UnityEngine; using UnityEngine.Networking; class LocalFilePathInfo { public string Path; /// /// 是否可读可写文件夹 /// public bool IsLocalRW; public LocalFilePathInfo() { Path = ""; IsLocalRW = false; } public LocalFilePathInfo(string path,bool rw) { Path = path; IsLocalRW = rw; } } public class DownloadMgr : Singleton { public const int TimeOut = 60; private string downloadBaseUrl; private string downloadUrl; public string DownloadUrl { get => downloadUrl; } private int downloadTaskMaxNum; public int DownloadTaskMaxNum { get => downloadTaskMaxNum; } private string localFilePath; public string LocalFilePath { get => localFilePath; } private List needDownloadList; private Dictionary downloadingList; private List downloadSucessList; private List remoteResList; private Dictionary localResMap; private Dictionary remoteResMap; private bool isInited = false; private GameDataFormatInfo downloadFormatInfo; private string versionFileName; private string assetsFileName; private string downloadedAssetsFileName; private ulong downloadSize; private ulong downloadingSize; public ulong DownloadSize { get => downloadSize + GetDownloadingSize(); } private ulong totalSize;//总大小 public ulong TotalSize { get => totalSize; } public bool CheckFinish { get;private set; } public bool DowmloadError { get; private set; } //private byte[] versionData; //private byte[] mainfestData; private string versionData; //private UnityWebRequest mainfestData; private int tryGetVersionCount; public bool isQiangDownload; public string ResVersion { get; private set; } public override void Init() { InitField(); } private ulong GetDownloadingSize() { downloadingSize = 0; foreach (var item in downloadingList) { downloadingSize += item.Value; } return downloadingSize; } private string GetIOSDownloadPath() { return "res/IosRes/"; //#if GAME_DEBUG //#else //#endif } private string GetAndroidDownloadPath() { #if GAME_ONE return "res/WDAndroidRes/"; #elif GAME_DEBUG return "res/TestServerRes/"; #else return "res/WDAndroidRes/"; #endif } private string GetDownloadBaseUrl() { #if GAME_ONE return "http://101.43.46.101:88/"; #elif CN_I7GAME_ZHUAN return "http://43.248.186.74:88/"; #elif CN_I7GAME_0_1 return "http://103.239.245.64:88/"; // "http://weix.vvfyj.cn/";// #elif GAME_DEBUG return "http://110.40.223.119:88/"; #else #if UNITY_EDITOR return "http://43.248.187.68:88/"; #else return "http://cdn.yishanyou.com:88/"; #endif #endif } private string GetBaseUrl() { #if GAME_ONE return "";//"http://101.43.46.101:81/"; #elif CN_I7GAME_ZHUAN return "http://43.248.186.74:81/"; #elif CN_I7GAME_0_1 return "http://103.239.245.64:81/"; #elif GAME_DEBUG return "";//"http://110.40.223.119:81/"; #else #if UNITY_EDITOR return "";//"http://43.248.187.68:81/"; #else return ""//"http://cdn.yishanyou.com:81/"; #endif #endif } private string GetDownloadUrl() { #if UNITY_IOS return GetDownloadBaseUrl() + GetIOSDownloadPath(); #else return GetDownloadBaseUrl() + GetAndroidDownloadPath(); #endif } private string GetDownloadUrlFormServer(string baseUrl) { #if UNITY_IOS return baseUrl + GetIOSDownloadPath(); #else return baseUrl + GetAndroidDownloadPath(); #endif } private void InitField() { if (!isInited) { downloadUrl = GetDownloadUrl(); downloadTaskMaxNum = 5; localFilePath = GetLocalResPath();//FileSystem.LocalDocumentPath; versionFileName = "Version"; assetsFileName = "mainfest"; downloadedAssetsFileName = "downloaded"; needDownloadList = new List(100); downloadSucessList = new List(100); downloadingList = new Dictionary(downloadTaskMaxNum); remoteResList = new List(1024); localResMap = new Dictionary(1024); remoteResMap = new Dictionary(1024); isInited = true; CheckFinish = false; DowmloadError = false; downloadSize = 0; downloadingSize = 0; totalSize = 0; tryGetVersionCount = 0; isQiangDownload = true; ResVersion = "0.0.0.0"; } } // private string GetPlatform() { #if UNITY_EDITOR return "IOS";// "PC"; #else #if UNITY_IOS return "IOS";// #else return "Android";// #endif #endif } public void CheckVersion() { string baseUrl = GetBaseUrl(); if (!string.IsNullOrEmpty(baseUrl) && tryGetVersionCount <= 3) { baseUrl += $"serverlist/download?platform={GetPlatform()}"; AssetDownloader.Instance.DownLoadFileByCoroutine(baseUrl,webReq=> { if (webReq == null) { CheckFinish = true; DowmloadError = true; return; } string url = webReq.downloadHandler.text; url = url.Substring(1, url.Length - 2); if (url.IndexOf("http://") < 0) { url = "http://"+ url; } Debug.Log("请求得到:" + url); downloadUrl = GetDownloadUrlFormServer(url + "/"); StartCheck(); }); } else { downloadUrl = GetDownloadUrl(); StartCheck(); } } private void StartCheck() { ReadDataFromFile(versionFileName, lVdatas => { if (lVdatas!= null) { ResVersion = Encoding.UTF8.GetString(lVdatas); } AssetDownloader.Instance.DownLoadFileByCoroutine(GetRemoteUrl(versionFileName), webRqst => { tryGetVersionCount++; if (webRqst == null) { SetCheckFinishAddDownError(); return; } byte[] data = webRqst.downloadHandler.data; string version = Encoding.UTF8.GetString(data); Debug.Log("服务器 version = " + version); versionData = version; OnloadedLocalVersion(lVdatas, version); }); }, true); } public void DownLoadRemoteRes() { int size = needDownloadList.Count; int downsize = downloadSucessList.Count; downloadSize = 0; totalSize = 0; for (int i = 0; i < size; i++) { if (needDownloadList[i].FullName == "Version") { needDownloadList[i].State = DownloadTaskState.DownloadSuccess; continue; } totalSize += needDownloadList[i].Size; DownloadDataEntity entity = downloadSucessList.FindFirst(it=>it.FullName == needDownloadList[i].FullName); if (downsize > 0 && entity != null && entity.MD5 == needDownloadList[i].MD5) { needDownloadList[i].State = DownloadTaskState.DownloadSuccess; downloadSize += needDownloadList[i].Size; } else { needDownloadList[i].Callback = DownloadedCallback; needDownloadList[i].UpDateCallback = DownloadedUpdateCallback; AssetDownloader.Instance.AddDownloadTask(needDownloadList[i]); } } } public bool DownLoadFinish() { bool ret = true; int size = needDownloadList.Count; for (int i = 0; i < size; i++) { if (needDownloadList[i].State != DownloadTaskState.DownloadSuccess) { ret = false; } } return ret || DowmloadError || !AssetDownloader.Instance.HasTask(); } public bool CheckNeedDownload() { return downloadSize < totalSize; } private void DownloadedUpdateCallback(DownloadTask task) { if (task == null) { // DowmloadError = true; //AssetDownloader.Instance.CancelDownLoad = true; return; } Log($"下载:{task.downloadSize}/{task.CurDownloadEntity.Size} 进度:{task.CurLoadProgress*100}%"); //downloadingSize = task.downloadSize; if (!downloadingList.ContainsKey(task.CurDownloadEntity)) { downloadingList.Add(task.CurDownloadEntity, task.downloadSize); } else { downloadingList[task.CurDownloadEntity] = task.downloadSize; } } public void Log(string str) { #if UNITY_EDITOR Debug.Log(str); #endif } private void DownloadedCallback(DownloadTask task) { if (task == null || task.State == DownloadTaskState.Error) { //DowmloadError = true; //AssetDownloader.Instance.CancelDownLoad = true; if (task != null &&task.CurDownloadEntity.DownloadErrCount >= 3) { task.CurDownloadEntity.Url = GetDownloadUrl() + task.CurDownloadEntity.FullName; Log("重新设置Url = " + task.CurDownloadEntity.Url); } else if (task != null && task.CurDownloadEntity.DownloadErrCount >= 10) { DowmloadError = true; } return; } DownloadDataEntity entity = downloadSucessList.FindFirst(it => it.FullName == task.CurDownloadEntity.FullName); if (entity != null) { entity.MD5 = task.CurDownloadEntity.MD5; entity.Size = task.CurDownloadEntity.Size; } else { downloadSucessList.Add(task.CurDownloadEntity); } if (downloadingList.ContainsKey(task.CurDownloadEntity)) { downloadingList.Remove(task.CurDownloadEntity); } downloadingSize = 0; downloadSize += task.downloadSize; string savepath = localFilePath + downloadedAssetsFileName; CsvWriter csvWriter = new CsvWriter(savepath, "", downloadSucessList, downloadFormatInfo); csvWriter.Write(); } private List SerizlizeResList(byte[] data) { CsvReader csvReader = new CsvReader(FileHelper.GetName(assetsFileName), data); if (downloadFormatInfo == null) { downloadFormatInfo = new GameDataFormatInfo(csvReader.Fields(), csvReader.Types()); } Type type = typeof(DownloadDataEntity); MethodInfo methodInfo = type.GetMethod("OnCsvLoad", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); methodInfo?.Invoke(null, new object[] { csvReader }); List resList = DownloadDataEntity.AllData(); DownloadDataEntity.Clear(); return resList; } private void GetRemoteResList() { string remoteversionpath = downloadUrl + assetsFileName; AssetDownloader.Instance.DownLoadFileByCoroutine(remoteversionpath, webRqst => { if (webRqst == null) { SetCheckFinishAddDownError(); return; } byte[] data = webRqst.downloadHandler.data; // mainfestData = new byte[data.Length]; // data.CopyTo(mainfestData,0); //mainfestData = webRqst; List reResList = SerizlizeResList(data); if (reResList != null) { OnRomoteListDownloaded(reResList); } }); } private void OnRomoteListDownloaded(List downloadDatas) { remoteResList.AddRange(downloadDatas); int size = downloadDatas.Count; for (int i = 0; i < size; i++) { remoteResMap.Add(downloadDatas[i].FullName, downloadDatas[i]); } //#if UNITY_IOS // ReadDataFromFile(assetsFileName, OnloadedLocalResList, false, 1); //#else // ReadDataFromFile(assetsFileName, OnloadedLocalResList); //#endif ReadDataFromFile(assetsFileName, OnloadedLocalResList); } //uint major //uint minor //uint release //uint patch private void SetCheckFinish() { versionData = null; CheckFinish = true; } private void SetCheckFinishAddDownError() { if (isQiangDownload && tryGetVersionCount <= 10) { CheckVersion(); } else { CheckFinish = true; DowmloadError = true; } } private string GetLocalResPath() { string localPath = FileSystem.LocalDocumentPath; //#if UNITY_IOS // localPath = "file://" + localPath; //#endif return localPath; } private string GetStreamResPath() { string streamPath = FileSystem.LocalPackagePath; #if UNITY_IOS streamPath = "file://" + streamPath; #endif return streamPath; } private void OnloadedLocalVersion(byte[] datas, string version) { string lVersionstr = "0.0.0.0"; if (datas != null) { lVersionstr = Encoding.UTF8.GetString(datas); } VersionCode lVersionCode = lVersionstr;//本地 local version VersionCode RVersionCode = version;// 远端 version string streamPath = GetStreamResPath(); AssetDownloader.Instance.AssetLoadByCorutine(streamPath + versionFileName, filedatas => { if (filedatas == null) { GetRemoteResList(); Debug.Log("====== SVersionstr 为空 ====="); return; } VersionCode SVersionCode;// stream data version string SVersionstr = Encoding.UTF8.GetString(filedatas); SVersionCode = SVersionstr; VersionCode localMaxCode = lVersionCode > SVersionCode ? lVersionCode : SVersionCode; ResVersion = localMaxCode; if (localMaxCode == RVersionCode) { Debug.Log($"lVersionstr = {lVersionstr} SVersionstr = {SVersionstr} ===== version = {version} 不需要更新"); SetCheckFinish(); return; } if (localMaxCode >= RVersionCode && SVersionCode == localMaxCode) { Debug.Log($"lVersionstr = {lVersionstr} SVersionstr = {SVersionstr} ===== version = {version} 不需要跟新 需要删除之前下载资源"); versionData = null; ReadDataFromFile(downloadedAssetsFileName, dlocaldatas => { if (dlocaldatas != null) { List localdownloadedList = SerizlizeResList(dlocaldatas); if (localdownloadedList != null && localdownloadedList.Count > 0) { downloadSucessList.AddRange(localdownloadedList); } DeleteDownloadFiles(); } else { SetCheckFinish(); } },true); } else { Debug.Log($"lVersionstr = {lVersionstr} SVersionstr = {SVersionstr} ===== version = {version} 需要更新"); GetRemoteResList(); //CheckFinish = true; } }); } private void OnloadedVersion(byte[] datas,string version) { if (datas == null) { GetRemoteResList(); Debug.Log("====== data 为空 ====="); } else { string lVersionstr = Encoding.UTF8.GetString(datas); Debug.Log($"lVersionstr = {lVersionstr} ===== version = {version} "); if (lVersionstr == version) { Debug.Log($"lVersionstr = {lVersionstr} ===== version = {version} 不需要更新"); SetCheckFinish(); return; } else { VersionCode lVersionCode = lVersionstr;//本地 local version VersionCode RVersionCode = version;// 远端 version if (RVersionCode < lVersionCode) { SetCheckFinish(); } else if (RVersionCode.major > lVersionCode.major || (RVersionCode.major == lVersionCode.major && RVersionCode.minor > lVersionCode.minor)) { AssetDownloader.Instance.AssetLoadByCorutine(GetStreamResPath() + versionFileName, filedatas => { VersionCode SVersionCode;// stream data version string SVersionstr = Encoding.UTF8.GetString(filedatas); SVersionCode = SVersionstr; if (SVersionCode >= RVersionCode && SVersionCode >= lVersionCode) { Debug.Log($"lVersionstr = {SVersionstr} ===== version = {version} 重新安装后需要删除之前资源"); versionData = null; ReadDataFromFile(downloadedAssetsFileName, dlocaldatas=> { if (dlocaldatas != null) { List localdownloadedList = SerizlizeResList(dlocaldatas); if (localdownloadedList != null && localdownloadedList.Count > 0) { downloadSucessList.AddRange(localdownloadedList); } DeleteDownloadFiles(); } }); } else { Debug.Log($"lVersionstr = {lVersionstr} ===== version = {version} 需要更新"); GetRemoteResList(); //CheckFinish = true; } }); } else if (RVersionCode.release > lVersionCode.release || (RVersionCode.release == lVersionCode.release && RVersionCode.patch > lVersionCode.patch)) { Debug.Log($"lVersionstr = {lVersionstr} ===== version = {version} 需要更新"); GetRemoteResList(); } else { SetCheckFinish(); } } } } private void OnloadedLocalResList(byte[] datas) { if (datas == null) { } else { List localList = SerizlizeResList(datas); if (localList != null) { int size = localList.Count; for (int i = 0; i < size; i++) { localResMap.Add(localList[i].FullName, localList[i]); } } } ReadDataFromFile(downloadedAssetsFileName, OnloadedDownloadResList); } private void OnloadedDownloadResList(byte[] datas) { if (datas != null) { List localdownloadedList = SerizlizeResList(datas); if (localdownloadedList != null && localdownloadedList.Count > 0) { downloadSucessList.AddRange(localdownloadedList); } } GetNeedDownloadlist(); DownLoadRemoteRes(); } private List GetLoacalResList(string path) { List resList = null; if (System.IO.File.Exists(path)) { byte[] data = System.IO.File.ReadAllBytes(path); resList = SerizlizeResList(data); } return resList; } private void GetNeedDownloadlist() { string ver = "Version"; if (localResMap.ContainsKey(ver) && remoteResMap.ContainsKey(ver)&& localResMap[ver].MD5 == remoteResMap[ver].MD5) { Debug.Log("版本为最新状态"); SetCheckFinish(); //string savepath = localFilePath + downloadedAssetsFileName; //FileHelper.DeleteFile(savepath); return; } if (localResMap == null || localResMap.Count <= 0) { foreach (var item in remoteResMap) { needDownloadList.Add(item.Value); } } else { foreach (var item in remoteResMap) { if (item.Key == ver) { continue; } if (!localResMap.ContainsKey(item.Key) || localResMap[item.Key].MD5 != item.Value.MD5) { Debug.Log($"需跟新文件:{item.Value.FullName}"); needDownloadList.Add(item.Value); } } } CheckFinish = true; } private string GetRemoteUrl( string name) { return downloadUrl + name; } private LocalFilePathInfo GetLocalFilePath(string name, bool onlyLocal = false, uint streamPerPath = 0) { LocalFilePathInfo ret = new LocalFilePathInfo(); string path = LocalFilePath + name; ret.IsLocalRW = true; if (!FileSystem.Exists(path)) { Debug.Log($" ========= LocalFilePath Path = {path} 不存在"); if (!onlyLocal) { string lpPath = GetStreamResPath(); for (int i = 0; i < streamPerPath; i++) { lpPath += "../"; } path = lpPath + name; } ret.IsLocalRW = false; } ret.Path = path; Debug.Log($" ========= local Path = {path} "); return ret; } private void ReadDataFromFile(string name,Action callback,bool onlyLocal = false,uint streamPerPath = 0) { LocalFilePathInfo dlocalPath = GetLocalFilePath(name,onlyLocal, streamPerPath); if (dlocalPath.IsLocalRW) { try { byte[] dlocaldatas = File.ReadAllBytes(dlocalPath.Path); callback?.Invoke(dlocaldatas); } catch { Debug.Log("读取失败" + dlocalPath.Path); callback?.Invoke(null); } } else if(onlyLocal) { callback?.Invoke(null); } else { AssetDownloader.Instance.AssetLoadByCorutine(dlocalPath.Path, callback); } } private IEnumerator DeleteFiles() { ; int length = downloadSucessList.Count; string savepath = null; for (int i = 0; i < length; i++) { if (downloadSucessList[i].FullName == "font.unity3d") { Debug.Log("跳过删除"); continue; } savepath = localFilePath + downloadSucessList[i].FullName; Debug.Log(savepath); FileHelper.DeleteFile(savepath); yield return null; } FileHelper.DeleteFile(localFilePath + downloadedAssetsFileName); yield return null; FileHelper.DeleteFile(localFilePath + versionFileName); yield return null; FileHelper.DeleteFile(LocalFilePath + assetsFileName); yield return null; SetCheckFinish(); } public void DeleteDownloadFiles() { AssetDownloader.Instance.DoByCorutine(DeleteFiles); } public void Free() { if (!DowmloadError) { if (!string.IsNullOrEmpty(versionData)) { FileHelper.WirteStringToFile(localFilePath + versionFileName, versionData); ResVersion = versionData; } if (remoteResList.Count > 0) { CsvWriter csvWriter = new CsvWriter(LocalFilePath + assetsFileName, "", remoteResList, downloadFormatInfo); csvWriter.Write(); } } needDownloadList = null; downloadSucessList = null; remoteResList = null; localResMap = null; remoteResMap = null; versionData = null; //mainfestData = null; AssetDownloader.Instance.ClearTasks(); } }