title support for spigot protocol hack

This commit is contained in:
Jesse Boyd 2015-01-24 17:11:02 -07:00
parent 955425a315
commit 6862b90569
4 changed files with 524 additions and 417 deletions

View File

@ -6,9 +6,15 @@ import org.bukkit.entity.Player;
public class DefaultTitle extends AbstractTitle { public class DefaultTitle extends AbstractTitle {
@Override @Override
public void sendTitle(Player player, String head, String sub, ChatColor head_color, ChatColor sub_color) { public void sendTitle(Player player, String head, String sub, ChatColor head_color, ChatColor sub_color) {
try {
DefaultTitleManager title = new DefaultTitleManager(head,sub,1, 2, 1); DefaultTitleManager title = new DefaultTitleManager(head,sub,1, 2, 1);
title.setTitleColor(head_color); title.setTitleColor(head_color);
title.setSubtitleColor(sub_color); title.setSubtitleColor(sub_color);
title.send(player); title.send(player);
} }
catch (Throwable e) {
AbstractTitle.TITLE_CLASS = new HackTitle();
AbstractTitle.TITLE_CLASS.sendTitle(player, head, sub, head_color, sub_color);
}
}
} }

View File

@ -0,0 +1,24 @@
package com.intellectualcrafters.plot.titles;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
import com.intellectualcrafters.plot.PlotMain;
import com.intellectualcrafters.plot.config.Settings;
public class HackTitle extends AbstractTitle {
@Override
public void sendTitle(Player player, String head, String sub, ChatColor head_color, ChatColor sub_color) {
try {
HackTitleManager title = new HackTitleManager(head,sub,1, 2, 1);
title.setTitleColor(head_color);
title.setSubtitleColor(sub_color);
title.send(player);
}
catch (Throwable e) {
PlotMain.sendConsoleSenderMessage("&cYour server version does not support titles!");
Settings.TITLES = false;
AbstractTitle.TITLE_CLASS = null;
}
}
}

View File

