From ae7c9fc57cf840165d272feaee01db1f571939ef Mon Sep 17 00:00:00 2001 From: HaHaWTH <102713261+HaHaWTH@users.noreply.github.com> Date: Thu, 21 Nov 2024 04:11:35 +0800 Subject: [PATCH] Version 1.2 --- pom.xml | 16 +++- .../wdsj/imagepreviewer/ImagePreviewer.java | 4 +- .../command/ConstructCommandExecutor.java | 33 ++++++++ .../io/wdsj/imagepreviewer/config/Config.java | 31 +++++-- .../hook/floodgate/AbstractForm.java | 7 ++ .../hook/floodgate/FloodgateHook.java | 84 +++++++++++++++++++ .../imagepreviewer/image/ImageLoader.java | 4 +- .../imagepreviewer/listener/ChatListener.java | 24 ++++-- .../permission/PermissionsEnum.java | 1 + .../wdsj/imagepreviewer/util/MessageUtil.java | 4 + .../io/wdsj/imagepreviewer/util/Pair.java | 18 +--- src/main/resources/plugin.yml | 6 ++ 12 files changed, 195 insertions(+), 37 deletions(-) create mode 100644 src/main/java/io/wdsj/imagepreviewer/hook/floodgate/AbstractForm.java create mode 100644 src/main/java/io/wdsj/imagepreviewer/hook/floodgate/FloodgateHook.java diff --git a/pom.xml b/pom.xml index 6673c34..99b30d1 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ io.wdsj imagepreviewer - 1.1 + 1.2 jar ImagePreviewer @@ -101,6 +101,14 @@ sonatype https://oss.sonatype.org/content/groups/public/ + + opencollab-main + https://repo.opencollab.dev/main/ + + + opencollab-snapshots + https://repo.opencollab.dev/maven-snapshots/ + jitpack https://jitpack.io @@ -174,6 +182,12 @@ 2.6.1-SNAPSHOT provided + + org.geysermc.floodgate + api + 2.2.2-SNAPSHOT + provided + com.github.Anon8281 UniversalScheduler diff --git a/src/main/java/io/wdsj/imagepreviewer/ImagePreviewer.java b/src/main/java/io/wdsj/imagepreviewer/ImagePreviewer.java index eeeffb5..621babc 100644 --- a/src/main/java/io/wdsj/imagepreviewer/ImagePreviewer.java +++ b/src/main/java/io/wdsj/imagepreviewer/ImagePreviewer.java @@ -2,14 +2,12 @@ import com.github.Anon8281.universalScheduler.UniversalScheduler; import com.github.Anon8281.universalScheduler.scheduling.schedulers.TaskScheduler; -import com.github.Anon8281.universalScheduler.scheduling.tasks.MyScheduledTask; import io.wdsj.imagepreviewer.command.ConstructCommandExecutor; import io.wdsj.imagepreviewer.config.Config; import io.wdsj.imagepreviewer.image.ImageLoader; import io.wdsj.imagepreviewer.listener.ChatListener; import io.wdsj.imagepreviewer.packet.MapManager; import io.wdsj.imagepreviewer.permission.CachingPermTool; -import io.wdsj.imagepreviewer.task.MapDisplayDirectionTask; import io.wdsj.imagepreviewer.update.Updater; import io.wdsj.imagepreviewer.util.Util; import net.kyori.adventure.platform.bukkit.BukkitAudiences; @@ -26,7 +24,7 @@ public class ImagePreviewer extends JavaPlugin { private static ImagePreviewer instance; - public static final String PLUGIN_VERSION = "1.1"; + public static final String PLUGIN_VERSION = "1.2"; public static Logger LOGGER; private static Config config; private MapManager mapManager; diff --git a/src/main/java/io/wdsj/imagepreviewer/command/ConstructCommandExecutor.java b/src/main/java/io/wdsj/imagepreviewer/command/ConstructCommandExecutor.java index 7510219..bbb8024 100644 --- a/src/main/java/io/wdsj/imagepreviewer/command/ConstructCommandExecutor.java +++ b/src/main/java/io/wdsj/imagepreviewer/command/ConstructCommandExecutor.java @@ -2,12 +2,17 @@ import io.wdsj.imagepreviewer.ImagePreviewer; import io.wdsj.imagepreviewer.config.Config; +import io.wdsj.imagepreviewer.hook.floodgate.FloodgateHook; import io.wdsj.imagepreviewer.image.ImageLoader; +import io.wdsj.imagepreviewer.listener.ChatListener; import io.wdsj.imagepreviewer.packet.PacketMapDisplay; import io.wdsj.imagepreviewer.permission.CachingPermTool; import io.wdsj.imagepreviewer.permission.PermissionsEnum; import io.wdsj.imagepreviewer.util.MessageUtil; import io.wdsj.imagepreviewer.util.Util; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; @@ -37,6 +42,34 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command MessageUtil.sendMessage(sender, config.message_no_permission); return true; } + if (args[0].equalsIgnoreCase("history")) { + if (!(sender instanceof Player player)) { + MessageUtil.sendMessage(sender, config.message_command_player_only); + return true; + } + if (!CachingPermTool.hasPermission(PermissionsEnum.HISTORY, player)) { + MessageUtil.sendMessage(sender, config.message_no_permission); + return true; + } + if (ChatListener.getUrlHistory().isEmpty()) { + MessageUtil.sendMessage(sender, config.message_no_history_to_show); + return true; + } + if (config.hook_floodgate && FloodgateHook.isFloodgatePresent() && FloodgateHook.isFloodgatePlayer(player)) { + var fgPlayer = FloodgateHook.getFloodgatePlayer(player); + new FloodgateHook.PreviewHistoryForm().sendForm(fgPlayer); + } else { + MessageUtil.sendMessage(sender, config.message_history_command); + int index = 1; + for (var entry : ChatListener.getUrlHistory()) { + var component = Component.text(index++ + ". " + entry.sender() + ": " + entry.message()) + .color(NamedTextColor.GRAY) + .clickEvent(ClickEvent.runCommand("/imagepreviewer preview " + entry.message())); + ImagePreviewer.getInstance().getAudiences().player(player).sendMessage(component); + } + } + return true; + } if (args[0].equalsIgnoreCase("cancel")) { if (!(sender instanceof Player player)) { MessageUtil.sendMessage(sender, config.message_command_player_only); diff --git a/src/main/java/io/wdsj/imagepreviewer/config/Config.java b/src/main/java/io/wdsj/imagepreviewer/config/Config.java index 7043eef..9183e02 100644 --- a/src/main/java/io/wdsj/imagepreviewer/config/Config.java +++ b/src/main/java/io/wdsj/imagepreviewer/config/Config.java @@ -26,14 +26,18 @@ public class Config { public final int cache_maximum_size; public final long cache_expire_time; public final boolean preload_images_in_chat; - public final boolean listen_for_url_match; + public final boolean broadcast_on_match; public final Pattern url_match_regex; public final boolean use_invisible_item_frame, use_glowing_item_frame; public final String message_reload_success, message_preview_loading, message_no_permission, message_invalid_url, message_command_player_only, message_unknown_command, message_already_on_previewing, message_url_matched, message_hover_event , message_preview_still_loading, message_help_info, message_args_error, message_cancel_success, - message_nothing_to_cancel; + message_nothing_to_cancel, message_history_command, message_no_history_to_show; + + public final String form_title, form_history_content; + + public final boolean hook_floodgate; public Config(ImagePreviewer plugin, File dataFolder) throws Exception { this.plugin = plugin; @@ -68,18 +72,25 @@ public Config(ImagePreviewer plugin, File dataFolder) throws Exception { "The message that will be sent to the player when the URL is matched."); this.message_hover_event = getString("message.hover-event", "Click to preview image", "The hover event that will be sent to the player when they hover over the image."); - this.message_help_info = getString("message.help-info", """ + this.message_history_command = getString("message.command.history.header", "&eImagePreviewer History &b(Click link to preview)", + "The message that will be sent to the player when they enter the history command."); + this.message_no_history_to_show = getString("message.command.history.no-history", "&cNo history to show.", + "The message that will be sent to the player when they enter the history command and there is no history to show."); + this.message_help_info = getString("message.command.help.help-info", """ &b&l&nImage Previewer &a/imagepreviewer reload &7- Reload the plugin &a/imagepreviewer help &7- Show this message &a/imagepreviewer preview [time-ticks] &7- Preview an image from given url - &a/imagepreviewer cancel &7- Cancel running preview""" + &a/imagepreviewer cancel &7- Cancel running preview + &a/imagepreviewer history &7- Show previous url history""" ); - this.message_cancel_success = getString("message.cancel-success", "&aCancelled image preview.", + this.message_cancel_success = getString("message.command.cancel.cancel-success", "&aCancelled image preview.", "The message that will be sent to the player when they cancel the image preview."); - this.message_nothing_to_cancel = getString("message.nothing-to-cancel", "&cYou are not on previewing.", + this.message_nothing_to_cancel = getString("message.command.cancel.nothing-to-cancel", "&cYou are not on previewing.", "The message that will be sent to the player when they are not on previewing."); + this.form_title = getString("message.form.title", "&e&lImage Previewer", "The title of the form."); + this.form_history_content = getString("message.form.history-form.content", "Previous image urls in chat", "The content of the history form."); this.preview_mode = getInt("plugin.preview-mode", 2, """ @@ -110,12 +121,14 @@ public Config(ImagePreviewer plugin, File dataFolder) throws Exception { "If set to true, will use adaptive frame delay."); this.url_match_regex = Pattern.compile(getString("plugin.url-match-regex", "https?://[^\\s]+?\\.(?:png|bmp|jpg|jpeg|gif|webp)\\b", "The regex that will be used to match the URL.")); - this.listen_for_url_match = getBoolean("plugin.listen-for-url-match", true, - "If set to true, will listen for url match and send message to player."); + this.broadcast_on_match = getBoolean("plugin.broadcast-on-match", false, + "If set to true, will broadcast the matched URL component to all players."); this.use_invisible_item_frame = getBoolean("plugin.use-invisible-item-frame", false, "If set to true, will use invisible item frame to display image."); this.use_glowing_item_frame = getBoolean("plugin.use-glowing-item-frame", false, "If set to true, will use glowing item frame to display image."); + + this.hook_floodgate = getBoolean("hook.floodgate", true); } public void saveConfig() { @@ -129,6 +142,8 @@ public void saveConfig() { private void structureConfig() { createTitledSection("Plugin general setting", "plugin"); createTitledSection("Message", "message"); + createTitledSection("Floodgate form message", "message.form"); + createTitledSection("Plugin hook", "hook"); } public void createTitledSection(String title, String path) { diff --git a/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/AbstractForm.java b/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/AbstractForm.java new file mode 100644 index 0000000..e4d544c --- /dev/null +++ b/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/AbstractForm.java @@ -0,0 +1,7 @@ +package io.wdsj.imagepreviewer.hook.floodgate; + +import org.geysermc.floodgate.api.player.FloodgatePlayer; + +public abstract class AbstractForm { + public abstract void sendForm(FloodgatePlayer player); +} diff --git a/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/FloodgateHook.java b/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/FloodgateHook.java new file mode 100644 index 0000000..4c6ff46 --- /dev/null +++ b/src/main/java/io/wdsj/imagepreviewer/hook/floodgate/FloodgateHook.java @@ -0,0 +1,84 @@ +package io.wdsj.imagepreviewer.hook.floodgate; + +import io.wdsj.imagepreviewer.ImagePreviewer; +import io.wdsj.imagepreviewer.image.ImageLoader; +import io.wdsj.imagepreviewer.listener.ChatListener; +import io.wdsj.imagepreviewer.packet.PacketMapDisplay; +import io.wdsj.imagepreviewer.permission.CachingPermTool; +import io.wdsj.imagepreviewer.permission.PermissionsEnum; +import io.wdsj.imagepreviewer.util.MessageUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.geysermc.cumulus.form.SimpleForm; +import org.geysermc.cumulus.util.FormImage; +import org.geysermc.floodgate.api.FloodgateApi; +import org.geysermc.floodgate.api.player.FloodgatePlayer; + +import java.util.List; + +public class FloodgateHook { + + public static boolean isFloodgatePresent() { + return Bukkit.getPluginManager().getPlugin("floodgate") != null; + } + + public static boolean isFloodgatePlayer(Player player) { + return FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId()); + } + + public static FloodgatePlayer getFloodgatePlayer(Player player) { + return FloodgateApi.getInstance().getPlayer(player.getUniqueId()); + } + + public static class PreviewHistoryForm extends AbstractForm { + private final SimpleForm.Builder formBuilder; + private final List history; + public PreviewHistoryForm() { + this.formBuilder = SimpleForm.builder(); + formBuilder.title(MessageUtil.translateColors(ImagePreviewer.config().form_title)); + formBuilder.content(MessageUtil.translateColors(ImagePreviewer.config().form_history_content)); + this.history = ChatListener.getUrlHistory(); + history.forEach(entry -> { + formBuilder.button(entry.sender() + ": " + entry.message(), FormImage.Type.URL, entry.message()); + }); + } + + @Override + public void sendForm(FloodgatePlayer player) { + formBuilder.validResultHandler(result -> { + int index = result.clickedButtonId(); + if (index >= ChatListener.getUrlHistory().size()) return; + var entry = history.get(index); + var plugin = ImagePreviewer.getInstance(); + var config = ImagePreviewer.config(); + var uuid = player.getCorrectUniqueId(); + var javaPlayer = Bukkit.getPlayer(uuid); + if (javaPlayer == null) return; + if (!CachingPermTool.hasPermission(PermissionsEnum.PREVIEW, javaPlayer)) { + MessageUtil.sendMessage(javaPlayer, config.message_no_permission); + return; + } + if (plugin.getMapManager().queuedPlayers.contains(uuid)) { + MessageUtil.sendMessage(javaPlayer, config.message_preview_still_loading); + return; + } + if (plugin.getMapManager().hasRunningPreview(javaPlayer)) { + MessageUtil.sendMessage(javaPlayer, config.message_already_on_previewing); + return; + } + plugin.getMapManager().queuedPlayers.add(uuid); + MessageUtil.sendMessage(javaPlayer, config.message_preview_loading); + ImageLoader.imageAsData(entry.message()) + .thenAccept(imageData -> { + new PacketMapDisplay(plugin, javaPlayer, imageData).spawn(); + }) + .exceptionally(ex -> { + MessageUtil.sendMessage(javaPlayer, config.message_invalid_url); + plugin.getMapManager().queuedPlayers.remove(uuid); + return null; + }); + }); + player.sendForm(formBuilder.build()); + } + } +} diff --git a/src/main/java/io/wdsj/imagepreviewer/image/ImageLoader.java b/src/main/java/io/wdsj/imagepreviewer/image/ImageLoader.java index b85b518..ecc847f 100644 --- a/src/main/java/io/wdsj/imagepreviewer/image/ImageLoader.java +++ b/src/main/java/io/wdsj/imagepreviewer/image/ImageLoader.java @@ -59,7 +59,7 @@ public static CompletableFuture imageAsData(String urlString) { boolean isAnimated = getFormatName(url).equalsIgnoreCase("gif") && !Config.isReloading && ImagePreviewer.config().process_multi_frame_gif; if (isAnimated) { var pairs = readAllFramesGif(url); - BufferedImage[] originalImages = pairs.getLeft(); + BufferedImage[] originalImages = pairs.left(); if (originalImages.length == 0) { throw new IllegalArgumentException("The provided URL is not a valid image: " + urlString); } @@ -72,7 +72,7 @@ public static CompletableFuture imageAsData(String urlString) { for (BufferedImage resizedImage : resizedImagesList) { imageDataList.add(MapPalette.imageToBytes(resizedImage)); } - ImageData data = new ImageData(imageDataList, true, pairs.getRight()); + ImageData data = new ImageData(imageDataList, true, pairs.right()); if (ImagePreviewer.config().enable_image_cache) { imageCache.put(urlString, data); } diff --git a/src/main/java/io/wdsj/imagepreviewer/listener/ChatListener.java b/src/main/java/io/wdsj/imagepreviewer/listener/ChatListener.java index 9b516e0..fadc9c8 100644 --- a/src/main/java/io/wdsj/imagepreviewer/listener/ChatListener.java +++ b/src/main/java/io/wdsj/imagepreviewer/listener/ChatListener.java @@ -1,5 +1,6 @@ package io.wdsj.imagepreviewer.listener; +import com.google.common.collect.EvictingQueue; import io.wdsj.imagepreviewer.ImagePreviewer; import io.wdsj.imagepreviewer.config.Config; import io.wdsj.imagepreviewer.image.ImageLoader; @@ -12,12 +13,14 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerChatEvent; +import java.util.List; import java.util.regex.Matcher; public class ChatListener implements Listener { + private static final EvictingQueue urlHistory = EvictingQueue.create(10); @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) public void onChat(AsyncPlayerChatEvent event) { - if (Config.isReloading || !ImagePreviewer.config().listen_for_url_match) return; + if (Config.isReloading) return; String message = event.getMessage(); Matcher matcher = ImagePreviewer.config().url_match_regex.matcher(message); String url; @@ -31,10 +34,19 @@ public void onChat(AsyncPlayerChatEvent event) { .exceptionally(throwable -> null); } String playerName = event.getPlayer().getName(); - Component component = Component.text(ImagePreviewer.config().message_url_matched.replace("%player%", playerName)) - .color(NamedTextColor.GREEN) - .hoverEvent(HoverEvent.showText(Component.text(ImagePreviewer.config().message_hover_event))) - .clickEvent(ClickEvent.runCommand("/imagepreviewer preview " + url)); - ImagePreviewer.getInstance().getAudiences().players().sendMessage(component); + urlHistory.offer(new MessageEntry(url, playerName)); + if (ImagePreviewer.config().broadcast_on_match) { + Component component = Component.text(ImagePreviewer.config().message_url_matched.replace("%player%", playerName)) + .color(NamedTextColor.GREEN) + .hoverEvent(HoverEvent.showText(Component.text(ImagePreviewer.config().message_hover_event))) + .clickEvent(ClickEvent.runCommand("/imagepreviewer preview " + url)); + ImagePreviewer.getInstance().getAudiences().players().sendMessage(component); + } + } + + public static List getUrlHistory() { + return List.copyOf(urlHistory); + } + public record MessageEntry(String message, String sender) { } } diff --git a/src/main/java/io/wdsj/imagepreviewer/permission/PermissionsEnum.java b/src/main/java/io/wdsj/imagepreviewer/permission/PermissionsEnum.java index 69d60a3..99460f6 100644 --- a/src/main/java/io/wdsj/imagepreviewer/permission/PermissionsEnum.java +++ b/src/main/java/io/wdsj/imagepreviewer/permission/PermissionsEnum.java @@ -5,6 +5,7 @@ public enum PermissionsEnum { PREVIEW_TIME("imagepreviewer.use.time"), CANCEL_PREVIEW("imagepreviewer.command.cancel"), RELOAD("imagepreviewer.command.reload"), + HISTORY("imagepreviewer.command.history"), HELP("imagepreviewer.command.help"); private final String permission; diff --git a/src/main/java/io/wdsj/imagepreviewer/util/MessageUtil.java b/src/main/java/io/wdsj/imagepreviewer/util/MessageUtil.java index d064019..1867286 100644 --- a/src/main/java/io/wdsj/imagepreviewer/util/MessageUtil.java +++ b/src/main/java/io/wdsj/imagepreviewer/util/MessageUtil.java @@ -8,4 +8,8 @@ public static void sendMessage(CommandSender sender, String message) { if (message.isBlank()) return; sender.sendMessage(ChatColor.translateAlternateColorCodes('&', message)); } + + public static String translateColors(String message) { + return ChatColor.translateAlternateColorCodes('&', message); + } } diff --git a/src/main/java/io/wdsj/imagepreviewer/util/Pair.java b/src/main/java/io/wdsj/imagepreviewer/util/Pair.java index 4490fbe..5a8ff01 100644 --- a/src/main/java/io/wdsj/imagepreviewer/util/Pair.java +++ b/src/main/java/io/wdsj/imagepreviewer/util/Pair.java @@ -1,23 +1,7 @@ package io.wdsj.imagepreviewer.util; -public class Pair { - private final L left; - private final R right; - - public Pair(L left, R right) { - this.left = left; - this.right = right; - } - +public record Pair(L left, R right) { public static Pair of(L left, R right) { return new Pair<>(left, right); } - - public L getLeft() { - return left; - } - - public R getRight() { - return right; - } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 8419062..b9ad2e8 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -8,6 +8,8 @@ authors: folia-supported: true depend: - packetevents +softdepend: + - floodgate permissions: imagepreviewer.*: default: false @@ -18,9 +20,13 @@ permissions: imagepreviewer.command.reload: true imagepreviewer.command.help: true imagepreviewer.command.cancel: true + imagepreviewer.command.history: true imagepreviewer.command.reload: default: op description: Allows the reloading of the plugin's configuration. + imagepreviewer.command.history: + default: true + description: Allows the display of the previous history. imagepreviewer.command.help: default: true description: Allows the display of the plugin's help menu.