1

「良い」ゲームサーバーの書き方を考えています。私にはいくつかのアイデアがありましたが、サーバーを作成したことはありませんでした。頭がおかしいコードを書くことになりたくありません。

TCP接続などの処理方法は知っていますが、問題はサーバーとクライアントの間でどのように通信するかです。

例: 私は TicTacTow のようなゲームを書いています。ユーザーがセルをクリックしたので、そのサーバーに伝えたいと思います。サーバーは、ユーザーがそのセルをクリックできるかどうかを検証し、クライアントに通知する必要があります。サーバーが「はい」と答えた場合。クライアントがこれを「X」として表示することをクリックできます。

今私の問題:そのフィールドをクリックしたいことをサーバーに正確に伝えるにはどうすればよいですか。ここで別の質問に出くわし、最終的にコマンドパターンを使用することになりました。しかし、私の理解が正しければ、インターフェイスを実装するコマンドを作成する必要があります。そのコマンドのインスタンスをシリアル化し、サーバーに送信します。サーバーはコマンドを実行します。しかし、私はそれに関する主な問題を抱えています:

  1. コマンドがティクタクトのセルを X に設定する場合、セルを X に設定する必要がある GameBoard をインターフェイス ICommand の Invoke-Method に渡す必要があることをサーバーに伝えるにはどうすればよいですか。
  2. それは非常に安全ではありませんか?つまり、すべてのファイルを削除するか、サーバーを停止してサーバーに送信するコマンドを作成できます。Command-Pattern がそれほど良いアイデアだとは思いません。

だから私はもっと良いものを探しています。クライアントとサーバーに簡単で拡張可能なアーキテクチャが必要です。何か良いパターンはありますか?

ああ、別の質問: シリアライザーを使用しますか、それともデータを自分でエンコードしますか?

4

1 に答える 1

7

オブジェクトのシリアル化を直接使用することを思いついたのはなぜですか?これはゲームサーバーにとって非常に悪いことです。ゲームサーバーは低速で、壊れやすく(特に、シリアル化する複雑なオブジェクトグラフがある場合)、一般的にハックです。解決策は、独自のゲーム通信プロトコルを最初から設計して実装することです。幸い、それは簡単です。

シリアル化は、主に退屈なビジネスアプリケーションとデータ層で行われます。その目的は、多くの場合、異種システム間でデータを簡単に転送することです。そのため、XMLベースのシリアル化がよく使用されます。これらの要件はゲームには適用されません。

オブジェクトをシリアル化すると、データメンバーのみがシリアル化され、メソッドはシリアル化されないことに注意してください。したがって、悪意のあるコードが送信されることに対するあなたの恐れは根拠がありません。

とにかく、サーバー設計の最初のルールは、クライアントを決して信頼しないことです。つまり、ゲームサーバーは、現在のゲーム状態のコピーをメモリに保持し、クライアントから送信される各ゲーム移動メッセージにゲームのルールを適用する必要があります。

鳥の目の観点から、これが私があなたの三目並べシステムを設計する方法です:

プロトコル

ゲームの状態への変更はイベント駆動型であり、ターンベースであるため、動詞と引数に基づくプロトコルが最適です。これが厳密に2人用ゲーム(2つのクライアントと1つのサーバーを使用)であると仮定すると、次の動詞があります。

クライアントからサーバーへ

JOIN
READY
MOVE (x, y)
QUIT

サーバーからクライアント

JOINED
ISREADY
MOVED (player, x, y)
DENIED (reason)
LEFTGAME (player)

クライアントからサーバーへの動詞は自明である必要があります。サーバーからクライアントへの動詞は、元のクライアントからサーバーへのメッセージのプロキシバージョンであることに気付くでしょう。ただし、MOVEDには移動したプレーヤーのIDが含まれ、クライアントに移動が拒否されたことを通知するDENIEDも含まれています。LEFTGAME動詞は、他のプレーヤーが終了したことを通知するために使用されます。

サーバ

これは、架空のTic-tac-toeサーバーの不完全な疑似コード実装です。私が書いたコードは、アプリケーションのネットワークレイヤーのみに関係しており、三目並べのゲームルールと動きを取り巻く実際のロジックはすべてTicTacToeBoardクラス内に含まれており、ここでは説明しませんが、実装は簡単です。

Taskサーバーロジックはシングルスレッドで発生します。.NETで最新の/ asyncIOAPIを使用する場合、着信メッセージを逆多重化するためのロジックをシングルスレッドで非常に簡単に実行できます。それ以外の場合は、リモート接続ごとに1つのスレッドを使用すると迅速になります。それに対処する簡単な方法もあります(2人用ゲームはスケーリングについて心配する必要はありませんが、これは数百人のプレーヤーにうまくスケーリングすることはできません)。WaitForAndGetConnection(これらのリモート接続を処理するためのコードはここでは詳しく説明されていません。抽象、、、GetLastIncomingMessageおよびメソッドの背後に隠されていSendMessageます)。

    // Ready-room state
    connection1 = WaitForAndGetConnection();
    connection2 = WaitForAndGetConnection();
    SendMessage( connection1, "JOINED" ); // inform player 1 that another player joined
    
    p1Ready = false, p2Ready = false;
    while(message = GetLastIncomingMessage() && !p1Ready && !p2Ready) {
        if( message.From == connection1 && message.Verb == "READY" ) p1Ready = true;
        if( message.From == connection2 && message.Verb == "READY" ) p2Ready = true;        
    }
    
    SendMessage( connection1, "ISREADY" ); // inform the players the game has started
    SendMessage( connection2, "ISREADY" ); // inform the players the game has started
    
    // Game playing state
    TicTacToeBoard board = new TicTacToeBoard(); // this class represents the game state and game rules
    
    p1Move = true; // indicates whose turn it is to move
    while(message = GetLastIncomingMessage()) {
        
        if( message.Verb == "MOVE" ) {
        
            if( p1Move && message.From == connection1 ) {
                 if( board.Player1Move( message.X, message.Y ) ) {
                    SendMessage( connection1, "MOVED (1, " + message.X + "," + message.Y + " )");
                    SendMessage( connection2, "MOVED (1, " + message.X + "," + message.Y + " )");
                    p1Move = false;
                } else {
                    SendMessage( message.From, "DENIED \"Disallowed move.\".");
                }
                
            } else if( !p1Move && message.From == connection2 ) {
                
                if( board.Player2Move( message.X, message.Y ) ) {
                    SendMessage( connection1, "MOVED (2, " + message.X + "," + message.Y + " )");
                    SendMessage( connection2, "MOVED (2, " + message.X + "," + message.Y + " )");
                    p1Move = true;
                } else {
                    SendMessage( message.From, "DENIED \"Disallowed move.\".");
                }
                
            } else {
                SendMessage( message.From, "DENIED \"It isn't your turn to move\".");
            }
            if( board.IsEnded ) { 
                // handle game-over...
            } 
        } else if( message.Verb == ... // handle all of the other verbs, like QUIT 
        }
    }
于 2013-02-16T09:42:01.587 に答える