マインクラフトのソースコードは興味深いです。

2026/03/20 7:33

マインクラフトのソースコードは興味深いです。

RSS: https://news.ycombinator.com/rss

要約

日本語訳:

元の要約は高レベルのアイデアを捉えているものの、キー・ポイントリストに含まれる多くの具体的実装詳細が省略されています。より完全なバージョンには次の項目を含めるべきです。

  • ポインタカウンタ用の正確なパッキング方式と、そのロックフリー読み取りメカニズム。
  • 特定のライト圧縮インデックス(
    128/129
    )と、チャンクあたり約16 KBから約3 KBへのメモリ節約効果。
  • モンロー(Z‑order)インデクシング手法、16ビット圧縮レベルフィールド、および均一ブロックがタイルIDをインデックスに直接格納する方法。
  • XPhysicalAlloc
    を用いた物理ページのアロケーション、チャンクごとの固定8 KB割り当て、および PS3 ハードウェアでヒープ断片化を回避する理由。
  • 爆発ロジックのレイマーチング詳細と、オブシディアン壁後ろにあるダメージバグ修正。
  • エンティティID用の2048ビットビットフィールドアロケータ、並列配列、および ID 再利用衝突を防ぐ仕組み。
  • 「ワーム」洞窟アルゴリズムの詳細(楕円形バブル、
    sin(d/16π)
    ベルカーブ、「ファス」係数)と隣接バイオームの水色ブレンド。
  • パーリンノイズにおける五次平滑多項式、オクターヴスタッキング、および 16 777 216 ブロックでの座標ラッピングによるシームレスワールド化。
  • スレッドごとの一時 AABB プール戦略、XOR‑ツリー SIMD 差分チェック、符号拡張トリック。

これらの詳細を含めることで、要約はキー・ポイントリストと完全に整合しつつ、主旨を明確かつ理解しやすく保つことができます。

本文

2026年3月19日

Minecraft の「Old Console」ソースコードが 2026 年 3 月 1 日に流出しました。多くの人々がポートを始め、モッドや動画を作成しています。ソースをレビューしているうちに、共有したい興味深いトリックとアルゴリズムが数多く見つかりました。


1. 時代の初期

  • Mojang は「4J 形状」のブロッキーワールドをアップデートごとに作り上げてきました。
  • マイクロソフトは Fire Nation が攻撃した後に到着し、領域は新しい時代へ…(ただの馬鹿みたいなこと)。
  • PS3 バージョンも「またひどいですが、我々のスタイルのひどさです」。

元々 Minecraft は Notch によって Java で書かれました。コンソールは Java を実行できないため、誰かが全体を C++ に書き直す必要がありました。4J Studios(スコットランドの小さなゲームスタジオ)が、Java 版 Minecraft をコンソール向けにポートするために雇われました。

ポートは約 3,300 の C++ ソースファイルで構成され、2 つのプロジェクトに分かれています:
Minecraft.Client – レンダリング、UI、プラットフォーム関連。
Minecraft.World – ゲームプレイ、ワールド生成、ネットワーク。

地形生成からクリーパー爆発、レッドストーンまで、すべてを 256 MB の RAM と PS3 の Cell プロセッサ向けに C++ で最初から書き直しました。


2. ポインタとカウンタを一つの数値に詰め込む

SparseLightStorage.cpp
では、メモリポインタと光平面数を 64 ビット整数にまとめています:

dataAndCount = 0x007F000000000000L |
                ((__int64)planeIndices & 0x0000ffffffffffffL);
  • x86‑64 の仮想アドレスは下位 48 ビットしか使わず、上位 16 ビットはゼロです。
  • 4J はその上位ビットを光平面の割り当て数として利用しています。

読み出すとき:

int   count = (sourceDataAndCount >> 48) & 0xffff;
unsigned char *ptr =
        (unsigned char *)(sourceDataAndCount & 0x0000ffffffffffffL);

この値全体を単一の compare‑and‑exchange で原子操作できるため、光データの読み取りはロック不要です。書き込みのみがコピーとスワップのコストを負担しますが、ライト変更はまれなので、書き込みは稀です。

コンパイラはポインタ切り捨てについて警告します;4J は

#pragma warning ( disable : 4826 )
で抑制しています。
これにより秒間何百万ものロック取得を削減できます。


3. 約20行のガーベジコレクタ

ポインタは更新時に他スレッドから読み取られる可能性があるため、古いポインタを即座に解放できません。そこで三つの delete キューを回転させます:

void SparseLightStorage::queueForDelete(unsigned char *data)
{
    deleteQueue[deleteQueueIndex].Push(data);
}

