From 234a265362c1d29da542c78c4624790587950432 Mon Sep 17 00:00:00 2001 From: ALEXTANG <574809918@qq.com> Date: Mon, 27 Jun 2022 11:40:03 +0800 Subject: [PATCH] Loader Loader --- .../Runtime/HotUpdate/Runtime/LoadData.cs | 35 ++ .../Runtime/HotUpdate/Runtime/LoadMgr.cs | 243 +++++++++++++ .../HotUpdate/Runtime/LoaderUtilities.cs | 326 ++++++++++++++++++ 3 files changed, 604 insertions(+) diff --git a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadData.cs b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadData.cs index cff8cda3..8dddbfa2 100644 --- a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadData.cs +++ b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadData.cs @@ -22,5 +22,40 @@ namespace TEngine public class LoadData { + public UpdateType Type; //是否底包更新 + public UpdateStyle Style; //是否强制更新 + public UpdateNotice Notice;//是否提示 + public List List; + public List All; + } + + public enum UpdateType + { + None = 0, + PackageUpdate = 1, //底包更新 + ResourceUpdate = 2,//资源更新 + } + + public enum UpdateStyle + { + None = 0, + Froce = 1, //强制 + Optional = 2,//非强制 + } + + public enum UpdateNotice + { + None = 0, + Notice = 1, //提示 + NoNotice = 2,//非提示 + } + + public enum VersionRequestErrorCode + { + Ok = 1, + Error = 2, + Invalid_Param = 3, + Update_Process = 4, + Patch_Not_Exist = 5 } } diff --git a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadMgr.cs b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadMgr.cs index 4103dab7..7dc3fc16 100644 --- a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadMgr.cs +++ b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoadMgr.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.IO; +using UnityEngine; namespace TEngine { @@ -35,6 +37,9 @@ namespace TEngine private const int MaxTryCount = 3; private bool _connectBack; private bool _needUpdate = false; + private LoadData _currentData; + private UpdateType _currentType = UpdateType.None; + private readonly string _downloadFilePath = FileSystem.ResourceRoot + "/"; public LoadMgr() { @@ -93,6 +98,244 @@ namespace TEngine { } + + try + { + var onlineParamStr = LoaderUtilities.HttpGet(OnlineParamUrl); + var onlineParam = (Dictionary)MiniJSON.Json.Deserialize(onlineParamStr); + string onlineResVersion = onlineParam["resVersion"].ToString(); + TLogger.LogInfo($"onlineResVersion:{onlineResVersion}"); + LatestResId = onlineResVersion; + var resListStr = LoaderUtilities.HttpGet(GetOnlineResListUrl(LatestResId) + "Md5CheckList.json"); + var resDic = (Dictionary)MiniJSON.Json.Deserialize(resListStr); + List resList = (List)resDic["target"]; + _connectBack = true; + + OnHttpResponse(CreateLoadData(onlineParam, resList)); + } + catch (Exception e) + { +#if UNITY_EDITOR + StartGame(); +#else + LoaderUtilities.DelayFun(RequestVersion, new WaitForSeconds(1f)); +#endif + TLogger.LogError(e.Message); + } + return; + } + + internal void OnHttpResponse(LoadData formatData) + { + if (formatData == null) + { + return; + } + //检测一下是不是本地存在该版本,如果存在就本地做回退,不需要走下载流程 + _currentData = formatData; + var exitLocalVersion = _CheckLocalVersion(_currentData.List); + if (exitLocalVersion) + { + _needUpdate = LatestResId != GameConfig.Instance.ResId; + StartGame(); + return; + } + //检测一下本地是否存在资源,如果存在了就直接解压就行了,如果不存在还需要下载 + _currentData.List = _CheckLocalExitResource(_currentData.List); + _currentType = _currentData.Type; + + //TODO + } + + /// + /// 本地检测,过滤掉本地存在了的资源 + /// + /// + /// + private List _CheckLocalExitResource(List res) + { + var list = new List(); + foreach (var item in res) + { + string resPath = _downloadFilePath + item.Url; + if (File.Exists(resPath)) + { + if (string.CompareOrdinal(LoaderUtilities.GetMd5Hash(resPath), item.Md5) == 0) + { + continue; + } + + list.Add(item); + } + else + { + list.Add(item); + } + } + return list; + } + + /// + /// 检测是否是版本回退了 + /// + /// + /// + private bool _CheckLocalVersion(List res) + { + foreach (var item in res) + { + string tempVersion = item.Url.Split('_')[1]; + if (!GameConfig.Instance.CheckLocalVersion(tempVersion)) + { + return false; + } + } + + return true; + } + + void StartGame() + { + if (_startGameEvent != null) + { + _startGameEvent(); + } + } + + internal LoadData CreateLoadData(Dictionary onlineParam, List resList = null) + { + if (onlineParam == null) + { + + return null; + } + var data = new LoadData(); + var result = int.Parse("1"); + var versionStr = LoaderUtilities.HttpGet(GetOnlineResListUrl(LatestResId) + "version.json"); + var versionDic = (Dictionary)MiniJSON.Json.Deserialize(versionStr); + string version = versionDic["AppVersion"].ToString(); + switch (result) + { + case (int)VersionRequestErrorCode.Ok: + { + data.Type = UpdateType.ResourceUpdate; + data.Style = UpdateStyle.Optional; + data.Notice = UpdateNotice.Notice; + + data.Type = (UpdateType)int.Parse(onlineParam["UpdateType"].ToString()); + + if (GameConfig.Instance.ResId == LatestResId) + { + TLogger.LogInfo("GameConfig.Instance.ResId 匹配,无需更新"); + data.Type = UpdateType.None; + } + if (data.Type != UpdateType.PackageUpdate) + { + data.Style = (UpdateStyle)int.Parse(onlineParam["UpdateStyle"].ToString()); + data.Notice = (UpdateNotice)int.Parse(onlineParam["UpdateNotice"].ToString()); + } + data.List = new List(); + data.All = new List(); + + if (resList != null) + { + for (int i = 0; i < resList.Count; i++) + { + var item = (Dictionary)resList[i]; + string fileName = item["fileName"].ToString(); + var itemTemp = new LoadResource + { + RemoteUrl = _onlineResListUrl + fileName, + Md5 = item["md5"].ToString(), + Size = (long)item["fileSize"], + Url = fileName + "_" + LatestResId, + }; + data.List.Add(itemTemp); + data.All.Add(itemTemp); + } + } + break; + } + + case (int)VersionRequestErrorCode.Patch_Not_Exist: + LatestResId = string.Empty; +#if UNITY_EDITOR_WIN || _DEVELOPMENT_BUILD_ + StartGame(); +#else + //TODO +#endif + return null; + default: + + return null; + } + if (GameConfig.Instance.ResId == LatestResId) + { + TLogger.LogInfo($"GameConfig.Instance.ResId{GameConfig.Instance.ResId} == onlineResVersion{LatestResId}"); + } + else + { + TLogger.LogInfo($"GameConfig.Instance.ResId{GameConfig.Instance.ResId} != onlineResVersion{LatestResId}"); + } + + return data; + } + + private string _resListUrl = string.Empty; + private string _onlineParamUrl = string.Empty; + internal const string Url = "http://1.12.241.46:8081/TXYXGame/"; + + internal string ResListUrl + { + get + { + if (string.IsNullOrEmpty(_resListUrl)) + { +#if UNITY_EDITOR || UNITY_ANDROID + _resListUrl = Url + "AssetBundles/Android/ver_" + GameConfig.Instance.ResId + "/"; +#elif UNITY_IOS || UNITY_IPHONE + _resListUrl = Url + "AssetBundles/IOS/ver_" + GameConfig.Instance.ResId + "/"; +#elif UNITY_STANDALONE_WIN + _resListUrl = Url + "AssetBundles/WIN/ver_" + GameConfig.Instance.ResId + "/"; +#endif + } + return _resListUrl; + } + } + + private string _onlineResListUrl = string.Empty; + internal string GetOnlineResListUrl(string onlineResId) + { + if (string.IsNullOrEmpty(_onlineResListUrl)) + { +#if UNITY_EDITOR || UNITY_ANDROID + _onlineResListUrl = Url + "AssetBundles/Android/ver_" + onlineResId + "/"; +#elif UNITY_IOS || UNITY_IPHONE + _onlineResListUrl = Url + "AssetBundles/IOS/ver_" + onlineResId + "/"; +#elif UNITY_STANDALONE_WIN + _onlineResListUrl = Url + "AssetBundles/WIN/ver_" + onlineResId + "/"; +#endif + } + TLogger.LogInfo(_onlineResListUrl); + return _onlineResListUrl; + } + + internal string OnlineParamUrl + { + get + { + if (string.IsNullOrEmpty(_onlineParamUrl)) + { +#if UNITY_EDITOR || UNITY_ANDROID + _onlineParamUrl = Url + "androidVer.json"; +#elif UNITY_IOS || UNITY_IPHONE + _onlineParamUrl = Url + "iosVer.json"; +#elif UNITY_STANDALONE_WIN + _onlineParamUrl = Url + "winVer.json"; +#endif + } + return _onlineParamUrl; + } } } } diff --git a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoaderUtilities.cs b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoaderUtilities.cs index 3d2739f2..1515525f 100644 --- a/Assets/TEngine/Runtime/HotUpdate/Runtime/LoaderUtilities.cs +++ b/Assets/TEngine/Runtime/HotUpdate/Runtime/LoaderUtilities.cs @@ -1,10 +1,14 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Security.Cryptography; using System.Text; +using System.Threading; using System.Threading.Tasks; +using UnityEngine; namespace TEngine { @@ -63,5 +67,327 @@ namespace TEngine return string.Empty; } } + + #region WebRequest + + public class NetContent + { + public bool Statue; + public bool Result; + public string MsgContent; + public byte[] data; + public Action SuccessCallback; + public Action ErrorCallback; + + public void SetResult(bool result, string content) + { + Statue = true; + Result = result; + MsgContent = content; + } + + public void SetParam(byte[] param) + { + data = param; + } + + public void SetAction(Action success, Action faile) + { + SuccessCallback = success; + ErrorCallback = faile; + } + + public void Clear() + { + Statue = false; + Result = false; + MsgContent = ""; + } + } + + private static NetContent _msgContent = new NetContent(); + private static Coroutine _coroutine; + private static HttpWebRequest webReq; + + /// + /// 网络请求 + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static void PostWebRequest( + string postUrl, + string paramData, + Action successCallback = null, + Action errorCallback = null + ) + { + TLogger.LogInfo($"TEngine.LoaderUtilities.PostWebRequest url:{postUrl},paramData:{paramData}"); + _msgContent.Clear(); + + byte[] byteArray = Encoding.UTF8.GetBytes(paramData); + + if (webReq != null) + { + webReq.Abort(); + webReq = null; + GC.Collect(); + } + + webReq = (HttpWebRequest)WebRequest.Create(new Uri(postUrl)); + + webReq.Method = "POST"; + webReq.ContentType = "application/json"; + webReq.ContentLength = byteArray.Length; + _msgContent.SetParam(byteArray); + _msgContent.SetAction(successCallback, errorCallback); + + var result = webReq.BeginGetRequestStream(RequestComplete, webReq); + ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, TimeoutCallback, result, 2000, true); + + if (_coroutine != null) + { + MonoManager.Instance.StopCoroutine(_coroutine); + } + _coroutine = MonoManager.Instance.StartCoroutine(_Response(_msgContent)); + } + + private static IEnumerator _Response(NetContent content) + { + while (content.Statue == false) + { + yield return null; + } + + if (content.Result) + { + _msgContent.SuccessCallback?.Invoke(content.Result, content.MsgContent); + } + else + { + _msgContent.ErrorCallback?.Invoke(content.Result, ""); + } + + if (_coroutine != null) + { + MonoManager.Instance.StopCoroutine(_coroutine); + _coroutine = null; + } + } + + private static void RequestComplete(IAsyncResult result) + { + if (result != null && result.IsCompleted) + { + var request = result.AsyncState as HttpWebRequest; + try + { + Stream stream = request.EndGetRequestStream(result); + stream.Write(_msgContent.data, 0, _msgContent.data.Length); + stream.Close(); + stream.Dispose(); + var rspResult = webReq.BeginGetResponse(ResponseComplete, webReq); + ThreadPool.RegisterWaitForSingleObject(rspResult.AsyncWaitHandle, TimeoutCallback, result, 2000, true); + } + catch (Exception e) + { + _msgContent.SetResult(false, e.ToString()); + throw; + } + } + } + + private static void ResponseComplete(IAsyncResult asyncResult) + { + HttpWebRequest request; + HttpWebResponse response; + StreamReader reader; + Stream recive; + + request = (asyncResult.AsyncState as HttpWebRequest); + var res = request.EndGetResponse(asyncResult); + response = res as HttpWebResponse; + if (response != null && response.StatusCode == HttpStatusCode.OK) + { + recive = response.GetResponseStream(); + reader = new StreamReader(recive); + try + { + string result = reader.ReadToEnd(); + _msgContent.SetResult(true, result); + reader.Close(); + reader.Dispose(); + recive.Close(); + recive.Dispose(); + response.Close(); + request.Abort(); + } + catch (Exception e) + { + _msgContent.SetResult(false, e.ToString()); + throw; + } + finally + { + if (reader != null) + { + reader.Close(); + reader.Dispose(); + } + + if (request != null) + { + reader.Dispose(); + } + + if (response != null) + { + response.Close(); + } + + if (request != null) + { + request.Abort(); + } + } + } + else + { + if (response == null) + { + _msgContent.SetResult(false, "HttpResponse:response is null"); + } + else + { + _msgContent.SetResult(false, "HttpResponse:" + response.StatusCode.ToString()); + } + } + } + + private static void TimeoutCallback(object state, bool timedOut) + { + if (!timedOut) return; + _msgContent.SetResult(false, "timeout"); + IAsyncResult result = state as IAsyncResult; + if (result != null) + { + try + { + Stream stream = webReq.EndGetRequestStream(result); + stream.Close(); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + webReq.Abort(); + } + } + + /// + /// 延迟执行 + /// + /// + /// + public static Coroutine DelayFun(Action callback, YieldInstruction time) + { + return MonoManager.Instance.StartCoroutine(DelayFunIEnumerator(callback, time)); + } + + public static IEnumerator DelayFunIEnumerator(Action callback, YieldInstruction time) + { + yield return time; + callback(); + } + #endregion + + /// + /// 数据格式转换 + /// + /// 数据 + /// + public static string FormatData(long data) + { + string result = ""; + if (data < 0) + data = 0; + + if (data > 1024 * 1024) + { + result = ((int)(data / (1024 * 1024))).ToString() + "MB"; + } + else if (data > 1024) + { + result = ((int)(data / 1024)).ToString() + "KB"; + } + else + { + result = data + "B"; + } + + return result; + } + + /// + /// 获取文件大小 + /// + /// 文件路径 + /// + public static long GetFileSize(string path) + { + using (FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + return file.Length; + } + return 0; + } + + /// + /// GET请求与获取结果 + /// + public static string HttpGet(string url, string postDataStr = "") + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url + (postDataStr == "" ? "" : "?") + postDataStr); + request.Method = "GET"; + request.ContentType = "text/html;charset=UTF-8"; + + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + Stream myResponseStream = response.GetResponseStream(); + StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8); + string retString = myStreamReader.ReadToEnd(); + myStreamReader.Close(); + myResponseStream.Close(); + + return retString; + } + + /// + /// POST请求与获取结果 + /// + public static string HttpPost(string url, string postDataStr) + { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + request.ContentLength = postDataStr.Length; + StreamWriter writer = new StreamWriter(request.GetRequestStream(), Encoding.ASCII); + writer.Write(postDataStr); + writer.Flush(); + HttpWebResponse response = (HttpWebResponse)request.GetResponse(); + string encoding = response.ContentEncoding; + if (encoding == null || encoding.Length < 1) + { + encoding = "UTF-8"; //默认编码 + } + StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(encoding)); + string retString = reader.ReadToEnd(); + return retString; + } } }