diff --git a/src/dev/_2lstudios/hamsterapi/HamsterAPI.java b/src/dev/_2lstudios/hamsterapi/HamsterAPI.java index b5bd673..79e7b4d 100644 --- a/src/dev/_2lstudios/hamsterapi/HamsterAPI.java +++ b/src/dev/_2lstudios/hamsterapi/HamsterAPI.java @@ -34,9 +34,9 @@ public static synchronized HamsterAPI getInstance() { } private static String getVersion(Server server) { - final String packageName = server.getClass().getPackage().getName(); - return packageName.substring(packageName.lastIndexOf('.') + 1); - } + final String packageName = server.getClass().getPackage().getName(); + return packageName.substring(packageName.lastIndexOf('.') + 1); + } private void initialize() { final Server server = getServer(); @@ -77,7 +77,7 @@ public void onEnable() { pluginManager.registerEvents(new PlayerQuitListener(hamsterPlayerManager), this); for (final Player player : server.getOnlinePlayers()) { - final HamsterPlayer hamsterPlayer = this.hamsterPlayerManager.get(player); + final HamsterPlayer hamsterPlayer = this.hamsterPlayerManager.add(player); hamsterPlayer.tryInject(); } @@ -92,7 +92,9 @@ public void onDisable() { for (final Player player : server.getOnlinePlayers()) { final HamsterPlayer hamsterPlayer = this.hamsterPlayerManager.get(player); - hamsterPlayer.uninject(); + if (hamsterPlayer != null) { + hamsterPlayer.uninject(); + } this.hamsterPlayerManager.remove(player); } diff --git a/src/dev/_2lstudios/hamsterapi/hamsterplayer/HamsterPlayer.java b/src/dev/_2lstudios/hamsterapi/hamsterplayer/HamsterPlayer.java index 34b1d3c..f43ec68 100644 --- a/src/dev/_2lstudios/hamsterapi/hamsterplayer/HamsterPlayer.java +++ b/src/dev/_2lstudios/hamsterapi/hamsterplayer/HamsterPlayer.java @@ -4,6 +4,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.channels.ClosedChannelException; +import java.util.UUID; import org.bukkit.Server; import org.bukkit.entity.Player; @@ -39,50 +40,93 @@ public Player getPlayer() { return this.player; } - // Sends an ActionBar to the HamsterPlayer - public void sendActionbar(final String text) { + public void sendActionbarPacketOld(final String text) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException { final Reflection reflection = hamsterAPI.getReflection(); + final Object chatAction = toChatBaseComponent.invoke(null, "{ \"text\":\"" + text + "\" }"); + final Object packet = reflection.getPacketPlayOutChat().getConstructor(iChatBaseComponentClass, byte.class) + .newInstance(chatAction, (byte) 2); - try { - Object chatAction = toChatBaseComponent.invoke(null, "{ \"text\":\"" + text + "\" }"); - Object packet = reflection.getNMSClass("PacketPlayOutChat") - .getConstructor(iChatBaseComponentClass, byte.class).newInstance(chatAction, (byte) 2); + sendPacket(packet); + } - sendPacket(packet); - } catch (final Exception e) { - e.printStackTrace(); + public void sendActionbarPacketNew(final String text) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException, NoSuchMethodException, SecurityException { + final Reflection reflection = hamsterAPI.getReflection(); + final Object chatAction = toChatBaseComponent.invoke(null, "{ \"text\":\"" + text + "\" }"); + final Class chatMessageTypeClass = reflection.getChatMessageType(); + final Object[] enumConstants = chatMessageTypeClass.getEnumConstants(); + final Object packet = reflection.getPacketPlayOutChat() + .getConstructor(iChatBaseComponentClass, chatMessageTypeClass, UUID.class).newInstance(chatAction, enumConstants[2], player.getUniqueId()); + + sendPacket(packet); + } + + // Sends an ActionBar to the HamsterPlayer + public void sendActionbar(final String text) { + try { + sendActionbarPacketNew(text); + } catch (final Exception e1) { + try { + sendActionbarPacketOld(text); + } catch (final Exception e2) { + hamsterAPI.getLogger().info("Failed to send actionbar packet to player " + player.getName() + "!"); + } } } - // Sends a Title to the HamsterPlayer - public void sendTitle(String title, String subtitle, int fadeInTime, int showTime, int fadeOutTime) { + public void sendTitlePacketOld(final String title, final String subtitle, final int fadeInTime, final int showTime, final int fadeOutTime) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, + SecurityException, InstantiationException, NoSuchFieldException { final Reflection reflection = hamsterAPI.getReflection(); - try { - Object chatTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + title + "\" }"); - Constructor titleConstructor = reflection.getNMSClass("PacketPlayOutTitle").getConstructor( - reflection.getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0], iChatBaseComponentClass, - int.class, int.class, int.class); - - Object packet = titleConstructor - .newInstance(reflection.getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0] - .getDeclaredField("TITLE").get(null), chatTitle, fadeInTime, showTime, fadeOutTime); - - Object chatSubTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + subtitle + "\" }"); - Constructor timingTitleConstructor = reflection.getNMSClass("PacketPlayOutTitle").getConstructor( - reflection.getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0], iChatBaseComponentClass, - int.class, int.class, int.class); - - Object timingPacket = timingTitleConstructor - .newInstance( - reflection.getNMSClass("PacketPlayOutTitle").getDeclaredClasses()[0] - .getDeclaredField("SUBTITLE").get(null), - chatSubTitle, fadeInTime, showTime, fadeOutTime); + final Object chatTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + title + "\" }"); + final Object chatSubTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + subtitle + "\" }"); + final Class enumTitleActionClass = reflection.getPacketPlayOutTitle().getDeclaredClasses()[0]; + final Constructor titleConstructor = reflection.getPacketPlayOutTitle().getConstructor(enumTitleActionClass, + iChatBaseComponentClass, int.class, int.class, int.class); + final Object titlePacket = titleConstructor.newInstance(enumTitleActionClass.getDeclaredField("TITLE").get(null), + chatTitle, fadeInTime, showTime, fadeOutTime); + final Object subtitlePacket = titleConstructor.newInstance( + enumTitleActionClass.getDeclaredField("SUBTITLE").get(null), chatSubTitle, fadeInTime, showTime, + fadeOutTime); + + sendPacket(titlePacket); + sendPacket(subtitlePacket); + } - sendPacket(packet); - sendPacket(timingPacket); - } catch (Exception e) { - e.printStackTrace(); + public void sendTitlePacketNew(final String title, final String subtitle, final int fadeInTime, final int showTime, final int fadeOutTime) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, + SecurityException, InstantiationException, NoSuchFieldException { + final Reflection reflection = hamsterAPI.getReflection(); + + final Constructor timingTitleConstructor = reflection.getClientboundSetTitlesAnimationPacket() + .getConstructor(int.class, int.class, int.class); + final Object timingPacket = timingTitleConstructor.newInstance(fadeInTime, showTime, fadeOutTime); + + final Object chatTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + title + "\" }"); + final Constructor titleConstructor = reflection.getClientboundSetTitleTextPacket() + .getConstructor(iChatBaseComponentClass); + final Object titlePacket = titleConstructor.newInstance(chatTitle); + + final Object chatSubTitle = toChatBaseComponent.invoke(null, "{ \"text\":\"" + subtitle + "\" }"); + final Constructor subTitleConstructor = reflection.getClientboundSetSubtitleTextPacket() + .getConstructor(iChatBaseComponentClass); + final Object subTitlePacket = subTitleConstructor.newInstance(chatSubTitle); + + sendPacket(timingPacket); + sendPacket(titlePacket); + sendPacket(subTitlePacket); + } + + // Sends a Title to the HamsterPlayer + public void sendTitle(final String title, final String subtitle, final int fadeInTime, final int showTime, final int fadeOutTime) { + try { + sendTitlePacketNew(title, subtitle, fadeInTime, showTime, fadeOutTime); + } catch (final Exception e1) { + try { + sendTitlePacketOld(title, subtitle, fadeInTime, showTime, fadeOutTime); + } catch (final Exception e2) { + hamsterAPI.getLogger().info("Failed to send title packet to player " + player.getName() + "!"); + } } } @@ -107,12 +151,12 @@ public void disconnect(final String reason) { try { final Object chatKick = toChatBaseComponent.invoke(null, "{ \"text\":\"" + reason + "\" }"); - final Object packet = reflection.getNMSClass("PacketPlayOutKickDisconnect") - .getConstructor(iChatBaseComponentClass).newInstance(chatKick); + final Object packet = reflection.getPacketPlayOutKickDisconnect().getConstructor(iChatBaseComponentClass) + .newInstance(chatKick); sendPacket(packet); } catch (final Exception e) { - e.printStackTrace(); + hamsterAPI.getLogger().info("Failed to send disconnect packet to player " + player.getName() + "!"); } hamsterAPI.getBungeeMessenger().sendPluginMessage("kickPlayer", player.getName(), reason); @@ -128,7 +172,7 @@ public void sendPacket(final Object packet) { try { sendPacketMethod.invoke(playerConnection, packet); } catch (final Exception e) { - e.printStackTrace(); + hamsterAPI.getLogger().info("Failed to send packet to player " + player.getName() + "!"); } } @@ -166,12 +210,12 @@ public void setup() final Reflection reflection = hamsterAPI.getReflection(); final Object handler = player.getClass().getDeclaredMethod("getHandle").invoke(player); - this.playerConnection = reflection.getField(handler, "playerConnection"); - this.networkManager = reflection.getField(playerConnection, "networkManager"); - this.channel = (Channel) reflection.getField(networkManager, "channel"); - this.iChatBaseComponentClass = reflection.getNMSClass("IChatBaseComponent"); + this.playerConnection = reflection.getField(handler, reflection.getPlayerConnection()); + this.networkManager = reflection.getField(playerConnection, reflection.getNetworkManager()); + this.channel = (Channel) reflection.getField(networkManager, Channel.class); + this.iChatBaseComponentClass = reflection.getIChatBaseComponent(); this.sendPacketMethod = this.playerConnection.getClass().getDeclaredMethod("sendPacket", - reflection.getNMSClass("Packet")); + reflection.getPacket()); this.toChatBaseComponent = iChatBaseComponentClass.getDeclaredClasses()[0].getDeclaredMethod("a", String.class); this.setup = true; @@ -208,7 +252,7 @@ public void inject() throws IllegalAccessException, InvocationTargetException, N "No ChannelHandler was found on the pipeline to inject " + hamsterChannelHandler); } - this.injected = true; + this.injected = true; } } diff --git a/src/dev/_2lstudios/hamsterapi/listeners/PlayerJoinListener.java b/src/dev/_2lstudios/hamsterapi/listeners/PlayerJoinListener.java index 3c02b04..67b60f1 100644 --- a/src/dev/_2lstudios/hamsterapi/listeners/PlayerJoinListener.java +++ b/src/dev/_2lstudios/hamsterapi/listeners/PlayerJoinListener.java @@ -2,6 +2,7 @@ import java.util.logging.Logger; +import org.bukkit.ChatColor; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; diff --git a/src/dev/_2lstudios/hamsterapi/messengers/BungeeMessenger.java b/src/dev/_2lstudios/hamsterapi/messengers/BungeeMessenger.java index 4ebe587..a804d1b 100644 --- a/src/dev/_2lstudios/hamsterapi/messengers/BungeeMessenger.java +++ b/src/dev/_2lstudios/hamsterapi/messengers/BungeeMessenger.java @@ -7,7 +7,7 @@ import org.bukkit.plugin.Plugin; import dev._2lstudios.hamsterapi.HamsterAPI; -import dev._2lstudios.hamsterapi.utils.Utilities; +import dev._2lstudios.hamsterapi.utils.BukkitUtils; public class BungeeMessenger { private final HamsterAPI instance; @@ -17,7 +17,7 @@ public BungeeMessenger(final HamsterAPI instance) { } public void sendPluginMessage(final String subChannel, final String... args) { - final Player messenger = Utilities.getRandomPlayer(); + final Player messenger = BukkitUtils.getRandomPlayer(); if (messenger != null) { final ByteArrayDataOutput out = ByteStreams.newDataOutput(); diff --git a/src/dev/_2lstudios/hamsterapi/utils/BufferIO.java b/src/dev/_2lstudios/hamsterapi/utils/BufferIO.java index 11ae478..b8c0fa8 100644 --- a/src/dev/_2lstudios/hamsterapi/utils/BufferIO.java +++ b/src/dev/_2lstudios/hamsterapi/utils/BufferIO.java @@ -22,10 +22,10 @@ public class BufferIO { private final int compressionThreshold; public BufferIO(final Reflection reflection, final String bukkitVersion, final int compressionThreshold) { - this.packetDataSerializerClass = reflection.getNMSClass("PacketDataSerializer"); - this.networkManagerClass = reflection.getNMSClass("NetworkManager"); - this.enumProtocolClass = reflection.getNMSClass("EnumProtocol"); - this.enumProtocolDirectionClass = reflection.getNMSClass("EnumProtocolDirection"); + this.packetDataSerializerClass = reflection.getPacketDataSerializer(); + this.networkManagerClass = reflection.getNetworkManager(); + this.enumProtocolClass = reflection.getEnumProtocol(); + this.enumProtocolDirectionClass = reflection.getEnumProtocolDirection(); this.inflater = new Inflater(); this.bukkitVersion = Integer.parseInt(bukkitVersion); this.compressionThreshold = compressionThreshold; diff --git a/src/dev/_2lstudios/hamsterapi/utils/Utilities.java b/src/dev/_2lstudios/hamsterapi/utils/BukkitUtils.java similarity index 93% rename from src/dev/_2lstudios/hamsterapi/utils/Utilities.java rename to src/dev/_2lstudios/hamsterapi/utils/BukkitUtils.java index 20cca70..43dcfee 100644 --- a/src/dev/_2lstudios/hamsterapi/utils/Utilities.java +++ b/src/dev/_2lstudios/hamsterapi/utils/BukkitUtils.java @@ -5,7 +5,7 @@ import org.bukkit.Bukkit; import org.bukkit.entity.Player; -public class Utilities { +public class BukkitUtils { public static Player getRandomPlayer() { final Collection players = Bukkit.getServer().getOnlinePlayers(); diff --git a/src/dev/_2lstudios/hamsterapi/utils/Reflection.java b/src/dev/_2lstudios/hamsterapi/utils/Reflection.java index 78d1661..0f58b6e 100644 --- a/src/dev/_2lstudios/hamsterapi/utils/Reflection.java +++ b/src/dev/_2lstudios/hamsterapi/utils/Reflection.java @@ -8,7 +8,7 @@ public class Reflection { private final String version; private final Map> classes = new HashMap<>(); - public Reflection(String version) { + public Reflection(final String version) { this.version = version; } @@ -24,26 +24,31 @@ public Class getClass(final String className) throws ClassNotFoundException { return craftBukkitClass; } - public Object getField(final Object object, final String fieldName) - throws NoSuchFieldException, IllegalAccessException { + public Object getField(final Object object, final Class fieldType) throws IllegalAccessException { if (object == null) { throw new IllegalAccessException("Tried to access field from a null object"); } - final Object fieldValue; - final Field field = object.getClass().getField(fieldName); - final boolean accessible = field.isAccessible(); + for (final Field field : object.getClass().getFields()) { + if (field.getType().equals(fieldType)) { + final boolean accessible = field.isAccessible(); - field.setAccessible(true); - fieldValue = field.get(object); - field.setAccessible(accessible); + field.setAccessible(true); - return fieldValue; + final Object value = field.get(object); + + field.setAccessible(accessible); + + return value; + } + } + + return null; } - public Class getNewNMSClass117(String key) { + private Class getNewNMClass(String key) { try { - return getClass("net.minecraft.server." + key); + return getClass("net.minecraft." + key); } catch (final ClassNotFoundException e) { /* Ignored */ } @@ -51,17 +56,20 @@ public Class getNewNMSClass117(String key) { return null; } - public Class getNMSClass(String key) { + private Class getNetMinecraftClass(String key) { try { - return getClass("net.minecraft.server." + this.version + "." + key); - } catch (final ClassNotFoundException e1) { + final int lastDot = key.lastIndexOf("."); + final String lastKey = key.substring(lastDot > 0 ? lastDot + 1 : 0, key.length()); + + return getClass("net.minecraft.server." + this.version + "." + lastKey); + } catch (final ClassNotFoundException e) { /* Ignored */ } - return getNewNMSClass117(key); + return getNewNMClass(key); } - public Class getCraftBukkitClass(String key) { + private Class getCraftBukkitClass(String key) { try { getClass("org.bukkit.craftbukkit." + this.version + "." + key); } catch (final ClassNotFoundException e) { @@ -70,4 +78,72 @@ public Class getCraftBukkitClass(String key) { return null; } + + public Class getItemStack() { + return getNetMinecraftClass("world.item.ItemStack"); + } + + public Class getMinecraftKey() { + return getNetMinecraftClass("resources.MinecraftKey"); + } + + public Class getEnumProtocol() { + return getNetMinecraftClass("network.EnumProtocol"); + } + + public Class getEnumProtocolDirection() { + return getNetMinecraftClass("network.protocol.EnumProtocolDirection"); + } + + public Class getNetworkManager() { + return getNetMinecraftClass("network.NetworkManager"); + } + + public Class getPacketDataSerializer() { + return getNetMinecraftClass("network.PacketDataSerializer"); + } + + public Class getPacket() { + return getNetMinecraftClass("network.protocol.Packet"); + } + + public Class getIChatBaseComponent() { + return getNetMinecraftClass("network.chat.IChatBaseComponent"); + } + + public Class getPacketPlayOutKickDisconnect() { + return getNetMinecraftClass("network.protocol.game.PacketPlayOutKickDisconnect"); + } + + public Class getPacketPlayOutTitle() { + return getNetMinecraftClass("network.protocol.game.PacketPlayOutTitle"); + } + + public Class getPacketPlayOutChat() { + return getNetMinecraftClass("network.protocol.game.PacketPlayOutChat"); + } + + public Class getPlayerConnection() { + return getNetMinecraftClass("server.network.PlayerConnection"); + } + + public Class getClientboundSetTitlesAnimationPacket() { + return getNetMinecraftClass("network.protocol.game.ClientboundSetTitlesAnimationPacket"); + } + + public Class getClientboundSetTitleTextPacket() { + return getNetMinecraftClass("network.protocol.game.ClientboundSetTitleTextPacket"); + } + + public Class getClientboundSetSubtitleTextPacket() { + return getNetMinecraftClass("network.protocol.game.ClientboundSetSubtitleTextPacket"); + } + + public Class getChatMessageType() { + return getNetMinecraftClass("network.chat.ChatMessageType"); + } + + public Class getCraftItemStack() { + return getCraftBukkitClass("inventory.CraftItemStack"); + } } diff --git a/src/dev/_2lstudios/hamsterapi/wrappers/PacketWrapper.java b/src/dev/_2lstudios/hamsterapi/wrappers/PacketWrapper.java index 4f5f13e..dce8db5 100644 --- a/src/dev/_2lstudios/hamsterapi/wrappers/PacketWrapper.java +++ b/src/dev/_2lstudios/hamsterapi/wrappers/PacketWrapper.java @@ -26,11 +26,11 @@ public class PacketWrapper { public PacketWrapper(final Object packet) { final Reflection reflection = HamsterAPI.getInstance().getReflection(); - final Class minecraftKeyClass = reflection.getNMSClass("MinecraftKey"); + final Class minecraftKeyClass = reflection.getMinecraftKey(); final Class packetClass = packet.getClass(); - final Class itemStackClass = reflection.getNMSClass("ItemStack"); + final Class itemStackClass = reflection.getItemStack(); - this.craftItemStackClass = reflection.getCraftBukkitClass("inventory.CraftItemStack"); + this.craftItemStackClass = reflection.getCraftItemStack(); this.packet = packet; this.name = packetClass.getSimpleName();