diff --git a/src/main/java/emu/lunarcore/server/game/GameSession.java b/src/main/java/emu/lunarcore/server/game/GameSession.java index e8dcf57..85887b7 100644 --- a/src/main/java/emu/lunarcore/server/game/GameSession.java +++ b/src/main/java/emu/lunarcore/server/game/GameSession.java @@ -16,6 +16,7 @@ import it.unimi.dsi.fastutil.ints.Int2LongOpenHashMap; import kcp.highway.Ukcp; import lombok.AccessLevel; import lombok.Getter; +import us.hebi.quickbuf.ProtoMessage; @Getter public class GameSession { @@ -176,7 +177,7 @@ public class GameSession { // Log if (LunarCore.getConfig().getLogOptions().packets) { if (!(LunarCore.getConfig().getLogOptions().filterLoopingPackets && CmdIdUtils.LOOP_PACKETS.contains(cmdId))) { - logPacket("RECV", cmdId, null); + logPacket("RECV", cmdId, Utils.EMPTY_BYTE_ARRAY); } } } @@ -189,6 +190,10 @@ public class GameSession { buf.release(); } } + + public void logPacket(String sendOrRecv, int opcode, ProtoMessage payload) { + logPacket(sendOrRecv, opcode, payload != null ? payload.toByteArray() : Utils.EMPTY_BYTE_ARRAY); + } public void logPacket(String sendOrRecv, int opcode, byte[] payload) { LunarCore.getLogger().info(sendOrRecv + ": " + CmdIdUtils.getCmdIdName(opcode) + " (" + opcode + ")" + System.lineSeparator() + Utils.bytesToHex(payload)); diff --git a/src/main/java/emu/lunarcore/server/packet/BasePacket.java b/src/main/java/emu/lunarcore/server/packet/BasePacket.java index 50e85f9..a14d2eb 100644 --- a/src/main/java/emu/lunarcore/server/packet/BasePacket.java +++ b/src/main/java/emu/lunarcore/server/packet/BasePacket.java @@ -1,10 +1,12 @@ package emu.lunarcore.server.packet; -import java.io.ByteArrayOutputStream; import java.io.IOException; +import emu.lunarcore.util.Utils; import lombok.Getter; import us.hebi.quickbuf.ProtoMessage; +import us.hebi.quickbuf.ProtoSink; +import us.hebi.quickbuf.ProtoSource; @Getter public class BasePacket { @@ -12,67 +14,119 @@ public class BasePacket { public static final int TAIL_CONST = 0xd7a152c8; private int cmdId; - private byte[] data; + private ProtoMessage data; public BasePacket(int cmdId) { this.cmdId = cmdId; } - public BasePacket(int cmdId, byte[] data) { - this.cmdId = cmdId; - this.data = data; - } - public void setCmdId(int cmdId) { this.cmdId = cmdId; } - + public void setData(byte[] data) { - this.data = data; + this.data = new RawProto(data); } public void setData(ProtoMessage proto) { - this.data = proto.toByteArray(); + this.data = proto; } public byte[] build() { - if (getData() == null) { - this.data = new byte[0]; + // Setup + int protoSize = 0; + + // Set proto message size + if (this.data != null) { + protoSize = this.data.getSerializedSize(); + } + + // Create proto sink + byte[] packet = new byte[16 + protoSize]; + ProtoSink output = ProtoSink.newInstance(packet, 0, packet.length); + + try { + // Write packet header + this.writeUint32(output, HEADER_CONST); + this.writeUint16(output, cmdId); + this.writeUint16(output, 0); + this.writeUint32(output, protoSize); + + // Write protobuf message + if (this.data != null) { + this.data.writeTo(output); + } + + // Write packet footer + this.writeUint32(output, TAIL_CONST); + } catch (Exception e) { + // Should never happen } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(4 + 2 + 4 + getData().length + 4); - - this.writeUint32(baos, HEADER_CONST); - this.writeUint16(baos, cmdId); - this.writeUint16(baos, 0); // Empty header - this.writeUint32(baos, data.length); - this.writeBytes(baos, data); - this.writeUint32(baos, TAIL_CONST); - - byte[] packet = baos.toByteArray(); return packet; } - private void writeUint16(ByteArrayOutputStream baos, int i) { + private void writeUint16(ProtoSink out, int i) throws Exception { // Unsigned short - baos.write((byte) ((i >>> 8) & 0xFF)); - baos.write((byte) (i & 0xFF)); + out.writeRawByte((byte) ((i >>> 8) & 0xFF)); + out.writeRawByte((byte) (i & 0xFF)); } - private void writeUint32(ByteArrayOutputStream baos, int i) { + private void writeUint32(ProtoSink out, int i) throws Exception { // Unsigned int (long) - baos.write((byte) ((i >>> 24) & 0xFF)); - baos.write((byte) ((i >>> 16) & 0xFF)); - baos.write((byte) ((i >>> 8) & 0xFF)); - baos.write((byte) (i & 0xFF)); + out.writeRawByte((byte) ((i >>> 24) & 0xFF)); + out.writeRawByte((byte) ((i >>> 16) & 0xFF)); + out.writeRawByte((byte) ((i >>> 8) & 0xFF)); + out.writeRawByte((byte) (i & 0xFF)); } - - private void writeBytes(ByteArrayOutputStream baos, byte[] bytes) { - try { - baos.write(bytes); - } catch (IOException e) { - e.printStackTrace(); + + /** + * A byte array wrapped in a ProtoMessage object + */ + @SuppressWarnings("rawtypes") + private static class RawProto extends ProtoMessage { + private byte[] data; + + public RawProto(byte[] data) { + this.data = data; } + + @Override + public ProtoMessage copyFrom(ProtoMessage other) { + data = other.toByteArray(); + return this; + } + + @Override + public ProtoMessage clear() { + data = Utils.EMPTY_BYTE_ARRAY; + return this; + } + + @Override + protected int computeSerializedSize() { + return data.length; + } + + @Override + public void writeTo(ProtoSink output) throws IOException { + output.writeRawBytes(data); + } + + @Override + public ProtoMessage mergeFrom(ProtoSource input) throws IOException { + return this; // Skip + } + + @Override + public boolean equals(Object obj) { + return false; // Skip + } + + @Override + public ProtoMessage clone() { + return null; // Skip + } + } } diff --git a/src/main/java/emu/lunarcore/util/Utils.java b/src/main/java/emu/lunarcore/util/Utils.java index a4a959b..b562dca 100644 --- a/src/main/java/emu/lunarcore/util/Utils.java +++ b/src/main/java/emu/lunarcore/util/Utils.java @@ -10,13 +10,14 @@ import java.util.concurrent.ThreadLocalRandom; public class Utils { private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); - + public static final Object EMPTY_OBJECT = new Object(); public static final int[] EMPTY_ARRAY = new int[0]; + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; public static final String EMPTY_STRING = ""; public static String bytesToHex(byte[] bytes) { - if (bytes == null) return ""; + if (bytes == null || bytes.length == 0) return EMPTY_STRING; char[] hexChars = new char[bytes.length * 2]; for (int j = 0; j < bytes.length; j++) { int v = bytes[j] & 0xFF;