diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 75899d3..553ae5e 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { api("org.ahocorasick:ahocorasick:0.6.3") compileOnlyApi(libs.annotations) + compileOnlyApi(libs.gson) // Adventure! compileOnlyApi(libs.adventure.api) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f337866..f624d58 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,8 +3,10 @@ adventure = "4.12.0" creative = "1.3.0" # keep in sync with creative-central's creative version creative-central = "0.10.0-SNAPSHOT" annotations = "22.0.0" +gson = "2.10.1" [libraries] +gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" } adventure-api = { group = "net.kyori", name = "adventure-api", version.ref = "adventure" } adventure-text-minimessage = { group = "net.kyori", name = "adventure-text-minimessage", version.ref = "adventure" } creative-api = { group = "team.unnamed", name = "creative-api", version.ref = "creative" } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index 4258c4d..756214a 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -10,7 +10,7 @@ repositories { maven("https://repo.essentialsx.net/releases/") // EssentialsDiscord maven("https://nexus.scarsz.me/content/groups/public/") // DiscordSRV maven("https://m2.dv8tion.net/releases") // JDA - Required by DiscordSRV - maven("https://repo.unnamed.team/repository/unnamed-public/") // creative + maven("https://repo.unnamed.team/repository/unnamed-public/") // creative, command-flow mavenCentral() } @@ -26,6 +26,10 @@ dependencies { // Optional libraries compileOnly(libs.adventure.text.minimessage) + // Internal libraries + implementation("me.fixeddev:commandflow-universal:0.6.0") // command-flow + implementation("me.fixeddev:commandflow-bukkit:0.6.0") // command-flow + // Optional plugin hooks compileOnly("me.clip:placeholderapi:2.10.10") compileOnly(files("../lib/TownyChat-0.91.jar", "../lib/EzChat-3.0.3.jar")) @@ -43,5 +47,6 @@ tasks { shadowJar { val pkg = "team.unnamed.creativeglyphs.lib" relocate("org.ahocorasick", "$pkg.ahocorasick") + relocate("me.fixeddev.commandflow", "$pkg.commandflow") } } diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/CreativeGlyphsPlugin.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/CreativeGlyphsPlugin.java index 0fcf48d..2335f7f 100644 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/CreativeGlyphsPlugin.java +++ b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/CreativeGlyphsPlugin.java @@ -7,7 +7,7 @@ import org.bukkit.plugin.java.JavaPlugin; import team.unnamed.creative.central.CreativeCentralProvider; import team.unnamed.creative.central.event.pack.ResourcePackGenerateEvent; -import team.unnamed.creativeglyphs.plugin.command.RootCommand; +import team.unnamed.creativeglyphs.plugin.command.CommandService; import team.unnamed.creativeglyphs.plugin.integration.carbon.CarbonChatIntegration; import team.unnamed.creativeglyphs.plugin.integration.essentialsdiscord.EssentialsDiscordIntegration; import team.unnamed.creativeglyphs.plugin.listener.misc.AnvilEditListener; @@ -87,8 +87,8 @@ public void onEnable() { EventBus eventBus = EventBus.create(this); - Objects.requireNonNull(getCommand("emojis"), "'emojis' command not registered") - .setExecutor(new RootCommand(this).asExecutor()); + // todo:! + new CommandService(this).start(); Set hooks = IntegrationManager.integrationManager(this) .register(new CarbonChatIntegration(this)) diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ArgumentStack.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ArgumentStack.java deleted file mode 100644 index 82a0f94..0000000 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ArgumentStack.java +++ /dev/null @@ -1,39 +0,0 @@ -package team.unnamed.creativeglyphs.plugin.command; - -import java.util.Arrays; -import java.util.List; - -public class ArgumentStack { - - private final List args; - private int cursor; - - private ArgumentStack(List args) { - this.args = args; - } - - public String next() { - return args.get(cursor++); - } - - public void back() { - cursor--; - } - - public String peek() { - return args.get(cursor); - } - - public int available() { - return args.size() - cursor; - } - - public boolean hasNext() { - return cursor < args.size(); - } - - public static ArgumentStack of(String... args) { - return new ArgumentStack(Arrays.asList(args)); - } - -} diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandRunnable.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandRunnable.java deleted file mode 100644 index cb2f2ad..0000000 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandRunnable.java +++ /dev/null @@ -1,17 +0,0 @@ -package team.unnamed.creativeglyphs.plugin.command; - -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; - -public interface CommandRunnable { - - void run(CommandSender sender, ArgumentStack args); - - default CommandExecutor asExecutor() { - return (sender, command, label, args) -> { - run(sender, ArgumentStack.of(args)); - return true; - }; - } - -} diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandService.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandService.java new file mode 100644 index 0000000..d14d0eb --- /dev/null +++ b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/CommandService.java @@ -0,0 +1,31 @@ +package team.unnamed.creativeglyphs.plugin.command; + +import me.fixeddev.commandflow.annotated.AnnotatedCommandTreeBuilder; +import me.fixeddev.commandflow.annotated.part.PartInjector; +import me.fixeddev.commandflow.annotated.part.defaults.DefaultsModule; +import me.fixeddev.commandflow.bukkit.BukkitCommandManager; +import me.fixeddev.commandflow.bukkit.factory.BukkitModule; +import org.jetbrains.annotations.NotNull; +import team.unnamed.creativeglyphs.plugin.CreativeGlyphsPlugin; + +import static java.util.Objects.requireNonNull; + +public final class CommandService { + private final CreativeGlyphsPlugin plugin; + + public CommandService(final @NotNull CreativeGlyphsPlugin plugin) { + this.plugin = requireNonNull(plugin, "plugin"); + } + + public void start() { + final var manager = new BukkitCommandManager(plugin.getName()); + final var partInjector = PartInjector.create(); + + partInjector.install(new DefaultsModule()); + partInjector.install(new BukkitModule()); + + final var builder = AnnotatedCommandTreeBuilder.create(partInjector); + + manager.registerCommands(builder.fromClass(new GlyphsCommand(plugin))); + } +} diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ListSubCommand.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/GlyphsCommand.java similarity index 67% rename from plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ListSubCommand.java rename to plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/GlyphsCommand.java index 8f33ad2..84f3981 100644 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/ListSubCommand.java +++ b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/GlyphsCommand.java @@ -1,49 +1,97 @@ package team.unnamed.creativeglyphs.plugin.command; +import me.fixeddev.commandflow.annotated.CommandClass; +import me.fixeddev.commandflow.annotated.annotation.Command; +import me.fixeddev.commandflow.annotated.annotation.Named; +import me.fixeddev.commandflow.annotated.annotation.OptArg; import net.md_5.bungee.api.chat.ClickEvent; import net.md_5.bungee.api.chat.HoverEvent; import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.configuration.ConfigurationSection; +import org.jetbrains.annotations.NotNull; +import team.unnamed.creative.central.CreativeCentralProvider; import team.unnamed.creativeglyphs.Glyph; import team.unnamed.creativeglyphs.plugin.CreativeGlyphsPlugin; import team.unnamed.creativeglyphs.plugin.util.Permissions; +import java.io.IOException; +import java.net.URL; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.TreeMap; -public class ListSubCommand implements CommandRunnable { +import static java.util.Objects.requireNonNull; + +@Command(names = { "glyphs", "glyph", "emojis", "emoji" }) +public final class GlyphsCommand implements CommandClass { + private static final String API_URL = "https://artemis.unnamed.team/tempfiles/get/%id%"; private final CreativeGlyphsPlugin plugin; - public ListSubCommand(CreativeGlyphsPlugin plugin) { - this.plugin = plugin; + public GlyphsCommand(final @NotNull CreativeGlyphsPlugin plugin) { + this.plugin = requireNonNull(plugin, "plugin"); } - @Override - @SuppressWarnings("deprecation") // Spigot - public void run(CommandSender sender, ArgumentStack args) { + @Command(names = {"help", "?"}, permission = "emojis.admin") + public void help(final @NotNull CommandSender sender) { + sender.sendMessage(ChatColor.translateAlternateColorCodes( + '&', + plugin.getConfig().getString("messages.help", "Message not found") + )); + } + + @Command(names = "reload", permission = "emojis.admin") + public void reload() { + plugin.reloadConfig(); + plugin.registry().load(); + } + + @Command(names = "update", permission = "emojis.admin") + public void update(final @NotNull CommandSender sender, final @NotNull @Named("id") String id) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> execute(sender, id)); + } + + @Command(names = "edit", permission = "emojis.admin") + public void edit(final @NotNull CommandSender sender) { + } + + private void execute(CommandSender sender, String id) { + try { + URL url = new URL(API_URL.replace("%id%", id)); + Collection glyphs = plugin.importer().importHttp(url); + + // synchronous update and save + Bukkit.getScheduler().runTask(plugin, () -> { + plugin.registry().setGlyphs(glyphs); + plugin.registry().save(); + // asynchronous export + CreativeCentralProvider.get().generate(); + }); + } catch (IOException e) { + sender.sendMessage(ChatColor.RED + "Something went wrong, please" + + " contact an administrator to read the console."); + e.printStackTrace(); + } catch (IllegalStateException e) { + // stack trace in this case isn't so relevant + sender.sendMessage(ChatColor.RED + e.getMessage()); + } + } + + @Command(names = "list") + @SuppressWarnings("deprecation") // Spigot + public void list(final @NotNull CommandSender sender, final @OptArg("0") @Named("page") int page) { // load the configuration for listing emojis ConfigurationSection listConfig = plugin.getConfig().getConfigurationSection("messages.list"); if (listConfig == null) { throw new IllegalStateException("No configuration for list subcommand"); } - int page = -1; - if (args.hasNext()) { - try { - page = Integer.parseInt(args.next()) - 1; - } catch (NumberFormatException ignored) { - // lets 'page' be -1, detected later - } - } else { - page = 0; - } - // load the separation config ConfigurationSection separationConfig = listConfig.getConfigurationSection("separation"); Map separators = new TreeMap<>((a, b) -> b - a); @@ -113,9 +161,9 @@ public void run(CommandSender sender, ArgumentStack args) { TextComponent component = new TextComponent( ChatColor.translateAlternateColorCodes( - '&', + '&', listConfig.getString(basePath + ".content", "Not found") - ) + ) .replace("", glyph.replacement()) .replace("", glyph.name()) ); @@ -154,5 +202,4 @@ public void run(CommandSender sender, ArgumentStack args) { .replace("", String.valueOf(maxPages)) ); } - } diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/RootCommand.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/RootCommand.java deleted file mode 100644 index 6875a89..0000000 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/RootCommand.java +++ /dev/null @@ -1,62 +0,0 @@ -package team.unnamed.creativeglyphs.plugin.command; - -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import team.unnamed.creativeglyphs.plugin.CreativeGlyphsPlugin; - -import java.util.Locale; - -public class RootCommand implements CommandRunnable { - - private final CreativeGlyphsPlugin plugin; - - private final CommandRunnable listSubCommand; - private final CommandRunnable updateSubCommand; - - public RootCommand(CreativeGlyphsPlugin plugin) { - this.plugin = plugin; - - this.listSubCommand = new ListSubCommand(plugin); - this.updateSubCommand = new UpdateSubCommand(plugin); - } - - @Override - public void run(CommandSender sender, ArgumentStack args) { - - // if no permission for subcommands or no arguments given, - // just send the emoji list - if ((!sender.isOp() && !sender.hasPermission("emojis.admin")) || !args.hasNext()) { - listSubCommand.run(sender, args); - return; - } - - switch (args.next().toLowerCase(Locale.ROOT)) { - case "update" -> updateSubCommand.run(sender, args); - case "reload" -> { - plugin.reloadConfig(); - plugin.registry().load(); - } - case "list" -> listSubCommand.run(sender, args); - case "help" -> - // todo: replace this - sender.sendMessage(ChatColor.translateAlternateColorCodes( - '&', - plugin.getConfig().getString("messages.help", "Message not found") - )); - default -> { - // try to parse the page number, if it's a number, run - // the list subcommand - try { - args.back(); - Integer.parseInt(args.peek()); - listSubCommand.run(sender, args); - return; - } catch (NumberFormatException ignored) { - } - - sender.sendMessage(ChatColor.RED + "Unknown subcommand"); - } - } - } - -} diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/UpdateSubCommand.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/UpdateSubCommand.java deleted file mode 100644 index f35dca9..0000000 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/command/UpdateSubCommand.java +++ /dev/null @@ -1,58 +0,0 @@ -package team.unnamed.creativeglyphs.plugin.command; - -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import team.unnamed.creative.central.CreativeCentralProvider; -import team.unnamed.creativeglyphs.Glyph; -import team.unnamed.creativeglyphs.plugin.CreativeGlyphsPlugin; - -import java.io.IOException; -import java.net.URL; -import java.util.Collection; - -public class UpdateSubCommand implements CommandRunnable { - - private static final String API_URL = "https://artemis.unnamed.team/tempfiles/get/%id%"; - - private final CreativeGlyphsPlugin plugin; - - public UpdateSubCommand(CreativeGlyphsPlugin plugin) { - this.plugin = plugin; - } - - @Override - public void run(CommandSender sender, ArgumentStack args) { - if (args.available() != 1) { - sender.sendMessage(ChatColor.RED + "Bad usage, use: /emojis update "); - return; - } - - String downloadId = args.next(); - Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> execute(sender, downloadId)); - } - - private void execute(CommandSender sender, String id) { - try { - URL url = new URL(API_URL.replace("%id%", id)); - Collection glyphs = plugin.importer().importHttp(url); - - // synchronous update and save - Bukkit.getScheduler().runTask(plugin, () -> { - plugin.registry().setGlyphs(glyphs); - plugin.registry().save(); - - // asynchronous export - CreativeCentralProvider.get().generate(); - }); - } catch (IOException e) { - sender.sendMessage(ChatColor.RED + "Something went wrong, please" + - " contact an administrator to read the console."); - e.printStackTrace(); - } catch (IllegalStateException e) { - // stack trace in this case isn't so relevant - sender.sendMessage(ChatColor.RED + e.getMessage()); - } - } - -} diff --git a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/util/ArtemisGlyphImporter.java b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/util/ArtemisGlyphImporter.java index 3fd6d27..9bb7b24 100644 --- a/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/util/ArtemisGlyphImporter.java +++ b/plugin/src/main/java/team/unnamed/creativeglyphs/plugin/util/ArtemisGlyphImporter.java @@ -27,8 +27,6 @@ public class ArtemisGlyphImporter { private static final String USER_AGENT = "creative-glyphs-importer"; - private final JsonParser jsonParser = new JsonParser(); - /** * Imports emojis from the given {@code url} * using the HTTP protocol and expecting a JSON @@ -43,70 +41,11 @@ public class ArtemisGlyphImporter { public Collection importHttp(URL url) throws IOException, IllegalStateException { - HttpURLConnection connection - = (HttpURLConnection) url.openConnection(); - // setup request - connection.setConnectTimeout(5000); - connection.setRequestMethod("GET"); - connection.setRequestProperty("User-Agent", USER_AGENT); Collection glyphs; - // execute and read response - try (Reader responseReader = new BufferedReader( - new InputStreamReader(connection.getInputStream()) - )) { - // response should be like: - // { - // present: true/false, - // file: 'base64 string' - // } - JsonObject response = jsonParser.parse(responseReader) - .getAsJsonObject(); - - // check for presence: false - if (!response.get("present").getAsBoolean()) { - throw new IllegalStateException("Emojis not found in" + - " the given location"); - } - - // read 'file' base64 data - byte[] base64File = response.get("file") - .getAsString() - .getBytes(StandardCharsets.UTF_8); - - try (InputStream input = Base64.getDecoder() - .wrap(new ByteArrayInputStream(base64File))) { - glyphs = GlyphReader.mcglyph().read(input); - } - } catch (IOException e) { - int status; - try { - status = connection.getResponseCode(); - } catch (IOException ignored) { - // if getResponseCode() failed, just throw - // the original exception - throw e; - } - if (status == 404) { - // 404: Not found - // this status is currently not used in our backend, - // but we should prepare this for the future - throw new IllegalStateException( - "Emojis not found in the given location", e); - } else if (status == 429) { - // 429: Too many requests - throw new IllegalStateException( - "Too many requests, you're being rate-" + - "limited, try again later", e); - } else { - // we're not going to handle this status - // code so just throw the exception - throw e; - } - } return glyphs; } diff --git a/plugin/src/main/resources/plugin.yml b/plugin/src/main/resources/plugin.yml index 2cfbbb7..95c7d60 100644 --- a/plugin/src/main/resources/plugin.yml +++ b/plugin/src/main/resources/plugin.yml @@ -14,8 +14,4 @@ softdepend: - MiniPlaceholders - EssentialsDiscord - CarbonChat - - unemojis # We want to detect if unemojis (old version of creative-glyphs) is installed, to warn the server admin to remove it -commands: - emojis: - description: Main command for the creative-glyphs plugin - usage: / update \ No newline at end of file + - unemojis # We want to detect if unemojis (old version of creative-glyphs) is installed, to warn the server admin to remove it \ No newline at end of file diff --git a/plugin/src/test/java/team/unnamed/creativeglyphs/command/ArgumentStackTest.java b/plugin/src/test/java/team/unnamed/creativeglyphs/command/ArgumentStackTest.java deleted file mode 100644 index ad029ad..0000000 --- a/plugin/src/test/java/team/unnamed/creativeglyphs/command/ArgumentStackTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package team.unnamed.creativeglyphs.command; - -import org.junit.jupiter.api.Test; -import team.unnamed.creativeglyphs.plugin.command.ArgumentStack; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class ArgumentStackTest { - - @Test - public void test() { - ArgumentStack args = ArgumentStack.of("arg0", "arg1", "arg2"); - - assertTrue(args.hasNext()); - assertEquals(3, args.available()); - assertEquals("arg0", args.next()); - - assertTrue(args.hasNext()); - assertEquals(2, args.available()); - assertEquals("arg1", args.next()); - - assertTrue(args.hasNext()); - assertEquals(1, args.available()); - assertEquals("arg2", args.next()); - - assertFalse(args.hasNext()); - assertEquals(0, args.available()); - assertThrows(IndexOutOfBoundsException.class, args::next); - } - -}