存档目录与文件命名(save-directory-layout)

基本信息

属性 值
区域名载体 cubicchunks.regionlib.api.region.key.RegionKey,只有 getName() 一个字符串字段
名字的用途 直接当文件名(打包区)或目录名(.ext),所以 equals / hashCode 都只基于 name
顶层布局 SaveCubeColumns.create(Path) 建 region2d/ 与 region3d/ 两个子目录

命名规则表

三种 key 类型的 getRegionKey() 与 getId() 打包方式(全部逐字取自源码):

key 类 LOC_BITS 每区域条目数 区域名格式 正则(IKeyProvider.isValid)
EntryLocation2D 5 32 × 32 = 1024 <regX>.<regZ>.2dr -?\d+\.-?\d+\.2dr
EntryLocation3D 4 16 × 16 × 16 = 4096 <regX>.<regY>.<regZ>.3dr -?\d+\.-?\d+\.-?\d+\.3dr
MinecraftChunkLocation 5 32 × 32 = 1024 r.<regX>.<regZ>.<extension> r\.-?\d+\.-?\d+\.<extension>(构造时拼串)

其中 regX = entryX >> LOC_BITS(entryY / entryZ 同理),负坐标靠算术右移自然带出 -。

id 打包

key 类 getId()
EntryLocation2D ((entryX & 31) << 5) | (entryZ & 31) —— X 在高位
EntryLocation3D ((entryX & 15) << 8) | ((entryY & 15) << 4) | (entryZ & 15)
MinecraftChunkLocation ((entryZ & 31) << 5) | (entryX & 31) —— Z 在高位,与 EntryLocation2D 相反

MinecraftChunkLocation.getRegionKey() 用 "r." + regX + "." + regZ + "." + extension(先 X 后 Z),但 getId() 是 Z 在高位。而 EntryLocation2D 是 getRegionKey() 先 X 后 Z、getId() 也 X 在高位。三者互不相同,切勿混用。

<extension> 取值

MinecraftSaveSection.MinecraftRegionType 只有 MCR 与 MCA 两个枚举值,createAt 里用 type.name().toLowerCase() 转小写,所以最终文件名是:

枚举 区域名 文件名
MCR r.<regX>.<regZ>.mcr r.<regX>.<regZ>.mcr
MCA r.<regX>.<regZ>.mca r.<regX>.<regZ>.mca

目录树

SaveCubeColumns.create(Path directory)(impl/SaveCubeColumns.java):

<directory>/
├── region2d/                     # SaveSection2D.createAt
│   ├── <regX>.<regZ>.2dr         # 打包区域文件(512 B/扇区)
│   └── <regX>.<regZ>.2dr.ext/    # ExtRegion 目录
│       └── <id>                  # 例如 0 / 1 / 1023
└── region3d/                     # SaveSection3D.createAt
    ├── <regX>.<regY>.<regZ>.3dr
    └── <regX>.<regY>.<regZ>.3dr.ext/
        └── <id>

写入过程中的 <id>.tmp 也短暂出现在 .ext 目录里(构造函数的位图构建会忽略非数字名)。

MinecraftSaveSection.createAt(Path, MinecraftRegionType) 不建子目录,直接用传入的 directory 存 r.X.Z.mcr / r.X.Z.mca。

命名约束(javadoc 声明 vs 实际实现不一致)

IKey.getRegionKey() 的 javadoc 写:

The name may be used as a file name, so names not matching ^[a-z0-9\._\-]+$ are not supported. Uppercase characters are not allowed to avoid case-sensitivity issues across different operating systems.

但三个 isValid 正则都以 -?\d+ 开头,允许数字和 - 之外的场景不匹配——$ 之外还漏了 [0-9]。也就是说:负坐标生成的区域名(如 -1.0.2dr)并不满足 javadoc 声明的字符集,却恰恰是 isValid 认可的合法名字。javadoc 与实现存在矛盾,以实现为准。

枚举已有区域

SimpleRegionFactory.allRegions():

return Files.list(this.directory)
        .map(Path::getFileName)
        .map(Path::toString)
        .map(RegionKey::new)
        .filter(this.keyProvider::isValid);
特性 行为
过滤 只保留能通过 keyProvider.isValid 的名字,.ext 目录等一律排除
目录不存在 Files.list 抛 java.nio.file.NoSuchFileException(无 try-catch)
资源释放 没有 try-with-resources,返回的 Stream 未被关闭(SimpleRegionFactory.java:89-94)——调用方必须自己关(各 allRegions 的 javadoc 也都强调了这点)
流语义 只保证"调用时刻已创建"的区域一定出现,迭代期间新建的不保证

SharedCachedRegionProvider.allKeys() 在此之上还会对每个区域 for (int id = 0; id < keyCount; id++) 逐 id 造 key,再一次性 forExistingRegion + removeIf(r -> !r.hasValue(key)) 过滤——注释说明这样比 IRegionProvider#fromRegionAndId 慢的方式快(ArrayList#removeIf)。

本 mod 仓库里随包发布的数据文件

没有。 ls -d src/main/resources → No such file or directory;git ls-files | grep -v '\.java$' 只返回构建脚本、许可与 CI 配置。无 mcmod.info、无语言文件、无任何 JSON/YAML 数据文件——所有区域/扇区布局都是代码里硬编码的常量(上表那些 LOC_BITS / .2dr / .ext 字面量),不存在可被玩家改写的配置文件。

相关条目