セルフホスト
joinVoiceRoom() には小さな Cloudflare Worker が必要です。自分のアカウントにデプロイし、費用も自分で負担します。私たちは何もホストしません。Worker はプレイヤー間の接続メッセージを中継し、人数の上限を守らせ、TURN の認証情報を発行します。音声はプレイヤー間か TURN を通り、Worker は通りません。
デプロイ
Cloudflare アカウントが必要です。
templates/workerをプロジェクトにコピーし、そこでnpm installを実行します。- Cloudflare のダッシュボードの Realtime で TURN キーを作成します。キー ID と API トークンが発行されます。
wrangler.jsoncのALLOWED_ORIGINSにゲームのアドレスを設定します。- シークレットを追加してデプロイします。
npx wrangler secret put TURN_KEY_IDnpx wrangler secret put TURN_KEY_API_TOKENnpx wrangler deploy- Worker のアドレスを
wss://earshot-voice.<your-subdomain>.workers.devの形でゲームに渡します。
2 つの異なるネットワークからテストしてください。Cloudflare で使用量のアラートも設定してください。
シークレットが 2 つそろっていないと、Worker は公開 STUN サーバーだけを提供します。このとき ipPolicy: "relay" は no-turn-server で失敗します。"direct" は、ネットワークが許す範囲でプレイヤーをつなげます。
設定
wrangler.jsonc の vars です。値はすべて文字列です。
| 変数 | 意味 | テンプレートの値 |
|---|---|---|
ALLOWED_ORIGINS | 参加を許可するオリジン(カンマ区切り)。例:https://mygame.example | http://localhost:4710 |
MAX_PLAYERS | ルームあたりの人数。これを超えると room-full になります | 8 |
TURN_TTL_SECONDS | TURN 認証情報の有効期間 | 14400 |
オリジンは、スキーム、ホスト、ポートまで完全に一致する必要があり、末尾にスラッシュはつけません。開発中はローカルと本番のオリジンを並べて書いてください。
各ルームを保持する Durable Object のバインディングとマイグレーションは、すでにファイルに入っています。
canJoin
公開する前に、src/index.js の canJoin を編集してください。テンプレートのままでは、ルームコードを知っていれば誰でも参加でき、BAN や年齢については何もしません。false を返すと接続を拒否します。
async function canJoin(request, env, { room, peer }) {
return isLoggedIn(request.headers.get("Cookie"), env); // 自分のログイン確認。
}isLoggedIn は自分で書くコードの例です。接続 URL は joinVoiceRoom() が組み立てるので、必要な情報はリクエストのヘッダーから読んでください。拒否されたプレイヤーには connection-failed が返ります。
- オリジンの確認で防げるのは他の Web サイトで、独自のクライアントは防げません。その気になれば、開いた Worker から TURN の認証情報を取得できます。費用が重要ならログインを必須にしてください。
- 知らない人同士のマッチングはありません。ルームコードはフレンド向けです。モデレーションができないなら、そのままにしてください。
- プライバシーの告知と、プレイヤーのいる地域の子どもやオンラインの安全に関する法律は、開発者の責任です。これは法的助言ではありません。詳しくはボイスを見てください。
費用
費用がかかるのはリレーの設定です。これは最悪の場合の計算で、全員がリレーを使い、話し続けるとします。パケットのオーバーヘッドを含む 32 kbps の Opus で、1 ストリームあたり 1 時間に約 24 MB です。Cloudflare は TURN の送信トラフィックに対し、毎月 1,000 GB の無料枠を超えた分へ 1 GB あたり $0.05 を課金します(料金、2026-11-04 時点)。頼る前に最新の料金を確認してください。全員が全員に接続するので、n 人のルームには n x (n - 1) 本のストリームが流れます。4 人で 12 本、8 人で 56 本です。
| ルーム(全員リレー) | 1 時間あたりの GB | 無料枠を超えた後の 1 時間あたりの費用 | 無料枠で使えるルーム時間 |
|---|---|---|---|
| 4 人 | 0.29 | $0.014 | 約 3,470 |
| 8 人 | 1.34 | $0.067 | 約 740 |
実際のルームの費用はこれより少なくなります。十分に離れたプレイヤーへの送信は止まり、プッシュトゥトークはキーを離している間は無音を送るからです。無音はデータ量が少ないものの、トラフィックにはなります。この表は TURN だけの費用です。ルームサーバーの Workers と Durable Objects の使用量は別に課金されます。
トラブルシューティング
joined.error | 確認すること |
|---|---|
connection-failed | URL、次にページの正確なオリジンと ALLOWED_ORIGINS、その次に canJoin。createRoomCode() のコードは必ず Worker の形式チェックを通ります。自作のコードは英数字、-、_ で 16〜64 文字にしてください |
no-turn-server | シークレットが 2 つとも設定されていることを確認し、再度デプロイします |
room-full | MAX_PLAYERS |