リーダーボードが欲しいよ
ゲームは個人で遊ぶのも面白いのですが、プレイヤーどうしの交流要素があると盛り上がったりしますよね。直接の対戦でなくとも、スコアや達成度を競ったりする仕組みも考えられます。
それで、GitHubのIssueを漁っていたところ、リーダーボード(プレイヤーのスコアランキング)のAPIが欲しいよ、というIssueがありました。
WASM-4の作者のブルーノ兄貴は、「個人的には、興味があるのはレトロなコンピューターゲームなんだ。ソーシャルな機能は別に反対じゃないけど、もっといろんなゲームが集まってからかな……」みたいな感じでした。まあそういうことなので公式APIは当分実装されないでしょうが、いろいろ抜け道はありそうです。
WASM-4のセーブデータを外部から読み取る
WASM-4にはバンドルしたときのHTMLをカスタムする方法が用意されています。w4 bundle
コマンドに--html-template
オプションでテンプレートファイルを渡せばいいようです。
これを利用して、HTML側にネットワークアクセスのコードを仕込みます。ゲームのセーブデータはローカルストレージに書き込まれますので、これを読み取れば現在のハイスコアなどは外部から読み取ることができます。
HTML自体にもSNSログインの機能を付けて、プレイヤーを認証できるようにしたいと思います。これでツイッターのあの人がどこまでプレイしたかとかわかるようになります。
ローカルストレージは文字列しか格納できませんが、WASM-4ランタイムのコードを見ると、z85というフォーマットでバイナリ列を文字列に変換してからローカルストレージに格納しているようです。Z85というのがどのような形式なのかはよく知らないのですが、WASM-4のランタイムからデコーダーのコードをコピーしてきて、デコードしたらDataView
でデータを読み取ります。
<script async>
// function decode (string: string, dest: number[] | Uint8Array | Uint8ClampedArray): number
function decode (string, dest) {
const DECODER = [
0x00, 0x44, 0x00, 0x54, 0x53, 0x52, 0x48, 0x00,
0x4B, 0x4C, 0x46, 0x41, 0x00, 0x3F, 0x3E, 0x45,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x40, 0x00, 0x49, 0x42, 0x4A, 0x47,
0x51, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
0x3B, 0x3C, 0x3D, 0x4D, 0x00, 0x4E, 0x43, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
0x21, 0x22, 0x23, 0x4F, 0x00, 0x50, 0x00, 0x00
];
let byte_nbr = 0,
char_nbr = 0,
value = 0;
const string_len = string.length,
dest_len = dest.length;
if ((string.length % 5) == 0) {
while (char_nbr < string_len) {
const idx = string.charCodeAt(char_nbr++) - 32;
if ((idx < 0) || (idx >= DECODER.length)) {
return byte_nbr;
}
value = (value * 85) + DECODER[idx];
if ((char_nbr % 5) == 0) {
let divisor = 256 * 256 * 256;
while (divisor >= 1) {
if (byte_nbr >= dest_len) {
return byte_nbr;
}
dest[byte_nbr++] = (value / divisor) % 256;
divisor /= 256;
}
value = 0;
}
}
}
return byte_nbr;
}
setInterval(() => {
const str = localStorage.getItem("Tower Climber-disk");
const array = new Uint8Array(1000);
const byte_nbr = decode(str, array)
const view = new DataView(array.buffer)
const version = view.getUint8(0);
const x = view.getFloat32(1, true);
const y = view.getFloat32(5, true);
console.log(version, x, y);
}, 1000)
</script>
ちなみに、エンディアンに注意です。WASMではリトルエンディアンなのですが、JavaScriptのDataView
ではビッグエンディアンがデフォルトです。クソっ!
なお、ローカルストレージに書き込まれるときのキーは、デフォルトではゲーム名-disk
になるようです。これも、--html-disk-prefix
というオプションを指定することで
これでセーブデータを外部から読み取ることができました。あとはこれを適当なデータベースに書き込めばOKです。
参考文献
次回予告
次回はマルチプレイヤー機能を試してみます。こんなに簡単にマルチプレイヤーゲームになるなんて驚きです。