オブジェクトのシリアル化を直接使用することを思いついたのはなぜですか?これはゲームサーバーにとって非常に悪いことです。ゲームサーバーは低速で、壊れやすく(特に、シリアル化する複雑なオブジェクトグラフがある場合)、一般的にハックです。解決策は、独自のゲーム通信プロトコルを最初から設計して実装することです。幸い、それは簡単です。
シリアル化は、主に退屈なビジネスアプリケーションとデータ層で行われます。その目的は、多くの場合、異種システム間でデータを簡単に転送することです。そのため、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で最新の/ async
IOAPIを使用する場合、着信メッセージを逆多重化するためのロジックをシングルスレッドで非常に簡単に実行できます。それ以外の場合は、リモート接続ごとに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
}
}