GameFramework:资源热更代码分析,检查版本信息,下载版本文件,校验版本文件,得到更新文件数量,下载文件,TaskPool
资源下载流程图
流程跳转的日志
版本信息-版本检测-下载版本列表-下载散文件代码分析
- 进入ProcedureCheckVersion.OnEnter流程,进行task请求,userData为StarForce.ProcedureCheckVersion
创建WebRequest网络请求任务调用堆栈为
初始化任务:UnityGameFramework.Runtime.WWWFormInfo GameFramework.TaskBase:Initialize (int,string,int,object) GameFramework.WebRequest.WebRequestManager/WebRequestTask:Create (string,byte[],string,int,single,object) GameFramework.WebRequest.WebRequestManager:AddWebRequest (string,byte[],string,int,object) UnityGameFramework.Runtime.WebRequestComponent:AddWebRequest (string,byte[],UnityEngine.WWWForm,string,int,object) UnityGameFramework.Runtime.WebRequestComponent:AddWebRequest (string,object) StarForce.ProcedureCheckVersion:OnEnter (GameFramework.Fsm.IFsm`1<GameFramework.Procedure.IProcedureManager>)
- 请求成功,读取服务器中Version.txt文件,Version.txt文件内容为
{ "ForceUpdateGame": false, "LatestGameVersion": "0.1.0", "InternalGameVersion": 0, "InternalResourceVersion": 2, "UpdatePrefixUri": "http://10.12.24.82:10089/Windows", "VersionListLength": 7160, "VersionListHashCode": 1835127216, "VersionListCompressedLength": 2649, "VersionListCompressedHashCode": -1660134522 }
- 如果不需要强行更新版本,下载版本信息文件GameFrameworkVersion.6d61d1b0.dat
调用堆栈
增加下载任务:保存地址:C:/Users/luoyikun_l/AppData/LocalLow/Game Framework/Star Force/GameFrameworkVersion.dat源地址:http://10.12.24.82:10089/Windows/GameFrameworkVersion.6d61d1b0.dat GameFramework.Download.DownloadManager:AddDownload (string,string,string,int,object) GameFramework.Download.DownloadManager:AddDownload (string,string,object) GameFramework.Resource.ResourceManager/VersionListProcessor:UpdateVersionList (int,int,int,int) GameFramework.Resource.ResourceManager:UpdateVersionList (int,int,int,int,GameFramework.Resource.UpdateVersionListCallbacks) UnityGameFramework.Runtime.ResourceComponent:UpdateVersionList (int,int,int,int,GameFramework.Resource.UpdateVersionListCallbacks) StarForce.ProcedureUpdateVersion:OnEnter (GameFramework.Fsm.IFsm`1<GameFramework.Procedure.IProcedureManager>)
- VersionListProcessor中OnDownloadSuccess,监听的是DownloadMananger下载成功通知,用来检验GameFrameworkVersion.dat(版本文件list信息集合)是否按照Version.txt 里信息下载成功
- ProcedureCheckResources:资源检查流程,用来确定待下载的列表
ResourceChecker,进入到文件检查阶段,有多少文件需要下载
核心函数
//得到各个文件的状态,移除,移动,更新。可以得到差异更新总量
private void RefreshCheckInfoStatus()
调用堆栈
Check resources complete, '18' resources need to update, compressed length is '11438117', uncompressed length is '12336939'. StarForce.ProcedureCheckResources:OnCheckResourcesComplete (int,int,int,long,long) GameFramework.Resource.ResourceManager:OnCheckerResourceCheckComplete (int,int,int,long,long) GameFramework.Resource.ResourceManager/ResourceChecker:RefreshCheckInfoStatus () GameFramework.Resource.ResourceManager/ResourceChecker:OnLoadUpdatableVersionListSuccess (string,byte[],single,object)
- ProcedureUpdateResources资源更新流程,下载资源,并创建文件系统
文件系统支持差异更新,例如上次要下18个,中断后再下载6个
Check resources complete, ‘6’ resources need to update, compressed length is ‘130607’, uncompressed length is ‘192052’.
使用ResourceUpdater进行下载,update中轮询,把所有下载任务,依次塞入空闲代理下载,同时可以3个下载
类说明
ResourceUpdater
update中轮询与TaskPool结合
public void Update(float elapseSeconds, float realElapseSeconds) { if (m_UpdateWaitingInfo.Count > 0) { int freeCount = m_DownloadManager.FreeAgentCount - m_DownloadManager.WaitingTaskCount; if (freeCount > 0) { for (int i = 0, count = 0; i < m_UpdateWaitingInfo.Count && count < freeCount; i++) { if (DownloadResource(m_UpdateWaitingInfo[i])) { count++; } } } return; } }
每下载完一个,m_UpdateCandidateInfo-1,m_UpdateCandidateInfo.Count <= 0,候选下载全部完成,更新完毕
DownloadManager
创建任务池,并增加任务代理
m_TaskPool = new TaskPool<DownloadTask>(); DownloadAgent agent = new DownloadAgent(downloadAgentHelper); agent.DownloadAgentStart += OnDownloadAgentStart; agent.DownloadAgentUpdate += OnDownloadAgentUpdate; agent.DownloadAgentSuccess += OnDownloadAgentSuccess; agent.DownloadAgentFailure += OnDownloadAgentFailure; m_TaskPool.AddAgent(agent);
在DownloadComponent start 创建m_DownloadAgentHelperCount (目前为3)个任务代理,代表能同时下载的代理数
具体下载代理是DownloadAgent决定
DownloadAgent又由DownloadAgentHelperBase 下载代理辅助器决定使用哪种下载方式
gf中内置了UnityWebRequest实现的下载代理辅助器。
UnityWebRequestDownloadAgentHelper
真正的下载器,可以替换成任意下载api
使用 UnityWebRequest 实现的下载代理辅助器,针对的是单个下载
update判断 m_UnityWebRequest.isDone,如果有网络错误立马抛出事件
DownloadAgent
下载完成,OnDownloadAgentHelperComplete
下载代理器成功了,把临时文件.download 变为正式文件,可用于断点续传
并且发送通知:ResourceManager.OnDownloadSuccess
VersionListProcessor
//版本资源列表处理器。
创建时,监听DownloadManager.DownloadSuccess
执行OnDownloadSuccess
下载成功了
- 下载的压缩长度一致
- crc32码一致
- 进行解压操作,且解压正常(默认是GZip解压)
- 解压后的大小与服务器文件描述一致
- 写入解压后的文件
- 通知版本list文件更新成功
下载失败,删除文件
全部校验成功,关闭并清理版本资源列表处理器。
TaskPool
TaskPool是任务池,使用链表与栈来维护任务与任务代理,并提供任务的添加与移除等操作。WebRequest(网络请求), Download(下载), LoadResource(加载资源)时使用。
ITask是任务接口,主要存储任务执行时所需的数据
ITaskAgent是任务代理接口,负责对任务进行处理(实际的处理逻辑则通过调用具体模块的代理辅助器执行,这一点在后面编写需要使用任务功能的模块时会涉及到)
TaskPool中成员
private readonly Stack<ITaskAgent<T>> m_FreeAgents; //通过栈维护所有空闲代理, 这个代理就是任务的具体实现过程,在框架中很多地方都使用了这种接口方式,具有高度的扩展性。 private readonly GameFrameworkLinkedList<ITaskAgent<T>> m_WorkingAgents;//通过一个链表维护所有的工作代理 private readonly GameFrameworkLinkedList<T> m_WaitingTasks;//通过一个链表维护所有的等待任务
更新流程详解
1.资源列表的校验
应用每一次启动,首先需要你先得到当前资源总表的内部版本号(内部版本号在ResourceBuilder面板设置。至于内部版本号的动态获取过程,采用何种方式,则需要自己来实现,例如读unity的版本设置),然后即可使用这个最新的内部版本号,调用
CheckVersionListResult ResourceComponent.CheckVersionList(int latestInternalResourceVersion)
来判定当前的资源总表是不是最新的,此时 CheckVersionList 方法会访问读写目录,并尝试读取 GameFrameworkVersion.dat 文件,如果文件没有或者内部携带的资源内部版本号与服务器中version.txt 不同,则会返回资源需要更新的枚举值,否则则会返回不需要更新。
服务器中远程版本信息version.txt 内容为
{ "ForceUpdateGame": false, "LatestGameVersion": "0.1.0", "InternalGameVersion": 0, "InternalResourceVersion": 2, "UpdatePrefixUri": "http://10.12.24.82:10089/Windows", "VersionListLength": 7138, "VersionListHashCode": -1969978894, "VersionListCompressedLength": 2653, "VersionListCompressedHashCode": 861209557 }
项目中Builtin Data信息BuildInfo.txt,只会决定获取远程version.txt的路径,游戏app版本号是代码获取,res版本号是按照本地可读写目录GameFrameworkVersion.dat文件是否存在
BuildInfo.txt文件内容
{ "GameVersion": "0.1.0", "InternalGameVersion": 0, "CheckVersionUrl": "http://10.12.24.82:10089/{0}/Version.txt", "WindowsAppUrl": "https://starforce.gameframework.cn", "MacOSAppUrl": "https://starforce.gameframework.cn", "IOSAppUrl": "https://starforce.gameframework.cn", "AndroidAppUrl": "https://starforce.gameframework.cn", "END_OF_JSON": "" }
2.资源列表的更新
如果资源列表需要更新,则此时需要配置好你的更新地址,即公布在网络上用于下载Full 目录中文件的地址。
string ResourceComponent.UpdatePrefixUri
然后调用更新方法
void ResourceComponent.UpdateVersionList(int versionListLength, int versionListHashCode, int versionListZipLength, int versionListZipHashCode, UpdateVersionListCallbacks updateVersionListCallbacks)
前四个参数分别为
versionListLength 资源总表文件长度
versionListHashCode 资源总表文件校验和
versionListZipLength 资源总表文件压缩后的zip文件长度
versionListZipHashCode 资源总表文件压缩后的zip文件校验和
这些参数在 “生成目录/BuildReport/BuildLog.txt” 文件的信息中可以查看.
出现负数不影响使用,为crc32码转为int出现的负数
3.资源校验
首先资源列表文件事实上一共有三份,除了总表 GameFrameworkVersion.XXXX.dat 和 只读目录下的 GameFreamworkList.dat 之外, 读写目录下也有一份 GameFreamworkList.dat 资源列表文件,这份文件记录的是最后一次更新完毕后读写目录中的资源文件信息(如果你的应用程序是第一次启动,且没有更新过资源,那么这一份文件也是不存在的,但是如果你成功的更新过资源,则由GF框架自动为你生成 )
现在先设置一下资源模块当前的变体,因为校验资源这一步即校验资源文件是否要更新,同时也会建立资源信息数据库用于之后的加载操作,所以在你校验资源信息之前务必设置好当前要使用的资源变体,之后如果调用加载资源方法会在此时建立的资源信息数据库中寻找资源信息。除当前变体之外的其他变体的资源信息将不会在资源数据库中,如果加载的是其他变体的资源文件,则被作为无此资源信息的情况处理。这里调用
void ResourceComponent.SetCurrentVariant(string currentVariant)
则可以设置变体,默认情况下为“”,也就是无变体。接着就可以正式调用
void ResourceComponent.CheckResources(CheckResourcesCompleteCallback checkResourcesCompleteCallback)
m_ResourceChecker.CheckResources(m_CurrentVariant, ignoreOtherVariant);
开始校验资源,CheckResources 会以分别读取三个资源列表文件进行比较,
如果只读目录或者读写目录下的 GameFreamworkList.dat 文件不存在,则以该目录下没有资源文件处理。
GameFramework.Resource.ResourceManager.ResourceChecker.CheckResources
//读写中GameFrameworkVersion.dat m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadWritePath, RemoteVersionListFileName)), new LoadBytesCallbacks(OnLoadUpdatableVersionListSuccess, OnLoadUpdatableVersionListFailure), null); //只读中GameFrameworkList.dat,为pack资源,随app发布的 m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadOnlyPath, LocalVersionListFileName)), new LoadBytesCallbacks(OnLoadReadOnlyVersionListSuccess, OnLoadReadOnlyVersionListFailure), null); //读写中"GameFrameworkList.dat",为每次更新完后更新当前目录的资源 m_ResourceManager.m_ResourceHelper.LoadBytes(Utility.Path.GetRemotePath(Path.Combine(m_ResourceManager.m_ReadWritePath, LocalVersionListFileName)), new LoadBytesCallbacks(OnLoadReadWriteVersionListSuccess, OnLoadReadWriteVersionListFailure), null);
读取完三个资源列表文件内容后,将会以总表中的资源信息为准,通过总表中记录的资源信息的文件hashcode值作为唯一标识。
如果某个资源信息在 两个GameFreamworkList.dat 资源表都没有记录,则将这个资源信息对应的资源文件作为需要更新的资源文件处理,记录在等待更新列表中。
除此之外,如果发现总表中记录的资源信息,在只读目录和读写目录下的 GameFreamworkList.dat 资源表中都存在,则会删除在读写目录中的对应资源文件已节省空间(因为只读目录中已经有了一份资源文件,不需要存储两份)。
读取三个文件后做的处理
CheckResources.RefreshCheckInfoStatus()
在建立完更新列表和资源信息数据库之后,接下来就可以进行资源文件更新了。
4.资源更新
资源更新模块会自动根据我们上一步校验资源时建立的资源文件更新列表,前往之前设置的下载地址,依次的下载资源文件到读写目录中,并在下载完毕后,自动生成读写目录中的 GameFreamworkList.dat 资源表文件,这个文件记录了读写目录至本次更新结束完毕后,读写目录下的资源文件信息。方便下一次的更新操作。调用
void UpdateResources(UpdateResourcesCompleteCallback updateResourcesCompleteCallback)
即可开启更新流程,这个流程很简单就是下载文件,然后生成读写目录下的资源列表文件
GameFrameworkVersion.dat 里面具体内容
UpdatableVersionList序列化而来
{ "IsValid": true, "ApplicableGameVersion": "0.1.0", "InternalResourceVersion": 5, "m_IsValid": true, "m_ApplicableGameVersion": "0.1.0", "m_InternalResourceVersion": 5, "m_Assets": [{ "Name": "Assets/GameMain/UI/UISprites/Common/info.png", "m_Name": "Assets/GameMain/UI/UISprites/Common/info.png", "m_DependencyAssetIndexes": [] }, { "Name": "Assets/GameMain/UI/UIForms/DialogForm.prefab", "m_Name": "Assets/GameMain/UI/UIForms/DialogForm.prefab", "m_DependencyAssetIndexes": [7, 91, 92, 109] }, { ..... "m_Resources": [{ "Name": "Configs", "Variant": null, "Extension": "dat", "LoadType": 0, "Length": 1343, "HashCode": -456997092, "CompressedLength": 1129, "CompressedHashCode": 1676554304, "m_Name": "Configs", "m_Variant": null, "m_Extension": "dat", "m_LoadType": 0, "m_Length": 1343, "m_HashCode": -456997092, "m_CompressedLength": 1129, "m_CompressedHashCode": 1676554304, "m_AssetIndexes": [38, 66] }, "m_FileSystems": [], "m_ResourceGroups": [{ "Name": "Base", "m_Name": "Base", "m_ResourceIndexes": [0, 1, 3, 4, 5, 6, 7] }, { "Name": "Music", "m_Name": "Music", "m_ResourceIndexes": [10, 11, 12] }]
GameFrameworkList.dat里面具体内容
LocalVersionList序列化而来
比上面了缺少asset信息
{ "IsValid": true, "m_IsValid": true, "m_Resources": [{ "Name": "Configs", "Variant": null, "Extension": "dat", "LoadType": 0, "Length": 1343, "HashCode": -456997092, "m_Name": "Configs", "m_Variant": null, "m_Extension": "dat", "m_LoadType": 0, "m_Length": 1343, "m_HashCode": -456997092 }, {
posted on 2022-07-06 23:19 luoyikun 阅读(142) 评论(0) 编辑 收藏 举报 来源
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)