Started splitting MessageFactory into a MessageWriter and ComponentFactory

This commit is contained in:
Artemis-the-gr8 2022-06-28 17:39:53 +02:00
parent 6ddc543bd0
commit 97b653a67a
11 changed files with 706 additions and 599 deletions

View File

@ -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");

View File

@ -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();
}

View File

@ -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;

View File

@ -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.
<p>GRAY: ChatColor Gray</p>
<p>DARK_PURPLE: #6E3485 (used for default sub-titles, title-underscores and brackets)</p>
<p>MEDIUM_BLUE: #55AAFF (used for all plain feedback and error messages)</p>
<p>LIGHT_BLUE: #55C6FF (used for default hover-text)</p>
<p>DARK_GOLD: ChatColor Gold (used for first parts of usage messages and for first parts of hover-text accent)</p>
<p>MEDIUM_GOLD: #FFB80E (used for second parts of usage messages and for second parts of hover-text accent) </p>
<p>YELLOW: ChatColor Yellow (used for third parts of usage messages)</p>
*/
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;
}
}

View File

@ -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<String, NamedTextColor> 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<String, TextDecoration> styles = TextDecoration.NAMES;
return styles.value(configString);
}
}
}

View File

@ -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<String, Integer> 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<String> 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<String, NamedTextColor> 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<String, TextDecoration> 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));
}
}

View File

@ -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<String, Integer> 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<String> 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));
}
}

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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));
}
}
}

View File

@ -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...");