void SparseLightStorage::tick()
{
    int freeIndex = (deleteQueueIndex + 1) % 3;
    unsigned char *toFree = NULL;
    do {
        toFree = deleteQueue[freeIndex].Pop();
        if (toFree) free(toFree);
    } while (toFree);

    deleteQueueIndex = (deleteQueueIndex + 1) % 3;
}

書き込みインデックスは毎ティックで 1 進みますが、解放は常に 2 ステップ先のキューから行われます。これにより、すべてのポインタは少なくとも二回分のゲームティックを経過してから解放され、読者は完了する時間を確保できます。

Linux カーネルのデータ構造に似た epoch‑based メモリ再利用スキームで、約 20 行のコードです。


4. ライト圧縮

Minecraft の照明はブロックあたり 4 ビット(0–15)です。
単純なチャンクは 128 × 16 × 16 × 0.5 = 16 384 バイトのライトデータを保持します。

観察:ほとんどの水平平面は全てゼロ(地下のブロック光)または全て十五(地上の天空光)です。したがってフル平面ではなくインデックスだけで済むようにしました:

static const int ALL_0_INDEX  = 128;   // この平面は全暗
static const int ALL_15_INDEX = 129;   // この平面は全明

各 Y レベルに 1 バイトを割り当てます。128 または 129 の場合、データは割り当てられず値が暗黙的になります。混合照明の平面だけが 128 バイトを使用します。

典型的なチャンクでは約 20–30 平面に実際のデータが必要 → 80 % のメモリ削減
この圧縮でほとんどのチャンクは 3 KB、元は 16 KB に抑えられ、256 MB の RAM を持つコンソールでも大きな違いを生みます。


5. ビット散乱座標(Z‑order 曲線)

圧縮タイルストレージは各 16×128×16 チャンクを 4×4×4 タイルの 512 ブロックに分割します。

(x,y,z)
をブロックとタイルインデックスへ変換する際にビット交差(Morton コード)を使用:

inline void CompressedTileStorage::getBlockAndTile(int *block, int *tile,
                                                   int x, int y, int z)
{
    *block = ((x & 0x0c) << 5) | ((z & 0x0c) << 3) | (y >> 2);
    *tile  = ((x & 0x03) << 4) | ((z & 0x03) << 2) | (y & 0x03);
}

メモリオフセットへ戻す:

inline int CompressedTileStorage::getIndex(int block, int tile)
{
    // index bits: xxxxzzzzyyyyyyy
    int index = ((block & 0x180) << 6) | ((block & 0x060) << 4) |
                ((block & 0x01f) << 2);
    index |= ((tile & 0x30) << 7) | ((tile & 0x0c) << 5) | (tile & 0x03);
    return index;
}

これは手作りの Morton コードです。X、Y、Z 座標のビットを交差させることで、3D 空間で近接したブロックがメモリ上でも近くに配置され、キャッシュヒット率が大幅に向上します。

ブロックの 16‑bit インデックスは圧縮モードをエンコード:

…00 → 1 ビット/タイル(2 種類)
…01 → 2 ビット/タイル(4 種類)
…10 → 4 ビット/タイル(16 種類)
…11 → 8 ビット/タイル(完全、未圧縮)
111   → 0 ビット/タイル(一様ブロック、ID が上位 8 ビットに格納)

4×4×4 ブロック全体が同一タイル(石・空気・水など)であれば、タイル ID はインデックスの上位 8 ビットに直接保存されます。ポインタや間接参照は不要です。

結果:32 KB のチャンクが約 4 KB に圧縮。1 KB を節約することで、より多くのチャンクをロード状態に保てるため、描画距離が延び、世界が大きく感じられます。


6. ヒープをバイパス

圧縮ライト用メモリは

XPhysicalAlloc
で確保し、ヒープを回避します:

indicesAndData = (unsigned char *)XPhysicalAlloc(32768 + 4096,
                                                MAXULONG_PTR, 4096,
                                                PAGE_READWRITE);

コメントでは物理アロケーションによりページ単位でクリーンに解放できると説明。標準ヒープを使うと時間経過とともに断片化が進み、コンソールの限られた RAM で問題になるためです。

チャンクは最大でも 8 KB の圧縮データしか必要としないことが測定されており、予測可能なオーバーヘッドは許容範囲です。


7. Java 標準ライブラリのクローン

