@Override
  protected void onApply(CtBehavior behavior, Bytecode bytecode) throws BadBytecode {

    bytecode = BytecodeTools.prepareMethodForBytecode(behavior, bytecode);

    // loop through the opcodes and change any GT/GE opcodes to ICMPGT/ICMPGE
    CodeIterator iterator = behavior.getMethodInfo().getCodeAttribute().iterator();
    while (iterator.hasNext()) {
      int index = iterator.next();
      int opcode = iterator.byteAt(index);
      switch (opcode) {
        case Opcode.IFGT:

          // overwrite the opcode
          iterator.writeByte(Opcode.IF_ICMPGT, index);

          // insert the method call
          iterator.insertAt(index, bytecode.get());
          behavior.getMethodInfo().getCodeAttribute().computeMaxStack();

          break;

        case Opcode.IFGE:

          // overwrite the opcode
          iterator.writeByte(Opcode.IF_ICMPGE, index);

          // insert the method call
          iterator.insertAt(index, bytecode.get());
          behavior.getMethodInfo().getCodeAttribute().computeMaxStack();

          break;
      }
    }
  }
    public static void rewriteFakeMethod(CodeIterator methodBody, String methodDescriptor) {
      String ret = DescriptorUtils.getReturnType(methodDescriptor);
      // if the return type is larger than one then it is not a primitive
      // so it does not need to be boxed
      if (ret.length() != 1) {
        return;
      }
      byte ar = (byte) Opcode.ARETURN;
      byte[] areturn = {ar};
      // void methods are special
      if (ret.equals("V")) {

        while (methodBody.hasNext()) {
          try {
            int index = methodBody.next();
            int opcode = methodBody.byteAt(index);
            // replace a RETURN opcode with
            // ACONST_NULL
            // ARETURN
            // to return a null value
            if (opcode == Opcode.RETURN) {
              Bytecode code = new Bytecode(methodBody.get().getConstPool());
              code.add(Opcode.ACONST_NULL);
              code.add(Opcode.ARETURN);
              methodBody.insertAt(index, code.get());
            }
          } catch (BadBytecode e) {
            throw new RuntimeException(e);
          }
        }
      } else {
        while (methodBody.hasNext()) {
          try {
            int index = methodBody.next();
            int opcode = methodBody.byteAt(index);

            switch (opcode) {
              case Opcode.IRETURN:
              case Opcode.LRETURN:
              case Opcode.DRETURN:
              case Opcode.FRETURN:
                // write a NOP over the old return instruction
                // insert the boxing code to get an object on the stack
                Bytecode b = new Bytecode(methodBody.get().getConstPool());
                Boxing.box(b, ret.charAt(0));
                b.addOpcode(Opcode.ARETURN);
                methodBody.insertAt(index, b.get());
            }
          } catch (BadBytecode e) {
            throw new RuntimeException(e);
          }
        }
      }
    }
  private void initExtraHarvest() {
    try {
      CtClass terraForming =
          HookManager.getInstance()
              .getClassPool()
              .get("com.wurmonline.server.behaviours.Terraforming");

      CtClass[] paramTypes = {
        HookManager.getInstance().getClassPool().get("com.wurmonline.server.creatures.Creature"),
        CtPrimitiveType.intType,
        CtPrimitiveType.intType,
        CtPrimitiveType.booleanType,
        CtPrimitiveType.intType,
        CtPrimitiveType.floatType,
        HookManager.getInstance().getClassPool().get("com.wurmonline.server.items.Item")
      };

      CtMethod method =
          terraForming.getMethod(
              "harvest", Descriptor.ofMethod(CtPrimitiveType.booleanType, paramTypes));
      MethodInfo methodInfo = method.getMethodInfo();
      CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
      CodeIterator codeIterator = codeAttribute.iterator();

      LocalVariableAttribute attr =
          (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);
      int quantityIndex = -1;
      for (int i = 0; i < attr.tableLength(); i++) {
        if ("quantity".equals(attr.variableName(i))) {
          quantityIndex = attr.index(i);
        }
      }

      if (quantityIndex == -1) {
        throw new HookException("Quantity variable can not be resolved");
      }

      while (codeIterator.hasNext()) {
        int pos = codeIterator.next();
        int op = codeIterator.byteAt(pos);
        if (op == CodeIterator.ISTORE) {
          int fieldRefIdx = codeIterator.byteAt(pos + 1);
          if (quantityIndex == fieldRefIdx) {
            Bytecode bytecode = new Bytecode(codeIterator.get().getConstPool());
            bytecode.addIconst(extraHarvest);
            bytecode.add(Bytecode.IADD);
            codeIterator.insertAt(pos, bytecode.get());
            break;
          }
        }
      }
    } catch (NotFoundException | BadBytecode e) {
      throw new HookException(e);
    }
  }