410 likes | 642 Vues
征途客户端的更新过程. 征途项目中心 绿色征途项目组. 演讲人:吴明生. 一 . 流程. 征途项目中心 绿色征途项目组. 二 . 几点补充. 三 . Q & A. 四 . 以后的专研方向. 六 . 如何初步确定蓝屏原因. 五 . 关于 BUG 重现那点. 1 客户端数据的流动过程. 征途项目中心 绿色征途项目组. 流程. 2 更新程序的更新过程. 三 . Q & A. 四 . 以后的专研方向. 六 . 如何初步确定蓝屏原因.
E N D
征途客户端的更新过程 征途项目中心绿色征途项目组 演讲人:吴明生
一. 流程 征途项目中心绿色征途项目组 二.几点补充 三.Q & A 四.以后的专研方向 六.如何初步确定蓝屏原因 五.关于BUG重现那点
1 客户端数据的流动过程 征途项目中心绿色征途项目组 流程 2 更新程序的更新过程 三.Q & A 四.以后的专研方向 六.如何初步确定蓝屏原因 五.关于BUG重现那点
征途项目中心绿色征途项目组 客户端数据流动过程 表格文件和配置文件经过文件打包工具(makefilepack.exe)生成datas.pak 图片文件经过打图包工具(makeglnew)生成相应的图包文件如npc.pak body.pak... 做征途客户端的应该对前面的打图包打表比较熟悉,下面举个例子介绍差异生成的结果,为后面详细介绍更新程序做准备
客户端数据流动过程 征途项目中心绿色征途项目组 假设一个功能增加了如下文件: 通过差异生成工具会生产如下文件::
客户端数据流动过程 征途项目中心绿色征途项目组 通过差异生成工具生成的文件 上传到更新服务器的时候先上传差异文件,最后上传flist.xml。玩家就可以更新了,接下来会详细介绍更新程序的更新流程
更新程序的更新过程 征途项目中心绿色征途项目组
更新程序的更新过程 征途项目中心绿色征途项目组
更新程序的更新过程 征途项目中心绿色征途项目组
下载服务器列表 征途项目中心绿色征途项目组 服务器列表文件srvlist.xml
下载服务器列表 征途项目中心绿色征途项目组 更新程序启动的时候会启动服务器列表下载线程,来下载服务器列表文件srvlist.xml 服务器列表下载线程会根据当前是电信还是网通的选择相应的http地址来下载服务器列表文件,一般情况下都有好几个下载地址,当一个下载不了的时候会选择下一个下载地址。服务器列表文件有两个版本,压缩的和非压缩的,优先下载压缩的,如果压缩的下载不下来则下载非压缩的
下载服务器列表 征途项目中心绿色征途项目组 代码片段
下载服务器列表 征途项目中心绿色征途项目组 代码片段
下载更新文件列表 征途项目中心绿色征途项目组 在更新程序点“更新”按钮的时候会启一个更新文件列表加载线程来加载本地的flist.xml,防止主线程卡住,下面是一个flist.xml的部分截图,它包含了要更新文件的路径,文件名,文件时间,校验码,文件大小等等信息。
下载更新文件列表 征途项目中心绿色征途项目组 在加载完flist.xml(只是本地的,还是开始下载),向主窗口发送LOADFLIST_COMPLETE消息 主窗口在收到LOADFLIST_COMPLETE消息开始启动文件下载线程,下载服务器上的更新文件列表 征途客户端更新过程介绍 第 02页
下载更新文件列表 征途项目中心绿色征途项目组 下载更新文件列表代码片段
下载具体的文件 征途项目中心绿色征途项目组 在更新文件列表下载下来以后,就开始下载具体的文件,直接看代码片段 )
下载具体的文件 征途项目中心绿色征途项目组 SortDownlist() SortDownList函数把更新文件列表中的文件都拿出来放到一个mDownlist的结构中,这里图包是作为一个文件(一个整体)插入到mDownlist中的,然后对mDownlist进行排序,像一些重要的文件比如更新程序自身,反病毒的程序放在最后下载。排完序后就会遍历mDownlist进行文件的下载工作 ProcessExistFiles () ProcessExistFiles函数进行下载前的准备工作,比如检查 图包的完整性,看看图包能否打开,下面将详细介绍 ProcessNormalFiles(),ProcessNomalFiles()才进行真正 的下载工作。
下载具体的文件 征途项目中心绿色征途项目组 ProcessNoamlFiles()
下载具体的文件 征途项目中心绿色征途项目组 直接看代码片段 DownloadFIle(szSrcFileName, szFileName, item.szie, item.dwCRC,, &item.downloadSucceeded) { //得到临时目标文件名 Char szTempDstFile[MAX_PATH]; Sprintf(szTempDstFile, “%s.tmp”, dst) //如果已经存在临时文件(比如datas.pak.tmp),则打开并计算临时文件的CRC,如果 //临时文件的CRC和当前下载列表的CRC是一样的,则不用下载,直接用临时文件覆 //盖目标文件 If (IsFileExist(szTempDstFile)) { DWORD dwCRCOld = calculateCRCStream(&str); If (dwCRCOld == dwCRC) { *bDownloadSucceeded = true; Return UpdateFile(szTemDstFile, dst); } } }
下载具体的文件 征途项目中心绿色征途项目组 直接看代码片段 //如果目标文件已经存在,并且目标文件的CRC和要下载的文件的CRC一样的话,也不需 //要下载 If (IsFileExist(dst)) { 。。。 } //建立临时文件(压缩)(比如magic6.pak.dl) _snprintf(szTempFile, MAX_PATH, “%s.dl”, dst); szTemFile[MAX_PATH - 1] = 0; MakeFileDir(szTemFIle, 0) FileStream fstr; // 生成临时文件 If (!fstr.open(szTempFile, FileStream::Write)) { appDump << “不能打开文件写入\t” << szTempFile << “\n” return false; }
下载具体的文件 征途项目中心绿色征途项目组 直接看代码片段 //生成服务器源文件路径 _snprintf(szSrcFile, MAX_PATH, “%s/%s”, mPath.c_str(), src); mPath(“/Zhengtu/Client/Autoupdate6”) src(“data\magic6.pak”) //取得压缩文件名,将服务器文件下载保存到临时文件 Cosnt char* pFile = GetCompressFileNmae(szSrcFile) Try{ mWebConnection.Get2(pFile, & fstr) } //把从服务器下载的压缩文件解压并覆盖目标文件 enumStreamError retUncompress = uncompressStream(szTemFile, dst); //如果解压不成功,则解压到以tmp结尾的临时文件中 uncompressStream(szTempFile,szTemDstFile) //解压结束后不管解压有没成功,会删除下载下来的压缩文件(如magic6.pak.dl)
下载具体的文件 征途项目中心绿色征途项目组 直接看代码片段 //下载并解压完一个文件后还有个校验的过程 // 读取目标文件 // 计算目标文件校验码 // 对比目标文件和文件列表的校验码 // 如果打不开目标文件或者校验码不一致,都算下载失败 ::MyDeleteFile(dst) *bDownloadSucceeded = false; }
下载具体的文件 征途项目中心绿色征途项目组 下载结束后的处理 //在下载线程中,所有的下载完成后会断开web连接 mWebConnection.Disconnect(); //并调用函数 //OnEndDownload(!mEnd) //OnEndDownload保存本地的flist.xml并且更新下载状态,最后给//主窗口发送WM_DOWNLOADEND消息 mDownloadSucceed = CheckDownloadSucceeded(); mUpdateSucceded = CheckUpdateSucceeded(); SaveFList(mFiles, “flist.xml”, true, flase) gbDownloadSucceeded = IsDownloadSucceeded(); gbUpdateSucceeded = IsUpdateSucceeded();
下载具体的文件 征途项目中心绿色征途项目组 下载结束后的处理 主窗口在对WM_DOWNLOADEND的处理函数中设置“更新完成,如果先前是直接点开始游戏额,这时候就启动游戏。 CPatchUpdateDlg::OnEndDownload() { If (gbUpdateSelf) { UpdateSelf(); } If (gbDownloadSucceded { 更新完成 如需要则启动游戏 } Else { SetLabInfo(“游戏更新失败,可能网络错误或IO失败 If (!sExit) { // 重新连接 SetLabInfo(“重新连接。。。) OnBnClickedButtonUpdate(); } } }
嘟嘟更新 征途项目中心绿色征途项目组 最近绿色版新放了dudu完整版的一些功能,更新程序也做了一些改动,玩家更新下来后,如下所示:会在,《游戏》根目录下新建一个dudu目录。
嘟嘟更新 征途项目中心绿色征途项目组 关于游戏自动更新工具修改的流程如下: 1,自动更新首先会下载更新,对更新列表中的每个待处理的文件节点进行排序。 我们会故意将dudu\dudu.exe 这个节点排到最后一个 2,在每次更新前,检测dudu是否已安装过。 3,实际更新文件的过程中。 如果安装过,则更新的过程中凡是遇到dudu的文件直接跳过,不作任何处理。 如果没有安装过,则按照正常的流程更新。 程序中是怎么检测dudu是否安装过的? 1, 如果用户手动下载《嘟嘟》,然后运行安装程序后,会写入注册表:HKEY_CURRENT_USER\Sotfware\上海巨人网络科技有限公司\ GiantIM\ InstallPath 2, 如果用户通过其他任何渠道,拿到一个绿色免安装版的《嘟嘟》。只要运行dudu.exe,同样会写入上述注册表中信息。 也就是说,从外部安装《嘟嘟》,是可以从注册表中查出来的。
嘟嘟更新 征途项目中心绿色征途项目组 自动更新工具是这样检测嘟嘟是否安装过的: 1, 如果注册表中,可以看出安装过,那就肯定安装过(手动删除嘟嘟目录后认为 未安装过) 2, 如果注册表中,显示没有安装过,但是《游戏》根目录的dudu目录下存在dudu.exe这个文件,则也认定嘟嘟安装过。 3, 否则,则《嘟嘟》没有安装过。
二.几点补充 征途项目中心绿色征途项目组 文件命名 线 程 自更新 增量更新 二进制更新
文件命名 征途项目中心绿色征途项目组 下载图包是临时文件夹得命名: 图包名去掉后缀+”_pack, 比如magic4.pak 临时文件夹得名字是magic4_pack 下载压缩包的命名 要下载的文件+”dl”比如:datas.pak 对应下载下来的压缩包的名字是datas.pak.dl 临时文件的命名: +”.tmp”比如datas.pak.tmp. 临时文件的用途相当于一种容错,当压缩文件下载下来 的时候,如果解压并覆盖目标文件出错,则把文件解压到临时文件中,下次更新 的时候发现有临时文件在 ,则优先比较临时文件的CRC,如果CRC和要下载的文件 CRC一样,则直接用临时文件替换目标文件。
线程 征途项目中心绿色征途项目组 征途更新程序里线程用得比较多 主线程: 主窗口所在的线程,协调各个线程的关系 下载服务器列表线程ThreadProc() 主窗口(线程)在OnInitDialog()时就启动服务器列表下载线程, 下载结束的时候给主窗口发送SERVERLIST_READY消息 下载网页线程DownloadBoardHtemThreadProc() 主窗口在收到SERVERLIST_REDDY 消息启动网页下载线程,并把服务器列表显示在树控件中 本地flist.xml加载线程LoadFListProc() 点击游戏更新的时候会启动LoadFListProc ,加载结束会向主窗口发送LOADFLIST_COMPLETE消息 文件下载线程DownloadProcT() 主窗口在收到LOADFLIST_COMPLETE消息后实例化 PatchDownload对象,并由其函数BegingDownload()启动文件下载线程,进行flist.xml文件 以及游戏文件的下载,等所有文件下载结束后,保存最新的flist.xml,并且给主窗口发送 下载结束的消息WM_DOWNLOADEND,主窗口在收到WM_DOWNLOADEND消息后根据 条件进行游戏启动还是再尝试更新的操作 至于其它的如 木马检测线程ping线程这里就不讨论了 。
自更新 征途项目中心绿色征途项目组 所谓的自更新就是更新程序更新自己,一般情况下程序运行起来后是不允许直接替换的, 征途更新程序的自更新是通过重命名实现的 For (tDwonItems::iterator it = mDownlist.begin(); it!= mDownlist.lend; ++it { .。。。 If (stricmp(szFileName, gszExeName == 0) { ::rename(“patchuodate.exe, “patchupdate.exe.tmp2); 。。。 } 重命名后就可一与更新普通游戏文件一样正常更新了,新的更新程序在下次启动时生效, 重命名后的文件也相当于一个备份,如果凑巧在更新patchupdate.exe的时候断电或则重启, 则可以通过手动把patchupdate.exe.tmp2命名回去 。
增量更新 征途项目中心绿色征途项目组 征途客户端的更新采用增量更新的机制,也就是说每次更新只更新修改过 或则新增的文件,而不是更新全部文件,这种机制相对于完整更新的好处 是更新是比较少的,速度非常快,缺点是在不同的区来回更新的时候有时 会出现图包错误,解决这个错误就是通过完整更新实现,目前的资源修复 工具就是通过完整更新实现的。下面来看一下不同的版本来回更新怎么 出问题的以及怎么避免 先来看看 图包的组织,图包是按照 组/帧来组织的,一个图包下面可以 有很多组,组下面可以有很多帧,每一帧又可以是一张或则多张图片。
增量更新 征途项目中心绿色征途项目组 图包是按照组来组织的,打图包时,图包下面的每一组都会生成.grp 文件,同时每个图包还会生成一个attributes.ghd文件, 这个文件包含了 每个.grp文件的校验信息,来保证图包的完整性 我们游戏第一次更新出去的时候,更新文件列表是空的,因为第一次会 先放一个完整的客户端,我们是增量更新,完整客户端和完整客户端比, 没有任何增量。假定之后我们放了一次全区版本,Interfaces2下增加了 324组的图包,这时候全区的更新文件列表如下: 假如测试区一个功能在原有的328组下增加了新的图片,这是测试区 的更新文件列表会是这样:
增量更新 征途项目中心绿色征途项目组 当使用全区的客户端换到测试区进行更新,更新完是没问题的。因为 更新到的校验文件(attributs.ghd)和图包文件都是最测试区的。但是当你又 回到全局进行更新的时候,由于全区的更新文件列表里只有attributes.ghd 文件,没有328.grp文件。所以它只是把attributes.ghd文件重新更新了一遍。 这时你进游戏会发现所有使用328.grp里图片的功能都会显示异常,图片根本 显示不出来,原因就是这时的客户端使用的328.grp是测试区的,而使用的 校验文件是全区的,很明显校验文件的校验码和328.grp文件本身不一致。 客户端碰到328.grp就不再加载了。 这里出现问题的根本原因是校验文件和图包文件(.grp)没有同步更新, 该怎么减少出现这问题呢,一是重要的图包更新尽量早点全区更新,如果这个 图包肯定要放的,全区更新还是还是比较方便的,客户端随便都可以做补丁。 二就是图包一组一组地放,每个功能增加的美术资源放在新的一组里。比如 测试区增加了一组新的图包,全区的玩家在更新完测试区后又更新全局,他的 本地图包多了一个组,虽然这个组的校验信息在校验文件里是找不到的,但同时 他在全区版本根本用不到这个组的功能。随着新功能的全区,他这个组文件也就 用上了。 当出现这个问题可以使用资源修复工具进行修复,资源修复工具相当于使用 了更详细的flist.xml的更新程序,每个图包每个组的信息都会在上面。
二进制更新 征途项目中心绿色征途项目组
二进制更新 征途项目中心绿色征途项目组 Rsync算法 该算法主要用于同步两个不同计算机上的文件,它通过检查两份文件的差异, 已最低的传输代价来达到文件同步的目的,当2个文件比较类似的时候该算法效率 很高,下面介绍下该算法的大致过程 假设有两台电脑S和 C 电脑S上有文件A,电脑C上有文件B, A文件和B文件很相 似,你可以假设B文件就是你电脑上的老图包,A文件就是服务器上的新图包,这 个新图包只是在老图包的基础上增加了几张图片。远程同步算法包含了如下步骤 1,电脑C将文件B切成一块块没有重叠的文件块,每块长度都是S个字节,当然, 最后一块的长度很可能小于S个字节。 2,对切好的每一个文件块生成2个校验码,一个32位的弱校验码,一个128位的 强校验码。 3,计算机C把这些生成的校验码发给计算机S 4,计算机S从任何一个偏移来找长度为S的并且2个校验码都和C发过来的一样的 文件块,因为滚动 校验的机制,这个过程执行起来非常快。 5,S给C发送一系列数据来生成文件A,这些数据或则是对B文件文件块的引用,或 则是一些文件碎片,这些文件碎片是原来文件B所没有的
二进制更新 征途项目中心绿色征途项目组 整个过程结束计算机C获得了文件A的一份拷贝,但是仅仅下载了有差异的部分,再 就是多了一些校验码和文件块索引的流量。 这个算法有2个很重要的地方,一个是滚动校验,一个是多次比较机制来找相同的文 件块 rsync算法采用了双校验码,一个是弱校验,一个是强检验,所谓弱校验是可能不同的 block会得到相同的校验值,强校验就是不同的block一定得不到相同的校验值,注意弱校验只是一个过滤器,将不可能匹配的block过滤掉,它不能发现成功的匹配,因为它的目的是阻挡没有意义的强校验值计算,所以弱校验就不能太耗时,否则还不如直接计算强校验呢, rsync算法采用了滚动校验的方式。所谓的滚动检验是针对一个序列的很多校验值,每一个校验值有两种方式得到,一种就是按照常规方式用函数求得,另一种方式就是用该序列的前一个元素的校验值得到,滚动校验有一种递归的意思,是的,它就是递归,而且很简单,我们看看它的公式吧:
二进制更新 征途项目中心绿色征途项目组 上面的式子中s就是最后的校验码,这种校验码的特性就是后续的值可以通过递归 关系很快地计算出来 这样,在任何偏移,长度为S的文件块校验的计算就像滚雪球 一样可以用前面的结果,非常快,rsync就是用这个简单又快速的算法快速淘汰了很多根本不可能的block匹配,很高效!
二进制更新 征途项目中心绿色征途项目组 如何匹配 基本就是三趟比较,S收到校验码后会根据每一个的block的checksum构建一个hash表,然后按照offset在文件A中向前推进的时候会先在hash中找,如果找到就遍历这个hash entry的吊链,然后匹配弱校验,如果弱检验通过就计算强校验,如果强校验通过则表明该block是相同的,不需要同步,继续下一个 block的第一个offset,如果不同,那么继续下一个offset,如果匹配成功的话,就会传输前一个匹配成功的block和该block之间的数据。 可见rsync算法的精髓就是双校验方式,弱校验采用了高效的滚动校验的方式。滚动校验依赖前一个元素的校验值可以快速的计算出后一个的校验值。 这个更新方式通用性也非常好,不用考虑文件内部的组织,对它来说,所有的文件都是 字节流或则二进制数据。感兴趣的同事可以点这个网址: http:// samba.anu.edu.au/rsync/tech_report/tech_report.html
征途项目中心绿色征途项目组 Q&A 谢谢 五.关于BUG重现那点