@ -0,0 +1,490 @@
package com.intellectualcrafters.plot.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
*
* @version 1.0.4
* @author Maxim Van de Wynckel
*/
public class HackTitleManager {
/* Title packet */
private Class<?> packetTitle;
/* Title packet actions ENUM */
private Class<?> packetActions;
/* Chat serializer */
private Class<?> nmsChatSerializer;
/* 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<?>, Class<?>> CORRESPONDING_TYPES = new HashMap<Class<?>, Class<?>>();
/**
* Create a new 1.8 title
*
* @param title
* Title
*/
public HackTitleManager(String title) {
this.title = title;
loadClasses();
}
/**
* Create a new 1.8 title
*
* @param title
* Title text
* @param subtitle
* Subtitle text
*/
public HackTitleManager(String title, String subtitle) {
this.title = title;
this.subtitle = subtitle;
loadClasses();
}
/**
* Copy 1.8 title
*
* @param title
* Title
*/
public HackTitleManager(HackTitleManager title) {
// 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
*/
public HackTitleManager(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() {
packetTitle = getClass("org.spigotmc.ProtocolInjector$PacketTitle");
packetActions = getClass("org.spigotmc.ProtocolInjector$PacketTitle$Action");
nmsChatSerializer = getNMSClass("ChatSerializer");
}
/**
* 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 (getProtocolVersion(player) >= 47 && isSpigot()
&& packetTitle != null) {
// First reset previous settings
resetTitle(player);
try {
// Send timings first
Object handle = getHandle(player);
Object connection = getField(handle.getClass(),
"playerConnection").get(handle);
Object[] actions = packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(),
"sendPacket");
Object packet = packetTitle.getConstructor(packetActions,
Integer.TYPE, Integer.TYPE, Integer.TYPE).newInstance(
actions[2], 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);
// Send title
Object serialized = getMethod(nmsChatSerializer, "a",
String.class).invoke(
null,
"{text:\""
+ ChatColor.translateAlternateColorCodes('&',
title) + "\",color:"
+ titleColor.name().toLowerCase() + "}");
packet = packetTitle.getConstructor(packetActions,
getNMSClass("IChatBaseComponent")).newInstance(
actions[0], serialized);
sendPacket.invoke(connection, packet);
if (subtitle != "") {
// Send subtitle if present
serialized = getMethod(nmsChatSerializer, "a", String.class)
.invoke(null,
"{text:\""
+ ChatColor
.translateAlternateColorCodes(
'&', subtitle)
+ "\",color:"
+ subtitleColor.name()
.toLowerCase() + "}");
packet = packetTitle.getConstructor(packetActions,
getNMSClass("IChatBaseComponent")).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) {
if (getProtocolVersion(player) >= 47 && isSpigot()) {
try {
// Send timings first
Object handle = getHandle(player);
Object connection = getField(handle.getClass(),
"playerConnection").get(handle);
Object[] actions = packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(),
"sendPacket");
Object packet = packetTitle.getConstructor(packetActions)
.newInstance(actions[3]);
sendPacket.invoke(connection, packet);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Reset the title settings
*
* @param player
* Player
*/
public void resetTitle(Player player) {
if (getProtocolVersion(player) >= 47 && isSpigot()) {
try {
// Send timings first
Object handle = getHandle(player);
Object connection = getField(handle.getClass(),
"playerConnection").get(handle);
Object[] actions = packetActions.getEnumConstants();
Method sendPacket = getMethod(connection.getClass(),
"sendPacket");
Object packet = packetTitle.getConstructor(packetActions)
.newInstance(actions[4]);
sendPacket.invoke(connection, packet);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Get the protocol version of the player
*
* @param player
* Player
* @return Protocol version
*/
private int getProtocolVersion(Player player) {
int version = 0;
try {
Object handle = getHandle(player);
Object connection = getField(handle.getClass(), "playerConnection")
.get(handle);
Object networkManager = getValue("networkManager", connection);
version = (Integer) getMethod("getVersion",
networkManager.getClass()).invoke(networkManager);
return version;
} catch (Exception ex) {
ex.printStackTrace();
}
return version;
}
/**
* Check if running spigot
*
* @return Spigot
*/
private boolean isSpigot() {
return Bukkit.getVersion().contains("Spigot");
}
/**
* Get class by url
*
* @param namespace
* Namespace url
* @return Class
*/
private Class<?> getClass(String namespace) {
try {
return Class.forName(namespace);
} catch (Exception e) {
}
return null;
}
private Field getField(String name, Class<?> clazz) throws Exception {
return clazz.getDeclaredField(name);
}
private Object getValue(String name, Object obj) throws Exception {
Field f = getField(name, obj.getClass());
f.setAccessible(true);
return f.get(obj);
}
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(Object obj) {
try {
return getMethod("getHandle", obj.getClass()).invoke(obj);
} 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 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;
}
}

View File

@ -1,413 +0,0 @@
////////////////////////////////////////////////////////////////////////////////////////////////////
// PlotSquared - A plot manager and world generator for the Bukkit API /
// Copyright (c) 2014 IntellectualSites/IntellectualCrafters /
// /
// This program is free software; you can redistribute it and/or modify /
// it under the terms of the GNU General Public License as published by /
// the Free Software Foundation; either version 3 of the License, or /
// (at your option) any later version. /
// /
// This program is distributed in the hope that it will be useful, /
// but WITHOUT ANY WARRANTY; without even the implied warranty of /
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /
// GNU General Public License for more details. /
// /
// You should have received a copy of the GNU General Public License /
// along with this program; if not, write to the Free Software Foundation, /
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /
// /
// You can contact us via: support@intellectualsites.com /
////////////////////////////////////////////////////////////////////////////////////////////////////
package com.intellectualcrafters.plot.unused;
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
*
* @author Maxim Van de Wynckel
* @version 1.0.3
*/
public class Title {
private static final Map<Class<?>, Class<?>> CORRESPONDING_TYPES;
static {
CORRESPONDING_TYPES = new HashMap<>();
}
/* Title packet */
private Class<?> packetTitle;
/* Title packet actions ENUM */
private Class<?> packetActions;
/* Chat serializer */
private Class<?> nmsChatSerializer;
/* 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
*/
public Title(final String title) {
this.title = title;
loadClasses();
}
/**
* Create a new 1.8 title
*
* @param title Title text
* @param subtitle Subtitle text
*/
public Title(final String title, final String subtitle) {
this.title = "";
this.title = title;
this.subtitle = subtitle;
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 Title(final String title, final String subtitle, final int fadeInTime, final int stayTime, final int fadeOutTime) {
this.title = "";
this.title = title;
this.subtitle = subtitle;
this.fadeInTime = fadeInTime;
this.stayTime = stayTime;
this.fadeOutTime = fadeOutTime;
loadClasses();
}
private static boolean equalsTypeArray(final Class<?>[] a, final 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
*/
private void loadClasses() {
this.packetTitle = getClass("org.spigotmc.ProtocolInjector$PacketTitle");
this.packetActions = getClass("org.spigotmc.ProtocolInjector$PacketTitle$Action");
this.nmsChatSerializer = getNMSClass("ChatSerializer");
}
/**
* Set the title color
*
* @param color Chat color
*/
public void setTitleColor(final ChatColor color) {
this.titleColor = color;
}
/**
* Set the subtitle color
*
* @param color Chat color
*/
public void setSubtitleColor(final ChatColor color) {
this.subtitleColor = color;
}
/**
* Set title fade in time
*
* @param time Time
*/
public void setFadeInTime(final int time) {
this.fadeInTime = time;
}
/**
* Set title fade out time
*
* @param time Time
*/
public void setFadeOutTime(final int time) {
this.fadeOutTime = time;
}
/**
* Set title stay time
*
* @param time Time
*/
public void setStayTime(final 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
*/
public void send(final Player player) {
if ((getProtocolVersion(player) >= 47) && isSpigot() && (this.packetTitle != null)) {
// First reset previous settings
resetTitle(player);
try {
// Send timings first
final Object handle = getHandle(player);
final Object connection = getField(handle.getClass(), "playerConnection").get(handle);
final Object[] actions = this.packetActions.getEnumConstants();
final Method sendPacket = getMethod(connection.getClass(), "sendPacket");
Object packet = this.packetTitle.getConstructor(this.packetActions, Integer.TYPE, Integer.TYPE, Integer.TYPE).newInstance(actions[2], 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, getNMSClass("IChatBaseComponent")).newInstance(actions[0], serialized);
sendPacket.invoke(connection, packet);
if (!this.subtitle.equals("")) {
// 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, getNMSClass("IChatBaseComponent")).newInstance(actions[1], serialized);
sendPacket.invoke(connection, packet);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
}
/**
* Broadcast the title to all players
*/
public void broadcast() {
for (final Player p : Bukkit.getOnlinePlayers()) {
send(p);
}
}
/**
* Clear the title
*
* @param player Player
*/
public void clearTitle(final Player player) {
if ((getProtocolVersion(player) >= 47) && isSpigot()) {
try {
// Send timings first
final Object handle = getHandle(player);
final Object connection = getField(handle.getClass(), "playerConnection").get(handle);
final Object[] actions = this.packetActions.getEnumConstants();
final Method sendPacket = getMethod(connection.getClass(), "sendPacket");
final Object packet = this.packetTitle.getConstructor(this.packetActions).newInstance(actions[3]);
sendPacket.invoke(connection, packet);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
/**
* Reset the title settings
*
* @param player Player
*/
public void resetTitle(final Player player) {
if ((getProtocolVersion(player) >= 47) && isSpigot()) {
try {
// Send timings first
final Object handle = getHandle(player);
final Object connection = getField(handle.getClass(), "playerConnection").get(handle);
final Object[] actions = this.packetActions.getEnumConstants();
final Method sendPacket = getMethod(connection.getClass(), "sendPacket");
final Object packet = this.packetTitle.getConstructor(this.packetActions).newInstance(actions[4]);
sendPacket.invoke(connection, packet);
} catch (final Exception e) {
e.printStackTrace();
}
}
}
/**
* Get the protocol version of the player
*
* @param player Player
*
* @return Protocol version
*/
private int getProtocolVersion(final Player player) {
int version = 0;
try {
final Object handle = getHandle(player);
final Object connection = getField(handle.getClass(), "playerConnection").get(handle);
final Object networkManager = getValue("networkManager", connection);
version = (Integer) getMethod("getVersion", networkManager.getClass()).invoke(networkManager);
return version;
} catch (final Exception ex) {
// ex.printStackTrace(); <-- spammy console
}
return version;
}
/**
* Check if running spigot
*
* @return Spigot
*/
private boolean isSpigot() {
return Bukkit.getVersion().contains("Spigot");
}
/**
* Get class by url
*
* @param namespace Namespace url
*
* @return Class
*/
private Class<?> getClass(final String namespace) {
try {
return Class.forName(namespace);
} catch (final Exception e) {
return null;
}
}
private Field getField(final String name, final Class<?> clazz) throws Exception {
return clazz.getDeclaredField(name);
}
private Object getValue(final String name, final Object obj) throws Exception {
final Field f = getField(name, obj.getClass());
f.setAccessible(true);
return f.get(obj);
}
private Class<?> getPrimitiveType(final Class<?> clazz) {
return CORRESPONDING_TYPES.containsKey(clazz) ? CORRESPONDING_TYPES.get(clazz) : clazz;
}
private Class<?>[] toPrimitiveTypeArray(final Class<?>[] classes) {
final int a = classes != null ? classes.length : 0;
final Class<?>[] types = new Class<?>[a];
for (int i = 0; i < a; i++) {
types[i] = getPrimitiveType(classes[i]);
}
return types;
}
private Object getHandle(final Object obj) {
try {
return getMethod("getHandle", obj.getClass()).invoke(obj);
} catch (final Exception e) {
e.printStackTrace();
return null;
}
}
private Method getMethod(final String name, final Class<?> clazz, final Class<?>... paramTypes) {
final Class<?>[] t = toPrimitiveTypeArray(paramTypes);
for (final Method m : clazz.getMethods()) {
final Class<?>[] types = toPrimitiveTypeArray(m.getParameterTypes());
if (m.getName().equals(name) && equalsTypeArray(types, t)) {
return m;
}
}
return null;
}
private String getVersion() {
final String name = Bukkit.getServer().getClass().getPackage().getName();
return name.substring(name.lastIndexOf('.') + 1) + ".";
}
private Class<?> getNMSClass(final String className) {
final String fullName = "net.minecraft.server." + getVersion() + className;
Class<?> clazz = null;
try {
clazz = Class.forName(fullName);
} catch (final Exception e) {
e.printStackTrace();
}
return clazz;
}
private Field getField(final Class<?> clazz, final String name) {
try {
final Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field;
} catch (final Exception e) {
e.printStackTrace();
return null;
}
}
private Method getMethod(final Class<?> clazz, final String name, final Class<?>... args) {
for (final 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(final Class<?>[] l1, final 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;
}
}