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;
}