diff --git a/modules/API/src/main/java/com/comphenix/protocol/injector/netty/WirePacket.java b/modules/API/src/main/java/com/comphenix/protocol/injector/netty/WirePacket.java index adb8fc29..7f2ac2bc 100644 --- a/modules/API/src/main/java/com/comphenix/protocol/injector/netty/WirePacket.java +++ b/modules/API/src/main/java/com/comphenix/protocol/injector/netty/WirePacket.java @@ -16,30 +16,71 @@ */ package com.comphenix.protocol.injector.netty; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.lang.reflect.Method; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import com.comphenix.protocol.utility.MinecraftMethods; +import com.comphenix.protocol.utility.MinecraftReflection; + import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; /** + * A packet represented only by its id and bytes. * @author dmulloy2 */ - public class WirePacket { private final int id; private final byte[] bytes; + /** + * Constructs a new WirePacket with a given type and contents + * @param type Type of the packet + * @param bytes Contents of the packet + */ + public WirePacket(PacketType type, byte[] bytes) { + this.id = checkNotNull(type, "type cannot be null").getCurrentId(); + this.bytes = bytes; + } + + /** + * Constructs a new WirePacket with a given id and contents + * @param id ID of the packet + * @param bytes Contents of the packet + */ public WirePacket(int id, byte[] bytes) { this.id = id; this.bytes = bytes; } + /** + * Gets this packet's ID + * @return The ID + */ public int getId() { return id; } + /** + * Gets this packet's contents as a byte array + * @return The contents + */ public byte[] getBytes() { return bytes; } + /** + * Writes the id of this packet to a given output + * @param output Output to write to + */ public void writeId(ByteBuf output) { + checkNotNull(output, "output cannot be null!"); + // From PacketDataSerializer#d(int) + int i = id; while ((i & -128) != 0) { output.writeByte(i & 127 | 128); @@ -49,7 +90,78 @@ public class WirePacket { output.writeByte(i); } + /** + * Writes the contents of this packet to a given output + * @param output Output to write to + */ public void writeBytes(ByteBuf output) { + checkNotNull(output, "output cannot be null!"); output.writeBytes(bytes); } + + /** + * Fully writes the ID and contents of this packet to a given output + * @param output Output to write to + */ + public void writeFully(ByteBuf output) { + writeId(output); + writeBytes(output); + } + + /** + * Serializes this packet into a byte buffer + * @return The buffer + */ + public ByteBuf serialize() { + ByteBuf buffer = Unpooled.buffer(); + writeFully(buffer); + return buffer; + } + + /** + * Creates a WirePacket from an existing PacketContainer + * @param packet Existing packet + * @return The resulting WirePacket + */ + public static WirePacket fromPacket(PacketContainer packet) { + checkNotNull(packet, "packet cannot be null!"); + + int id = packet.getType().getCurrentId(); + + ByteBuf buffer = PacketContainer.createPacketBuffer(); + Method write = MinecraftMethods.getPacketWriteByteBufMethod(); + + try { + write.invoke(packet.getHandle(), buffer); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException("Failed to serialize packet contents.", ex); + } + + return new WirePacket(id, buffer.array()); + } + + /** + * Creates a WirePacket from an existing Minecraft packet + * @param packet Existing Minecraft packet + * @return The resulting WirePacket + * @throws IllegalArgumentException If the packet is null or not a Minecraft packet + */ + public static WirePacket fromPacket(Object packet) { + checkNotNull(packet, "packet cannot be null!"); + checkArgument(MinecraftReflection.isPacketClass(packet), "packet must be a Minecraft packet"); + + PacketType type = PacketType.fromClass(packet.getClass()); + int id = type.getCurrentId(); + + ByteBuf buffer = PacketContainer.createPacketBuffer(); + Method write = MinecraftMethods.getPacketWriteByteBufMethod(); + + try { + write.invoke(packet, buffer); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException("Failed to serialize packet contents.", ex); + } + + return new WirePacket(id, buffer.array()); + } }