Initial Netty implementation for networking
Split into 1 LoginServer and 1 ChannelServer per channel. There is still a lot of cleanup and refactoring to be done. Currently, the reliance on IoSession holding client state is the most pressing issue to be addressed.
This commit is contained in:
12
src/main/java/net/netty/AbstractServer.java
Normal file
12
src/main/java/net/netty/AbstractServer.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package net.netty;
|
||||
|
||||
public abstract class AbstractServer {
|
||||
final int port;
|
||||
|
||||
AbstractServer(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public abstract void start();
|
||||
public abstract void stop();
|
||||
}
|
||||
40
src/main/java/net/netty/ChannelServer.java
Normal file
40
src/main/java/net/netty/ChannelServer.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package net.netty;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
|
||||
public class ChannelServer extends AbstractServer {
|
||||
private final int world;
|
||||
private final int channel;
|
||||
private Channel nettyChannel;
|
||||
|
||||
public ChannelServer(int port, int world, int channel) {
|
||||
super(port);
|
||||
this.world = world;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
EventLoopGroup parentGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup childGroup = new NioEventLoopGroup();
|
||||
ServerBootstrap bootstrap = new ServerBootstrap()
|
||||
.group(parentGroup, childGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new ChannelServerInitializer(world, channel));
|
||||
|
||||
this.nettyChannel = bootstrap.bind(port).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (nettyChannel == null) {
|
||||
throw new IllegalStateException("Must start ChannelServer before stopping it");
|
||||
}
|
||||
|
||||
nettyChannel.close().syncUninterruptibly();
|
||||
}
|
||||
}
|
||||
31
src/main/java/net/netty/ChannelServerInitializer.java
Normal file
31
src/main/java/net/netty/ChannelServerInitializer.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package net.netty;
|
||||
|
||||
import client.MapleClient;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.PacketProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ChannelServerInitializer extends ServerChannelInitializer {
|
||||
private static final Logger log = LoggerFactory.getLogger(ChannelServerInitializer.class);
|
||||
|
||||
private final int world;
|
||||
private final int channel;
|
||||
|
||||
public ChannelServerInitializer(int world, int channel) {
|
||||
this.world = world;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel socketChannel) {
|
||||
final String clientIp = socketChannel.remoteAddress().getHostName();
|
||||
log.debug("Client connected to world {}, channel {} from {}", world, channel, clientIp);
|
||||
|
||||
PacketProcessor packetProcessor = PacketProcessor.getChannelServerProcessor(world, channel);
|
||||
final MapleClient client = new MapleClient(packetProcessor, world, channel);
|
||||
client.setSessionId(sessionId.getAndIncrement());
|
||||
|
||||
initPipeline(socketChannel, client);
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,9 @@ public class ClientCyphers {
|
||||
this.receive = receive;
|
||||
}
|
||||
|
||||
public static ClientCyphers generateNew() {
|
||||
MapleAESOFB send = new MapleAESOFB(InitializationVector.generateSend(), ServerConstants.VERSION);
|
||||
MapleAESOFB receive = new MapleAESOFB(InitializationVector.generateReceive(), ServerConstants.VERSION);
|
||||
public static ClientCyphers of(InitializationVector sendIv, InitializationVector receiveIv) {
|
||||
MapleAESOFB send = new MapleAESOFB(sendIv, (short) (0xFFFF - ServerConstants.VERSION));
|
||||
MapleAESOFB receive = new MapleAESOFB(receiveIv, ServerConstants.VERSION);
|
||||
return new ClientCyphers(send, receive);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
package net.netty;
|
||||
|
||||
import client.MapleClient;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ClientInitializer extends ChannelInitializer<SocketChannel> {
|
||||
private static final Logger log = LoggerFactory.getLogger(ClientInitializer.class);
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel socketChannel) {
|
||||
final String clientIp = socketChannel.remoteAddress().getHostName();
|
||||
log.debug("Client initiated new connection from: {}", clientIp);
|
||||
|
||||
socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.generateNew()));
|
||||
socketChannel.pipeline().addLast("MapleClient", new MapleClient());
|
||||
}
|
||||
}
|
||||
38
src/main/java/net/netty/LoginServer.java
Normal file
38
src/main/java/net/netty/LoginServer.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package net.netty;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
|
||||
public class LoginServer extends AbstractServer {
|
||||
public static final int WORLD = -1;
|
||||
public static final int CHANNEL = -1;
|
||||
private Channel channel;
|
||||
|
||||
public LoginServer(int port) {
|
||||
super(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
EventLoopGroup parentGroup = new NioEventLoopGroup();
|
||||
EventLoopGroup childGroup = new NioEventLoopGroup();
|
||||
ServerBootstrap bootstrap = new ServerBootstrap()
|
||||
.group(parentGroup, childGroup)
|
||||
.channel(NioServerSocketChannel.class)
|
||||
.childHandler(new LoginServerInitializer());
|
||||
|
||||
this.channel = bootstrap.bind(port).syncUninterruptibly().channel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (channel == null) {
|
||||
throw new IllegalStateException("Must start LoginServer before stopping it");
|
||||
}
|
||||
|
||||
channel.close().syncUninterruptibly();
|
||||
}
|
||||
}
|
||||
23
src/main/java/net/netty/LoginServerInitializer.java
Normal file
23
src/main/java/net/netty/LoginServerInitializer.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package net.netty;
|
||||
|
||||
import client.MapleClient;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import net.PacketProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class LoginServerInitializer extends ServerChannelInitializer {
|
||||
private static final Logger log = LoggerFactory.getLogger(LoginServerInitializer.class);
|
||||
|
||||
@Override
|
||||
public void initChannel(SocketChannel socketChannel) {
|
||||
final String clientIp = socketChannel.remoteAddress().getHostName();
|
||||
log.debug("Client connected to login server from {} ", clientIp);
|
||||
|
||||
PacketProcessor packetProcessor = PacketProcessor.getLoginServerProcessor();
|
||||
final MapleClient client = new MapleClient(packetProcessor, LoginServer.WORLD, LoginServer.CHANNEL);
|
||||
client.setSessionId(sessionId.getAndIncrement());
|
||||
|
||||
initPipeline(socketChannel, client);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
package net.netty;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import net.mina.MapleCustomEncryption;
|
||||
import net.packet.ByteBufInPacket;
|
||||
import tools.MapleAESOFB;
|
||||
|
||||
import java.util.List;
|
||||
@@ -28,7 +30,7 @@ public class PacketDecoder extends ReplayingDecoder<Void> {
|
||||
in.readBytes(packet);
|
||||
receiveCypher.crypt(packet);
|
||||
MapleCustomEncryption.decryptData(packet);
|
||||
out.add(packet);
|
||||
out.add(new ByteBufInPacket(Unpooled.wrappedBuffer(packet)));
|
||||
// TODO conditionally log the packet
|
||||
}
|
||||
|
||||
|
||||
22
src/main/java/net/netty/ServerChannelInitializer.java
Normal file
22
src/main/java/net/netty/ServerChannelInitializer.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package net.netty;
|
||||
|
||||
import client.MapleClient;
|
||||
import constants.net.ServerConstants;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public abstract class ServerChannelInitializer extends ChannelInitializer<SocketChannel> {
|
||||
static final AtomicLong sessionId = new AtomicLong(7777);
|
||||
|
||||
void initPipeline(SocketChannel socketChannel, MapleClient client) {
|
||||
final InitializationVector sendIv = InitializationVector.generateSend();
|
||||
final InitializationVector recvIv = InitializationVector.generateReceive();
|
||||
socketChannel.writeAndFlush(Unpooled.wrappedBuffer(MaplePacketCreator.getHello(ServerConstants.VERSION, sendIv, recvIv)));
|
||||
socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.of(sendIv, recvIv)));
|
||||
socketChannel.pipeline().addLast("MapleClient", client);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user