diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java
index 50b511a..27b64ca 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/Main.java
@@ -6,7 +6,7 @@ import com.gmail.artemis.the.gr8.playerstats.commands.TabCompleter;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.listeners.JoinListener;
import com.gmail.artemis.the.gr8.playerstats.msg.LanguageKeyHandler;
-import com.gmail.artemis.the.gr8.playerstats.msg.MessageFactory;
+import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.msg.PrideMessageFactory;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Bukkit;
@@ -36,15 +36,15 @@ public class Main extends JavaPlugin {
LanguageKeyHandler language = new LanguageKeyHandler();
//for now always use the PrideMessageFactory (it'll use the regular formatting when needed)
- MessageFactory messageFactory = new PrideMessageFactory(config, language);
+ MessageWriter messageWriter = new PrideMessageFactory(config, language);
//initialize the threadManager
- ThreadManager threadManager = new ThreadManager(adventure(), config, messageFactory, this);
+ ThreadManager threadManager = new ThreadManager(adventure(), config, messageWriter, this);
//register all commands and the tabCompleter
PluginCommand statcmd = this.getCommand("statistic");
if (statcmd != null) {
- statcmd.setExecutor(new StatCommand(adventure(), messageFactory, threadManager));
+ statcmd.setExecutor(new StatCommand(adventure(), messageWriter, threadManager));
statcmd.setTabCompleter(new TabCompleter());
}
PluginCommand reloadcmd = this.getCommand("statisticreload");
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java
index c4335d1..fee8fe5 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/ThreadManager.java
@@ -1,10 +1,10 @@
package com.gmail.artemis.the.gr8.playerstats;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
+import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
-import com.gmail.artemis.the.gr8.playerstats.msg.MessageFactory;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.command.CommandSender;
@@ -19,16 +19,16 @@ public class ThreadManager {
private final Main plugin;
private final BukkitAudiences adventure;
private static ConfigHandler config;
- private static MessageFactory messageFactory;
+ private static MessageWriter messageWriter;
private ReloadThread reloadThread;
private StatThread statThread;
private static long lastRecordedCalcTime;
- public ThreadManager(BukkitAudiences a, ConfigHandler c, MessageFactory m, Main p) {
+ public ThreadManager(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p) {
adventure = a;
config = c;
- messageFactory = m;
+ messageWriter = m;
plugin = p;
statThreadID = 0;
@@ -41,7 +41,7 @@ public class ThreadManager {
if (reloadThread == null || !reloadThread.isAlive()) {
reloadThreadID += 1;
- reloadThread = new ReloadThread(adventure, config, messageFactory, plugin, threshold, reloadThreadID, statThread, sender);
+ reloadThread = new ReloadThread(adventure, config, messageWriter, plugin, threshold, reloadThreadID, statThread, sender);
reloadThread.start();
}
else {
@@ -52,7 +52,7 @@ public class ThreadManager {
public void startStatThread(StatRequest request) {
statThreadID += 1;
- statThread = new StatThread(adventure, config, messageFactory, plugin, statThreadID, threshold, request, reloadThread);
+ statThread = new StatThread(adventure, config, messageWriter, plugin, statThreadID, threshold, request, reloadThread);
statThread.start();
}
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java
index 35a1c71..a06cdfe 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/commands/StatCommand.java
@@ -5,7 +5,7 @@ import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
-import com.gmail.artemis.the.gr8.playerstats.msg.MessageFactory;
+import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Material;
@@ -23,23 +23,23 @@ import org.jetbrains.annotations.Nullable;
public class StatCommand implements CommandExecutor {
private final BukkitAudiences adventure;
- private final MessageFactory messageFactory;
+ private final MessageWriter messageWriter;
private final ThreadManager threadManager;
- public StatCommand(BukkitAudiences a, MessageFactory m, ThreadManager t) {
+ public StatCommand(BukkitAudiences a, MessageWriter m, ThreadManager t) {
adventure = a;
- messageFactory = m;
+ messageWriter = m;
threadManager = t;
}
@Override
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) {
if (args.length == 0 || args[0].equalsIgnoreCase("help")) { //in case of less than 1 argument or "help", display the help message
- adventure.sender(sender).sendMessage(messageFactory.helpMsg(sender instanceof ConsoleCommandSender));
+ adventure.sender(sender).sendMessage(messageWriter.helpMsg(sender instanceof ConsoleCommandSender));
}
else if (args[0].equalsIgnoreCase("examples") ||
args[0].equalsIgnoreCase("example")) { //in case of "statistic examples", show examples
- adventure.sender(sender).sendMessage(messageFactory.usageExamples(sender instanceof ConsoleCommandSender));
+ adventure.sender(sender).sendMessage(messageWriter.usageExamples(sender instanceof ConsoleCommandSender));
}
else {
StatRequest request = generateRequest(sender, args);
@@ -143,17 +143,17 @@ public class StatCommand implements CommandExecutor {
private @Nullable TextComponent checkRequest(StatRequest request) {
boolean isConsoleSender = request.getCommandSender() instanceof ConsoleCommandSender;
if (request.getStatistic() == null) {
- return messageFactory.missingStatName(isConsoleSender);
+ return messageWriter.missingStatName(isConsoleSender);
}
Statistic.Type type = request.getStatistic().getType();
if (request.getSubStatEntry() == null && type != Statistic.Type.UNTYPED) {
- return messageFactory.missingSubStatName(type, isConsoleSender);
+ return messageWriter.missingSubStatName(type, isConsoleSender);
}
else if (!matchingSubStat(request)) {
- return messageFactory.wrongSubStatType(type, request.getSubStatEntry(), isConsoleSender);
+ return messageWriter.wrongSubStatType(type, request.getSubStatEntry(), isConsoleSender);
}
else if (request.getSelection() == Target.PLAYER && request.getPlayerName() == null) {
- return messageFactory.missingPlayerName(isConsoleSender);
+ return messageWriter.missingPlayerName(isConsoleSender);
}
else {
return null;
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/enums/PluginColor.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/enums/PluginColor.java
new file mode 100644
index 0000000..008f15c
--- /dev/null
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/enums/PluginColor.java
@@ -0,0 +1,33 @@
+package com.gmail.artemis.the.gr8.playerstats.enums;
+
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
+
+/** This enum represents the colorscheme PlayerStats uses in its output messages.
+
GRAY: ChatColor Gray
+ DARK_PURPLE: #6E3485 (used for default sub-titles, title-underscores and brackets)
+ MEDIUM_BLUE: #55AAFF (used for all plain feedback and error messages)
+ LIGHT_BLUE: #55C6FF (used for default hover-text)
+ DARK_GOLD: ChatColor Gold (used for first parts of usage messages and for first parts of hover-text accent)
+ MEDIUM_GOLD: #FFB80E (used for second parts of usage messages and for second parts of hover-text accent)
+ YELLOW: ChatColor Yellow (used for third parts of usage messages)
+ */
+public enum PluginColor {
+ GRAY (NamedTextColor.GRAY),
+ DARK_PURPLE (TextColor.fromHexString("#6E3485")),
+ MEDIUM_BLUE(TextColor.fromHexString("#55AAFF")),
+ LIGHT_BLUE (TextColor.fromHexString("#55C6FF")),
+ DARK_GOLD (NamedTextColor.GOLD),
+ MEDIUM_GOLD (TextColor.fromHexString("#FFD52B")),
+ YELLOW (NamedTextColor.YELLOW);
+
+ private final TextColor color;
+
+ PluginColor(TextColor color) {
+ this.color = color;
+ }
+
+ public TextColor getColor() {
+ return color;
+ }
+}
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/ComponentFactory.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/ComponentFactory.java
new file mode 100644
index 0000000..7f5ca38
--- /dev/null
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/ComponentFactory.java
@@ -0,0 +1,323 @@
+package com.gmail.artemis.the.gr8.playerstats.msg;
+
+import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
+import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
+import com.gmail.artemis.the.gr8.playerstats.enums.Target;
+import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
+import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
+import com.gmail.artemis.the.gr8.playerstats.utils.NumberFormatter;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.TranslatableComponent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
+import net.kyori.adventure.text.format.TextDecoration;
+import net.kyori.adventure.util.Index;
+import org.bukkit.Bukkit;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import static net.kyori.adventure.text.Component.*;
+import static net.kyori.adventure.text.Component.text;
+
+public class ComponentFactory {
+
+ private static ConfigHandler config;
+ private final LanguageKeyHandler language;
+
+ public ComponentFactory(ConfigHandler c, LanguageKeyHandler l) {
+ config = c;
+ language = l;
+ }
+
+ /** Returns [PlayerStats] followed by a single space. */
+ public TextComponent pluginPrefix(boolean isConsoleSender) {
+ return text("[")
+ .color(PluginColor.GRAY.getColor())
+ .append(text("PlayerStats").color(PluginColor.DARK_GOLD.getColor()))
+ .append(text("]"))
+ .append(space());
+ }
+
+ /** Returns [PlayerStats] surrounded by underscores on both sides. */
+ public TextComponent prefixTitle(boolean isConsoleSender) {
+ String underscores = "____________"; //12 underscores for both console and in-game
+ TextColor underscoreColor = PluginColor.DARK_PURPLE.getColor();
+
+ if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
+ underscoreColor = NamedTextColor.DARK_PURPLE;
+ }
+
+ return text(underscores).color(underscoreColor)
+ .append(text(" ")) //4 spaces
+ .append(pluginPrefix(isConsoleSender))
+ .append(text(" ")) //3 spaces (since prefix already has one)
+ .append(text(underscores));
+ }
+
+ /** Returns a TextComponent with the input String as content, with color Gray and decoration Italic.*/
+ public TextComponent subTitle(String content) {
+ return text(content).color(PluginColor.GRAY.getColor()).decorate(TextDecoration.ITALIC);
+ }
+
+ /** Returns a TextComponents that represents a full message, with [PlayerStats] prepended. */
+ public TextComponent msg(String msg, boolean isConsoleSender) {
+ return pluginPrefix(isConsoleSender)
+ .append(text(msg)
+ .color(PluginColor.MEDIUM_BLUE.getColor()));
+ }
+
+ /** Returns a plain TextComponent that represents a single message line.
+ Each message part has its own designated color.
+ @param part1 color DARK_GOLD
+ @param part2 color MEDIUM_GOLD
+ @param part3 color YELLOW
+ @param part4 color GRAY
+ */
+ public TextComponent msgPart(@Nullable String part1, @Nullable String part2, @Nullable String part3, @Nullable String part4) {
+ TextComponent.Builder msg = Component.text();
+ if (part1 != null) {
+ msg.append(text(part1)
+ .color(PluginColor.DARK_GOLD.getColor()));
+ if (part2 != null || part3 != null || part4 != null) {
+ msg.append(space());
+ }
+ }
+ if (part2 != null) {
+ msg.append(text(part2)
+ .color(PluginColor.MEDIUM_GOLD.getColor()));
+ if (part3 != null || part4 != null) {
+ msg.append(space());
+ }
+ }
+ if (part3 != null) {
+ msg.append(text(part3)
+ .color(PluginColor.YELLOW.getColor()));
+ if (part4 != null) {
+ msg.append(space());
+ }
+ }
+ if (part4 != null) {
+ msg.append(text(part4)
+ .color(PluginColor.GRAY.getColor()));
+ }
+ return msg.build();
+ }
+
+ /** Returns a TextComponent with hover-text that can consist of three different parts,
+ divided over two different lines. Each part has its own designated color. If all the
+ input Strings are null, it will return an empty Component.
+ @param plainText the non-hovering part
+ @param color the color for the non-hovering part
+ @param hoverLineOne text on the first line, with color LIGHT_BLUE
+ @param hoverLineTwoA text on the second line, with color DARK_GOLD
+ @param hoverLineTwoB text on the second part of the second line, with color MEDIUM_GOLD
+ */
+ public TextComponent hoverMsgPart(@NotNull String plainText, @NotNull PluginColor color, @Nullable String hoverLineOne, @Nullable String hoverLineTwoA, @Nullable String hoverLineTwoB) {
+ TextComponent base = Component.text(plainText).color(color.getColor());
+ TextComponent.Builder hoverText = Component.text();
+ if (hoverLineOne != null) {
+ hoverText.append(text(hoverLineOne)
+ .color(PluginColor.LIGHT_BLUE.getColor()));
+ if (hoverLineTwoA != null || hoverLineTwoB != null) {
+ hoverText.append(newline());
+ }
+ }
+ if (hoverLineTwoA != null) {
+ hoverText.append(text(hoverLineTwoA)
+ .color(PluginColor.DARK_GOLD.getColor()));
+ if (hoverLineTwoB != null) {
+ hoverText.append(space());
+ }
+ }
+ if (hoverLineTwoB != null) {
+ hoverText.append(text(hoverLineTwoB).color(PluginColor.MEDIUM_GOLD.getColor()));
+ }
+ return base.hoverEvent(HoverEvent.showText(hoverText.build()));
+ }
+
+
+ public TextComponent playerName(Target selection, String playerName) {
+ return createComponent(playerName,
+ getColorFromString(config.getPlayerNameFormatting(selection, false)),
+ getStyleFromString(config.getPlayerNameFormatting(selection, true)));
+ }
+
+ public TranslatableComponent statName(@NotNull StatRequest request) {
+ String statName = request.getStatistic().name();
+ String subStatName = request.getSubStatEntry();
+ if (!config.useTranslatableComponents()) {
+ statName = getPrettyName(statName);
+ subStatName = getPrettyName(subStatName);
+ }
+ else {
+ statName = language.getStatKey(request.getStatistic());
+ switch (request.getStatistic().getType()) {
+ case BLOCK -> subStatName = language.getBlockKey(request.getBlock());
+ case ENTITY -> subStatName = language.getEntityKey(request.getEntity());
+ case ITEM -> subStatName = language.getItemKey(request.getItem());
+ case UNTYPED -> {
+ }
+ }
+ }
+ return statName(request.getSelection(), statName, subStatName);
+ }
+
+ private TranslatableComponent statName(@NotNull Target selection, @NotNull String statKey, @Nullable String subStatKey) {
+ TranslatableComponent.Builder totalName;
+ TextComponent subStat = subStatName(selection, subStatKey);
+ TextColor statNameColor = getColorFromString(config.getStatNameFormatting(selection, false));
+ TextDecoration statNameStyle = getStyleFromString(config.getStatNameFormatting(selection, true));
+
+ if (statKey.equalsIgnoreCase("stat_type.minecraft.killed") && subStat != null) {
+ totalName = killEntity(subStat);
+ }
+ else if (statKey.equalsIgnoreCase("stat_type.minecraft.killed_by") && subStat != null) {
+ totalName = entityKilledBy(subStat);
+ }
+ else {
+ totalName = translatable().key(statKey);
+ if (subStat != null) totalName.append(space()).append(subStat);
+ }
+
+ if (statNameStyle != null) totalName.decoration(statNameStyle, TextDecoration.State.TRUE);
+ return totalName
+ .color(statNameColor)
+ .build();
+ }
+
+ private @Nullable TextComponent subStatName(Target selection, @Nullable String subStatName) {
+ if (subStatName != null) {
+ TextDecoration style = getStyleFromString(config.getSubStatNameFormatting(selection, true));
+ TextComponent.Builder subStat = text()
+ .append(text("("))
+ .append(translatable()
+ .key(subStatName))
+ .append(text(")"))
+ .color(getColorFromString(config.getSubStatNameFormatting(selection, false)));
+
+ subStat.decorations(TextDecoration.NAMES.values(), false);
+ if (style != null) subStat.decoration(style, TextDecoration.State.TRUE);
+ return subStat.build();
+ }
+ else {
+ return null;
+ }
+ }
+
+ /** Construct a custom translation for kill_entity with the language key for commands.kill.success.single ("Killed %s").
+ @return a TranslatableComponent Builder with the subStat Component as args.*/
+ private TranslatableComponent.Builder killEntity(@NotNull TextComponent subStat) {
+ return translatable()
+ .key("commands.kill.success.single") //"Killed %s"
+ .args(subStat);
+ }
+
+ /** Construct a custom translation for entity_killed_by with the language keys for stat.minecraft.deaths
+ ("Number of Deaths") and book.byAuthor ("by %s").
+ @return a TranslatableComponent Builder with stat.minecraft.deaths as key, with a ChildComponent
+ with book.byAuthor as key and the subStat Component as args.*/
+ private TranslatableComponent.Builder entityKilledBy(@NotNull TextComponent subStat) {
+ return translatable()
+ .key("stat.minecraft.deaths") //"Number of Deaths"
+ .append(space())
+ .append(translatable()
+ .key("book.byAuthor") //"by %s"
+ .args(subStat));
+ }
+
+ public TextComponent statNumber(Target selection, long number) {
+ return createComponent(NumberFormatter.format(number),
+ getColorFromString(config.getStatNumberFormatting(selection, false)),
+ getStyleFromString(config.getStatNumberFormatting(selection, true)));
+ }
+
+ public TextComponent title(Target selection, String content) {
+ return createComponent(content,
+ getColorFromString(config.getTitleFormatting(selection, false)),
+ getStyleFromString(config.getTitleFormatting(selection, true)));
+ }
+
+ public TextComponent titleNumber(int number) {
+ return createComponent(number + "",
+ getColorFromString(config.getTitleNumberFormatting(false)),
+ getStyleFromString(config.getTitleNumberFormatting(true)));
+ }
+
+ public TextComponent serverName() {
+ TextComponent colon = text(":").color(getColorFromString(config.getServerNameFormatting(false)));
+ return createComponent(config.getServerName(),
+ getColorFromString(config.getServerNameFormatting(false)),
+ getStyleFromString(config.getServerNameFormatting(true)))
+ .append(colon);
+ }
+
+ public TextComponent rankingNumber(String number) {
+ return createComponent(number,
+ getColorFromString(config.getRankNumberFormatting(false)),
+ getStyleFromString(config.getRankNumberFormatting(true)));
+ }
+
+ public TextComponent dots(String dots) {
+ return createComponent(dots,
+ getColorFromString(config.getDotsFormatting(false)),
+ getStyleFromString(config.getDotsFormatting(true)));
+ }
+
+ private TextComponent createComponent(String content, TextColor color, @Nullable TextDecoration style) {
+ return style == null ? text(content).color(color) : text(content).color(color).decoration(style, TextDecoration.State.TRUE);
+ }
+
+ /** Replace "_" with " " and capitalize each first letter of the input.
+ @param input String to prettify, case-insensitive*/
+ private String getPrettyName(String input) {
+ if (input == null) return null;
+ StringBuilder capitals = new StringBuilder(input.toLowerCase());
+ capitals.setCharAt(0, Character.toUpperCase(capitals.charAt(0)));
+ while (capitals.indexOf("_") != -1) {
+ MyLogger.replacingUnderscores();
+
+ int index = capitals.indexOf("_");
+ capitals.setCharAt(index + 1, Character.toUpperCase(capitals.charAt(index + 1)));
+ capitals.setCharAt(index, ' ');
+ }
+ return capitals.toString();
+ }
+
+ private TextColor getColorFromString(String configString) {
+ if (configString != null) {
+ try {
+ if (configString.contains("#")) {
+ return TextColor.fromHexString(configString);
+ }
+ else {
+ return getTextColorByName(configString);
+ }
+ }
+ catch (IllegalArgumentException | NullPointerException exception) {
+ Bukkit.getLogger().warning(exception.toString());
+ }
+ }
+ return null;
+ }
+
+ private TextColor getTextColorByName(String textColor) {
+ Index names = NamedTextColor.NAMES;
+ return names.value(textColor);
+ }
+
+ private @Nullable TextDecoration getStyleFromString(@NotNull String configString) {
+ if (configString.equalsIgnoreCase("none")) {
+ return null;
+ }
+ else if (configString.equalsIgnoreCase("magic")) {
+ return TextDecoration.OBFUSCATED;
+ }
+ else {
+ Index styles = TextDecoration.NAMES;
+ return styles.value(configString);
+ }
+ }
+
+}
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageFactory.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageFactory.java
deleted file mode 100644
index 19ae412..0000000
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageFactory.java
+++ /dev/null
@@ -1,556 +0,0 @@
-package com.gmail.artemis.the.gr8.playerstats.msg;
-
-import com.gmail.artemis.the.gr8.playerstats.enums.Target;
-import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
-import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
-import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
-import com.gmail.artemis.the.gr8.playerstats.utils.NumberFormatter;
-import net.kyori.adventure.text.Component;
-import net.kyori.adventure.text.TextComponent;
-import net.kyori.adventure.text.TranslatableComponent;
-import net.kyori.adventure.text.event.HoverEvent;
-import net.kyori.adventure.text.format.NamedTextColor;
-import net.kyori.adventure.text.format.TextColor;
-import net.kyori.adventure.text.format.TextDecoration;
-import net.kyori.adventure.util.Index;
-import org.bukkit.Bukkit;
-import org.bukkit.Statistic;
-import org.bukkit.map.MinecraftFont;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-import java.util.*;
-
-import static net.kyori.adventure.text.Component.*;
-
-public class MessageFactory {
-
- private static ConfigHandler config;
- private final LanguageKeyHandler language;
-
- private final TextColor msgColor; //my favorite shade of light blue, somewhere between blue and aqua
- private final TextColor hoverBaseColor; //light blue - one shade lighter than msgColor
- private final TextColor accentColor1; //gold - one shade lighter than standard gold
- private final TextColor accentColor2; //yellow - a few shades darker than standard yellow
-
- public MessageFactory(ConfigHandler c, LanguageKeyHandler l) {
- config = c;
- language = l;
-
- msgColor = TextColor.fromHexString("#55AAFF");
- hoverBaseColor = TextColor.fromHexString("#55C6FF");
- accentColor1 = TextColor.fromHexString("#FFB80E");
- accentColor2 = TextColor.fromHexString("#FFD52B");
- }
-
- protected TextComponent pluginPrefix(boolean isConsoleSender) {
- return text("[")
- .color(NamedTextColor.GRAY)
- .append(text("PlayerStats").color(NamedTextColor.GOLD))
- .append(text("]"))
- .append(space());
- }
-
- public TextComponent reloadedConfig(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("Config reloaded!")
- .color(msgColor));
- }
-
- public TextComponent stillReloading(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("The plugin is still (re)loading, your request will be processed when it is done!")
- .color(msgColor));
- }
-
- public TextComponent partiallyReloaded(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("The reload process was interrupted. If you notice unexpected behavior, please reload PlayerStats again to fix it!")
- .color(msgColor));
- }
-
- public TextComponent waitAMoment(boolean longWait, boolean isConsoleSender) {
- return longWait ? pluginPrefix(isConsoleSender)
- .append(text("Calculating statistics, this may take a minute...")
- .color(msgColor))
- : pluginPrefix(isConsoleSender)
- .append(text("Calculating statistics, this may take a few moments...")
- .color(msgColor));
- }
-
- public TextComponent formatExceptions(@NotNull String exception, boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text(exception)
- .color(msgColor));
- }
-
- public TextComponent missingStatName(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("Please provide a valid statistic name!")
- .color(msgColor));
- }
-
- public TextComponent missingSubStatName(Statistic.Type statType, boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("Please add a valid ")
- .append(text(getSubStatTypeName(statType)))
- .append(text(" to look up this statistic!"))
- .color(msgColor));
- }
-
- public TextComponent missingPlayerName(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("Please specify a valid player-name!")
- .color(msgColor));
- }
-
- public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry, boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("\"")
- .append(text(subStatEntry))
- .append(text("\""))
- .append(text(" is not a valid "))
- .append(text(getSubStatTypeName(statType)))
- .append(text("!"))
- .color(msgColor));
- }
-
- public TextComponent unknownError(boolean isConsoleSender) {
- return pluginPrefix(isConsoleSender)
- .append(text("Something went wrong with your request, please try again or see /statistic for a usage explanation!")
- .color(msgColor));
- }
-
- public TextComponent helpMsg(boolean isConsoleSender) {
- if (!isConsoleSender) {
- return config.useHoverText() ? helpMsgHover() : helpMsgPlain(false);
- }
- else {
- return helpMsgPlain(true);
- }
- }
-
- public TextComponent usageExamples(boolean isConsoleSender) {
- TextComponent spaces = text(" "); //4 spaces
- TextComponent arrow = text("→ ").color(NamedTextColor.GOLD);
- TextColor accentColor = TextColor.fromHexString("#FFE339");
-
- if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
- arrow = text("-> ").color(NamedTextColor.GOLD);
- accentColor = NamedTextColor.YELLOW;
- }
-
- return Component.newline()
- .append(getPrefixAsTitle(isConsoleSender))
- .append(newline())
- .append(text("Examples: ").color(NamedTextColor.GOLD))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("/statistic animals_bred top").color(accentColor))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("/statistic mine_block diorite me").color(accentColor))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("/statistic deaths player Artemis_the_gr8").color(accentColor))
- .append(newline());
- }
-
- public TextComponent formatPlayerStat(int stat, @NotNull StatRequest request) {
- if (!request.isValid()) return unknownError(request.isConsoleSender());
- return Component.text()
- .append(playerNameComponent(Target.PLAYER, request.getPlayerName() + ": "))
- .append(statNumberComponent(Target.PLAYER, stat))
- .append(space())
- .append(statNameComponent(request))
- .append(space())
- .build();
- }
-
- public TextComponent formatTopStats(@NotNull LinkedHashMap topStats, @NotNull StatRequest request) {
- if (!request.isValid()) return unknownError(request.isConsoleSender());
-
- TextComponent.Builder topList = Component.text();
- topList.append(getTopStatTitle(topStats.size(), request));
-
- boolean useDots = config.useDots();
- Set playerNames = topStats.keySet();
- MinecraftFont font = new MinecraftFont();
-
- int count = 0;
- for (String playerName : playerNames) {
- count = count+1;
-
- topList.append(newline())
- .append(rankingNumberComponent(count + ". "))
- .append(playerNameComponent(Target.TOP, playerName));
-
- if (useDots) {
- topList.append(space());
-
- int dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/2);
- if (request.isConsoleSender()) {
- dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/6) + 7;
- }
- else if (config.playerNameIsBold()) {
- dots = (int) Math.round((130.0 - font.getWidth(count + ". ") - (font.getWidth(playerName) * 1.19))/2);
- }
- if (dots >= 1) {
- topList.append(dotsComponent(".".repeat(dots)));
- }
- }
- else {
- topList.append(playerNameComponent(Target.TOP, ":"));
- }
- topList.append(space()).append(statNumberComponent(Target.TOP, topStats.get(playerName)));
- }
- return topList.build();
- }
-
- public TextComponent formatServerStat(long stat, @NotNull StatRequest request) {
- if (!request.isValid()) return unknownError(request.isConsoleSender());
- return Component.text()
- .append(titleComponent(Target.SERVER, config.getServerTitle()))
- .append(space())
- .append(serverNameComponent())
- .append(space())
- .append(statNumberComponent(Target.SERVER, stat))
- .append(space())
- .append(statNameComponent(request))
- .append(space())
- .build();
- }
-
- protected TextComponent getPrefixAsTitle(boolean isConsoleSender) {
- String underscores = "____________"; //12 underscores for both console and in-game
- TextColor underscoreColor = TextColor.fromHexString("#6E3485"); //a dark shade of purple
-
- if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
- underscoreColor = NamedTextColor.DARK_PURPLE;
- }
-
- return text(underscores).color(underscoreColor)
- .append(text(" ")) //4 spaces
- .append(pluginPrefix(isConsoleSender))
- .append(text(" ")) //3 spaces (since prefix already has one)
- .append(text(underscores));
- }
-
- protected TextComponent getTopStatTitle(int topLength, @NotNull StatRequest request) {
- return Component.text()
- .append(newline())
- .append(pluginPrefix(request.isConsoleSender()))
- .append(titleComponent(Target.TOP, config.getTopStatsTitle()))
- .append(space())
- .append(titleNumberComponent(topLength))
- .append(space())
- .append(statNameComponent(request))
- .append(space())
- .build();
- }
-
- protected TextComponent playerNameComponent(Target selection, String playerName) {
- return getComponent(playerName,
- getColorFromString(config.getPlayerNameFormatting(selection, false)),
- getStyleFromString(config.getPlayerNameFormatting(selection, true)));
- }
-
- protected TranslatableComponent statNameComponent(@NotNull StatRequest request) {
- if (request.getStatistic() == null) return null;
- TextColor statNameColor = getColorFromString(config.getStatNameFormatting(request.getSelection(), false));
- TextDecoration statNameStyle = getStyleFromString(config.getStatNameFormatting(request.getSelection(), true));
-
- String statName = request.getStatistic().name();
- String subStatName = request.getSubStatEntry();
- if (!config.useTranslatableComponents()) {
- statName = getPrettyName(statName);
- subStatName = getPrettyName(subStatName);
- }
- else {
- statName = language.getStatKey(request.getStatistic());
- switch (request.getStatistic().getType()) {
- case BLOCK -> subStatName = language.getBlockKey(request.getBlock());
- case ENTITY -> subStatName = language.getEntityKey(request.getEntity());
- case ITEM -> subStatName = language.getItemKey(request.getItem());
- case UNTYPED -> {
- }
- }
- }
- TextComponent subStat = subStatNameComponent(request.getSelection(), subStatName);
- TranslatableComponent.Builder totalName;
-
- if (statName.equalsIgnoreCase("stat_type.minecraft.killed") && subStat != null) {
- totalName = killEntityComponent(subStat);
- }
- else if (statName.equalsIgnoreCase("stat_type.minecraft.killed_by") && subStat != null) {
- totalName = entityKilledByComponent(subStat);
- }
- else {
- totalName = translatable().key(statName);
- if (subStat != null) totalName.append(space()).append(subStat);
- }
-
- if (statNameStyle != null) totalName.decoration(statNameStyle, TextDecoration.State.TRUE);
- return totalName
- .color(statNameColor)
- .build();
- }
-
- protected @Nullable TextComponent subStatNameComponent(Target selection, @Nullable String subStatName) {
- if (subStatName != null) {
- TextDecoration style = getStyleFromString(config.getSubStatNameFormatting(selection, true));
- TextComponent.Builder subStat = text()
- .append(text("("))
- .append(translatable()
- .key(subStatName))
- .append(text(")"))
- .color(getColorFromString(config.getSubStatNameFormatting(selection, false)));
-
- subStat.decorations(TextDecoration.NAMES.values(), false);
- if (style != null) subStat.decoration(style, TextDecoration.State.TRUE);
- return subStat.build();
- }
- else {
- return null;
- }
- }
-
- /** Construct a custom translation for kill_entity with the language key for commands.kill.success.single ("Killed %s").
- @return a TranslatableComponent Builder with the subStat Component as args.*/
- private TranslatableComponent.Builder killEntityComponent(@NotNull TextComponent subStat) {
- return translatable()
- .key("commands.kill.success.single") //"Killed %s"
- .args(subStat);
- }
-
- /** Construct a custom translation for entity_killed_by with the language keys for stat.minecraft.deaths
- ("Number of Deaths") and book.byAuthor ("by %s").
- @return a TranslatableComponent Builder with stat.minecraft.deaths as key, with a ChildComponent
- with book.byAuthor as key and the subStat Component as args.*/
- private TranslatableComponent.Builder entityKilledByComponent(@NotNull TextComponent subStat) {
- return translatable()
- .key("stat.minecraft.deaths") //"Number of Deaths"
- .append(space())
- .append(translatable()
- .key("book.byAuthor") //"by %s"
- .args(subStat));
- }
-
- protected TextComponent statNumberComponent(Target selection, long number) {
- return getComponent(NumberFormatter.format(number),
- getColorFromString(config.getStatNumberFormatting(selection, false)),
- getStyleFromString(config.getStatNumberFormatting(selection, true)));
- }
-
- protected TextComponent titleComponent(Target selection, String content) {
- return getComponent(content,
- getColorFromString(config.getTitleFormatting(selection, false)),
- getStyleFromString(config.getTitleFormatting(selection, true)));
- }
-
- protected TextComponent titleNumberComponent(int number) {
- return getComponent(number + "",
- getColorFromString(config.getTitleNumberFormatting(false)),
- getStyleFromString(config.getTitleNumberFormatting(true)));
- }
-
- protected TextComponent serverNameComponent() {
- TextComponent colon = text(":").color(getColorFromString(config.getServerNameFormatting(false)));
- return getComponent(config.getServerName(),
- getColorFromString(config.getServerNameFormatting(false)),
- getStyleFromString(config.getServerNameFormatting(true)))
- .append(colon);
- }
-
- protected TextComponent rankingNumberComponent(String number) {
- return getComponent(number,
- getColorFromString(config.getRankNumberFormatting(false)),
- getStyleFromString(config.getRankNumberFormatting(true)));
- }
-
- protected TextComponent dotsComponent(String dots) {
- return getComponent(dots,
- getColorFromString(config.getDotsFormatting(false)),
- getStyleFromString(config.getDotsFormatting(true)));
- }
-
- private TextComponent getComponent(String content, TextColor color, @Nullable TextDecoration style) {
- return style == null ? text(content).color(color) : text(content).color(color).decoration(style, TextDecoration.State.TRUE);
- }
-
- /** Replace "_" with " " and capitalize each first letter of the input.
- @param input String to prettify, case-insensitive*/
- private String getPrettyName(String input) {
- if (input == null) return null;
- StringBuilder capitals = new StringBuilder(input.toLowerCase());
- capitals.setCharAt(0, Character.toUpperCase(capitals.charAt(0)));
- while (capitals.indexOf("_") != -1) {
- MyLogger.replacingUnderscores();
-
- int index = capitals.indexOf("_");
- capitals.setCharAt(index + 1, Character.toUpperCase(capitals.charAt(index + 1)));
- capitals.setCharAt(index, ' ');
- }
- return capitals.toString();
- }
-
- private TextColor getColorFromString(String configString) {
- if (configString != null) {
- try {
- if (configString.contains("#")) {
- return TextColor.fromHexString(configString);
- }
- else {
- return getTextColorByName(configString);
- }
- }
- catch (IllegalArgumentException | NullPointerException exception) {
- Bukkit.getLogger().warning(exception.toString());
- }
- }
- return null;
- }
-
- private TextColor getTextColorByName(String textColor) {
- Index names = NamedTextColor.NAMES;
- return names.value(textColor);
- }
-
- private @Nullable TextDecoration getStyleFromString(@NotNull String configString) {
- if (configString.equalsIgnoreCase("none")) {
- return null;
- }
- else if (configString.equalsIgnoreCase("magic")) {
- return TextDecoration.OBFUSCATED;
- }
- else {
- Index styles = TextDecoration.NAMES;
- return styles.value(configString);
- }
- }
-
- /** Returns "block", "entity", "item", or "sub-statistic" if the provided Type is null. */
- private String getSubStatTypeName(Statistic.Type statType) {
- String subStat = "sub-statistic";
- if (statType == null) return subStat;
- switch (statType) {
- case BLOCK -> subStat = "block";
- case ENTITY -> subStat = "entity";
- case ITEM -> subStat = "item";
- }
- return subStat;
- }
-
- /** Returns the usage-explanation with hovering text */
- private @NotNull TextComponent helpMsgHover() {
- TextComponent spaces = text(" "); //4 spaces
- TextComponent arrow = text("→ ").color(NamedTextColor.GOLD); //alt + 26
- TextColor arguments = NamedTextColor.YELLOW;
-
- return Component.newline()
- .append(getPrefixAsTitle(false))
- .append(newline())
- .append(text("Hover over the arguments for more information!").color(NamedTextColor.GRAY).decorate(TextDecoration.ITALIC))
- .append(newline())
- .append(text("Usage: ").color(NamedTextColor.GOLD)).append(text("/statistic").color(arguments))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("name").color(arguments)
- .hoverEvent(HoverEvent.showText(text("The name that describes the statistic").color(hoverBaseColor)
- .append(newline())
- .append(text("Example: ").color(accentColor1))
- .append(text("\"animals_bred\"").color(accentColor2)))))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("sub-statistic").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("Some statistics need an item, block or entity as extra input").color(hoverBaseColor)
- .append(newline())
- .append(text("Example: ").color(accentColor1)
- .append(text("\"mine_block diorite\"").color(accentColor2))))))
- .append(newline())
- .append(spaces)
- .append(text("→").color(NamedTextColor.GOLD)
- .hoverEvent(HoverEvent.showText(
- text("Choose one").color(TextColor.fromHexString("#6E3485")))))
- .append(space())
- .append(text("me").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("See your own statistic").color(hoverBaseColor))))
- .append(text(" | ").color(arguments))
- .append(text("player").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("Choose any player that has played on your server").color(hoverBaseColor))))
- .append(text(" | ").color(arguments))
- .append(text("server").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("See the combined total for everyone on your server").color(hoverBaseColor))))
- .append(text(" | ").color(arguments))
- .append(text("top").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("See the top ").color(hoverBaseColor)
- .append(text(config.getTopListMaxSize()).color(hoverBaseColor)))))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("player-name").color(arguments)
- .hoverEvent(HoverEvent.showText(
- text("In case you typed ").color(hoverBaseColor)
- .append(text("\"player\"").color(accentColor2)
- .append(text(", add the player's name").color(hoverBaseColor))))));
- }
-
- /** Returns the usage-explanation without any hovering text.
- If BukkitVersion is CraftBukkit, this doesn't use unicode symbols or hex colors */
- private @NotNull TextComponent helpMsgPlain(boolean isConsoleSender) {
- TextComponent spaces = text(" "); //4 spaces
- TextComponent arrow = text("→ ").color(NamedTextColor.GOLD); //alt + 26;
- TextComponent bullet = text("• ").color(NamedTextColor.GOLD); //alt + 7
- TextColor arguments = NamedTextColor.YELLOW;
- TextColor accentColor = accentColor2;
-
- if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
- arrow = text("-> ").color(NamedTextColor.GOLD);
- bullet = text("* ").color(NamedTextColor.GOLD);
- accentColor = NamedTextColor.GOLD;
- }
-
- return Component.newline()
- .append(getPrefixAsTitle(isConsoleSender))
- .append(newline())
- .append(text("Type \"/statistic examples\" to see examples!").color(NamedTextColor.GRAY).decorate(TextDecoration.ITALIC))
- .append(newline())
- .append(text("Usage: ").color(NamedTextColor.GOLD))
- .append(text("/statistic").color(arguments))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("name").color(arguments))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("{sub-statistic}").color(arguments))
- .append(space())
- .append(text("(a block, item or entity)").color(NamedTextColor.GRAY))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("me | player | server | top").color(arguments))
- .append(newline())
- .append(spaces).append(spaces).append(bullet)
- .append(text("me:").color(accentColor))
- .append(space()).append(text("your own statistic").color(NamedTextColor.GRAY))
- .append(newline())
- .append(spaces).append(spaces).append(bullet)
- .append(text("player:").color(accentColor))
- .append(space()).append(text("choose a player").color(NamedTextColor.GRAY))
- .append(newline())
- .append(spaces).append(spaces).append(bullet)
- .append(text("server:").color(accentColor))
- .append(space()).append(text("everyone on the server combined").color(NamedTextColor.GRAY))
- .append(newline())
- .append(spaces).append(spaces).append(bullet)
- .append(text("top:").color(accentColor))
- .append(space()).append(text("the top").color(NamedTextColor.GRAY)
- .append(space()).append(text(config.getTopListMaxSize())))
- .append(newline())
- .append(spaces).append(arrow)
- .append(text("{player-name}").color(arguments));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java
new file mode 100644
index 0000000..f2893e2
--- /dev/null
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/MessageWriter.java
@@ -0,0 +1,308 @@
+package com.gmail.artemis.the.gr8.playerstats.msg;
+
+import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
+import com.gmail.artemis.the.gr8.playerstats.enums.Target;
+import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
+import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.TextComponent;
+import net.kyori.adventure.text.event.HoverEvent;
+import net.kyori.adventure.text.format.NamedTextColor;
+import net.kyori.adventure.text.format.TextColor;
+import org.bukkit.Bukkit;
+import org.bukkit.Statistic;
+import org.bukkit.map.MinecraftFont;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.*;
+
+import static net.kyori.adventure.text.Component.*;
+
+/** Composes messages to send to Players or Console. This class is responsible
+ for constructing a final component with the text content of the desired message.
+ The component parts (with appropriate formatting) are supplied by a ComponentFactory.*/
+public class MessageWriter {
+
+ private static ConfigHandler config;
+ private static ComponentFactory componentFactory;
+
+ private final TextColor hoverBaseColor; //light blue - one shade lighter than msgColor
+ private final TextColor accentColor1; //gold - one shade lighter than standard gold
+ private final TextColor accentColor2; //yellow - a few shades darker than standard yellow
+
+ public MessageWriter(ConfigHandler c, LanguageKeyHandler l) {
+ config = c;
+ componentFactory = new ComponentFactory(c, l);
+
+ hoverBaseColor = TextColor.fromHexString("#55C6FF");
+ accentColor1 = TextColor.fromHexString("#FFB80E");
+ accentColor2 = TextColor.fromHexString("#FFD52B");
+ }
+
+ public TextComponent reloadedConfig(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "Config reloaded!", isConsoleSender);
+ }
+
+ public TextComponent stillReloading(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "The plugin is still (re)loading, " +
+ "your request will be processed when it is done!", isConsoleSender);
+ }
+
+ public TextComponent partiallyReloaded(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "The reload process was interrupted. " +
+ "If you notice unexpected behavior, " +
+ "please reload PlayerStats again to fix it!", isConsoleSender);
+ }
+
+ public TextComponent waitAMoment(boolean longWait, boolean isConsoleSender) {
+ String msg = longWait ? "Calculating statistics, this may take a minute..." :
+ "Calculating statistics, this may take a few moments...";
+ return componentFactory.msg(msg, isConsoleSender);
+ }
+
+ public TextComponent formatExceptions(@NotNull String exception, boolean isConsoleSender) {
+ return componentFactory.msg(exception, isConsoleSender);
+ }
+
+ public TextComponent missingStatName(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "Please provide a valid statistic name!", isConsoleSender);
+ }
+
+ public TextComponent missingSubStatName(Statistic.Type statType, boolean isConsoleSender) {
+ return componentFactory.msg(
+ "Please add a valid " +
+ getSubStatTypeName(statType) +
+ " to look up this statistic!", isConsoleSender);
+ }
+
+ public TextComponent missingPlayerName(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "Please specify a valid player-name!", isConsoleSender);
+ }
+
+ public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry, boolean isConsoleSender) {
+ return componentFactory.msg(
+ "\"" + subStatEntry + "\" is not a valid " + getSubStatTypeName(statType) + "!", isConsoleSender);
+ }
+
+ public TextComponent unknownError(boolean isConsoleSender) {
+ return componentFactory.msg(
+ "Something went wrong with your request, " +
+ "please try again or see /statistic for a usage explanation!", isConsoleSender);
+ }
+
+
+ public TextComponent formatPlayerStat(int stat, @NotNull StatRequest request) {
+ if (!request.isValid()) return unknownError(request.isConsoleSender());
+ return Component.text()
+ .append(componentFactory.playerName(Target.PLAYER, request.getPlayerName() + ": "))
+ .append(componentFactory.statNumber(Target.PLAYER, stat))
+ .append(space())
+ .append(componentFactory.statName(request))
+ .append(space())
+ .build();
+ }
+
+ public TextComponent formatTopStats(@NotNull LinkedHashMap topStats, @NotNull StatRequest request) {
+ if (!request.isValid()) return unknownError(request.isConsoleSender());
+
+ TextComponent.Builder topList = Component.text()
+ .append(newline())
+ .append(componentFactory.pluginPrefix(request.isConsoleSender()))
+ .append(componentFactory.title(Target.TOP, config.getTopStatsTitle()))
+ .append(space())
+ .append(componentFactory.titleNumber(topStats.size()))
+ .append(space())
+ .append(componentFactory.statName(request));
+
+ boolean useDots = config.useDots();
+ boolean boldNames = config.playerNameIsBold();
+
+ Set playerNames = topStats.keySet();
+ MinecraftFont font = new MinecraftFont();
+
+ int count = 0;
+ for (String playerName : playerNames) {
+ count = count+1;
+
+ topList.append(newline())
+ .append(componentFactory.rankingNumber(count + ". "))
+ .append(componentFactory.playerName(Target.TOP, playerName));
+
+ if (useDots) {
+ topList.append(space());
+
+ int dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/2);
+ if (request.isConsoleSender()) {
+ dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/6) + 7;
+ }
+ else if (boldNames) {
+ dots = (int) Math.round((130.0 - font.getWidth(count + ". ") - (font.getWidth(playerName) * 1.19))/2);
+ }
+ if (dots >= 1) {
+ topList.append(componentFactory.dots(".".repeat(dots)));
+ }
+ }
+ else {
+ topList.append(componentFactory.playerName(Target.TOP, ":"));
+ }
+ topList.append(space()).append(componentFactory.statNumber(Target.TOP, topStats.get(playerName)));
+ }
+ return topList.build();
+ }
+
+ public TextComponent formatServerStat(long stat, @NotNull StatRequest request) {
+ if (!request.isValid()) return unknownError(request.isConsoleSender());
+ return Component.text()
+ .append(componentFactory.title(Target.SERVER, config.getServerTitle()))
+ .append(space())
+ .append(componentFactory.serverName())
+ .append(space())
+ .append(componentFactory.statNumber(Target.SERVER, stat))
+ .append(space())
+ .append(componentFactory.statName(request))
+ .append(space())
+ .build();
+ }
+
+ /** Returns "block", "entity", "item", or "sub-statistic" if the provided Type is null. */
+ private String getSubStatTypeName(Statistic.Type statType) {
+ String subStat = "sub-statistic";
+ if (statType == null) return subStat;
+ switch (statType) {
+ case BLOCK -> subStat = "block";
+ case ENTITY -> subStat = "entity";
+ case ITEM -> subStat = "item";
+ }
+ return subStat;
+ }
+
+ public TextComponent usageExamples(boolean isConsoleSender) {
+ TextComponent spaces = text(" "); //4 spaces
+ TextComponent arrow = text("→ ").color(NamedTextColor.GOLD);
+ TextColor accentColor = TextColor.fromHexString("#FFE339");
+
+ if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
+ arrow = text("-> ").color(NamedTextColor.GOLD);
+ accentColor = NamedTextColor.YELLOW;
+ }
+
+ return Component.newline()
+ .append(componentFactory.prefixTitle(isConsoleSender))
+ .append(newline())
+ .append(text("Examples: ").color(NamedTextColor.GOLD))
+ .append(newline())
+ .append(spaces).append(arrow)
+ .append(text("/statistic animals_bred top").color(accentColor))
+ .append(newline())
+ .append(spaces).append(arrow)
+ .append(text("/statistic mine_block diorite me").color(accentColor))
+ .append(newline())
+ .append(spaces).append(arrow)
+ .append(text("/statistic deaths player Artemis_the_gr8").color(accentColor))
+ .append(newline());
+ }
+
+ public TextComponent helpMsg(boolean isConsoleSender) {
+ if (isConsoleSender || !config.useHoverText()) {
+ return helpMsgPlain(isConsoleSender);
+ }
+ else {
+ return helpMsgHover();
+ }
+ }
+
+ /** Returns the usage-explanation with hovering text */
+ private TextComponent helpMsgHover() {
+ String arrow = " → "; //4 spaces, alt + 26, 1 space
+
+ return Component.newline()
+ .append(componentFactory.prefixTitle(false))
+ .append(newline())
+ .append(componentFactory.subTitle("Hover over the arguments for more information!"))
+ .append(newline())
+ .append(componentFactory.msgPart("Usage:", null, "/statistic", null))
+ .append(newline())
+ .append(componentFactory.msgPart(arrow, null, null, null)
+ .append(componentFactory.hoverMsgPart("name", PluginColor.YELLOW,
+ "The name that describes the statistic",
+ "Example:",
+ "\"animals_bred\"")))
+ .append(newline())
+ .append(spaces).append(arrow)
+ .append(text("sub-statistic").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("Some statistics need an item, block or entity as extra input").color(hoverBaseColor)
+ .append(newline())
+ .append(text("Example: ").color(accentColor1)
+ .append(text("\"mine_block diorite\"").color(accentColor2))))))
+ .append(newline())
+ .append(spaces)
+ .append(text("→").color(NamedTextColor.GOLD)
+ .hoverEvent(HoverEvent.showText(
+ text("Choose one").color(TextColor.fromHexString("#6E3485")))))
+ .append(space())
+ .append(text("me").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("See your own statistic").color(hoverBaseColor))))
+ .append(text(" | ").color(arguments))
+ .append(text("player").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("Choose any player that has played on your server").color(hoverBaseColor))))
+ .append(text(" | ").color(arguments))
+ .append(text("server").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("See the combined total for everyone on your server").color(hoverBaseColor))))
+ .append(text(" | ").color(arguments))
+ .append(text("top").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("See the top ").color(hoverBaseColor)
+ .append(text(config.getTopListMaxSize()).color(hoverBaseColor)))))
+ .append(newline())
+ .append(spaces).append(arrow)
+ .append(text("player-name").color(arguments)
+ .hoverEvent(HoverEvent.showText(
+ text("In case you typed ").color(hoverBaseColor)
+ .append(text("\"player\"").color(accentColor2)
+ .append(text(", add the player's name").color(hoverBaseColor))))));
+ }
+
+ //TODO create ConsoleComponentFactory for Bukkit
+
+ /** Returns the usage-explanation without any hovering text.
+ If BukkitVersion is CraftBukkit, this doesn't use unicode symbols or hex colors */
+ private TextComponent helpMsgPlain(boolean isConsoleSender) {
+ String arrow = " →"; //4 spaces, alt + 26
+ String bullet = " •"; //8 spaces, alt + 7
+ if (isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit")) {
+ arrow = " ->";
+ bullet = " *";
+ }
+ return Component.newline()
+ .append(componentFactory.prefixTitle(isConsoleSender))
+ .append(newline())
+ .append(componentFactory.subTitle("Type \"statistic examples\" to see examples!"))
+ .append(newline())
+ .append(componentFactory.msgPart("Usage:", null, "/statistic", null))
+ .append(newline())
+ .append(componentFactory.msgPart(arrow, null, "name", null))
+ .append(newline())
+ .append(componentFactory.msgPart(arrow, null, "{sub-statistic}", "(a block, item or entity)"))
+ .append(newline())
+ .append(componentFactory.msgPart(arrow, null, "me | player | server | top", null))
+ .append(newline())
+ .append(componentFactory.msgPart(bullet, "me:", null, "your own statistic"))
+ .append(newline())
+ .append(componentFactory.msgPart(bullet, "player:", null, "choose a player"))
+ .append(newline())
+ .append(componentFactory.msgPart(bullet, "server:", null, "everyone on the server combined"))
+ .append(newline())
+ .append(componentFactory.msgPart(bullet, "top:", null, "the top " + config.getTopListMaxSize()))
+ .append(newline())
+ .append(componentFactory.msgPart(arrow, null, "{player-name", null));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideMessageFactory.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideMessageFactory.java
index 3845ed8..2773bb3 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideMessageFactory.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/msg/PrideMessageFactory.java
@@ -12,7 +12,7 @@ import java.time.Month;
import static net.kyori.adventure.text.Component.*;
-public class PrideMessageFactory extends MessageFactory {
+public class PrideMessageFactory extends MessageWriter {
private static ConfigHandler config;
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java
index 80700be..f872567 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/reload/ReloadThread.java
@@ -3,8 +3,8 @@ package com.gmail.artemis.the.gr8.playerstats.reload;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
+import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
-import com.gmail.artemis.the.gr8.playerstats.msg.MessageFactory;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
@@ -29,19 +29,19 @@ public class ReloadThread extends Thread {
private final BukkitAudiences adventure;
private static ConfigHandler config;
- private static MessageFactory messageFactory;
+ private static MessageWriter messageWriter;
private final Main plugin;
private final StatThread statThread;
private final CommandSender sender;
- public ReloadThread(BukkitAudiences a, ConfigHandler c, MessageFactory m, Main p, int threshold, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
+ public ReloadThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p, int threshold, int ID, @Nullable StatThread s, @Nullable CommandSender se) {
this.threshold = threshold;
reloadThreadID = ID;
adventure = a;
config = c;
- messageFactory = m;
+ messageWriter = m;
plugin = p;
statThread = s;
@@ -76,13 +76,13 @@ public class ReloadThread extends Thread {
catch (ConcurrentModificationException e) {
MyLogger.logException(e, "ReloadThread", "run(), trying to update OfflinePlayerList during a reload");
if (sender != null) {
- adventure.sender(sender).sendMessage(messageFactory.partiallyReloaded(sender instanceof ConsoleCommandSender));
+ adventure.sender(sender).sendMessage(messageWriter.partiallyReloaded(sender instanceof ConsoleCommandSender));
}
}
MyLogger.logTimeTakenDefault("ReloadThread", ("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
if (sender != null) {
- adventure.sender(sender).sendMessage(messageFactory.reloadedConfig(sender instanceof ConsoleCommandSender));
+ adventure.sender(sender).sendMessage(messageWriter.reloadedConfig(sender instanceof ConsoleCommandSender));
}
}
}
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java
index 45e393e..b40f80d 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/statistic/StatThread.java
@@ -2,16 +2,15 @@ package com.gmail.artemis.the.gr8.playerstats.statistic;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
+import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
-import com.gmail.artemis.the.gr8.playerstats.msg.MessageFactory;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.OfflinePlayer;
-import org.bukkit.Statistic;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
@@ -31,11 +30,11 @@ public class StatThread extends Thread {
private final BukkitAudiences adventure;
private static ConfigHandler config;
- private static MessageFactory messageFactory;
+ private static MessageWriter messageWriter;
private final Main plugin;
//constructor (called on thread creation)
- public StatThread(BukkitAudiences a, ConfigHandler c, MessageFactory m, Main p, int ID, int threshold, StatRequest s, @Nullable ReloadThread r) {
+ public StatThread(BukkitAudiences a, ConfigHandler c, MessageWriter m, Main p, int ID, int threshold, StatRequest s, @Nullable ReloadThread r) {
this.threshold = threshold;
request = s;
@@ -43,7 +42,7 @@ public class StatThread extends Thread {
adventure = a;
config = c;
- messageFactory = m;
+ messageWriter = m;
plugin = p;
this.setName("StatThread-" + ID);
@@ -55,7 +54,7 @@ public class StatThread extends Thread {
public void run() throws IllegalStateException, NullPointerException {
MyLogger.threadStart(this.getName());
- if (messageFactory == null || plugin == null) {
+ if (messageWriter == null || plugin == null) {
throw new IllegalStateException("Not all classes off the plugin are running!");
}
if (request == null) {
@@ -65,7 +64,7 @@ public class StatThread extends Thread {
try {
MyLogger.waitingForOtherThread(this.getName(), reloadThread.getName());
adventure.sender(request.getCommandSender())
- .sendMessage(messageFactory
+ .sendMessage(messageWriter
.stillReloading(request.getCommandSender() instanceof ConsoleCommandSender));
reloadThread.join();
} catch (InterruptedException e) {
@@ -80,26 +79,26 @@ public class StatThread extends Thread {
if (selection == Target.TOP || selection == Target.SERVER) {
if (ThreadManager.getLastRecordedCalcTime() > 20000) {
- adventure.sender(sender).sendMessage(messageFactory.waitAMoment(true, isConsoleSencer));
+ adventure.sender(sender).sendMessage(messageWriter.waitAMoment(true, isConsoleSencer));
}
else if (ThreadManager.getLastRecordedCalcTime() > 2000) {
- adventure.sender(sender).sendMessage(messageFactory.waitAMoment(false, isConsoleSencer));
+ adventure.sender(sender).sendMessage(messageWriter.waitAMoment(false, isConsoleSencer));
}
try {
if (selection == Target.TOP) {
- adventure.sender(sender).sendMessage(messageFactory.formatTopStats(getTopStats(), request));
+ adventure.sender(sender).sendMessage(messageWriter.formatTopStats(getTopStats(), request));
}
else {
- adventure.sender(sender).sendMessage(messageFactory.formatServerStat(getServerTotal(), request));
+ adventure.sender(sender).sendMessage(messageWriter.formatServerStat(getServerTotal(), request));
}
} catch (ConcurrentModificationException e) {
if (!isConsoleSencer) {
- adventure.sender(sender).sendMessage(messageFactory.unknownError(false));
+ adventure.sender(sender).sendMessage(messageWriter.unknownError(false));
}
} catch (Exception e) {
- adventure.sender(sender).sendMessage(messageFactory.formatExceptions(e.toString(), isConsoleSencer));
+ adventure.sender(sender).sendMessage(messageWriter.formatExceptions(e.toString(), isConsoleSencer));
MyLogger.logException(e, "StatThread", "run(), trying to calculate or format a top or server statistic");
}
}
@@ -107,10 +106,10 @@ public class StatThread extends Thread {
else if (selection == Target.PLAYER) {
try {
adventure.sender(sender).sendMessage(
- messageFactory.formatPlayerStat(getIndividualStat(), request));
+ messageWriter.formatPlayerStat(getIndividualStat(), request));
} catch (UnsupportedOperationException | NullPointerException e) {
- adventure.sender(sender).sendMessage(messageFactory.formatExceptions(e.toString(), isConsoleSencer));
+ adventure.sender(sender).sendMessage(messageWriter.formatExceptions(e.toString(), isConsoleSencer));
}
}
}
diff --git a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/MyLogger.java b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/MyLogger.java
index d26417d..842b3cf 100644
--- a/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/MyLogger.java
+++ b/src/main/java/com/gmail/artemis/the/gr8/playerstats/utils/MyLogger.java
@@ -79,7 +79,7 @@ public class MyLogger {
}
}
- /** If DebugLevel is MEDIUM or HIGH, logs when the while loop in MessageFactory, getLanguageKey is being run. */
+ /** If DebugLevel is MEDIUM or HIGH, logs when the while loop in MessageWriter, getLanguageKey is being run. */
public static void replacingUnderscores() {
if (debugLevel != DebugLevel.LOW) {
logger.info("Replacing underscores and capitalizing names...");