gt:                 Player player = (Player) e.getPlayer();
context: package com.archeinteractive.dev.commonutils.effects.ui;
  
  import java.lang.ref.WeakReference;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.Method;
  import java.util.Comparator;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  import java.util.PriorityQueue;
  
  import org.bukkit.Material;
  import org.bukkit.entity.Player;
  import org.bukkit.event.EventHandler;
  import org.bukkit.event.HandlerList;
  import org.bukkit.event.Listener;
  import org.bukkit.event.inventory.InventoryCloseEvent;
  import org.bukkit.event.inventory.InventoryOpenEvent;
  import org.bukkit.event.player.PlayerInteractEvent;
  import org.bukkit.inventory.ItemStack;
  import org.bukkit.inventory.meta.ItemMeta;
  import org.bukkit.material.MaterialData;
  import org.bukkit.plugin.java.JavaPlugin;
  import org.bukkit.scheduler.BukkitRunnable;
  
  import com.archeinteractive.dev.commonutils.reflection.CommonReflection;
  import com.archeinteractive.dev.commonutils.reflection.EntityHandler;
  import com.archeinteractive.dev.commonutils.reflection.VersionHandler;
  
  public class ToolTip {
  
      @SuppressWarnings("deprecation")
      /** The MaterialData that should be used when a player has no held item to modify. */
      private static final MaterialData EMPTY_MATERIAL = new MaterialData(Material.CARPET, (byte) 15);
      /** The inventory ID that NMS code considers the player inventory. */
      private static final int PLAYER_INVENTORY_ID = 0;
      /** The slot ID that NMS code considers the first hotbar slot. */
      private static final int HOTBAR_OFFSET = 36;
      /** The default priority level for a ToolTipMessage. */
      private static final int DEFAULT_PRIORITY = 0;
      
      private final JavaPlugin plugin;
      private final Map<String, ToolTipQueue> main_queue;
      private final ToolTipListener listener;
      private QueueHandler task;
      
      /**
       * Create a new ToolTip handler, registered under the given
       * plugin, and automatically start the processing task.
       * 
       * @param plugin    The plugin to register under.
       */
      public ToolTip(JavaPlugin plugin) {
          this(plugin, true);
      }
      
      /**
       * Create a new ToolTip handler, registered under the given
       * plugin, specifying if the processing task should be started.
       * 
       * @param plugin    The plugin to register under.
       * @param process   Whether or not the processing task should be started.
       */
      public ToolTip(JavaPlugin plugin, boolean process) {
          this.plugin = plugin;
          this.main_queue = new HashMap<>();
          this.listener = new ToolTipListener();
          this.task = null;
          
          plugin.getServer().getPluginManager().registerEvents(listener, plugin);
          
          if (process)
              startQueue();
      }
      
      private void advanceQueue() {
          for (Map.Entry<String, ToolTipQueue> element : new HashSet<>(main_queue.entrySet())) {
              ToolTipQueue q = element.getValue();
              
              if (q.advance())
                  continue;
              
              main_queue.remove(element.getKey());
          }
      }
  
      /**
       * Get whether the queue is currently being processed.
       * 
       * @return <b>true</b> if the queue is being processed,
       *   <b>false</b> otherwise.
       */
      public boolean isQueueProcessing() {
          return task != null;
      }
  
      /**
       * Pauses the processing of the queue.
       * 
       * @throws IllegalStateException
       *     If processing of the queue is already stopped.
       */
      public void pauseQueue() {
          if (!isQueueProcessing())
              throw new IllegalStateException("Queue is already stopped!");
          
          task.cancel();
          task = null;
      }
  
      /**
       * Starts the processing of the queue.
       * 
       * @throws IllegalStateException
       *     If processing of the queue is already started.
       */
      public void startQueue() {
          if (isQueueProcessing())
              throw new IllegalStateException("Queue is already running!");
          
          task = new QueueHandler();
          task.runTaskTimer(plugin, 20L, 20L);
      }
  
      /**
       * Get whether or not the given player
       * currently has any messages in the queue.
       * 
       * @param player    The Player to test.
       * @return <b>true</b> if the player has messages
       *   in the queue, <b>false</b> otherwise.
       */
      public boolean hasQueue(Player player) {
          return main_queue.containsKey(player.getName());
      }
      
      /**
       * Add a message to the given player's queue,
       * with a default display length of 1 second.
       * 
       * @param player    The Player to act on.
       * @param message   The message to display.
       */
      public void queueMessage(Player player, String message) {
          queueMessage(player, message, 1);
      }
      
      /**
       * Add a message to the given player's queue,
       * with a specified display length.
       * 
       * @param player    The Player to act on.
       * @param message   The message to display.
       * @param seconds   The number of seconds to display for.
       */
      public void queueMessage(Player player, String message, int seconds) {
          if (player == null)
              throw new IllegalArgumentException("Player cannot be null!");
          
          if (message == null)
              throw new IllegalArgumentException("Message cannot be null!");
          
          ToolTipQueue q = main_queue.get(player.getName());
          
          if (q == null) {
              main_queue.put(player.getName(), new ToolTipQueue(player));
              q = main_queue.get(player.getName());
          }
          
          q.push(message, seconds);
          q.update();
      }
      
      /**
       * Force the given player's current message to advance;
       * subsequently, there may be no messages in the queue.
       * 
       * @param player    The Player whose queue should be advanced.
       */
      public void advanceQueue(Player player) {
          if (hasQueue(player)) {
              ToolTipQueue q = main_queue.get(player.getName());
              
              if (q != null) {
                  q.drop();
              }
          }
      }
  
      /**
       * Clear the given player's message queue.
       * 
       * @param player    The Player whose queue should be cleared.
       */
      public void clearQueue(Player player) {
          if (hasQueue(player)) {
              main_queue.remove(player.getName());
          }
      }
      
      protected void finalize() throws Throwable {
          HandlerList.unregisterAll(listener);
          super.finalize();
      }
      
      private class ToolTipListener implements Listener {
          @EventHandler
          public void onInventoryOpen(InventoryOpenEvent e) {
              if (e.getPlayer() instanceof Player) {
  