ゲーム

11年間にわたるリバースエンジニアリングでDOS版「ウイングコマンダー」の2人同時協力プレイ実現に成功


子どもの頃にDOS版「ウイングコマンダー」の協力プレイを楽しんでいた兄弟が、11年もの間リバースエンジニアリングを続け、ついに夢に見た「僚機との協力プレイ」を実現しました。単一のコックピットを共有するのではなくそれぞれの機体を駆って協力プレイしたいという子どもの頃の夢から始まり、ゲームのメモリ・コード・ネットワークプロトコルの深淵を探求するなど、数年間に及ぶ複雑な技術的努力へとプロジェクトは発展しました。

Wing Commander Together
https://fireflyk64.github.io/wcmulti/

プロジェクトの最初の着想は2015年10月4日、ダニエル・ホーン氏とパトリック・ホーン氏の兄弟が「ウイングコマンダー」のメモリフットプリント全体がDOSで使用可能な640KBのRAMに収まる可能性に気づいたことから始まりました。中心となるアイデアは、ゲームのメモリから自機と僚機の座標を抽出し、ネットワークパケットを使用して僚機を制御することでした。メモリをダンプして、自機と僚機のXYZ座標が格納されている場所を特定できれば、ネットワークパケットで僚機を制御できるはずです。


兄弟はDOSBoxデバッガーを使用した体系的なアプローチを採用しました。プレイヤーの宇宙空間の空き領域での移動の前後にメモリのスナップショットを取得し、両者を比較することで変更されたメモリ領域、すなわち空間座標のデータが格納されたメモリ領域を特定することができました。


この手法はチェイスカメラビューと相互参照することによりブラッシュアップされ、最終的に船の位置を13D3:A9C2、速度をCAE2で特定するに至りました。


さらに詳しく調べることで、方向ベクトルとそのメモリ位置、例えば右はAEB6、上はB1B6、前はB4B6であること、ゲームがエンティティプロパティのために64個のスロットを使用しており、カメラが0x3D、一時変数が0x3Fに配置されていることがわかりました。


メモリ領域が近接していたことにより、回転に関連する単一バイトの変更を特定することが可能になりました。以下は水平飛行からのヨーイングでforward[0]が9バイト変化し、up[0]が00 01から7E 00に変化した様子です。


プロジェクトが進むにつれて、単純なメモリ書き込みだけでは武器の発射のようなアクションには不十分であることが明らかになり、ゲーム自身の関数を「乗っ取る」必要が出てきました。これが、特にゲームが動的にロードするオーバーレイを使用していることを考慮して、正しい引数で関数を呼び出すための「トランポリンシステム」の開発につながりました。「Loading WING COMMANDER」という「デッドストリング」を13D3:0187のトランポリンのプレースホルダーとして使用することでブレークスルーが達成されました。デッドストリングを命令で上書きすることにより、ゲームにfireAllGuns()といった特定の関数を呼び出させることができるようになりました。

00                          terminates any string pointing here.
pushf; pusha                the game is mid-frame; leave registers as we found them
xor si,si; push si          argument: ship 0. Later thunks use mov si,imm16 for each argument
call far 12D7:0156          through the stub, so it works wherever the overlay is loaded
pop si; popa; popf          cdecl: caller cleans the arguments
retf                        back to the CS:IP DOSBox pushed

この手法によりレーザー砲の起動が可能になり、最終的にはより複雑なアクションの実装につながりました。


2017年8月に、シンプルなTCPクライアント/サーバーアーキテクチャと protobufメッセージを使用したネットワークプロトコルが作成されました。各フレームでサーバーは全船の位置・発砲イベント・消滅・ダメージ情報をクライアントに送信し、逆にクライアントはレーザーが発射された場合、または重要なステータス更新(射出・着陸・死亡)が発生した場合に、自身の位置を返信送信しました。


大きな問題となったのは、特に隕石やレーザー弾のような一時的なオブジェクトにおいて、サーバーとクライアント間で一貫したエンティティスロットを確保することでした。チームはサーバーの船IDをローカルIDに変換するためのマッピングシステムを実装し、以前は隕石がマルチプレイヤーで正しく機能しなかった問題を解決しました。


2019年6月、ミッションの勝利計算が不安定で扱いにくいことが発覚したため、実際の勝利ポイントの集計場所を見つけるために二分探索を行うことにしました。64Kバイトのデータセグメント全体をネットワーク経由でコピーし、徐々に半分に分割していきました。何度も二分探索を繰り返した結果、ミッションツリーが格納されている80バイトの場所を13D3:D248に特定しました。そして10月には全体の勝利ポイントカウンターが13D3:C280、ミッション番号をそれぞれ13D3:C255とC256で見つけられたため、ドロップダウンリストからプレイするミッションを選択できるようになりました。


2020年初頭、通信テキストを再生する機能を特定できたことにより、ネットワーク経由でチャットを送信し、入力されたチャットをシステムに直接組み込んで、僚機のVDU(ディスプレイ)で再生することができるようになりました。ゲーム内の激しい戦闘シーンを彷彿とさせる、非常に没入感のあるチャット方法を実現しました。


2020年に最終調整を行いゲームはプレイ可能な状態になったものの、友人たちと実際に楽しむ方法についてはアイデアが尽きてしまっており、ゲームはGitHub上に放置されたまましばらく放置されていたそうです。


しかし2026年、WebRTC技術の普及と「lobbylink」という新しいシステムを開発したことでゲームは復活を果たしました。LobbylinkはWebSocketライブラリのみに依存するシンプルなGo言語のロビーシステムであり、ピアツーピアのWebRTC接続をオーケストレーションします。


Lobbylinkはあらゆる小規模サーバーで動作し、WebRTCと共有ルームコードを使用してプレイヤー同士が簡単に接続できるようにします。サーバーには静的な発信元許可リストがあり、ほぼすべての言語のブラウザまたはクライアントソフトウェアがルームコードを使用して接続できます。


さらに追加機能としてLLMに依頼し、2番目のWebRTCピア接続を介したボイスチャットが統合されたことにより、ゲームプレイ中のリアルタイム音声通信が可能になりました。この最後の実装により、ついにプレイヤーは「一緒に飛んで一緒に笑う」ことができるようになったとのことです。

実際に「ウイングコマンダー」をオンラインで2人協力プレイする様子は以下の動画で、およそ23分55秒頃から見ることができます。

Wing Commander Co-Op: Reverse Engineering x86 Assembly and Memory to Add Multiplayer. - YouTube

・関連記事
Windows 11で動作する最古のGUIアプリケーションは? - GIGAZINE

アメリカ陸軍の大隊が再入隊者募集のため「GTA6を遊ぶための休暇」を提供 - GIGAZINE

「模倣こそがデザイン」と述べるデザイナーが示すデザインや設計とコピーの歴史 - GIGAZINE

コントローラーで「DOOM」や「プリンス・オブ・ペルシャ」などのレトロゲームをブラウザ上で遊べるプラットフォーム「DOS_deck」で実際にゲームをプレイしてみた - GIGAZINE

FPSの金字塔「DOOM」の生みの親であるジョン・ロメロ氏が「1990年代におけるインディーゲームの開発はどうだったのか」を語る - GIGAZINE

Chromeの新API「WebGPU」ではどれだけパフォーマンスが改善されるのか? - GIGAZINE

軍艦からレーザービームでドローンを撃墜するテストに成功したとアメリカ海軍が発表 - GIGAZINE

in AI,   動画,   ソフトウェア,   ゲーム, Posted by log1c_sh

You can read the machine translated English article After 11 years of reverse engineering, w….