.mca 内に格納されるチャンク1つ分の NBT 構造。ここから先は形式依存である。
本書は 26.1 で導入された形式(DataVersion 4786 以降) を対象とし、
記述は Java版 26.2 (DataVersion 4903) の実データで確認した。
前提: 20 Anvil リージョン形式 / 31 パレット付きコンテナ
ルートは名前なし(空文字列)の TAG_Compound。
下表は Java版 26.2 の実ワールド(3,481 チャンク)を走査して確認した内容である。
「出現」欄は、走査した full/未完成を含む全チャンクのうち何割に現れたかを示す。
| キー | 型 | 出現 | 内容 |
|---|---|---|---|
DataVersion |
Int |
全部 | チャンク構造のバージョン。26.1 は 4786、26.2 は 4903 |
xPos |
Int |
全部 | 絶対チャンクX座標 |
zPos |
Int |
全部 | 絶対チャンクZ座標 |
yPos |
Int |
全部 | 最下段セクションのY位置。オーバーワールドは -4 |
Status |
String |
全部 | 生成段階(→ 1.1) |
LastUpdate |
Long |
全部 | 最終更新のゲーム内時刻 |
InhabitedTime |
Long |
全部 | プレイヤー滞在時間(tick) |
sections |
List<Compound> |
全部 | セクション(→ 2章) |
block_entities |
List<Compound> |
全部 | ブロックエンティティ |
block_ticks |
List<Compound> |
全部 | 予約されたブロック更新 |
fluid_ticks |
List<Compound> |
全部 | 予約された液体更新 |
Heightmaps |
Compound |
全部 | 高さマップ(→ 3章) |
structures |
Compound |
全部 | starts と References を持つ |
PostProcessing |
List<List> |
全部 | 生成後処理の待ち位置 |
isLightOn |
Byte |
一部 | 光源計算済みフラグ。実データでは minecraft:full のチャンクにのみ現れた |
entities |
List<Compound> |
一部 | 生成途中のチャンクだけが持つ。完成すると entities/ のリージョンへ移る |
carving_mask |
ByteArray |
一部 | 洞窟の削り取り状況。minecraft:carvers 前後の段階でのみ現れる |
実データに現れた値(生成の進行順)。
| 値 | 意味 |
|---|---|
minecraft:structure_starts |
構造物の開始点だけ決まった段階 |
minecraft:biomes |
バイオームまで決まった段階 |
minecraft:carvers |
洞窟の削り取りまで済んだ段階 |
minecraft:initialize_light |
光源の初期化まで済んだ段階 |
minecraft:full |
生成完了。ブロック操作の対象になるのはこれ |
minecraft:full 以外のチャンクは構造が不完全である。
set_block は minecraft:full のチャンクに対してのみ行うのが安全で、
それ以外は既定でエラーとする(allow_incomplete_chunk で明示的に許可できる)。
本ライブラリは上記以外のキーも含め、読んだ要素をすべて保持する。 未知のキーを落とさないことで、将来の追加要素があってもデータを壊さない。
Chunk 型は解釈済みの値(sections など)と、元の NbtCompound の両方を持ち、
書き出し時は「解釈して変更した部分だけを元の Compound に反映する」方式をとる。
sections は List<Compound>。1要素が 16×16×16 ブロックを表す。
| キー | 型 | 内容 |
|---|---|---|
Y |
Byte |
セクションのY位置。オーバーワールドは -5..20 |
block_states |
Compound |
ブロック状態のパレット付きコンテナ(4096 エントリ) |
biomes |
Compound |
バイオームのパレット付きコンテナ(64 エントリ) |
BlockLight |
ByteArray |
2048 バイト。1ブロックあたり 4bit |
SkyLight |
ByteArray |
2048 バイト。1ブロックあたり 4bit |
Y が yPos - 1 や最上段+1 になっている光源専用セクションが存在しうる。
これらは block_states を持たないため、ブロックAPIから見ると空セクションとして扱う。
(26.2 の実ワールドでは観測されず、全セクションが block_states と biomes を持っていた。
ただし将来・他の生成条件で現れうるため、block_states の有無は必ず確認する。)
BlockLight / SkyLight は必要なセクションにしか無い。
実データでは 83,544 セクションのうち BlockLight が 5,103、SkyLight が 4,904 だった。
sections は Y の昇順に並んでいるが、並び順に依存してはならない。
読み込み時に Y から索引を作る。
{ Name: "minecraft:oak_stairs", Properties: { facing: "north", half: "top", waterlogged: "false" } }
Nameは必須のString。名前空間が省略されていたらminecraft:を補うPropertiesは任意のCompound。値はすべてString(数値や真偽値も文字列)Propertiesが空の場合、Minecraft はキー自体を出力しない。本ライブラリも同様にする
パレット要素は次の形式で文字列化・解析できる(BlockState)。
<名前空間>:<パス>[<キー>=<値>,<キー>=<値>,...]
- プロパティは必ず名前の昇順に並べて出力する。 これにより、同じブロック状態は 内部の並び順に関係なく常に同じ文字列になり、全言語で一致する
- プロパティが空のときは角括弧ごと省略する
- 解析時は名前空間の省略を許し、
minecraft:を補う - 解析時のみ、
=の前後と,の直後の空白を読み飛ばす。出力側は空白を入れない
解析の失敗はすべて INVALID_ARGUMENT(呼び出し側が渡した文字列の誤りであり、
ファイルの中身の破損ではないため)。
| 入力 | 結果 |
|---|---|
stone |
minecraft:stone |
minecraft:oak_stairs[half=top,facing=north] |
minecraft:oak_stairs[facing=north,half=top] |
minecraft:oak_stairs[] |
minecraft:oak_stairs |
| `` (空文字列) | INVALID_ARGUMENT |
minecraft:oak_stairs[facing=north |
INVALID_ARGUMENT(閉じ括弧が無い) |
minecraft:oak_stairs[]extra |
INVALID_ARGUMENT(] で終わっていない) |
minecraft:oak_stairs[facing] |
INVALID_ARGUMENT(= が無い) |
minecraft:oak_stairs[=north] |
INVALID_ARGUMENT(キーが空) |
minecraft:oak_stairs[facing=north,facing=south] |
INVALID_ARGUMENT(キーの重複) |
キーの重複を後勝ちで受け入れないのは、どちらが採用されたのかが呼び出し側から 分からないまま書き込まれてしまうため。
block index = (y & 15) * 256 + (z & 15) * 16 + (x & 15)
biome index = ((y & 15) / 4) * 16 + ((z & 15) / 4) * 4 + ((x & 15) / 4)
& 15 により負の座標でも正しくセクション内相対値になる。
セクションの選択は sectionY = y >> 4(算術右シフト)で行う。
ブロックそのもの以外に、特定の座標に結びついたデータが チャンクのルート直下に 3 つある。
| キー | 内容 | 主なキー |
|---|---|---|
block_entities |
チェスト・看板・スポナーなどの中身 | id, x, y, z |
block_ticks |
ブロックのティック予約 | i(ブロックID), x, y, z, t, p |
fluid_ticks |
液体のティック予約 | 同上 |
いずれも List<Compound> で、座標は絶対ワールド座標の Int で持つ。
チャンク内相対ではないので、チャンク座標から換算して突き合わせる必要がある。
実データの例(Java版 26.2):
block_entities: {id:"minecraft:mob_spawner", x:-318, y:-40, z:-302, SpawnData:{...}, ...}
block_ticks: {i:"minecraft:acacia_leaves", x:-93, y:75, z:-305, t:0, p:0}
ブロックを別の種類へ置き換えたら、その座標を指す 2.3 の要素をすべて取り除く。
取り除かないと、たとえばチェストのあった座標に石を置いたとき
block_entities にチェストの中身が残る。ブロックと中身が食い違った状態になり、
Minecraft 側で警告やアイテムの復活といった予期しない挙動を招く。
しかも利用者からは見えにくい壊れ方をする。
| 状況 | 動作 |
|---|---|
| 別の種類のブロックを置いた | その座標の要素を 3 つのリストすべてから取り除く |
| 同じブロック状態を置き直した | 何もしない(状態が変わっていないので掃除する理由がない) |
要素が x y z を持たない |
対象か判断できないので触らない |
新しいブロックが本来必要とする block_entity(置いたのがチェストなら空のチェスト)は
生成しない。どのブロックが block entity を必要とするかの判定には
ブロック定義テーブルが要り、それ自体が別の成果物になるため
(adr/0004 と同じ理由)。
必要なら利用者が生の NBT へ直接足す。
Heightmaps は Compound で、各値は LongArray。
256 エントリ(16×16)を 9 ビット幅の BitStorage(跨ぎなし)で詰めたもの。
bits = 9
values_per_long = 64 / 9 = 7
long_count = ceil(256 / 7) = 37
index = z * 16 + x
値 = (そのXZ列で条件を満たす最上位ブロックのY) - yPos*16 + 1
| キー | 意味 |
|---|---|
WORLD_SURFACE |
空気でない最上位ブロック |
MOTION_BLOCKING |
移動を妨げるか液体である最上位ブロック |
MOTION_BLOCKING_NO_LEAVES |
上記から葉ブロックを除いたもの |
OCEAN_FLOOR |
移動を妨げる固体の最上位ブロック |
読み書き(そのまま保持)のみ対応し、再計算は行わない。 どのブロックが「移動を妨げる」かの判定にはブロック定義テーブルが必要で、 それ自体が別の大きな成果物になるため(→ adr/0004)。
ブロックを改変した後は次のいずれかを推奨する。
chunk.clear_heightmaps()—Heightmapsを削除する。Minecraft が次回読み込み時に再計算する- 何もしない — 見た目(草の生成やモブスポーン判定)が一時的にずれる可能性がある
同様に chunk.invalidate_lighting() は isLightOn を 0 にし、光源の再計算を促す。
entities/ と poi/ のリージョンファイルも同じ Anvil 形式だが、中身が異なる。
entities(dimensions/<ns>/<path>/entities/r.X.Z.mca)
| キー | 型 | 内容 |
|---|---|---|
DataVersion |
Int |
|
Position |
IntArray |
[chunkX, chunkZ] の 2 要素 |
Entities |
List<Compound> |
エンティティ。各要素が id(String) と Pos(List<Double>) を持つ |
poi(dimensions/<ns>/<path>/poi/r.X.Z.mca)
| キー | 型 | 内容 |
|---|---|---|
DataVersion |
Int |
|
Sections |
Compound |
キーがセクションYの10進文字列("-1" など)、値が下記 |
Sections の各値:
{ Valid: 1b, Records: [ { pos: [I; 279, -16, 182], free_tickets: 1, type: "minecraft:home" } ] }
これらは生の NbtCompound として読み書きできるところまでを対象とし、
型付きの API は提供しない。
ブロックの位置と範囲を表す値型を用意する。
BlockPos { x, y, z }
chunk_pos() この座標を含むチャンクの座標(x >> 4, z >> 4)
local_x() local_z() チャンク内での位置 (0..15)
offset(dx, dy, dz) 各軸へずらした座標
Cuboid { min_x, min_y, min_z, max_x, max_y, max_z }
of(x1, y1, z1, x2, y2, z2) 両端から作る。大小の順序は問わない
size_x() size_y() size_z() 各軸の長さ
volume() 含まれるブロックの個数
contains(x, y, z) 範囲に含まれるか
positions() 範囲内の座標を順に返す
Cuboid は両端を含む。of(0, 0, 0, 0, 0, 0) は 1 ブロックで volume() は 1。
positions() の並びは Y、Z、X の順とし、X をいちばん内側で動かす。
X はチャンク内で連続するので、同じセクションを続けて触れる。
チャンク座標への変換は算術右シフトで行う。 論理右シフトでは負の座標が正しく求まらない。
対応の判定はワールドの形式で行う。Minecraft のバージョン番号は条件にしない。
いまの形式は 26.1(DataVersion 4786) で入った。
これ以降のバージョンは、形式が変わらない限りそのまま読み書きできる。
MIN_SUPPORTED_DATA_VERSION = 4786 扱える形式の下限
TARGET_DATA_VERSION = 4903 動作を確かめたバージョン(26.2)
- 読み込み時、
DataVersionが4786以上なら何もしない- 対象より新しいバージョンでも警告を出さない
- 形式が同じなら読めるので、知らせる必要がない
4786未満ならReadOptions.on_version_mismatchに従うWarn(既定): 警告コールバックを呼んで続行するError:UNSUPPORTED_DATA_VERSIONIgnore: 何もしない
- 書き込み時、
DataVersionは読んだ値をそのまま残す- 書き換えると、そのワールドを開くゲーム側の判断を誤らせる
4786未満のチャンクは既定でUNSUPPORTED_DATA_VERSIONWriteOptions.allow_foreign_data_version = trueで明示的に許可できる。 このときもDataVersionは書き換えない
形式が変わったときだけ下限を上げる。 上げたら、そのワールドを扱えなくなる 利用者が出るので、バージョン番号のいちばん上の桁も上げる。
詳細は 07 バージョンポリシー。