BE区块系列机制解析(七)——区块数据替换(上)
Naytia_帕芙洛
编辑于 2026年02月07日 16:10
收录于文集
共12篇

区块数据替换的内容比较多,将会分成上下两篇介绍。


一、区块存储数据库(DbChunkStorage)

区块数据库是一个存储区块compoundtag(nbt数据)的一个容器,是一个数据的缓冲区。区块数据库是一种特殊的区块管理器(chunksource)。它负责加载线程、服务器线程和IO线程之间的数据交互。这部分内容可能在未来的专栏里分析。

二、加载数据前的处理

首先它还是会给任务结构里的chunkviewsource内的区块尝试上互斥锁,如果失败就直接返回

加载线程在从任务队列里获取了数据替换任务后,首先会取出任务结构里的区块对象,重置区块里的所有dirtytickscounter。这么做是为了避免区块装饰阶段产生的更新影响数据替换。随后该函数会根据区块的坐标和维度创建区块键,并在区块数据库里根据这个键查询是否存在已经缓存了的区块。

如果没有,就把区块状态从5改成6直接返回。

如果有,它便会新建一个levelchunk对象,并把它的loadstate改成5。然后它会通过dbstorage(数据库)里的一个迭代器迭代那个key查到的所有东西,并和key组合成新的key。然后它会通过这个key去加载区块里的数据。

三、加载区块数据

key "+": 加载区块群系数据。

key ",":加载区块的LoadFormat,这代表了区块存储时的游戏版本。

代码块
PlainText
自动换行
复制代码
LevelChunkFormat : schar {
    V9_00                          = 0,
    V9_02                          = 1,
    V9_05                          = 2,
    V17_0                          = 3,
    V18_0                          = 4,
    VConsole1ToV18_0               = 5,
    V1_2_0                         = 6,
    V1_2_0Bis                      = 7,
    V1_3_0                         = 8,
    V1_8_0                         = 9,
    V1_9_0                         = 10,
    V1_10_0                        = 11,
    V1_11_0                        = 12,
    V1_11_1                        = 13,
    V1_11_2                        = 14,
    V1_12_0                        = 15,
    V1_14_0                        = 16,
    V1_15_0                        = 17,
    V1_16_0                        = 18,
    V1_16_0Bis                     = 19,
    V1_16_100                      = 20,
    V1_16_100Bis                   = 21,
    V1_16_210                      = 22,
    V1_16_300CavesCliffsPart1      = 23,
    V1_16_300CavesCliffsInternalV1 = 24,
    V1_16_300CavesCliffsPart2      = 25,
    V1_16_300CavesCliffsInternalV2 = 26,
    V1_16_300CavesCliffsPart3      = 27,
    V1_16_300CavesCliffsInternalV3 = 28,
    V1_16_300CavesCliffsPart4      = 29,
    V1_16_300CavesCliffsInternalV4 = 30,
    V1_16_300CavesCliffsPart5      = 31,
    V1_16_300CavesCliffsInternalV5 = 32,
    V1_18_0                        = 33,
    V1_18_0Internal                = 34,
    V1_18_1                        = 35,
    V1_18_1Internal                = 36,
    V1_18_2                        = 37,
    V1_18_2Internal                = 38,
    V1_18_3                        = 39,
    V1_18_3IndividualActorStorage  = 40,
    V1_21_4                        = 41,
    Count                          = 42,
};
复制成功

key "-":对群系的一些处理

key "/": 去序列化子区块,包含了区块记录的方块和extrablock两个二维数组

key“0”:对旧版本的方块做处理

key“1”:去序列化方块实体。首先会在一个循环里不断读nbt数据,取出方块实体的数据进行方块实体的加载。首先阅读方块实体nbt里的“id”数据,根据id决定创建哪一类方块实体。然后创建完了新的方块实体进行方块实体的load逻辑(blockactor::load)然后取出x,y,z的nbt数据写入方块实体对象的xyz标签里。然后它会检测这个位置的方块对应的方块实体和这个方块实体是不是同类型的,如果是就加入区块的方块实体哈希表。

key”3“:去序列化计划刻与随机计划刻列表

key”4“:去序列化早期版本保存的遗留extrablock,就是早期版本区块的extrablock保存在了一个单独的key里,要在这里加载

key”5“:去序列化hardcodedspawningarea(HSA)

key”?“:去序列化区块元数据

四、加载数据后的处理

首先它会在dbstorage里看能不能找到这个区块的实体数据,如果找到了就把它放进区块的一个变量里存储起来,等待区块的第一tick时去序列化这些实体nbt数据并加载生成实体。

然后它会修复错误的方块实体,如果有本应有方块实体的方块没有方块实体,一部分这种方块会新建一个方块实体,一部分不会(根据坐标决定,别的专栏有讲)

然后它会进行区块升级的逻辑(下篇专栏讲)

然后它会进行区块混合的逻辑(无实验验证):

首先获取这个区块的混合模式。如果区块是在PE版本创建的,主世界混合模式为1,地狱为3,末地为5。如果区块是在1.16以前的版本创建的且在地狱,混合模式为4

对于混合模式4,游戏会根据一系列基于柏林噪声的插值算法将部分周围生成好的区块的边界的一整竖列方块复制过来,根据一个地狱方块替换表去替换方块,让新版本区块和旧版本区块有些过渡。还会把群系复制过来。这个过程走的直接是levelchunk::setblock并且没有blocksource,也就是这个放置方块的行为不会产生任何与其它方块的交互。

如果是混合模式5,就会生成末地的黑曜石柱子啥的

混合模式3的逻辑和4大差不差

混合模式1做的要复杂很多,不过和34是在做一件事

在结束了这些处理之后,游戏会用新的区块替代旧的区块对象,并且把它的loadstate改成6