今回使うコード一覧
deno.js
JavaScript
let users = new Map();
let opponent = null;
Deno.serve({
port: 3000,
handler: async request => {
const { socket, response } = Deno.upgradeWebSocket(request);
const sendToClient = (target, key, value) => {
const data = {};
data[key] = value;
target.send(JSON.stringify(data));
}
const setUser = () => {
const n = users.size;
if (n % 2 === 0) {
opponent = socket;
users.set(opponent, null);
} else {
users.set(socket, opponent);
users.set(opponent, socket);
sendToClient(socket, 'matched', new Date());
sendToClient(opponent, 'matched', new Date());
}
console.log(n);
}
socket.onopen = () => {
setUser();
};
socket.onmessage = e => {
};
socket.onclose = () => {
const opponent = users.get(socket);
console.log(opponent);
if (opponent !== undefined && opponent !== null) {
console.log('ok');
sendToClient(opponent, 'connection-lost', new Date());
}
users.delete(socket); //切断されたユーザを消す
users.delete(opponent); //切断されたユーザの対戦相手を消す
};
socket.onerror = error => console.error("ERROR:", error);
return response;
},
});
index.html
HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>sample</title>
</head>
<body>
<p id="loading">対戦相手を探しています...</p>
<script>
const wsUri = "ws://localhost:3000/";
const socket = new WebSocket(wsUri);
const switchData = (data) => {
data = JSON.parse(data);
const key = Object.keys(data).toString();
const value = Object.values(data).toString();
switch (key) {
case 'matched':
document.getElementById('loading').innerText = `接続完了: ${value}`;
break;
case 'connection-lost':
document.getElementById('loading').innerText = '対戦相手の接続が切れました';
}
}
socket.onopen = (e) => {
console.log("CONNECTED");
};
socket.onclose = (e) => {
console.log("DISCONNECTED");
};
socket.onmessage = (e) => {
console.log(e.data);
switchData(e.data);
};
socket.onerror = (e) => {
console.log(`ERROR: ${e.data}`);
};
</script>
</body>
</html>
Mapで二人対戦のマッチングシステムを作る
usersというmapにユーザの情報を入れていきます。
今回はユーザの情報をsocketと表します。
usersの要素数を2で割ったときの余りが0の時はopponentという変数にsocketの情報を代入します。
opponentは一時的にsocketの情報を保存しておくための変数です。
if文のelseの部分でマッチングしたことを表しています。
mapのkeyに自分のsocketが入り、valueに対戦相手のsocketが入っている状況を作ります。
これで対戦相手の情報をmapに入れることができました。
JavaScript
const n = users.size;
if (n % 2 === 0) {
opponent = socket;
users.set(opponent, null);
} else {
users.set(socket, opponent);
users.set(opponent, socket);
}
マッチングが完了したという情報を自分と対戦相手に送信します。
JavaScript
sendToClient(socket, 'matched', new Date());
sendToClient(opponent, 'matched', new Date());
対戦相手の接続が切れた時
接続が切れたユーザのペアをmapから消します。
JavaScript
users.delete(socket); //切断されたユーザを消す
users.delete(opponent); //切断されたユーザの対戦相手を消す
2人対戦型のシステムで、どちらかの接続が切れた時、対戦相手の接続が切れた方のユーザへ通知を送信します。
対戦相手の接続が切れた状況でブラウザのタブをリロードしてしまうと、サーバ側でエラーが出て止まってしまいます。これは、マッチングしていないのに接続が切れるという状況だからです。
これを解決するために、下記のようなif文を使います。
JavaScript
const opponent = users.get(socket);
if (opponent !== undefined && opponent !== null) {
console.log('ok');
sendToClient(opponent, 'connection-lost', new Date());
}
まとめ
mapに対戦相手の情報を入れる。
マッチングされていない状況でのリロードに気をつける。