private void send(CommandSender sender, String jsonString) {
   if (!(sender instanceof Player)) {
     sender.sendMessage(toOldMessageFormat());
     return;
   }
   Player player = (Player) sender;
   try {
     Object handle = Reflection.getHandle(player);
     Object connection = Reflection.getField(handle.getClass(), "playerConnection").get(handle);
     Reflection.getMethod(connection.getClass(), "sendPacket", Reflection.getNMSClass("Packet"))
         .invoke(connection, createChatPacket(jsonString));
   } catch (IllegalArgumentException e) {
     Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
   } catch (IllegalAccessException e) {
     Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
   } catch (InstantiationException e) {
     Bukkit.getLogger().log(Level.WARNING, "Underlying class is abstract.", e);
   } catch (InvocationTargetException e) {
     Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
   } catch (NoSuchMethodException e) {
     Bukkit.getLogger().log(Level.WARNING, "Could not find method.", e);
   } catch (ClassNotFoundException e) {
     Bukkit.getLogger().log(Level.WARNING, "Could not find class.", e);
   }
 }
 /**
  * Set the behavior of the current editing component to display information about a statistic
  * parameter with an entity type when the client hovers over the text.
  *
  * <p>Tooltips do not inherit display characteristics, such as color and styles, from the message
  * component on which they are applied.
  *
  * @param which The statistic to display.
  * @param entity The sole entity type parameter to the statistic.
  * @return This builder instance.
  * @exception IllegalArgumentException If the statistic requires a parameter which was not
  *     supplied, or was supplied a parameter that was not required.
  */
 public FancyMessage statisticTooltip(final Statistic which, EntityType entity) {
   Type type = which.getType();
   if (type == Type.UNTYPED) {
     throw new IllegalArgumentException("That statistic needs no additional parameter!");
   }
   if (type != Type.ENTITY) {
     throw new IllegalArgumentException(
         "Wrong parameter type for that statistic - needs " + type + "!");
   }
   try {
     Object statistic =
         Reflection.getMethod(
                 Reflection.getOBCClass("CraftStatistic"),
                 "getEntityStatistic",
                 Statistic.class,
                 EntityType.class)
             .invoke(null, which, entity);
     return achievementTooltip(
         (String) Reflection.getField(Reflection.getNMSClass("Statistic"), "name").get(statistic));
   } catch (IllegalAccessException e) {
     Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
     return this;
   } catch (IllegalArgumentException e) {
     Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
     return this;
   } catch (InvocationTargetException e) {
     Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
     return this;
   }
 }
  private Object createChatPacket(String json)
      throws IllegalArgumentException, IllegalAccessException, InstantiationException,
          InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
    if (nmsChatSerializerGsonInstance == null) {
      // Find the field and its value, completely bypassing obfuscation
      Class<?> chatSerializerClazz;

      String version = Reflection.getVersion();
      double majorVersion = Double.parseDouble(version.replace('_', '.').substring(1, 4));
      int lesserVersion = Integer.parseInt(version.substring(6, 7));

      if (majorVersion < 1.8 || (majorVersion == 1.8 && lesserVersion == 1)) {
        chatSerializerClazz = Reflection.getNMSClass("ChatSerializer");
      } else {
        chatSerializerClazz = Reflection.getNMSClass("IChatBaseComponent$ChatSerializer");
      }

      if (chatSerializerClazz == null) {
        throw new ClassNotFoundException("Can't find the ChatSerializer class");
      }

      for (Field declaredField : chatSerializerClazz.getDeclaredFields()) {
        if (Modifier.isFinal(declaredField.getModifiers())
            && Modifier.isStatic(declaredField.getModifiers())
            && declaredField.getType().getName().endsWith("Gson")) {
          // We've found our field
          declaredField.setAccessible(true);
          nmsChatSerializerGsonInstance = declaredField.get(null);
          fromJsonMethod =
              nmsChatSerializerGsonInstance
                  .getClass()
                  .getMethod("fromJson", String.class, Class.class);
          break;
        }
      }
    }

    // Since the method is so simple, and all the obfuscated methods have the same name, it's easier
    // to reimplement 'IChatBaseComponent a(String)' than to reflectively call it
    // Of course, the implementation may change, but fuzzy matches might break with signature
    // changes
    Object serializedChatComponent =
        fromJsonMethod.invoke(
            nmsChatSerializerGsonInstance, json, Reflection.getNMSClass("IChatBaseComponent"));

    return nmsPacketPlayOutChatConstructor.newInstance(serializedChatComponent);
  }
 /**
  * Set the behavior of the current editing component to display information about an item when the
  * client hovers over the text.
  *
  * <p>Tooltips do not inherit display characteristics, such as color and styles, from the message
  * component on which they are applied.
  *
  * @param itemStack The stack for which to display information.
  * @return This builder instance.
  */
 public FancyMessage itemTooltip(final ItemStack itemStack) {
   try {
     Object nmsItem =
         Reflection.getMethod(
                 Reflection.getOBCClass("inventory.CraftItemStack"), "asNMSCopy", ItemStack.class)
             .invoke(null, itemStack);
     return itemTooltip(
         Reflection.getMethod(
                 Reflection.getNMSClass("ItemStack"),
                 "save",
                 Reflection.getNMSClass("NBTTagCompound"))
             .invoke(nmsItem, Reflection.getNMSClass("NBTTagCompound").newInstance())
             .toString());
   } catch (Exception e) {
     e.printStackTrace();
     return this;
   }
 }
  public FancyMessage(final TextualComponent firstPartText) {
    messageParts = new ArrayList<MessagePart>();
    messageParts.add(new MessagePart(firstPartText));
    jsonString = null;
    dirty = false;

    if (nmsPacketPlayOutChatConstructor == null) {
      try {
        nmsPacketPlayOutChatConstructor =
            Reflection.getNMSClass("PacketPlayOutChat")
                .getDeclaredConstructor(Reflection.getNMSClass("IChatBaseComponent"));
        nmsPacketPlayOutChatConstructor.setAccessible(true);
      } catch (NoSuchMethodException e) {
        Bukkit.getLogger().log(Level.SEVERE, "Could not find Minecraft method or constructor.", e);
      } catch (SecurityException e) {
        Bukkit.getLogger().log(Level.WARNING, "Could not access constructor.", e);
      }
    }
  }
 /**
  * Set the behavior of the current editing component to display information about an achievement
  * when the client hovers over the text.
  *
  * <p>Tooltips do not inherit display characteristics, such as color and styles, from the message
  * component on which they are applied.
  *
  * @param which The achievement to display.
  * @return This builder instance.
  */
 public FancyMessage achievementTooltip(final Achievement which) {
   try {
     Object achievement =
         Reflection.getMethod(
                 Reflection.getOBCClass("CraftStatistic"), "getNMSAchievement", Achievement.class)
             .invoke(null, which);
     return achievementTooltip(
         (String)
             Reflection.getField(Reflection.getNMSClass("Achievement"), "name").get(achievement));
   } catch (IllegalAccessException e) {
     Bukkit.getLogger().log(Level.WARNING, "Could not access method.", e);
     return this;
   } catch (IllegalArgumentException e) {
     Bukkit.getLogger().log(Level.WARNING, "Argument could not be passed.", e);
     return this;
   } catch (InvocationTargetException e) {
     Bukkit.getLogger().log(Level.WARNING, "A error has occured durring invoking of method.", e);
     return this;
   }
 }