PS3 ワールドは Java Edition と完全に一致させる必要があるため、Java の標準ライブラリを多く再実装しました:

  • Random
    (48 ビット状態の LCG)
  • HashMap
    補助ハッシュ
  • ストリーム・ファイルクラス(
    InputStream
    OutputStream
    など)
  • タスクスケジューリング用
    Comparable
    インタフェース
  • ポーション効果用
    List.hashCode()

例:Java の整数キーに対する補助ハッシュ:

int operator()(const int &k) const {
    int h = k;
    h += ~(h << 9);
    h ^= (unsigned int)(h >> 14);
    h += (h << 4);
    h ^= (unsigned int)(h >> 10);
    return h;
}

64 ビットキーの場合:

int operator()(const __int64 &k) const {
    return hash((int)(k ^ (((__uint64)k) >> 32)));
}

8. 爆発:物理ではなくレイキャスティング

爆発は 16×16×16 グリッドの表面セルから約 1,352 本のレイを放ちます:

for (int xx = 0; xx < 16; ++xx)
 for (int yy = 0; yy < 16; ++yy)
  for (int zz = 0; zz < 16; ++zz) {
      if ((xx != 0 && xx != 15) &&
          (yy != 0 && yy != 15) &&
          (zz != 0 && zz != 15))
          continue; // 内部はスキップ

      double xd = xx / 15.0 * 2 - 1;
      double yd = yy / 15.0 * 2 - 1;
      double zd = zz / 15.0 * 2 - 1;
      double d  = sqrt(xd*xd + yd*yd + zd*zd);
      xd /= d; yd /= d; zd /= d;

      float remainingPower = r * (0.7f + level->random->nextFloat() * 0.6f);
      float stepSize       = 0.3f;

      while (remainingPower > 0) {
          int t = level->getTile(xt, yt, zt);
          if (t > 0)
              remainingPower -=
                 (Tile::tiles[t]->getExplosionResistance(source)+0.3f)*stepSize;
          if (remainingPower > 0)
              toBlow.insert(TilePos(xt, yt, zt));

          xp += xd * stepSize;
          yp += yd * stepSize;
          zp += zd * stepSize;
          remainingPower -= stepSize * 0.75f;
      }
  }

レイはパワーがゼロになると停止し、各ブロックが爆発抵抗を減算します。これによりオブシディアン壁が TNT を防ぎ、土が阻止される現象を再現できます。


9. エンティティ ID:ビットフィールド割り当て

ネットワーク化されたエンティティはサーバーとクライアントで同一の ID を持つ必要があります。2048 スロットのビットフィールドアロケータを使用:

unsigned int Entity::entityIdUsedFlags[64] = {0};

int Entity::getSmallId() {
    for (int i=0; i<64; ++i) {
        unsigned int uiFlags = entityIdUsedFlags[i];
        if (uiFlags != 0xffffffff) {
            unsigned int uiMask = 0x80000000;
            for (int j=0; j<32; ++j) {
                if ((uiFlags & uiMask) == 0) {
                    uiFlags |= uiMask;
                    entityIdUsedFlags[i] = uiFlags;
                    return i*32 + j;
                }
                uiMask >>= 1;
            }
        }
    }
    app.DebugPrintf("Out of small entity Ids... possible leak?\n");
    __debugbreak();
}

ID 0–2047 は 11 ビットで表現でき、帯域幅を節約します。ローカル専用エンティティは 2048 以上の ID を使用します。


10. 洞窟掘削:ワームアルゴリズム

洞窟は「ワーム」が空間を移動し、方向が徐々に変化しながら各ステップで楕円形バブルを切り取ります:

double r = (Mth::sin(d / 16.0f * PI) * radius + 1) * ss + 1;
if (xd*xd + yd*yd + zd*zd < random->nextDouble() * fuss +
    (1 - fuss))
    // ブロックを除去

正弦はベルカーブを作り、洞窟の中央で広がり両端で狭くなる形状にします。

fuss
要因が壁面に粗さを付与します。


11. 水色合成

バイオーム境界では水は隣接する 9 個のバイオームの色を平均化します:

for (int oz = -1; oz <= 1; ++oz)
 for (int ox = -1; ox <= 1; ++ox) {
     int waterColor = level->getBiome(x+ox, z+oz)->getWaterColor();
     totalRed   += (waterColor & 0xff0000) >> 16;
     totalGreen += (waterColor & 0x00ff00) >> 8;
     totalBlue  += (waterColor & 0x0000ff);
 }
return (((totalRed/9)&0xFF)<<16) |
       (((totalGreen/9)&0xFF)<<8)  |
       ((totalBlue/9)&0xFF);

12. 木は上から下へ成長

