-
Notifications
You must be signed in to change notification settings - Fork 25
Use byte buffer for packets to avoid copying #46
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -57,32 +57,29 @@ public class ShroomAESCipher { | |
| (byte) 0x84, (byte) 0x7F, (byte) 0x61, (byte) 0x1E, (byte) 0xCF, (byte) 0xC5, (byte) 0xD1, (byte) 0x56, | ||
| (byte) 0x3D, (byte) 0xCA, (byte) 0xF4, (byte) 0x05, (byte) 0xC6, (byte) 0xE5, (byte) 0x08, (byte) 0x49}; | ||
|
|
||
| public static final byte[] IG_SEED = {(byte) 0xf2, 0x53, (byte) 0x50, (byte) 0xc6}; | ||
| public static final byte[] IG_SEED = { (byte) 0xf2, 0x53, (byte) 0x50, (byte) 0xc6 }; | ||
|
|
||
| private static Cipher cipher = initCipher(); | ||
|
|
||
| private final short mapleVersion; | ||
| private final Cipher cipher; | ||
| private byte[] iv; | ||
|
|
||
| public ShroomAESCipher(InitializationVector iv, short mapleVersion) { | ||
| private static Cipher initCipher() { | ||
| try { | ||
| cipher = Cipher.getInstance("AES"); | ||
| Cipher cipher = Cipher.getInstance("AES"); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the old algo, please update to the new one. |
||
| cipher.init(Cipher.ENCRYPT_MODE, AES_KEY); | ||
| return cipher; | ||
| } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| } | ||
|
|
||
| private final short mapleVersion; | ||
| private byte[] iv; | ||
|
|
||
| public ShroomAESCipher(InitializationVector iv, short mapleVersion) { | ||
| this.iv = iv.getBytes(); | ||
| this.mapleVersion = mapleVersion; | ||
| } | ||
|
|
||
| public ShroomAESCipher(byte[] iv, short mapleVersion) { | ||
| try { | ||
| cipher = Cipher.getInstance("AES"); | ||
| cipher.init(Cipher.ENCRYPT_MODE, AES_KEY); | ||
| } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
|
|
||
| this.iv = iv; | ||
| this.mapleVersion = mapleVersion; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| package com.dori.SpringStory.connection.netty; | ||
|
|
||
| import com.dori.SpringStory.client.MapleClient; | ||
| import com.dori.SpringStory.connection.crypto.InitializationVector; | ||
| import com.dori.SpringStory.connection.crypto.ShroomAESCipher; | ||
| import com.dori.SpringStory.connection.packet.packets.CLogin; | ||
| import com.dori.SpringStory.constants.ServerConstants; | ||
|
|
@@ -19,20 +20,17 @@ | |
| import static com.dori.SpringStory.connection.netty.NettyClient.CLIENT_KEY; | ||
|
|
||
| public interface BaseAcceptor { | ||
|
|
||
| private static byte getFinalRandomByteForIV(){ | ||
| return (byte) Math.abs(Math.random() * 255); | ||
| } | ||
|
|
||
| /** | ||
| * Creating an acceptor based on ServerBootstrap. | ||
| * @param port - server port. | ||
| * @param channelPool - The pool of the server (if needed - login / chat acceptor). | ||
| * @param channel - The current channel if it's a channel acceptor. | ||
| * @param logger - The acceptor logger. | ||
| * | ||
| * @param port - server port. | ||
| * @param channelPool - The pool of the server (if needed - login / chat | ||
| * acceptor). | ||
| * @param channel - The current channel if it's a channel acceptor. | ||
| * @param logger - The acceptor logger. | ||
| */ | ||
| static void createAcceptor(int port, @Nullable Map<String, Channel> channelPool, | ||
| @Nullable MapleChannel channel, @NonNull Logger logger){ | ||
| @Nullable MapleChannel channel, @NonNull Logger logger) { | ||
| EventLoopGroup bossGroup = new NioEventLoopGroup(); | ||
| EventLoopGroup workerGroup = new NioEventLoopGroup(); | ||
| try { | ||
|
|
@@ -44,26 +42,30 @@ static void createAcceptor(int port, @Nullable Map<String, Channel> channelPool, | |
| @Override | ||
| protected void initChannel(SocketChannel ch) { | ||
| // Updated to v95 - | ||
| byte[] siv = new byte[]{82, 48, 120, getFinalRandomByteForIV()}; | ||
| byte[] riv = new byte[]{70, 114, 122, getFinalRandomByteForIV()}; | ||
| InitializationVector siv = InitializationVector.generateSend(); | ||
| InitializationVector riv = InitializationVector.generateReceive(); | ||
| short rver = ServerConstants.VERSION; | ||
| short sver = (short) ~ServerConstants.VERSION; | ||
| // Set Decoder/Handler/Encoder - | ||
| ch.pipeline().addLast(new PacketDecoder(new ShroomAESCipher(riv, ServerConstants.VERSION)), new ChannelHandler(), new PacketEncoder(new ShroomAESCipher(siv, (short) ~ServerConstants.VERSION))); | ||
| ch.pipeline().addLast(new PacketDecoder(riv, rver), | ||
| new ChannelHandler(), | ||
| new PacketEncoder(siv, sver)); | ||
| // Init Encoder outPacketMapping - | ||
| PacketEncoder.initOutPacketOpcodesHandling(); | ||
| // Create new client for the connection - | ||
| MapleClient c = new MapleClient(ch); | ||
| // Init connection for the client - | ||
| logger.serverNotice(String.format("[CHAT] Opened session with %s in Acceptor", c.getIP())); | ||
| c.write(CLogin.sendConnect(siv,riv)); | ||
| c.write(CLogin.sendConnect(siv.getBytes(), riv.getBytes())); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. your Initialization vector save int and start convert to bytes each time you call getBytes, it's redundant and expensive thus i've removed it and kept it as byte array |
||
| // If we get a ChannelPool, then it will add the IP to the pool - | ||
| if(channelPool != null){ | ||
| if (channelPool != null) { | ||
| // Add to the channel pool - | ||
| channelPool.put(c.getIP(), ch); | ||
| } | ||
| // ChannelInitializer attributes - | ||
| ch.attr(CLIENT_KEY).set(c); | ||
|
|
||
| //EventManager.addFixedRateEvent(c::sendPing, 0, 10000); | ||
| // EventManager.addFixedRateEvent(c::sendPing, 0, 10000); | ||
| } | ||
| }); | ||
| // Adding channel options - | ||
|
|
@@ -72,8 +74,9 @@ protected void initChannel(SocketChannel ch) { | |
| // Bind and start to accept incoming connections. | ||
| ChannelFuture f = b.bind(port).sync(); | ||
| // If we get a MapleChannel then print the listening to the channel and port - | ||
| if(channel != null){ | ||
| logger.notice(String.format("Channel %d-%d listening on port %d", channel.getWorldId(), channel.getChannelId(), channel.getPort())); | ||
| if (channel != null) { | ||
| logger.notice(String.format("Channel %d-%d listening on port %d", channel.getWorldId(), | ||
| channel.getChannelId(), channel.getPort())); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. unnecessary dropping line |
||
| } | ||
| // Wait until the server socket is closed. | ||
| // In this example, this does not happen, but you can do that to gracefully | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| package com.dori.SpringStory.connection.netty; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.Map.Entry; | ||
| import java.util.concurrent.locks.ReentrantReadWriteLock; | ||
| import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; | ||
|
|
||
| import com.dori.SpringStory.connection.packet.OutPacket; | ||
|
|
||
| public class BroadcastSet<T extends NettyClient> { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. after thinking on what you said yesterday - |
||
| private final Map<Integer, T> clients; | ||
| private final ReentrantReadWriteLock lock; | ||
|
|
||
| public BroadcastSet() { | ||
| this.clients = new HashMap<>(); | ||
| // A fair lock is not required as locks will be short-lived | ||
| this.lock = new ReentrantReadWriteLock(false); | ||
| } | ||
|
|
||
| /** | ||
| * Broadcasts the given packet to all clients in the set | ||
| * | ||
| * @param packet | ||
| */ | ||
| public void broadcast(OutPacket packet) { | ||
| ReadLock lock = this.lock.readLock(); | ||
| try { | ||
| for (T client : clients.values()) { | ||
| client.write((OutPacket) packet.retainedDuplicate()); | ||
| } | ||
| } finally { | ||
| packet.release(); | ||
| lock.unlock(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Broadcasts the given packet to all clients in the set, excluding the client with the given id | ||
| * | ||
| * @param packet | ||
| * @param excludeId | ||
| */ | ||
| public void broadcastFilter(OutPacket packet, int excludeId) { | ||
| ReadLock lock = this.lock.readLock(); | ||
| // Atmost one client which would be filtered in theory | ||
| // TODO: check if that is a good idea, because that's not always | ||
| // if the exclude id is another char | ||
| if (clients.size() <= 1) { | ||
| packet.release(); | ||
| return; | ||
| } | ||
| try { | ||
| for (Entry<Integer, T> e : clients.entrySet()) { | ||
| if (e.getKey() != excludeId) { | ||
| e.getValue().write((OutPacket) packet.retainedDuplicate()); | ||
| } | ||
| } | ||
| } finally { | ||
| packet.release(); | ||
| lock.unlock(); | ||
| } | ||
| } | ||
|
|
||
| public void addClient(int id, T client) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with what i suggested it's not needed anymore |
||
| lock.writeLock().lock(); | ||
| try { | ||
| clients.put(id, client); | ||
| } finally { | ||
| lock.writeLock().unlock(); | ||
| } | ||
| } | ||
|
|
||
| public void removeClient(int id) { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. with what i suggested it's not needed anymore |
||
| lock.writeLock().lock(); | ||
| try { | ||
| clients.remove(id); | ||
| } finally { | ||
| lock.writeLock().unlock(); | ||
| } | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the offset is always 0, please inline it. no need for it to be a param.