[游戏开发][Unity]Assetbundle下载篇(1)热更前准备与下载AB包清单

news2024/11/26 16:02:12

热更流程都不是固定的,每个人写的状态机也有所差别,但是一些必要步骤肯定不可能少,例如下载清单,对比版本,下载AB包,标记下载完成。我接下来的每一篇文章都是下载AB包的重要步骤,大概率是不能省略的。

检查沙盒路径是否存在

public static string MakePersistentLoadPath(string path)
{
#if UNITY_EDITOR
        // 注意:为了方便调试查看,编辑器下把存储目录放到项目里
        string projectPath = Path.GetDirectoryName(Application.dataPath).Replace("\\","/");
        projectPath = GetRegularPath(projectPath);
        return StringFormat.Format("{0}/Sandbox/{1}", projectPath, path);
#else
        return StringFormat.Format("{0}/Sandbox/{1}", Application.persistentDataPath, path);
#endif
}

检查下载临时目录是否存在

请注意,热更时下载AB包先下载到临时目录,之后再拷贝到沙盒目录

public static string MakeDownloadTempPath(string path)
{
#if UNITY_EDITOR
    string projectPath = Path.GetDirectoryName(Application.dataPath).Replace("\\", "/");
    projectPath = GetRegularPath(projectPath);
    return StringFormat.Format("{0}/Sandbox_Temp/{1}", projectPath, path);
#else
    return StringFormat.Format("{0}/Sandbox_Temp/{1}", Application.persistentDataPath, path);
#endif
}

路径都确认存在后,开始下载


前文有介绍过,生成的AB包清单长这个样子,把这个文件生成二进制bytes文件扔到服务器上去下载。

第一行是SVN版本号

第二行是AB包数量

从第三行开始是资源包信息,以=号分割开有效数据,分别是

MD5.unity3d = 资源路径 = 资源路径的HashId = 包体KB大小 = SVN版本号 = 启动热更模式

每一行数据封装了一个PatchElement类,代码在下文中

我们项目封装了一下UnityWebRequest,叫WebDataRequest,你不想封装直接用UnityWebRequest即可,WebDataRequest代码在后面有。

private IEnumerator DownLoad()
{
    // 解析APP里的补丁清单
    string filePath = AssetPathHelper.MakeStreamingLoadPath(PatchDefine.InitManifestFileName);
    string url = AssetPathHelper.ConvertToWWWPath(filePath);
    using (WebDataRequest downloader = new WebDataRequest(url))
    {
        yield return downloader.DownLoad();
        if (downloader.States == EWebRequestStates.Success) 
        {
            PatchHelper.Log(ELogLevel.Log, "Parse app patch manifest.");
            ParseAppPatchManifest(downloader.GetData());
        }
        else
        {
            throw new System.Exception($"Fatal error : Failed download file : {url}");
        }
    }
}

// 解析补丁清单文件相关接口
public void ParseAppPatchManifest(byte[] data)
{
    if (AppPatchManifest != null)
        throw new Exception("Should never get here.");
    AppPatchManifest = new PatchManifest(true);
    AppPatchManifest.Parse(data);
}

PatchManifest类是一个专门解析AB包清单的类,看代码也能知道,Parse方法的最终目的,就是把清单的每一行数据解析成PatchElement然后加入到字典中存起来等着下载时调用

/// <summary>
/// 补丁清单文件
/// </summary>
public class PatchManifest
{
    private bool _isParse = false;

    /// <summary>
    /// 资源版本号
    /// </summary>
    public int DllVersion { private set; get; }
    public int ResVersion { private set; get; }
    private bool IsInit = false;

    /// <summary>
    /// 所有打包文件列表
    /// </summary>
    public readonly Dictionary<string, PatchElement> Elements = new Dictionary<string, PatchElement>();

    public PatchManifest(bool isInit = false)
    {
        IsInit = isInit;
    }

    /// <summary>
    /// 解析数据
    /// </summary>
    public void Parse(byte[] data)
    {
        using (var ms = new MemoryStream(data))
        {
            using(var br = new BinaryReader(ms))
            {
                Parse(br);
            }
        }
    }

    public void ParseFile(string filePath)
    {
        using (var fs = File.OpenRead(filePath))
        {
            using (var br = new BinaryReader(fs))
            {
                Parse(br);
            }
        }
    }

