跳到正文
文档
本页内容

版本与迁移

哪个数字给什么定版本,旧文件如何迁移,以及为什么拒绝读取来自更新构建的文件

三种版本

标记全称给什么定版本存放位置
gvgame version游戏客户端Unity项目的bundleVersion
svsdk version作为库的SDK,按其公开API遵循semverSdkVersion.Value
mgmodel generation模型格式,每个域一个代每个根上的[ModelGeneration],ModelGenerations.Current

当前版本:gv 1.0.0、sv 1.0.0、mg 1

gv和sv的数字不必互相跟随,但大多数情况下是一致的。版本会一起提升,共同的更新使用同一个版本号

各部分的含义:

  • gv:major是全局更新(剧情、多人游戏),minor是普通功能,patch是紧急修复
  • sv,在SDK承诺API稳定之后:major是破坏性的API变更(删除类型,或改变签名或成员的含义),minor是无需任何人应对的新增,patch是不改变任何签名的修复

mg比其他两个更重要。SDK根据它决定是迁移文件还是拒绝读取

设置界面按这个顺序并带标记显示全部三个:gv 1.0.0, sv 1.0.0, mg 1。没有标记的话,错误报告里两个相同的数字就无法区分

警告

在SDK另行说明之前,sv跟随游戏,1.0.0并不承诺API稳定。sv的major变化目前也说明不了针对DLL编译的代码是否兼容

不是版本的数字

还有两个数字看起来像“版本”,但并不是:

  • LevelMeta.LevelVersion(键vrs):作者给关卡定的版本,是"1.0"这样的字符串
  • BlobFormat.Generation:.blob的字节布局。更多:Blob格式

代:每个域一个整数

域是作为一个整体迁移的根。一共有20个:Level、LevelMeta、UserSettings、Prefab、ThemeData、PublishProfile等。其中包括嵌套在Level内部的部分:LevelSettings、GameLevel、AudioLevel、LevelResources、LevelHints。每个域把自己的代写入封套的g键

常量值含义
ModelGenerations.Invalid-1完全没有代
ModelGenerations.Test0用于演练迁移路径的Versions/V0脚手架
ModelGenerations.Release1游戏目前写入的代,所有域都处于这一代
ModelGenerations.Current最新界面和报告显示的代

代是一个数字,而不是major.minor。文件结构的变化要么需要迁移,要么不需要,没有中间档

代的编号来自同一个全局计数器。发生变化的域取ModelGenerations.Current + 1,而不是自己的下一个空闲编号。正因如此,版本行中唯一的mg才有意义

更新的代被拒绝

每次模型变化都是新的一代。结构变化、新增成员和新的枚举值都一样算。因此,高于当前构建所知的代,就是这个构建确定从未见过的结构

一个例外:一个全新的、现有模型都没有提到的模型,例如单独存放在自己文件中的化身皮肤。它是一个新域,没有可迁移的来源,所以没有代发生变化。发生变化的是开始引用它的那个现有模型的代

这样的文件完全不读取:不填默认值,也不跳过部分内容。NewerGenerationException带有Domain、FileGeneration和BuildGeneration,宿主会请玩家更新

refused: 'Level' is at generation 2, newer than this build's 1 (domain Level, file 2, this SDK 1) - update the SDK

更旧的代会迁移

结构发生变化的域会得到:

  1. 来自全局计数器的新代
  2. 放在Versions/V<old>/下的旧结构冻结快照:一个带[ModelGeneration(domain, <old>)]的[GenerateModel]类
  3. 放在Versions/V<old>/Migrations/下的ModelMigration<TFrom, TTo>
public class GameEventsV0ToV1 : ModelMigration<GameEventsV0, GameEvents>
{
    public override GameEvents Migrate(GameEventsV0 from) => new();
}

VersionedTypeRegistry通过反射找到快照和迁移器,并沿着链一路迁移到当前的结构。有损读取所做的每一次替换都记入SerializationReport,绝不悄无声息

读取前就拒绝

LevelMeta.MinGeneration(键min_generation)是关卡中所有域用到的最高代。它在保存时由LevelGenerations.Required()计算,从不手动填写

客户端在打开level.json之前先比较它。这样来自未来的关卡不用读取几兆字节的内容就能被拒绝。没有声明的文件在这里存-1

完整的设计记录是SDK仓库中的VERSIONING.md