网络/离线消息路由

本文关键字:路由 消息 离线 网络 | 更新日期: 2023-09-27 18:05:12

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

当用户设置引擎作为MMO运行时,所有通信都是通过服务器完成的。对象通过它们的网络套接字发送和接收消息、执行命令和从环境接收数据。

修改后的引擎将支持单人离线游戏。是否存在可用于通过中心位置路由消息/数据的模式,该中心位置可以根据需要在服务器或本地客户机之间重定向?我不想用条件语句来检查引擎是否联网。我更希望有某种调度器来处理这种通信。

如果有人能指出我在正确的方向来实现这一点,我会很感激!

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

网络/离线消息路由

免责声明

这绝对不是最好的方法,但却是最省力的方法。在我看来,服务器不应该意识到它正在广播到单个客户端或多个远程客户端,因为服务器的所有客户端都是远程的。你应该简单地不接受任何非本地来源的连接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 s,并向其广播,这将把您刚刚告诉LocalClient接收的消息推送到IObserver<IMessage> -它应该是在您的服务器中消费事件的一方。

您可以通过创建IClientFactory来更改LocalClientRemoteClient,然后派生创建LocalClientFactoryRemoteClientFactory(为简洁起见省略),并使用依赖注入来更改您使用的客户端工厂类型。