3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Deno DeployとWebSocketを使ったオンライン四目並べゲームの作成

Posted at

なぜオンライン四目並べか

Deno DeployについてLTする機会があったので
以前作ったChatGPT DALL-E3で美少女四目並べ(note記事)を元に
Deno Deploy+Websocketでオンライン四目並べができるゲームを作成しました。

できたもの

YonmokuOnlineQiita.gif

概要

プロジェクトの目的: このプロジェクトは、DenoとWebSocketを用いたリアルタイムWebアプリケーションの開発過程とその技術的な側面を理解することを目的としています。

アプリケーションの概要: 開発されたアプリケーションは、オンラインでプレイできる四目並べゲームです。プレイヤーはリアルタイムで駒を配置し、ゲームの進行を他のプレイヤーと共有します。

技術スタック: 主な技術として、フロントエンドにはJavaScriptとHTML5 Canvasを、バックエンドにはDenoとWebSocketプロトコルを使用しています。また、データ管理とリアルタイム通信にはSupabaseを活用しています。

ポイント: リアルタイムWebアプリケーションの開発には、ユーザー間のインタラクションの即時性とデータの同期が重要です。この記事では、これらの要素を如何にして実現しているかに焦点を当てます。

使用したライブラリ

Deno

  • dotenv
  • Oak (Webミドルウェアフレームワーク)
  • supabase-js
  • dejs (テンプレートエンジン)

JavaScript

 未使用

Deno Deployについて

Deno Deployは、Deno(TypeScript)で書かれたWEBアプリケーションをデプロイするためのサービスです。このサービスは、無料で1日最大10万リクエストまで利用可能で、HTTPSとカスタムドメインにも対応しています。
Deno Deployにはドメイン名の変更機能やカスタムドメインへの変更機能があり、SSL証明書の自動設定も無料で提供されています​​​​​​。便利機能としては、リアルタイムログの閲覧、サーバーレスタイプでの動作、リリースリビジョンの保持などがあります​
https://qiita.com/AKB428/items/ebebed20dbebd2b9ab78

WebSocket通信の詳細

websocket通知されるアクション

  • キャラクター選択 (キャラクターの選択は対戦ゲーム雰囲気の演出のみ)
  • ゲームの開始 (2名揃ったらサーバーからPush)
  • ケーム中 駒をおく(自分の駒を送信、相手の駒を受信)
  • ゲーム終了

スクリーンショット 2023-12-19 13.19.20.png

クライアント側の処理

ウェブソケットの接続は、JavaScriptを使用して WebSocket オブジェクトを作成することで開始されます。
接続が開始されると、クライアントはサーバーにメッセージを送信できます。
クライアントはサーバーからのメッセージをリアルタイムで受信し、適切に処理します。

サーバー側の処理

サーバーは、DenoのWebSocket APIを利用してクライアントからの接続を受け入れます。
クライアントからのデータを受信した際には、適切な応答や処理を行い、必要に応じてクライアントにデータを送信します。
ゲームの状態更新やプレイヤー間のアクションをリアルタイムで処理し、反映させます。

通信の流れ

クライアントが行動(例: 石を置く、キャラクターを選択する)を行う。
行動データはWebSocketを介してサーバーに送信されます。
サーバーはこのデータを処理し、ゲームの状態を更新。
更新されたゲーム状態は、同じ部屋にいる他のクライアントにブロードキャストされます。

Deno&Deno Deployは標準でWebSocketに対応している

スクリーンショット 2023-12-19 12.22.49.png

https://deno.com/blog/deploy-streams
https://developer.mozilla.org/ja/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_JavaScript_Deno

websocket クライアント側

