游戏热更新
version数据解析
在本地打包时会有跳过热更新或者测试热更新的需求,但没有文档解析打包界面version相关选项的作用,也不知道登录界面版本字符串的含义,所以这篇文章对打包过程的version相关参数和选项作详细解析,以及简单讲解游戏包更新流程。
参数解析
- Build Package的Version:仅用作展示,作为游戏登录界面的版本字符串的一部分。
- Build Package的VersionCode:作为GameConst.txt里的NativeVersionCode,用来更新包外的GameConst.txt。当包内与包外的GameConst里的NativeVersionCode不同时,会覆盖外部GameConst.txt文件。另一个作用是作为请求remoteVersionManifest文件获取最新版本号的参数。
- 游戏常量配置的游戏是否启用热更新:对应GameConst.txt的gameOpenHotUpdate字段,可以忽略Version而跳过热更。
- 登录界面的版本字符串分为MSDK和非MSDK格式,区别就是MSDK格式多了腾讯热更版本号,对游戏更新逻辑没影响。
- MSDK格式:V{msdkVersion}-{LocalVersion}-{VersionName}-{VersionCode}
- 非MSDK格式:V{LocalVersion}-{VersionName}-{VersionCode}
LocalVersion用于对比remoteVersion决定更新,VersionName对应Build Package的Version,VersionCode对应Build Package的VersionCode。
LocalVersion的值记录于Assets\Resources\version.manifest文件中
更新流程
版本号a.b.c各级的作用:
热更线程代码
DownloadThread.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class DownloadThread { public void Start() { if (!_isStop || _thread != null) { CLogger.Log("DownloadThread::Start() - Download Thread Already Started:" + !_isStop + "#thread:" + ((_thread == null) ? "null" : _thread.ManagedThreadId.ToString())); return; } _isStop = false; _thread = new Thread(new ThreadStart(RunDownloading)); CLogger.Log("DownloadThread::Start() - Create Download Thread:" + _thread.ManagedThreadId); _thread.IsBackground = true; _thread.Start(); } private void RunDownloading() { for (; ; ) { if (_isStop) { CLogger.Log("DownloadThread::RunDownloading() - Download Thread Meet Stop Flag,ThreadId:" + Thread.CurrentThread.ManagedThreadId); break; } if (_currentTask == null) { lock (_pendingTasks) { int num = _pendingTasks.Count; if (num > 0) { _currentTask = _pendingTasks.Dequeue(); } else { _isWaitting = true; this.Wait(); _isWaitting = false; } } }
if (!_isStop && _currentTask != null) { DownloadFromBreakPoint(_currentTask); } } }
|
lock关键字
IEquatasble https://www.cnblogs.com/lian–ying/p/9502879.html
C#多线程编程
http://images.china-pub.com/ebook4610001-4615000/4613657/ch01.pdf
http://apps.mxinfos.com/电子书籍/多线程编程.pdf
断点续传
下载热更文件使用了断点续传,原因是当下载大文件中途断网或退出时,可以保证下次更新在上次下载进度基础上继续正常下载。
断点续传需要服务端支持,需支持允许分段方式请求的文件数据。
使用http协议头字段range告知服务器下载文件字节数据范围值,例如:range:bytes=500-1000。配合If-Range:Etag/if-modified判断文件是否发生变化。详细看考这篇文章。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| if (File.Exists(task.storagePath)) { using (FileStream fileStream = new FileStream(task.storagePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) { receivedLength = fileStream.Length; toDownloadLength = totalLength - receivedLength; fileStream.Close(); }
if (receivedLength != dfi.receivedSize) { CLogger.Log(string.Format("DownloadThread::DownloadFromBreakPoint() - break point save receive size is wrong for file[{0}], saveSize={1}, fileSize={2}", _currentTaskFileName, dfi.receivedSize, receivedLength)); } } task.fileLength = totalLength; task.receivedLength = receivedLength; _currentTaskTotalBytes = totalLength; _currentTaskReceivedBytes = receivedLength;
bool transferOkay = true; if (toDownloadLength > 0L) { CLogger.Log("DownloadThread::DownloadFromBreakPoint() - start http download, The request url is [" + uri + "] with range [" + receivedLength + "," + totalLength + "]");
HttpWebRequest request2 = (HttpWebRequest)WebRequest.Create(uri); request2.Timeout = kTimeOut; request2.KeepAlive = true; request2.ReadWriteTimeout = kTimeOut; request2.AddRange((int)receivedLength, (int)totalLength);
HttpWebResponse response2 = (HttpWebResponse)request2.GetResponse(); transferOkay = this.ReadBytesFromResponse(task, response2); response2.Close(); request2.Abort(); } if (transferOkay) { this.OnDownloadFinished(task, null); }
private bool ReadBytesFromResponse(DownloadTask task, WebResponse response) { bool okay = false; DownloadFileTransferInfo fileInfo = _transferMgr.GetDownloadFileInfo(task.file); FileUtils.Instance.CheckDirExistsForFile(task.storagePath);
using (FileStream fileStream = new FileStream(task.storagePath, task.receivedLength == 0 ? FileMode.Create : FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) { fileStream.Position = task.receivedLength; byte[] array = new byte[1024]; using (Stream responseStream = response.GetResponseStream()) { int bytesRead = 0; while (task.receivedLength < task.fileLength) { bytesRead = responseStream.Read(array, 0, array.Length); fileStream.Write(array, 0, bytesRead); task.receivedLength += bytesRead; _currentTaskReceivedBytes = task.receivedLength;
_transferMgr.UpdateFileTransferProgress(fileInfo, task.receivedLength); }
okay = true; }
if (task.receivedLength != task.fileLength) { string s = string.Format("DownloadThread::ReadBytesFromResponse() - Download length not fit Error:{0}/{1}", task.receivedLength, task.fileLength); CLogger.LogError(s); okay = false; this.OnDownloadFinished(task, new Exception(s)); } }
return okay; }
|
上面断点续传的是ab文件,下面续传zip文件:
BreakpointTransferZip.cs
热更包与基线
热更文件检出
热更包的资源文件通过对比两个版本的资源变更情况得出
用git对比?选出具有相同目录结构文件?
网上的一般方式是用MD5检出变更的文件,服务器会下发最新版文件的MD5信息,用之和本地文件MD5对比。
应不用选出具有相同目录结构的文件包,服务端会下发热更文件的相对存储路径。
基线怎么来呢?
很简单,记录上一次打包的所有ab到cache文件即可。百田热更基线记录abCache的时机为每次发热更包后,腾讯的则是发整包版本后。