@Override
 public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion) {
   if (protocolVersion < ProtocolConstants.MINECRAFT_SNAPSHOT) {
     items = new Item[1];
     Item item = items[0] = new Item();
     item.displayName = item.username = readString(buf);
     action = !buf.readBoolean() ? Action.REMOVE_PLAYER : Action.ADD_PLAYER;
     item.ping = buf.readShort();
   } else {
     action = Action.values()[DefinedPacket.readVarInt(buf)];
     items = new Item[DefinedPacket.readVarInt(buf)];
     for (int i = 0; i < items.length; i++) {
       Item item = items[i] = new Item();
       item.setUuid(DefinedPacket.readUUID(buf));
       switch (action) {
         case ADD_PLAYER:
           item.username = DefinedPacket.readString(buf);
           item.properties = new String[DefinedPacket.readVarInt(buf)][];
           for (int j = 0; j < item.properties.length; j++) {
             String name = DefinedPacket.readString(buf);
             String value = DefinedPacket.readString(buf);
             if (buf.readBoolean()) {
               item.properties[j] = new String[] {name, value, DefinedPacket.readString(buf)};
             } else {
               item.properties[j] = new String[] {name, value};
             }
           }
           item.gamemode = DefinedPacket.readVarInt(buf);
           item.ping = DefinedPacket.readVarInt(buf);
           if (buf.readBoolean()) {
             item.displayName = DefinedPacket.readString(buf);
           }
           break;
         case UPDATE_GAMEMODE:
           item.gamemode = DefinedPacket.readVarInt(buf);
           break;
         case UPDATE_LATENCY:
           item.ping = DefinedPacket.readVarInt(buf);
           break;
         case UPDATE_DISPLAY_NAME:
           if (buf.readBoolean()) {
             item.displayName = DefinedPacket.readString(buf);
           }
       }
     }
   }
 }
  @Override
  public void rewriteServerbound(ByteBuf packet, int oldId, int newId) {
    super.rewriteServerbound(packet, oldId, newId);
    // Special cases
    int readerIndex = packet.readerIndex();
    int packetId = DefinedPacket.readVarInt(packet);
    int packetIdLength = packet.readerIndex() - readerIndex;

    if (packetId == 0x18 /* Spectate */) {
      UUID uuid = DefinedPacket.readUUID(packet);
      ProxiedPlayer player;
      if ((player = BungeeCord.getInstance().getPlayer(uuid)) != null) {
        int previous = packet.writerIndex();
        packet.readerIndex(readerIndex);
        packet.writerIndex(readerIndex + packetIdLength);
        DefinedPacket.writeUUID(
            ((UserConnection) player).getPendingConnection().getOfflineId(), packet);
        packet.writerIndex(previous);
      }
    }
    packet.readerIndex(readerIndex);
  }
  @Override
  @SuppressFBWarnings("DLS_DEAD_LOCAL_STORE")
  public void rewriteClientbound(ByteBuf packet, int oldId, int newId) {
    super.rewriteClientbound(packet, oldId, newId);

    // Special cases
    int readerIndex = packet.readerIndex();
    int packetId = DefinedPacket.readVarInt(packet);
    int packetIdLength = packet.readerIndex() - readerIndex;
    if (packetId == 0x0D /* Collect Item */) {
      DefinedPacket.readVarInt(packet);
      rewriteVarInt(packet, oldId, newId, packet.readerIndex());
    } else if (packetId == 0x1B /* Attach Entity */) {
      rewriteInt(packet, oldId, newId, readerIndex + packetIdLength + 4);
    } else if (packetId == 0x13 /* Destroy Entities */) {
      int count = DefinedPacket.readVarInt(packet);
      int[] ids = new int[count];
      for (int i = 0; i < count; i++) {
        ids[i] = DefinedPacket.readVarInt(packet);
      }
      packet.readerIndex(readerIndex + packetIdLength);
      packet.writerIndex(readerIndex + packetIdLength);
      DefinedPacket.writeVarInt(count, packet);
      for (int id : ids) {
        if (id == oldId) {
          id = newId;
        } else if (id == newId) {
          id = oldId;
        }
        DefinedPacket.writeVarInt(id, packet);
      }
    } else if (packetId == 0x0E /* Spawn Object */) {

      DefinedPacket.readVarInt(packet);
      int type = packet.readUnsignedByte();

      if (type == 60 || type == 90) {
        packet.skipBytes(14);
        int position = packet.readerIndex();
        int readId = packet.readInt();
        int changedId = -1;
        if (readId == oldId) {
          packet.setInt(position, newId);
          changedId = newId;
        } else if (readId == newId) {
          packet.setInt(position, oldId);
          changedId = oldId;
        }
        if (changedId != -1) {
          if (changedId == 0 && readId != 0) { // Trim off the extra data
            packet.readerIndex(readerIndex);
            packet.writerIndex(packet.readableBytes() - 6);
          } else if (changedId != 0 && readId == 0) { // Add on the extra data
            packet.readerIndex(readerIndex);
            packet.capacity(packet.readableBytes() + 6);
            packet.writerIndex(packet.readableBytes() + 6);
          }
        }
      }
    } else if (packetId == 0x0C /* Spawn Player */) {
      DefinedPacket.readVarInt(packet); // Entity ID
      int idLength = packet.readerIndex() - readerIndex - packetIdLength;
      UUID uuid = DefinedPacket.readUUID(packet);
      ProxiedPlayer player;
      if ((player = BungeeCord.getInstance().getPlayerByOfflineUUID(uuid)) != null) {
        int previous = packet.writerIndex();
        packet.readerIndex(readerIndex);
        packet.writerIndex(readerIndex + packetIdLength + idLength);
        DefinedPacket.writeUUID(player.getUniqueId(), packet);
        packet.writerIndex(previous);
      }
    } else if (packetId == 0x42 /* Combat Event */) {
      int event = packet.readUnsignedByte();
      if (event == 1 /* End Combat*/) {
        DefinedPacket.readVarInt(packet);
        rewriteInt(packet, oldId, newId, packet.readerIndex());
      } else if (event == 2 /* Entity Dead */) {
        int position = packet.readerIndex();
        rewriteVarInt(packet, oldId, newId, packet.readerIndex());
        packet.readerIndex(position);
        DefinedPacket.readVarInt(packet);
        rewriteInt(packet, oldId, newId, packet.readerIndex());
      }
    }
    packet.readerIndex(readerIndex);
  }