随机序列存储格式

来自Minecraft Wiki
跳转到导航 跳转到搜索
本文章所述内容仅适用于Java版

随机序列Random Sequences)是游戏内提供可控随机化的随机数发生器序列。随机序列存储文件是存档内存储随机序列所使用的文件。

存储格式[编辑 | 编辑源代码]

随机序列存储文件位于<维度根目录>/data/random_sequences.dat。例如主世界的随机序列存储文件位于<存档根目录>/data/random_sequences.dat下界的位于<存档根目录>/DIM-1/data/random_sequences.dat

随机序列存储文件使用GZip压缩的NBT文件格式保存,其内部有下列NBT结构:

  • NBT复合标签/JSON对象 根标签
    • NBT复合标签/JSON对象*
      *
      data
      :随机序列数据。
      • 布尔型*include_sequence_id:计算随机序列时是否代入随机序列的命名空间ID计算。此项可由/random reset *修改。如果不存在则认为是true
      • 布尔型*include_world_seed:计算随机序列时是否代入世界种子计算,如果不代入世界种子则任何存档内此随机序列行为一致。此项可由/random reset *修改。如果不存在则认为是true
      • 整型*salt:初始化随机序列时掺入的。如果不存在则认为是0。
      • NBT复合标签/JSON对象*
        *
        sequences
        :当前存档内已创建的随机序列。
        • NBT复合标签/JSON对象<命名空间ID>:一个随机序列。
          • 长整型数组*
            *
            source
            :随机数源数据。
            • 长整型:随机数种子的低64位。
            • 长整型:随机数种子的高64位。
    • 整型*DataVersion:保存此强制加载区块存储文件的游戏的数据版本。如果此项不存在则游戏认为此项是1343(Java版1.12.2)。

随机序列[编辑 | 编辑源代码]

创建[编辑 | 编辑源代码]

构造Xoroshiro128++随机数发生器的过程

创建随机序列需要有3个参数:随机序列的命名空间IDidentifier世界种子worldSeed和盐salt。根据布尔型include_sequence_id布尔型include_world_seed,游戏会决定是否将命名空间ID和世界种子加入计算中以提高随机序列的随机化程度。

游戏使用的随机数发生器是Xoroshiro128++,这个随机数发生器使用128位的种子。

游戏首先计算出来一个64位的初始种子,再将初始种子拆开成为128位的未混合的种子

long initialSeed = salt;
if (include_world_seed)
	initialSeed ^= worldSeed;
long unmixedLo64 = initialSeed ^ 0x6A09E667F3BCC909L;
long unmixedHi64 = unmixedLo64 + -7046029254386353131L;

此处如果指定了布尔型include_world_seed,那么游戏会把世界种子代入计算,使得同样配置的随机序列在不同的存档中产生的随机数发生器种子不同。

接下来,如果随机序列指定了布尔型include_sequence_idtrue,则计算命名空间ID的MD5值,并与未混合的种子进行异或。这使得同一个存档中,不同命名空间ID的随机序列可以具有不同的随机数发生器种子。

if (include_sequence_id) {
	byte[] md5sum = computeMD5(identifier);
    long md5Lo64 = md5Low64bit(md5sum);
    long md5Hi64 = md5High64bit(md5sum);
    unmixedLo64 ^= md5Lo64;
    unmixedHi64 ^= md5Hi64;
}

最后,将未混合的种子进行混合,得到Xoroshiro128++随机数发生器的种子。

public static long mixStafford13(long l) {
	l = (l ^ l >>> 30) * -4658895280553007687L;
	l = (l ^ l >>> 27) * -7723592293110705685L;
	return l ^ l >>> 31;
}

long mixedLo64 = mixStafford13(unmixedLo64);
long mixedHi64 = mixStafford13(unmixedHi64);

如果计算生成的种子为0,那么游戏会将低64位设置为-7046029254386353131L,高64位设置为7640891576956012809L。

生成随机数[编辑 | 编辑源代码]

当游戏需要使用随机序列生成整数时,游戏会根据Xoroshiro128++随机数发生器算法产生随机数;如果生成的是浮点数,则根据浮点数的位数生成对应的随机浮点数;如果生成的是符合高斯分布的浮点数,则需要附加使用Marsaglia极坐标高斯方法生成。

存储行为[编辑 | 编辑源代码]

虽然每个维度都有随机序列存储文件,但实际上游戏基本上只会用主世界的随机序列存储文件。只有当主世界随机序列存储文件不存在时,各维度才会尝试读取自身维度的随机序列存储文件。

当随机序列存储文件不存在或不完整时,游戏按照下列参数初始化文件:

  • 布尔型include_sequence_id初始化为true
  • 布尔型include_world_seed初始化为true
  • 整型salt初始化为0。

当游戏使用随机序列时,随机序列会被取出,计算后更新为新的种子再被放回。如果使用随机序列时命名空间ID对应的随机序列不存在,游戏会创建对应的随机序列。

游戏内绝大多数战利品表计算都使用了随机序列,包括破坏方块时的掉落物杀死实体时的掉落物钓鱼时可以钓上的物品猪灵的以物易物等,但世界生成过程中产生的战利品箱不由随机序列控制,而受到世界种子的直接影响。

使用/random reset会重置指定的随机序列,并根据给出的参数重新创建随机序列。/random reset *会重置创建所有随机序列,并且给出的参数会被同步入随机序列存储文件内。

历史[编辑 | 编辑源代码]

Java版
1.20pre1加入了随机序列。
1.20.223w31a修改了随机序列的初始化方式。

导航[编辑 | 编辑源代码]