private static void processPacket(
      PacketType packet, JPackage dirPackage, JCodeModel codeModel, boolean fromClient)
      throws JClassAlreadyExistsException {
    // get the packet info
    String packetName =
        (packet.getInfo() == null || packet.getInfo().getName() == null)
            ? "Unknown"
            : packet.getInfo().getName();
    String packetNamePrefixed = "P" + String.format("%03d", packet.getHeader()) + "_" + packetName;
    String packetDescription =
        (packet.getInfo() == null
                || packet.getInfo().getDescription() == null
                || packet.getInfo().getDescription().isEmpty())
            ? ""
            : "\n" + WordUtils.wrap(packet.getInfo().getDescription(), /* maximumLength */ 50);

    JDefinedClass packetClass =
        dirPackage._class(JMod.FINAL | JMod.PUBLIC, packetNamePrefixed)._extends(GWMessage.class);

    LOGGER.info("+-Processing packet: {}", packetNamePrefixed);
    LOGGER.debug("|+-Packet description: {}", packetDescription);

    StringBuilder packetJavadoc =
        new StringBuilder("Auto-generated by PacketCodeGen.").append(packetDescription);

    JDocComment jDocComment = packetClass.javadoc();
    jDocComment.add(packetJavadoc.toString());

    AtomicInteger numberOfUnknowns = new AtomicInteger(); // unknown field number

    // get all fields in this packet
    for (FieldType field : packet.getField()) {
      processField(field, packetClass, codeModel, numberOfUnknowns, fromClient);
    }

    // generate the header method
    JMethod headerMeth = packetClass.method(JMod.PUBLIC, short.class, "getHeader");
    headerMeth.annotate(Override.class);
    headerMeth.body()._return(JExpr.lit(packet.getHeader().intValue()));

    // generate getters, setters
    for (JFieldVar fieldVar : packetClass.fields().values()) {
      processAccessors(fieldVar, packetClass, fromClient);
    }

    // generate the toString method
    processToString(packetClass, codeModel);
  }
  private static void processField(
      FieldType field,
      JDefinedClass packetClass,
      JCodeModel codeModel,
      AtomicInteger numberOfUnknowns,
      boolean fromClient)
      throws JClassAlreadyExistsException {
    boolean isNested =
        (field.getType() == PacketSimpleTypes.OPTIONAL)
            || (field.getType() == PacketSimpleTypes.ARRAY_STATIC)
            || (field.getType() == PacketSimpleTypes.ARRAY_VAR_SMALL)
            || (field.getType() == PacketSimpleTypes.ARRAY_VAR_BIG);

    boolean isArray =
        (field.getType() == PacketSimpleTypes.BUFFER_STATIC)
            || (field.getType() == PacketSimpleTypes.BUFFER_VAR_SMALL)
            || (field.getType() == PacketSimpleTypes.BUFFER_VAR_BIG)
            || (isNested && (field.getType() != PacketSimpleTypes.OPTIONAL));

    boolean isStaticLength =
        (isArray)
            && ((field.getType() == PacketSimpleTypes.ARRAY_STATIC)
                || (field.getType() == PacketSimpleTypes.BUFFER_STATIC));

    // length is either not set for non arrays and static arrays,
    // its 1byte for small stuff and 2bytes for big stuff.
    int prefixLength =
        (!isArray || isStaticLength)
            ? -1
            : (field.getType() == PacketSimpleTypes.ARRAY_VAR_SMALL)
                    || (field.getType() == PacketSimpleTypes.BUFFER_VAR_SMALL)
                ? 1
                : 2;

    String name = "";

    if (field.getInfo() == null || field.getInfo().getName() == null) {
      // check if we got special fields...
      if (field.getType() == PacketSimpleTypes.AGENTID) {
        name = "AgentID";
      } else if (field.getType() == PacketSimpleTypes.OPTIONAL) {
        name = "Optional";
      } else {
        name = "Unknown";
      }

      name += numberOfUnknowns.incrementAndGet();
    } else {
      name = field.getInfo().getName();
    }

    String fieldName = WordUtils.uncapitalize(name);
    String fieldDescription =
        (field.getInfo() == null
                || field.getInfo().getDescription() == null
                || field.getInfo().getDescription().isEmpty())
            ? ""
            : "\n" + WordUtils.wrap(field.getInfo().getDescription(), /* maximumLength */ 50);

    JType fieldType;

    if (isNested) {
      JDefinedClass nestedClass =
          packetClass
              ._class(JMod.FINAL | JMod.STATIC | JMod.PUBLIC, "Nested" + name)
              ._implements(NestedMarker.class);

      AtomicInteger numberOfUnknownsNested = new AtomicInteger();

      for (FieldType nested : field.getField()) {
        processField(nested, nestedClass, codeModel, numberOfUnknownsNested, fromClient);
      }

      // generate getters, setters
      for (JFieldVar fieldVar : nestedClass.fields().values()) {
        processAccessors(fieldVar, nestedClass, fromClient);
      }

      processToString(nestedClass, codeModel);

      // nested classes are either arrays or optional...
      // meaning we will later have to test if they are null before reading/writing
      fieldType = isArray ? nestedClass.array() : nestedClass;
    } else {
      Class<?> fieldClass = convertFieldTypeToClass(field);
      fieldType = codeModel._ref(fieldClass);
    }

    LOGGER.debug("|+-Processing field: {}, of type: {}", fieldName, fieldType);

    // add the field
    JFieldVar packetField = packetClass.field(JMod.PRIVATE, fieldType, fieldName);

    if (fieldDescription != null && !fieldDescription.trim().equals("")) {
      JDocComment jDocComment = packetField.javadoc();
      jDocComment.add(fieldDescription);
    }

    // and dont forget array annotations if necessary
    if (isArray) {
      int size = (int) (field.getElements() == null ? -1 : field.getElements());

      packetField
          .annotate(IsArray.class)
          .param("constant", isStaticLength)
          .param("size", size)
          .param("prefixLength", prefixLength);
    }

    // or any special annotations
    if (field.getType() == PacketSimpleTypes.LONG) {
      packetField.annotate(IsInt64.class);
    }
    if (field.getType() == PacketSimpleTypes.VARINT) {
      packetField.annotate(IsVarInt.class);
    }
    if (field.getType() == PacketSimpleTypes.ASCII) {
      packetField.annotate(IsASCII.class);
    }
  }