package eu.univento.core.api; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.Plugin; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.function.Consumer; /** * @author Janhektor This class in licensed under GPLv3 For more information * look at http://www.gnu.org/licenses/gpl-3.0 */ public final class SignInput implements Listener, Runnable { private final static String VERSION; static { String path = Bukkit.getServer().getClass().getPackage().getName(); VERSION = path.substring(path.lastIndexOf(".") + 1, path.length()); } private final Plugin plugin; private final Map> inputResults; public SignInput(Plugin plugin) { this.plugin = plugin; this.inputResults = new HashMap>(); Bukkit.getScheduler().runTaskTimer(this.plugin, this, 0L, 20 * 3L); } /** * Use this method to read the SignInput from a player The accept()-method * of your consumer will be called, when the player close the sign * * @return boolean successful * @param p * - The Player, who have to type an input * @param result * - The consumer (String[]) for the result; String[] contains * strings for 4 lines */ public boolean readInput(Player p, Consumer result) { inputResults.put(p.getUniqueId(), result); try { Class packetClass = Class.forName(getNMSClass("PacketPlayOutOpenSignEditor")); Class blockPositionClass = Class.forName(getNMSClass("BlockPosition")); Constructor blockPosCon = blockPositionClass .getConstructor(new Class[] { int.class, int.class, int.class }); Object blockPosition = blockPosCon.newInstance(new Object[] { 0, 0, 0 }); Constructor packetCon = packetClass.getConstructor(new Class[] { blockPositionClass }); Object packet = packetCon.newInstance(new Object[] { blockPosition }); Method getHandle = p.getClass().getMethod("getHandle"); Object nmsPlayer = getHandle.invoke(p); Field pConnectionField = nmsPlayer.getClass().getField("playerConnection"); Object pConnection = pConnectionField.get(nmsPlayer); Method sendMethod = pConnection.getClass().getMethod("sendPacket", new Class[] { Class.forName(getNMSClass("Packet")) }); sendMethod.invoke(pConnection, new Object[] { packet }); return true; } catch (Exception ex) { ex.printStackTrace(); return false; } } /* Garbage Collection */ @Override public void run() { for (UUID uuid : inputResults.keySet()) { if (Bukkit.getPlayer(uuid) == null) inputResults.remove(uuid); } } /* Events */ @EventHandler public void onJoin(PlayerJoinEvent e) { Player p = e.getPlayer(); getNettyChannel(p).pipeline().addAfter("decoder", "signListener", new MessageToMessageDecoder() { @Override protected void decode(ChannelHandlerContext chc, Object packet, List packetList) throws Exception { if (instanceOf(packet, getNMSClass("PacketPlayInUpdateSign"))) { Method bMethod = packet.getClass().getMethod("b"); Object chatBaseComponents = bMethod.invoke(packet); String[] lines = new String[4]; for (int i = 0; i < 4; i++) { Object chatComponent = Array.get(chatBaseComponents, i); Method getText = chatComponent.getClass().getMethod("getText"); lines[i] = (String) getText.invoke(chatComponent); } if (inputResults.containsKey(p.getUniqueId())) { inputResults.get(p.getUniqueId()).accept(lines); inputResults.remove(p.getUniqueId()); } } packetList.add(packet); } }); } /* Util Methods */ private Channel getNettyChannel(Player p) { Channel ch = null; try { Method getHandle = p.getClass().getMethod("getHandle"); Object nmsPlayer = getHandle.invoke(p); Field pConnectionField = nmsPlayer.getClass().getField("playerConnection"); Object pConnection = pConnectionField.get(nmsPlayer); Field networkManagerField = pConnection.getClass().getField("networkManager"); Object networkManager = networkManagerField.get(pConnection); ch = (Channel) networkManager.getClass().getField("k").get(networkManager); } catch (Exception ex) { ex.printStackTrace(); } return ch; } private boolean instanceOf(Object o, String className) { try { return Class.forName(className).isInstance(o); } catch (ClassNotFoundException e) { e.printStackTrace(); } return false; } private String getNMSClass(String className) { return "net.minecraft.server." + VERSION + "." + className; } }