    /// <summary>
    /// 解析数据
    /// </summary>
    public void Parse(BinaryReader br)
    {
        if (br == null)
            throw new Exception("Fatal error : Param is null.");
        if (_isParse)
            throw new Exception("Fatal error : Package is already parse.");

        _isParse = true;

        // 读取版本号            
        DllVersion = br.ReadInt32();
        ResVersion = br.ReadInt32();

        GameVersion.PatchResDesc = ResVersion + "   dllVer:" + DllVersion;
        int fileCount = br.ReadInt32();
        // 读取所有Bundle的数据
        for(var i = 0; i < fileCount; i++)
        {
            var ele = PatchElement.Deserialize(br, IsInit);
            if (Elements.ContainsKey(ele.Name))
                throw new Exception($"Fatal error : has same pack file : {ele.Name}");
            Elements.Add(ele.Name, ele);
        }
    }
}

PatchElement是AB包清单每一行数据的封装,PatchManifest的Parse里会循环创建一个Elements字典保存这些数据。

public class PatchElement
{
    /// <summary>
    /// 文件名称
    /// </summary>
    public string Name { private set; get; }

    /// <summary>
    /// 文件MD5
    /// </summary>
    public string MD5 { private set; get; }

    /// <summary>
    /// 文件版本
    /// </summary>
    public int Version { private set; get; }

    /// <summary>
    /// 文件大小
    /// </summary>
    public long SizeKB { private set; get; }

    /// <summary>
    /// 构建类型
    /// buildin 在安装包中
    /// ingame  游戏中下载
    /// </summary>
    public string Tag { private set; get; }

    /// <summary>
    /// 是否是安装包内的Patch
    /// </summary>
    public bool IsInit { private set; get; }

    /// <summary>
    /// 下载文件的保存路径
    /// </summary>
    public string SavePath;

    /// <summary>
    /// 每次更新都会先下载到Sandbox_Temp目录,防止下到一半重启导致逻辑不一致报错
    /// temp目录下的文件在重新进入更新流程时先校验md5看是否要跳过下载
    /// </summary>
    public bool SkipDownload { get; set; }


    public PatchElement(string name, string md5, int version, long sizeKB, string tag, bool isInit = false)
    {
        Name = name;
        MD5 = md5;
        Version = version;
        SizeKB = sizeKB;
        Tag = tag;
        IsInit = isInit;
        SkipDownload = false;
    }

    public void Serialize(BinaryWriter bw)
    {
        bw.Write(Name);
        bw.Write(MD5);
        bw.Write(SizeKB);
        bw.Write(Version);
        if (IsInit)
            bw.Write(Tag);
    }

    public static PatchElement Deserialize(BinaryReader br, bool isInit = false)
    {
        var name = br.ReadString();
        var md5 = br.ReadString();
        var sizeKb = br.ReadInt64();
        var version = br.ReadInt32();
        var tag = EBundlePos.buildin.ToString();
        if (isInit)
            tag = br.ReadString();
        return new PatchElement(name, md5, version, sizeKb, tag, isInit);
    }
}

下面是下载数据封装,本质还是 UnityWebRequest

public class WebDataRequest : WebRequestBase, IDisposable
{
    public WebDataRequest(string url) : base(url)
    {
    }
    public override IEnumerator DownLoad()
    {
        // Check fatal
        if (States != EWebRequestStates.None)
            throw new Exception($"{nameof(WebDataRequest)} is downloading yet : {URL}");

        States = EWebRequestStates.Loading;

        // 下载文件
        CacheRequest = new UnityWebRequest(URL, UnityWebRequest.kHttpVerbGET);
        DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
        CacheRequest.downloadHandler = handler;
        CacheRequest.disposeDownloadHandlerOnDispose = true;
        CacheRequest.timeout = Timeout;
        yield return CacheRequest.SendWebRequest();

        // Check error
        if (CacheRequest.isNetworkError || CacheRequest.isHttpError)
        {
            MotionLog.LogWarning($"Failed to download web data : {URL} Error : {CacheRequest.error}");
            States = EWebRequestStates.Fail;
        }
        else
        {
            States = EWebRequestStates.Success;
        }
    }

