From c76670b6f1b1b7abd2660bd8521cd57fc27021a8 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 2 Mar 2024 12:34:47 +0100 Subject: [PATCH 1/4] Use byte buffer for packets to avoid copying --- .../connection/crypto/ShandaCipher.java | 4 +- .../connection/netty/BaseAcceptor.java | 39 ++++---- .../connection/netty/LoginAcceptor.java | 2 +- .../connection/netty/NettyClient.java | 25 ----- .../connection/netty/PacketDecoder.java | 9 +- .../connection/netty/PacketEncoder.java | 59 ++++++++---- .../connection/packet/InPacket.java | 26 +----- .../connection/packet/OutPacket.java | 80 ++++------------ .../SpringStory/connection/packet/Packet.java | 92 +++++++++++++------ .../packet/handlers/LoginHandler.java | 2 +- .../world/fieldEntities/Field.java | 4 +- 11 files changed, 158 insertions(+), 184 deletions(-) diff --git a/src/main/java/com/dori/SpringStory/connection/crypto/ShandaCipher.java b/src/main/java/com/dori/SpringStory/connection/crypto/ShandaCipher.java index 4905a34..d58373b 100644 --- a/src/main/java/com/dori/SpringStory/connection/crypto/ShandaCipher.java +++ b/src/main/java/com/dori/SpringStory/connection/crypto/ShandaCipher.java @@ -6,8 +6,8 @@ public interface ShandaCipher { static void encryptData(ByteBuf buf, + int offset, int length) { - int offset = 0; for (int j = 0; j < 6; j++) { byte remember = 0; byte dataLength = (byte) (length & 0xFF); @@ -43,8 +43,8 @@ static void encryptData(ByteBuf buf, } static void decryptData(ByteBuf buf, + int offset, int length) { - int offset = 0; for (int j = 1; j <= 6; j++) { byte remember = 0; byte dataLength = (byte) (length & 0xFF); diff --git a/src/main/java/com/dori/SpringStory/connection/netty/BaseAcceptor.java b/src/main/java/com/dori/SpringStory/connection/netty/BaseAcceptor.java index 952a0c5..47b3798 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/BaseAcceptor.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/BaseAcceptor.java @@ -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 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 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())); // 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())); } // Wait until the server socket is closed. // In this example, this does not happen, but you can do that to gracefully diff --git a/src/main/java/com/dori/SpringStory/connection/netty/LoginAcceptor.java b/src/main/java/com/dori/SpringStory/connection/netty/LoginAcceptor.java index eaeea3b..999ab3c 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/LoginAcceptor.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/LoginAcceptor.java @@ -8,7 +8,7 @@ import static com.dori.SpringStory.constants.ServerConstants.LOGIN_PORT; // Taken from http://netty.io/wiki/user-guide-for-4.x.html -public class LoginAcceptor implements Runnable{ +public class LoginAcceptor implements Runnable { // Channel pool - public static Map channelPool = new HashMap<>(); // Logger - diff --git a/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java b/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java index 4fe53f8..3f9d64d 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java @@ -53,12 +53,6 @@ public class NettyClient { */ protected final Channel ch; - /** - * Lock regarding the encoding of packets to be sent to remote - * sessions. - */ - private final ReentrantLock lock; - /** * InPacket object for this specific session since this can help * scaling compared to keeping OutPacket for each session. @@ -68,7 +62,6 @@ public class NettyClient { public NettyClient() { ch = null; r = new InPacket(); - lock = new ReentrantLock(true); // note: lock is fair to ensure logical sequence is maintained server-side } /** @@ -79,7 +72,6 @@ public NettyClient() { public NettyClient(Channel c) { ch = c; r = new InPacket(); - lock = new ReentrantLock(true); // note: lock is fair to ensure logical sequence is maintained server-side } /** @@ -131,21 +123,4 @@ public void close() { public String getIP() { return ch.remoteAddress().toString().split(":")[0].substring(1); } - - /** - * Acquires the encoding state for this specific send IV. This is to - * prevent multiple encoding states to be possible at the same time. If - * allowed, the send IV would mutate to an unusable IV and the session would - * be dropped as a result. - */ - public final void acquireEncoderState() { - lock.lock(); - } - - /** - * Releases the encoding state for this specific send IV. - */ - public final void releaseEncodeState() { - lock.unlock(); - } } diff --git a/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java b/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java index 3ce0a32..be5b849 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java @@ -1,5 +1,6 @@ package com.dori.SpringStory.connection.netty; +import com.dori.SpringStory.connection.crypto.InitializationVector; import com.dori.SpringStory.connection.crypto.ShandaCipher; import com.dori.SpringStory.connection.crypto.ShroomAESCipher; import com.dori.SpringStory.connection.packet.InPacket; @@ -15,13 +16,17 @@ public class PacketDecoder extends ReplayingDecoder { private static final Logger log = new Logger(PacketDecoder.class); + public static final int MAX_PACKET_LEN = 2 * 4096; private final ShroomAESCipher receiveCypher; + public PacketDecoder(InitializationVector iv, short version) { + this(new ShroomAESCipher(iv, version)); + } + public PacketDecoder(ShroomAESCipher receiveCypher) { super(-1); - this.receiveCypher = receiveCypher; } @@ -41,7 +46,7 @@ protected void decode(ChannelHandlerContext chc, this.checkpoint(-1); receiveCypher.crypt(pktBuf, 0, packetLength); if (ENABLE_ENCRYPTION) { - ShandaCipher.decryptData(pktBuf, packetLength); + ShandaCipher.decryptData(pktBuf, 0, packetLength); } out.add(new InPacket(pktBuf)); } diff --git a/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java b/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java index 64c4570..a66457c 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java @@ -1,5 +1,6 @@ package com.dori.SpringStory.connection.netty; +import com.dori.SpringStory.connection.crypto.InitializationVector; import com.dori.SpringStory.connection.crypto.ShandaCipher; import com.dori.SpringStory.connection.crypto.ShroomAESCipher; import com.dori.SpringStory.connection.packet.OutPacket; @@ -12,6 +13,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.locks.ReentrantLock; import static com.dori.SpringStory.constants.ServerConstants.ENABLE_ENCRYPTION; @@ -26,46 +28,63 @@ public final class PacketEncoder extends MessageToByteEncoder { private static final Logger logger = new Logger(PacketEncoder.class); private static final Map outPacketHeaders = new HashMap<>(); - private final ShroomAESCipher sendCypher; + private final ShroomAESCipher cipher; + private final ReentrantLock lock; + private boolean first; + + public PacketEncoder(InitializationVector iv, short version) { + this(new ShroomAESCipher(iv, version)); + } public PacketEncoder(ShroomAESCipher sendCypher) { - this.sendCypher = sendCypher; + this.cipher = sendCypher; + this.first = true; + this.lock = new ReentrantLock(true); } public static void initOutPacketOpcodesHandling() { Arrays.stream(OutHeader.values()).forEach(opcode -> outPacketHeaders.put(opcode.getValue(), opcode)); } + void encodeToBuffer(OutPacket pkt, ByteBuf out) { + int len = pkt.getLength(); + out.writeIntLE(cipher.encodeHeader(len)); + int ix = out.writerIndex(); + out.writeBytes(pkt.content()); + + // Encrypt shanda + if (ENABLE_ENCRYPTION) { + ShandaCipher.encryptData(out, ix, len); + } + + // Encrypt aes + cipher.crypt(out, ix, len); + } + @Override - protected void encode(ChannelHandlerContext chc, OutPacket outPacket, ByteBuf bb) { + protected void encode(ChannelHandlerContext chc, OutPacket pkt, ByteBuf out) { try { - ByteBuf bufferData = outPacket.getBufferData(); - int len = bufferData.readableBytes(); - NettyClient c = chc.channel().attr(NettyClient.CLIENT_KEY).get(); - if (c != null) { - OutHeader outHeader = outPacketHeaders.get(outPacket.getHeader()); + // Skip encryption for the first packet(Handshake) + if (!first) { + OutHeader outHeader = outPacketHeaders.get(pkt.getHeader()); if (!OutHeader.isSpamHeader(outHeader)) { - logger.sent(String.valueOf(outPacket.getHeader()), "0x" + Integer.toHexString(outPacket.getHeader()).toUpperCase(), outHeader.name(), outPacket.toString()); - } - bb.writeIntLE(sendCypher.encodeHeader(len)); - if (ENABLE_ENCRYPTION) { - ShandaCipher.encryptData(bufferData, len); + logger.sent(String.valueOf(pkt.getHeader()), "0x" + Integer.toHexString(pkt.getHeader()).toUpperCase(), outHeader.name(), pkt.toString()); } - sendCypher.crypt(bufferData, 0, len); - c.acquireEncoderState(); + this.lock.lock(); try { - bb.writeBytes(bufferData); + this.encodeToBuffer(pkt, out); } finally { - c.releaseEncodeState(); + this.lock.unlock(); } } else { - logger.debug("Plain sending packet: " + outPacket); - bb.writeBytes(bufferData); + logger.debug("Plain sending packet: " + pkt); + out.writeBytes(pkt.content()); + this.first = false; } } catch (Exception e) { logger.error("Error occurred while parsing OutPacket!! ", e); } finally { - outPacket.release(); + pkt.release(); } } } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java b/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java index 8349110..65642e0 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java @@ -36,23 +36,6 @@ public InPacket(byte[] data) { this(Unpooled.copiedBuffer(data)); } - @Override - public int getLength() { - return byteBuf.readableBytes(); - } - - @Override - public byte[] getData() { - byte[] bytes = new byte[byteBuf.readableBytes()]; - byteBuf.duplicate().readBytes(bytes); - return bytes; - } - - @Override - public InPacket clone() { - return new InPacket(byteBuf); - } - /** * Reads a single byte of the ByteBuf. * @return The byte that has been read. @@ -119,7 +102,8 @@ public String decodeString() { @Override public String toString() { - return MapleUtils.readableByteArray(Arrays.copyOfRange(getData(), getData().length - getUnreadAmount(), getData().length)); // Substring after copy of range xd + return super.toString(); + //return MapleUtils.readableByteArray(Arrays.copyOfRange(getData(), getData().length - getUnreadAmount(), getData().length)); // Substring after copy of range xd } @@ -163,10 +147,6 @@ public int getUnreadAmount() { return byteBuf.readableBytes(); } - public void release() { - byteBuf.release(); - } - /** * Reads a rectangle (int l, int t, int r, int b) and returns this. * @return The rectangle that has been read. @@ -176,6 +156,6 @@ public Rect decodeIntRect() { } public boolean decodeBool(){ - return decodeByte() > 0; + return decodeByte() != 0; } } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java b/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java index dd90e3e..3e01818 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java @@ -9,10 +9,8 @@ import io.netty.buffer.*; import java.time.LocalDateTime; -import java.util.Arrays; public class OutPacket extends Packet { - private ByteBuf baos; private boolean loopback = false; private boolean encryptedByShanda = false; private short op; @@ -24,8 +22,7 @@ public class OutPacket extends Packet { * @param op The opcode of this OutPacket. */ public OutPacket(short op) { - super(new byte[]{}); - baos = Unpooled.buffer(); + super(Unpooled.buffer()); encodeShort(op); this.op = op; } @@ -43,8 +40,7 @@ public OutPacket(int op) { * Creates a new OutPacket, and initializes the data as empty. */ public OutPacket() { - super(new byte[]{}); - baos = Unpooled.buffer(); + super(Unpooled.buffer()); } /** @@ -53,10 +49,8 @@ public OutPacket() { * @param data The data this net.swordie.ms.connection.packet has to be initialized with. */ public OutPacket(byte[] data, short opcode) { - super(data); + super(Unpooled.wrappedBuffer(data)); op = opcode; - baos = Unpooled.buffer(); - encodeArr(data); } /** @@ -67,6 +61,10 @@ public OutPacket(byte[] data, short opcode) { public OutPacket(OutHeader header) { this(header.getValue()); } + + public InPacket toInPacket() { + return new InPacket(buf); + } /** * Returns the header of this OutPacket. @@ -93,7 +91,7 @@ public void encodeByte(int b) { * @param b The byte to encode. */ public void encodeByte(byte b) { - baos.writeByte(b); + buf.writeByte(b); } public void encodeBool(boolean bNext) { @@ -107,7 +105,7 @@ public void encodeBool(boolean bNext) { * @param bArr The byte array to encode. */ public void encodeArr(byte[] bArr) { - baos.writeBytes(bArr); + this.buf.writeBytes(bArr); } /** @@ -125,7 +123,7 @@ public void encodeArr(String arr) { * @param c The character to encode */ public void encodeChar(char c) { - baos.writeByte(c); + buf.writeByte(c); } /** @@ -134,7 +132,7 @@ public void encodeChar(char c) { * @param b The boolean to encode (0/1) */ public void encodeByte(boolean b) { - baos.writeBoolean(b); + buf.writeBoolean(b); } /** @@ -143,15 +141,15 @@ public void encodeByte(boolean b) { * @param s The short to encode. */ public void encodeShort(short s) { - baos.writeShortLE(s); + buf.writeShortLE(s); } public void encodeShortBE(short s) { - baos.writeShort(s); + buf.writeShort(s); } public void encodeIntBE(int i) { - baos.writeInt(i); + buf.writeInt(i); } /** @@ -160,7 +158,7 @@ public void encodeIntBE(int i) { * @param i The integer to encode. */ public void encodeInt(int i) { - baos.writeIntLE(i); + buf.writeIntLE(i); } /** @@ -169,7 +167,7 @@ public void encodeInt(int i) { * @param l The long to encode. */ public void encodeLong(long l) { - baos.writeLongLE(l); + buf.writeLongLE(l); } /** @@ -187,7 +185,7 @@ public void encodeString(String s) { return; } encodeShort((short) s.length()); - baos.writeCharSequence(s, CHARSET); + buf.writeCharSequence(s, CHARSET); } /** @@ -211,41 +209,6 @@ public void encodeString(String s, short length) { } } - @Override - public void setData(byte[] nD) { - super.setData(nD); - baos.clear(); - encodeArr(nD); - } - - @Override - public byte[] getData() { - if (super.getLength() == 0) { - super.setData(ByteBufUtil.getBytes(baos)); -// baos.release(); - } - return super.getData(); - } - - public ByteBuf getBufferData() { - return this.baos; - } - - @Override - public Packet clone() { - return new OutPacket(getData(), op); - } - - /** - * Returns the length of the ByteArrayOutputStream. - * - * @return The length of baos. - */ - @Override - public int getLength() { - return getData().length; - } - public boolean isLoopback() { return loopback; } @@ -256,7 +219,7 @@ public boolean isEncryptedByShanda() { @Override public String toString() { - return MapleUtils.readableByteArray(Arrays.copyOfRange(getData(), 2, getData().length)); + return super.toString(); } public void encodeShort(int value) { @@ -318,11 +281,4 @@ public void encodeFT(LocalDateTime localDateTime) { public void encode(Encodable encodable) { encodable.encode(this); } - - @Override - public void release() { - super.release(); - this.baos.release(); - this.baos = null; - } } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/Packet.java b/src/main/java/com/dori/SpringStory/connection/packet/Packet.java index 33c5049..4ce4b5d 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/Packet.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/Packet.java @@ -17,8 +17,8 @@ */ package com.dori.SpringStory.connection.packet; -import com.dori.SpringStory.utils.MapleUtils; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufHolder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -29,54 +29,90 @@ * functionality because it is a MapleStory packet. * */ -public class Packet implements Cloneable { - - private byte[] data; +public class Packet implements ByteBufHolder { protected static final Charset CHARSET = StandardCharsets.ISO_8859_1; + protected ByteBuf buf; - public Packet(byte[] data) { - this.data = new byte[data.length]; - System.arraycopy(data, 0, this.data, 0, data.length); - } - - public Packet(ByteBuf data) { + public Packet(ByteBuf buf) { + this.buf = buf; //this.data = new byte[data.readableBytes()]; //System.arraycopy(data, 0, this.data, 0, data.length); } public int getLength() { - if (data != null) { - return data.length; - } - return 0; + return this.buf.readableBytes(); } public int getHeader() { - if (data.length < 2) { + if (this.buf.readableBytes() < 2) { return 0xFFFF; } - return (data[0] + (data[1] << 8)); + return this.buf.getShortLE(0); + } + + @Override + public String toString() { + return "[Pck] | " + this.buf.toString(); } - public void setData(byte[] nD) { - data = nD; + @Override + public int refCnt() { + return this.buf.refCnt(); } - public byte[] getData() { - return data; + @Override + public boolean release(int arg0) { + return this.buf.release(arg0); } - + @Override - public String toString() { - if (data == null) return ""; - return "[Pck] | " + MapleUtils.readableByteArray(data); + public ByteBuf content() { + return this.buf; } - + + @Override + public Packet copy() { + return new Packet(this.buf.copy()); + } + + @Override + public Packet duplicate() { + return new Packet(this.buf.duplicate()); + } + + @Override + public ByteBufHolder retainedDuplicate() { + return new Packet(this.buf.retainedDuplicate()); + } + @Override - public Packet clone() { - return new Packet(data); + public ByteBufHolder replace(ByteBuf content) { + return new Packet(content); } - public void release(){} + @Override + public ByteBufHolder retain() { + return new Packet(this.buf.retain()); + } + + @Override + public ByteBufHolder retain(int increment) { + return new Packet(this.buf.retain(increment)); + } + + @Override + public ByteBufHolder touch() { + return new Packet(this.buf.touch()); + } + + @Override + public ByteBufHolder touch(Object hint) { + return new Packet(this.buf.touch(hint)); + } + + @Override + public boolean release() { + return this.buf.release(); + } } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java b/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java index 4980fce..aa4cc74 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java @@ -187,7 +187,7 @@ public static void handleCreateSecurityHandle(MapleClient c, InPacket inPacket) outPacket.encodeString(AUTO_LOGIN_PASSWORD); outPacket.encodeArr(new byte[27]); - handleLoginPassword(c, new InPacket(outPacket.getData())); + handleLoginPassword(c, outPacket.toInPacket()); } } } diff --git a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java index 61e5c76..a19b234 100644 --- a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java +++ b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java @@ -265,7 +265,7 @@ public void removeMob(int objId) { } public void broadcastPacket(OutPacket outPacket) { - getPlayers().values().forEach(chr -> chr.write((OutPacket) outPacket.clone())); + getPlayers().values().forEach(chr -> chr.write((OutPacket) outPacket.retainedDuplicate())); } public void broadcastPacket(OutPacket outPacket, MapleChar exceptChr) { @@ -274,7 +274,7 @@ public void broadcastPacket(OutPacket outPacket, MapleChar exceptChr) { getPlayers().values().forEach( chr -> { if (chr.getId() != exceptChr.getId()) { - chr.write((OutPacket) outPacket.clone()); + chr.write((OutPacket) outPacket.retainedDuplicate()); } } ); From dc04510dba05855fdd6a2449eaad5716c2401bf3 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 2 Mar 2024 13:11:31 +0100 Subject: [PATCH 2/4] Enforce outpacket containing a header, clean up some smaller deprecated stuff, introduce a braodcast set --- .../connection/netty/BroadcastSet.java | 77 +++++++++++++++++++ .../connection/netty/NettyClient.java | 1 - .../connection/netty/PacketDecoder.java | 16 ++-- .../connection/netty/PacketEncoder.java | 16 ++-- .../connection/packet/InPacket.java | 3 - .../connection/packet/OutPacket.java | 50 +----------- .../SpringStory/connection/packet/Packet.java | 4 +- .../packet/handlers/LoginHandler.java | 7 +- .../connection/packet/packets/CLogin.java | 3 +- .../world/fieldEntities/Field.java | 60 ++++++++++----- 10 files changed, 137 insertions(+), 100 deletions(-) create mode 100644 src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java diff --git a/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java b/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java new file mode 100644 index 0000000..9ff26d2 --- /dev/null +++ b/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java @@ -0,0 +1,77 @@ +package com.dori.SpringStory.connection.netty; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; + +import com.dori.SpringStory.connection.packet.OutPacket; + +public class BroadcastSet { + private final Map 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(); + try { + for (Entry 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) { + lock.writeLock().lock(); + try { + clients.put(id, client); + } finally { + lock.writeLock().unlock(); + } + } + + public void removeClient(int id) { + lock.writeLock().lock(); + try { + clients.remove(id); + } finally { + lock.writeLock().unlock(); + } + } + +} diff --git a/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java b/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java index 3f9d64d..d6fc529 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/NettyClient.java @@ -23,7 +23,6 @@ import io.netty.util.AttributeKey; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.locks.ReentrantLock; /** * Abstraction for Netty channels that contains some attribute keys diff --git a/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java b/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java index be5b849..3f93775 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/PacketDecoder.java @@ -4,6 +4,7 @@ import com.dori.SpringStory.connection.crypto.ShandaCipher; import com.dori.SpringStory.connection.crypto.ShroomAESCipher; import com.dori.SpringStory.connection.packet.InPacket; +import com.dori.SpringStory.connection.packet.Packet; import com.dori.SpringStory.logger.Logger; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; @@ -16,18 +17,15 @@ public class PacketDecoder extends ReplayingDecoder { private static final Logger log = new Logger(PacketDecoder.class); - - public static final int MAX_PACKET_LEN = 2 * 4096; - - private final ShroomAESCipher receiveCypher; + private final ShroomAESCipher cipher; public PacketDecoder(InitializationVector iv, short version) { this(new ShroomAESCipher(iv, version)); } - public PacketDecoder(ShroomAESCipher receiveCypher) { + public PacketDecoder(ShroomAESCipher cipher) { super(-1); - this.receiveCypher = receiveCypher; + this.cipher = cipher; } @Override @@ -36,15 +34,15 @@ protected void decode(ChannelHandlerContext chc, List out) { int packetLength = state(); if(packetLength == -1) { - final int packetLen = receiveCypher.decodeHeader(inPacketData.readIntLE()); + final int packetLen = cipher.decodeHeader(inPacketData.readIntLE()); checkpoint(packetLen); - if(packetLen < 0 || packetLen > MAX_PACKET_LEN) + if(packetLen < 0 || packetLen > Packet.MAX_PKT_LEN) throw new EncoderException("Packet length out of limits"); packetLength = packetLen; } ByteBuf pktBuf = inPacketData.readRetainedSlice(packetLength); this.checkpoint(-1); - receiveCypher.crypt(pktBuf, 0, packetLength); + cipher.crypt(pktBuf, 0, packetLength); if (ENABLE_ENCRYPTION) { ShandaCipher.decryptData(pktBuf, 0, packetLength); } diff --git a/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java b/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java index a66457c..c08d782 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/PacketEncoder.java @@ -13,7 +13,6 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.locks.ReentrantLock; import static com.dori.SpringStory.constants.ServerConstants.ENABLE_ENCRYPTION; @@ -29,7 +28,6 @@ public final class PacketEncoder extends MessageToByteEncoder { private static final Logger logger = new Logger(PacketEncoder.class); private static final Map outPacketHeaders = new HashMap<>(); private final ShroomAESCipher cipher; - private final ReentrantLock lock; private boolean first; public PacketEncoder(InitializationVector iv, short version) { @@ -39,7 +37,6 @@ public PacketEncoder(InitializationVector iv, short version) { public PacketEncoder(ShroomAESCipher sendCypher) { this.cipher = sendCypher; this.first = true; - this.lock = new ReentrantLock(true); } public static void initOutPacketOpcodesHandling() { @@ -68,21 +65,18 @@ protected void encode(ChannelHandlerContext chc, OutPacket pkt, ByteBuf out) { if (!first) { OutHeader outHeader = outPacketHeaders.get(pkt.getHeader()); if (!OutHeader.isSpamHeader(outHeader)) { - logger.sent(String.valueOf(pkt.getHeader()), "0x" + Integer.toHexString(pkt.getHeader()).toUpperCase(), outHeader.name(), pkt.toString()); - } - this.lock.lock(); - try { - this.encodeToBuffer(pkt, out); - } finally { - this.lock.unlock(); + logger.sent(String.valueOf(pkt.getHeader()), + "0x" + Integer.toHexString(pkt.getHeader()).toUpperCase(), outHeader.name(), + pkt.toString()); } + this.encodeToBuffer(pkt, out); } else { logger.debug("Plain sending packet: " + pkt); out.writeBytes(pkt.content()); this.first = false; } } catch (Exception e) { - logger.error("Error occurred while parsing OutPacket!! ", e); + logger.error("Error occurred while enncoding OutPacket!", e); } finally { pkt.release(); } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java b/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java index 65642e0..c558f8e 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/InPacket.java @@ -1,12 +1,9 @@ package com.dori.SpringStory.connection.packet; -import com.dori.SpringStory.utils.MapleUtils; import com.dori.SpringStory.utils.utilEntities.Position; import com.dori.SpringStory.utils.utilEntities.Rect; import io.netty.buffer.*; -import java.util.Arrays; - public class InPacket extends Packet { private final ByteBuf byteBuf; diff --git a/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java b/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java index 3e01818..4ca671a 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/OutPacket.java @@ -11,11 +11,7 @@ import java.time.LocalDateTime; public class OutPacket extends Packet { - private boolean loopback = false; - private boolean encryptedByShanda = false; - private short op; private static final Logger log = new Logger(OutPacket.class); - /** * Creates a new OutPacket with a given op. Immediately encodes the op. * @@ -24,7 +20,6 @@ public class OutPacket extends Packet { public OutPacket(short op) { super(Unpooled.buffer()); encodeShort(op); - this.op = op; } /** @@ -36,23 +31,6 @@ public OutPacket(int op) { this((short) op); } - /** - * Creates a new OutPacket, and initializes the data as empty. - */ - public OutPacket() { - super(Unpooled.buffer()); - } - - /** - * Creates a new OutPacket with given data. - * - * @param data The data this net.swordie.ms.connection.packet has to be initialized with. - */ - public OutPacket(byte[] data, short opcode) { - super(Unpooled.wrappedBuffer(data)); - op = opcode; - } - /** * Creates a new OutPacket with a given header. Immediately encodes the header's short value. * @@ -61,21 +39,11 @@ public OutPacket(byte[] data, short opcode) { public OutPacket(OutHeader header) { this(header.getValue()); } - + public InPacket toInPacket() { return new InPacket(buf); } - /** - * Returns the header of this OutPacket. - * - * @return the header of this OutPacket. - */ - @Override - public int getHeader() { - return op; - } - /** * Encodes a single byte to this OutPacket. * @@ -144,14 +112,6 @@ public void encodeShort(short s) { buf.writeShortLE(s); } - public void encodeShortBE(short s) { - buf.writeShort(s); - } - - public void encodeIntBE(int i) { - buf.writeInt(i); - } - /** * Encodes an integer to this OutPacket, in little endian. * @@ -209,14 +169,6 @@ public void encodeString(String s, short length) { } } - public boolean isLoopback() { - return loopback; - } - - public boolean isEncryptedByShanda() { - return encryptedByShanda; - } - @Override public String toString() { return super.toString(); diff --git a/src/main/java/com/dori/SpringStory/connection/packet/Packet.java b/src/main/java/com/dori/SpringStory/connection/packet/Packet.java index 4ce4b5d..8f3552e 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/Packet.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/Packet.java @@ -30,13 +30,13 @@ * */ public class Packet implements ByteBufHolder { + public static final int MAX_PKT_LEN = 4096 * 2; + protected static final int MAX_BUF_LEN = 2048; protected static final Charset CHARSET = StandardCharsets.ISO_8859_1; protected ByteBuf buf; public Packet(ByteBuf buf) { this.buf = buf; - //this.data = new byte[data.readableBytes()]; - //System.arraycopy(data, 0, this.data, 0, data.length); } public int getLength() { diff --git a/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java b/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java index aa4cc74..aff53ab 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/handlers/LoginHandler.java @@ -182,12 +182,15 @@ public static void handleCharSelect(MapleClient c, InPacket inPacket) { public static void handleCreateSecurityHandle(MapleClient c, InPacket inPacket) { // If it's true will auto login as admin - if (AUTO_LOGIN) { - OutPacket outPacket = new OutPacket(); + OutPacket outPacket = new OutPacket(0); outPacket.encodeString(AUTO_LOGIN_USERNAME); outPacket.encodeString(AUTO_LOGIN_PASSWORD); outPacket.encodeArr(new byte[27]); - handleLoginPassword(c, outPacket.toInPacket()); + + InPacket pkt = outPacket.toInPacket(); + pkt.decodeShort(); // Skip the header + handleLoginPassword(c, pkt); } } } diff --git a/src/main/java/com/dori/SpringStory/connection/packet/packets/CLogin.java b/src/main/java/com/dori/SpringStory/connection/packet/packets/CLogin.java index ae45367..2d04aa6 100644 --- a/src/main/java/com/dori/SpringStory/connection/packet/packets/CLogin.java +++ b/src/main/java/com/dori/SpringStory/connection/packet/packets/CLogin.java @@ -22,8 +22,7 @@ public interface CLogin { static OutPacket sendConnect(byte[] siv, byte[] riv) { - OutPacket outPacket = new OutPacket(); - outPacket.encodeShort((short) 14); // hand-shake packet size + OutPacket outPacket = new OutPacket(14);// hand-shake packet size outPacket.encodeShort(ServerConstants.VERSION); outPacket.encodeString(ServerConstants.MINOR_VERSION); outPacket.encodeArr(riv); // IV is an int diff --git a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java index a19b234..6ea2aad 100644 --- a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java +++ b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java @@ -67,7 +67,8 @@ public Field(MapData mapData) { this.partyOnly = mapData.isPartyOnly(); this.expeditionOnly = mapData.isExpeditionOnly(); this.needSkillForFly = mapData.isNeedSkillForFly(); - this.fixedMobCapacity = mapData.getFixedMobCapacity() != 0 ? mapData.getFixedMobCapacity() : DEFAULT_FIELD_MOB_CAPACITY; + this.fixedMobCapacity = mapData.getFixedMobCapacity() != 0 ? mapData.getFixedMobCapacity() + : DEFAULT_FIELD_MOB_CAPACITY; this.createMobInterval = mapData.getCreateMobInterval(); this.timeOut = mapData.getTimeOut(); this.timeLimit = mapData.getTimeLimit(); @@ -104,8 +105,8 @@ public Field(MapData mapData) { mobsSpawnPoints.add(new PositionData(mob.getPosition(), mob.getFh())); } } else { - //TODO: see what lifes i've missed - //this.addLife(life.deepCopy()); + // TODO: see what lifes i've missed + // this.addLife(life.deepCopy()); } } this.dropsDisabled = mapData.isDropsDisabled(); @@ -152,7 +153,7 @@ public void spawnPlayer(MapleChar chr, boolean characterData) { chr.setMapId(getId()); c.write(CStage.onSetField(c.getChr(), c.getChr().getField(), (short) 0, c.getChannel(), 0, characterData, (byte) 1, (short) 0, - "", new String[]{""})); + "", new String[] { "" })); // Spawn lifes for the client - this.spawnLifesForCharacter(chr); if (firstPlayerInField) { @@ -205,11 +206,13 @@ public void spawnLifesForCharacter(MapleChar chr) { // Spawn Mobs for the client - mobs.values().forEach(mob -> chr.write(CMobPool.mobEnterField(mob))); // Spawn Drops for the client - - drops.values().forEach(drop -> chr.write(CDropPool.dropEnterField(drop, DropEnterType.INSTANT, DropOwnType.USER_OWN, drop.getOwnerID(), drop.getPosition(), (short) 0, true))); + drops.values().forEach(drop -> chr.write(CDropPool.dropEnterField(drop, DropEnterType.INSTANT, + DropOwnType.USER_OWN, drop.getOwnerID(), drop.getPosition(), (short) 0, true))); } public void assignControllerToMobs(MapleChar chr) { - //TODO: assigning a char suppose to be random and not the new char that enter the map each time! + // TODO: assigning a char suppose to be random and not the new char that enter + // the map each time! // Assign Controller to Mobs for the client - mobs.values().forEach(mob -> { @@ -219,7 +222,8 @@ public void assignControllerToMobs(MapleChar chr) { } public void assignControllerToNpcs(MapleChar chr) { - //TODO: assigning a char suppose to be random and not the new char that enter the map each time! + // TODO: assigning a char suppose to be random and not the new char that enter + // the map each time! // Assign Controller to Mobs for the client - npcs.values().forEach(npc -> { @@ -231,7 +235,8 @@ public void assignControllerToNpcs(MapleChar chr) { public void spawnMobById(int mobId, MapleChar controller) { Mob mob = MobDataHandler.getMobByID(mobId); if (mob != null) { - //TODO: i want to redo the position concept - randomize it on the initial spawn points a map have + // TODO: i want to redo the position concept - randomize it on the initial spawn + // points a map have Position pos = controller.getPosition(); mob.setPosition(pos.deepCopy()); mob.setVPosition(pos.deepCopy()); @@ -265,19 +270,28 @@ public void removeMob(int objId) { } public void broadcastPacket(OutPacket outPacket) { - getPlayers().values().forEach(chr -> chr.write((OutPacket) outPacket.retainedDuplicate())); + try { + getPlayers().values().forEach(chr -> chr.write((OutPacket) outPacket.retainedDuplicate())); + } finally { + // Release the local reference + outPacket.release(); + } } public void broadcastPacket(OutPacket outPacket, MapleChar exceptChr) { // No point broadcast a packet when you're alone in the map - - if (getPlayers().size() > 1) { - getPlayers().values().forEach( - chr -> { - if (chr.getId() != exceptChr.getId()) { - chr.write((OutPacket) outPacket.retainedDuplicate()); - } - } - ); + try { + if (getPlayers().size() > 1) { + getPlayers().values().forEach( + chr -> { + if (chr.getId() != exceptChr.getId()) { + chr.write((OutPacket) outPacket.retainedDuplicate()); + } + }); + } + } finally { + // Release the local reference + outPacket.release(); } } @@ -302,7 +316,8 @@ private Drop generateDropByMobDropData(MobDropData dropData, int ownerID, float return drop; } - public void drop(List dropsData, int srcID, int ownerID, Foothold fh, Position position, float mesoRate, float dropRate) { + public void drop(List dropsData, int srcID, int ownerID, Foothold fh, Position position, + float mesoRate, float dropRate) { int x = position.getX(); int diff = 0; int minX = position.getX(); @@ -353,8 +368,10 @@ public void spawnDrop(Drop drop, int srcID, Position srcPos, boolean isTradeable Position fromPos = new Position(drop.getPosition()); fromPos.setY(fromPos.getY() - 20); - broadcastPacket(CDropPool.dropEnterField(drop, dropEnterType, DropOwnType.USER_OWN, srcID, fromPos, replay, true)); - EventManager.addEvent(getRandomUuidInLong(), EventType.REMOVE_DROP_FROM_FIELD, new RemoveDropFromField(drop, this), DROP_REMAIN_ON_GROUND_TIME); + broadcastPacket( + CDropPool.dropEnterField(drop, dropEnterType, DropOwnType.USER_OWN, srcID, fromPos, replay, true)); + EventManager.addEvent(getRandomUuidInLong(), EventType.REMOVE_DROP_FROM_FIELD, + new RemoveDropFromField(drop, this), DROP_REMAIN_ON_GROUND_TIME); } public void spawnDrop(Drop drop, Position fromPos) { @@ -386,7 +403,8 @@ public void shutdownField() { getNpcs().values().forEach(npc -> npc.setField(null)); getNpcs().clear(); // Clear Drops - - getDrops().keySet().forEach(dropObjID -> EventManager.cancelEvent(MapleUtils.concat((long) getId(), dropObjID), EventType.REMOVE_DROP_FROM_FIELD)); + getDrops().keySet().forEach(dropObjID -> EventManager.cancelEvent(MapleUtils.concat((long) getId(), dropObjID), + EventType.REMOVE_DROP_FROM_FIELD)); getDrops().clear(); } } From 6a13c03ccf462b4707da88e43a8a32d1fd0850d6 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 2 Mar 2024 13:19:57 +0100 Subject: [PATCH 3/4] Introduce a broadcast set --- .../connection/netty/BroadcastSet.java | 8 +++++- .../world/fieldEntities/Field.java | 28 +++++-------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java b/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java index 9ff26d2..e9f40ea 100644 --- a/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java +++ b/src/main/java/com/dori/SpringStory/connection/netty/BroadcastSet.java @@ -3,7 +3,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; -import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; @@ -44,6 +43,13 @@ public void broadcast(OutPacket packet) { */ 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 e : clients.entrySet()) { if (e.getKey() != excludeId) { diff --git a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java index 6ea2aad..80edb88 100644 --- a/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java +++ b/src/main/java/com/dori/SpringStory/world/fieldEntities/Field.java @@ -2,6 +2,7 @@ import com.dori.SpringStory.client.MapleClient; import com.dori.SpringStory.client.character.MapleChar; +import com.dori.SpringStory.connection.netty.BroadcastSet; import com.dori.SpringStory.connection.packet.OutPacket; import com.dori.SpringStory.connection.packet.packets.*; import com.dori.SpringStory.constants.GameConstants; @@ -44,6 +45,7 @@ public class Field extends MapData { private long creationTime; private long deprecationStartTime; private List mobsSpawnPoints = new ArrayList<>(); + private BroadcastSet tx = new BroadcastSet<>(); public Field(int id) { super(id); @@ -148,6 +150,7 @@ public void spawnPlayer(MapleChar chr, boolean characterData) { MapleClient c = chr.getMapleClient(); // Add player to the field - players.putIfAbsent(chr.getId(), chr); + tx.addClient(chr.getId(), c); // Update for the char instance the field data - chr.setField(this); chr.setMapId(getId()); @@ -171,6 +174,7 @@ public void spawnPlayer(MapleChar chr, boolean characterData) { public void removePlayer(MapleChar chr) { players.remove(chr.getId()); + tx.removeClient(chr.getId()); } private void addNPC(Npc npc) { @@ -269,30 +273,12 @@ public void removeMob(int objId) { } } - public void broadcastPacket(OutPacket outPacket) { - try { - getPlayers().values().forEach(chr -> chr.write((OutPacket) outPacket.retainedDuplicate())); - } finally { - // Release the local reference - outPacket.release(); - } + public void broadcastPacket(OutPacket packet) { + tx.broadcast(packet); } public void broadcastPacket(OutPacket outPacket, MapleChar exceptChr) { - // No point broadcast a packet when you're alone in the map - - try { - if (getPlayers().size() > 1) { - getPlayers().values().forEach( - chr -> { - if (chr.getId() != exceptChr.getId()) { - chr.write((OutPacket) outPacket.retainedDuplicate()); - } - }); - } - } finally { - // Release the local reference - outPacket.release(); - } + tx.broadcastFilter(outPacket, id); } private Drop generateDropByMobDropData(MobDropData dropData, int ownerID, float mesoRate) { From 73f62998c46431d367144556ca9cb6346a2761d4 Mon Sep 17 00:00:00 2001 From: = Date: Sat, 2 Mar 2024 13:22:15 +0100 Subject: [PATCH 4/4] Make aes cipher static --- .../connection/crypto/ShroomAESCipher.java | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/dori/SpringStory/connection/crypto/ShroomAESCipher.java b/src/main/java/com/dori/SpringStory/connection/crypto/ShroomAESCipher.java index 4410091..c0b8bb4 100644 --- a/src/main/java/com/dori/SpringStory/connection/crypto/ShroomAESCipher.java +++ b/src/main/java/com/dori/SpringStory/connection/crypto/ShroomAESCipher.java @@ -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"); 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; }