SQLDoom: SQLデータベース内に完全にオリジナル1993年版Doomエンジンを実行する

TL;DR

SQLDoomは、CedarDB上でSQLクエリのみを使用して、オリジナル1993年版Doomのゲームロジックとレンダラーを完全に実行します。ゲームループは従来の35 Hzのtickレートを維持し、最大60 FPSで320×200のフレームバッファを出力します。入力、タイミング、ビットマップ表示は薄いPythonクライアントが担当し、同じエンジンで4人対戦のデスマッチサーバーも動作します。


SQLDoomとは

SQLDoomは、Doomのシェアウェアエピソード1を忠実に移植したもので、すべてのゲームロジック、状態、レンダリングをCedarDBデータベースエンジンによって実行されるSQLステートメントに移行しています。外部コードは最小限のPythonスクリプトのみで、以下の機能を実行します:

  1. pygameを介してキーボード/マウス入力を読み取ります。
  2. 1/35 秒ごとに SELECT doom_run_game_tic(...) を呼び出して世界を進捗させます。
  3. 単一のSQLクエリで新しいフレームを要求し、返されたビットマップを描画します。

それ以外のすべて——モンスターAI、プレイヤー移動、武器処理、BSP走査、壁/視平面のラスタライズ——はテーブル、ストアドプロシージャ、CTEを多用したクエリの中に存在します。


アーキテクチャ概要

このシステムは2つの独立したパイプラインに分かれています:

  • ゲームロジックパイプライン – 固定の35 Hzで実行され、関係型ゲームステートテーブルを更新し、更新された状態以外は何も返しません。
  • レンダラパイプライン – 純関数型のSQLクエリで、現在の状態テーブルを読み取り、各ピクセルの (x, y, rgb) を含む64 000行の結果を出力します。
Python (pygame) ↔️ SQLロジックテーブル ↔️ SQLレンダラ → ビットマップ → 表示

この分離により、クライアントはフレームを希望するだけの速度で要求できます(作者は最大60 FPSを報告)が、ロジックは元の28.6 ms/ティックの時間予算に固定されています。


Doomデータを関係モデルにロードする

DoomのWADフォーマットはすでに関係型テーブルにきれいにマッピングされています:

  • VERTEXES → vertexes
  • LINEDEFS → linedefs
  • SIDEDEFS → sidedefs
  • SECTORS → sectors
  • THINGS → things

1000行程度のPythonインポーターがWADを解析し、ラップトップ上で約18秒で約110テーブルを埋めます。関係型スキーマにより、レベルのジオメトリを直接クエリ可能であり、ブログ記事に含まれる鳥瞰図のASCIIレンダリングクエリでその例が示されています。


ゲームティックの実装

1つのストアドプロシージャ doom_run_game_tic が、すべてのティックをカプセル化しています。内部では、CedarScript(PL/pgSQLに似た言語)が、このティックで実行すべきサブシステムを決定するビットマスク計画を構築します。その後、以下の関数の連鎖を呼び出します:

  • doom_cs_move – プレイヤーおよびモンスターの移動
  • doom_cs_doors – ドアの開閉
  • doom_cs_weapon – 武器の状態、ヒットスキャン、ダメージ
  • doom_cs_monsters – モンスターAIステートマシン
  • doom_cs_sector_fx – セクター効果(床、天井)

モンスターAIの例では、再帰的CTEが視界、距離を計算し、CASE式で次のステートを決定し、最後に1つのUPDATEでmonster_aiを更新しています。

パフォーマンス: 最悪ケース(E4M1、46体の目覚めたモンスター)では10.45 ms(28.6 ms予算の約37%)。通常のティック(6体のモンスター)では平均2.15 ms(約8%)。全体のロジックは約5 900行のSQLで構成され、オリジナルのCソースの約9 000行より少ないです。


SQLでのレンダラパイプライン

89個のCTEを含む単一のSQLクエリが、完全なフレームバッファを生成します。パイプラインはDoomの古典的な手順を模倣しています:

  1. BSP走査 – 事前に計算された node_path_steps 物理ビューが、各ルートからサブセクターへのパスを40ビットの sort_key としてエンコードします。このキーで並べ替えることで、再帰なしに前から後へのサブセクター順序が得られます。
  2. 壁のラスタライズ – generate_series が各壁セグメントを画面列に展開し、その後ピクセルごとの断片に変換します。
  3. 視平面(床/天井)の塗りつぶし – ウィンドウ関数が各列の残りのクリッピングされていないバンドを計算し、天井/床のスパンを出力します。
  4. スプライトのレンダリング – ビルボード用の類似した断片生成。
  5. 深度解決 – すべての候補断片を64ビットの winner_key にパックし、最上位ビットが深度、表面優先度、安定した引き分け回避をエンコードします。各ピクセルごとに MIN(winner_key) が可視断片を選択します。