game.js
const hostname = window.location.hostname;
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${hostname}/ws?roomId=${roomInfo.room_id}`;
console.log(wsUrl);
const socket = new WebSocket(wsUrl);

socket.onopen = function (e) {
    console.log("[open] Connection established");
};

socket.onmessage = function (event) {
    const data = JSON.parse(event.data);
    console.log(data);

websocketサーバー側(deno oakを使用)

server.ts
async function handleWebSocket(ctx: Context) {
    const sock = await ctx.upgrade();
    sockets.add(sock);
    const roomId = ctx.request.url.searchParams.get('roomId') || 'defaultRoom';

    if (!socketsOfRoom[roomId]) {
        socketsOfRoom[roomId] = [];
    }
    socketsOfRoom[roomId].push(sock);
    //console.log(socketsOfRoom);

    sock.onopen = () => {
        console.log("WebSocket opened");
        //sock.send(JSON.stringify({ role: role }));
    }

    sock.onclose = () => {

ルームの概念とWebSocketの制御

3者のブラウザは同一ルーム[akb73]にいる

スクリーンショット 2023-12-19 1.11.18.png

四目オンラインには複数のルーム(=ゲームボード)が存在する

Untitled diagram-2023-12-18-175104.png

ルームAの更新をルームBのプレイヤーに伝えてはならない
ルーム概念-2023-12-22-133600.png

同一ルームの情報しかブロードキャストしないようにする制御が必要

四目ルームごとのブロードキャスト-2023-12-18-162232.png

(解決方法)websocket受信時にsocketをハッシュマップで持つ(Deno)

server.ts
async function handleWebSocket(ctx: Context) {
    const sock = await ctx.upgrade();
    sockets.add(sock);
    const roomId = ctx.request.url.searchParams.get('roomId') || 'defaultRoom';

    if (!socketsOfRoom[roomId]) {
        socketsOfRoom[roomId] = [];
    }
    socketsOfRoom[roomId].push(sock);
    //console.log(socketsOfRoom);

    sock.onopen = () => {
        console.log("WebSocket opened");
        //sock.send(JSON.stringify({ role: role }));
    }

特定のルームへブロードキャスト(Deno)

server.ts
function broadcastToRoom(roomId: string, message: string) {
    const socketsInRoom = socketsOfRoom[roomId];
    if (socketsInRoom) {
        for (const socket of socketsInRoom) {
            if (socket.readyState === WebSocket.OPEN) {
                socket.send(message);
            }
        }
    }
}

完成したルーム制御図
四目ルーム通知-2023-12-19-041501.png

同一の部屋のクライアントがDeno Deployのリージョンをまたがる問題

yonmokuRegion違い-2023-12-22-130811.png

Player1が先手、Playser2が後手、Player3が試合閲覧者の場合に
全員が同一ルームにいてもPlayer1~3がどこのリージョンのどのマシンにいるかがわからないので各々のサーバーがもっているWebsocketを同期できない。

Deno Deployではリージョン固定を制御できない

スクリーンショット 2023-12-10 22.48.35.png

Supabase側でボードの駒の動き等を集約してポーリングする

Supabaseのリアルタイム機能ではテーブルの更新をキャッチしてサーバ側に通知してくれることができる。そのためDenoDeployのどのリージョンやどのマシンインスタンスの更新でもSupabase側でキャッチできるのでSupabaseに接続しているDenoDeployサーバ群は共通の更新情報をうけとることができる!🥰🥰🥰🥰

yonmoku1-2023-12-18-160414.png

Supabaseのテーブル設定でリアルタイム機能をONにする

スクリーンショット 2023-12-12 0.20.03.png

supabaseのリアルタイム監視機能を使う(deno実装)

server.ts
// 特定のルームIDのgame_statesテーブルの変更を購読
//https://supabase.com/docs/guides/realtime/postgres-changes
supabase
    .channel('game_states_channel')
    .on("postgres_changes",
        {
            event: "INSERT",
            schema: "public",
            table: `game_states`, // DBのテーブル名
            // filter: `room_id=eq.${roomId}` 
        },
        (payload) => {
            //console.log('データが変更されました:', payload);
            const roomId = payload.new.room_id;
            const player_id = parseInt(payload.new.player_id);
            const placeStone = payload.new.action.placeStone;
            const playerColor = PlayerColor[player_id];
            const message = JSON.stringify({ action: WsSendAction.PlaceStone, playerId: player_id, playerColor: playerColor, placeStoneXY: placeStone });
            console.log(`broadcastToRoom ${roomId} ${message}`);
            broadcastToRoom(roomId, message);
        })
    .subscribe();

  • eventに*をつけるとすべてのイベントをキャッチできます
  • filterを使うと特定の条件の行のみ絞れます
  • テーブルの更新を一括で受信するのでサーバー側がどれだけ負荷に耐えられるかは不明、場合によってはメッセージキューシステムを別に挟む必要があるかも

テストプレイ

サンドボックスとしてルームakb21-50を開放

  • スマートフォン表示でやってください
  • デフォルトキャラクター以外を選択しないとバグります
  • 先手と後手がブッキングしないように2名でやるときはP1かP2をきめておいてください
  • なんかうまく通信できなかったらリロードでなんとかなります
  • 1名で2役やるときはブラウザはわけてください(cookie制御があるため)

https://yonmoku.deno.dev/room/akb21
https://yonmoku.deno.dev/room/akb50

その他

  • ローカル開発ではMacのセキュリティの制限なのかport80で起動しないとwebsocketがうまく通信できなかった(開発環境のデフォは8080など)
  • ChatGPT4を使いながらコーディングしたがDenoの環境周り、supabaseのリアルタイム周りの情報は古すぎて役に立たなかった。

終わりに

Deno Deployでルームに対応したオンラインボードゲームの作成方法を紹介しました。
とりあえずこの方法でなんとかなると思います。
Deno DeployもSupabaseも一定量は無料なので、Deno Deployに興味ある人は2つを組み合わせてオンライン将棋やオンライン麻雀を作ってみてはどうでしょうか。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?