From b4f6831e54593fdba3cba11a479f61b7e1e03edd Mon Sep 17 00:00:00 2001 From: Andre_601 <11576465+Andre601@users.noreply.github.com> Date: Sun, 11 Jul 2021 01:37:09 +0200 Subject: [PATCH 1/8] Update development from master (#662) * Add contributing and expansion section * Start using new Issue template system * Create bug_report.yml * Rename feature_request.md to feature_request_old.md * Create feature_request.yml * fix unique name * Update feature_request_old.md * Add Checkboxes * Add checkboxes * disable default issue body * Delete bug_report_old.md * Delete feature_request_old.md * Rename bug_report_new.yml to bug_report.yml * Check if deleting this fixed the PR... * Use description in favour of about * improve feature_request.md * Update bug_report.yml * Assign "Type: Issue (Unconfirmed)" label * Use lists and not comma-separated string * Update feature_request.yml * Use id option for error and dump fields * Add field for logs * Remove deprecated issue_body type * Update feature_request.yml * Improve description of bug_report.yml * Initial 1.17 Changes * add render * Revert build.gradle dependencies change * Fixed duplicate files * Initial test on adventure * started moving to pure adventure * finished kyori impl * added 1.17 to nmsversion (what does this even do) * removed dev for release * added dev back Co-authored-by: PiggyPiglet Co-authored-by: darbyjack Co-authored-by: PiggyPiglet --- build.gradle | 13 +- gradlew | 0 .../placeholderapi/PlaceholderAPIPlugin.java | 18 + .../cloud/CommandECloudExpansionList.java | 114 +- .../placeholderapi/expansion/NMSVersion.java | 3 +- .../clip/placeholderapi/libs/JSONMessage.java | 1120 ----------------- 6 files changed, 86 insertions(+), 1182 deletions(-) mode change 100644 => 100755 gradlew delete mode 100644 src/main/java/me/clip/placeholderapi/libs/JSONMessage.java diff --git a/build.gradle b/build.gradle index f38ba27..5df263f 100644 --- a/build.gradle +++ b/build.gradle @@ -13,17 +13,21 @@ version "2.10.10-DEV-${System.getProperty("BUILD_NUMBER")}" description "An awesome placeholder provider!" repositories { + maven({ url = "https://oss.sonatype.org/content/repositories/snapshots/" }) + mavenCentral() + mavenLocal() maven({ url = "https://repo.codemc.org/repository/maven-public" }) maven({ url = "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" }) } dependencies { - implementation "com.google.code.gson:gson:2.8.6" implementation "org.bstats:bstats-bukkit:2.2.1" - compileOnly "org.spigotmc:spigot-api:1.17-R0.1-SNAPSHOT" + implementation "net.kyori:adventure-platform-bukkit:4.0.0-SNAPSHOT" + + compileOnly "org.spigotmc:spigot-api:1.17.1-R0.1-SNAPSHOT" compileOnly "org.jetbrains:annotations:19.0.0" testImplementation "org.openjdk.jmh:jmh-core:1.23" @@ -60,6 +64,7 @@ shadowJar { archiveClassifier.set("") relocate "org.bstats", "me.clip.placeholderapi.metrics" + relocate "net.kyori", "me.clip.placeholderapi.libs.kyori" } license { @@ -69,10 +74,6 @@ license { header = file('config/headers/main.txt') } - matching('**/JSONMessage.java') { - header = file('config/headers/jsonmessage.txt') - } - ext { year = 2021 } diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java b/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java index 094bddf..b121bd3 100644 --- a/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java +++ b/src/main/java/me/clip/placeholderapi/PlaceholderAPIPlugin.java @@ -32,6 +32,8 @@ import me.clip.placeholderapi.expansion.manager.CloudExpansionManager; import me.clip.placeholderapi.expansion.manager.LocalExpansionManager; import me.clip.placeholderapi.listeners.ServerLoadEventListener; import me.clip.placeholderapi.updatechecker.UpdateChecker; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.serializer.craftbukkit.MinecraftComponentSerializer; import org.bstats.bukkit.Metrics; import org.bstats.charts.AdvancedPie; import org.bstats.charts.SimplePie; @@ -75,6 +77,8 @@ public final class PlaceholderAPIPlugin extends JavaPlugin { @NotNull private final CloudExpansionManager cloudExpansionManager = new CloudExpansionManager(this); + private BukkitAudiences adventure; + /** * Gets the static instance of the main class for PlaceholderAPI. This class is not the actual API * class, this is the main class that extends JavaPlugin. For most API methods, use static methods @@ -140,6 +144,8 @@ public final class PlaceholderAPIPlugin extends JavaPlugin { setupMetrics(); setupExpansions(); + adventure = BukkitAudiences.create(this); + if (config.isCloudEnabled()) { getCloudExpansionManager().load(); } @@ -158,6 +164,9 @@ public final class PlaceholderAPIPlugin extends JavaPlugin { Bukkit.getScheduler().cancelTasks(this); + adventure.close(); + adventure = null; + instance = null; } @@ -185,6 +194,15 @@ public final class PlaceholderAPIPlugin extends JavaPlugin { return cloudExpansionManager; } + @NotNull + public BukkitAudiences getAdventure() { + if(adventure == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + + return adventure; + } + /** * Obtain the configuration class for PlaceholderAPI. * diff --git a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java index e35d846..cb4d38c 100644 --- a/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java +++ b/src/main/java/me/clip/placeholderapi/commands/impl/cloud/CommandECloudExpansionList.java @@ -26,14 +26,7 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.primitives.Ints; import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.stream.Collectors; @@ -43,15 +36,22 @@ import me.clip.placeholderapi.commands.PlaceholderCommand; import me.clip.placeholderapi.configuration.ExpansionSort; import me.clip.placeholderapi.expansion.PlaceholderExpansion; import me.clip.placeholderapi.expansion.cloud.CloudExpansion; -import me.clip.placeholderapi.libs.JSONMessage; import me.clip.placeholderapi.util.Format; import me.clip.placeholderapi.util.Msg; -import org.bukkit.ChatColor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.event.ClickEvent; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Unmodifiable; +import static net.kyori.adventure.text.Component.*; +import static net.kyori.adventure.text.format.NamedTextColor.*; + public final class CommandECloudExpansionList extends PlaceholderCommand { private static final int PAGE_SIZE = 10; @@ -132,77 +132,81 @@ public final class CommandECloudExpansionList extends PlaceholderCommand { builder.append(" &bPage&7: &a") .append(page) - .append("&r") - .append('\n'); + .append("&r"); } - @NotNull - private static JSONMessage getMessage(@NotNull final List expansions, + private static Component getMessage(@NotNull final List expansions, final int page, final int limit, @NotNull final String target) { final SimpleDateFormat format = PlaceholderAPIPlugin.getDateFormat(); - final StringBuilder tooltip = new StringBuilder(); - final JSONMessage message = JSONMessage.create(); + final TextComponent.Builder message = text(); for (int index = 0; index < expansions.size(); index++) { final CloudExpansion expansion = expansions.get(index); + final TextComponent.Builder line = text(); - tooltip.append("&bClick to download this expansion!") - .append('\n') - .append('\n') - .append("&bAuthor: &f") - .append(expansion.getAuthor()) - .append('\n') - .append("&bVerified: ") - .append(expansion.isVerified() ? "&a&l✔&r" : "&c&l❌&r") - .append('\n') - .append("&bLatest Version: &f") - .append(expansion.getLatestVersion()) - .append('\n') - .append("&bReleased: &f") - .append(format.format(expansion.getLastUpdate())); + final int expansionNumber = index + ((page - 1) * PAGE_SIZE) + 1; + line.append(text(expansionNumber + ". ", DARK_GRAY)); - final String description = expansion.getDescription(); - if (description != null && !description.isEmpty()) { - tooltip.append('\n') - .append('\n') - .append("&f") - .append(description.replace("\r", "").trim()); + final NamedTextColor expansionColour; + + if (expansion.shouldUpdate()) { + expansionColour = GOLD; + } else { + if (expansion.hasExpansion()) { + expansionColour = GREEN; + } else { + expansionColour = GRAY; + } } - message.then(Msg.color( - "&8" + (index + ((page - 1) * PAGE_SIZE) + 1) + ".&r " + (expansion.shouldUpdate() ? "&6" - : expansion.hasExpansion() ? "&a" : "&7") + expansion.getName())); + line.append(text(expansion.getName(), expansionColour)); - message.tooltip(Msg.color(tooltip.toString())); - message.suggestCommand("/papi ecloud download " + expansion.getName()); + line.clickEvent(ClickEvent.suggestCommand("/papi ecloud download " + expansion.getName())); - if (index < expansions.size() - 1) { - message.newline(); + final TextComponent.Builder hoverText = text("Click to download this expansion!", AQUA) + .append(newline()).append(newline()) + .append(text("Author: ", AQUA)).append(text(expansion.getAuthor(), WHITE)) + .append(newline()) + .append(text("Verified: ", AQUA)).append(text(expansion.isVerified() ? "✔" : "❌", expansion.isVerified() ? GREEN : RED, TextDecoration.BOLD)) + .append(newline()) + .append(text("Released: ", AQUA)).append(text(format.format(expansion.getLastUpdate()), WHITE)) + .toBuilder(); + + Optional.ofNullable(expansion.getDescription()) + .filter(description -> !description.isEmpty()) + .ifPresent(description -> hoverText.append(newline()).append(newline()) + .append(text(description.replace("\r", "").trim(), WHITE)) + ); + + line.hoverEvent(HoverEvent.showText(hoverText.build())); + + if (index != expansions.size() - 1) { + line.append(newline()); } - tooltip.setLength(0); + message.append(line.build()); } if (limit > 1) { - message.newline(); + message.append(newline()); + + final TextComponent.Builder left = text("◀", page > 1 ? GRAY : DARK_GRAY).toBuilder(); - message.then("◀") - .color(page > 1 ? ChatColor.GRAY : ChatColor.DARK_GRAY); if (page > 1) { - message.runCommand("/papi ecloud list " + target + " " + (page - 1)); + left.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page - 1))); } - message.then(" " + page + " ").color(ChatColor.GREEN); + final TextComponent.Builder right = text("▶", page < limit ? GRAY : DARK_GRAY).toBuilder(); - message.then("▶") - .color(page < limit ? ChatColor.GRAY : ChatColor.DARK_GRAY); if (page < limit) { - message.runCommand("/papi ecloud list " + target + " " + (page + 1)); + right.clickEvent(ClickEvent.runCommand("/papi ecloud list " + target + " " + (page + 1))); } + + message.append(left, text(" " + page + " ", GREEN), right); } - return message; + return message.build(); } private static void addExpansionTable(@NotNull final List expansions, @@ -321,8 +325,8 @@ public final class CommandECloudExpansionList extends PlaceholderCommand { final int limit = (int) Math.ceil((double) expansions.size() / PAGE_SIZE); - final JSONMessage message = getMessage(values, page, limit, params.get(0)); - message.send(((Player) sender)); + final Component message = getMessage(values, page, limit, params.get(0)); + plugin.getAdventure().player((Player) sender).sendMessage(message); } @Override diff --git a/src/main/java/me/clip/placeholderapi/expansion/NMSVersion.java b/src/main/java/me/clip/placeholderapi/expansion/NMSVersion.java index a4b2ac3..541a004 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/NMSVersion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/NMSVersion.java @@ -41,7 +41,8 @@ public enum NMSVersion { SPIGOT_1_15_R1("v1_15_R1"), SPIGOT_1_16_R1("v1_16_R1"), SPIGOT_1_16_R2("v1_16_R2"), - SPIGOT_1_16_R3("v1_16_R3"); + SPIGOT_1_16_R3("v1_16_R3"), + SPIGOT_1_17_R1("v1_17_R1"); private final String version; diff --git a/src/main/java/me/clip/placeholderapi/libs/JSONMessage.java b/src/main/java/me/clip/placeholderapi/libs/JSONMessage.java deleted file mode 100644 index 07c100d..0000000 --- a/src/main/java/me/clip/placeholderapi/libs/JSONMessage.java +++ /dev/null @@ -1,1120 +0,0 @@ -/* - * Copyright (c) 2018-2021 Peter Blood - * - * 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.clip.placeholderapi.libs; - -import com.google.common.base.Strings; -import com.google.common.collect.BiMap; -import com.google.common.collect.ImmutableBiMap; -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.Vector; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.entity.Player; - -/** - * This is a complete JSON message builder class. To create a new JSONMessage do {@link - * #create(String)} - * - * @author Rayzr - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -public class JSONMessage { - - private static final BiMap stylesToNames; - - static { - ImmutableBiMap.Builder builder = ImmutableBiMap.builder(); - for (final ChatColor style : ChatColor.values()) { - if (!style.isFormat()) { - continue; - } - - String styleName; - switch (style) { - case MAGIC: - styleName = "obfuscated"; - break; - case UNDERLINE: - styleName = "underlined"; - break; - default: - styleName = style.name().toLowerCase(); - break; - } - - builder.put(style, styleName); - } - stylesToNames = builder.build(); - } - - - private final List parts = new ArrayList<>(); - private int centeringStartIndex = -1; - - /** - * Creates a new {@link JSONMessage} object - * - * @param text The text to start with - */ - private JSONMessage(String text) { - parts.add(new MessagePart(text)); - } - - /** - * Creates a new {@link JSONMessage} object - * - * @param text The text to start with - * @return A new {@link JSONMessage} object - */ - public static JSONMessage create(String text) { - return new JSONMessage(text); - } - - /** - * Creates a new {@link JSONMessage} object - * - * @return A new {@link JSONMessage} object - */ - public static JSONMessage create() { - return create(""); - } - - /** - * Sends an action bar message - * - * @param message The message to send - * @param players The players you want to send it to - */ - public static void actionbar(String message, Player... players) { - ReflectionHelper.sendPacket(ReflectionHelper - .createActionbarPacket(ChatColor.translateAlternateColorCodes('&', message)), players); - } - - /** - * @return The latest {@link MessagePart} - * @throws ArrayIndexOutOfBoundsException If {@code parts.size() <= 0}. - */ - public MessagePart last() { - if (parts.size() <= 0) { - throw new ArrayIndexOutOfBoundsException("No MessageParts exist!"); - } - return parts.get(parts.size() - 1); - } - - /** - * Converts this {@link JSONMessage} instance to actual JSON - * - * @return The JSON representation of this {@link JSONMessage} - */ - public JsonObject toJSON() { - JsonObject obj = new JsonObject(); - - obj.addProperty("text", ""); - - JsonArray array = new JsonArray(); - - parts.stream() - .map(MessagePart::toJSON) - .forEach(array::add); - - obj.add("extra", array); - - return obj; - } - - /** - * Converts this {@link JSONMessage} object to a String representation of the JSON. This is an - * alias of {@code toJSON().toString()}. - */ - @Override - public String toString() { - return toJSON().toString(); - } - - /** - * Converts this {@link JSONMessage} object to the legacy formatting system, which uses formatting - * codes (like &6, &l, &4, etc.) - * - * @return This {@link JSONMessage} instance {@link JSONMessage} in legacy format - */ - public String toLegacy() { - StringBuilder output = new StringBuilder(); - - parts.stream() - .map(MessagePart::toLegacy) - .forEach(output::append); - - return output.toString(); - } - - /** - * Sends this {@link JSONMessage} to all the players specified - * - * @param players The players you want to send this to - */ - public void send(Player... players) { - if (ReflectionHelper.MAJOR_VER >= 16) { -// ReflectionHelper.sendTextPacket(toString(), players); -// return; - } - - ReflectionHelper.sendPacket(ReflectionHelper.createTextPacket(toString()), players); - } - - /** - * Sends this as a title to all the players specified - * - * @param fadeIn How many ticks to fade in - * @param stay How many ticks to stay - * @param fadeOut How many ticks to fade out - * @param players The players to send this to - */ - public void title(int fadeIn, int stay, int fadeOut, Player... players) { - ReflectionHelper - .sendPacket(ReflectionHelper.createTitleTimesPacket(fadeIn, stay, fadeOut), players); - ReflectionHelper.sendPacket(ReflectionHelper.createTitlePacket(toString()), players); - } - - /** - * Sends this as a subtitle to all the players specified. Must be used after sending a {@link - * #title(int, int, int, Player...) title}. - * - * @param players The players to send this to - */ - public void subtitle(Player... players) { - ReflectionHelper.sendPacket(ReflectionHelper.createSubtitlePacket(toString()), players); - } - - /** - * Sends an action bar message - * - * @param players The players you want to send this to - */ - public void actionbar(Player... players) { - actionbar(toLegacy(), players); - } - - /** - * Sets the color of the current message part. - * - * @param color The color to set - * @return This {@link JSONMessage} instance - */ - public JSONMessage color(ChatColor color) { - if (!color.isColor()) { - throw new IllegalArgumentException(color.name() + " is not a color."); - } - - last().setColor(color); - return this; - } - - /** - * Sets the color of the current message part. - *
If the provided color is a hex color ({@code #rrggbb}) but the major version of MC is older - * than 1.16 will this - * default to the color WHITE. - * - * @param color The color to set - * @return This {@link JSONMessage} instance - */ - public JSONMessage color(String color) { - return color(color, ChatColor.WHITE); - } - - /** - * Sets the color of the current message part. - *
If the provided color is a hex color ({@code #rrggbb}) but the major version of MC is older - * than 1.16 will the provided - * default ChatColor be used instead. - * - * @param color The color to set - * @param def The default ChatColor to use, when MC version is older than 1.16 - * @return This {@link JSONMessage} instance - */ - public JSONMessage color(String color, ChatColor def) { - if (color.startsWith("#") && ReflectionHelper.MAJOR_VER < 16) { - return color(def); - } - - last().setColor(color); - return this; - } - - /** - * Sets the font of the current message part. - *
When this is used on versions older than 1.16 will this do nothing. - * - * @param font The font to set - * @return This {@link JSONMessage} instance - */ - public JSONMessage font(String font) { - if (ReflectionHelper.MAJOR_VER < 16) { - return this; - } - - last().setFont(font); - return this; - } - - /** - * Adds a style to the current message part. - * - * @param style The style to add - * @return This {@link JSONMessage} instance - */ - public JSONMessage style(ChatColor style) { - last().addStyle(style); - return this; - } - - /** - * Makes the text run a command. - * - * @param command The command to run - * @return This {@link JSONMessage} instance - */ - public JSONMessage runCommand(String command) { - last().setOnClick(ClickEvent.runCommand(command)); - return this; - } - - /** - * Makes the text suggest a command. - * - * @param command The command to suggest - * @return This {@link JSONMessage} instance - */ - public JSONMessage suggestCommand(String command) { - last().setOnClick(ClickEvent.suggestCommand(command)); - return this; - } - - /** - * Opens a URL. - * - * @param url The url to open - * @return This {@link JSONMessage} instance - */ - public JSONMessage openURL(String url) { - last().setOnClick(ClickEvent.openURL(url)); - return this; - } - - /** - * Copies the provided text to the Clipboard of the player. - *
When this is used on versions older than 1.15 will this default to {@link - * #suggestCommand(String) suggestCommand(String)}. - * - * @param text The text to copy - * @return This {@link JSONMessage} instance - */ - public JSONMessage copyText(String text) { - last().setOnClick(ClickEvent.copyText(text)); - return this; - } - - /** - * Changes the page of a book. Using this in a non-book context is useless and will probably - * error. - * - * @param page The page to change to - * @return This {@link JSONMessage} instance - */ - public JSONMessage changePage(int page) { - last().setOnClick(ClickEvent.changePage(page)); - return this; - } - - /** - * Shows text when you hover over it - * - * @param text The text to show - * @return This {@link JSONMessage} instance - */ - public JSONMessage tooltip(String text) { - last().setOnHover(HoverEvent.showText(text)); - return this; - } - - /** - * Shows text when you hover over it - * - * @param message The text to show - * @return This {@link JSONMessage} instance - */ - public JSONMessage tooltip(JSONMessage message) { - last().setOnHover(HoverEvent.showText(message)); - return this; - } - - /** - * Shows an achievement when you hover over it - * - * @param id The id of the achievement - * @return This {@link JSONMessage} instance - */ - public JSONMessage achievement(String id) { - last().setOnHover(HoverEvent.showAchievement(id)); - return this; - } - - /** - * Adds another part to this {@link JSONMessage} - * - * @param text The text to start the next {@link MessagePart} with - * @return This {@link JSONMessage} instance - */ - public JSONMessage then(String text) { - return then(new MessagePart(text)); - } - - /** - * Adds another part to this {@link JSONMessage} - * - * @param nextPart The next {@link MessagePart} - * @return This {@link JSONMessage} instance - */ - public JSONMessage then(MessagePart nextPart) { - parts.add(nextPart); - return this; - } - - /** - * Adds a horizontal bar to the message of the given length - * - * @param length The length of the horizontal bar - * @return This {@link JSONMessage} instance - */ - public JSONMessage bar(int length) { - return then(Strings.repeat("-", length)).color(ChatColor.DARK_GRAY) - .style(ChatColor.STRIKETHROUGH); - } - - /** - * Adds a horizontal bar to the message that's 53 characters long. This is the default width of - * the player's chat window. - * - * @return This {@link JSONMessage} instance - */ - public JSONMessage bar() { - return bar(53); - } - - /** - * Adds a blank line to the message - * - * @return This {@link JSONMessage} instance - */ - public JSONMessage newline() { - return then("\n"); - } - - /** - * Sets the starting point to begin centering JSONMessages. - * - * @return This {@link JSONMessage} instance - */ - public JSONMessage beginCenter() { - // Start with the NEXT message part. - centeringStartIndex = parts.size(); - return this; - } - - /** - * Ends the centering of the JSONMessage text. - * - * @return This {@link JSONMessage} instance - */ - public JSONMessage endCenter() { - int current = centeringStartIndex; - - while (current < parts.size()) { - Vector currentLine = new Vector<>(); - int totalLineLength = 0; - - for (; ; current++) { - MessagePart part = current < parts.size() ? parts.get(current) : null; - String raw = part == null ? null : ChatColor.stripColor(part.toLegacy()); - int rawLength = raw == null ? 0 : raw.length(); - - if (current >= parts.size() || totalLineLength + rawLength >= 53) { - int padding = Math.max(0, (53 - totalLineLength) / 2); - currentLine.firstElement() - .setText(Strings.repeat(" ", padding) + currentLine.firstElement().getText()); - currentLine.lastElement().setText(currentLine.lastElement().getText() + "\n"); - currentLine.clear(); - break; - } - - totalLineLength += rawLength; - currentLine.add(part); - } - } - - MessagePart last = parts.get(parts.size() - 1); - last.setText(last.getText().substring(0, last.getText().length() - 1)); - - centeringStartIndex = -1; - - return this; - } - - /////////////////////////// - // BEGIN UTILITY CLASSES // - /////////////////////////// - - /** - * Represents the JSON format that all click/hover events in JSON messages must follow. - *
- *
- * Reference - * - * @author Rayzr - */ - public static class MessageEvent { - - private String action; - private Object value; - - public MessageEvent(String action, Object value) { - this.action = action; - this.value = value; - } - - /** - * @return A {@link JsonObject} representing the properties of this {@link MessageEvent} - */ - public JsonObject toJSON() { - JsonObject obj = new JsonObject(); - obj.addProperty("action", action); - /* - * MC 1.16 changed "value" to "contents", but only for Hover events... Don't ask why. - * Since this lib only has tooltip and achievement can we simply check if action starts with "show_" - */ - String valueType = - (ReflectionHelper.MAJOR_VER >= 16 && action.startsWith("show_")) ? "contents" : "value"; - - if (value instanceof JsonElement) { - obj.add(valueType, (JsonElement) value); - } else { - obj.addProperty(valueType, value.toString()); - } - return obj; - } - - /** - * @return The action - */ - public String getAction() { - return action; - } - - /** - * @param action The action to set - */ - public void setAction(String action) { - this.action = action; - } - - /** - * @return The value - */ - public Object getValue() { - return value; - } - - /** - * @param value The value to set - */ - public void setValue(Object value) { - this.value = value; - } - - } - - public static class ClickEvent { - - /** - * Runs a command. - * - * @param command The command to run - * @return The {@link MessageEvent} - */ - public static MessageEvent runCommand(String command) { - return new MessageEvent("run_command", command); - } - - /** - * Suggests a command by inserting it in chat. - * - * @param command The command to suggest - * @return The {@link MessageEvent} - */ - public static MessageEvent suggestCommand(String command) { - return new MessageEvent("suggest_command", command); - } - - /** - * Requires web links to be enabled on the client. - * - * @param url The url to open - * @return The {@link MessageEvent} - */ - public static MessageEvent openURL(String url) { - return new MessageEvent("open_url", url); - } - - /** - * Only used with written books. - * - * @param page The page to switch to - * @return The {@link MessageEvent} - */ - public static MessageEvent changePage(int page) { - return new MessageEvent("change_page", page); - } - - /** - * Copies the provided text to the clipboard of the player. - *
When used on versions older than 1.15 will this {@link #suggestCommand(String) suggest - * the text} instead. - * - * @param text The text to copy. - * @return The {@link MessageEvent} - */ - public static MessageEvent copyText(String text) { - if (ReflectionHelper.MAJOR_VER < 15) { - return suggestCommand(text); - } - - return new MessageEvent("copy_to_clipboard", text); - } - - } - - public static class HoverEvent { - - /** - * Shows text when you hover over it - * - * @param text The text to show - * @return The {@link MessageEvent} - */ - public static MessageEvent showText(String text) { - return new MessageEvent("show_text", text); - } - - /** - * Shows text when you hover over it - * - * @param message The {@link JSONMessage} to show - * @return The {@link MessageEvent} - */ - public static MessageEvent showText(JSONMessage message) { - JsonArray arr = new JsonArray(); - arr.add(new JsonPrimitive("")); - arr.add(message.toJSON()); - return new MessageEvent("show_text", arr); - } - - /** - * Shows an achievement when you hover over it - * - * @param id The id of the achievement - * @return The {@link MessageEvent} - */ - public static MessageEvent showAchievement(String id) { - return new MessageEvent("show_achievement", id); - } - - } - - private static class ReflectionHelper { - - private static final String version; - private static Constructor chatComponentText; - - private static Class packetPlayOutChat; - private static Field packetPlayOutChatComponent; - private static Field packetPlayOutChatMessageType; - private static Field packetPlayOutChatUuid; - private static Object enumChatMessageTypeMessage; - private static Object enumChatMessageTypeActionbar; - - private static Constructor titlePacketConstructor; - private static Constructor titleTimesPacketConstructor; - private static Object enumActionTitle; - private static Object enumActionSubtitle; - - private static Field connection; - private static MethodHandle GET_HANDLE; - private static MethodHandle SEND_PACKET; - private static MethodHandle STRING_TO_CHAT; - private static boolean SETUP; - private static int MAJOR_VER = -1; - - static { - String[] split = Bukkit.getServer().getClass().getPackage().getName().split("\\."); - version = split[split.length - 1]; - - try { - MAJOR_VER = Integer.parseInt(version.split("_")[1]); - - final Class craftPlayer = getClass("{obc}.entity.CraftPlayer"); - Method getHandle = craftPlayer.getMethod("getHandle"); - connection = getHandle.getReturnType().getField("playerConnection"); - Method sendPacket = connection.getType().getMethod("sendPacket", getClass("{nms}.Packet")); - - chatComponentText = getClass("{nms}.ChatComponentText").getConstructor(String.class); - - final Class iChatBaseComponent = getClass("{nms}.IChatBaseComponent"); - - Method stringToChat; - - if (MAJOR_VER < 8) { - stringToChat = getClass("{nms}.ChatSerializer").getMethod("a", String.class); - } else { - stringToChat = getClass("{nms}.IChatBaseComponent$ChatSerializer") - .getMethod("a", String.class); - } - - GET_HANDLE = MethodHandles.lookup().unreflect(getHandle); - SEND_PACKET = MethodHandles.lookup().unreflect(sendPacket); - STRING_TO_CHAT = MethodHandles.lookup().unreflect(stringToChat); - - packetPlayOutChat = getClass("{nms}.PacketPlayOutChat"); - packetPlayOutChatComponent = getField(packetPlayOutChat, "a"); - packetPlayOutChatMessageType = getField(packetPlayOutChat, "b"); - packetPlayOutChatUuid = MAJOR_VER >= 16 ? getField(packetPlayOutChat, "c") : null; - - Class packetPlayOutTitle = getClass("{nms}.PacketPlayOutTitle"); - Class titleAction = getClass("{nms}.PacketPlayOutTitle$EnumTitleAction"); - - titlePacketConstructor = packetPlayOutTitle.getConstructor(titleAction, iChatBaseComponent); - titleTimesPacketConstructor = packetPlayOutTitle - .getConstructor(int.class, int.class, int.class); - - enumActionTitle = titleAction.getField("TITLE").get(null); - enumActionSubtitle = titleAction.getField("SUBTITLE").get(null); - - if (MAJOR_VER >= 12) { - Method getChatMessageType = getClass("{nms}.ChatMessageType").getMethod("a", byte.class); - - enumChatMessageTypeMessage = getChatMessageType.invoke(null, (byte) 1); - enumChatMessageTypeActionbar = getChatMessageType.invoke(null, (byte) 2); - } - - SETUP = true; - } catch (Exception e) { - e.printStackTrace(); - SETUP = false; - } - } - - static void sendPacket(Object packet, Player... players) { - assertIsSetup(); - - if (packet == null) { - return; - } - - for (Player player : players) { - try { - SEND_PACKET.bindTo(connection.get(GET_HANDLE.bindTo(player).invoke())).invoke(packet); - } catch (Throwable e) { - System.err.println("Failed to send packet"); - e.printStackTrace(); - } - } - - } - - static Object createActionbarPacket(String message) { - assertIsSetup(); - - Object packet = createTextPacket(message); - setType(packet, (byte) 2); - return packet; - } - - static Object createTextPacket(String message) { - assertIsSetup(); - - try { - Object packet = packetPlayOutChat.newInstance(); - setFieldValue(packetPlayOutChatComponent, packet, fromJson(message)); - setFieldValue(packetPlayOutChatUuid, packet, UUID.randomUUID()); - setType(packet, (byte) 1); - return packet; - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - static Object createTitlePacket(String message) { - assertIsSetup(); - - try { - return titlePacketConstructor.newInstance(enumActionTitle, fromJson(message)); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - static Object createTitleTimesPacket(int fadeIn, int stay, int fadeOut) { - assertIsSetup(); - - try { - return titleTimesPacketConstructor.newInstance(fadeIn, stay, fadeOut); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - static Object createSubtitlePacket(String message) { - assertIsSetup(); - - try { - return titlePacketConstructor.newInstance(enumActionSubtitle, fromJson(message)); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - private static void setType(Object chatPacket, byte type) { - assertIsSetup(); - - if (MAJOR_VER < 12) { - setFieldValue(packetPlayOutChatMessageType, chatPacket, type); - return; - } - - switch (type) { - case 1: - setFieldValue(packetPlayOutChatMessageType, chatPacket, enumChatMessageTypeMessage); - break; - case 2: - setFieldValue(packetPlayOutChatMessageType, chatPacket, enumChatMessageTypeActionbar); - break; - default: - throw new IllegalArgumentException("type must be 1 or 2"); - } - } - - /** - * Creates a ChatComponentText from plain text - * - * @param message The text to convert to a chat component - * @return The chat component - */ - static Object componentText(String message) { - assertIsSetup(); - - try { - return chatComponentText.newInstance(message); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - - } - - /** - * Attempts to convert a String representing a JSON message into a usable object - * - * @param json The JSON to attempt to parse - * @return The object representing the text in JSON form, or null if something went - * wrong converting the String to JSON data - */ - static Object fromJson(String json) { - assertIsSetup(); - - if (!json.trim().startsWith("{")) { - return componentText(json); - } - - try { - return STRING_TO_CHAT.invoke(json); - } catch (Throwable e) { - e.printStackTrace(); - return null; - } - } - - private static void assertIsSetup() { - if (!SETUP) { - throw new IllegalStateException("JSONMessage.ReflectionHelper is not set up yet!"); - } - } - - private static Class getClass(String path) throws ClassNotFoundException { - return Class.forName(path.replace("{nms}", "net.minecraft.server." + version) - .replace("{obc}", "org.bukkit.craftbukkit." + version)); - } - - private static void setFieldValue(Field field, Object instance, Object value) { - if (field == null) { - // useful for fields that might not exist - return; - } - - try { - field.set(instance, value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } - } - - private static Field getField(Class classObject, String fieldName) { - try { - Field field = classObject.getDeclaredField(fieldName); - field.setAccessible(true); - return field; - } catch (NoSuchFieldException e) { - e.printStackTrace(); - return null; - } - } - - private static int getVersion() { - return MAJOR_VER; - } - } - - /** - * Defines a section of the message, and represents the format that all JSON messages must follow - * in Minecraft. - *
- *
- * Reference - * - * @author Rayzr - */ - public static class MessagePart { - - private final List styles = new ArrayList<>(); - private MessageEvent onClick; - private MessageEvent onHover; - private String color; - private ChatColor legacyColor; - private String font; - private String text; - - public MessagePart(String text) { - this.text = text == null ? "null" : text; - } - - /** - * Converts this {@link MessagePart} into a {@link JsonObject} - * - * @return The Minecraft-compatible {@link JsonObject} - */ - public JsonObject toJSON() { - Objects.requireNonNull(text); - - JsonObject obj = new JsonObject(); - obj.addProperty("text", text); - - if (color != null && !color.isEmpty()) { - obj.addProperty("color", color.toLowerCase()); - } - - for (ChatColor style : styles) { - obj.addProperty(stylesToNames.get(style), true); - } - - if (onClick != null) { - obj.add("clickEvent", onClick.toJSON()); - } - - if (onHover != null) { - obj.add("hoverEvent", onHover.toJSON()); - } - - if (font != null) { - obj.addProperty("font", font); - } - - return obj; - - } - - /** - * @return This {@link MessagePart} in legacy-style color/formatting codes - */ - public String toLegacy() { - StringBuilder output = new StringBuilder(); - ChatColor legacyColor = getColor(); - - if (legacyColor != null) { - output.append(legacyColor); - } - - styles.stream() - .map(ChatColor::toString) - .forEach(output::append); - - return output.append(text).toString(); - } - - /** - * @return The click event bound - */ - public MessageEvent getOnClick() { - return onClick; - } - - /** - * @param onClick The new click event to bind - */ - public void setOnClick(MessageEvent onClick) { - this.onClick = onClick; - } - - /** - * @return The hover event bound - */ - public MessageEvent getOnHover() { - return onHover; - } - - /** - * @param onHover The new hover event to bind - */ - public void setOnHover(MessageEvent onHover) { - this.onHover = onHover; - } - - /** - * @return The color - */ - public String getColorValue() { - return color; - } - - /** - * @return The color - * @deprecated Use {@link #getColorValue()} instead - */ - @Deprecated - public ChatColor getColor() { - if (legacyColor != null) { - return legacyColor; - } - - if (this.color.startsWith("#") && ReflectionHelper.MAJOR_VER < 16) { - throw new IllegalStateException( - "Custom Hex colors can only be used in Minecraft 1.16 or newer!"); - } - - try { - return ChatColor.valueOf(this.color.toUpperCase()); - } catch (Exception ex) { - return null; - } - } - - /** - * @param color The color to set - * @deprecated Use {@link #setColor(String)} instead - */ - @Deprecated - public void setColor(ChatColor color) { - setColor(color == null ? null : color.name().toLowerCase()); - setLegacyColor(color); - } - - /** - * @param color The color to set - */ - public void setColor(String color) { - if (color != null && color.isEmpty()) { - throw new IllegalArgumentException("Color cannot be null!"); - } - this.color = color; - } - - /** - * @param color The legacy ChatColor to set - * @deprecated Use {@link #setColor(String)} instead - */ - @Deprecated - public void setLegacyColor(ChatColor color) { - legacyColor = color; - } - - /** - * @return The list of styles - */ - public List getStyles() { - return styles; - } - - /** - * @param style The new style to add - */ - public void addStyle(ChatColor style) { - if (style == null) { - throw new IllegalArgumentException("Style cannot be null!"); - } - if (!style.isFormat()) { - throw new IllegalArgumentException(style.name() + " is not a style!"); - } - styles.add(style); - } - - /** - * @return The font used - */ - public String getFont() { - return font; - } - - /** - * @param font The font to use - */ - public void setFont(String font) { - this.font = font; - } - - /** - * @return The raw text - */ - public String getText() { - return text; - } - - /** - * @param text The raw text to set - */ - public void setText(String text) { - this.text = text; - } - - } -} \ No newline at end of file From d79f9725bd1637d6f6ebf50bbc2976bbabf0124f Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Thu, 22 Jul 2021 23:31:48 +0200 Subject: [PATCH 2/8] Add logging options to PlaceholderExpansion --- .../expansion/PlaceholderExpansion.java | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index 1e71a45..8d5ec15 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -22,6 +22,7 @@ package me.clip.placeholderapi.expansion; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import me.clip.placeholderapi.PlaceholderAPIPlugin; import me.clip.placeholderapi.PlaceholderHook; import org.bukkit.Bukkit; @@ -275,7 +276,6 @@ public abstract class PlaceholderExpansion extends PlaceholderHook { * @param def The default boolean to return when the ConfigurationSection is null * @return boolean from the provided path or the default one provided */ - @NotNull public final boolean getBoolean(@NotNull final String path, final boolean def) { final ConfigurationSection section = getConfigSection(); return section == null ? def : section.getBoolean(path, def); @@ -293,6 +293,48 @@ public abstract class PlaceholderExpansion extends PlaceholderHook { final ConfigurationSection section = getConfigSection(); return section != null && section.contains(path); } + + /** + * Logs the provided message with the provided Level in the console. + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param level The Level at which the message should be logged with + * @param msg The message to log + */ + public void log(Level level, String msg) { + getPlaceholderAPI().getLogger().log(level, "[" + getName() + "] " + msg); + } + + /** + * Logs the provided message with Level "info". + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param msg The message to log + */ + public void info(String msg) { + log(Level.INFO, msg); + } + + /** + * Logs the provided message with Level "warning". + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param msg The message to log + */ + public void warning(String msg) { + log(Level.WARNING, msg); + } + + /** + * Logs the provided message with Level "severe" (error). + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param msg The message to log + */ + public void severe(String msg) { + log(Level.SEVERE, msg); + } + /** * Whether the provided Object is an instance of this PlaceholderExpansion. From 721904335d86bc0a0ad45041bb4964177a7e7f86 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Thu, 22 Jul 2021 23:56:29 +0200 Subject: [PATCH 3/8] Add overload for Throwable --- .../expansion/PlaceholderExpansion.java | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index 8d5ec15..907dc61 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -305,6 +305,18 @@ public abstract class PlaceholderExpansion extends PlaceholderHook { getPlaceholderAPI().getLogger().log(level, "[" + getName() + "] " + msg); } + /** + * Logs the provided message and Throwable with the provided Level in the console. + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param level The Level at which the message should be logged with + * @param msg The message to log + * @param throwable The Throwable to log + */ + public void log(Level level, String msg, Throwable throwable) { + getPlaceholderAPI().getLogger().log(level, msg, throwable); + } + /** * Logs the provided message with Level "info". *
The message will be prefixed with {@link #getName() [<expansion name>]} @@ -334,6 +346,17 @@ public abstract class PlaceholderExpansion extends PlaceholderHook { public void severe(String msg) { log(Level.SEVERE, msg); } + + /** + * Logs the provided message and Throwable with Level "severe" (error). + *
The message will be prefixed with {@link #getName() [<expansion name>]} + * + * @param msg The message to log + * @param throwable The Throwable to log + */ + public void severe(String msg, Throwable throwable) { + log(Level.SEVERE, msg, throwable); + } /** From fe0bb12d530858eba8290742e5f4013228e68579 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Sat, 31 Jul 2021 00:11:24 +0200 Subject: [PATCH 4/8] Add missing [] prefix --- .../me/clip/placeholderapi/expansion/PlaceholderExpansion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java index 907dc61..a7ff90b 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java +++ b/src/main/java/me/clip/placeholderapi/expansion/PlaceholderExpansion.java @@ -314,7 +314,7 @@ public abstract class PlaceholderExpansion extends PlaceholderHook { * @param throwable The Throwable to log */ public void log(Level level, String msg, Throwable throwable) { - getPlaceholderAPI().getLogger().log(level, msg, throwable); + getPlaceholderAPI().getLogger().log(level, "[" + getName() + "] " + msg, throwable); } /** From 38099198d602c72a9337c667892f97fae351caf6 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Mon, 20 Sep 2021 02:42:38 +0200 Subject: [PATCH 5/8] Include version in registration message --- .../events/ExpansionRegisterEvent.java | 21 +++++++++--- .../events/ExpansionUnregisterEvent.java | 16 ++++++---- .../events/ExpansionsLoadedEvent.java | 32 ++++++++++++++++--- .../placeholderapi/expansion/Cacheable.java | 8 +++-- .../placeholderapi/expansion/Cleanable.java | 8 +++-- .../expansion/Configurable.java | 26 +++++++++++---- .../placeholderapi/expansion/Relational.java | 15 +++++++++ .../placeholderapi/expansion/Taskable.java | 16 +++++++--- .../manager/LocalExpansionManager.java | 32 +++++++++++++++---- 9 files changed, 136 insertions(+), 38 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/events/ExpansionRegisterEvent.java b/src/main/java/me/clip/placeholderapi/events/ExpansionRegisterEvent.java index 6f812dc..e54e3e9 100644 --- a/src/main/java/me/clip/placeholderapi/events/ExpansionRegisterEvent.java +++ b/src/main/java/me/clip/placeholderapi/events/ExpansionRegisterEvent.java @@ -27,8 +27,11 @@ import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; /** - * Indicates that a {@link PlaceholderExpansion} has been registered by - * PlaceholderAPI. + * This event indicates that a single {@link PlaceholderExpansion PlaceholderExpansion} has + * been registered in PlaceholderAPI. + * + *

To know when all Expansions have been registered, use the + * {@link me.clip.placeholderapi.events.ExpansionsLoadedEvent ExpansionsLoadedEvent} instead. */ public final class ExpansionRegisterEvent extends Event implements Cancellable { @@ -48,15 +51,25 @@ public final class ExpansionRegisterEvent extends Event implements Cancellable { } /** - * The {@link PlaceholderExpansion expansion} that was registered. + * The {@link PlaceholderExpansion PlaceholderExpansion} that was registered in PlaceholderAPI. + *
The PlaceholderExpansion will be available for use when the event + * {@link #isCancelled() was not cancelled}! * - * @return The {@link PlaceholderExpansion} instance. + * @return Current instance of the registered {@link PlaceholderExpansion PlaceholderExpansion} */ @NotNull public PlaceholderExpansion getExpansion() { return expansion; } + /** + * Indicates if this event was cancelled or not. + *
A cancelled Event will result in the {@link #getExpansion() PlaceholderExpansion} NOT + * being added to PlaceholderAPI's internal list and will therefore be considered not registered + * anymore. + * + * @return Whether the event has been cancelled or not. + */ @Override public boolean isCancelled() { return cancelled; diff --git a/src/main/java/me/clip/placeholderapi/events/ExpansionUnregisterEvent.java b/src/main/java/me/clip/placeholderapi/events/ExpansionUnregisterEvent.java index 4c0beb1..6156003 100644 --- a/src/main/java/me/clip/placeholderapi/events/ExpansionUnregisterEvent.java +++ b/src/main/java/me/clip/placeholderapi/events/ExpansionUnregisterEvent.java @@ -26,15 +26,19 @@ import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; /** - * Indicates that a {@link PlaceholderExpansion} had been unregistered by - * PlaceholderAPI. + * This event indicates that a {@link PlaceholderExpansion PlaceholderExpansion} has been + * unregistered by PlaceholderAPI. + * + *

Note that this event is triggered before the PlaceholderExpansion is completely + * removed. + *
This includes removing any Listeners, stopping active tasks and clearing the cache of + * the PlaceholderExpansion. */ public final class ExpansionUnregisterEvent extends Event { @NotNull private static final HandlerList HANDLERS = new HandlerList(); - - + @NotNull private final PlaceholderExpansion expansion; @@ -48,9 +52,9 @@ public final class ExpansionUnregisterEvent extends Event { } /** - * The {@link PlaceholderExpansion expansion} that was unregistered. + * The {@link PlaceholderExpansion PlaceholderExpansion} that was unregistered. * - * @return The {@link PlaceholderExpansion} instance. + * @return The {@link PlaceholderExpansion PlaceholderExpansion} instance. */ @NotNull public PlaceholderExpansion getExpansion() { diff --git a/src/main/java/me/clip/placeholderapi/events/ExpansionsLoadedEvent.java b/src/main/java/me/clip/placeholderapi/events/ExpansionsLoadedEvent.java index b173007..f9ca024 100644 --- a/src/main/java/me/clip/placeholderapi/events/ExpansionsLoadedEvent.java +++ b/src/main/java/me/clip/placeholderapi/events/ExpansionsLoadedEvent.java @@ -21,18 +21,42 @@ package me.clip.placeholderapi.events; +import java.util.Collections; +import java.util.List; +import me.clip.placeholderapi.expansion.PlaceholderExpansion; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; /** - * Indicates that all {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlayceholderExpansions} - * have been loaded. - *
This event is fired on Server load and when reloading the - * confiuration. + * This event indicated that all {@link PlaceholderExpansion PlaceholderExpansions} have + * been registered in PlaceholderAPI and can now be used. + *
This even will also be triggered whenever PlaceholderAPI gets reloaded. + * + *

All PlaceholderExpansions, except for those loaded by plugins, are loaded + * after Spigot triggered its ServerLoadEvent (1.13+), or after PlaceholderAPI has been enabled. */ public class ExpansionsLoadedEvent extends Event { + + private final List expansions; + + public ExpansionsLoadedEvent(List expansions) { + this.expansions = Collections.unmodifiableList(expansions); + } + /** + * Returns a unmodifiable list of {@link PlaceholderExpansion PlaceholderExpansions} that + * have been registered by PlaceholderAPI. + * + *

This list does not include manually registered PlaceholderExpansions. + * + * @return List of {@link PlaceholderExpansion registered PlaceholderExpansions}. + */ + @NotNull + public final List getExpansions(){ + return expansions; + } + @NotNull private static final HandlerList HANDLERS = new HandlerList(); diff --git a/src/main/java/me/clip/placeholderapi/expansion/Cacheable.java b/src/main/java/me/clip/placeholderapi/expansion/Cacheable.java index aa105f8..20b2fb8 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/Cacheable.java +++ b/src/main/java/me/clip/placeholderapi/expansion/Cacheable.java @@ -21,9 +21,11 @@ package me.clip.placeholderapi.expansion; /** - * This interface allows a class which extends a {@link PlaceholderExpansion} to have the clear - * method called when the implementing expansion is unregistered from PlaceholderAPI. This is useful - * if we want to do things when the implementing hook is unregistered + * Classes implementing this interface will have a {@link #clear() clear void} that is called + * by PlaceholderAPI whenever the {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansion} + * is unregistered. + * + *

This allows you to execute things such as clearing internal caches, saving data to files, etc. * * @author Ryan McCarthy */ diff --git a/src/main/java/me/clip/placeholderapi/expansion/Cleanable.java b/src/main/java/me/clip/placeholderapi/expansion/Cleanable.java index b408acf..9b369eb 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/Cleanable.java +++ b/src/main/java/me/clip/placeholderapi/expansion/Cleanable.java @@ -23,9 +23,11 @@ package me.clip.placeholderapi.expansion; import org.bukkit.entity.Player; /** - * This interface allows a class which extends a {@link PlaceholderExpansion} to have the cleanup - * method called every time a player leaves the server. This is useful if we want to clean up after - * the player + * Classes implementing this interface will have a {@link #cleanup(Player) cleanup void} that is + * called by PlaceholderAPI whenever a Player leaves the server. + * + *

This can be useful for cases where you keep data of the player in a cache or similar + * and want to free up space whenever they leave. * * @author Ryan McCarthy */ diff --git a/src/main/java/me/clip/placeholderapi/expansion/Configurable.java b/src/main/java/me/clip/placeholderapi/expansion/Configurable.java index a20b73b..1e07fb4 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/Configurable.java +++ b/src/main/java/me/clip/placeholderapi/expansion/Configurable.java @@ -23,18 +23,32 @@ package me.clip.placeholderapi.expansion; import java.util.Map; /** - * Any {@link PlaceholderExpansion} class which implements configurable will have any options listed - * in the {@link #getDefaults()} map automatically added to the PlaceholderAPI config.yml file + * Implementing this interface allows {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansions} + * to set a list of default configuration values through the {@link #getDefaults() getDefaults method} + * that should be added to the config.yml of PlaceholderAPI. + * + *

The entries will be added under {@code expansions} as their own section. + *

Example:

+ * returning a Map with key {@code foo} and value {@code bar} will result in the following config entry: + * + *

+ * expansions:
+ *   myexpansion:
+ *     foo: "bar"
+ * 
+ * + *

The configuration is set before the PlaceholderExpansion is registered! * * @author Ryan McCarthy */ public interface Configurable { /** - * This method will be called before the implementing class is registered to obtain a map of - * configuration options that the implementing class needs These paths and values will be added to - * the PlaceholderAPI config.yml in the configuration section expansions.(placeholder - * identifier).(your key): (your value) + * The map returned by this method will be used to set config options in PlaceholderAPI's config.yml. + * + *

The key and value pairs are set under a section named after your + * {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansion} in the + * {@code expansions} section of the config. * * @return Map of config path / values which need to be added / removed from the PlaceholderAPI * config.yml file diff --git a/src/main/java/me/clip/placeholderapi/expansion/Relational.java b/src/main/java/me/clip/placeholderapi/expansion/Relational.java index 6d05e94..ba1872c 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/Relational.java +++ b/src/main/java/me/clip/placeholderapi/expansion/Relational.java @@ -22,7 +22,22 @@ package me.clip.placeholderapi.expansion; import org.bukkit.entity.Player; +/** + * Implementing this interface allows your {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansion} + * to be used as a relational placeholder expansion. + * + *

Relational placeholders take two Players as input and are always prefixed with {@code rel_}, + * so {@code %foo_bar%} becomes {@code %rel_foo_bar%} + */ public interface Relational { + /** + * This method is called whenever a placeholder starting with {@code rel_} is called. + * + * @param one The first player used for the placeholder. + * @param two The second player used for the placeholder. + * @param identifier The text right after the expansion's name (%expansion_identifier%) + * @return Parsed String from the expansion. + */ String onPlaceholderRequest(Player one, Player two, String identifier); } diff --git a/src/main/java/me/clip/placeholderapi/expansion/Taskable.java b/src/main/java/me/clip/placeholderapi/expansion/Taskable.java index fa0ac21..f71aef8 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/Taskable.java +++ b/src/main/java/me/clip/placeholderapi/expansion/Taskable.java @@ -20,18 +20,24 @@ package me.clip.placeholderapi.expansion; - +/** + * Implementing this interface adds the {@link #start() start} and {@link #stop() stop} void + * methods to your {@link me.clip.placeholderapi.expansion.PlaceholderExpansion PlaceholderExpansion}. + * + *

This can be used to execute methods and tasks whenever the PlaceholderExpansion has been + * successfully (un)registered. + */ public interface Taskable { /** - * Called when the implementing class has successfully been registered to the placeholder map - * Tasks that need to be performed when this expansion is registered should go here + * Called when the implementing class has successfully been registered to the placeholder map. + *
Tasks that need to be performed when this expansion is registered should go here */ void start(); /** - * Called when the implementing class has been unregistered from PlaceholderAPI Tasks that need to - * be performed when this expansion has unregistered should go here + * Called when the implementing class has been unregistered from PlaceholderAPI. + *
Tasks that need to be performed when this expansion has unregistered should go here */ void stop(); } diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index 872215c..a59893d 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -259,7 +259,8 @@ public final class LocalExpansionManager implements Listener { Bukkit.getPluginManager().registerEvents(((Listener) expansion), plugin); } - plugin.getLogger().info("Successfully registered expansion: " + expansion.getIdentifier()); + plugin.getLogger().info("Successfully registered expansion: " + expansion.getIdentifier() + + " [" + expansion.getVersion() + "]"); if (expansion instanceof Taskable) { ((Taskable) expansion).start(); @@ -319,18 +320,35 @@ public final class LocalExpansionManager implements Listener { plugin.getLogger().log(Level.SEVERE, "failed to load class files of expansions", exception); return; } - - final long registered = classes.stream() + + final List registered = classes.stream() .filter(Objects::nonNull) .map(this::register) .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + + final long needsUpdate = registered.stream() + .map(expansion -> plugin.getCloudExpansionManager().findCloudExpansionByName(expansion.getName()).orElse(null)) + .filter(Objects::nonNull) + .filter(CloudExpansion::shouldUpdate) .count(); - Msg.msg(sender, - registered == 0 ? "&6No expansions were registered!" - : registered + "&a placeholder hooks successfully registered!"); + StringBuilder message = new StringBuilder(registered.size() == 0 ? "&6" : "&a") + .append(registered.size()) + .append(' ') + .append("placeholder hook(s) registered!"); + + if (needsUpdate > 0) { + message.append("&6") + .append(needsUpdate) + .append(" placeholder hook(s) have an update available."); + } + + + Msg.msg(sender, message.toString()); - Bukkit.getPluginManager().callEvent(new ExpansionsLoadedEvent()); + Bukkit.getPluginManager().callEvent(new ExpansionsLoadedEvent(registered)); }); } From e969f374055c0a3af9b694fda16ecc5a81bad628 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Wed, 1 Dec 2021 19:05:28 +0100 Subject: [PATCH 6/8] Improve StringBuilder and also fix possible NPE --- .../expansion/manager/LocalExpansionManager.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index a59893d..5ba2821 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -160,7 +160,11 @@ public final class LocalExpansionManager implements Listener { @NotNull final Class clazz) { try { final PlaceholderExpansion expansion = createExpansionInstance(clazz); - + + if(expansion == null){ + return Optional.empty(); + } + Objects.requireNonNull(expansion.getAuthor(), "The expansion author is null!"); Objects.requireNonNull(expansion.getIdentifier(), "The expansion identifier is null!"); Objects.requireNonNull(expansion.getVersion(), "The expansion version is null!"); @@ -340,9 +344,11 @@ public final class LocalExpansionManager implements Listener { .append("placeholder hook(s) registered!"); if (needsUpdate > 0) { - message.append("&6") + message.append(' ') + .append("&6") .append(needsUpdate) - .append(" placeholder hook(s) have an update available."); + .append(' ') + .append("placeholder hook(s) have an update available."); } From 3ba29f11476fd255232eed9923c542d70b600e83 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Wed, 1 Dec 2021 19:09:02 +0100 Subject: [PATCH 7/8] Fix possible NPE for findExpansionsOnDisk --- .../expansion/manager/LocalExpansionManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index 5ba2821..29db58b 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -370,7 +370,12 @@ public final class LocalExpansionManager implements Listener { @NotNull public CompletableFuture<@NotNull List<@Nullable Class>> findExpansionsOnDisk() { - return Arrays.stream(folder.listFiles((dir, name) -> name.endsWith(".jar"))) + File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar")); + if(files == null){ + return CompletableFuture.supplyAsync(Collections::emptyList); + } + + return Arrays.stream(files) .map(this::findExpansionInFile) .collect(Futures.collector()); } From 31deef2a62caca663027dc089add04f11ccbbb99 Mon Sep 17 00:00:00 2001 From: Andre601 <11576465+Andre601@users.noreply.github.com> Date: Wed, 1 Dec 2021 21:30:41 +0100 Subject: [PATCH 8/8] Use completedFuture instead --- .../placeholderapi/expansion/manager/LocalExpansionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java index 29db58b..abded95 100644 --- a/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java +++ b/src/main/java/me/clip/placeholderapi/expansion/manager/LocalExpansionManager.java @@ -372,7 +372,7 @@ public final class LocalExpansionManager implements Listener { public CompletableFuture<@NotNull List<@Nullable Class>> findExpansionsOnDisk() { File[] files = folder.listFiles((dir, name) -> name.endsWith(".jar")); if(files == null){ - return CompletableFuture.supplyAsync(Collections::emptyList); + return CompletableFuture.completedFuture(Collections.emptyList()); } return Arrays.stream(files)