385 lines
12 KiB
Java
Raw Normal View History

2015-07-26 16:51:12 +02:00
package com.plotsquared.bukkit.titles;
2015-01-18 12:11:51 -08:00
2016-03-23 13:16:05 -04:00
import com.plotsquared.bukkit.chat.Reflection;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
2015-01-18 12:11:51 -08:00
import java.lang.reflect.Field;
2015-02-03 12:47:10 +11:00
import java.lang.reflect.InvocationTargetException;
2015-01-18 12:11:51 -08:00
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* [ PlotSquared DefaultTitleManager by Maxim Van de Wynckel ]
*
* @author Maxim Van de Wynckel
2015-02-20 17:34:19 +11:00
*
2015-01-18 12:11:51 -08:00
*/
2015-09-13 14:04:31 +10:00
public class DefaultTitleManager {
private static final Map<Class<?>, Class<?>> CORRESPONDING_TYPES = new HashMap<>();
2015-02-20 17:34:19 +11:00
/* Title packet */
private Class<?> packetTitle;
/* Title packet actions ENUM */
private Class<?> packetActions;
/* Chat serializer */
private Class<?> nmsChatSerializer;
private Class<?> chatBaseComponent;
/* Title text and color */
private String title = "";
private ChatColor titleColor = ChatColor.WHITE;
/* Subtitle text and color */
private String subtitle = "";
private ChatColor subtitleColor = ChatColor.WHITE;
/* Title timings */
private int fadeInTime = -1;
private int stayTime = -1;
private int fadeOutTime = -1;
private boolean ticks = false;
2015-02-20 17:34:19 +11:00
/**
* Create a new 1.8 title
*
* @param title
* Title
2015-02-23 12:32:27 +11:00
* @throws ClassNotFoundException
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public DefaultTitleManager(String title) throws ClassNotFoundException {
2015-02-20 17:34:19 +11:00
this.title = title;
loadClasses();
}
2015-02-20 17:34:19 +11:00
/**
* Create a new 1.8 title
*
* @param title
* Title text
* @param subtitle
* Subtitle text
2015-02-23 12:32:27 +11:00
* @throws ClassNotFoundException
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public DefaultTitleManager(String title, String subtitle) throws ClassNotFoundException {
2015-02-20 17:34:19 +11:00
this.title = title;
this.subtitle = subtitle;
loadClasses();
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Copy 1.8 title.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param title Title
2015-02-23 12:32:27 +11:00
* @throws ClassNotFoundException
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public DefaultTitleManager(DefaultTitleManager title) throws ClassNotFoundException {
2015-02-20 17:34:19 +11:00
// Copy title
this.title = title.title;
2016-03-23 13:09:13 -04:00
this.subtitle = title.subtitle;
this.titleColor = title.titleColor;
this.subtitleColor = title.subtitleColor;
this.fadeInTime = title.fadeInTime;
this.fadeOutTime = title.fadeOutTime;
this.stayTime = title.stayTime;
this.ticks = title.ticks;
2015-02-20 17:34:19 +11:00
loadClasses();
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Create a new 1.8 title.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param title Title text
* @param subtitle Subtitle text
* @param fadeInTime Fade in time
* @param stayTime Stay on screen time
* @param fadeOutTime Fade out time
2015-02-23 12:32:27 +11:00
* @throws ClassNotFoundException
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public DefaultTitleManager(String title, String subtitle, int fadeInTime, int stayTime, int fadeOutTime)
throws ClassNotFoundException {
2015-02-20 17:34:19 +11:00
this.title = title;
this.subtitle = subtitle;
this.fadeInTime = fadeInTime;
this.stayTime = stayTime;
this.fadeOutTime = fadeOutTime;
loadClasses();
}
2016-03-23 13:09:13 -04:00
private static boolean equalsTypeArray(Class<?>[] a, Class<?>[] o) {
if (a.length != o.length) {
return false;
}
for (int i = 0; i < a.length; i++) {
if (!a[i].equals(o[i]) && !a[i].isAssignableFrom(o[i])) {
return false;
}
}
return true;
}
2015-02-20 17:34:19 +11:00
/**
* Load spigot and NMS classes
2015-02-23 12:32:27 +11:00
* @throws ClassNotFoundException
2015-02-20 17:34:19 +11:00
*/
2015-09-13 14:04:31 +10:00
private void loadClasses() throws ClassNotFoundException {
2016-03-23 13:16:05 -04:00
this.packetTitle = Reflection.getNMSClass("PacketPlayOutTitle");
this.packetActions = Reflection.getNMSClass("EnumTitleAction");
this.chatBaseComponent = Reflection.getNMSClass("IChatBaseComponent");
this.nmsChatSerializer = Reflection.getNMSClass("ChatSerializer");
2015-02-20 17:34:19 +11:00
}
/**
* Get title text
*
* @return Title text
*/
public String getTitle() {
2016-03-23 13:09:13 -04:00
return this.title;
}
2015-02-20 17:34:19 +11:00
/**
* Set title text
*
* @param title
* Title
*/
2016-03-23 13:09:13 -04:00
public void setTitle(String title) {
2015-02-20 17:34:19 +11:00
this.title = title;
}
2015-02-20 17:34:19 +11:00
/**
* Get subtitle text
2015-02-20 17:34:19 +11:00
*
* @return Subtitle text
2015-02-20 17:34:19 +11:00
*/
public String getSubtitle() {
2016-03-23 13:09:13 -04:00
return this.subtitle;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set subtitle text.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param subtitle Subtitle text
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setSubtitle(String subtitle) {
2015-02-20 17:34:19 +11:00
this.subtitle = subtitle;
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set the title color.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param color Chat color
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setTitleColor(ChatColor color) {
this.titleColor = color;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set the subtitle color.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param color Chat color
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setSubtitleColor(ChatColor color) {
this.subtitleColor = color;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set title fade in time.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param time Time
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setFadeInTime(int time) {
this.fadeInTime = time;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set title fade out time.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param time Time
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setFadeOutTime(int time) {
this.fadeOutTime = time;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set title stay time.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param time Time
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void setStayTime(int time) {
this.stayTime = time;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set timings to ticks.
2015-02-20 17:34:19 +11:00
*/
2015-09-13 14:04:31 +10:00
public void setTimingsToTicks() {
2016-03-23 13:09:13 -04:00
this.ticks = true;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Set timings to seconds.
2015-02-20 17:34:19 +11:00
*/
2015-09-13 14:04:31 +10:00
public void setTimingsToSeconds() {
2016-03-23 13:09:13 -04:00
this.ticks = false;
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Send the title to a player.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param player Player
* @throws Exception
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void send(Player player) throws Exception {
if (this.packetTitle != null) {
2015-02-20 17:34:19 +11:00
// First reset previous settings
resetTitle(player);
// Send timings first
2016-03-23 13:09:13 -04:00
Object handle = getHandle(player);
Object connection = getField(handle.getClass(), "playerConnection").get(handle);
Object[] actions = this.packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(), "sendPacket");
Object packet = this.packetTitle.getConstructor(this.packetActions, this.chatBaseComponent, Integer.TYPE, Integer.TYPE, Integer.TYPE)
.newInstance(actions[2], null, this.fadeInTime * (this.ticks ? 1 : 20),
this.stayTime * (this.ticks ? 1 : 20), this.fadeOutTime * (this.ticks ? 1 : 20));
2015-02-20 17:34:19 +11:00
// Send if set
2016-03-23 13:09:13 -04:00
if (this.fadeInTime != -1 && this.fadeOutTime != -1 && this.stayTime != -1) {
2015-02-20 17:34:19 +11:00
sendPacket.invoke(connection, packet);
}
// Send title
2016-03-23 13:09:13 -04:00
Object serialized = getMethod(this.nmsChatSerializer, "a", String.class).invoke(null,
"{text:\"" + ChatColor.translateAlternateColorCodes('&', this.title) + "\",color:" + this.titleColor.name().toLowerCase() + "}");
packet = this.packetTitle.getConstructor(this.packetActions, this.chatBaseComponent).newInstance(actions[0], serialized);
2015-02-20 17:34:19 +11:00
sendPacket.invoke(connection, packet);
2016-03-23 13:09:13 -04:00
if (!this.subtitle.isEmpty()) {
2015-02-20 17:34:19 +11:00
// Send subtitle if present
2016-03-23 13:09:13 -04:00
serialized = getMethod(this.nmsChatSerializer, "a", String.class).invoke(null,
"{text:\"" + ChatColor.translateAlternateColorCodes('&', this.subtitle) + "\",color:" + this.subtitleColor.name()
.toLowerCase() + "}");
packet = this.packetTitle.getConstructor(this.packetActions, this.chatBaseComponent).newInstance(actions[1], serialized);
2015-02-20 17:34:19 +11:00
sendPacket.invoke(connection, packet);
}
}
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Broadcast the title to all players.
*
* @throws Exception
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void broadcast() throws Exception {
for (Player p : Bukkit.getOnlinePlayers()) {
2015-02-20 17:34:19 +11:00
send(p);
}
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Clear the title.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param player Player
* @throws Exception
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void clearTitle(Player player) throws Exception {
2015-09-11 20:09:22 +10:00
// Send timings first
2016-03-23 13:09:13 -04:00
Object handle = getHandle(player);
Object connection = getField(handle.getClass(), "playerConnection").get(handle);
Object[] actions = this.packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(), "sendPacket");
Object packet = this.packetTitle.getConstructor(this.packetActions, this.chatBaseComponent).newInstance(actions[3], null);
2015-09-11 20:09:22 +10:00
sendPacket.invoke(connection, packet);
2015-02-20 17:34:19 +11:00
}
2015-02-20 17:34:19 +11:00
/**
2016-03-23 13:09:13 -04:00
* Reset the title settings.
2015-02-20 17:34:19 +11:00
*
2016-03-23 13:09:13 -04:00
* @param player Player
* @throws Exception
2015-02-20 17:34:19 +11:00
*/
2016-03-23 13:09:13 -04:00
public void resetTitle(Player player) throws Exception {
2015-09-11 20:09:22 +10:00
// Send timings first
2016-03-23 13:09:13 -04:00
Object handle = getHandle(player);
Object connection = getField(handle.getClass(), "playerConnection").get(handle);
Object[] actions = this.packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(), "sendPacket");
Object packet = this.packetTitle.getConstructor(this.packetActions, this.chatBaseComponent).newInstance(actions[4], null);
2015-03-15 13:08:45 +11:00
sendPacket.invoke(connection, packet);
2015-02-20 17:34:19 +11:00
}
2016-03-23 13:09:13 -04:00
private Class<?> getPrimitiveType(Class<?> clazz) {
2015-02-20 17:34:19 +11:00
return CORRESPONDING_TYPES.containsKey(clazz) ? CORRESPONDING_TYPES.get(clazz) : clazz;
}
2016-03-23 13:09:13 -04:00
private Class<?>[] toPrimitiveTypeArray(Class<?>[] classes) {
int a = classes != null ? classes.length : 0;
Class<?>[] types = new Class<?>[a];
2015-09-13 14:04:31 +10:00
for (int i = 0; i < a; i++) {
2015-02-20 17:34:19 +11:00
types[i] = getPrimitiveType(classes[i]);
}
return types;
}
2016-03-23 13:09:13 -04:00
private Object getHandle(Object obj) {
2015-09-13 14:04:31 +10:00
try {
2015-02-20 17:34:19 +11:00
return getMethod("getHandle", obj.getClass()).invoke(obj);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
} catch (InvocationTargetException e) {
2015-02-20 17:34:19 +11:00
e.printStackTrace();
return null;
}
}
2016-03-23 13:09:13 -04:00
private Method getMethod(String name, Class<?> clazz, Class<?>... paramTypes) {
Class<?>[] t = toPrimitiveTypeArray(paramTypes);
for (Method m : clazz.getMethods()) {
Class<?>[] types = toPrimitiveTypeArray(m.getParameterTypes());
2015-09-13 14:04:31 +10:00
if (m.getName().equals(name) && equalsTypeArray(types, t)) {
return m;
}
2015-02-20 17:34:19 +11:00
}
return null;
}
2016-03-23 13:09:13 -04:00
private Field getField(Class<?> clazz, String name) {
2015-09-13 14:04:31 +10:00
try {
2016-03-23 13:09:13 -04:00
Field field = clazz.getDeclaredField(name);
2015-02-20 17:34:19 +11:00
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
e.printStackTrace();
return null;
} catch (SecurityException e) {
2015-02-20 17:34:19 +11:00
e.printStackTrace();
return null;
}
}
2016-03-23 13:09:13 -04:00
private Method getMethod(Class<?> clazz, String name, Class<?>... args) {
for (Method m : clazz.getMethods()) {
if (m.getName().equals(name) && (args.length == 0 || ClassListEqual(args, m.getParameterTypes()))) {
2015-02-20 17:34:19 +11:00
m.setAccessible(true);
return m;
}
}
return null;
}
2016-03-23 13:09:13 -04:00
private boolean ClassListEqual(Class<?>[] l1, Class<?>[] l2) {
2015-09-13 14:04:31 +10:00
if (l1.length != l2.length) {
return false;
}
boolean equal = true;
2015-09-13 14:04:31 +10:00
for (int i = 0; i < l1.length; i++) {
if (l1[i] != l2[i]) {
2015-02-20 17:34:19 +11:00
equal = false;
break;
}
}
return equal;
}
2015-01-18 12:11:51 -08:00
}