using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using System.IO; using UnityEngine.Networking; /// /// 更新助手 /// public class UpdateHelper : MonoBehaviour { /// /// 安卓更新文件名称 /// public static string AndroidUpdateFileName = "UpdateFileAndroid.json"; /// /// IOS更新文件名称 /// public static string IosUpdateFileName = "UpdateFileIOS.json"; /// /// 资源储存的根目录 /// private string resSaveRootPath = ""; /// /// 单键 /// private static UpdateHelper mInst = null; /// /// 客户端的更新文件信息 /// private UpdateFileInfo mClientUpdateFileInfo = null; /// /// 服务器上的更新文件信息 /// private UpdateFileInfo mServerUpdateFileInfo = null; /// /// 服务器更新文件的数据 /// private byte[] mServerUpdateFileDatas = null; /// /// 更新列表 /// private List mUpdateList = new List(); /// /// 开始更新的时间 /// private float mUpdateTime = -1; /// /// 更新信息 /// private UpdateInfo mUpdateInfo = new UpdateInfo(); /// /// 更新迭代器 /// private IEnumerator mUpdateIEnumerator = null; /// /// 超时时间 /// private const float mTimeOut = 10.0f; /// /// 服务器地址 /// private string mServerUrl = ""; /// /// 获取单键实例 /// /// 单键实例 public static UpdateHelper Instance() { return mInst; } /// /// 唤醒 /// private void Awake() { mInst = this; resSaveRootPath = Application.persistentDataPath + "/Res/"; } /// /// 获取更新文件列表 /// public void GetUpdateFile(string serverUrl, Action ac) { Debug.Log("开始获取更新文件"); mServerUrl = serverUrl; mUpdateTime = Time.time; ResetUpdateInfo(); // 资源保存的路径 if (!Directory.Exists(resSaveRootPath)) { Directory.CreateDirectory(resSaveRootPath); } // 加载客户端更新文件 LoadClientMD5FileInfo((md5) => { }); // 加载服务器更新文件 LoadServerMD5FileInfo(serverUrl, (md5) => { if (ac != null) { ac(); } }); } /// /// 当前版本号 /// /// public void GetCurVersion(Action ac) { LoadClientMD5FileInfo((info) => { if (info != null) { // 基带版本>=本地版本清空沙盒 if (GlobalConfig.ClientBsaeVersion >= mClientUpdateFileInfo.Version) { mUpdateInfo.mCurFileIndex = 1; mUpdateInfo.mCurFileProgress = 1; mUpdateInfo.mCurFlieName = ""; mUpdateInfo.mTotalFileCount = 1; mUpdateInfo.mUpdateComplete = true; // 删除Res文件夹 if (Directory.Exists(resSaveRootPath)) { Debug.Log("版本已是最新,发现过期的资源文件,开始清理..."); Directory.Delete(resSaveRootPath, true); // 直接删除路径 Debug.Log("清理过期的资源文件成功..."); return; } } ac(info.Version); } else { ac(1); } }); } /// /// 获取完整版本号 /// /// public string GetFullVersion() { if (mServerUpdateFileInfo != null) { int midVer = mServerUpdateFileInfo.Version / 1000; int minVer = mServerUpdateFileInfo.Version % 1000; string fullVersion = GlobalConfig.ClientVersion + "." + midVer + "." + minVer; PlayerPrefs.SetString("Version", fullVersion); PlayerPrefs.Save(); return fullVersion; } else if (mClientUpdateFileInfo != null) { int midVer = mClientUpdateFileInfo.Version / 1000; int minVer = mClientUpdateFileInfo.Version % 1000; string fullVersion = GlobalConfig.ClientVersion + "." + midVer + "." + minVer; PlayerPrefs.SetString("Version", fullVersion); PlayerPrefs.Save(); return fullVersion; } else { return GlobalConfig.ClientVersion + ".0.0"; } } /// /// 获取本地MD5文件 /// /// public void LoadClientMD5FileInfo(Action ac) { string fullUrl = ""; // 加载客户端更新文件 if (mClientUpdateFileInfo == null) { if (Application.platform == RuntimePlatform.IPhonePlayer) { fullUrl = GetPath(IosUpdateFileName); } else { fullUrl = GetPath(AndroidUpdateFileName); } StartCoroutine(IE_GetUpdateFile(fullUrl, false, (clientFile) => { if (clientFile != null) { mClientUpdateFileInfo = clientFile; ac(mClientUpdateFileInfo); } else { ac(null); mUpdateTime = -1; mClientUpdateFileInfo = null; UI_CueDialog.Instance().Open("加载本地MD5文件失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame); } })); } else { ac(mClientUpdateFileInfo); } } /// /// 加载服务端MD5文件 /// /// /// public void LoadServerMD5FileInfo(string serverUrl, Action ac) { string fullUrl = ""; if (mServerUpdateFileInfo == null) { if (Application.platform == RuntimePlatform.IPhonePlayer) { fullUrl = serverUrl + "/" + IosUpdateFileName; } else { fullUrl = serverUrl + "/" + AndroidUpdateFileName; } StartCoroutine(IE_GetUpdateFile(fullUrl, true, (serverFile) => { if (serverFile != null) { mServerUpdateFileInfo = serverFile; ac(mServerUpdateFileInfo); } else { ac(null); mUpdateTime = -1; mServerUpdateFileInfo = null; UI_CueDialog.Instance().Open("从服务器获取更新列表失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame); } })); } else { ac(mServerUpdateFileInfo); } } /// /// 开始更新 /// public void CheckUpdate() { //当用户使用移动网络时,提醒用户是否继续更新 if (Application.internetReachability == NetworkReachability.ReachableViaCarrierDataNetwork) { UI_CueDialog.Instance().Open("检测到您当前使用的是流量,更新将消耗您的流量,是否继续更新!", "更新提示", E_DialogType.TwoBntton, StartUpdate, ExitGame); } else { StartUpdate(); } } /// /// 开始更新 /// private void StartUpdate() { // 提取需要更新的列表 mUpdateList.Clear(); for (int i = 0; i < mServerUpdateFileInfo.Infos.Count; i++) { bool add = true; for (int j = 0; j < mClientUpdateFileInfo.Infos.Count; j++) { if (mClientUpdateFileInfo.Infos[j].Name == mServerUpdateFileInfo.Infos[i].Name) { if (mClientUpdateFileInfo.Infos[j].MD5 == mServerUpdateFileInfo.Infos[i].MD5) { // md5相同,无需重新下载 add = false; break; } } } // 添加到更新列表 if (add) { mUpdateList.Add(mServerUpdateFileInfo.Infos[i].Name); } } // 获取更新的断点续传信息 string LastUpdateInfo = PlayerPrefs.GetString("YLSJ_LastUpdateInfo", "0_0"); string[] strs = LastUpdateInfo.Split('_'); int startIndex = 0; if (strs.Length == 2) { int version = int.Parse(strs[0]); int index = int.Parse(strs[1]); // 储存版本和服务版本一致,更新从记录的索引开始 if (version == mServerUpdateFileInfo.Version) { startIndex = index; } } // 开始更新 mUpdateIEnumerator = IE_DownLoadUpdateFile(startIndex); StartCoroutine(mUpdateIEnumerator); } /// /// 重设更新信息 /// private void ResetUpdateInfo() { mClientUpdateFileInfo = null; mServerUpdateFileInfo = null; mUpdateInfo.mCurFileIndex = 0; mUpdateInfo.mCurFileProgress = 0; mUpdateInfo.mCurFlieName = ""; mUpdateInfo.mTotalFileCount = 0; mUpdateInfo.mUpdateComplete = false; } /// /// 获取更新信息 /// /// public UpdateInfo GetUpdateInfo() { return mUpdateInfo; } /// /// 协同加载 更新文件 /// /// 当前的更新信息 /// 返回资源AssetBundle private IEnumerator IE_DownLoadUpdateFile(int index) { if (index >= 0 && index < mUpdateList.Count) { mUpdateInfo.mTotalFileCount = mUpdateList.Count; string url = ""; for (int i = index; i < mUpdateList.Count; i++) { // 加载服务器更新文件 if (Application.platform == RuntimePlatform.IPhonePlayer) { url = mServerUrl + "UpdateIOS/" + mUpdateList[i]; } else { url = mServerUrl + "UpdateAndroid/" + mUpdateList[i]; } mUpdateInfo.mCurFileIndex = i; mUpdateInfo.mCurFlieName = mUpdateList[i]; // 是否有错误 bool isError = false; // 上次的进度 float lastProgress = 0; // 上次进度的时间 float lastProgresstime = Time.time; WWW www = new WWW(url); // 未完成,更新进度 while (www.isDone == false) { if (lastProgress == www.progress) { // 检测是否超时 if (lastProgresstime + mTimeOut <= Time.time) { isError = true; break; } } else { mUpdateInfo.mCurFileProgress = www.progress + 0.05f; lastProgress = www.progress; lastProgresstime = Time.time; } yield return null; } // 未超时,但有网络返回的错误 if (isError == false && string.IsNullOrEmpty(www.error) == false) { continue; } // 成功 if (isError == false) { mUpdateInfo.mCurFileProgress = 1; byte[] datas = www.bytes; // 保存 string savePath = resSaveRootPath + mUpdateInfo.mCurFlieName; File.WriteAllBytes(savePath, datas); // 储存更新信息 PlayerPrefs.SetString("YLSJ_LastUpdateInfo", mServerUpdateFileInfo.Version + "_" + (i + 1)); PlayerPrefs.Save(); } // 失败 else { mServerUpdateFileDatas = null; UI_CueDialog.Instance().Open("更新失败!是否重试!", "更新提示", E_DialogType.TwoBntton, ReUpdate, ExitGame); break; } www.Dispose(); yield return new WaitForSeconds(0.1f); } // 更新完成 mUpdateInfo.mUpdateComplete = true; // 储存更新文件 string fileSavePath = ""; if (Application.platform == RuntimePlatform.IPhonePlayer) { fileSavePath = resSaveRootPath + IosUpdateFileName; } else { fileSavePath = resSaveRootPath + AndroidUpdateFileName; } if (mServerUpdateFileDatas != null) { File.WriteAllBytes(fileSavePath, mServerUpdateFileDatas); } } } /// /// 重新更新 /// private void ReUpdate() { // 停止正在更新的 if (mUpdateIEnumerator != null) { StopCoroutine(mUpdateIEnumerator); mUpdateIEnumerator = null; } // 开始更新 mUpdateIEnumerator = IE_DownLoadUpdateFile(mUpdateInfo.mCurFileIndex); StartCoroutine(mUpdateIEnumerator); } /// /// 退出游戏 /// private void ExitGame() { Application.Quit(); } /// /// 更新 /// private void Update() { // 未启用更新 if (mUpdateTime < 0) { return; } // 开始检测更新 if (mClientUpdateFileInfo != null && mServerUpdateFileInfo != null) { CheckUpdate(); mUpdateTime = -1; } else { if (mUpdateTime + 10 <= Time.time) { mUpdateTime = -1; UI_CueDialog.Instance().Open("从服务器获取更新列表失败!请稍后重试!", "更新提示", E_DialogType.OneButton, ExitGame); } } } /// /// 协同加载 服务器上的更新文件信息 /// /// 地址 /// 是否保存 /// 更新文件信息 /// 返回资源AssetBundle private IEnumerator IE_GetUpdateFile(string url, bool save, Action fileInfo) { Debug.Log("获取配置的地址:" + url); //UnityWebRequest www = UnityWebRequest.Get(url); //yield return www.Send(); // 是否有错误 bool isError = false; // 上次的进度 float lastProgress = 0; // 上次进度的时间 float lastProgresstime = Time.time; WWW www = new WWW(url); // 未完成,更新进度 while (www.isDone == false) { if (lastProgress == www.progress) { // 检测是否超时 if (lastProgresstime + mTimeOut <= Time.time) { isError = true; break; } } else { mUpdateInfo.mCurFileProgress = www.progress + 0.05f; lastProgress = www.progress; lastProgresstime = Time.time; } yield return null; } // 未超时,但有网络返回的错误 if (isError == false && string.IsNullOrEmpty(www.error) == false) { isError = true; } // 成功 if (isError == false) { if (save) { mServerUpdateFileDatas = www.bytes; } byte[] datas = www.bytes; string DecrypText = System.Text.Encoding.UTF8.GetString(datas); fileInfo.Invoke(JsonUtility.FromJson(DecrypText)); } // 失败 else { fileInfo(null); } www.Dispose(); } /// /// 获取异步加载资源路径(用于 www 加载) /// /// 资源名称 /// 返回实际路径 public string GetPath(string strName) { string strPath = string.Empty; string persistentDataPath = resSaveRootPath + strName; if (Application.isEditor) { if (File.Exists(persistentDataPath)) { strPath = "file:///" + resSaveRootPath + strName; } else { strPath = "file://" + Application.streamingAssetsPath + "/" + strName; } } else { if (File.Exists(persistentDataPath)) { strPath = "file://" + resSaveRootPath + strName; } else { if (RuntimePlatform.Android == Application.platform) { strPath = Application.streamingAssetsPath + "/" + strName; } else { strPath = "file://" + Application.streamingAssetsPath + "/" + strName; } } } return strPath; } } /// /// 更新信息 /// public class UpdateInfo { /// /// 总共的文件数 /// public int mTotalFileCount = 0; /// /// 当前文件索引 /// public int mCurFileIndex = 0; private float _curFileProgress; /// /// 当前文件的索引 /// public float mCurFileProgress { set { this._curFileProgress = value; if (_curFileProgress > 1.0f) { _curFileProgress = 1.0f; } } get { return _curFileProgress; } } /// /// 当前文件的名称 /// public string mCurFlieName = ""; /// /// 更新是否完成 /// public bool mUpdateComplete = false; }