网络/离线消息路由
本文关键字:路由 消息 离线 网络 | 更新日期: 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
来更改LocalClient
和RemoteClient
,然后派生创建LocalClientFactory
和RemoteClientFactory
(为简洁起见省略),并使用依赖注入来更改您使用的客户端工厂类型。