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.