葉は幹より先に配置され、最高 Y 座標から順に下方向へ進みます:

for (int yy = y + treeHeight; yy >= y - grassHeight + treeHeight; --yy) {
    int yo   = yy - (y + treeHeight);
    int offs = extraWidth + 1 - yo/2;
    for (int xx = x - offs; xx <= x + offs; ++xx)
     for (int zz = z - offs; zz <= z + offs; ++zz) {
         if (abs(xo)==offs && abs(zo)==offs &&
             (random->nextInt(2)==0 || yo==0))
              continue;
         if (!Tile::solid[level->getTile(xx, yy, zz)])
             placeBlock(level, xx, yy, zz,
                        Tile::leaves_Id, leafType);
     }
}

ブロックを下から上へ配置すると、各葉で高さマップが再計算されるため非効率です。上から下に更新すれば列ごとに一度だけ高さマップが更新されます。


13. ゾンビの日照確率

ゾンビが火をつけるのは確率的です:

if (br > 0.5f && random->nextFloat() * 30 <
    (br - 0.4f) * 2 &&
    level->canSeeSky(floor(x), floor(y), floor(z))) {
    setOnFire(8);
}

明るさ 0.5 のときは約 0.67 %/ティック、最大明るさで約 4 %。自然に見える挙動を実現しています。


14. チャージドクリーパー

void Creeper::thunderHit(const LightningBolt *bolt) {
    Monster::thunderHit(bolt);
    entityData->set(DATA_IS_POWERED, (byte)1);
}

発砲時:

if (isPowered())
    level->explode(this, x, y, z, 6, destroyBlocks); // 半径 6
else
    level->explode(this, x, y, z, 3, destroyBlocks); // 半径 3

1 バイトのフラグで爆発半径が 3→6 に倍増します。爆発力は半径に比例するため、チャージドクリーパーは約四倍破壊力を持ちます。


15. 地形生成(Perlin ノイズ)

勾配計算はビット操作でコンパクト化:

double ImprovedNoise::grad(int hash, double x, double y, double z) {
    int h = hash & 15;
    double u = h < 8 ? x : y,
           v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
    return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}

平滑多項式:

double u = x*x*x*(x*(x*6-15)+10); // 6x^5 – 15x^4 + 10x^3

マルチオクターブノイズは周波数を重ねます:

for (int i=0; i<levels; ++i) {
    value += noiseLevels[i]->getValue(x*pow, y*pow, z*pow)/pow;
    pow /= 2;
}

地形は 16 777 216 ブロックごとに繰り返します(

xb %= 16777216;
)。


16. AABB プール

衝突検出には一時的な軸揃った境界箱(AABBs)が必要です。各スレッドで循環バッファを使いヒープ割り当てを回避します:

AABB *AABB::newTemp(double x0, double y0, double z0,
                    double x1, double y1, double z1) {
    ThreadStorage *tls = (ThreadStorage *)TlsGetValue(tlsIdx);
    AABB *thisAABB = &tls->pool[tls->poolPointer];
    thisAABB->set(x0, y0, z0, x1, y1, z1);
    tls->poolPointer = (tls->poolPointer + 1) % ThreadStorage::POOL_SIZE;
    return thisAABB;
}

各スレッドに 1024 個の事前割り当て済み AABB があり、new/delete やロック競合は発生しません。


17. XOR 木でメモリ比較

チャンクが変更されたか判断するために 64 バイトブロックを手動 SIMD で比較します:

__int64 d0 = pOld[0] ^ pNew[0];
...
__int64 d7 = pOld[7] ^ pNew[7];

d0 |= d1; d2 |= d3; d4 |= d5; d6 |= d7;
d0 |= d2; d4 |= d6;
if (d0 | d4) return false;

8 回の XOR、4 回の OR、さらに 2 回の OR、最後に一度だけ判定。ブランチレスで高速です。


18. 座標復元用符号拡張

座標がキャッシュエントリにパックされている場合、復元時に符号拡張します:

xx = (xx << 22) >> 22;   // 負の値を符号拡張

1 行で両方の符号(正・負)を扱える単純かつ高速な手法です。


開発者メモ

コードには

// 4J Stu – I have no idea what was going on here
// didn’t went ok
といったコメントが散在しています。これは小規模チーム向けに書かれた実運用ビルドの真相を示すサインであり、公開向けではありません。ソースファイルには変更理由や必要なハック、まだ脆弱と感じる箇所への正直なメモが含まれています。


以上 – Minecraft コンソール版の混沌としたながらも高性能な世界を垣間見ることができました。

同じ日のほかのニュース

