すーみそー
sumiso_c0db8cだ。
統合版マイクラ、スーパーフラットワールドの設定ファイルlevel.datを解析して
オリジナルのスーパーフラットを作成してみた。
本記事は統合版マイクラ 1.21.124で動作確認を行いました。
先行研究
「クラフターズコロニー」さんではカスタムフラットジェネレーターを公開されています。
https://minecraft-mcworld.com/custom-flat/
ソースコードはGitHubにてMITライセンスで提供されています。こちらを参考にしました。
level.dat取得
適当なスーパーフラットのワールドをマイクラで作成し、level.datを取得します。
セーブデータより、level.datを取得します。
%APPDATA%\Minecraft Bedrock\Users\[ユーザーのid]\games\com.mojang\minecraftWorlds\[ワールドのフォルダ]\level.dat
エクスプローラーで%APPDATA%\Minecraft Bedrock\Users\にアクセスすると数字のフォルダがあるため
そこからたどっていくのがオススメです。
ワールドのフォルダ名は謎の英字記号の組み合わせで、どのワールドか分かりません。
開いてworld_icon.jpegをみれば、ワールド選択画面の画像を確認できます。
level.dat解析
level.datファイルはバイナリデータのため、メモ帳などの普通のテキストエディタでは開けません。jupyter notebook上でpythonを利用して解析していきます。
環境構築はこちら。
pythonでのバイナリデータ操作の解説はこちらの記事をご参照ください。
ファイル読み込み
バイナリデータを読み込みます。
filepath = 'level.dat'
# ファイルを読み込む
with open(filepath, 'rb') as f:
data = f.read()
data
b'\n\x00\x00\x00\...(以下略)
bytesオブジェクトが文字列表現で出力されます。こちらをテキストファイルにコピペして保存します。以下このファイルから該当部分を目視でコピペし、解析しています。
シード値
シード値が正の値の場合、RandomSeedに続く8バイトのデータがシード値を表しています。
この部分をコピペしてデコードします。
RandomSeed\x8d\x00\x00\x00\x00\x00\x00\x00
import struct
seed_bytes = b'\x8d\x00\x00\x00\x00\x00\x00\x00'
seed = struct.unpack('<q', seed_bytes)[0]
print(f"シード値: {seed}")
シード値: 141
ワールド名
LevelName直後の2バイトがワールド名の長さを表し
その次がワールド名本体です。
LevelName\x1e\x00sumiso\xe3\x82\xb9\xe3\x83\xbc\xe3\x83\x91\xe3\x83\xbc\xe3\x83\x95\xe3\x83\xa9\xe3\x83\x83\xe3\x83\x88
name_length = struct.unpack('<H', b'\x1e\x00')[0]
print(f"ワールド名の長さ:{name_length}バイト")
ワールド名の長さ:30バイト
name_bytes = b'sumiso\xe3\x82\xb9\xe3\x83\xbc\xe3\x83\x91\xe3\x83\xbc\xe3\x83\x95\xe3\x83\xa9\xe3\x83\x83\xe3\x83\x88'
level_name = name_bytes.decode('utf-8')
print(f"ワールド名: {level_name}")
ワールド名: sumisoスーパーフラット
地形の設定
FlatWorldLayers直後の2バイトが地層の定義の長さを表し
続けて地層の定義がjson形式で記載されています。
FlatWorldLayers\xfb\x00{フラットワールドの地層}
地層の定義をフォーマットしたものがこちら
{
"biome_id": 1,
"block_layers": [
{
"block_name": "minecraft:bedrock",
"count": 1
},
{
"block_name": "minecraft:dirt",
"count": 2
},
{
"block_name": "minecraft:grass_block",
"count": 1
}
],
"encoding_version": 6,
"preset_id": "ClassicFlat",
"world_version": "version.post_1_18"
}
biome_idはwikiに記載があります。
https://minecraft.wiki/w/Biome#Bedrock_Edition
1は平原バイオームです。
block_nameはコマンドでブロックを指定するときの名前です。
world_versionは1.18の洞窟と崖のアップデートでの地形生成ルール大幅変更後、かと思われます。
level.dat生成
以上の解析を踏まえて、これらの値を書き換えてlevel.datを再構築
オリジナルスーパーフラットを作成してみました。
テンプレート作成
解析したlevel.datをテンプレートとし、値を書き換えます。
変更のない部分のバイナリデータをコピーし、部分ごとに定義しておきます。
head = b'\(ファイルの最初から)FlatWorldLayers'
after_FlatWorldLayers = b'\n\x01(中略)LevelName'
after_LevelName = b'\x03\x13\x00(中略)RandomSeed'
after_seed = b'\x01\x10\x00SpawnV1Villagers(ファイルの最後まで)'
地形の設定
地形をお好みで書き換えます。
import json
# JSON構築
flat_json = {
"biome_id": 5,
"block_layers": [
{
"block_name": "minecraft:bedrock",
"count": 1
},
{
"block_name": "minecraft:gray_concrete",
"count": 63
},
{
"block_name": "minecraft:gray_concrete",
"count": 100
}
],
"encoding_version": 6,
"preset_id": None,
"world_version": "version.post_1_18"
}
json_str = json.dumps(flat_json,separators=(',', ':'),)
json_bytes = json_str.encode('utf-8')
json_len = len(json_bytes) + 1
# 2バイトの長さ情報(リトルエンディアン)
json_header = struct.pack('<H', json_len)
バイオームはタイガ、灰色のコンクリートを高さ100まで敷き詰めます。
"preset_id": Noneにしておかないとクラシックフラットが爆誕します。
jsonはスペースを入れずに詰めて作成し、長さを+1します。
シード値
シード値を変換します。
seed = 141
seed_bytes = struct.pack('<q', seed)
ワールド名
ワールド名と長さ情報のバイナリデータを生成します。
world_name = 'sumiso creative テンプレート'
world_name_bytes = world_name.encode('utf-8')
world_name_len_bytes = len(world_name_bytes).to_bytes(2, 'little')
データ結合
作成したバイナリデータを結合します。結合したバイナリデータを元に、ファイルサイズを取得、ヘッダー部に上書きします。
# バイナリ結合
binary = head + json_header + json_bytes + after_FlatWorldLayers + world_name_len_bytes + world_name_bytes + after_LevelName + seed_bytes + after_seed
# ファイルサイズをヘッダーに上書き
file_size = len(binary) - 8
file_size_bytes = struct.pack('<I', file_size)
binary = binary[:4] + file_size_bytes + binary[8:]
binary
mcworld生成
level.datを生成、マイクラにインポートできるようにmcworld形式にまとめます。
import zipfile
import os
# 1. level.dat を保存
with open("level.dat", "wb") as f:
f.write(binary)
# 2. ZIPファイルに圧縮(level.dat を含む)
with zipfile.ZipFile("world.zip", "w", zipfile.ZIP_DEFLATED) as zipf:
zipf.write("level.dat")
# 3. ZIPファイルを .mcworld にリネーム
os.rename("world.zip", "sumiso_world.mcworld")
動作確認
作成したmcworldをマイクラにインポートします。
灰色のコンクリートのスーパーフラット、高さ100ができました。これでスライムチャンク湧きのスライムに悩まされることはありません。
まとめ
本記事では、統合版マイクラのlevel.datファイルをpythonで解析し、オリジナルスーパーフラットを生成してみました。
level.dataには他にも様々な設定値が定義されています。バイナリデータを読み解く自信のある方は解析に挑戦してみてください。


