From dea0a452dffdeff61bf4985df45836c88d38b6b3 Mon Sep 17 00:00:00 2001 From: Jesse Boyd Date: Wed, 30 Nov 2016 16:28:50 +1100 Subject: [PATCH] Add fading 1.11 titles --- .../com/plotsquared/bukkit/BukkitMain.java | 55 +- ...efaultTitle.java => DefaultTitle_110.java} | 4 +- .../bukkit/titles/DefaultTitle_111.java | 22 + .../bukkit/titles/DefaultTitle_19.java | 2 +- .../bukkit/titles/TitleManager_1_11.java | 526 ++++++++++++++++++ 5 files changed, 564 insertions(+), 45 deletions(-) rename Bukkit/src/main/java/com/plotsquared/bukkit/titles/{DefaultTitle.java => DefaultTitle_110.java} (84%) create mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_111.java create mode 100644 Bukkit/src/main/java/com/plotsquared/bukkit/titles/TitleManager_1_11.java diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java index f79259eb3..eb097a4a1 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/BukkitMain.java @@ -16,21 +16,7 @@ import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.object.RunnableVal; import com.intellectualcrafters.plot.object.SetupObject; import com.intellectualcrafters.plot.object.chat.PlainChatManager; -import com.intellectualcrafters.plot.util.AbstractTitle; -import com.intellectualcrafters.plot.util.ChatManager; -import com.intellectualcrafters.plot.util.ChunkManager; -import com.intellectualcrafters.plot.util.ConsoleColors; -import com.intellectualcrafters.plot.util.EconHandler; -import com.intellectualcrafters.plot.util.EventUtil; -import com.intellectualcrafters.plot.util.InventoryUtil; -import com.intellectualcrafters.plot.util.MainUtil; -import com.intellectualcrafters.plot.util.SchematicHandler; -import com.intellectualcrafters.plot.util.SetupUtils; -import com.intellectualcrafters.plot.util.StringMan; -import com.intellectualcrafters.plot.util.TaskManager; -import com.intellectualcrafters.plot.util.UUIDHandler; -import com.intellectualcrafters.plot.util.UUIDHandlerImplementation; -import com.intellectualcrafters.plot.util.WorldUtil; +import com.intellectualcrafters.plot.util.*; import com.intellectualcrafters.plot.util.block.QueueProvider; import com.intellectualcrafters.plot.uuid.UUIDWrapper; import com.plotsquared.bukkit.database.plotme.ClassicPlotMeConnector; @@ -45,22 +31,8 @@ import com.plotsquared.bukkit.listeners.PlayerEvents_1_8; import com.plotsquared.bukkit.listeners.PlayerEvents_1_9; import com.plotsquared.bukkit.listeners.PlotPlusListener; import com.plotsquared.bukkit.listeners.WorldEvents; -import com.plotsquared.bukkit.titles.DefaultTitle_19; -import com.plotsquared.bukkit.util.BukkitChatManager; -import com.plotsquared.bukkit.util.BukkitChunkManager; -import com.plotsquared.bukkit.util.BukkitCommand; -import com.plotsquared.bukkit.util.BukkitEconHandler; -import com.plotsquared.bukkit.util.BukkitEventUtil; -import com.plotsquared.bukkit.util.BukkitHybridUtils; -import com.plotsquared.bukkit.util.BukkitInventoryUtil; -import com.plotsquared.bukkit.util.BukkitSchematicHandler; -import com.plotsquared.bukkit.util.BukkitSetupUtils; -import com.plotsquared.bukkit.util.BukkitTaskManager; -import com.plotsquared.bukkit.util.BukkitUtil; -import com.plotsquared.bukkit.util.BukkitVersion; -import com.plotsquared.bukkit.util.Metrics; -import com.plotsquared.bukkit.util.SendChunk; -import com.plotsquared.bukkit.util.SetGenCB; +import com.plotsquared.bukkit.titles.DefaultTitle_111; +import com.plotsquared.bukkit.util.*; import com.plotsquared.bukkit.util.block.BukkitLocalQueue; import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_7; import com.plotsquared.bukkit.util.block.BukkitLocalQueue_1_8; @@ -72,6 +44,15 @@ import com.plotsquared.bukkit.uuid.LowerOfflineUUIDWrapper; import com.plotsquared.bukkit.uuid.OfflineUUIDWrapper; import com.plotsquared.bukkit.uuid.SQLUUIDHandler; import com.sk89q.worldedit.WorldEdit; +import java.io.File; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; @@ -88,16 +69,6 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; -import java.io.File; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain { private static ConcurrentHashMap pluginMap; @@ -604,7 +575,7 @@ public final class BukkitMain extends JavaPlugin implements Listener, IPlotMain PS.log(C.PREFIX + " &c[WARN] Titles are disabled - please update your version of Bukkit to support this feature."); Settings.TITLES = false; } else { - AbstractTitle.TITLE_CLASS = new DefaultTitle_19(); + AbstractTitle.TITLE_CLASS = new DefaultTitle_111(); if (wrapper instanceof DefaultUUIDWrapper || wrapper.getClass() == OfflineUUIDWrapper.class && !Bukkit.getOnlineMode()) { Settings.UUID.NATIVE_UUID_PROVIDER = true; } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle.java b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_110.java similarity index 84% rename from Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle.java rename to Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_110.java index 8a0ead590..324c6bd32 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_110.java @@ -4,7 +4,7 @@ import com.intellectualcrafters.plot.object.PlotPlayer; import com.intellectualcrafters.plot.util.AbstractTitle; import com.plotsquared.bukkit.object.BukkitPlayer; -public class DefaultTitle extends AbstractTitle { +public class DefaultTitle_110 extends AbstractTitle { @Override public void sendTitle(PlotPlayer player, String head, String sub, int in, int delay, int out) { @@ -12,7 +12,7 @@ public class DefaultTitle extends AbstractTitle { DefaultTitleManager title = new DefaultTitleManager(head, sub, in, delay, out); title.send(((BukkitPlayer) player).player); } catch (Exception ignored) { - AbstractTitle.TITLE_CLASS = new DefaultTitle_183(); + AbstractTitle.TITLE_CLASS = new DefaultTitle_19(); AbstractTitle.TITLE_CLASS.sendTitle(player, head, sub, in, delay, out); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_111.java b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_111.java new file mode 100644 index 000000000..6afb3341e --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_111.java @@ -0,0 +1,22 @@ +package com.plotsquared.bukkit.titles; + +import com.intellectualcrafters.plot.object.PlotPlayer; +import com.intellectualcrafters.plot.util.AbstractTitle; +import com.plotsquared.bukkit.object.BukkitPlayer; +import org.bukkit.entity.Player; + +@SuppressWarnings("deprecation") +public class DefaultTitle_111 extends AbstractTitle { + + @Override + public void sendTitle(PlotPlayer player, String head, String sub, int in, int delay, int out) { + try { + final Player playerObj = ((BukkitPlayer) player).player; + TitleManager_1_11 title = new TitleManager_1_11(head, sub, in, delay, out); + title.send(playerObj); + } catch (Throwable ignored) { + AbstractTitle.TITLE_CLASS = new DefaultTitle_110(); + AbstractTitle.TITLE_CLASS.sendTitle(player, head, sub, in, delay, out); + } + } +} diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_19.java b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_19.java index a25b20779..f6f90c2f2 100644 --- a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_19.java +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/DefaultTitle_19.java @@ -21,7 +21,7 @@ public class DefaultTitle_19 extends AbstractTitle { } }, delay * 20); } catch (Throwable ignored) { - AbstractTitle.TITLE_CLASS = new DefaultTitle(); + AbstractTitle.TITLE_CLASS = new DefaultTitle_183(); AbstractTitle.TITLE_CLASS.sendTitle(player, head, sub, in, delay, out); } } diff --git a/Bukkit/src/main/java/com/plotsquared/bukkit/titles/TitleManager_1_11.java b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/TitleManager_1_11.java new file mode 100644 index 000000000..935f3d850 --- /dev/null +++ b/Bukkit/src/main/java/com/plotsquared/bukkit/titles/TitleManager_1_11.java @@ -0,0 +1,526 @@ +package com.plotsquared.bukkit.titles; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Player; + +/** + * Minecraft 1.8 Title + * For 1.11 + * + * @author Maxim Van de Wynckel + * @version 1.1.0 + */ +public class TitleManager_1_11 { + /* Title packet */ + private static Class packetTitle; + /* Title packet actions ENUM */ + private static Class packetActions; + /* Chat serializer */ + private static Class nmsChatSerializer; + private static Class chatBaseComponent; + /* NMS player and connection */ + private static Class nmsPlayer; + private static Class nmsPlayerConnection; + private static Field playerConnection; + private static Method sendPacket; + private static Class obcPlayer; + private static Method methodPlayerGetHandle; + /* 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; + + private static final Map, Class> CORRESPONDING_TYPES = new HashMap, Class>(); + + public TitleManager_1_11() { + loadClasses(); + } + + /** + * Create a new 1.8 title + * + * @param title Title + */ + public TitleManager_1_11(String title) { + this.title = title; + loadClasses(); + } + + /** + * Create a new 1.8 title + * + * @param title Title text + * @param subtitle Subtitle text + */ + public TitleManager_1_11(String title, String subtitle) { + this.title = title; + this.subtitle = subtitle; + loadClasses(); + } + + /** + * Copy 1.8 title + * + * @param title Title + */ + public TitleManager_1_11(TitleManager_1_11 title) { + // Copy title + this.title = title.getTitle(); + this.subtitle = title.getSubtitle(); + this.titleColor = title.getTitleColor(); + this.subtitleColor = title.getSubtitleColor(); + this.fadeInTime = title.getFadeInTime(); + this.fadeOutTime = title.getFadeOutTime(); + this.stayTime = title.getStayTime(); + this.ticks = title.isTicks(); + 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 + */ + public TitleManager_1_11(String title, String subtitle, int fadeInTime, int stayTime, int fadeOutTime) { + this.title = title; + this.subtitle = subtitle; + this.fadeInTime = fadeInTime; + this.stayTime = stayTime; + this.fadeOutTime = fadeOutTime; + loadClasses(); + } + + /** + * Load spigot and NMS classes + */ + private void loadClasses() { + if (packetTitle == null) { + packetTitle = getNMSClass("PacketPlayOutTitle"); + packetActions = getNMSClass("PacketPlayOutTitle$EnumTitleAction"); + chatBaseComponent = getNMSClass("IChatBaseComponent"); + nmsChatSerializer = getNMSClass("ChatComponentText"); + nmsPlayer = getNMSClass("EntityPlayer"); + nmsPlayerConnection = getNMSClass("PlayerConnection"); + playerConnection = getField(nmsPlayer, + "playerConnection"); + sendPacket = getMethod(nmsPlayerConnection, "sendPacket"); + obcPlayer = getOBCClass("entity.CraftPlayer"); + methodPlayerGetHandle = getMethod("getHandle", obcPlayer); + } + } + + /** + * Set title text + * + * @param title Title + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Get title text + * + * @return Title text + */ + public String getTitle() { + return this.title; + } + + /** + * Set subtitle text + * + * @param subtitle Subtitle text + */ + public void setSubtitle(String subtitle) { + this.subtitle = subtitle; + } + + /** + * Get subtitle text + * + * @return Subtitle text + */ + public String getSubtitle() { + return this.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() { + ticks = true; + } + + /** + * Set timings to seconds + */ + public void setTimingsToSeconds() { + ticks = false; + } + + /** + * Send the title to a player + * + * @param player Player + */ + public void send(Player player) { + if (packetTitle != null) { + // First reset previous settings + resetTitle(player); + try { + // Send timings first + Object handle = getHandle(player); + Object connection = playerConnection.get(handle); + Object[] actions = packetActions.getEnumConstants(); + Object packet = packetTitle.getConstructor(packetActions, + chatBaseComponent, Integer.TYPE, Integer.TYPE, + Integer.TYPE).newInstance(actions[3], null, + fadeInTime * (ticks ? 1 : 20), + stayTime * (ticks ? 1 : 20), + fadeOutTime * (ticks ? 1 : 20)); + // Send if set + if (fadeInTime != -1 && fadeOutTime != -1 && stayTime != -1) + sendPacket.invoke(connection, packet); + + Object serialized; + if (!subtitle.equals("")) { + // Send subtitle if present + serialized = nmsChatSerializer.getConstructor(String.class) + .newInstance(subtitleColor + + ChatColor.translateAlternateColorCodes('&', + subtitle)); + packet = packetTitle.getConstructor(packetActions, + chatBaseComponent).newInstance(actions[1], + serialized); + sendPacket.invoke(connection, packet); + } + + // Send title + serialized = nmsChatSerializer.getConstructor( + String.class).newInstance(titleColor + + ChatColor.translateAlternateColorCodes('&', title)); + packet = packetTitle.getConstructor(packetActions, + chatBaseComponent).newInstance(actions[0], serialized); + sendPacket.invoke(connection, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public void updateTimes(Player player) { + if (TitleManager_1_11.packetTitle != null) { + try { + Object handle = getHandle(player); + Object connection = playerConnection.get(handle); + Object[] actions = TitleManager_1_11.packetActions.getEnumConstants(); + Object packet = TitleManager_1_11.packetTitle.getConstructor( + new Class[]{TitleManager_1_11.packetActions, chatBaseComponent, + Integer.TYPE, Integer.TYPE, Integer.TYPE}) + .newInstance( + actions[3], + null, + this.fadeInTime + * (this.ticks ? 1 : 20), + this.stayTime + * (this.ticks ? 1 : 20), + this.fadeOutTime + * (this.ticks ? 1 : 20)); + if ((this.fadeInTime != -1) && (this.fadeOutTime != -1) + && (this.stayTime != -1)) { + sendPacket.invoke(connection, packet); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public void updateTitle(Player player) { + if (TitleManager_1_11.packetTitle != null) { + try { + Object handle = getHandle(player); + Object connection = getField(handle.getClass(), + "playerConnection").get(handle); + Object[] actions = TitleManager_1_11.packetActions.getEnumConstants(); + Method sendPacket = getMethod(connection.getClass(), + "sendPacket"); + Object serialized = nmsChatSerializer.getConstructor( + String.class) + .newInstance(titleColor + + ChatColor.translateAlternateColorCodes('&', + this.title)); + Object packet = TitleManager_1_11.packetTitle + .getConstructor( + new Class[]{TitleManager_1_11.packetActions, + chatBaseComponent}).newInstance( + actions[0], serialized); + sendPacket.invoke(connection, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + public void updateSubtitle(Player player) { + if (TitleManager_1_11.packetTitle != null) { + try { + Object handle = getHandle(player); + Object connection = playerConnection.get(handle); + Object[] actions = TitleManager_1_11.packetActions.getEnumConstants(); + Object serialized = nmsChatSerializer.getConstructor( + String.class) + .newInstance(subtitleColor + + ChatColor.translateAlternateColorCodes('&', + this.subtitle)); + Object packet = TitleManager_1_11.packetTitle + .getConstructor( + new Class[]{TitleManager_1_11.packetActions, + chatBaseComponent}).newInstance( + actions[1], serialized); + sendPacket.invoke(connection, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** + * Broadcast the title to all players + */ + public void broadcast() { + for (Player p : Bukkit.getOnlinePlayers()) { + send(p); + } + } + + /** + * Clear the title + * + * @param player Player + */ + public void clearTitle(Player player) { + try { + // Send timings first + Object handle = getHandle(player); + Object connection = playerConnection.get(handle); + Object[] actions = packetActions.getEnumConstants(); + Object packet = packetTitle.getConstructor(packetActions, + chatBaseComponent).newInstance(actions[4], null); + sendPacket.invoke(connection, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Reset the title settings + * + * @param player Player + */ + public void resetTitle(Player player) { + try { + // Send timings first + Object handle = getHandle(player); + Object connection = playerConnection.get(handle); + Object[] actions = packetActions.getEnumConstants(); + Object packet = packetTitle.getConstructor(packetActions, + chatBaseComponent).newInstance(actions[5], null); + sendPacket.invoke(connection, packet); + } catch (Exception e) { + e.printStackTrace(); + } + } + + 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 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; + } + + private Object getHandle(Player player) { + try { + return methodPlayerGetHandle.invoke(player); + } catch (Exception 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(); + String version = name.substring(name.lastIndexOf('.') + 1) + "."; + return version; + } + + private Class getNMSClass(String className) { + String fullName = "net.minecraft.server." + getVersion() + className; + Class clazz = null; + try { + clazz = Class.forName(fullName); + } catch (Exception e) { + e.printStackTrace(); + } + return clazz; + } + + private Class getOBCClass(String className) { + String fullName = "org.bukkit.craftbukkit." + getVersion() + className; + Class clazz = null; + try { + clazz = Class.forName(fullName); + } catch (Exception e) { + e.printStackTrace(); + } + return clazz; + } + + + private Field getField(Class clazz, String name) { + try { + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + return field; + } catch (Exception 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) { + boolean equal = true; + if (l1.length != l2.length) + return false; + for (int i = 0; i < l1.length; i++) + if (l1[i] != l2[i]) { + equal = false; + break; + } + return equal; + } + + public ChatColor getTitleColor() { + return titleColor; + } + + public ChatColor getSubtitleColor() { + return subtitleColor; + } + + public int getFadeInTime() { + return fadeInTime; + } + + public int getFadeOutTime() { + return fadeOutTime; + } + + public int getStayTime() { + return stayTime; + } + + public boolean isTicks() { + return ticks; + } +} \ No newline at end of file