From 25511f898c7352a03ccfdeb3902ee353b69b5d6d Mon Sep 17 00:00:00 2001 From: Luck Date: Wed, 7 Dec 2016 21:08:30 +0000 Subject: [PATCH] implement clickable message base - towards #68 --- bukkit/pom.xml | 8 +- .../luckperms/bukkit/BukkitSenderFactory.java | 11 ++ .../compat/BukkitJsonMessageHandler.java | 122 ++++++++++++++++++ .../bukkit/compat/MessageHandler.java | 68 ++++++++++ .../compat/SpigotJsonMessageHandler.java | 40 ++++++ bungee/pom.xml | 4 + .../luckperms/bungee/BungeeSenderFactory.java | 12 ++ common/pom.xml | 7 + .../commands/sender/AbstractSender.java | 10 ++ .../common/commands/sender/Sender.java | 9 ++ .../common/commands/sender/SenderFactory.java | 4 + .../lucko/luckperms/common/data/Importer.java | 7 + sponge/pom.xml | 4 + .../luckperms/sponge/SpongeSenderFactory.java | 11 ++ 14 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/BukkitJsonMessageHandler.java create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/MessageHandler.java create mode 100644 bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/SpigotJsonMessageHandler.java diff --git a/bukkit/pom.xml b/bukkit/pom.xml index c8c372b0c..70e6cecc2 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -50,6 +50,10 @@ + + io.github.mkremins.fanciful + me.lucko.luckperms.lib.fanciful + org.slf4j me.lucko.luckperms.lib.slf4j @@ -108,8 +112,8 @@ - org.bukkit - bukkit + org.spigotmc + spigot-api 1.8.8-R0.1-SNAPSHOT provided diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSenderFactory.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSenderFactory.java index 7a9ec271c..cd8faa334 100644 --- a/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSenderFactory.java +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/BukkitSenderFactory.java @@ -22,6 +22,7 @@ package me.lucko.luckperms.bukkit; +import me.lucko.luckperms.bukkit.compat.MessageHandler; import me.lucko.luckperms.common.LuckPermsPlugin; import me.lucko.luckperms.common.commands.sender.SenderFactory; import me.lucko.luckperms.common.constants.Constants; @@ -31,9 +32,14 @@ import org.bukkit.entity.Player; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + public class BukkitSenderFactory extends SenderFactory { + private final MessageHandler messageHandler; + public BukkitSenderFactory(LuckPermsPlugin plugin) { super(plugin); + messageHandler = new MessageHandler(); } @Override @@ -57,6 +63,11 @@ public class BukkitSenderFactory extends SenderFactory { sender.sendMessage(s); } + @Override + protected void sendMessage(CommandSender sender, FancyMessage message) { + messageHandler.sendJsonMessage(sender, message); + } + @Override protected boolean hasPermission(CommandSender sender, String node) { return sender.hasPermission(node); diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/BukkitJsonMessageHandler.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/BukkitJsonMessageHandler.java new file mode 100644 index 000000000..00347a28a --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/BukkitJsonMessageHandler.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.bukkit.compat; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class BukkitJsonMessageHandler { + private static boolean setup = false; + private static boolean triedAndFailed = false; + + private static Method GET_HANDLE_METHOD; + private static Field PLAYER_CONNECTION_FIELD; + private static Method SEND_PACKET_METHOD; + private static Constructor PACKET_CHAT_CONSTRUCTOR; + private static Method SERIALIZE_METHOD; + + private static void setup(Object player) throws Exception { + Class craftPlayerClass = player.getClass(); + GET_HANDLE_METHOD = craftPlayerClass.getDeclaredMethod("getHandle"); + + Object handleObject = GET_HANDLE_METHOD.invoke(player); + Class handleClass = handleObject.getClass(); + + PLAYER_CONNECTION_FIELD = handleClass.getDeclaredField("playerConnection"); + + Object playerConnectionObject = PLAYER_CONNECTION_FIELD.get(handleObject); + + Method[] playerConnectionMethods = playerConnectionObject.getClass().getDeclaredMethods(); + for (Method m : playerConnectionMethods) { + if (m.getName().equals("sendPacket")) { + SEND_PACKET_METHOD = m; + break; + } + } + + Class packetChatClass = Class.forName(getVersionedClassName("PacketPlayOutChat")); + Constructor[] packetConstructors = packetChatClass.getDeclaredConstructors(); + for (Constructor c : packetConstructors) { + Class[] parameters = c.getParameterTypes(); + if (parameters.length == 1 && parameters[0].getName().endsWith("IChatBaseComponent")) { + PACKET_CHAT_CONSTRUCTOR = c; + break; + } + } + + Class baseComponentClass = Class.forName(getVersionedClassName("IChatBaseComponent")); + Class chatSerializerClass = baseComponentClass.getClasses()[0]; + + SERIALIZE_METHOD = chatSerializerClass.getDeclaredMethod("a", String.class); + } + + private static String getVersionedClassName(String className) { + Class server = Bukkit.getServer().getClass(); + if (!server.getSimpleName().equals("CraftServer")) { + throw new RuntimeException("Couldn't reflect into server " + server); + } + + String version; + if (server.getName().equals("org.bukkit.craftbukkit.CraftServer")) { + // Non versioned class + version = "."; + } else { + version = server.getName().substring("org.bukkit.craftbukkit".length()); + version = version.substring(0, version.length() - "CraftServer".length()); + } + + return "net.minecraft.server" + version + className; + } + + private static synchronized boolean trySetup(Object player) { + if (setup) return true; + if (triedAndFailed) return false; + + try { + setup(player); + setup = true; + return true; + } catch (Exception e) { + triedAndFailed = true; + return false; + } + } + + public boolean sendJsonMessage(Player player, String json) { + if (!trySetup(player)) { + return false; + } + + try { + Object connection = PLAYER_CONNECTION_FIELD.get(GET_HANDLE_METHOD.invoke(player)); + SEND_PACKET_METHOD.invoke(connection, PACKET_CHAT_CONSTRUCTOR.newInstance(SERIALIZE_METHOD.invoke(null, json))); + return true; + } catch (Exception e) { + return false; + } + } +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/MessageHandler.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/MessageHandler.java new file mode 100644 index 000000000..aa937f60f --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/MessageHandler.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.bukkit.compat; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import io.github.mkremins.fanciful.FancyMessage; + +public class MessageHandler { + private final BukkitJsonMessageHandler bukkitHandler; + private final SpigotJsonMessageHandler spigotHandler; + + public MessageHandler() { + bukkitHandler = new BukkitJsonMessageHandler(); + spigotHandler = isSpigot() ? new SpigotJsonMessageHandler() : null; + } + + public void sendJsonMessage(CommandSender sender, FancyMessage message) { + if (sender instanceof Player) { + Player player = (Player) sender; + String json = message.toJSONString(); + + // Try Bukkit. + if (bukkitHandler.sendJsonMessage(player, json)) { + return; + } + + // Try Spigot. + if (spigotHandler != null && spigotHandler.sendJsonMessage(player, json)) { + return; + } + } + + // Fallback to Bukkit + sender.sendMessage(message.toOldMessageFormat()); + } + + private static boolean isSpigot() { + try { + Class.forName("net.md_5.bungee.chat.ComponentSerializer"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + +} diff --git a/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/SpigotJsonMessageHandler.java b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/SpigotJsonMessageHandler.java new file mode 100644 index 000000000..764eabf1c --- /dev/null +++ b/bukkit/src/main/java/me/lucko/luckperms/bukkit/compat/SpigotJsonMessageHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Lucko (Luck) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.bukkit.compat; + +import net.md_5.bungee.chat.ComponentSerializer; + +import org.bukkit.entity.Player; + +public class SpigotJsonMessageHandler { + + public boolean sendJsonMessage(Player player, String json) { + try { + player.spigot().sendMessage(ComponentSerializer.parse(json)); + return true; + } catch (Exception e) { + return false; + } + } + +} diff --git a/bungee/pom.xml b/bungee/pom.xml index 2287aee1e..4d4a63377 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -45,6 +45,10 @@ false + + io.github.mkremins.fanciful + me.lucko.luckperms.lib.fanciful + org.slf4j me.lucko.luckperms.lib.slf4j diff --git a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeSenderFactory.java b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeSenderFactory.java index 40123ebd2..312e873e8 100644 --- a/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeSenderFactory.java +++ b/bungee/src/main/java/me/lucko/luckperms/bungee/BungeeSenderFactory.java @@ -29,9 +29,12 @@ import me.lucko.luckperms.common.constants.Constants; import net.md_5.bungee.api.CommandSender; import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.chat.ComponentSerializer; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + public class BungeeSenderFactory extends SenderFactory { public BungeeSenderFactory(LuckPermsPlugin plugin) { super(plugin); @@ -58,6 +61,15 @@ public class BungeeSenderFactory extends SenderFactory { sender.sendMessage(new TextComponent(s)); } + @Override + protected void sendMessage(CommandSender sender, FancyMessage message) { + try { + sender.sendMessage(ComponentSerializer.parse(message.toJSONString())); + } catch (Exception e) { + sendMessage(sender, message.toOldMessageFormat()); + } + } + @Override protected boolean hasPermission(CommandSender sender, String node) { return sender.hasPermission(node); diff --git a/common/pom.xml b/common/pom.xml index 946e8036d..720c2d10c 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -32,6 +32,13 @@ ${project.version} compile + + + io.github.mkremins + fanciful + 1.1.0 + compile + com.zaxxer diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/sender/AbstractSender.java b/common/src/main/java/me/lucko/luckperms/common/commands/sender/AbstractSender.java index 1b7ed220a..7baac980c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/sender/AbstractSender.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/sender/AbstractSender.java @@ -31,6 +31,8 @@ import me.lucko.luckperms.common.constants.Permission; import java.lang.ref.WeakReference; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + /** * Simple implementation of {@link Sender} using a {@link SenderFactory} * @@ -60,6 +62,14 @@ public class AbstractSender implements Sender { } } + @Override + public void sendMessage(FancyMessage message) { + final T t = ref.get(); + if (t != null) { + factory.sendMessage(t, message); + } + } + @Override public boolean hasPermission(Permission permission) { if (isConsole()) return true; diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/sender/Sender.java b/common/src/main/java/me/lucko/luckperms/common/commands/sender/Sender.java index d8c3aaedf..221feb3c1 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/sender/Sender.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/sender/Sender.java @@ -28,6 +28,8 @@ import me.lucko.luckperms.common.constants.Permission; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + /** * Wrapper interface to represent a CommandSender/CommandSource within the common command implementations. */ @@ -61,6 +63,13 @@ public interface Sender { */ void sendMessage(String s); + /** + * Send a json message to the Sender. + * + * @param message the message to send. + */ + void sendMessage(FancyMessage message); + /** * Check if the Sender has a permission. * diff --git a/common/src/main/java/me/lucko/luckperms/common/commands/sender/SenderFactory.java b/common/src/main/java/me/lucko/luckperms/common/commands/sender/SenderFactory.java index 2c899360d..bd3276688 100644 --- a/common/src/main/java/me/lucko/luckperms/common/commands/sender/SenderFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/commands/sender/SenderFactory.java @@ -28,6 +28,8 @@ import me.lucko.luckperms.common.LuckPermsPlugin; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + /** * Factory class to make a thread-safe sender instance * @@ -43,6 +45,8 @@ public abstract class SenderFactory { protected abstract void sendMessage(T t, String s); + protected abstract void sendMessage(T t, FancyMessage message); + protected abstract boolean hasPermission(T t, String node); public final Sender wrap(T t) { diff --git a/common/src/main/java/me/lucko/luckperms/common/data/Importer.java b/common/src/main/java/me/lucko/luckperms/common/data/Importer.java index 86a8d51cb..a4fc52c35 100644 --- a/common/src/main/java/me/lucko/luckperms/common/data/Importer.java +++ b/common/src/main/java/me/lucko/luckperms/common/data/Importer.java @@ -44,6 +44,8 @@ import java.util.Map; import java.util.UUID; import java.util.stream.Collectors; +import io.github.mkremins.fanciful.FancyMessage; + /** * Class to handle import operations */ @@ -212,6 +214,11 @@ public class Importer { instance.logMessage(s); } + @Override + public void sendMessage(FancyMessage message) { + instance.logMessage(message.toOldMessageFormat()); + } + @Override public boolean hasPermission(Permission permission) { return true; diff --git a/sponge/pom.xml b/sponge/pom.xml index d82f36268..839688eb8 100644 --- a/sponge/pom.xml +++ b/sponge/pom.xml @@ -52,6 +52,10 @@ + + io.github.mkremins.fanciful + me.lucko.luckperms.lib.fanciful + com.zaxxer.hikari me.lucko.luckperms.lib.hikari diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java index b5006e614..7dce1110d 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/SpongeSenderFactory.java @@ -32,6 +32,8 @@ import org.spongepowered.api.text.serializer.TextSerializers; import java.util.UUID; +import io.github.mkremins.fanciful.FancyMessage; + public class SpongeSenderFactory extends SenderFactory { public SpongeSenderFactory(LuckPermsPlugin plugin) { super(plugin); @@ -59,6 +61,15 @@ public class SpongeSenderFactory extends SenderFactory { source.sendMessage(TextSerializers.LEGACY_FORMATTING_CODE.deserialize(s)); } + @Override + protected void sendMessage(CommandSource source, FancyMessage message) { + try { + source.sendMessage(TextSerializers.JSON.deserialize(message.toJSONString())); + } catch (Exception e) { + sendMessage(source, message.toOldMessageFormat()); + } + } + @Override protected boolean hasPermission(CommandSource source, String node) { return source.hasPermission(node);