LagAssist is a plugin that provides tools to prevent, analyse or resolve lag.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

559 lines
16 KiB

package cx.sfy.LagAssist.packets;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.command.CommandMap;
import org.bukkit.command.PluginCommand;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.MapMeta;
import org.bukkit.map.MapView;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.SimplePluginManager;
import org.bukkit.scoreboard.Scoreboard;
import org.json.simple.JSONObject;
import cx.sfy.LagAssist.Main;
import cx.sfy.LagAssist.utils.V1_8;
import cx.sfy.LagAssist.utils.VersionMgr;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.ComponentBuilder;
public class Reflection {
private static Map<Class<?>, Field[]> cached = new HashMap<Class<?>, Field[]>();
private static String version = ServerPackage.getServerVersion();
public static enum Classes {
CraftWorld(), CraftBlock(), CraftPlayer(), Material(), MapMeta(), WorldServer(), ChunkProviderServer(),
PacketPlayOutTitle(), IChatBaseComponent(), World();
private Class<?> type;
public Class<?> getType() {
return type;
}
}
public static enum Methods {
setMapId(), getMapId(), getPlayerHandle(), getBlockType(), getChunkProviderServer(), chunkExists(),
getIChatBaseComponent(), setViewDistance();
private Method mthd;
public Method getMethod() {
return mthd;
}
}
public static void Enabler() {
// PUTTING CLASSES IN ENUM.
Classes.CraftWorld.type = getClass("{cb}.CraftWorld");
Classes.World.type = getClass("{b}.World");
Classes.CraftBlock.type = getClass("{cb}.block.CraftBlock");
Classes.CraftPlayer.type = getClass("{cb}.entity.CraftPlayer");
Classes.Material.type = getClass("{b}.Material");
Classes.MapMeta.type = getClass("{b}.inventory.meta.MapMeta");
Classes.WorldServer.type = getClass("{nms}.WorldServer");
Classes.ChunkProviderServer.type = getClass("{nms}.ChunkProviderServer");
Classes.IChatBaseComponent.type = getClass("{nms}.IChatBaseComponent");
Classes.PacketPlayOutTitle.type = getClass("{nms}.PacketPlayOutTitle");
// PUTTING METHODS IN ENUM.
Methods.setMapId.mthd = getMethod(Classes.MapMeta.getType(), "setMapId", int.class);
Methods.getMapId.mthd = getMethod(Classes.MapMeta.getType(), "getMapId");
Methods.getPlayerHandle.mthd = getMethod(Classes.CraftPlayer.getType(), "getHandle");
Methods.getBlockType.mthd = getMethod(Classes.CraftBlock.getType(), "getType");
Methods.getChunkProviderServer.mthd = getMethod(Classes.WorldServer.getType(), "getChunkProviderServer");
Methods.getIChatBaseComponent.mthd = getMethod(Classes.IChatBaseComponent.getType(), "a", String.class);
Methods.chunkExists.mthd = getMethod(Classes.ChunkProviderServer.getType(), VersionMgr.ChunkExistsName(),
int.class, int.class);
Methods.setViewDistance.mthd = getMethod(Classes.World.getType(), "setViewDistance", int.class);
}
@SuppressWarnings("deprecation")
public static void sendAction(Player player, String s) {
if (Bukkit.getVersion().contains("1.8")) {
V1_8.sendActionbar(player, s);
} else {
player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new ComponentBuilder(s).create());
}
}
public void sendTitle(Player p, int fadein, int stay, int fadeout, String title, String subtitle) {
try {
Object enumTitle = Classes.PacketPlayOutTitle.getType().getDeclaredClasses()[0].getField("TITLE").get(null);
Object enumSubtitle = Classes.PacketPlayOutTitle.getType().getDeclaredClasses()[0].getField("SUBTITLE")
.get(null);
Object titlebase = runMethod(null, Methods.getIChatBaseComponent.getMethod(),
"{\"text\": \"" + title + "\"}");
Object subtitlebase = runMethod(null, Methods.getIChatBaseComponent.getMethod(),
"{\"text\": \"" + subtitle + "\"}");
Class<?> packetcls = Classes.PacketPlayOutTitle.getType();
Constructor<?> constr = packetcls.getConstructor(
Classes.PacketPlayOutTitle.getType().getDeclaredClasses()[0], Classes.IChatBaseComponent.getType(),
int.class, int.class, int.class);
Object packetTitle = constr.newInstance(enumTitle, titlebase, fadein, stay, fadeout);
Object packetSubtitle = constr.newInstance(enumSubtitle, subtitlebase, fadein, stay, fadeout);
sendPlayerPacket(p, packetTitle);
sendPlayerPacket(p, packetSubtitle);
}
catch (Exception e) {
e.printStackTrace();
}
}
public static MapView getMapView(int i) {
Class<?> buk = Bukkit.class;
try {
Method getMap = buk.getDeclaredMethod("getMap", VersionMgr.isNewMaterials() ? int.class : short.class);
return (MapView) (VersionMgr.isNewMaterials() ? getMap.invoke(null, i) : getMap.invoke(null, (short) i));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static int getId(MapView view) {
if (view == null) {
return 0;
}
Class<?> mv = MapView.class;
try {
Method getMap = mv.getDeclaredMethod("getId");
Object obj = getMap.invoke(view);
if (obj instanceof Short) {
return (short) obj;
} else if (obj instanceof Integer) {
return (int) obj;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@SuppressWarnings("unchecked")
static JSONObject convert(String text) {
JSONObject json = new JSONObject();
json.put("text", text);
return json;
}
public static Class<?> getClass(String classname) {
try {
String path = classname.replace("{nms}", "net.minecraft.server." + version)
.replace("{nm}", "net.minecraft." + version).replace("{cb}", "org.bukkit.craftbukkit." + version)
.replace("{b}", "org.bukkit");
return Class.forName(path);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Object getCraftWorld(World w) {
Class<?> crwclass = Classes.CraftWorld.getType();
System.out.println(crwclass.getName());
Object craftworld = crwclass.cast(w);
return craftworld;
}
public static Object getWorldServer(Object craftWorld) {
try {
return getFieldValue(craftWorld, "world");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Object getChunkProvider(Object worldServer) {
try {
return runMethod(worldServer, Methods.getChunkProviderServer.getMethod());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static boolean isChunkExistent(Object chunkProvider, int x, int z) {
try {
return (boolean) runMethod(chunkProvider, Methods.chunkExists.getMethod(), x, z);
} catch (Exception e) {
e.printStackTrace();
}
// NOTE: ASSUMING TRUE IN ORDER TO IGNORE IF EXCEPTION POPS UP.
return true;
}
public static boolean isTile(Block b) {
return !b.getState().getClass().getSimpleName().toLowerCase().contains("craftblockstate");
}
public static Entity getEntity(Location l) {
try {
Collection<Entity> ents = l.getWorld().getNearbyEntities(l, 1, 1, 1);
for (Entity ent : ents) {
return ent;
}
} catch (Exception e) {
return null;
}
return null;
}
public static void setmapId(ItemStack s, int id) {
MapMeta mapm = (MapMeta) s.getItemMeta();
try {
runMethod(mapm, Methods.setMapId.getMethod(), id);
} catch (Exception e) {
e.printStackTrace();
}
s.setItemMeta(mapm);
}
public static int getMapId(ItemStack s) {
MapMeta mapm = (MapMeta) s.getItemMeta();
try {
return (int) runMethod(mapm, Methods.getMapId.getMethod());
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public static Object getNmsPlayer(Player p) {
if (p == null) {
return null;
}
Method getHandle;
try {
getHandle = p.getClass().getMethod("getHandle");
return getHandle.invoke(p);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Object getNmsScoreboard(Scoreboard s) throws Exception {
Method getHandle = s.getClass().getMethod("getHandle");
return getHandle.invoke(s);
}
public static String getObjectSerialized(Object obj) {
YamlConfiguration conf = new YamlConfiguration();
Class<?> cls = obj.getClass();
String loc = cls.getSimpleName();
createObjectSerialized(conf, loc, obj, 0);
return conf.saveToString();
}
public static void createObjectSerialized(YamlConfiguration conf, String loc, Object obj, int recursiveness) {
Class<?> cls = obj.getClass();
if (!cached.containsKey(cls)) {
cached.put(cls, cls.getDeclaredFields());
}
for (Field fl : cached.get(cls)) {
fl.setAccessible(true);
Object newobj = getFieldValue(fl, obj);
if (newobj == null) {
continue;
}
String name = fl.getName();
String type = newobj.getClass().getSimpleName();
String newloc = loc + "." + type + "." + name;
if (isSimple(newobj) || recursiveness > 1) {
conf.set(newloc, convertToString(newobj));
continue;
}
createObjectSerialized(conf, newloc, newobj, recursiveness + 1);
}
}
// public static long getObjectSize(Object obj, int recursiveness) {
// if (obj == null) {
// return 0;
// }
//
//
// if (obj instanceof Serializable) {
// return getSerializedSize((Serializable) obj);
// }
//
// Class<?> cls = obj.getClass();
//
// long size = 0;
//
// if (!cached.containsKey(cls)) {
// cached.put(cls, cls.getDeclaredFields());
// }
//
// for (Field fl : cached.get(cls)) {
// fl.setAccessible(true);
// Object nobj = getFieldValue(fl, obj);
// size+=getObjectSize(nobj, recursiveness+1);
// }
//
// return size;
// }
//
// private static long getSerializedSize(Serializable obj) {
//
// try {
// ByteArrayOutputStream bos = new ByteArrayOutputStream();
// ObjectOutputStream oos = new ObjectOutputStream(bos);
// oos.writeObject(obj);
// oos.flush();
// byte[] data = bos.toByteArray();
//
// return data.length;
// } catch (Exception e) {
// return 0;
// }
// }
public static Object getFieldValue(Object instance, String fieldName) throws Exception {
Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(instance);
}
@SuppressWarnings("unchecked")
public static <T> T getFieldValue(Field field, Object obj) {
try {
return (T) field.get(obj);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Field getField(Class<?> clazz, String fieldName) throws Exception {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... resl) {
Method method;
try {
method = clazz.getDeclaredMethod(methodName, resl);
method.setAccessible(true);
return method;
} catch (Exception e) {
return null;
}
}
public static Object runMethod(Object obj, Method m, Object... resl) throws Exception {
return m.invoke(obj, resl);
}
public static Object runMethod(Object obj, String name, Object... resl) throws Exception {
Class<?>[] classes = new Class<?>[resl.length];
for (int i = 0; i < resl.length; i++) {
classes[i] = resl[i].getClass();
}
return getMethod(obj.getClass(), name, classes).invoke(obj, resl);
}
public static void setValue(Object instance, String field, Object value) {
try {
Field f = instance.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(instance, value);
} catch (Throwable t) {
t.printStackTrace();
}
}
public static void sendAllPacket(Object packet) throws Exception {
for (Player p : Bukkit.getOnlinePlayers()) {
Object nmsPlayer = getNmsPlayer(p);
Object connection = nmsPlayer.getClass().getField("playerConnection").get(nmsPlayer);
connection.getClass().getMethod("sendPacket", getClass("{nms}.Packet")).invoke(connection, packet);
}
}
public static int getPing(Player p) {
try {
Object entityPlayer = Methods.getPlayerHandle.getMethod().invoke(p);
return (int) getFieldValue(entityPlayer, "ping");
} catch (Exception e) {
return -1;
}
}
public static void sendListPacket(List<String> players, Object packet) {
try {
for (String name : players) {
Object nmsPlayer = getNmsPlayer(Bukkit.getPlayer(name));
Object connection = nmsPlayer.getClass().getField("playerConnection").get(nmsPlayer);
connection.getClass().getMethod("sendPacket", getClass("{nms}.Packet")).invoke(connection, packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void sendPlayerPacket(Player p, Object packet) throws Exception {
Object nmsPlayer = getNmsPlayer(p);
Object connection = nmsPlayer.getClass().getField("playerConnection").get(nmsPlayer);
connection.getClass().getMethod("sendPacket", getClass("{nms}.Packet")).invoke(connection, packet);
}
public static PluginCommand getCommand(String name, Plugin plugin) {
PluginCommand command = null;
try {
Constructor<PluginCommand> c = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
c.setAccessible(true);
command = c.newInstance(name, plugin);
} catch (Exception e) {
e.printStackTrace();
}
return command;
}
public static CommandMap getCommandMap() {
CommandMap commandMap = null;
try {
if (Bukkit.getPluginManager() instanceof SimplePluginManager) {
Field f = SimplePluginManager.class.getDeclaredField("commandMap");
f.setAccessible(true);
commandMap = (CommandMap) f.get(Bukkit.getPluginManager());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return commandMap;
}
private static String convertToString(Object obj) {
Class<?> cls = obj.getClass();
if (cls.isArray()) {
String stg = "";
int length = Array.getLength(obj);
for (int i = 0; i < length; i++) {
stg += convertToString(Array.get(obj, i));
}
return stg;
}
return obj.toString();
}
private static boolean isSimple(Object obj) {
Class<?> cls = obj.getClass();
if (cls.isPrimitive()) {
return true;
}
if (cls.isEnum()) {
return true;
}
if (cls == Integer.class) {
return true;
}
if (cls == Boolean.class) {
return true;
}
if (cls == String.class) {
return true;
}
if (cls == Character.class) {
return true;
}
if (cls == Byte.class) {
return true;
}
if (cls == Short.class) {
return true;
}
if (cls == Float.class) {
return true;
}
if (cls == Double.class) {
return true;
}
if (cls == Long.class) {
return true;
}
return false;
}
public static void setViewDistance(World w, int amount) {
try {
runMethod(w, Methods.setViewDistance.mthd, amount);
Main.sendDebug("Succesfully set view distance at " + amount + " in world " + w.getName(), 1);
} catch (Exception e) {
Main.sendDebug("Exception at setViewDistance (" + w.getName() + ", " + amount + ")", 1);
}
}
}