package com.supaham.commons.bukkit.title;
import com.google.common.base.Preconditions;
import com.supaham.commons.bukkit.text.FancyMessage;
import com.supaham.commons.bukkit.text.MessagePart;
import com.supaham.commons.bukkit.utils.ReflectionUtils;
import org.bukkit.entity.Player;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Title messaging system completely based on packets through static methods provided by this
* class.
* <p />
* <b>Note:</b> As of 1.8, subtitles do not appear without the title. This means that in order for
* a {@link #sendSubtitle(Player, FancyMessage)} to visually display for the player,
* {@link #sendTitle(Player, FancyMessage)} must be sent immediately before or after the subtitle.
*
* @see #sendTimes(Player, int, int, int)
* @see #sendSubtitle(Player, FancyMessage)
* @see #sendTitle(Player, FancyMessage)
*/
public class Title {
private static Class<?> packetClass = ReflectionUtils.getNMSClass("PacketPlayOutTitle");
private static Class<?> chatPacket = ReflectionUtils.getNMSClass("PacketPlayOutChat");
private static Class<? extends Enum> packetActionClass = ((Class<? extends Enum>) ReflectionUtils
.getNMSClass("PacketPlayOutTitle$EnumTitleAction"));
private static Class<?> chatComponentClass = ReflectionUtils.getNMSClass("IChatBaseComponent");
private static Constructor ctor1, ctor3;
static {
try {
ctor1 = packetClass.getDeclaredConstructor(packetActionClass, chatComponentClass);
ctor3 = packetClass.getDeclaredConstructor(packetActionClass, chatComponentClass,
int.class, int.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
/**
* Sends title time properties to a {@link Player}. This is typically called before sending the
* title in order for the title message to utilize these properties.
*
* @param player player to send the times to
* @param fadeIn fade in duration (in ticks)
* @param stay stay duration (in ticks)
* @param fadeOut fade out duration (in ticks)
*/
public static void sendTimes(@Nonnull Player player, int fadeIn, int stay, int fadeOut) {
sendTitle(player, fadeIn, stay, fadeOut, null, null);
}
/**
* Sends a subtitle message to a {@link Player}.
*
* @param player player to send the subtitle to
* @param subtitle subtitle to send
*
* @see #sendTimes(Player, int, int, int)
* @see #sendTitle(Player, FancyMessage)
*/
public static void sendSubtitle(@Nonnull Player player, @Nullable FancyMessage subtitle) {
Preconditions.checkNotNull(subtitle, "subtitle cannot be null.");
sendTitle(player, null, null, null, null, subtitle);
}
/**
* Sends a subtitle message to a {@link Player}.
*
* @param player player to send the subtitle to
* @param subtitle subtitle to send
*
* @see #sendTimes(Player, int, int, int)
* @see #sendTitle(Player, FancyMessage)
*/
public static void sendSubtitle(@Nonnull Player player, @Nonnull FancyMessage title,
@Nullable FancyMessage subtitle) {
Preconditions.checkNotNull(title, "title cannot be null.");
Preconditions.checkNotNull(subtitle, "subtitle cannot be null.");
sendTitle(player, null, null, null, title, subtitle);
}
/**
* Sends a title message to a {@link Player}.
*
* @param player player to send the subtitle to
* @param title title to send
*
* @see #sendTimes(Player, int, int, int)
* @see #sendSubtitle(Player, FancyMessage)
*/
public static void sendTitle(@Nonnull Player player, @Nonnull FancyMessage title) {
Preconditions.checkNotNull(title, "title cannot be null.");
sendTitle(player, null, null, null, title, null);
}
private static void sendTitle(@Nonnull Player player,
Integer fadeIn, Integer stay, Integer fadeOut,
@Nullable FancyMessage title, @Nullable FancyMessage subtitle) {
Preconditions.checkNotNull(player, "player cannot be null.");
try {
if (fadeIn != null && stay != null && fadeOut != null) {
Preconditions.checkArgument(fadeIn >= 0, "fadeIn must be 0 or larger.");
Preconditions.checkArgument(stay >= 0, "stay must be 0 or larger.");
Preconditions.checkArgument(fadeOut >= 0, "fadeOut must be 0 or larger.");
Object packet = ctor3.newInstance(Action.TIMES.nmsAction, null, fadeIn, stay, fadeOut);
ReflectionUtils.sendPacket(player, packet);
}
if (title != null) {
Object titlePacket = ctor1.newInstance(Action.TITLE.nmsAction, title.getNMSChatObject());
ReflectionUtils.sendPacket(player, titlePacket);
}
if (subtitle != null) {
Object subtitlePacket = ctor1.newInstance(Action.SUBTITLE.nmsAction,
subtitle.getNMSChatObject());
ReflectionUtils.sendPacket(player, subtitlePacket);
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
public static void sendActionBarMessage(@Nonnull Player player,
@Nonnull MessagePart messagePart) {
Preconditions.checkNotNull(player, "player cannot be null.");
Preconditions.checkNotNull(messagePart, "Message part cannot be null.");
try {
Constructor<?> ctor = chatPacket.getDeclaredConstructor(chatComponentClass, byte.class);
Object packet = ctor.newInstance(messagePart.getNMSChatObject(), (byte) 2);
ReflectionUtils.sendPacket(player, packet);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException
| InstantiationException e) {
e.printStackTrace();
}
}
/**
* This doesn't work properly as {@link FancyMessage} doesn't serialize everything in the first
* text json string-object.
*
* @see #sendActionBarMessage(Player, MessagePart)
*/
@Deprecated
public static void sendActionBarMessage(@Nonnull Player player,
@Nonnull FancyMessage fancyMessage) {
Preconditions.checkNotNull(player, "player cannot be null.");
Preconditions.checkNotNull(fancyMessage, "fancy message cannot be null.");
try {
Constructor<?> ctor = chatPacket.getDeclaredConstructor(chatComponentClass, byte.class);
Object packet = ctor.newInstance(fancyMessage.getNMSChatObject(), (byte) 2);
ReflectionUtils.sendPacket(player, packet);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException
| InstantiationException e) {
e.printStackTrace();
}
}
private enum Action {
TITLE, SUBTITLE, TIMES, CLEAR, RESET;
private final Object nmsAction;
Action() {
nmsAction = Preconditions.checkNotNull(Enum.valueOf(packetActionClass, name()));
}
}
private Title() {
throw new AssertionError("Didn't you read the Title? Ha. ha. Read the documentation pls.");
}
}