Autopatcher
自动补丁系统
Autopatcher是一个用于管理两个或多个系统之间丢失的或者改变的文件,在他们之间对不同的文件进行复制。这个插件可以处理传输文件,可以压缩传输的文件,文件安全和文件操作。但是它不处理最基本的连接,或提供用户连接接口。对于基础连接,则使用RakPeerInterface或者是PacketizedTCP进行系统之间的连接。至于UI的形式,则完全依赖于你自己的要求设计。Autopatcher用在所有的在线游戏中,大多数的AAA商业游戏中。
在客户端,需要定义一个AutopatcherClient的实例。服务器需要定义一个AutopacherServer的实例。这些类的源码不是RakNet核心的一部分,他们位于 .\DependentExtensions\目录下,要使用Autopatcher插件需要将这些代码加入到工程中。
客户端:
在客户端一边,大多数的工作只需要AutopatherClient的几个方法就可以完成。
void SetFileListTransferPlugin(FileListTransfer * flt);
这个插件依赖于FileListTransfer插件,FileListTransfer插件用于发送文件。因此需要将插件实例注册到RakPeerInterface的实例,指向这个接口的指针应该传递给这个函数。
bool PatchApplication(const char *_applicationName,const char *_applicationDirectory,const char *lastUpdateDate,SystemAddress host,FileListTransferCBInterface *onFileCallback,const char *restartOutputFilename,const char *pathToRestartExe);
升级一个应用程序的指定目录,这个应用程序是在补丁服务器上有相同名字的应用程序。
_applicatonName – 应用程序的名字
_applicationDirectory – 要写输出文件的目录
_lastUpdateDate – 你最后从补丁服务器更新文件的时间字符串,因此仅仅需要检验最新的文件。如果是第一次,或者你先要进行全部扫描,只需要赋值0。成功调用PatchApplication之后,返回GetServerDate()。
host – 消息发送到的远端系统的地址信息
onFileCallback – (可选)对每一个文件要调用的回调函数。当在回调中fileINdex+1 == setCount,然后下载就完成了。
_restartOutputFilename – 如果需要重启应用程序,应用程序是写入重启数据的地方。在这个文件名中,可以包含一个路径。
pathToRestartExe – 从AutopathcerClientRestarter加载exe。argv[0]会重新加载这个应用程序。
在客户端也有其他的函数和类,可以从\Samples\AutopatcherClient例子中进行学习。
服务器:
在服务器一端,可以使用AutopatcherServer。
void SetFileListTransferPlugin(FileListTransfer * fit);
与AutopathcerClient类似,这个插件也要依赖于FileListTransfer插件,FileListTransfer是真正用来发送文件的插件。因此需要将这个插件注册给RakPeerInterface的实例,指向这个实例的指针应该传递给函数。
void SetAutopathcerRepositoryInterface(AutopathcerRepositoryInterface* ari);
使用这个函数,告诉AutopacherServer如何看管好补丁的网络传输。这个类仅仅用于autopacher的网络传输。所有的数据都存储在数据库中。使用这个函数,将接口传递给数据仓库。RakNet带有AutopacherPostgreRepository,如果需要就可以使用。
查看\Samples\AutoPatcherServer下的例子。它将AutopacherRepositoryInterface的实例用于Postgresql(AutopacherRepository)来存储在Postgresql数据库中的应用程序的所有文件。
目录结构体
可以使用目录结构体。例如,假设你的应用程序有如下的目录结构体:
Readme.txt
Music/Song1.wav
Music/Song2.wav
Autopathcer会保持目录结构完整。因此如果发送系统有了这个目录结构体,下载系统会做一个这个目录结构体的镜像。
1. 在DependentExtensions\bzip2-1.0.3中的所有的源文件
2. DependentExtensions\CreatePatch.h and .cpp
3. DependentExtensions\MemoryCompressor.h and .cpp
4. DependentExtensions\AutopatcherServer.h 和 .cpp
5. 在DependentExtensions\AutopatcherPostgreRepository中的所有源文件
6. 在DependentExtensions\PostgresqlInterface中的所有的源文件
7. 所有的源文件在\Samples\AutopatcherServer,应该想要使用默认的控制台应用程序运行在服务器。
服务器依赖项(使用Postgresql):
Postgresql 8.2 或更新版本,安装在C:\Program Files\Postgresql\8.2。安装目录不同,需要修改工程属性路径。不要忘记检查在Postgresql安装包的开发工具,否则头文件和lib不会安装。
客户端需要的文件
1.在DependentExtensions\bzip2-1.0.3中的所有源文件
2. DependentExtensions\MemoryCompressor.h and .cpp
3. DependentExtensions\ApplyPatch.h and .cpp
4. DependentExtensions\AutopatcherClient.h 和 .cpp
5. 在Samples\AutopatcherClient中的所有源文件,你应该会想将默认控制台应用程序当做是设计模板。
6. 在Samples\AutopatcherClientRestarter中的所有源文件,你会想要使用默认控制台程序在补丁安装完成后,重新启动应用程序。
使用TCP代替UDP(推荐)
RakPeerInterfaces使用的是UDP,如果RakNet的协议修改了,这里将出现问题 – 自动升级系统不能够连接到新的协议。推荐你使用TCP来代替UDP。在例子AutopatcherClientTest.cpp 和AutopatcherServerText_MysqL.cpp或者是AutopatcherServerTest.cpp中,要使用TCP代替UDP可以在这些文件的顶部定义USE_TCP。
变化:
1. 使用PacketizedTCP类代替RakPeerInterface
2. PacketizedTCP会使用HasCompletedConnectionAttempt(),HasNewIncomingConnection( )和HasLostConnection()方法返回连接状态变化,而不仅仅是字节标识符。
3. 将插件附加到PacketizedTCP实例,而不是RakPeerInterface实例。
对于大型游戏的优化:
AutopatcherClient::PatchApplication有一个参数lastUpdateDate。如果给这个参数传递了0,服务器不知道客户端使用的版本。那么会进行全面的扫描,服务器需要访问数据库来获取应用程序的每一个文件的哈希值,这样非常慢,如果可能的话,要尽量避免这种情况。
发布应用程序时,在补丁服务器应该使用time()函数获得时间戳,将这个时间与发布文件一起发布。然后可以将这个值传递给AutopatcherClient::PatchApplication。只要这个值大于或等于AutopatcherRepositoryInterface::UpdateApplicationFiles调用时的时间值,服务器可以使用它对从客户端使用的版本以后所做修改的文件来做优化查询。
AutopatcherServer通过CacheMostRecentPatch()函数来做更多的优化。如果使用了AutopatcherPostgreRepository插件,该方法会将最近的补丁存储到内存中。如果传递给PatchApplication()的时间比最近的补丁的时间还早,补丁就会完全跳过。如果这个值比最近的补丁的时间值小,但是比它前面的补丁的时间值大,补丁就直接从内存中获得,而不是访问硬盘来获取。要使得这个功能可用,每一次收到了ID_AUTOPATCHER_FINISHED则调用AutopatcherClient::GetServerDate()方法,保存到磁盘,将这个日期用于下一个PatchApplication()方法的调用。
性能tips总结:
1. 自动升级系统并不是用于服务整个应用程序,仅仅用于补丁。在打补丁之前,用户应该从FTP或WebServer获得了大部分的游戏文件。
2. 如果使用了AutopatcherPostgreRepository,使用AutopatcherServer::CacheMostRecentPatch()。
3. 即使没有CacheMostRecentPatch()方法,AutopatcherPostgreRepository也比AutopathcerMysqL快大约十倍。
4. 如果给AutopacherClient::PatchApplication()方法的lastUpdateDate参数传递了0,这会造成对文件的完整扫描,每一个文件都需要访问数据库。除非用户要求修复崩溃的游戏安装,否则不要这样设置,因为这样非常浪费时间。
5. 分发应用程序,在调用了UpdateApplicationFiles()方法之后要在服务器调用time()。将这个值与客户端的发布程序存储一起,用于第一次调用AutopatcherClient::PatchApplication方法的lastUpdateDate的默认值。
6. 一旦你收到了ID_AUTOPATHCER_FINISHED数据包,使用AutopatcherClient::GetServerDate()方法将这个返回值存储起来。用于下一次调用AutopatcherClient::PatchApplication()方法lastUpdateDate参数的默认值。
7. 使用CatheMostRecentPatch()方法,仅仅将最新的补丁放置到内存,因此不要动不动就调用UpdateApplicationFiles()方法。
内存使用上的一些注意:
补丁使用Colin Percival http://www.daemonology.net/bsdiff/上的代码来创建。补丁算法使用LaRSSon 和Sadakane的qsufsort,这些算法非常耗费内存,因此不推荐用于几百兆的文件。如果你使用了CacheMostRecentPath()方法,最近更新的补丁会保存在内存。此外,每一个用户的文件传输会花费8兆内存。
By 北洋小郭
转载请注明出处,切勿用于商业。谢谢!