From c29e1523dabfd149a4c6e591c28073c33d90d97f Mon Sep 17 00:00:00 2001 From: Dico200 Date: Wed, 15 Nov 2017 18:26:54 +0000 Subject: Fix RunnableCallback and convert to Maven project --- .gitignore | 8 ++ src/main/java/net/nemez/chatapi/ChatAPI.java | 132 +++++++++++++++++++++ .../net/nemez/chatapi/click/CallbackCommand.java | 38 ++++++ .../java/net/nemez/chatapi/click/CallbackMap.java | 53 +++++++++ .../net/nemez/chatapi/click/ClickCallback.java | 46 +++++++ src/main/java/net/nemez/chatapi/click/Message.java | 131 ++++++++++++++++++++ .../nemez/chatapi/click/PlayerQuitListener.java | 13 ++ .../net/nemez/chatapi/click/RunnableCallback.java | 18 +++ 8 files changed, 439 insertions(+) create mode 100644 .gitignore create mode 100644 src/main/java/net/nemez/chatapi/ChatAPI.java create mode 100644 src/main/java/net/nemez/chatapi/click/CallbackCommand.java create mode 100644 src/main/java/net/nemez/chatapi/click/CallbackMap.java create mode 100644 src/main/java/net/nemez/chatapi/click/ClickCallback.java create mode 100644 src/main/java/net/nemez/chatapi/click/Message.java create mode 100644 src/main/java/net/nemez/chatapi/click/PlayerQuitListener.java create mode 100644 src/main/java/net/nemez/chatapi/click/RunnableCallback.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40bd287 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +### IntelliJ specific files + +*.iml +.idea/ + +### Maven + +target/ diff --git a/src/main/java/net/nemez/chatapi/ChatAPI.java b/src/main/java/net/nemez/chatapi/ChatAPI.java new file mode 100644 index 0000000..6aaa1c8 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/ChatAPI.java @@ -0,0 +1,132 @@ +package net.nemez.chatapi; + +import java.lang.reflect.Field; +import java.util.Random; + +import org.bukkit.Bukkit; +import org.bukkit.command.CommandMap; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.java.JavaPlugin; + +import net.nemez.chatapi.click.CallbackCommand; +import net.nemez.chatapi.click.Message; +import net.nemez.chatapi.click.PlayerQuitListener; + +public class ChatAPI { + + /* message coloring permission */ + public static final String PERMISSION_CHAT_COLOR = "chat.color"; + /* message formatting permission */ + public static final String PERMISSION_CHAT_FORMAT = "chat.format"; + /* message magic formatting permission */ + public static final String PERMISSION_CHAT_MAGIC = "chat.magic"; + /* permission to send messages in chat */ + public static final String PERMISSION_CHAT_USE = "chat.use"; + /* message to send when the internal command is not ran correctly (ran by user) */ + public static final String MESSAGE_HELP_CLICK_CALLBACK = "&cThis is an internal command for ChatAPI and should not be ran by players manually."; + /* message to send when the internal command is not ran by a player */ + public static final String MESSAGE_PLAYER_CLICK_CALLBACK = "&cThis command can only be run by a player"; + /* the actual command name for use in click callbacks */ + private static String internalCommandName; + + /** + * Initializes ChatAPI and registers the required commands for clickable chat to function. + */ + public static void initialize(JavaPlugin plugin) { + if (internalCommandName != null) { + return; + } + Random rand = new Random(System.currentTimeMillis()); + internalCommandName = "chatapi-exec-" + Integer.toHexString(rand.nextInt(0xEFFF) + 0x1000); + try { + final Field cmdMap = Bukkit.getServer().getClass().getDeclaredField("commandMap"); + cmdMap.setAccessible(true); + CommandMap map = (CommandMap) cmdMap.get(Bukkit.getServer()); + map.register("net/nemez/chatapi", new CallbackCommand(internalCommandName)); + } catch (Exception e) { + plugin.getLogger().severe("Failed to register internal command '" + internalCommandName + "'"); + e.printStackTrace(); + } + internalCommandName = "chatapi:" + internalCommandName; + plugin.getServer().getPluginManager().registerEvents(new PlayerQuitListener(), plugin); + plugin.getLogger().info("ChatAPI initialized"); + } + + /** + * Colorifies a message using &format codes. Respects permissions. + * + * @param sender the command sender whose permissions to use. null if permissions are to be ignored. + * @param message the message to color + * @return colored message + */ + public static String colorify(CommandSender sender, String message) { + if (sender == null || sender.hasPermission(PERMISSION_CHAT_COLOR)) { + message = message.replaceAll("&([0-9a-fA-FrR])", "§$1"); + } + if (sender == null || sender.hasPermission(PERMISSION_CHAT_FORMAT)) { + message = message.replaceAll("&([l-oL-OrR])", "§$1"); + } + if (sender == null || sender.hasPermission(PERMISSION_CHAT_MAGIC)) { + message = message.replaceAll("&([kKrR])", "§$1"); + } + return message; + } + + /** + * Sends a colorified message to the command sender. + * + * @param sender the command sender to whom to send the message. + * @param message the message to send. + */ + public static void send(CommandSender sender, String message) { + if (sender == null) { + return; + } + sender.sendMessage(colorify(null, message)); + } + + /** + * Checks if a command sender has the permission node required to send chat messages. + * + * @param sender the command sender to check. + * @return true/false if sender can chat or is null. + */ + public static boolean canChat(CommandSender sender) { + if (sender == null) { + return true; + }else{ + return sender.hasPermission(PERMISSION_CHAT_USE); + } + } + + /** + * Creates a new message object that will be sent to the given command sender with regards to the second command sender's permissions. + * + * @param sender the command sender to whom to send the message. + * @param permissionSender the command sender whose permissions to use. + * @return message object + */ + public static Message createMessage(CommandSender sender, CommandSender permissionSender) { + return new Message(sender, permissionSender); + } + + /** + * Creates a new message object that will be sent to the given command sender. + * + * @param sender the command sender to whom to send the message. + * @return message object. + */ + public static Message createMessage(CommandSender sender) { + return createMessage(sender, null); + } + + /** + * Gets the name of the internal ChatAPI command used for click callbacks. + * This function is used internally and you don't need to worry about it. + * + * @return callback command name + */ + public static String getInternalCallbackCommand() { + return internalCommandName; + } +} diff --git a/src/main/java/net/nemez/chatapi/click/CallbackCommand.java b/src/main/java/net/nemez/chatapi/click/CallbackCommand.java new file mode 100644 index 0000000..8685108 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/CallbackCommand.java @@ -0,0 +1,38 @@ +package net.nemez.chatapi.click; + +import java.util.UUID; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import net.nemez.chatapi.ChatAPI; + +public class CallbackCommand extends Command { + + public CallbackCommand(String internalCommandName) { + super(internalCommandName); + } + + @Override + public boolean execute(CommandSender sender, String label, String[] args) { + if (!(sender instanceof Player)) { + ChatAPI.send(sender, ChatAPI.MESSAGE_PLAYER_CLICK_CALLBACK); + return true; + } + if ((args.length == 1 && args[0].equals("help")) || args.length != 1) { + ChatAPI.send(sender, ChatAPI.MESSAGE_HELP_CLICK_CALLBACK); + return true; + } + int id; + try { + id = Integer.parseInt(args[0]); + } catch (NumberFormatException e) { + ChatAPI.send(sender, ChatAPI.MESSAGE_HELP_CLICK_CALLBACK); + return true; + } + UUID uuid = ((Player) sender).getUniqueId(); + CallbackMap.execute(sender, uuid, id); + return true; + } +} diff --git a/src/main/java/net/nemez/chatapi/click/CallbackMap.java b/src/main/java/net/nemez/chatapi/click/CallbackMap.java new file mode 100644 index 0000000..6735ae9 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/CallbackMap.java @@ -0,0 +1,53 @@ +package net.nemez.chatapi.click; + +import java.util.HashMap; +import java.util.UUID; + +import org.bukkit.command.CommandSender; + +public class CallbackMap { + + private static HashMap> map = new HashMap>(); + + protected static int register(UUID uuid, ClickCallback callback) { + HashMap playerMap = map.get(uuid.toString()); + if (playerMap == null) { + playerMap = new HashMap(); + map.put(uuid.toString(), playerMap); + } + int largestId = 0; + for (int i : playerMap.keySet()) { + if (i > largestId) { + largestId = i; + } + } + int id = largestId + 1; + playerMap.put(id, callback); + return id; + } + + protected static void execute(CommandSender sender, UUID uuid, int id) { + HashMap playerMap = map.get(uuid.toString()); + if (playerMap == null) { + return; + } + ClickCallback cb = playerMap.get(id); + if (cb == null) { + return; + } + if (cb.isAsynchronous()) { + Thread t = new Thread() { + public void run() { + cb.execute(sender); + } + }; + t.start(); + }else{ + cb.execute(sender); + } + } + + protected static void discard(UUID uuid) { + map.remove(uuid.toString()); + } +} diff --git a/src/main/java/net/nemez/chatapi/click/ClickCallback.java b/src/main/java/net/nemez/chatapi/click/ClickCallback.java new file mode 100644 index 0000000..10e8f00 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/ClickCallback.java @@ -0,0 +1,46 @@ +package net.nemez.chatapi.click; + +import org.bukkit.command.CommandSender; + +import net.nemez.chatapi.ChatAPI; + +public abstract class ClickCallback { + + private boolean repeatable, async; + private String expiredMessage; + private boolean expired; + + public ClickCallback(boolean repeatable, boolean async, String expiredMessage) { + this.repeatable = repeatable; + this.async = async; + this.expiredMessage = expiredMessage; + this.expired = false; + } + + public abstract void run(CommandSender sender); + + public final void execute(CommandSender sender) { + if (!expired) { + run(sender); + }else{ + if (sender != null) { + sender.sendMessage(ChatAPI.colorify(null, expiredMessage)); + } + } + if (!repeatable) { + expired = true; + } + } + + public boolean isRepeatable() { + return repeatable; + } + + public boolean isAsynchronous() { + return async; + } + + public String getExpiredMessage() { + return expiredMessage; + } +} diff --git a/src/main/java/net/nemez/chatapi/click/Message.java b/src/main/java/net/nemez/chatapi/click/Message.java new file mode 100644 index 0000000..e265f03 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/Message.java @@ -0,0 +1,131 @@ +package net.nemez.chatapi.click; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import net.nemez.chatapi.ChatAPI; + +public class Message { + + private CommandSender sender; + private CommandSender permission; + private TextComponent message; + private String rawMessage; + + public Message(CommandSender sender, CommandSender permission) { + this.sender = sender; + this.permission = permission; + message = new TextComponent(""); + rawMessage = ""; + } + + public Message appendText(String text) { + text = ChatAPI.colorify(permission, text); + message.addExtra(text); + rawMessage += text; + return this; + } + + public Message appendLink(String text, String url) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendSendChat(String text, String msg) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, msg)); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendSuggest(String text, String suggestion) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, suggestion)); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendCallback(String text, ClickCallback callback) { + if (sender instanceof Player) { + int id = CallbackMap.register(((Player) sender).getUniqueId(), callback); + return appendSendChat(text, "/" + ChatAPI.getInternalCallbackCommand() + " " + id); + }else{ + return appendText(text); + } + } + + public Message appendTextHover(String text, String hover) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + addHoverText(component, hover); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendLinkHover(String text, String url, String hover) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)); + addHoverText(component, hover); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendSendChatHover(String text, String msg, String hover) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, msg)); + addHoverText(component, hover); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendSuggestHover(String text, String suggestion, String hover) { + text = ChatAPI.colorify(permission, text); + TextComponent component = new TextComponent(text); + component.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, suggestion)); + addHoverText(component, hover); + message.addExtra(component); + rawMessage += text; + return this; + } + + public Message appendCallbackHover(String text, ClickCallback callback, String hover) { + if (sender instanceof Player) { + int id = CallbackMap.register(((Player) sender).getUniqueId(), callback); + return appendSendChatHover(text, "/" + ChatAPI.getInternalCallbackCommand() + " " + id, hover); + }else{ + return appendTextHover(text, hover); + } + } + + public void send() { + if (sender == null || !ChatAPI.canChat(this.permission)) { + return; + } + if (sender instanceof Player) { + ((Player)sender).spigot().sendMessage(message); + }else{ + sender.sendMessage(rawMessage); + } + } + + private void addHoverText(TextComponent comp, String text) { + comp.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder(ChatAPI.colorify(permission, text)).create())); + } +} diff --git a/src/main/java/net/nemez/chatapi/click/PlayerQuitListener.java b/src/main/java/net/nemez/chatapi/click/PlayerQuitListener.java new file mode 100644 index 0000000..b515409 --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/PlayerQuitListener.java @@ -0,0 +1,13 @@ +package net.nemez.chatapi.click; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; + +public class PlayerQuitListener implements Listener { + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + CallbackMap.discard(event.getPlayer().getUniqueId()); + } +} diff --git a/src/main/java/net/nemez/chatapi/click/RunnableCallback.java b/src/main/java/net/nemez/chatapi/click/RunnableCallback.java new file mode 100644 index 0000000..2ee16da --- /dev/null +++ b/src/main/java/net/nemez/chatapi/click/RunnableCallback.java @@ -0,0 +1,18 @@ +package net.nemez.chatapi.click; + +import org.bukkit.command.CommandSender; + +public class RunnableCallback extends ClickCallback { + + private Runnable runnable; + + public RunnableCallback(Runnable runnable, boolean repeatable, boolean async, String expiredMessage) { + super(repeatable, async, expiredMessage); + this.runnable = runnable; + } + + @Override + public void run(CommandSender sender) { + runnable.run(); + } +} -- cgit v1.2.3