エラーハンドリングの実装パターン

GS2 を使用したゲーム開発における、実践的なエラーハンドリングとリトライ戦略のベストプラクティス

GS2 を利用したネットワーク機能の実装において、エラーハンドリングはゲームの安定性とユーザー体験(UX)に直結します。 ここでは、単なる例外のキャッチに留まらない、実践的な実装パターンを紹介します。

1. エラーの分類と対応方針

GS2 の例外は、その性質によって大きく 3 つに分類できます。

分類該当する例外 (例)対応方針
一時的なエラーInternalServerError, ServiceUnavailable, RequestTimeout自動リトライ。数回試行してもダメな場合はエラーダイアログを表示。
ロジック/設定エラーBadRequest, NotFound開発中に解消すべきエラー。リリース後はバグとしてログ収集し、ユーザーには適切なメッセージを表示。
認証・状態エラーUnauthorized, Conflict, QuotaLimitExceeded特別なリカバリ処理が必要。再ログインや、データの再同期(ドメインの再取得)を行う。

2. リトライ戦略の実装(指数バックオフ)

サーバーの過負荷や一時的なネットワークの不安定さによるエラーに対しては、即座にリトライするのではなく、間隔を空けてリトライする「指数バックオフ(Exponential Backoff)」が推奨されます。

Unity / C# での例(自動リトライ)

public async Task<T> ExecuteWithAutoRetry<T>(Func<Task<T>> action, int maxRetries = 3)
{
    for (int i = 0; i < maxRetries; i++)
    {
        try
        {
            return await action();
        }
        catch (Gs2.Core.Exception.Gs2Exception e) when (e.RecommendAutoRetry) // 自動リトライ推奨か判定
        {
            if (i == maxRetries - 1) throw;

            // 指数バックオフ: 1s, 2s, 4s...
            await Task.Delay((int)Math.Pow(2, i) * 1000);
        }
    }
    throw new Exception("Reached unreachable code");
}

手動リトライの判断

RecommendAutoRetryfalse かつ RecommendRetrytrue の場合(例:ConflictExceptionQuotaLimitExceededException)、自動で何度も繰り返すと状況を悪化させる可能性があるため、ユーザーに確認を促すダイアログを表示してリトライさせるのが一般的です。

3. 認証エラー (UnauthorizedException) の自動リカバリ

アクセストークンの有効期限切れなどで UnauthorizedException が発生した場合、ユーザーにタイトル画面へ戻るよう促すのではなく、バックグラウンドで自動的にログインし直してリクエストを再試行するのが理想的です。

実装イメージ

try
{
    await gs2.Inventory.Namespace("...").Me(GameSession).Inventory("...").ModelAsync();
}
catch (Gs2.Core.Exception.UnauthorizedException)
{
    // 1. バックグラウンドで再ログイン処理を実行
    GameSession = await ReLogin();
    
    // 2. 新しい GameSession を使ってリクエストを再試行
    await gs2.Inventory.Namespace("...").Me(GameSession).Inventory("...").ModelAsync();
}

4. コンフリクト (ConflictException) の解消

スタンプシートの実行中に通信が途切れた際などに ConflictException が発生することがあります。これは「前回の処理がまだサーバー側で完了していない、あるいは重複している」ことを示します。

  • 対応策: 多くの場合は単純なリトライで解消されます。GS2 SDK の高レベル API(Domain オブジェクト)を使用している場合、内部のキャッシュとサーバーの状態に不整合がある可能性があるため、Domain オブジェクトを再取得してからリトライすることをお勧めします。

5. ユーザーへのフィードバック(UX)

全てのエラーでダイアログを表示すると、ユーザーはストレスを感じます。

  1. サイレント・リトライ: 一時的なエラーは、まずバックグラウンドで 1〜2 回リトライします。その間、UI には「通信中…」のインジケータを表示するに留めます。
  2. 回復可能なダイアログ: リトライしても失敗した場合、「通信に失敗しました。電波の良い場所で再度お試しください」といったメッセージと [リトライ] [タイトルへ] ボタンを表示します。
  3. 致命的なエラー: メンテナンス中やアプリバージョンの強制アップデートが必要な場合は、タイトル画面へ戻す以外の選択肢がない専用のダイアログを表示します。

まとめ

  • リトライ可能なエラーには 指数バックオフ を適用する。
  • 認証エラーは 自動再ログイン で隠蔽する。
  • 開発中は カオスモード を有効にして、これらのハンドリングが正しく機能するか検証する。