mirror of
				https://github.com/IntellectualSites/PlotSquared.git
				synced 2025-11-04 03:03:43 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			394 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			394 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
package com.plotsquared.bukkit.titles;
 | 
						|
 | 
						|
import org.bukkit.Bukkit;
 | 
						|
import org.bukkit.ChatColor;
 | 
						|
import org.bukkit.entity.Player;
 | 
						|
 | 
						|
import java.lang.reflect.Field;
 | 
						|
import java.lang.reflect.InvocationTargetException;
 | 
						|
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
 | 
						|
 *
 | 
						|
 */
 | 
						|
public class DefaultTitleManager {
 | 
						|
 | 
						|
    private static final Map<Class<?>, Class<?>> CORRESPONDING_TYPES = new HashMap<>();
 | 
						|
    /* 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;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a new 1.8 title
 | 
						|
     *
 | 
						|
     * @param title
 | 
						|
     *            Title
 | 
						|
     * @throws ClassNotFoundException
 | 
						|
     */
 | 
						|
    public DefaultTitleManager(String title) throws ClassNotFoundException {
 | 
						|
        this.title = title;
 | 
						|
        loadClasses();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a new 1.8 title
 | 
						|
     *
 | 
						|
     * @param title
 | 
						|
     *            Title text
 | 
						|
     * @param subtitle
 | 
						|
     *            Subtitle text
 | 
						|
     * @throws ClassNotFoundException
 | 
						|
     */
 | 
						|
    public DefaultTitleManager(String title, String subtitle) throws ClassNotFoundException {
 | 
						|
        this.title = title;
 | 
						|
        this.subtitle = subtitle;
 | 
						|
        loadClasses();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Copy 1.8 title.
 | 
						|
     *
 | 
						|
     * @param title Title
 | 
						|
     * @throws ClassNotFoundException
 | 
						|
     */
 | 
						|
    public DefaultTitleManager(DefaultTitleManager title) throws ClassNotFoundException {
 | 
						|
        // Copy title
 | 
						|
        this.title = title.title;
 | 
						|
        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;
 | 
						|
        loadClasses();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Create a new 1.8 title.
 | 
						|
     *
 | 
						|
     * @param title Title text
 | 
						|
     * @param subtitle Subtitle text
 | 
						|
     * @param fadeInTime Fade in time
 | 
						|
     * @param stayTime Stay on screen time
 | 
						|
     * @param fadeOutTime Fade out time
 | 
						|
     * @throws ClassNotFoundException
 | 
						|
     */
 | 
						|
    public DefaultTitleManager(String title, String subtitle, int fadeInTime, int stayTime, int fadeOutTime)
 | 
						|
            throws ClassNotFoundException {
 | 
						|
        this.title = title;
 | 
						|
        this.subtitle = subtitle;
 | 
						|
        this.fadeInTime = fadeInTime;
 | 
						|
        this.stayTime = stayTime;
 | 
						|
        this.fadeOutTime = fadeOutTime;
 | 
						|
        loadClasses();
 | 
						|
    }
 | 
						|
 | 
						|
    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;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Load spigot and NMS classes
 | 
						|
     * @throws ClassNotFoundException
 | 
						|
     */
 | 
						|
    private void loadClasses() throws ClassNotFoundException {
 | 
						|
        this.packetTitle = getNMSClass("PacketPlayOutTitle");
 | 
						|
        this.packetActions = getNMSClass("EnumTitleAction");
 | 
						|
        this.chatBaseComponent = getNMSClass("IChatBaseComponent");
 | 
						|
        this.nmsChatSerializer = getNMSClass("ChatSerializer");
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get title text
 | 
						|
     *
 | 
						|
     * @return Title text
 | 
						|
     */
 | 
						|
    public String getTitle() {
 | 
						|
        return this.title;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set title text
 | 
						|
     *
 | 
						|
     * @param title
 | 
						|
     *            Title
 | 
						|
     */
 | 
						|
    public void setTitle(String title) {
 | 
						|
        this.title = title;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get subtitle text
 | 
						|
     *
 | 
						|
     * @return Subtitle text
 | 
						|
     */
 | 
						|
    public String getSubtitle() {
 | 
						|
        return this.subtitle;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set subtitle text.
 | 
						|
     *
 | 
						|
     * @param subtitle Subtitle text
 | 
						|
     */
 | 
						|
    public void setSubtitle(String subtitle) {
 | 
						|
        this.subtitle = subtitle;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set the title color.
 | 
						|
     *
 | 
						|
     * @param color Chat color
 | 
						|
     */
 | 
						|
    public void setTitleColor(ChatColor color) {
 | 
						|
        this.titleColor = color;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set the subtitle color.
 | 
						|
     *
 | 
						|
     * @param color Chat color
 | 
						|
     */
 | 
						|
    public void setSubtitleColor(ChatColor color) {
 | 
						|
        this.subtitleColor = color;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set title fade in time.
 | 
						|
     *
 | 
						|
     * @param time Time
 | 
						|
     */
 | 
						|
    public void setFadeInTime(int time) {
 | 
						|
        this.fadeInTime = time;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set title fade out time.
 | 
						|
     *
 | 
						|
     * @param time Time
 | 
						|
     */
 | 
						|
    public void setFadeOutTime(int time) {
 | 
						|
        this.fadeOutTime = time;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set title stay time.
 | 
						|
     *
 | 
						|
     * @param time Time
 | 
						|
     */
 | 
						|
    public void setStayTime(int time) {
 | 
						|
        this.stayTime = time;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set timings to ticks.
 | 
						|
     */
 | 
						|
    public void setTimingsToTicks() {
 | 
						|
        this.ticks = true;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Set timings to seconds.
 | 
						|
     */
 | 
						|
    public void setTimingsToSeconds() {
 | 
						|
        this.ticks = false;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Send the title to a player.
 | 
						|
     *
 | 
						|
     * @param player Player
 | 
						|
     * @throws Exception
 | 
						|
     */
 | 
						|
    public void send(Player player) throws Exception {
 | 
						|
        if (this.packetTitle != null) {
 | 
						|
            // First reset previous settings
 | 
						|
            resetTitle(player);
 | 
						|
            // Send timings first
 | 
						|
            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));
 | 
						|
            // Send if set
 | 
						|
            if (this.fadeInTime != -1 && this.fadeOutTime != -1 && this.stayTime != -1) {
 | 
						|
                sendPacket.invoke(connection, packet);
 | 
						|
            }
 | 
						|
            // Send title
 | 
						|
            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);
 | 
						|
            sendPacket.invoke(connection, packet);
 | 
						|
            if (!this.subtitle.isEmpty()) {
 | 
						|
                // Send subtitle if present
 | 
						|
                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);
 | 
						|
                sendPacket.invoke(connection, packet);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Broadcast the title to all players.
 | 
						|
     *
 | 
						|
     * @throws Exception
 | 
						|
     */
 | 
						|
    public void broadcast() throws Exception {
 | 
						|
        for (Player p : Bukkit.getOnlinePlayers()) {
 | 
						|
            send(p);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Clear the title.
 | 
						|
     *
 | 
						|
     * @param player Player
 | 
						|
     * @throws Exception
 | 
						|
     */
 | 
						|
    public void clearTitle(Player player) throws Exception {
 | 
						|
        // Send timings first
 | 
						|
        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);
 | 
						|
        sendPacket.invoke(connection, packet);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Reset the title settings.
 | 
						|
     *
 | 
						|
     * @param player Player
 | 
						|
     * @throws Exception
 | 
						|
     */
 | 
						|
    public void resetTitle(Player player) throws Exception {
 | 
						|
        // Send timings first
 | 
						|
        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);
 | 
						|
        sendPacket.invoke(connection, packet);
 | 
						|
    }
 | 
						|
 | 
						|
    private Class<?> getPrimitiveType(Class<?> clazz) {
 | 
						|
        return CORRESPONDING_TYPES.containsKey(clazz) ? CORRESPONDING_TYPES.get(clazz) : clazz;
 | 
						|
    }
 | 
						|
 | 
						|
    private Class<?>[] toPrimitiveTypeArray(Class<?>[] classes) {
 | 
						|
        int a = classes != null ? classes.length : 0;
 | 
						|
        Class<?>[] types = new Class<?>[a];
 | 
						|
        for (int i = 0; i < a; i++) {
 | 
						|
            types[i] = getPrimitiveType(classes[i]);
 | 
						|
        }
 | 
						|
        return types;
 | 
						|
    }
 | 
						|
 | 
						|
    private Object getHandle(Object obj) {
 | 
						|
        try {
 | 
						|
            return getMethod("getHandle", obj.getClass()).invoke(obj);
 | 
						|
        } catch (IllegalAccessException e) {
 | 
						|
            e.printStackTrace();
 | 
						|
            return null;
 | 
						|
        } catch (IllegalArgumentException e) {
 | 
						|
            e.printStackTrace();
 | 
						|
            return null;
 | 
						|
        } catch (InvocationTargetException e) {
 | 
						|
            e.printStackTrace();
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    private Method getMethod(String name, Class<?> clazz, Class<?>... paramTypes) {
 | 
						|
        Class<?>[] t = toPrimitiveTypeArray(paramTypes);
 | 
						|
        for (Method m : clazz.getMethods()) {
 | 
						|
            Class<?>[] types = toPrimitiveTypeArray(m.getParameterTypes());
 | 
						|
            if (m.getName().equals(name) && equalsTypeArray(types, t)) {
 | 
						|
                return m;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    private String getVersion() {
 | 
						|
        String name = Bukkit.getServer().getClass().getPackage().getName();
 | 
						|
        return name.substring(name.lastIndexOf('.') + 1) + ".";
 | 
						|
    }
 | 
						|
 | 
						|
    private Class<?> getNMSClass(String className) throws ClassNotFoundException {
 | 
						|
        String fullName = "net.minecraft.server." + getVersion() + className;
 | 
						|
        return Class.forName(fullName);
 | 
						|
    }
 | 
						|
 | 
						|
    private Field getField(Class<?> clazz, String name) {
 | 
						|
        try {
 | 
						|
            Field field = clazz.getDeclaredField(name);
 | 
						|
            field.setAccessible(true);
 | 
						|
            return field;
 | 
						|
        } catch (NoSuchFieldException e) {
 | 
						|
            e.printStackTrace();
 | 
						|
            return null;
 | 
						|
        } catch (SecurityException e) {
 | 
						|
            e.printStackTrace();
 | 
						|
            return null;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    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()))) {
 | 
						|
                m.setAccessible(true);
 | 
						|
                return m;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return null;
 | 
						|
    }
 | 
						|
 | 
						|
    private boolean ClassListEqual(Class<?>[] l1, Class<?>[] l2) {
 | 
						|
        if (l1.length != l2.length) {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
        boolean equal = true;
 | 
						|
        for (int i = 0; i < l1.length; i++) {
 | 
						|
            if (l1[i] != l2[i]) {
 | 
						|
                equal = false;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return equal;
 | 
						|
    }
 | 
						|
}
 |