网络/离线消息路由

时间:2014-05-24 18:36:17

标签: c# .net networking design-patterns

我正在努力重写我的基于MUD的游戏引擎,其中一个主要目标是使其更加模块化,将组件分离。我遇到的一件事就是分发信息。

当用户将引擎设置为以MMO形式运行时,所有通信都通过服务器完成。对象通过其网络套接字发送和接收消息,执行命令并从环境接收数据。

该引擎的修订版将支持单人游戏离线游戏。是否存在可用于通过中央位置路由消息/数据的模式,可以根据需要在服务器或本地客户端之间进行重定向?我不想用条件语句乱丢发动机以检查是否联网。我宁愿有一个处理这种沟通的调度员。

如果有人能指出我朝着正确的方向努力实现这一目标,我会很感激!

编辑:我一直在努力保持引擎非常抽象。我大量使用工厂模式,泛型和存储库来防止紧密耦合。我希望通过网络方面继续这种方法。在90%的情况下,网络通信将由客户端telnet客户端发送的命令引起。因此,处理接收命令和基于网络状态的处理是直截了当的。棘手的部分来自游戏循环,我必须将从众多对象发送的消息广播回客户端。所有启用网络的对象都实现了一个IServerObject接口,因此服务器知道它可以与哪些对象进行通信。我认为中央调度系统是有道理的,但不确定我是否有可以遵循的模式来帮助指导我。

1 个答案:

答案 0 :(得分:1)

<强>声明

这绝对不是最好的方式,但它是需要最少努力的方法。在我看来,服务器不应该知道它正在向一个客户端或多个远程客户端广播,因为服务器所有客户端都是远程的。你应该只是不接受任何非本地来源的连接IMO

/// <summary>
/// A wrapper around a client instance that is interested in
/// the game events. It is observable, and when you observe the
/// client, you can subscribe to the messages we receive from that
/// client.
/// </summary>
public interface IClient : IObservable<IMessage>, IDisposable
{
    /// <summary>
    /// Send the client a message.
    /// The client is responsible for encoding
    /// or decoding the message as necessary
    /// </summary>
    /// <param name="message"></param>
    void Send(IMessage message);
}

/// <summary>
/// Some information that can be sent to either a remote
/// client or a local client. This could be as simple as
/// EventArgs, just make sure you can write a decoder/encoder
/// for each of the types. The message types should derive
/// from this interface
/// </summary>
public interface IMessage
{

}

/// <summary>
/// A class that can serialize or deserialize messages
/// </summary>
/// <typeparam name="TSerializedType"></typeparam>
public interface IMessageSerializer<TSerializedType>
{
    TSerializedType Encode(IMessage message);

    IMessage Decode(TSerializedType serialized);
}

/// <summary>
/// This is a client located at a remote location (i.e,
/// not at this server and we're connecting to it via a Tcp
/// connection)
/// </summary>
public class RemoteClient : IClient
{
    private readonly TcpClient _client;

    /// <summary>
    /// Creates the Remote client.
    /// </summary>
    /// <param name="client">The underlying .NET connection</param>
    /// <param name="serializer">The instance of IMessageSerializer that can
    /// serialize or deserialize messages</param>
    public RemoteClient(TcpClient client, IMessageSerializer serializer)
    {
        ....
    }

    public void Send(IMessage message)
    {
        var serialized = serializer.Encode(message);
        client.Write(serialized);
    }
}

/// <summary>
/// This is the class you would use if you were connecting
/// to no one and just wanted to route messages back to yourself
/// </summary>
public class LocalClient : IClient
{
    /// <summary>
    /// Creates the local client
    /// </summary>
    /// <param name="messageQueue">An observer that is listening for messages.
    /// This is our message queue and it should be pointing at someone who
    /// is interested in consuming the messages (the game UI for example)</param>
    public LocalClient(IObserver<IMessage> messageQueue)
    {
    }

    public void Send(IMessage message)
    {
        messageQueue.OnNext(message);
    }
}

这与我在Minecraft服务器中使用的实现类似。您可以创建不同类型的客户端,具体取决于您是否有兴趣发送到远程客户端。您可以更进一步,跳过LocalClient方法,只是广播到IObserver<IMessage>

预期用途是您要创建LocalClient而不是RemoteClient并向其广播,这会将您刚刚告诉LocalClient的邮件推送到{ {1}} - 应该是在您的服务器中使用事件的一方。

您可以通过创建IObserver<IMessage>LocalClientRemoteClient之间进行更改,然后将其导出以创建IClientFactoryLocalClientFactory(为简洁而省略)和使用依赖注入来改变您使用的客户端工厂类型。