ASP.NET Core版 対人対戦できるぷよぷよをつくる(9)の続きです。ときどきゲーム中に通信が切れてしまうことがあります。これまでは自分で通信を切断した場合と同じ処理(試合放棄)をしていましたが、意図せず切れてしまった場合は再接続してゲームが途中で終わらないようにします。
Contents
クライアントサイドにおける処理
通信が切れるとこれまで以下のような処理をしていました。
JavaScript
1 2 3 4 5 6 |
connection.onclose(async () => { errer.style.display = 'block'; conectResult.innerText = "エラー:通信が切断されました"; isPlaying = false; StopBgm(); }); |
ここを再接続する処理に置き換えればよいです。0.5秒後、1秒後、2秒後に再接続を試みます。3秒待っても再接続が完了しない場合はあきらめます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
let isReconnected = false; connection.onclose(async () => { errer.style.display = 'block'; conectResult.innerText = "エラー:通信が切断されました"; // isPlaying = false; // StopBgm(); setTimeout(() => { connection.start().then(() => { isReconnected = true; }); }, 500); setTimeout(() => { if (!isReconnected) { connection.start().then(() => { isReconnected = true; }); } }, 1000); setTimeout(() => { if (!isReconnected) { connection.start().then(() => { isReconnected = true; }); } }, 2000); setTimeout(() => { if (!isReconnected) { isPlaying = false; StopBgm(); } }, 3000); setTimeout(() => { // フラグを元に戻す isReconnected = false; }, 5000); }); |
接続に成功したときの処理を追加します。
接続に成功したらconnectionIDのバックアップをとっています。切断⇒再接続にともなってconnectionIDが変更されてしまうので、サーバーサイドにこのペアを送信して同一ユーザーであることを知らせなければなりません。isReconnectedがtrueならこの処理が必要です。このときは古いconnectionIDと新しいconnectionIDといっしょに”Reconnected”を送信します。
通信の切断とともにisPlayingがfalseになってしまいます。そのため再接続に成功したら1秒待機してからisPlayingをtrueに変更しています。同時に通信が切れた旨を示す文字列を非表示にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
let oldConnectionID = ''; connection.on("SuccessfulConnectionToClient", function (result, id, rowmax, colmax) { oldConnectionID = connectionID; connectionID = id; rowMax = rowmax; colMax = colmax; document.getElementById("conect-result").innerHTML = `conect-result ${result}:${connectionID}`; let gameRecords = document.getElementById('game-records'); if (gameRecords != null) GetRecords() if (playerName != null && playerName.style.display != "none") { playerName.focus(); } else if (playerName != null && playerName.style.display == "none") { if (enteryButton != null) enteryButton.focus(); } // この部分を追加 if (isReconnected) { connection.invoke("Reconnected", connectionID, oldConnectionID).catch(); setTimeout(() => { errer.style.display = 'none'; isPlaying = true; }, 1000) } }); |
サーバーサイドにおける処理
クライアントサイドから”Reconnected”が送信されたら対応するConnectionIdsプロパティをもつPuyoMatchGameオブジェクトを探します。また再接続されたユーザーは観戦者かもしれません。該当するオブジェクトが見つかったらConnectionIdsプロパティの文字列を入れ替えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class PuyoMatchHub : Hub { public void Reconnected(string connectionID, string oldConnectionID) { List<PuyoMatchGame> games = new List<PuyoMatchGame>(Games); foreach (PuyoMatchGame game in games) { if (game.ConnectionIds[0] == oldConnectionID) { game.ConnectionIds[0] = connectionID; break; } if (game.ConnectionIds[1] == oldConnectionID) { game.ConnectionIds[1] = connectionID; break; } if (game.RemoveWatcher(oldConnectionID)) { game.AddWatcher(connectionID); break; } } } } |
通信が切断されたときの処理を示します。これまでは通信が切れたらすぐに試合放棄の処理をしていましたが、今回からはすぐにその処理をおこなわずに3秒待機します。もし3秒待ってもReconnectedメソッドの呼び出しがない場合はほんとうに試合放棄と見なして処理をおこないます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
public class PuyoMatchHub : Hub { public override async Task OnDisconnectedAsync(Exception? exception) { await base.OnDisconnectedAsync(exception); // この処理は切断時にすぐおこなう if (ClientProxyMap.ContainsKey(Context.ConnectionId)) ClientProxyMap.Remove(Context.ConnectionId); // エントリー状態でゲーム開始前のユーザーとの通信が切断された場合もこれまで同様、直ちにおこなう if (WaitingPlayer != null && WaitingPlayer.ConnectionId == Context.ConnectionId) { WaitingPlayer = null; var clients = ClientProxyMap.Values; foreach (IClientProxy client in clients) await client.SendAsync("SendWaitingPlayerInfoToClient", ""); } // 通信が切断されても再接続されるかもしれないので3秒待つ await Task.Delay(3000); // 3秒待機したあとContext.ConnectionIdに対応するプレーヤーがいるゲームは // 試合放棄されたものとして処理する CheckGameAbandoned(Context.ConnectionId); // 現在観戦できるゲームが変更されたことを観戦しているユーザーに通知する SendGamesToClients(); if (ClientProxyMap.Count == 0) TimerForUpdate.Stop(); } } |