BE区块系列机制解析(一)——区块的卸载与保存
Naytia_帕芙洛
2026年01月28日 16:34
收录于文集
共12篇

本来区块系列应该以区块加载为开始的,而且区块加载也是最复杂的。不过由于过两天要发那个基于区块保存机制的刷物品视频,就先做卸载保存的专栏了,看这篇专栏前请先阅读序章名词解释


请先阅读序章

一、区块们的保存流程

区块保存主要会由三个渠道触发,总体的框架是在服务器线程tick的某个位置触发保存——>发布区块保存的任务到任务队列——>服务器调度器阶段执行区块保存的任务将区块序列化成compoundtag——>IO线程将compoundtag写入存档

第一个触发区块保存的渠道是随机保存,每gt都会有一次,在每gt一个固定的世界级更新阶段中处理(LevelChunkSaveManager::trySaveChunks)。每gt区块保存管理器会遍历所有玩家并且看需不需要保存。然后选出一个随机的区块保存(每gt一个)。选择的流程是这样的:从mainchunksource的chunkmap里随机(概率也可能和在结构中的位置有细微联系)选出一个区块(LevelChunkSaveManager::_findRandomChunkToSave),然后检查这个区块(LevelChunkSaveManagerProxy::doesLevelChunkNeedSaving)是否有至少一个dirtytickscounter上次更新到现在的时间大于100gt或者第一次更新到现在的时间大于500gt,如果有,便把这个区块加入区块保存管理器存储的要保存区块的一个堆里面。然后保存管理器会构建一个区块保存任务发布到任务队列里。如果没有,就再找一个区块直到有一个区块需要保存或者遍历完了所有区块。

第二个触发区块保存的渠道是玩家移动(包括视角),当玩家移动时会更新玩家所属的chunkviewsource的area范围,这个过程中如果在某个区块更新后在area的范围外了(这个范围就是区块的开始加载范围,在模拟四,视距五的区块下是半径10。别的文章会详细解释)它便会进行是否要保存的判断,然后的流程就和随机保存后面的流程一样了(只是不寻找随机区块,而且只要dirtytickscounter记录的上次更新时间存在就行)。这个过程在gt中玩家行为系统更新中发生

第三个触发区块保存的渠道是Chunksource的删除,当玩家所属的chunksource删除时,或者维度的mainchunksource删除时,会发布区块保存的任务,流程和上面一样,而且会对自己管理的所有区块进行这个流程

另外,所有要卸载或保存的区块都必须loadState等于9以及不是isReadOnly的

二、区块卸载

区块卸载主要会由两个渠道触发分别对应区块保存的二和三,总体的流程是触发区块卸载——>检查是否需要区块保存(并进入区块保存流程)——>将要卸载的区块加入一个队列(LevelChunkPhase1Deleter::operator)——>Gametick中的区块回收阶段(维度级更新阶段)从队列中取出区块并删除。要卸载的区块和区块保存的第二、三类里要保存的区块一样

三、区块内部数据的保存(DbChunkStorage::_serializeChunk)

区块的保存是以key为单位进行的。每一类区块数据对应一个key。

以下是常见的chunkkey(现版本):

方块实体(blockEntities)

群系(Biome)

实体(Entities,要特别提到的是,实体在1.18.3以前是一种chunkkey,但是那之后就不储存的区块的nbt数据里了,但仍然和区块一起序列化)

计划刻与随机计划刻队列

子区块(SubChunks)

HardCodedSpawner

...

以下是区块内部数据保存的流程:

先检查并保存区块元数据(区块高度范围,区块版本等),这个过程会给levelStorage上一个共享锁

然后会修剪掉不需要的子区块,空的子区块会直接不保存,全是空气的子区块会保存为默认值。然后保存需要保存的子区块

然后会保存区块的3d群系信息

然后是重点:方块实体的保存

如果区块内的方块实体dirtytickscounter上次更新到现在的时间不为0,而且区块没有isChanged的方块实体(下个专栏会解释),并且区块内存在至少一个方块实体就会进入方块实体的保存流程。这个保存会检查方块实体和它所在位置的方块是否对应,如果不对应就不保存

然后会根据dirtytickscounter是否为0判断要不要保存计划刻和随机计划刻列表

最后会保存区块的实体。