用Netty做传奇2服务器-代理服务器
传奇2的源码, 估计被改了N手了, 封包格式那是相当的乱, 大部分时间都浪费在找正确的封包格式上了; 就比如 Ability
的Struct, 在C#和Delphi就能找到一个TAbility
和一个TOAbility
, 然后你发现用的是C#里的TOAbility
. 完了吧, 到StdItem
这个, 又发现Delphi版本里的TStdItem
是对的.
索性, 写个代理服务器将真服务器和真客户端的封包打印出来看看. 省的用MockClient 太麻烦了;
当前Netty服务器的封包流程, 也就是在Pipeline的流向:
[服务器原封包] -> 6BitDecoder -> [解密后的明文封包] -> PacketDecoder -> [ClientPacket对象] -> Handler处理
Handler发出去的是 ServerPacket, 所以流程是:
[ServerPacket] -> PacketEncode -> [明文封包] -> 6BitEncode -> [加密封包] -> 客户端
代理服务器实际上做这么两个准备:
- 连接传奇服务器, 拿到
serverChannel
- 监听一个端口比如7000
伪装成服务器
, 等待客户端连接, 当客户端连接之后, 拿到clientChannel
然后, 就是做这么两件事:
- 服务器Handler收到的东西,调用
clientChannel.writeAndFlush
发给客户端 - 客户端Handler收到的东西,调用
serverChannel.writeAndFlush
发给服务器
那么传奇服务器的封包通过代理服务器走到客户端的流程就是:
-------------------- 服务器连接Channel的Pipeline ----------------------------------------------
[服务器原封包] -> 6BitDecoder -> [解密后的明文封包] -> PacketDecoder -> [ClientPacket对象] ↘ |
---------------------------------------------------------------------------------------------
-------------------
| 代理服务器Handler |
-------------------
------------------- 客户端连接Channel的Pipeline -----------------------------------------------
[还原的加密封包] <- 6BitEncoder <- 还原的明文 <- ClientPacketEncoder <- [ClientPacket对象] ↙ |
---------------------------------------------------------------------------------------------
客户端发给传奇服务器的流程就是反过来
所以, 两端的pipeline分别就是:
// 服务器连接端的
ch.pipeline().addLast(
//这里是服务器的连接端
//来自服务器的封包, 解码成Packet
new DelimiterBasedFrameDecoder(2048, false, Unpooled.wrappedBuffer(new byte[]{'!'})),
new PacketBit6Decoder(),
new PacketDecoder(ServerPackets.class.getCanonicalName()),
//来自客户端的封包, 发给
new ClientPacketBit6Encoder(),
new PacketEncoder(),
new ChannelHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 来自服务器的封包, 通过 client 连接端 发给客户端
clientChannel.writeAndFlush(packet);
}
},
new ExceptionHandler()
);
// 客户端连接端的
ch.pipeline().addLast(
//这里是客户端的连接端
//客户端发来的封包, 解码成 Packet
new DelimiterBasedFrameDecoder(2048, false, Unpooled.wrappedBuffer(new byte[]{'!'})),
new ClientPacketBit6Decoder(),
new ClientPacketDecoder(ClientPackets.class.getCanonicalName()),
//发给客户端的封包, 从Packet还原成加密封包
new PacketBit6Encoder(),
new PacketEncoder(),
new ChannelHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
clientChannel = ctx;
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
serverChannel.writeAndFlush(msg);
}
}
);