// 在AssetBundleManager等资源管理器生成前,预先处理资源问题的管理器 // 提前处理资源更新,服务器列表下载等一系列情况 // 注:这个东西运作的时候,没有AssetBundle,没有Lua,不要试图做任何相关操作。 using System.Collections.Generic; using System.Reflection; using AssetUpdate; using Games.GlobeDefine; using UnityEngine; using UnityEngine.EventSystems; using SceneHub = UnityEngine.SceneManagement.SceneManager; public class AssetUpdateManager : MonoBehaviour { // 处理不同渠道的Php链接数据 private const string _channelConfig = "Channel.txt"; private IAssetAsyncAction _currentAction; public static Assembly dllAssembly { get; private set; } private static string _channel; public static string channel { get { return _channel; } private set { _channel = value; } } public static string assetVersionUri { get; private set; } public static bool useResources { get { return _useResources; } } private static bool _useResources = true; public AssetUpdateState state { get; private set; } public AssetUpdateError error { get; private set; } public static string assetUri { get; private set; } public static int assetVersion { get; private set; } public static string apkUri { get; private set; } public static string apkVersion { get; private set; } public List downloadList { get; private set; } public AssetDependencyInfo mainInfo { get; private set; } public Dictionary streamingPathInfo { get; private set; } private const string currentVersionPref = "CurrentVersion"; public static int LoadCurrentVersion() { var currentVersion = PlayerPrefs.GetInt(currentVersionPref, default(int)); if (currentVersion <= 0) { var versionFile = Resources.Load(AssetConst.currentVersionFile).text; int version; if (int.TryParse(versionFile, out version)) currentVersion = version; } return currentVersion; } private void SaveCurrentVersion() { PlayerPrefs.SetInt(currentVersionPref, assetVersion); } private void Awake() { Debug.Log("AssetUpdateManager Awake"); _useResources = false; AssetUpdateDownloaderTick.CreateInstance(); if (!FirstSceneBg.instance) FirstSceneBg.CreateInstance(); CreateEventSystem(); } public static void CreateEventSystem() { if (!EventSystem.current) { var eventObj = new GameObject("EventSystem"); eventObj.AddComponent(); eventObj.AddComponent(); DontDestroyOnLoad(eventObj); } } private void OnConfigLoad(WWW www) { var text = string.IsNullOrEmpty(www.error) ? www.text : string.Empty; var channelError = false; if (string.IsNullOrEmpty(text)) channelError = true; else { var lines = AssetUtils.TextToLines(text); if (lines.Length < 2) channelError = true; else { channel = lines[0].Trim(); assetVersionUri = lines[1].Trim(); if (string.IsNullOrEmpty(channel) || string.IsNullOrEmpty(assetVersionUri)) channelError = true; if (lines.Length > 2) { var extra = lines[2].Trim(); if (lines.Length > 2 && !string.IsNullOrEmpty(extra)) FirstSceneBg.instance.SetPublishText(extra); } } } if (channelError) Debug.LogError("Unable to Parse Channel file! This is not recoverable!"); else { // 清理上次执行残留的rawJsonText // 当前项目没有类似数据缓存库一样的类,暂时直接用静态传递 assetUri = string.Empty; state = AssetUpdateState.GetUpdateInfo; StartCurrentAction(); } FirstSceneBg.instance.ShowGameVersion(); // 清理上次执行残留的rawJsonText // 当前项目没有类似数据缓存库一样的类,暂时直接用静态传递 assetUri = string.Empty; state = AssetUpdateState.GetUpdateInfo; StartCurrentAction(); } private void Start() { // 特别处理Android Resume时期的诡异情况,该状况下,AssetBundle未被清空,但实际Bundle管理器已被析构 AssetBundle.UnloadAllAssetBundles(true); Debug.Log("加载渠道文件" + _channelConfig); AssetUpdateDownloaderTick.instance.LoadStreamingAsset(_channelConfig, OnConfigLoad); } private void OnDestroy() { // 即使AppQuit也需要处理OnDestroy,否则会在编辑器中导致下载不中断 if (state != AssetUpdateState.Complete && _currentAction != null) { _currentAction.Dispose(); _currentAction = null; } } private void StartCurrentAction() { switch (state) { case AssetUpdateState.GetUpdateInfo: { var httpAction = new HttpsGetAction(10f); _currentAction = httpAction; httpAction.Start(); } break; case AssetUpdateState.DownloadAssetList: { var assetListAction = new AssetVersionAction(); _currentAction = assetListAction; assetListAction.Start(assetVersion, assetUri); } break; case AssetUpdateState.CompareAssetList: { var assetCompareAction = new AssetCompareAction(); _currentAction = assetCompareAction; assetCompareAction.Start(mainInfo); } break; case AssetUpdateState.DownloadFile: { var downloadAction = new ProjectDownloadAction(); _currentAction = downloadAction; downloadAction.Start(assetUri, downloadList); } break; default: { Debug.LogError(string.Format("Unhandled state {0} in StartCurrentAction!", state)); } break; } } private static T ConvertAction(IAssetAsyncAction action) where T : class, IAssetAsyncAction { var result = action as T; if (result == null) Debug.LogError("Logic Error: current Action is not " + typeof(T)); return result; } public static JsonAssetInfo HandleHttpGetAction(HttpsGetAction httpAction) { JsonAssetInfo jsonAssetInfo = null; if (httpAction != null) { if (httpAction.jsonData == null) Debug.LogError("HttpsGetAction Error: failed to get json from server!"); else jsonAssetInfo = JsonAssetInfo.Create(httpAction.jsonData); } return jsonAssetInfo; } private void EndCurrentAction() { error = AssetUpdateError.UnknownError; switch (state) { case AssetUpdateState.GetUpdateInfo: { var httpAction = ConvertAction(_currentAction); error = AssetUpdateError.AssetJsonError; if (httpAction != null) { var jsonAssetInfo = HandleHttpGetAction(httpAction); if (jsonAssetInfo == null) Debug.LogError("Failed to convert jsonData to JsonAssetInfo!"); else { assetUri = jsonAssetInfo.assetUri; assetVersion = jsonAssetInfo.assetVersion; apkUri = jsonAssetInfo.apkUri; apkVersion = jsonAssetInfo.apkVersion; error = apkVersion == Application.version ? AssetUpdateError.Success : AssetUpdateError.ApkUpdate; } } } break; case AssetUpdateState.DownloadAssetList: { var assetInfoAction = ConvertAction(_currentAction); if (assetInfoAction != null) { error = assetInfoAction.error; if (error == AssetUpdateError.Success) mainInfo = assetInfoAction.mainInfo; } } break; case AssetUpdateState.CompareAssetList: { var assetCompareAction = ConvertAction(_currentAction); if (assetCompareAction != null) { downloadList = assetCompareAction.downloadList; streamingPathInfo = assetCompareAction.streamingPathInfo; error = AssetUpdateError.Success; } } break; case AssetUpdateState.DownloadFile: { var downloadAction = ConvertAction(_currentAction); if (downloadAction != null) error = downloadAction.error; } break; case AssetUpdateState.Complete: case AssetUpdateState.Error: break; default: { Debug.LogError(string.Format("Unhandled state {0} in StartCurrentAction!", state)); } break; } } private void HandleStateError() { var errorState = state; state = AssetUpdateState.Error; Debug.LogError("Error on Step " + errorState); switch (error) { case AssetUpdateError.AssetJsonError: { LiteConfirmWin.Open("资源同步错误", "无法获得服务器文件版本号!\n请在网络通畅的情况下再次尝试!", new LiteConfirmButtonData("确定", RestartProcess)); } break; case AssetUpdateError.VersionDataError: { LiteConfirmWin.Open("资源清单错误", "无法获得正确的服务器文件清单!\n请在网络通畅的情况下再次尝试!", new LiteConfirmButtonData("确定", RestartProcess)); } break; case AssetUpdateError.DownloadError: { LiteConfirmWin.Open("资源下载错误", "无法下载所需文件!\n请在网络通畅的情况下再次尝试!", new LiteConfirmButtonData("确定", RestartProcess)); } break; case AssetUpdateError.FileCreateError: { LiteConfirmWin.Open("读写错误", "无法更新资源文件!\n请检查硬盘容量,并确保游戏拥有响应读写权限!", new LiteConfirmButtonData("确定", RestartProcess)); } break; case AssetUpdateError.ApkError: case AssetUpdateError.ApkUpdate: { OpenApkUri(apkUri); } break; case AssetUpdateError.UserCancel: { // 用户自行选择取消更新 RestartProcess(); } break; default: { Debug.LogError(string.Format("Unhandled error type {0} in HandleStateError!", error)); LiteConfirmWin.Open("网络错误", "无法获得服务器列表\n请在网络通畅的情况下再次尝试!", new LiteConfirmButtonData("确定", RestartProcess)); } break; } } private void OnAllActionComplete() { _currentAction = null; TryToEndProcess(); } private static void OnApkUpdateConfirm(string uri) { Debug.Log(string.Format("Open apk uri at {0}!", uri)); Application.OpenURL(uri); // 锁死当前界面,防止用户关闭浏览器后,需要重启唤起 OpenApkUri(uri); } public static void OpenApkUri(string uri) { LiteConfirmWin.Open("安装包更新", "点击确定从商店下载最新版本的游戏安装包!\n" + uri, new LiteConfirmButtonData("确定", () => OnApkUpdateConfirm(uri))); } private void RestartProcess() { SceneHub.LoadScene(SceneHub.GetActiveScene().name); } private void Update() { if (state < AssetUpdateState.Complete && _currentAction != null) // 完成当前Action if (_currentAction.UpdateTimeout()) { // 从当前Action中读取错误码 EndCurrentAction(); if (error == AssetUpdateError.Success) { state++; if (state == AssetUpdateState.Complete) OnAllActionComplete(); else StartCurrentAction(); } else { HandleStateError(); } } } private void TryToEndProcess() { // if (_complete && _ui.complete) EndProcess(); GameObject.DestroyObject(this); if (state == AssetUpdateState.Complete) StartGame(); } private void StartGame() { SaveCurrentVersion(); SceneHub.LoadScene(GlobeVar.sceneLogin); } #region 通用功能 public enum AssetUpdateState { GetUpdateInfo, DownloadAssetList, CompareAssetList, DownloadFile, Complete, Error } public enum AssetUpdateError { Success, FileCreateError, AssetJsonError, VersionDataError, DownloadError, ApkError, UserCancel, ApkUpdate, UnknownError } #endregion }