一覧に戻る →

2026/03/20 5:33

コックピットは、サーバー用のウェブベースのグラフィカルインターフェイスです。

## Japanese Translation: **Cockpit** は、Linux 管理者がオペレーティングシステムから直接サーバーを管理できる軽量でブラウザベースのインターフェイスです。OS 内で動作し、Debian、Fedora、および RHEL など主要なディストリビューションに対応しており、コンテナの起動、ストレージやネットワークの設定、ログの確認、ブラウザを離れずにターミナルとグラフィカルコントロール間で切替えなどが可能です。 ユーザーは Cockpit からでも従来のシェルからでもサービスをシームレスに開始・停止でき、端末で発生したエラーは自動的に Cockpit のジャーナルインターフェイスに表示されます。 プロジェクトはリモート管理もサポートしており、ユーザーは SSH 経由で Cockpit がインストールされた他のマシンを追加・管理できるため、ホスト切替が簡単です。コミュニティサポートは Matrix チャネル(#cockpit:fedoraproject.org)とメールリストで提供されます。ドキュメントにはツールの使い方だけでなくコードベースへの貢献方法も網羅しており、ガイディングプリンシプル、リリースノート、およびプライバシーポリシーが含まれています。 複数の Linux ディストリビューションにわたる統一で使いやすい GUI を提供することで、Cockpit はサーバー管理を効率化し、ドキュメントと活発なコミュニケーションチャネルを通じて継続的な開発者貢献を促進することを目指しています。

2026/03/19 22:05

**Astral が OpenAI に参入**

## Japanese Translation: **(欠落していた詳細を補完)** ### 要約 Astral は、オープンソースの Python ツールを存続させつつ OpenAI の Codex チームに参加することに合意し、そのツールチェーンをモダンな Python 開発の中心に位置付けました。この取引は Astral の創業者が発表し、Python エコシステムの生産性を少なくとも 1 % 向上させる高レバレッジ戦略を強調しています。Ruff(高速リンター)、uv(依存関係解決ツール)、ty といった人気ツールへの継続的なサポートが含まれ、これらを Codex の AI コーディングアシスタントに統合する計画です。Astral のツールチェーンはゼロから数億件の月間ダウンロードへと成長し、Accel が主導した Casey Aylward 氏によるシード資金調達と Andreessen Horowitz が牽引した Jennifer Li 氏によるシリーズ B 資金調達で支えられています。創業者は Astral チームのユーザー重視の製品品質に感謝し、今後も高い基準を維持するとともに、ユーザーの信頼への感謝を表明しました。買収後、Astral はオープンソース提供物の開発を継続し、それらを Codex と統合し、ソフトウェアエンジニアリングにおける影響力を拡大します。これにより、開発者・企業・広範なエコシステムは、生産性を加速させる AI 強化型の堅牢な Python ユーティリティ―基盤となるツールセット―を享受できます。

2026/03/20 2:16

Google、未認証Androidアプリをサイドロードするための新しい24時間プロセスを発表

## Japanese Translation: Googleは2025年後半にAndroid向けの開発者認証プログラムを開始し、開発者が認証されていない場合はサイドロードされたアプリをブロックすることでマルウェアリスクを低減することを目指します。開発者は本人確認書類を提出し、アプリ署名キーをアップロードし、25ドルの手数料を支払う必要があります。 ユーザーは「未認証パッケージを許可」オプションを有効にして認証を回避できます。設定方法は、ビルド番号を7回タップして開発者向けオプションを解除し、スイッチを切り替えてPIN/パスワードで確認し、デバイスを再起動します。その後24時間待ち、次に「一時的に許可」または「無期限に許可」を選択します。24時間の遅延は、高度なソーシャルエンジニアリング攻撃を抑止するためです。 Googleは非Playソースからマルウェアに遭遇する確率が約50倍高いと引用し、このプログラムでそのリスクを低減すると主張しています。検証機能はすでにAndroid 16.1(2025年後半にリリース)に組み込まれており、全てのサポート対象デバイスで利用可能です。実施開始はブラジル、シンガポール、インドネシア、タイで2025年9月から行われ、2026年には世界中へ展開されます。 このプログラムは手数料などのハードルを追加するため、制裁対象国の開発者にとって不利になる可能性がありますが、Googleはその方針がそのような開発者を排除することを意図していないとし、検証済み開発者リストを非永続化に保ち法的課題を回避すると述べています。プライバシー擁護派は検証済み開発者のデータベースについて懸念を示し続けています。

マインクラフトのソースコードは興味深いです。 | そっか~ニュース