callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JsonObjectBuilder.JsonObject getChartData() throws Exception {
+ int value = callable.call();
+ if (value == 0) {
+ // Null = skip the chart
+ return null;
+ }
+ return new JsonObjectBuilder().appendField("value", value).build();
+ }
+ }
+
+ /**
+ * An extremely simple JSON builder.
+ *
+ * While this class is neither feature-rich nor the most performant one, it's sufficient enough
+ * for its use-case.
+ */
+ public static class JsonObjectBuilder {
+
+ private StringBuilder builder = new StringBuilder();
+
+ private boolean hasAtLeastOneField = false;
+
+ public JsonObjectBuilder() {
+ builder.append("{");
+ }
+
+ /**
+ * Appends a null field to the JSON.
+ *
+ * @param key The key of the field.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendNull(String key) {
+ appendFieldUnescaped(key, "null");
+ return this;
+ }
+
+ /**
+ * Appends a string field to the JSON.
+ *
+ * @param key The key of the field.
+ * @param value The value of the field.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, String value) {
+ if (value == null) {
+ throw new IllegalArgumentException("JSON value must not be null");
+ }
+ appendFieldUnescaped(key, "\"" + escape(value) + "\"");
+ return this;
+ }
+
+ /**
+ * Appends an integer field to the JSON.
+ *
+ * @param key The key of the field.
+ * @param value The value of the field.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, int value) {
+ appendFieldUnescaped(key, String.valueOf(value));
+ return this;
+ }
+
+ /**
+ * Appends an object to the JSON.
+ *
+ * @param key The key of the field.
+ * @param object The object.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, JsonObject object) {
+ if (object == null) {
+ throw new IllegalArgumentException("JSON object must not be null");
+ }
+ appendFieldUnescaped(key, object.toString());
+ return this;
+ }
+
+ /**
+ * Appends a string array to the JSON.
+ *
+ * @param key The key of the field.
+ * @param values The string array.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, String[] values) {
+ if (values == null) {
+ throw new IllegalArgumentException("JSON values must not be null");
+ }
+ String escapedValues =
+ Arrays.stream(values)
+ .map(value -> "\"" + escape(value) + "\"")
+ .collect(Collectors.joining(","));
+ appendFieldUnescaped(key, "[" + escapedValues + "]");
+ return this;
+ }
+
+ /**
+ * Appends an integer array to the JSON.
+ *
+ * @param key The key of the field.
+ * @param values The integer array.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, int[] values) {
+ if (values == null) {
+ throw new IllegalArgumentException("JSON values must not be null");
+ }
+ String escapedValues =
+ Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(","));
+ appendFieldUnescaped(key, "[" + escapedValues + "]");
+ return this;
+ }
+
+ /**
+ * Appends an object array to the JSON.
+ *
+ * @param key The key of the field.
+ * @param values The integer array.
+ * @return A reference to this object.
+ */
+ public JsonObjectBuilder appendField(String key, JsonObject[] values) {
+ if (values == null) {
+ throw new IllegalArgumentException("JSON values must not be null");
+ }
+ String escapedValues =
+ Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(","));
+ appendFieldUnescaped(key, "[" + escapedValues + "]");
+ return this;
+ }
+
+ /**
+ * Appends a field to the object.
+ *
+ * @param key The key of the field.
+ * @param escapedValue The escaped value of the field.
+ */
+ private void appendFieldUnescaped(String key, String escapedValue) {
+ if (builder == null) {
+ throw new IllegalStateException("JSON has already been built");
+ }
+ if (key == null) {
+ throw new IllegalArgumentException("JSON key must not be null");
+ }
+ if (hasAtLeastOneField) {
+ builder.append(",");
+ }
+ builder.append("\"").append(escape(key)).append("\":").append(escapedValue);
+ hasAtLeastOneField = true;
+ }
+
+ /**
+ * Builds the JSON string and invalidates this builder.
+ *
+ * @return The built JSON string.
+ */
+ public JsonObject build() {
+ if (builder == null) {
+ throw new IllegalStateException("JSON has already been built");
+ }
+ JsonObject object = new JsonObject(builder.append("}").toString());
+ builder = null;
+ return object;
+ }
+
+ /**
+ * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt.
+ *
+ *
This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'.
+ * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n").
+ *
+ * @param value The value to escape.
+ * @return The escaped value.
+ */
+ private static String escape(String value) {
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < value.length(); i++) {
+ char c = value.charAt(i);
+ if (c == '"') {
+ builder.append("\\\"");
+ } else if (c == '\\') {
+ builder.append("\\\\");
+ } else if (c <= '\u000F') {
+ builder.append("\\u000").append(Integer.toHexString(c));
+ } else if (c <= '\u001F') {
+ builder.append("\\u00").append(Integer.toHexString(c));
+ } else {
+ builder.append(c);
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * A super simple representation of a JSON object.
+ *
+ *
This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not
+ * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String,
+ * JsonObject)}.
+ */
+ public static class JsonObject {
+
+ private final String value;
+
+ private JsonObject(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String toString() {
+ return value;
+ }
+ }
+ }
+}
diff --git a/src/main/java/cn/lunadeer/ac48/utils/Notification.java b/src/main/java/cn/lunadeer/ac48/utils/Notification.java
new file mode 100644
index 0000000..05a9c12
--- /dev/null
+++ b/src/main/java/cn/lunadeer/ac48/utils/Notification.java
@@ -0,0 +1,64 @@
+package cn.lunadeer.ac48.utils;
+
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.Style;
+import net.kyori.adventure.text.format.TextColor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class Notification {
+ private static final Style i_style = Style.style(TextColor.color(139, 255, 123));
+ private static final Style w_style = Style.style(TextColor.color(255, 185, 69));
+ private static final Style e_style = Style.style(TextColor.color(255, 96, 72));
+ private static final Style d_style = Style.style(TextColor.color(0, 255, 255));
+
+ private static final String prefix = "[AC48] ";
+
+ public static void info(Player player, String msg) {
+ player.sendMessage(Component.text(prefix + msg, i_style));
+ }
+
+ public static void warn(Player player, String msg) {
+ player.sendMessage(Component.text(prefix + msg, w_style));
+ }
+
+ public static void error(Player player, String msg) {
+ player.sendMessage(Component.text(prefix + msg, e_style));
+ }
+
+ public static void info(CommandSender sender, String msg) {
+ sender.sendMessage(Component.text(prefix + msg, i_style));
+ }
+
+ public static void warn(CommandSender sender, String msg) {
+ sender.sendMessage(Component.text(prefix + msg, w_style));
+ }
+
+ public static void error(CommandSender sender, String msg) {
+ sender.sendMessage(Component.text(prefix + msg, e_style));
+ }
+
+ public static void info(Player player, Component msg) {
+ player.sendMessage(Component.text(prefix, i_style).append(msg));
+ }
+
+ public static void warn(Player player, Component msg) {
+ player.sendMessage(Component.text(prefix, w_style).append(msg));
+ }
+
+ public static void error(Player player, Component msg) {
+ player.sendMessage(Component.text(prefix, e_style).append(msg));
+ }
+
+ public static void info(CommandSender player, Component msg) {
+ player.sendMessage(Component.text(prefix, i_style).append(msg));
+ }
+
+ public static void warn(CommandSender player, Component msg) {
+ player.sendMessage(Component.text(prefix, w_style).append(msg));
+ }
+
+ public static void error(CommandSender player, Component msg) {
+ player.sendMessage(Component.text(prefix, e_style).append(msg));
+ }
+}
diff --git a/src/main/java/cn/lunadeer/ac48/utils/Scheduler.java b/src/main/java/cn/lunadeer/ac48/utils/Scheduler.java
new file mode 100644
index 0000000..36bd228
--- /dev/null
+++ b/src/main/java/cn/lunadeer/ac48/utils/Scheduler.java
@@ -0,0 +1,15 @@
+package cn.lunadeer.ac48.utils;
+
+import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
+import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class Scheduler {
+ public Scheduler(JavaPlugin plugin) {
+ region = plugin.getServer().getGlobalRegionScheduler();
+ async = plugin.getServer().getAsyncScheduler();
+ }
+
+ public GlobalRegionScheduler region;
+ public AsyncScheduler async;
+}
diff --git a/src/main/java/cn/lunadeer/ac48/utils/XLogger.java b/src/main/java/cn/lunadeer/ac48/utils/XLogger.java
new file mode 100644
index 0000000..d8a3771
--- /dev/null
+++ b/src/main/java/cn/lunadeer/ac48/utils/XLogger.java
@@ -0,0 +1,57 @@
+package cn.lunadeer.ac48.utils;
+
+import cn.lunadeer.ac48.AC48;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.util.logging.Logger;
+
+public class XLogger {
+ private static final JavaPlugin _plugin = AC48.instance;
+ private static final Logger _logger = _plugin.getLogger();
+
+ private static final String prefix = "[AC48] ";
+
+ public static void info(Player player, String message) {
+ Notification.info(player, prefix + "I | " + message);
+ if (AC48.config.isDebug())
+ debug("来自玩家[ " + player.getName() + " ] 的信息 | " + message);
+ }
+
+ public static void info(String message) {
+ _logger.info(" I | " + message);
+ }
+
+ public static void warn(Player player, String message) {
+ Notification.warn(player, prefix + "W | " + message);
+ if (AC48.config.isDebug())
+ debug("来自玩家[ " + player.getName() + " ] 的警告 | " + message);
+ }
+
+ public static void warn(String message) {
+ _logger.warning(" W | " + message);
+ }
+
+ public static void err(Player player, String message) {
+ Notification.error(player, prefix + "E | " + message);
+ if (AC48.config.isDebug())
+ debug("来自玩家[ " + player.getName() + " ] 的报错 | " + message);
+ }
+
+ public static void err(String message) {
+ _logger.severe(" E | " + message);
+ }
+
+ public static void debug(Player player, String message) {
+ if (!AC48.config.isDebug()) return;
+ if (player.isOp())
+ Notification.info(player, prefix + "D | " + message);
+ else
+ debug("来自玩家[ " + player.getName() + " ] 的调试 | " + message);
+ }
+
+ public static void debug(String message) {
+ if (!AC48.config.isDebug()) return;
+ _logger.info(" D | " + message);
+ }
+}
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index 1607b72..b7b5be8 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -23,4 +23,6 @@ Tag:
Material: "建材赞助"
Audience: "观众"
+CheckUpdate: true
+
Debug: false
\ No newline at end of file