    public byte[] GetData()
    {
        if (States == EWebRequestStates.Success)
            return CacheRequest.downloadHandler.data;
        else
            return null;
    }
    public string GetText()
    {
        if (States == EWebRequestStates.Success)
            return CacheRequest.downloadHandler.text;
        else
            return null;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/627099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Kibana:使用 Kibana 自带数据进行可视化(三)

在今天的练习中&#xff0c;我们将使用 Kibana 自带的数据来进行一些可视化的展示。希望对刚开始使用 Kibana 的用户有所帮助。这是这个系列的第三篇文章。这个是继上一篇文章 “Kibana&#xff1a;使用 Kibana 自带数据进行可视化&#xff08;二&#xff09;” 的续篇。 前提条…

学成在线----day5

1、媒资管理需求分析 2、为什么要用网关 当前要开发的是媒资管理服务&#xff0c;目前为止共三个微服务&#xff1a;内容管理、系统管理、媒资管理&#xff0c;如下图&#xff1a; 后期还会添加更多的微服务&#xff0c;当前这种由前端直接请求微服务的方式存在弊端&#xff…

微服务架构中的数据一致性:解决方案与实践| 得物技术

1 为什么要做服务之间的数据一致性 作为互联网公司的研发工程师&#xff0c;微服务的架构思想对于各位读者朋友来说&#xff0c;已经不是陌生东西。我们当中的大多数人&#xff0c;或多或少经历过从单体应用到微服务化的系统拆分和演进过程。我们按照庞大系统的业务功能和特征…

都说网络安全渗透工程师前景好,好在哪?

渗透工程师前景非常好&#xff0c;网络安全发展规模不断扩大&#xff0c;未来行业类的人才需求也会越来越多。就目前看来在网络安全方向上就业的薪资待遇也十分可观。 其就业方向有很广泛&#xff0c;如网络安全工程师&#xff0c;渗透测试工程师等。 渗透测试人员通常对网络…

keras搭建轻量级卷积神经网络CNN开发构建国家一级保护动物识别分析系统,集成开发GradCAM实现热力图分析可视化

动物识别相关的项目本质上属于图像识别&#xff0c;在我之前的博文中已经有过不少实践了&#xff0c;感兴趣的话可以自行移步阅读即可&#xff0c;这里不是说想要单纯地去做一个动物识别的项目&#xff0c;昨晚在玩手机的时候突然被小孩问到一个动物是不是国家保护动物&#xf…

SpringBoot 事件监听处理(五十一)

当死亡笼罩在脑海&#xff0c;请用生的信念打败它 上一章简单介绍了Retry重试机制(五十), 如果没有看过,请观看上一章 参考文章: https://blog.csdn.net/qq_37758497/article/details/118863308 一. Spring 事件监听 Spring的事件监听(也叫事件驱动)是观察者模式的一种实现&…

Windows 10磁盘碎片整理:含义和操作方法

什么是Windows磁盘碎片&#xff1f; 随着电脑硬盘使用时间的增长&#xff0c;磁盘上会产生大量的垃圾碎片。这些碎片会分布在磁盘的各个角落&#xff0c;严重影响磁盘的响应速度。为了在一定程度上提高系统性能&#xff0c;定期使用Windows10的磁盘碎片整理工具来进行碎片整…

Vue.js中的Mixin和组件插槽

Vue.js中的Mixin和组件插槽 介绍 在Vue.js中&#xff0c;Mixin和组件插槽是两个非常有用的概念。Mixin是一种可重用Vue组件的方式&#xff0c;而组件插槽则提供了一种在组件之间共享内容的方式。虽然这两个概念在功能上有所不同&#xff0c;但它们对于Vue.js应用程序的开发都非…

Vue 中的数据请求如何进行拦截与错误处理

Vue 中的数据请求拦截与错误处理 在 Vue.js 中&#xff0c;我们经常需要向后端服务器发送数据请求&#xff0c;以获取或提交数据。在这个过程中&#xff0c;我们可能会遇到一些问题&#xff0c;例如无效的请求参数、网络连接错误、服务器错误等。为了更好地处理这些问题&#…

优化和扩展:处理不同操作和参数的数字列表

引言 在编程中&#xff0c;我们有时需要根据输入执行不同的操作&#xff0c;而这些操作涉及到数字列表&#xff0c;并且每个操作可能具有不同数量的参数。本文将介绍如何优化和扩展代码&#xff0c;以便更好地处理这种情况。 问题描述 当前遇到的问题是需要根据输入执行不同…

Mysql中的Buffer pool

Buffer Pool在数据库里的地位 1、回顾一下Buffer Pool是个什么东西&#xff1f; 数据库中的Buffer Pool是个什么东西&#xff1f;其实他是一个非常关键的组件&#xff0c;数据库中的数据实际上最终都是要存放在磁盘文件上的&#xff0c;如下图所示。 但是我们在对数据库执行增…

2023最新Java面试八股文,阿里/腾讯 / 美团 / 字节 1 000道 Java 中高级面试题

企业调薪、裁员、组织架构调整等等&#xff0c;坏消息只多不少&#xff0c;最近也有很多来咨询跳槽的朋友&#xff0c;都是因为之前的公司出现了比较大的薪资和组织变动 近期有许多粉丝非常关注最新的面试题&#xff01;于是小编去各大平台搜罗了一份近期大厂面试的一些内容&a…

基础工程(cubeide串口调试,printf实现,延时函数)

0.基础工程&#xff08;cubeide串口调试&#xff0c;printf实现&#xff0c;延时函数&#xff09; 文章目录 0.基础工程&#xff08;cubeide串口调试&#xff0c;printf实现&#xff0c;延时函数&#xff09;外部时钟源CLOCK(RCC)系统时钟SYS与DEBUG设置UART串口设置cubeide设置…

世界研发管理组织在美国成立,中国籍研发管理专家江新安当选总干事

World R&D Management Organization世界研发管理组织&#xff08;WRDMO&#xff09;由来自世界各地的研发管理研究组织&#xff0c;创新技术研究机构&#xff0c;院校以及研发管理咨询机构联合发起。是一个具有开放性&#xff0c;无党派性&#xff0c;非营利性的国际先进研…

如从亿点点失误,到一点点失误,我是如何做的【工作失误怎么办】

前言 只要我们还在做事&#xff0c;或者说还活着&#xff0c;就没有不犯错的时候。作为一名前端搬砖工&#xff0c;哪怕工作中再仔细小心&#xff0c;也免不了一些失误。 那这是不是说&#xff0c;失误很正常&#xff0c;改了就是嘛&#xff1f; 这么说好像没错。作为失误本…

【计算机组成与体系结构Ⅰ】知识点整理

第一章 计算机系统概论 1.1 从源文件到可执行文件 .c源程序、.i源程序、.s汇编语言程序、.o可重定位目标程序、可执行目标程序&#xff1b;后两个为二进制&#xff0c;前面为文本 1.2 可执行文件的启动和执行 冯诺依曼结构计算机模型的5大基本部件&#xff1a;运算器、控制…

【ChatGLM】使用ChatGLM-6B-INT4模型进行P-Tunning训练记录及参数讲解

文章目录 模型训练步骤参数含义名词解释欠拟合泛化能力收敛性梯度爆炸 初步结论 小结 模型训练 首先说明一下训练的目的&#xff1a;提供本地问答知识文件&#xff0c;训练之后可以按接近原文语义进行回答&#xff0c;类似一个问答机器人。 步骤 安装微调需要的依赖&#xf…

C++【哈希表封装unordered_map/set】—含有源代码

文章目录 &#xff08;1&#xff09;修改原哈希表&#xff08;2&#xff09;迭代器&#xff08;3&#xff09;最后一步&#xff08;4&#xff09;关于key是自定义类型的额外补充(面试题)&#xff08;5&#xff09;源代码 &#xff08;1&#xff09;修改原哈希表 和红黑树封装一…

【链表Part01】| 203.移除链表元素、707.设计链表、206.反转链表

目录 ✿LeetCode203.移除链表元素❀ ✿LeetCode707.设计链表❀ ✿LeetCode206.反转链表❀ ✿LeetCode203.移除链表元素❀ 链接&#xff1a;203.移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff…

python数据分析

一、数据处理 1.爬取数据 我们将使用Python的requests和BeautifulSoup库来爬取数据。在这个示例中&#xff0c;我们将爬取豆瓣电影Top250的数据。 import requests from bs4 import BeautifulSoup url https://movie.douban.com/top250 headers {User-Agent: Mozilla/5.0 …