C#Gameserver架构

时间:2013-02-16 09:19:04

标签: c# design-patterns tcp tcpclient tcplistener

我想知道如何写一个“好”的游戏服务器。我有一些想法,但我从来没有制作过服务器,我不想最终编写brainfuck代码。

我知道如何处理TCP-Connections等等但我的问题是如何在服务器和客户端之间进行通信。

例如:我写了一个类似TicTacTow的游戏。现在用户点击了一个单元格,我想告诉该服务器。服务器应验证用户是否可以单击该单元并告知客户端。如果服务器说是;您可以单击客户端将其显示为“X”。

现在我的问题:我如何告诉服务器我想要点击该字段。我在这里遇到了另一个问题,他们最终使用了命令模式。但如果我理解正确,我将不得不创建一个实现接口的命令。我序列化该命令的一个实例并将其发送到服务器。服务器执行命令。但我必须解决主要问题:

  1. 如果命令将tictacto的单元格设置为X.如何告诉服务器他必须传递GameBoard,我需要将单元格设置为X到接口ICommand的Invoke-Method。 / LI>
  2. 这不是非常不安全吗?我的意思是我可以编写一个命令来删除我的所有文件或停止服务器并将其发送到服务器。我不相信命令模式是个好主意。
  3. 所以我正在寻找更好的东西。我只想为我的客户端和服务器提供一个简单易用的架构。有没有好的模式?

    哦,还有一个问题:您是否会使用序列化程序,还是自己对数据进行编码?

1 个答案:

答案 0 :(得分:6)

是什么让你直接使用对象序列化的想法?对于游戏服务器而言,这将是一件非常糟糕的事情,因为它很慢,可能容易中断(特别是如果你有一个复杂的对象图来序列化),而且通常是hackish。解决方案是从头开始设计和实现您自己的游戏通信协议 - 幸运的是,它很容易。

序列化主要在无聊的业务应用程序和数据层中完成,其目标是通常在异构系统之间轻松传输数据,这就是经常使用基于XML的序列化的原因。这些要求不适用于游戏。

请注意,序列化对象只会序列化数据成员,而不会序列化方法。因此,您对恶意代码发送的担忧是没有根据的。

无论如何,服务器设计的第一条规则是从不信任客户端 - 这意味着游戏服务器需要在内存中维护当前游戏状态的副本并将游戏规则应用于每个游戏状态从客户端发送的游戏移动消息。

从鸟瞰角度来看,这就是我设计你的井字游戏系统的方法:

协议

因为对游戏状态的更改是事件驱动的并且它是基于回合的,所以基于动词和参数的协议效果最好。我会有以下动词,假设这是一个严格的2人游戏(使用2个客户端和服务器)

客户端到服务器
JOIN
READY
MOVE (x, y)
QUIT
服务器到客户端
JOINED
ISREADY
MOVED (player, x, y)
DENIED (reason)
LEFT

客户端到服务器的动词应该是不言自明的。您会注意到服务器到客户端谓词是原始客户端到服务器消息的代理版本,但MOVED包含移动的玩家的ID,并且还包含DENIED以通知客户其移动被拒绝。 LEFT动词用于表示其他玩家已退出。

服务器

这是一个假设的Tic-tac-toe服务器的不完整的伪代码实现。我编写的代码只涉及应用程序的网络层,围绕井字游戏规则和移动的所有实际逻辑都包含在TicTacToeBoard类中,这里没有描述,但它应该实施起来很简单。

服务器逻辑发生在单个线程中,但必要时,用于复用来自网络的传入消息的逻辑需要在不同的线程上发生。这里没有详细介绍,它隐藏在抽象的WaitForAndGetConnectionGetLastIncomingMessageSendMessage方法背后。

// 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 
    }
}

HTH。