時間分解:

  • 壁のラスタライズ – 約1.7 ms
  • 視平面の塗りつぶし – 約3 ms
  • 深度解決およびピクセル単位の処理 – 約8.2 ms(最大コスト)
  • 合計平均フレーム – 約16 ms、Ryzen 7 7840Uで約60 FPSを達成;混雑したシーンでは元の35 FPSに低下します。

データベースによるマルチプレイヤー

世界全体がトランザクショナルデータベースに存在するため、マルチプレイヤーは本質的に無料です:

  • 単一の「レフェリー」プロセスがすべてのプレイヤーに35 Hzのクロックを供給します。
  • プレイヤー入力は SECURITY DEFINER 関数 api_input を通して送信され、値が制限され、mp_inputs に行が挿入されます。
  • 各ティックは BEGIN … COMMIT ブロック内で実行され、すべてのクライアントがティック前またはティック後のスナップショットのいずれかを必ず見ることを保証します——部分的な更新はありません。
  • ロールベースのアクセス制御により、プレイヤーはAPI関数のみにアクセスでき、任意のテーブル操作を防ぎます。

公開サーバーでは、1クライアントあたり3コアの環境で4人対戦のデスマッチを安定した35 FPSで実行しています。16コアのマシンでは追加のマッチを快適にホストできます。


データベースが良い選択肢となる理由

  • データ中心の設計 – 武器、モンスター、アニメーションフレームはすべてテーブルの行として扱われ、検査やリアルタイムの調整が簡単です。
  • 原子性と整合性 – 各ティックがトランザクションとして扱われ、従来の非同期バグが排除されます。
  • アクセス制御 – 細かい権限により、プレイヤーに意図したAPIのみを公開できます。
  • ECSのマッピング – エンティティは行、コンポーネントはテーブル、システムは集合ベースの UPDATE/INSERT ステートメントにマッピングされ、データベースの並列実行エンジンを活用できます。

コンパイルオーバーヘッド

CedarDBは複雑なクエリを実行前にLLVM IRにコンパイルします。並列比較のアセンブリコードでは、SQL生成コードは48指令に対して117指令を使用しており、追加の指令はテーブルへの結果書き込みに起因しています。複数の抽象レイヤーがあることを考慮すると、このギャップは小さいです。


コミュニティの反応(Hacker Newsのハイライト)

"SQLでかなり複雑なゲームロジックを表現するのは、意外と簡単だと驚きました。ゲームロジックはたったの~5900行のSQLです。" – bob1029

"元のCより少ない行数で、クエリプランナーをステートマシンとして悪用しているのは、極端なエンジニアリングの不正行為です。大好きです。" – soltanov

"ゲーム状態がSQLテーブルにあるという事実が、1年間の最適化の記憶を呼び起こしました…原子性の保証が一貫した状態を保証します…アクションゲームでその読み書きループを実行する?純粋な愚行ですが、私にとってはとても面白いです。" – noduerme

これらのコメントは、SQLの表現力の驚き、クエリプランナーをステートマシンとして使うエレガンス、リアルタイムアクションゲームにおけるスケーラビリティのトレードオフについての理解を強調しています。


開始方法

  1. CedarDB Community Edition をインストールします。
  2. Pythonの依存関係をインストール:psycopg2 と pygame。
  3. シェアウェア版 doom1.wad を入手します(例:apt install doom-wad-shareware)。
  4. リポジトリ https://github.com/cedardb/sqldoom をクローンし、READMEに従ってWADをロードし、Pythonドライバを起動します。

あるいは、元のブログ記事にリンクされたEUまたはUSサーバーで公開マッチに参加できます。


まとめ

SQLDoomは、関係型データベースがフル機能のリアルタイム3Dエンジンをホストできることを示しています:ゲームロジックは集合ベースの更新に自然に適合し、BSP駆動のレンダラはCTEとウィンドウ関数で表現可能であり、トランザクショナル保証によりマルチプレイヤーが簡単になります。データベースはプロダクションゲームの実用的なグラフィックスエンジンではありませんが、SQLが表現できる範囲の限界を押し広げる、説得力のある概念実証です。

Sources

関連

  • プロジェクト
  • プロジェクト
  • プロジェクト
  • Dispatch
  • プロジェクト