638 lines
24 KiB
C#
638 lines
24 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
using UnityEngine.UI;
|
|
|
|
namespace XGame
|
|
{
|
|
class UpdateModule : MonoBehaviour
|
|
{
|
|
public static UpdateModule Instance;
|
|
public class ResInfo
|
|
{
|
|
//public string fileName;
|
|
public string md5;
|
|
public string md5Path;
|
|
public int pack;
|
|
public int priority;
|
|
public int size;
|
|
}
|
|
|
|
class PackWairDownList
|
|
{
|
|
public List<string> list = new List<string>();
|
|
public int fileSize = 0;
|
|
public string md5 = "";
|
|
public bool isChange = false;
|
|
}
|
|
#if UNITY_ANDROID
|
|
static string sPlatform = "android";
|
|
#elif UNITY_IOS
|
|
static string sPlatform = "ios";
|
|
#else
|
|
static string sPlatform = "pc";
|
|
#endif
|
|
|
|
GameObject m_MainObj;
|
|
//更新相关
|
|
string UpdateServer;//
|
|
string UpdatePath;
|
|
string ServerListName = "files";
|
|
string UpdateListName = "updatefiles";
|
|
string TempListName = "tempfiles";
|
|
private Dictionary<string, ResInfo> m_gameResDict = new Dictionary<string, ResInfo>();
|
|
private Dictionary<string, ResInfo> m_localResDict = new Dictionary<string, ResInfo>();
|
|
private Dictionary<string, ResInfo> m_tempResDict = new Dictionary<string, ResInfo>();
|
|
private PackWairDownList[] m_packWairDownList = new PackWairDownList[10];
|
|
|
|
private int m_wairDownCount = 0;
|
|
private int m_downDownCount = 0;
|
|
//private bool m_bHotfix = false;
|
|
|
|
string m_sNewMD5;
|
|
string m_sOldMD5;
|
|
//bool m_bChange = false;
|
|
|
|
//UI 相关
|
|
Slider m_sliderUpdate;
|
|
TextMeshProUGUI m_textUpdate;
|
|
XProgress m_Proc;
|
|
|
|
private void Awake()
|
|
{
|
|
Instance = this;
|
|
#if UNITY_WEBGL
|
|
UpdatePath = $"{Application.streamingAssetsPath}/";
|
|
UpdateServer = $"{Application.streamingAssetsPath}/";
|
|
#else
|
|
UpdatePath = Application.persistentDataPath + "/";
|
|
UpdateServer = $"{XGame.AppConst.UpdateServer}{sPlatform}/";//$"http://127.0.0.1/update/{sPlatform}/"; //
|
|
#endif
|
|
m_Proc = new XProgress();
|
|
Init();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
for (int i = 0; i < 10; ++i)
|
|
{
|
|
m_packWairDownList[i] = new PackWairDownList();
|
|
}
|
|
|
|
/*/Test
|
|
//默认Main预设会直接挂好,直接使用下面的Update组件
|
|
GameObject sliderObj = GameObject.Find("Main(Clone)/Update/Slider");//Base");//
|
|
if (sliderObj != null)
|
|
{
|
|
m_sliderUpdate = sliderObj.transform.GetComponent<Slider>();
|
|
}
|
|
m_textUpdate = GameObject.Find("Main(Clone)/Update/Text").transform.GetComponent<TextMeshProUGUI>();
|
|
//*/
|
|
}
|
|
|
|
|
|
public string GetResMD5()
|
|
{
|
|
return m_sNewMD5;
|
|
}
|
|
//设置资源管理器的文件列表hash 字典
|
|
public void SetAllResHash()
|
|
{
|
|
if (m_gameResDict.Count == 0)
|
|
{//还未加载file.txt
|
|
LoadServerResourceMap();
|
|
}
|
|
string str;
|
|
XResLoader.m_ResHash.Clear();
|
|
foreach (var dic in m_gameResDict)
|
|
{
|
|
//保存UpdatePath/res
|
|
Hash128 hash = Hash128.Parse(dic.Value.md5);
|
|
XResLoader.m_ResHash[$"{AppConst.UpdatePath}{dic.Key}"] = hash;
|
|
str = hash.ToString();
|
|
}
|
|
}
|
|
public void AddResHash(Dictionary<string, ResInfo> resList)
|
|
{
|
|
foreach (var dic in resList)
|
|
{
|
|
//保存UpdatePath/res
|
|
Hash128 hash = Hash128.Parse(dic.Value.md5);
|
|
XResLoader.m_ResHash[$"{AppConst.UpdatePath}{dic.Key}"] = hash;
|
|
}
|
|
}
|
|
#if UNITY_WEBGL
|
|
public IEnumerator UpdateResAllWebGL(System.Action onDownloadComplete)
|
|
{
|
|
Dictionary<string, string> dicFilelist = new Dictionary<string, string>();
|
|
|
|
string downloadServer = UpdateServer;
|
|
UnityWebRequest uwr = UnityWebRequest.Get($"{downloadServer}updatever.txt");
|
|
yield return uwr.SendWebRequest();
|
|
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
|
{
|
|
m_sNewMD5 = uwr.downloadHandler.text;
|
|
int npos = m_sNewMD5.IndexOf(',');
|
|
if (npos != -1)//兼容单双md5
|
|
m_sNewMD5 = m_sNewMD5.Substring(npos+1, m_sNewMD5.Length - npos -1);
|
|
Debug.Log($"Download DataFile {m_sNewMD5} OK");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogErrorFormat($"{downloadServer}updatever.txt is not exists");
|
|
yield break;
|
|
}
|
|
}
|
|
#endif
|
|
public IEnumerator UpdateResAll(System.Action onDownloadComplete)
|
|
{
|
|
//检查更新
|
|
string sNewMD5File = $"{UpdatePath}updatever.txt";
|
|
string sOldMD5File = $"{UpdatePath}ver.txt";
|
|
if (File.Exists(sNewMD5File))
|
|
{
|
|
m_sNewMD5 = File.ReadAllText(sNewMD5File);
|
|
int npos = m_sNewMD5.IndexOf(',');
|
|
if (npos != -1)//兼容单双md5
|
|
m_sNewMD5 = m_sNewMD5.Substring(npos + 1, m_sNewMD5.Length - npos - 1);
|
|
}
|
|
else
|
|
{
|
|
//直接结束
|
|
if(onDownloadComplete != null)
|
|
onDownloadComplete();
|
|
yield break;
|
|
}
|
|
if (File.Exists(sOldMD5File))
|
|
{
|
|
m_sOldMD5 = File.ReadAllText(sOldMD5File);
|
|
}
|
|
else
|
|
{//无文件,说明是第一次需要都更新
|
|
//m_bChange = true;
|
|
}
|
|
if (m_sNewMD5 != m_sOldMD5)
|
|
{
|
|
//m_bChange = true;
|
|
}
|
|
else
|
|
{
|
|
//无差异直接结束
|
|
if (onDownloadComplete != null)
|
|
onDownloadComplete();
|
|
yield break;
|
|
}
|
|
yield return DownLoadAssets(onDownloadComplete);
|
|
}
|
|
|
|
//进度变化的回调
|
|
void UpdateProcess(int curFinish, int totalNum)
|
|
{
|
|
if(totalNum > 0)
|
|
m_Proc.Set(curFinish * 100 / totalNum);
|
|
//if(m_sliderUpdate != null)
|
|
// m_sliderUpdate.value = curFinish;
|
|
}
|
|
void OnFileDownloadBegin(string filename, int data)
|
|
{
|
|
}
|
|
void WriteVerFile()
|
|
{
|
|
using (StreamWriter sw = new StreamWriter($"{UpdatePath}ver.txt"))
|
|
{
|
|
sw.WriteLine(m_sNewMD5);
|
|
}
|
|
}
|
|
IEnumerator DownLoadAssets(Action onDownloadComplete)
|
|
{
|
|
bool isVerChange = true;
|
|
string[] sNewMd5s = m_sNewMD5.Split(',');
|
|
string[] sOldMd5s = { "SDFASD123SFDSerw","gagfewr21dsfa" };
|
|
if(!string.IsNullOrEmpty(m_sOldMD5))
|
|
sOldMd5s = m_sOldMD5.Split(',');
|
|
if (sNewMd5s[0] == sOldMd5s[0])
|
|
{//资源md5相同,写ver更新结束
|
|
WriteVerFile();
|
|
onDownloadComplete();
|
|
yield break;
|
|
}
|
|
//更新流程
|
|
//获得版本号updatever.txt,对比本地ver.txt
|
|
//yield return CheckPackage(UpdateServer, "updatever.txt", "ver.txt", (isChange, newmd5) =>
|
|
//{
|
|
// isVerChange = isChange;
|
|
// m_packWairDownList[0].md5 = newmd5;
|
|
// m_packWairDownList[0].isChange = isChange;
|
|
//});
|
|
m_packWairDownList[0].md5 = sNewMd5s[0];
|
|
m_packWairDownList[0].isChange = true;
|
|
//Debug.Log($"Update Res List Begin : {sNewMd5s[0]}");
|
|
if (isVerChange)
|
|
{
|
|
m_gameResDict.Clear();
|
|
m_localResDict.Clear();
|
|
m_tempResDict.Clear();
|
|
//有改变,需要下载并对比列表文件
|
|
yield return DownloadFile(UpdateServer, $"{ServerListName}.txt", result =>
|
|
{
|
|
if (result > 0)
|
|
{
|
|
System.DateTime dt = System.DateTime.Now;
|
|
LoadServerResourceMap();
|
|
Debug.LogFormat("LoadServerResourceMap {0} 耗时:{1}ms", ServerListName, (System.DateTime.Now - dt).TotalMilliseconds);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogFormat("File not exist : {0}{1}", UpdatePath, ServerListName);
|
|
isVerChange = false;
|
|
}
|
|
});
|
|
if (!isVerChange)//失败
|
|
{
|
|
onDownloadComplete();
|
|
yield break;
|
|
}
|
|
yield return LoadPackageResourceMap();
|
|
LoadLocalResourceMap();
|
|
|
|
int count = 1;
|
|
foreach (var gameRes in m_gameResDict)
|
|
{
|
|
ResInfo localRes = null;
|
|
if (m_localResDict.TryGetValue(gameRes.Key, out localRes) && localRes.md5 == gameRes.Value.md5)
|
|
{
|
|
//m_existFiles[gameRes.Key] = EExistLocation.Local;
|
|
}
|
|
else
|
|
{
|
|
if (m_tempResDict.TryGetValue(gameRes.Key, out localRes) && localRes.md5 == gameRes.Value.md5)
|
|
{
|
|
//m_existFiles[gameRes.Key] = EExistLocation.Local;
|
|
}
|
|
else
|
|
{
|
|
var packList = m_packWairDownList[0];//暂时只用一份更新,无分包
|
|
packList.list.Add(gameRes.Key);
|
|
packList.fileSize += gameRes.Value.size;
|
|
|
|
}
|
|
}
|
|
//m_packSizeRecord[gameRes.Value.pack - 1] += gameRes.Value.size;
|
|
UpdateProcess(count++, m_gameResDict.Count);
|
|
}
|
|
//m_sliderUpdate.maxValue = m_packWairDownList[0].list.Count;
|
|
//m_sliderUpdate.minValue = 0;
|
|
//m_sliderUpdate.value = 0;
|
|
m_Proc.Set(0);
|
|
//写入ver.txt文件(md5) 这样可以防止以后服务器没更新的时候还下载检查更新
|
|
if (m_packWairDownList[0].fileSize <= 0)
|
|
{
|
|
WriteVerFile();
|
|
isVerChange = false;
|
|
m_packWairDownList[0].isChange = false;
|
|
}
|
|
//下载过程
|
|
UnityWebRequest uwr;
|
|
string downloadServer = UpdateServer;
|
|
Debug.LogFormat("downloadServer : {0} Total:{1}", downloadServer, m_packWairDownList[0].list.Count);
|
|
int i = 0;
|
|
count = m_packWairDownList[0].list.Count;
|
|
string tempListFile = UpdatePath + TempListName + ".txt";
|
|
StreamWriter fs = null;
|
|
if (count > 0)
|
|
{
|
|
fs = new StreamWriter(tempListFile, true);
|
|
}
|
|
while (i < count)
|
|
{
|
|
string namePath = m_packWairDownList[0].list[i];
|
|
var path = downloadServer + namePath;
|
|
string outfile = UpdatePath + namePath;
|
|
|
|
uwr = UnityWebRequest.Get(path);
|
|
|
|
#region 下列代码修改下载的方式 用于处理断点下载和堆内存峰值
|
|
// by xl
|
|
string tempFile = outfile + ".temp";
|
|
if (File.Exists(tempFile))
|
|
{
|
|
File.Delete(tempFile);
|
|
}
|
|
XDownloadHandler dh = new XDownloadHandler(outfile);
|
|
dh.OnFinish = (int size) =>
|
|
{
|
|
if (size > 0)
|
|
{
|
|
if (m_gameResDict.TryGetValue(namePath, out ResInfo resInfo))
|
|
{
|
|
//没关闭流 可能写不完整
|
|
fs?.WriteLine(string.Format("{{'{0}','{1}','{2}'}}",
|
|
namePath, resInfo.md5, 1, resInfo.size));
|
|
}
|
|
++m_downDownCount;
|
|
if (m_textUpdate)
|
|
{
|
|
int percent = 100 * m_downDownCount / count;
|
|
m_textUpdate.text = $"{percent}%";//namePath;
|
|
}
|
|
UpdateProcess(m_downDownCount, m_wairDownCount);
|
|
++i;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("File Miss : " + path);
|
|
}
|
|
};
|
|
uwr.downloadHandler = dh;
|
|
#endregion
|
|
|
|
yield return uwr.SendWebRequest();
|
|
|
|
if (uwr.result != UnityWebRequest.Result.Success)
|
|
{
|
|
Debug.LogError("File Download Error : " + path);
|
|
}
|
|
|
|
}
|
|
//m_sliderUpdate.value = m_packWairDownList[0].list.Count;
|
|
m_packWairDownList[0].list.Clear();
|
|
m_packWairDownList[0].fileSize = 0;
|
|
m_packWairDownList[0].isChange = false;
|
|
//using (StreamWriter sw = new StreamWriter(string.Format("{0}ver.txt", UpdatePath)))
|
|
//{
|
|
// sw.WriteLine(m_packWairDownList[0].md5);
|
|
//}
|
|
WriteVerFile();
|
|
|
|
//if (pack <= 1)
|
|
{
|
|
fs?.Close();
|
|
yield return DownloadFile(downloadServer, ServerListName + ".txt", result =>
|
|
{
|
|
if (result > 0)
|
|
{
|
|
if (File.Exists(tempListFile))
|
|
{
|
|
File.Delete(tempListFile);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogErrorFormat("{0} is not exists", downloadServer + ServerListName + ".txt");
|
|
}
|
|
});
|
|
m_wairDownCount = 0;
|
|
m_downDownCount = 0;
|
|
//m_Proc.Set(100);
|
|
//m_downEndFun(isDllUpdate ? 1 : 0);
|
|
//yield break;
|
|
}
|
|
}
|
|
|
|
Debug.Log("Download Finish!");
|
|
if(onDownloadComplete != null)
|
|
onDownloadComplete();
|
|
}
|
|
|
|
//协程下载文件
|
|
IEnumerator DownloadFile(string downloadServer, string fileName, System.Action<int> onFinish)
|
|
{
|
|
UnityWebRequest uwr;
|
|
//Logger.LogFormat("download ServerList {0}", downloadServer + fileName);
|
|
uwr = UnityWebRequest.Get(downloadServer + fileName);
|
|
// uwr.timeout = m_httpRequestTimeout;
|
|
yield return uwr.SendWebRequest();
|
|
if (uwr.downloadHandler.isDone && uwr.result == UnityWebRequest.Result.Success)
|
|
{
|
|
string outfile = UpdatePath + fileName;
|
|
string dir = Path.GetDirectoryName(outfile);
|
|
//if (!Directory.Exists(dir))
|
|
//{
|
|
// Directory.CreateDirectory(dir);
|
|
//}
|
|
if (File.Exists(outfile))
|
|
File.Delete(outfile);
|
|
File.WriteAllBytes(outfile, uwr.downloadHandler.data);
|
|
onFinish(uwr.downloadHandler.data.Length);
|
|
}
|
|
else
|
|
{
|
|
//Logger.LogFormat("Download Failed {0}", downloadServer + fileName);
|
|
onFinish(0);
|
|
//if (RequestErrorCallback != null) { RequestErrorCallback("DownloadFile", uwr.error); }
|
|
}
|
|
}
|
|
IEnumerator CheckPackage(string downloadServer, string fileName, string verName, System.Action<bool, string> onFinish)
|
|
{
|
|
yield return DownloadFile(downloadServer, fileName, result =>
|
|
{
|
|
bool ischange = true;
|
|
string vermd51 = null, newmd51 = null;
|
|
if (result > 0 && File.Exists(UpdatePath + fileName))
|
|
{
|
|
using (StreamReader sr = new StreamReader(UpdatePath + fileName))
|
|
{
|
|
string line;
|
|
line = sr.ReadLine();
|
|
if (line != null || line != "")
|
|
{
|
|
newmd51 = line;
|
|
}
|
|
}
|
|
if (File.Exists(UpdatePath + verName))
|
|
{
|
|
using (StreamReader sr = new StreamReader(UpdatePath + verName))
|
|
{
|
|
string line;
|
|
line = sr.ReadLine();
|
|
if (line != null || line != "")
|
|
{
|
|
vermd51 = line;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newmd51 != null)
|
|
{
|
|
if (vermd51 != null && newmd51 == vermd51)
|
|
{
|
|
ischange = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ischange = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ischange = false;
|
|
}
|
|
onFinish(ischange, newmd51);
|
|
});
|
|
}
|
|
|
|
//加载包内文件
|
|
IEnumerator LoadPackageResourceMap()
|
|
{
|
|
string content;
|
|
//在协程里,加载完才往下走
|
|
yield return XResLoader.OnLoadPackageFile($"{ServerListName}.txt", (data) =>
|
|
{
|
|
string str;
|
|
if (data != null)
|
|
{
|
|
str = System.Text.Encoding.UTF8.GetString(data);
|
|
TextAsset textAsset = new TextAsset(str);//Resources.Load<TextAsset>($"{ServerListName}.txt");
|
|
|
|
if (textAsset != null)
|
|
{
|
|
content = textAsset.text;
|
|
LoadResourceMap(content, m_localResDict);
|
|
}
|
|
}
|
|
Debug.LogFormat("{0} file(s) exist in game.", m_localResDict.Count);
|
|
});
|
|
|
|
}
|
|
void LoadLocalResourceMap()
|
|
{
|
|
string localListFile = UpdatePath + UpdateListName + ".txt";
|
|
string content = "";
|
|
if (File.Exists(localListFile))
|
|
{
|
|
content = File.ReadAllText(localListFile);
|
|
LoadResourceMap(content, m_localResDict);
|
|
}
|
|
}
|
|
|
|
void LoadTempResourceMap()
|
|
{
|
|
string tempListFile = UpdatePath + TempListName + ".txt";
|
|
string content = "";
|
|
if (File.Exists(tempListFile))
|
|
{
|
|
content = File.ReadAllText(tempListFile);
|
|
//此处是为了修复 文件写入不完整的问题
|
|
if (!string.IsNullOrEmpty(content) && !content.EndsWith("}"))
|
|
{
|
|
int lastIdx = content.LastIndexOf("}");
|
|
content = content.Substring(0, lastIdx + 2);//包含换行
|
|
File.WriteAllText(tempListFile, content);
|
|
}
|
|
LoadResourceMap(content, m_tempResDict);
|
|
}
|
|
}
|
|
|
|
void LoadResourceMap(string content, Dictionary<string, ResInfo> resMap)
|
|
{
|
|
if (string.IsNullOrEmpty(content))
|
|
{
|
|
Debug.LogWarningFormat("Can't load file_list");
|
|
return;
|
|
}
|
|
string[] lines = content.Split('\n');
|
|
if (lines == null)
|
|
{
|
|
Debug.LogWarningFormat("Can't load res_list, it is required!");
|
|
return;
|
|
}
|
|
foreach (string line in lines)
|
|
{
|
|
if (line.Length < 2) { continue; }
|
|
|
|
string s = line.Substring(1, line.Length - 1 - 2); //去掉开头的{'和结尾的},
|
|
s = s.Replace("\'", ""); //把lua的字符串包围符去掉.
|
|
|
|
string[] strs = s.Split(',');
|
|
if (strs.Length > 2)
|
|
{
|
|
ResInfo resInfo = new ResInfo();
|
|
resInfo.md5Path = strs[0];
|
|
resInfo.md5 = strs[1];
|
|
resInfo.size = int.Parse(strs[2]);
|
|
resMap[strs[0]] = resInfo;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarningFormat("invalid line: ", line);
|
|
}
|
|
}
|
|
}
|
|
|
|
void LoadServerResourceMap()
|
|
{
|
|
string listPath = $"{UpdatePath}{ServerListName}.txt";
|
|
if (!File.Exists(listPath))
|
|
{//本地还没有下载过资源清单(首次启动 / 未触发增量更新),无缓存可读,安全跳过
|
|
Debug.LogWarning($"LoadServerResourceMap skip, not found: {listPath}");
|
|
return;
|
|
}
|
|
string content;//= ResourceLoader.instance.LoadAssetString(ServerListName + ".txt");//Android上读的是内部的文件
|
|
byte[] data;
|
|
long size;
|
|
using (var stream = File.OpenRead(listPath))// _{ m_sNewMD5}
|
|
{
|
|
size = stream.Seek(0, SeekOrigin.End);
|
|
data = new byte[size];
|
|
stream.Seek(0, SeekOrigin.Begin);
|
|
stream.Read(data, 0, data.Length);
|
|
content = System.Text.Encoding.UTF8.GetString(data);
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(content))
|
|
{
|
|
Debug.LogWarningFormat("LoadServerResourceMap Can't load file_list");
|
|
}
|
|
|
|
string[] lines = content.Split('\n');
|
|
//Debug.LogFormat("{0} lines in ServerListName", lines.Length);
|
|
if (lines != null)
|
|
{
|
|
foreach (string line in lines)
|
|
{
|
|
if (line.Length < 2) { continue; }
|
|
|
|
//fileName, md5, size
|
|
//{'ui/copy_fui.ab','2ca8dd785bf3e99decaab1f9e3e4cd22',1257},
|
|
string s = line.Substring(1, line.Length - 1 - 2); //去掉开头的{'和结尾的},
|
|
s = s.Replace("\'", ""); //把lua的字符串包围符去掉.
|
|
|
|
string[] strs = s.Split(',');
|
|
if (strs.Length > 2)
|
|
{
|
|
ResInfo resInfo = new ResInfo();
|
|
resInfo.md5Path = strs[0];
|
|
resInfo.md5 = strs[1];
|
|
resInfo.size = int.Parse(strs[2]);
|
|
m_gameResDict[strs[0]] = resInfo;
|
|
}
|
|
else
|
|
{
|
|
//Logger.LogWarningFormat("invalid line: ", line);
|
|
}
|
|
}
|
|
//Logger.LogFormat("{0} file(s) exist in game.", m_gameResDict.Count);
|
|
}
|
|
else
|
|
{//失败
|
|
//throw new InvalidResListException("Can't load server_res_list, it is required!");
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
//m_Proc.Update();
|
|
}
|
|
|
|
public XProgress GetProgress()
|
|
{
|
|
return m_Proc;
|
|
}
|
|
}
|
|
} |