using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using GameCore; using System.IO; using GameLogic; using UnityEngine.Networking; namespace ResUpdate { public class VersionPar { public string version { get; set; } public string sdkLodingUrl { get; set; } } /// /// 资源更新进度 /// public class ResUpdateProgress { /// /// 下载成功数量 /// public int SuccessNum { get; set; } /// /// 下载失败数量 /// public int FailedNum { get; set; } /// /// 所有需要下载的数量 /// public int TotalNum { get; set; } /// /// 已经下载完成的Size:Byte /// public long Size { get; set; } /// /// 已经下载完成的Size:KB /// public float SizeKB { get { return 1f * Size / 1024; } } /// /// 已经下载完成的Size:MB /// public float SizeMB { get { return 1f * Size / 1024 / 1024; } } /// /// 所有需要下载的Size:Byte /// public long TotalSize { get; set; } /// /// 所有需要下载的Size:KB /// public float totalSizeKB { get { return 1f *TotalSize / 1024; } } /// /// 所有需要下载的Size:MB /// public float TotalSizeMB { get { return 1f * TotalSize / 1024 / 1024; } } public float Progress { get { if (TotalSize == 0) return 0; return 1f * Size / TotalSize; } } /// /// 是否完成 /// public bool IsFinish { get { return TotalNum == (FailedNum + SuccessNum); } } /// /// 下载速度 /// public float LoadSpeed { get; set; } /// /// 是否成功 /// public bool IsSuccess { get { return FailedNum == 0; } } /// /// 重置 /// public void Reset() { SuccessNum = 0; FailedNum = 0; TotalNum = 0; Size = 0; TotalSize = 0; } } public enum ResourcesUpdateState { //获取游戏版本 GetGameConfigs, //获取游戏版本失败 GetGameConfigsFailed, //下载版本文件 DownLoadVersionFiles, //下载版本文件失败 DownLoadVersionFilesFailed, //从非wifi环境下载 DownLoadFromNoWifi, //wifi环境下载 DownLoadWithWifi, //更新资源 UpdateResourcesProgress, //更新资源失败 UpdateResourcesFailed, //更新完成 Success, //需要更换新包 OldPackageNeedChange, } /// /// 资源更新管理器 /// public class ResourcesUpdateManager : UnitySingleton { /// /// 资源更新回调 /// Action resourcsUpdateAction; /// /// 需要下载的列表 /// Dictionary downLoadFiles = new Dictionary(); /// /// 进度统计 /// Dictionary progressInfo = new Dictionary(); /// /// 资源更新进度 /// ResUpdateProgress progress = new ResUpdateProgress(); /// /// 下载URL /// string downLoadURL; /// /// 本地版本号 /// string localVersion; string sdkLodingUrl; DateTime startTime; /// /// 开始更新 /// /// 资源更新回调 public void BeginUpdate(string localVersion, Action resourcsUpdateAction) { this.localVersion = localVersion; this.resourcsUpdateAction = resourcsUpdateAction; this.downLoadFiles.Clear(); this.progress.Reset(); StartCoroutine(GetGameVersion()); } /// /// 是否允许非wifi情况下下载 /// /// public void BeginDownLoad(bool allowDownLoadFromNoWifi) { if (downLoadFiles.Count == 0) { UpdateSuccess(); return; } Action startDownAction = () => { startTime = DateTime.Now; progressInfo.Clear(); foreach (var each in downLoadFiles.Values) { ResourceDownloadManager.Instance.StartDownload(each.fileName, downLoadURL, "", OnDownLoadFileProgress, OnDownLoadFileFinish); } }; if (!IsNetWorkReachable(allowDownLoadFromNoWifi)) { SetResourcesUpdateState(false, ResourcesUpdateState.DownLoadFromNoWifi, new object[] { progress.TotalSize, startDownAction }); } else { SetResourcesUpdateState(false, ResourcesUpdateState.DownLoadWithWifi, new object[] { progress.TotalSize, startDownAction }); } } /// /// 下载进度回调 /// /// /// void OnDownLoadFileProgress(string fileName, DownLoadProgress downLoadProgress) { if (!progressInfo.ContainsKey(fileName)) { progressInfo.Add(fileName, downLoadProgress.Size); } progressInfo[fileName] = downLoadProgress.Size; this.progress.Size = 0; foreach (var each in progressInfo.Values) { this.progress.Size += each; } TimeSpan span = DateTime.Now - startTime; float second = (float)span.TotalSeconds; if (second > 0.0001) { this.progress.LoadSpeed = this.progress.Size / 1024 / second; } //更新进度 SetResourcesUpdateState(false, ResourcesUpdateState.UpdateResourcesProgress, this.progress); } /// /// 下载文件完成 /// /// 文件名 /// 下载结果 void OnDownLoadFileFinish(string fileName, bool result) { downLoadFiles.Remove(fileName); if (result) { progress.SuccessNum++; } else { progress.FailedNum++; } if (progress.IsFinish) { if (progress.IsSuccess) { UpdateSuccess(); // 打点 - 更新完成 SDK.SdkCustomEvent.CustomEvent("热更结束"); } else { Debug.LogError("load fail==============="+fileName); SetResourcesUpdateState(true, ResourcesUpdateState.UpdateResourcesFailed); // 打点 - 更新失败 SDK.SdkCustomEvent.CustomEvent("热更失败"); } } } /// /// 网络是否可用 /// /// /// bool IsNetWorkReachable(bool allowDownLoadFromNoWifi) { if (!allowDownLoadFromNoWifi && Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) return false; return true; } /// /// 获取游戏版本号 /// /// IEnumerator GetGameVersion() { SetResourcesUpdateState(false, ResourcesUpdateState.GetGameConfigs); if (!AppConst.isUpdate) { UpdateSuccess(); yield break; } downLoadURL = VersionManager.Instance.GetVersionInfo("resUrl") + VersionManager.Instance.GetVersionInfo("packageVersion") + "/" + AppConst.PlatformPath + "/"; string resUrl = downLoadURL + AppConst.GameVersionFile; Debug.Log("Download_Resouces_Url:" + resUrl); UnityWebRequest request = UnityWebRequest.Get(resUrl); request.certificateHandler = new AcceptAllCertificatesSignedWithASpecificPublicKey(); yield return request.SendWebRequest(); if (request.isNetworkError) { SetResourcesUpdateState(true, ResourcesUpdateState.GetGameConfigsFailed); } else { Debug.Log("www.text:" + request.downloadHandler.text); Hashtable table = MiniJSON.jsonDecode(request.downloadHandler.text) as Hashtable; if (table == null) { SetResourcesUpdateState(true, ResourcesUpdateState.GetGameConfigsFailed); } else { string packageVersion = table["packageVersion"] as string; if (VersionManager.CheckPackageVersionSame(packageVersion)) { string version = table["version"] as string; int result = VersionManager.VersionCompare(version, localVersion); //下载链接 Debug.Log(string.Format("ResUpdate=====>InitDownLoadURL,Version:{0},loadVersion:{1}", version, localVersion)); Debug.Log(string.Format("Version Compare result:{0}", result)); //如果版本号一致,就不进行更新了 if (result == 0) { Debug.Log(string.Format("version:{0},版本号一致,更新完成", version)); sdkLodingUrl = table["sdkLodingUrl"] as string; UpdateSuccess(); } else { localVersion = version; sdkLodingUrl = table["sdkLodingUrl"] as string; Debug.Log(string.Format("vertion_txt {0}", localVersion, sdkLodingUrl)); DownLoadVersionFiles(); } } else { string upAPKurl = table["upAPKurl"] as string; Debug.Log("upApkUrl" + upAPKurl); Application.OpenURL(upAPKurl); SetResourcesUpdateState(true, ResourcesUpdateState.OldPackageNeedChange); } } } //DownLoadVersionFiles(); } /// /// 下载版本文件 /// /// /// /// void DownLoadVersionFiles() { SetResourcesUpdateState(false, ResourcesUpdateState.DownLoadVersionFiles); ResourceDownloadManager.Instance.StartDownload(UpdateConfigs.FILES, downLoadURL, "", null, DownLoadVersionFilesFinishCallBack); } /// /// 下载版本文件结束回调 /// /// /// void DownLoadVersionFilesFinishCallBack(string fileName, bool isSuccess) { Debug.Log(string.Format("ResUpdate=====>DownLoadVersionFilesFinishCallBack,FileName:{0},IsSuccess:{1}", fileName, isSuccess)); if (isSuccess) { CalculateDownLoadFiles(); } else { SetResourcesUpdateState(false, ResourcesUpdateState.DownLoadVersionFilesFailed); } } /// /// 计算需要下载的文件列表 /// void CalculateDownLoadFiles() { if (BaseLogger.isDebug) BaseLogger.Log("ResUpdate=====>CalculateDownLoadFiles"); List newFiles = GetNewResourceFiles(); List streamFiles = GetStreamResourceFiles(); List persistentFiles = GetPersistentDataResourcesFiles(); ResourceFile newFile = null; ResourceFile streamFile = null; ResourceFile persistentFile = null; for (int i = 0; i < newFiles.Count; i++) { //> multiLan string[] nameArray = newFiles[i].fileName.Split('/'); if(nameArray.Length > 1) { //int L = ((int)Math.Floor((double)(AppConst.originLan / 100))) % 100; //if (nameArray[nameArray.Length - 2] == "artfont_en") //{ // if (PlayerPrefs.GetInt("multi_language_ResOpen_en", 0) == 0 && L != 1) // continue; //} #region 替换上面注释代码,意思是当前选中类型的语言比对下载,不是当前选中的语言不需要对比下载 int lan = PlayerPrefs.GetInt("multi_language", AppConst.originLan); int L = ((int)Math.Floor((double)(lan / 100))) % 100;//当前语言类型 if (nameArray[nameArray.Length - 2].Contains("artfont_")) {//文件夹匹配多语言格式 if (MultiLanguageHelper.MultiLanguageDictionary.ContainsKey(L)) { if (nameArray[nameArray.Length - 2] != MultiLanguageHelper.MultiLanguageDictionary[L].DirName) {//文件夹名字不是指定语言的文件夹 continue; } } } #endregion } newFile = newFiles[i]; streamFile = GetResourceFile(streamFiles, newFile.fileName); persistentFile = GetResourceFile(persistentFiles, newFile.fileName); if (CheckNeedDownLoad(newFile, streamFile, persistentFile)) { BaseLogger.LogFormat("NeedDownLoad====>FileName:{3}:[NewFile.CRC:{0}],[StreamFile.CRC:{1}],[PersistentFile.CRC:{2}]", newFile!=null?newFile.crc:"Null", streamFile != null ? streamFile.crc : "Null", persistentFile != null ? persistentFile.crc : "Null", newFile.fileName ); this.progress.TotalSize += newFile.size; downLoadFiles.Add(newFile.fileName, newFile); } } progress.TotalNum = downLoadFiles.Count; DeleteUnUseFiles(persistentFiles); BeginDownLoad(false); } /// /// 删除掉没用的资源 /// void DeleteUnUseFiles(List files) { if(BaseLogger.isDebug) BaseLogger.LogFormat("ResUpdate=====>DeleteUnUseFiles,Count:{0}", files != null ? files.Count : 0); if (files == null) return; for (int i = 0; i < files.Count; i++) { if (files[i].fileName.Contains(UpdateConfigs.FILES)) continue; FileUtil.DeleteFile(UpdateConfigs.PersistentDataPath + files[i].fileName); } } /// /// 检查是否需要下载 /// /// /// /// /// bool CheckNeedDownLoad(ResourceFile newFile, ResourceFile streamFile, ResourceFile persistentFile) { if (streamFile == null) { //如果本地没有,需要更新 if (persistentFile == null) return true; //如果本地的与服务器的不一样,需要更新 return File.Exists(UpdateConfigs.PersistentDataPath + persistentFile.fileName) && persistentFile.crc != newFile.crc; } else { //如果没有外部文件,与内部文件对比 if (persistentFile == null || !File.Exists(UpdateConfigs.PersistentDataPath + persistentFile.fileName)) return streamFile.crc != newFile.crc; //如果有外部文件与服务器文件一致,不需要更新 if (newFile.crc == persistentFile.crc) { return false; } //如果内部文件与服务器文件一致,说明强更后的APK中包含最新的资源,需要删除本地外部文件 else if (newFile.crc == streamFile.crc) { FileUtil.DeleteFile(UpdateConfigs.PersistentDataPath + newFile.fileName); return false; } else { return true; } } } /// /// 解析版本文件 /// List GetNewResourceFiles() { AssetBundle bundle = AssetBundle.LoadFromFile(UpdateConfigs.PersistentDataPath + UpdateConfigs.FILES);//, 0, AppConst.EncyptBytesLength); ResourceFiles files = bundle.LoadAsset("game"); List list = files.files; bundle.Unload(true); return list; } /// /// 获取流媒体目录中的文件:可能有,可能没有 /// /// List GetStreamResourceFiles() { AssetBundle bundle = AssetBundle.LoadFromFile(UpdateConfigs.StreamPath + UpdateConfigs.FILES);//, 0, AppConst.EncyptBytesLength); if (bundle != null) { ResourceFiles files = bundle.LoadAsset("game"); List list = files.files; bundle.Unload(true); return list; } else { Debug.LogError("GetStreamResourceFiles is null!"); } return null; } /// /// 获取持久化目录中的文件 /// /// List GetPersistentDataResourcesFiles() { if (BaseLogger.isDebug) BaseLogger.Log("ResUpdate=====>GetPersistentDataResourcesFiles"); string[] files = FileUtil.GetAllFiles(UpdateConfigs.PersistentDataPath); List list = new List(); string fileName = string.Empty; for (int i = 0; i < files.Length; i++) { fileName = files[i].Replace(UpdateConfigs.PersistentDataPath, string.Empty).Replace("\\","/"); ResourceFile file = new ResourceFile(fileName, FileToCRC32.GetFileCRC32(files[i])); list.Add(file); } return list; } /// /// 获取资源文件 /// /// /// /// ResourceFile GetResourceFile(List files, string fileName) { if (files == null) return null; ResourceFile file = null; for (int i = 0; i < files.Count; i++) { if (files[i].fileName == fileName) { file = files[i]; //找到了就从列表中删掉 files.RemoveAt(i); break; } } return file; } void UpdateSuccess() { VersionPar vp = new VersionPar(); vp.version = localVersion; vp.sdkLodingUrl = sdkLodingUrl; SetResourcesUpdateState(true, ResourcesUpdateState.Success, vp); } /// /// 设置资源更新状态 /// /// 是否已经更新完成 /// 更新状态 /// 参数 void SetResourcesUpdateState(bool isFinish, ResourcesUpdateState state, object param = null) { ThreadManager.Instance.QueueOnMainThread(() => { if (resourcsUpdateAction != null) { resourcsUpdateAction(isFinish, state, param); } if (isFinish) { resourcsUpdateAction = null; } }); } } }