Merge pull request #82 from itHotL/stat-formatting

Stat formatting
This commit is contained in:
Elise 2022-07-04 22:00:11 +02:00 committed by GitHub
commit 98d9f94544
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 1157 additions and 487 deletions

View File

@ -5,9 +5,7 @@ import com.gmail.artemis.the.gr8.playerstats.commands.StatCommand;
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.MessageWriter;
import com.gmail.artemis.the.gr8.playerstats.msg.PrideComponentFactory;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;

View File

@ -1,7 +1,6 @@
package com.gmail.artemis.the.gr8.playerstats.commands;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
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.utils.EnumHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
@ -9,7 +8,6 @@ import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
import com.gmail.artemis.the.gr8.playerstats.msg.MessageWriter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Statistic;
@ -22,8 +20,6 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static net.kyori.adventure.text.Component.text;
public class StatCommand implements CommandExecutor {
@ -47,16 +43,6 @@ public class StatCommand implements CommandExecutor {
args[0].equalsIgnoreCase("example")) { //in case of "statistic examples", show examples
adventure.sender(sender).sendMessage(messageWriter.usageExamples(isBukkitConsole));
}
else if (args[0].equalsIgnoreCase("test")) {
TextComponent msg = text("Tier 1").color(PluginColor.GOLD.getColor())
.append(text("Tier 2").color(PluginColor.MEDIUM_GOLD.getColor())
.append(text("Tier 3").color(TextColor.fromHexString("#FFEA40"))
.append(text("Tier 4").color(PluginColor.LIGHT_GOLD.getColor()))
.append(text("Tier 3?")))
.append(text("Tier 2?")))
.append(text("Tier 1?"));
adventure.sender(sender).sendMessage(msg);
}
else {
StatRequest request = generateRequest(sender, args);
TextComponent issues = checkRequest(request, isBukkitConsole);

View File

@ -15,7 +15,7 @@ public class TabCompleter implements org.bukkit.command.TabCompleter {
private final List<String> commandOptions;
//TODO add "example" to the list
public TabCompleter() {
commandOptions = new ArrayList<>();
commandOptions.add("top");

View File

@ -23,7 +23,7 @@ public class ConfigHandler {
saveDefaultConfig();
config = YamlConfiguration.loadConfiguration(configFile);
configVersion = 4;
configVersion = 5;
checkConfigVersion();
MyLogger.setDebugLevel(getDebugLevel());
@ -91,18 +91,60 @@ public class ConfigHandler {
return config.getInt("number-of-days-since-last-joined", 0);
}
/** Whether to use TranslatableComponents for statistic, block, item and entity names.
/** Whether to use TranslatableComponents wherever possible.
Currently supported: statistic, block, item and entity names.
<p>Default: true</p>*/
public boolean useTranslatableComponents() {
return config.getBoolean("translate-to-client-language", true);
}
/** Whether to use HoverComponents in the usage explanation.
/** Whether to use HoverComponents for additional information.
<p>Default: true</p>*/
public boolean useHoverText() {
return config.getBoolean("enable-hover-text", true);
}
public String getDistanceUnit(boolean isHoverText) {
return getUnitString(isHoverText, "blocks", "km", "distance-unit");
}
public String getDamageUnit(boolean isHoverText) {
return getUnitString(isHoverText, "hearts", "hp", "damage-unit");
}
public boolean autoDetectTimeUnit(boolean isHoverText) {
String path = "auto-detect-biggest-time-unit";
if (isHoverText) {
path = path + "-for-hover-text";
}
boolean defaultValue = !isHoverText;
return config.getBoolean(path, defaultValue);
}
public int getNumberOfExtraTimeUnits(boolean isHoverText) {
String path = "number-of-extra-units";
if (isHoverText) {
path = path + "-for-hover-text";
}
int defaultValue = isHoverText ? 0 : 1;
return config.getInt(path, defaultValue);
}
/** By default, getTimeUnit will return the maxUnit. If the optional minUnit flag is specified,
the minimum unit will be returned instead. */
public String getTimeUnit(boolean isHoverText) {
return getTimeUnit(isHoverText, false);
}
/** By default, getTimeUnit will return the maxUnit. If the optional smallUnit flag is specified,
the minimum unit will be returned instead. */
public String getTimeUnit(boolean isHoverText, boolean smallUnit) {
if (smallUnit) {
return getUnitString(isHoverText, "hours", "seconds", "smallest-time-unit");
}
return getUnitString(isHoverText, "days", "hours", "biggest-time-unit");
}
/** Whether to use festive formatting, such as pride colors.
<p>Default: true</p> */
public boolean useFestiveFormatting() {
@ -149,7 +191,7 @@ public class ConfigHandler {
<p>Style: "none"</p>
<p>Color Top: "green"</p>
<p>Color Individual/Server: "gold"</p>*/
public String getPlayerNameFormatting(Target selection, boolean isStyle) {
public String getPlayerNameDecoration(Target selection, boolean getStyle) {
String def;
if (selection == Target.TOP) {
def = "green";
@ -157,7 +199,7 @@ public class ConfigHandler {
else {
def = "gold";
}
return getStringFromConfig(selection, isStyle, def, "player-names");
return getDecorationString(selection, getStyle, def, "player-names");
}
/** Returns true if playerNames Style is "bold", false if it is not.
@ -175,22 +217,22 @@ public class ConfigHandler {
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
<p>Style: "none"</p>
<p>Color: "yellow"</p>*/
public String getStatNameFormatting(Target selection, boolean isStyle) {
return getStringFromConfig(selection, isStyle, "yellow", "stat-names");
public String getStatNameDecoration(Target selection, boolean getStyle) {
return getDecorationString(selection, getStyle, "yellow", "stat-names");
}
/** Returns a String that represents either a Chat Color, hex color code, or a Style. Default values are:
<p>Style: "none"</p>
<p>Color: "#FFD52B"</p>*/
public String getSubStatNameFormatting(Target selection, boolean isStyle) {
return getStringFromConfig(selection, isStyle, "#FFD52B", "sub-stat-names");
public String getSubStatNameDecoration(Target selection, boolean getStyle) {
return getDecorationString(selection, getStyle, "#FFD52B", "sub-stat-names");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color Top: "#55AAFF"</p>
<p>Color Individual/Server: "#ADE7FF"</p> */
public String getStatNumberFormatting(Target selection, boolean isStyle) {
public String getStatNumberDecoration(Target selection, boolean getStyle) {
String def;
if (selection == Target.TOP) {
def = "#55AAFF";
@ -198,14 +240,14 @@ public class ConfigHandler {
else {
def = "#ADE7FF";
}
return getStringFromConfig(selection, isStyle, def,"stat-numbers");
return getDecorationString(selection, getStyle, def,"stat-numbers");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color Top: "yellow"</p>
<p>Color Server: "gold"</p>*/
public String getTitleFormatting(Target selection, boolean isStyle) {
public String getTitleDecoration(Target selection, boolean getStyle) {
String def;
if (selection == Target.TOP) {
def = "yellow";
@ -213,41 +255,62 @@ public class ConfigHandler {
else {
def = "gold";
}
return getStringFromConfig(selection, isStyle, def, "title");
return getDecorationString(selection, getStyle, def, "title");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color: "gold"</p>*/
public String getTitleNumberFormatting(boolean isStyle) {
return getStringFromConfig(Target.TOP, isStyle, "gold", "title-number");
public String getTitleNumberDecoration(boolean getStyle) {
return getDecorationString(Target.TOP, getStyle, "gold", "title-number");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color: "#FFB80E"</p>*/
public String getServerNameFormatting(boolean isStyle) {
return getStringFromConfig(Target.SERVER, isStyle, "#FFB80E", "server-name");
public String getServerNameDecoration(boolean getStyle) {
return getDecorationString(Target.SERVER, getStyle, "#FFB80E", "server-name");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color: "gold"</p>*/
public String getRankNumberFormatting(boolean isStyle) {
return getStringFromConfig(Target.TOP, isStyle, "gold", "rank-numbers");
public String getRankNumberDecoration(boolean getStyle) {
return getDecorationString(Target.TOP, getStyle, "gold", "rank-numbers");
}
/** Returns a String that represents either a Chat Color, hex color code, or Style. Default values are:
<p>Style: "none"</p>
<p>Color: "dark_gray"</p> */
public String getDotsFormatting(boolean isStyle) {
return getStringFromConfig(Target.TOP, isStyle, "dark_gray", "dots");
public String getDotsDecoration(boolean getStyle) {
return getDecorationString(Target.TOP, getStyle, "dark_gray", "dots");
}
/** Returns the config value for a color or style option in string-format, the supplied default value, or null if no configSection was found. */
private @Nullable String getStringFromConfig(Target selection, boolean isStyle, String def, String pathName){
String path = isStyle ? pathName + "-style" : pathName;
String defaultValue = isStyle ? "none" : def;
/** Returns a String representing the Unit that should be used for a certain Unit.Type.
If no String can be retrieved from the config, the supplied defaultValue will be returned.
If the defaultValue is different for hoverText, an optional String defaultHoverValue can be supplied.
@param isHoverText if true, the unit for hovering text is returned, otherwise the unit for plain text
@param defaultValue the default unit for plain text
@param defaultHoverValue the default unit for hovering text
@param pathName the config path to retrieve the value from*/
private String getUnitString(boolean isHoverText, String defaultValue, String defaultHoverValue, String pathName) {
String path = isHoverText ? pathName + "-for-hover-text" : pathName;
String def = defaultValue;
if (isHoverText && defaultHoverValue != null) {
def = defaultHoverValue;
}
return config.getString(path, def);
}
/** Returns the config value for a color or style option in string-format, the supplied default value,
or null if no configSection was found.
@param selection the Target this decoration is meant for (Player, Server or Top)
@param getStyle if true, the result will be a style String, otherwise a color String
@param defaultColor the default color to return if the config value cannot be found (for style, the default is always "none")
@param pathName the config path to retrieve the value from*/
private @Nullable String getDecorationString(Target selection, boolean getStyle, String defaultColor, String pathName){
String path = getStyle ? pathName + "-style" : pathName;
String defaultValue = getStyle ? "none" : defaultColor;
ConfigurationSection section = getRelevantSection(selection);
return section != null ? section.getString(path, defaultValue) : null;

View File

@ -0,0 +1,199 @@
package com.gmail.artemis.the.gr8.playerstats.enums;
import org.bukkit.Statistic;
import org.jetbrains.annotations.NotNull;
public enum Unit {
NUMBER (Type.UNTYPED, "Times"),
KM (Type.DISTANCE, "km"),
MILE (Type.DISTANCE, "Miles"),
BLOCK (Type.DISTANCE, "Blocks"),
CM (Type.DISTANCE, "cm"),
HP (Type.DAMAGE, "HP"),
HEART (Type.DAMAGE, "Hearts"),
DAY (Type.TIME, "days"),
HOUR (Type.TIME, "hours"),
MINUTE (Type.TIME, "minutes"),
SECOND (Type.TIME, "seconds"),
TICK (Type.TIME, "ticks");
private final Type type;
private final String label;
Unit(Type type, String label) {
this.type = type;
this.label = label;
}
/** Returns a pretty name belonging to this enum constant. If the Unit is
NUMBER, it will return null. */
public String getLabel() {
return this.label;
}
public Type getType() {
return this.type;
}
public Unit getSmallerUnit(int stepsSmaller) {
switch (this) {
case DAY -> {
if (stepsSmaller >= 3) {
return Unit.SECOND;
} else if (stepsSmaller == 2) {
return Unit.MINUTE;
} else if (stepsSmaller == 1) {
return Unit.HOUR;
} else {
return this;
}
}
case HOUR -> {
if (stepsSmaller >= 2) {
return Unit.SECOND;
} else if (stepsSmaller == 1) {
return Unit.MINUTE;
} else {
return this;
}
}
case MINUTE -> {
if (stepsSmaller >= 1) {
return Unit.SECOND;
} else {
return this;
}
}
case KM -> {
if (stepsSmaller >= 2) {
return Unit.CM;
} else if (stepsSmaller == 1) {
return Unit.BLOCK;
} else {
return this;
}
}
case BLOCK -> {
if (stepsSmaller >= 1) {
return Unit.CM;
} else {
return this;
}
}
case HEART -> {
if (stepsSmaller >= 1) {
return Unit.HP;
} else {
return this;
}
}
default -> {
return this;
}
}
}
public double getSeconds() {
switch (this) {
case DAY -> {
return 86400;
}
case HOUR -> {
return 3600;
}
case MINUTE -> {
return 60;
}
case SECOND -> {
return 1;
}
case TICK -> {
return 1 / 20.0;
}
default -> {
return -1;
}
}
}
/** Returns the Unit corresponding to the given String. This String does NOT need to
match exactly (it can be "day" or "days", for example), and is case-insensitive.
@param unitName an approximation of the name belonging to the desired Unit, case-insensitive */
public static @NotNull Unit fromString(@NotNull String unitName) {
Unit unit;
switch (unitName.toLowerCase()) {
case "cm" -> unit = Unit.CM;
case "m", "block", "blocks" -> unit = Unit.BLOCK;
case "mile", "miles" -> unit = Unit.MILE;
case "km" -> unit = Unit.KM;
case "hp" -> unit = Unit.HP;
case "heart", "hearts" -> unit = Unit.HEART;
case "day", "days" -> unit = Unit.DAY;
case "hour", "hours" -> unit = Unit.HOUR;
case "minute", "minutes", "min" -> unit = Unit.MINUTE;
case "second", "seconds", "sec" -> unit = Unit.SECOND;
case "tick", "ticks" -> unit = Unit.TICK;
default -> unit = Unit.NUMBER;
}
return unit;
}
/** Returns the Unit.Type of this Statistic, which can be Untyped, Distance, Damage, or Time.
@param statistic the Statistic enum constant*/
public static @NotNull Type getTypeFromStatistic(Statistic statistic) {
String name = statistic.toString().toLowerCase();
if (name.contains("one_cm")) {
return Type.DISTANCE;
} else if (name.contains("damage")) {
return Type.DAMAGE;
} else if (name.contains("time") || name.contains("one_minute")) {
return Type.TIME;
} else {
return Type.UNTYPED;
}
}
/** Returns the most suitable timeUnit for this number.
@param type the Unit.Type of the statistic this number belongs to
@param number the statistic number as returned by Player.getStatistic()*/
public static Unit getMostSuitableUnit(Unit.Type type, long number) {
switch (type) {
case TIME -> {
long statNumber = number / 20;
if (statNumber >= 86400) {
return Unit.DAY;
} else if (statNumber >= 3600) {
return Unit.HOUR;
} else if (statNumber >= 60) {
return Unit.MINUTE;
} else {
return Unit.SECOND;
}
}
case DISTANCE -> {
if (number >= 100000) {
return Unit.KM;
} else {
return Unit.BLOCK;
}
}
case DAMAGE -> {
return Unit.HEART;
}
default -> {
return Unit.NUMBER;
}
}
}
public enum Type{
DAMAGE, //7 statistics
DISTANCE, //15 statistics
TIME, //5 statistics
UNTYPED;
Type() {
}
}
}

View File

@ -3,9 +3,6 @@ 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;
@ -13,6 +10,7 @@ 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.HSVLike;
import net.kyori.adventure.util.Index;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
@ -32,219 +30,116 @@ public class ComponentFactory {
config = c;
}
/** Returns [PlayerStats] followed by a single space. */
public TextComponent pluginPrefix(boolean isBukkitConsole) {
/** Returns [PlayerStats]. */
public TextComponent pluginPrefixComponent(boolean isBukkitConsole) {
return text("[")
.color(PluginColor.GRAY.getColor())
.append(text("PlayerStats").color(PluginColor.GOLD.getColor()))
.append(text("]"))
.append(space());
.append(text("]"));
}
/** Returns [PlayerStats] surrounded by underscores on both sides. */
public TextComponent prefixTitle(boolean isBukkitConsole) {
public TextComponent prefixTitleComponent(boolean isBukkitConsole) {
String underscores = "____________"; //12 underscores for both console and in-game
TextColor underscoreColor = isBukkitConsole ?
PluginColor.DARK_PURPLE.getConsoleColor() : PluginColor.DARK_PURPLE.getColor();
return text(underscores).color(underscoreColor)
.append(text(" ")) //4 spaces
.append(pluginPrefix(isBukkitConsole))
.append(text(" ")) //3 spaces (since prefix already has one)
.append(pluginPrefixComponent(isBukkitConsole))
.append(text(" ")) //4 spaces
.append(text(underscores));
}
/** Returns a TextComponent with the input String as content, with color Gray and decoration Italic.*/
public TextComponent subTitle(String content) {
public TextComponent subTitleComponent(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 isBukkitConsole) {
return pluginPrefix(isBukkitConsole)
.append(text(msg)
.color(PluginColor.MEDIUM_BLUE.getColor()));
public TextComponent messageComponent() {
return text().color(PluginColor.MEDIUM_BLUE.getColor()).build();
}
/** Returns a plain TextComponent that represents a single message line.
A space will be inserted after part1, part2 and part3.
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) {
return msgPart(part1, part2, part3, part4, false);
public TextComponent.Builder playerNameBuilder(String playerName, Target selection) {
return getComponentBuilder(playerName,
getColorFromString(config.getPlayerNameDecoration(selection, false)),
getStyleFromString(config.getPlayerNameDecoration(selection, true)));
}
/** Returns a plain TextComponent that represents a single message line.
A space will be inserted after part1, part2 and part3.
Each message part has its own designated color.
if isBukkitConsole is true, the colors will be the nearest ChatColor to the below colors.
@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, boolean isBukkitConsole) {
TextComponent.Builder msg = Component.text();
if (part1 != null) {
TextColor pluginColor = isBukkitConsole ? PluginColor.GOLD.getConsoleColor() : PluginColor.GOLD.getColor();
msg.append(text(part1)
.color(pluginColor))
.append(space());
/** @param prettyStatName a statName with underscores removed and each word capitalized
@param prettySubStatName if present, a subStatName with underscores removed and each word capitalized*/
public TextComponent statNameTextComponent(String prettyStatName, @Nullable String prettySubStatName, Target selection) {
TextComponent.Builder totalStatNameBuilder = getComponentBuilder(prettyStatName,
getColorFromString(config.getStatNameDecoration(selection, false)),
getStyleFromString(config.getStatNameDecoration(selection, true)));
TextComponent subStat = subStatNameTextComponent(prettySubStatName, selection);
if (!subStat.equals(Component.empty())) {
totalStatNameBuilder
.append(space().decorations(TextDecoration.NAMES.values(), false))
.append(subStatNameTextComponent(prettySubStatName, selection));
}
if (part2 != null) {
TextColor pluginColor = isBukkitConsole ? PluginColor.MEDIUM_GOLD.getConsoleColor() : PluginColor.MEDIUM_GOLD.getColor();
msg.append(text(part2)
.color(pluginColor))
.append(space());
}
if (part3 != null) {
TextColor pluginColor = isBukkitConsole ? PluginColor.LIGHT_GOLD.getConsoleColor() : PluginColor.LIGHT_GOLD.getColor();
msg.append(text(part3)
.color(pluginColor))
.append(space());
}
if (part4 != null) {
TextColor pluginColor = isBukkitConsole ? PluginColor.GRAY.getConsoleColor() : PluginColor.GRAY.getColor();
msg.append(text(part4)
.color(pluginColor));
}
return msg.build();
return totalStatNameBuilder.build();
}
/** Returns a TextComponent with a single line of hover-text in the specified color.
@param plainText the base message
@param hoverText the hovering text
@param hoverColor color of the hovering text */
public TextComponent simpleHoverPart(String plainText, String hoverText, PluginColor hoverColor) {
return simpleHoverPart(plainText, null, hoverText, hoverColor);
/** Returns a TextComponent for the subStatName, or an empty component.*/
private TextComponent subStatNameTextComponent(@Nullable String prettySubStatName, Target selection) {
if (prettySubStatName == null) {
return Component.empty();
} else {
return getComponentBuilder(null,
getColorFromString(config.getSubStatNameDecoration(selection, false)),
getStyleFromString(config.getSubStatNameDecoration(selection, true)))
.append(text("("))
.append(text(prettySubStatName))
.append(text(")"))
.build();
}
}
/** Returns a TextComponent with a single line of hover-text in the specified color.
If a PluginColor is provided for the plainText, the base color is set as well.
@param plainText the base message
@param plainColor color of the base message
@param hoverText the hovering text
@param hoverColor color of the hovering text */
public TextComponent simpleHoverPart(String plainText, @Nullable PluginColor plainColor, String hoverText, PluginColor hoverColor) {
TextComponent.Builder msg = Component.text()
.append(text(plainText))
.hoverEvent(HoverEvent.showText(
text(hoverText)
.color(hoverColor.getColor())));
if (plainColor != null) {
msg.color(plainColor.getColor());
}
return msg.build();
}
/** Returns a TextComponent with TranslatableComponent as a child.*/
public TextComponent statNameTransComponent(String statKey, String subStatKey, Target selection) {
TextComponent.Builder totalStatNameBuilder = getComponentBuilder(null,
getColorFromString(config.getStatNameDecoration(selection, false)),
getStyleFromString(config.getStatNameDecoration(selection, true)));
/** 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 GOLD
@param hoverLineTwoB text on the second part of the second line, with color LIGHT_GOLD
*/
public TextComponent complexHoverPart(@NotNull String plainText, @NotNull PluginColor color, String hoverLineOne, String hoverLineTwoA, 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());
}
TextComponent subStat = subStatNameTransComponent(subStatKey, selection);
if (statKey.equalsIgnoreCase("stat_type.minecraft.killed")) {
return totalStatNameBuilder.append(killEntityBuilder(subStat)).build();
}
if (hoverLineTwoA != null) {
hoverText.append(text(hoverLineTwoA)
.color(PluginColor.GOLD.getColor()));
if (hoverLineTwoB != null) {
hoverText.append(space());
}
}
if (hoverLineTwoB != null) {
hoverText.append(text(hoverLineTwoB).color(PluginColor.LIGHT_GOLD.getColor()));
}
return base.hoverEvent(HoverEvent.showText(hoverText.build()));
}
public TextComponent playerName(String playerName, Target selection) {
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 if (statKey.equalsIgnoreCase("stat_type.minecraft.killed_by")) {
return totalStatNameBuilder.append(entityKilledByBuilder(subStat)).build();
}
else {
statName = LanguageKeyHandler.getStatKey(request.getStatistic());
switch (request.getStatistic().getType()) {
case BLOCK -> subStatName = LanguageKeyHandler.getBlockKey(request.getBlock());
case ENTITY -> subStatName = LanguageKeyHandler.getEntityKey(request.getEntity());
case ITEM -> subStatName = LanguageKeyHandler.getItemKey(request.getItem());
case UNTYPED -> {
}
totalStatNameBuilder.append(translatable().key(statKey));
if (!subStat.equals(Component.empty())) {
totalStatNameBuilder.append(
space().decorations(TextDecoration.NAMES.values(), false)
.append(subStat));
}
return totalStatNameBuilder.build();
}
return statName(statName, subStatName, request.getSelection());
}
private TranslatableComponent statName(@NotNull String statKey, String subStatKey, @NotNull Target selection) {
TranslatableComponent.Builder totalName;
TextComponent subStat = subStatName(subStatKey, selection);
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(@Nullable String subStatName, Target selection) {
if (subStatName != null) {
TextDecoration style = getStyleFromString(config.getSubStatNameFormatting(selection, true));
TextComponent.Builder subStat = text()
/** Returns a TranslatableComponent for the subStatName, or an empty component.*/
private TextComponent subStatNameTransComponent(String subStatKey, Target selection) {
if (subStatKey != null) {
return getComponentBuilder(null,
getColorFromString(config.getSubStatNameDecoration(selection, false)),
getStyleFromString(config.getSubStatNameDecoration(selection, true)))
.append(text("("))
.append(translatable()
.key(subStatName))
.key(subStatKey))
.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;
.build();
}
return Component.empty();
}
/** 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) {
private TranslatableComponent.Builder killEntityBuilder(@NotNull TextComponent subStat) {
return translatable()
.key("commands.kill.success.single") //"Killed %s"
.args(subStat);
@ -254,7 +149,7 @@ public class ComponentFactory {
("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) {
private TranslatableComponent.Builder entityKilledByBuilder(@NotNull TextComponent subStat) {
return translatable()
.key("stat.minecraft.deaths") //"Number of Deaths"
.append(space())
@ -263,62 +158,96 @@ public class ComponentFactory {
.args(subStat));
}
public TextComponent statNumber(long number, Target selection) {
return createComponent(NumberFormatter.format(number),
getColorFromString(config.getStatNumberFormatting(selection, false)),
getStyleFromString(config.getStatNumberFormatting(selection, true)));
//TODO Add hoverComponent with full number
public TextComponent.Builder statNumberBuilder(String prettyNumber, Target selection) {
return getComponentBuilder(prettyNumber,
getColorFromString(config.getStatNumberDecoration(selection, false)),
getStyleFromString(config.getStatNumberDecoration(selection, true)));
}
public TextComponent title(String content, Target selection) {
return createComponent(content,
getColorFromString(config.getTitleFormatting(selection, false)),
getStyleFromString(config.getTitleFormatting(selection, true)));
public TextComponent statNumberHoverComponent(String mainNumber, String hoverNumber, @Nullable String hoverUnitName, @Nullable String hoverUnitKey, Target selection) {
TextColor baseColor = getColorFromString(config.getStatNumberDecoration(selection, false));
TextDecoration style = getStyleFromString(config.getStatNumberDecoration(selection, true));
TextComponent.Builder hoverText = getComponentBuilder(hoverNumber, getLighterColor(baseColor), style);
if (hoverUnitKey != null) {
hoverText.append(space())
.append(translatable().key(hoverUnitKey));
}
else if (hoverUnitName != null) {
hoverText.append(space())
.append(text(hoverUnitName));
}
return getComponent(mainNumber, baseColor, style).hoverEvent(HoverEvent.showText(hoverText));
}
public TextComponent titleNumber(int number) {
return createComponent(number + "",
getColorFromString(config.getTitleNumberFormatting(false)),
getStyleFromString(config.getTitleNumberFormatting(true)));
//TODO Make this dark gray (or at least darker than statNumber, and at least for time statistics)
public TextComponent statUnitComponent(String unitName, String unitKey, Target selection) {
if (!(unitName == null && unitKey == null)) {
TextComponent.Builder statUnitBuilder = getComponentBuilder(null,
getColorFromString(config.getSubStatNameDecoration(selection, false)),
getStyleFromString(config.getSubStatNameDecoration(selection, true)))
.append(text("["));
if (unitKey != null) {
statUnitBuilder.append(translatable()
.key(unitKey));
} else {
statUnitBuilder.append(text(unitName));
}
return statUnitBuilder.append(text("]")).build();
}
else {
return Component.empty();
}
}
public TextComponent serverName(String serverName) {
TextComponent colon = text(":").color(getColorFromString(config.getServerNameFormatting(false)));
return createComponent(serverName,
getColorFromString(config.getServerNameFormatting(false)),
getStyleFromString(config.getServerNameFormatting(true)))
public TextComponent titleComponent(String content, Target selection) {
return getComponent(content,
getColorFromString(config.getTitleDecoration(selection, false)),
getStyleFromString(config.getTitleDecoration(selection, true)));
}
public TextComponent titleNumberComponent(int number) {
return getComponent(number + "",
getColorFromString(config.getTitleNumberDecoration(false)),
getStyleFromString(config.getTitleNumberDecoration(true)));
}
public TextComponent serverNameComponent(String serverName) {
TextComponent colon = text(":").color(getColorFromString(config.getServerNameDecoration(false)));
return getComponent(serverName,
getColorFromString(config.getServerNameDecoration(false)),
getStyleFromString(config.getServerNameDecoration(true)))
.append(colon);
}
public TextComponent rankingNumber(String number) {
return createComponent(number,
getColorFromString(config.getRankNumberFormatting(false)),
getStyleFromString(config.getRankNumberFormatting(true)));
public TextComponent rankingNumberComponent(String number) {
return getComponent(number,
getColorFromString(config.getRankNumberDecoration(false)),
getStyleFromString(config.getRankNumberDecoration(true)));
}
public TextComponent dots(String dots) {
return createComponent(dots,
getColorFromString(config.getDotsFormatting(false)),
getStyleFromString(config.getDotsFormatting(true)));
public TextComponent.Builder dotsBuilder() {
return getComponentBuilder(null,
getColorFromString(config.getDotsDecoration(false)),
getStyleFromString(config.getDotsDecoration(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);
private TextComponent getComponent(String content, TextColor color, @Nullable TextDecoration style) {
return getComponentBuilder(content, color, style).build();
}
/** 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, ' ');
private TextComponent.Builder getComponentBuilder(@Nullable String content, TextColor color, @Nullable TextDecoration style) {
TextComponent.Builder builder = text()
.decorations(TextDecoration.NAMES.values(), false)
.color(color);
if (content != null) {
builder.append(text(content));
}
return capitals.toString();
if (style != null) {
builder.decorate(style);
}
return builder;
}
private TextColor getColorFromString(String configString) {
@ -343,6 +272,12 @@ public class ComponentFactory {
return names.value(textColor);
}
private TextColor getLighterColor(TextColor color) {
HSVLike oldColor = HSVLike.fromRGB(color.red(), color.green(), color.blue());
HSVLike newColor = HSVLike.hsvLike(oldColor.h(), 0.45F, oldColor.v());
return TextColor.color(newColor);
}
private @Nullable TextDecoration getStyleFromString(@NotNull String configString) {
if (configString.equalsIgnoreCase("none")) {
return null;
@ -355,5 +290,4 @@ public class ComponentFactory {
return styles.value(configString);
}
}
}

View File

@ -1,13 +1,16 @@
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.DebugLevel;
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.enums.Unit;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.ExampleMessage;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.HelpMessage;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.LanguageKeyHandler;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
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.TextColor;
import org.bukkit.Bukkit;
import org.bukkit.Statistic;
import org.bukkit.map.MinecraftFont;
@ -24,9 +27,13 @@ public class MessageWriter {
private static ConfigHandler config;
private static ComponentFactory componentFactory;
private final LanguageKeyHandler languageKeyHandler;
private final NumberFormatter formatter;
public MessageWriter(ConfigHandler c) {
config = c;
formatter = new NumberFormatter();
languageKeyHandler = new LanguageKeyHandler();
getComponentFactory();
}
@ -44,124 +51,266 @@ public class MessageWriter {
}
public TextComponent reloadedConfig(boolean isBukkitConsole) {
return componentFactory.msg(
"Config reloaded!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content("Config reloaded!"));
}
public TextComponent stillReloading(boolean isBukkitConsole) {
return componentFactory.msg(
"The plugin is (re)loading, " +
"your request will be processed when it is done!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"The plugin is (re)loading, your request will be processed when it is done!"));
}
public TextComponent waitAMoment(boolean longWait, boolean isBukkitConsole) {
String msg = longWait ? "Calculating statistics, this may take a minute..." :
"Calculating statistics, this may take a few moments...";
return componentFactory.msg(msg, isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(msg));
}
public TextComponent missingStatName(boolean isBukkitConsole) {
return componentFactory.msg(
"Please provide a valid statistic name!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"Please provide a valid statistic name!"));
}
public TextComponent missingSubStatName(Statistic.Type statType, boolean isBukkitConsole) {
return componentFactory.msg(
"Please add a valid " +
getSubStatTypeName(statType) +
" to look up this statistic!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"Please add a valid " + getSubStatTypeName(statType) + " to look up this statistic!"));
}
public TextComponent missingPlayerName(boolean isBukkitConsole) {
return componentFactory.msg(
"Please specify a valid player-name!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"Please specify a valid player-name!"));
}
public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry, boolean isBukkitConsole) {
return componentFactory.msg(
"\"" + subStatEntry + "\" is not a valid " + getSubStatTypeName(statType) + "!", isBukkitConsole);
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"\"" + subStatEntry + "\" is not a valid " + getSubStatTypeName(statType) + "!"));
}
public TextComponent unknownError(boolean isBukkitConsole) {
return componentFactory.msg(
return componentFactory.pluginPrefixComponent(isBukkitConsole)
.append(space())
.append(componentFactory.messageComponent().content(
"Something went wrong with your request, " +
"please try again or see /statistic for a usage explanation!", isBukkitConsole);
"please try again or see /statistic for a usage explanation!"));
}
public TextComponent formatPlayerStat(int stat, @NotNull StatRequest request) {
if (!request.isValid()) return unknownError(request.isBukkitConsoleSender());
return Component.text()
.append(componentFactory.playerName( request.getPlayerName() + ": ", Target.PLAYER))
.append(componentFactory.statNumber(stat, Target.PLAYER))
.append(space())
.append(componentFactory.statName(request))
.append(componentFactory.playerNameBuilder(request.getPlayerName(), Target.PLAYER)
.append(text(":"))
.append(space()))
.append(getStatNumberComponent(request.getStatistic(), stat, Target.PLAYER))
.append(space())
.append(getStatNameComponent(request))
.append(getStatUnitComponent(request.getStatistic(), request.getSelection()))
.build();
}
public TextComponent formatTopStats(@NotNull LinkedHashMap<String, Integer> topStats, @NotNull StatRequest request) {
if (!request.isValid()) return unknownError(request.isBukkitConsoleSender());
TextComponent.Builder topList = Component.text()
.append(newline())
.append(componentFactory.pluginPrefix(request.isBukkitConsoleSender()))
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP))
.append(space())
.append(componentFactory.titleNumber(topStats.size()))
.append(space())
.append(componentFactory.statName(request));
.append(componentFactory.pluginPrefixComponent(request.isBukkitConsoleSender())).append(space())
.append(componentFactory.titleComponent(config.getTopStatsTitle(), Target.TOP)).append(space())
.append(componentFactory.titleNumberComponent(topStats.size())).append(space())
.append(getStatNameComponent(request))
.append(getStatUnitComponent(request.getStatistic(), request.getSelection()));
ArrayList<Unit> timeUnits = null;
if (Unit.getTypeFromStatistic(request.getStatistic()) == Unit.Type.TIME) {
timeUnits = getTimeUnitRange(topStats.values().iterator().next());
}
boolean useDots = config.useDots();
boolean boldNames = config.playerNameIsBold();
Set<String> playerNames = topStats.keySet();
MinecraftFont font = new MinecraftFont();
Set<String> playerNames = topStats.keySet();
int count = 0;
for (String playerName : playerNames) {
count = count+1;
TextComponent.Builder playerNameBuilder = componentFactory.playerNameBuilder(playerName, Target.TOP);
count++;
topList.append(newline())
.append(componentFactory.rankingNumber(count + ". "))
.append(componentFactory.playerName(playerName, Target.TOP));
.append(componentFactory.rankingNumberComponent(count + ". "))
.append(playerNameBuilder);
if (useDots) {
topList.append(space());
int dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/2);
TextComponent.Builder dotsBuilder = componentFactory.dotsBuilder();
int dots;
if (request.isConsoleSender()) {
dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/6) + 7;
}
else if (boldNames) {
} else if (!boldNames) {
dots = (int) Math.round((130.0 - font.getWidth(count + ". " + playerName))/2);
} else {
dots = (int) Math.round((130.0 - font.getWidth(count + ". ") - (font.getWidth(playerName) * 1.19))/2);
}
if (dots >= 1) {
topList.append(componentFactory.dots(".".repeat(dots)));
topList.append(dotsBuilder.append(text((".".repeat(dots)))));
}
} else {
topList.append(playerNameBuilder.append(text(":")));
}
else {
topList.append(componentFactory.playerName(":", Target.TOP));
if (timeUnits != null) {
topList.append(space()).append(getTimeNumberComponent(topStats.get(playerName), request.getSelection(), timeUnits));
} else {
topList.append(space()).append(getStatNumberComponent(request.getStatistic(), topStats.get(playerName), Target.TOP));
}
topList.append(space()).append(componentFactory.statNumber(topStats.get(playerName), Target.TOP));
}
return topList.build();
}
public TextComponent formatServerStat(long stat, @NotNull StatRequest request) {
if (!request.isValid()) return unknownError(request.isBukkitConsoleSender());
return Component.text()
.append(componentFactory.title(config.getServerTitle(), Target.SERVER))
.append(componentFactory.titleComponent(config.getServerTitle(), Target.SERVER))
.append(space())
.append(componentFactory.serverName(config.getServerName()))
.append(componentFactory.serverNameComponent(config.getServerName()))
.append(space())
.append(componentFactory.statNumber(stat, Target.SERVER))
.append(space())
.append(componentFactory.statName(request))
.append(getStatNumberComponent(request.getStatistic(), stat, Target.SERVER))
.append(space())
.append(getStatNameComponent(request))
.append(getStatUnitComponent(request.getStatistic(), request.getSelection())) //space is provided by statUnit
.build();
}
/** Depending on the config settings, return either a TranslatableComponent representing
the statName (and potential subStatName), or a TextComponent with capitalized English names.*/
private TextComponent getStatNameComponent(StatRequest request) {
if (config.useTranslatableComponents()) {
String statKey = languageKeyHandler.getStatKey(request.getStatistic());
String subStatKey = request.getSubStatEntry();
if (subStatKey != null) {
switch (request.getStatistic().getType()) {
case BLOCK -> subStatKey = languageKeyHandler.getBlockKey(request.getBlock());
case ENTITY -> subStatKey = languageKeyHandler.getEntityKey(request.getEntity());
case ITEM -> subStatKey = languageKeyHandler.getItemKey(request.getItem());
default -> {
}
}
}
return componentFactory.statNameTransComponent(statKey, subStatKey, request.getSelection());
}
else {
return componentFactory.statNameTextComponent(
getPrettyName(request.getStatistic().toString()),
getPrettyName(request.getSubStatEntry()),
request.getSelection());
}
}
private TextComponent getStatNumberComponent(Statistic statistic, long statNumber, Target selection) {
Unit.Type type = Unit.getTypeFromStatistic(statistic);
Unit statUnit;
switch (type) {
case DISTANCE -> statUnit = Unit.fromString(config.getDistanceUnit(false));
case DAMAGE -> statUnit = Unit.fromString(config.getDamageUnit(false));
case TIME -> {
return getTimeNumberComponent(statNumber, selection, getTimeUnitRange(statNumber));
}
default -> statUnit = Unit.NUMBER;
}
String prettyNumber = formatter.format(statNumber, statUnit);
if (!config.useHoverText() || statUnit == Unit.NUMBER) {
return componentFactory.statNumberBuilder(prettyNumber, selection).build();
}
Unit hoverUnit = type == Unit.Type.DISTANCE ? Unit.fromString(config.getDistanceUnit(true)) :
Unit.fromString(config.getDamageUnit(true));
String prettyHoverNumber = formatter.format(statNumber, hoverUnit);
MyLogger.logMsg("mainNumber: " + prettyNumber + "\n" + "hoverNumber: " + prettyHoverNumber, DebugLevel.HIGH);
if (config.useTranslatableComponents()) {
String unitKey = languageKeyHandler.getUnitKey(hoverUnit);
if (unitKey == null) {
unitKey = hoverUnit.getLabel();
}
return componentFactory.statNumberHoverComponent(prettyNumber, prettyHoverNumber, null, unitKey, selection);
}
else {
return componentFactory.statNumberHoverComponent(prettyNumber, prettyHoverNumber, hoverUnit.getLabel(), null, selection);
}
}
private TextComponent getTimeNumberComponent(long statNumber, Target selection, ArrayList<Unit> unitRange) {
if (unitRange.size() <= 1 || (config.useHoverText() && unitRange.size() <= 3)) {
MyLogger.logMsg(
"There is something wrong with the time-units you specified, please check your config!",
true);
return componentFactory.statNumberBuilder("-", selection).build();
}
else {
String mainNumber = formatter.format(statNumber, unitRange.get(0), unitRange.get(1));
if (!config.useHoverText()) {
return componentFactory.statNumberBuilder(mainNumber, selection).build();
} else {
String hoverNumber = formatter.format(statNumber, unitRange.get(2), unitRange.get(3));
MyLogger.logMsg("mainNumber: " + mainNumber + ", hoverNumber: " + hoverNumber, DebugLevel.HIGH);
return componentFactory.statNumberHoverComponent(mainNumber, hoverNumber,
null, null, selection); //Time does not support translatable text,
} //because the unit and number are so tightly interwoven.
}
}
/** Get an ArrayList consisting of 2 or 4 timeUnits. The order of items is:
<p>0. maxUnit</p>
<p>1. minUnit</p>
<p>2. maxHoverUnit</p>
<p>3. minHoverUnit</p>*/
private ArrayList<Unit> getTimeUnitRange(long statNumber) {
ArrayList<Unit> unitRange = new ArrayList<>();
if (!config.autoDetectTimeUnit(false)) {
unitRange.add(Unit.fromString(config.getTimeUnit(false)));
unitRange.add(Unit.fromString(config.getTimeUnit(false, true)));
}
else {
Unit bigUnit = Unit.getMostSuitableUnit(Unit.Type.TIME, statNumber);
unitRange.add(bigUnit);
unitRange.add(bigUnit.getSmallerUnit(config.getNumberOfExtraTimeUnits(false)));
}
if (config.useHoverText()) {
if (!config.autoDetectTimeUnit(true)) {
unitRange.add(Unit.fromString(config.getTimeUnit(true)));
unitRange.add(Unit.fromString(config.getTimeUnit(true, true)));
}
else {
Unit bigHoverUnit = Unit.getMostSuitableUnit(Unit.Type.TIME, statNumber);
unitRange.add(bigHoverUnit);
unitRange.add(bigHoverUnit.getSmallerUnit(config.getNumberOfExtraTimeUnits(true)));
}
}
MyLogger.logMsg("total selected unitRange for this statistic: " + unitRange, DebugLevel.MEDIUM);
return unitRange;
}
private TextComponent getStatUnitComponent(Statistic statistic, Target selection) {
Unit statUnit;
switch (Unit.getTypeFromStatistic(statistic)) {
case DAMAGE -> statUnit = Unit.fromString(config.getDamageUnit(false));
case DISTANCE -> statUnit = Unit.fromString(config.getDistanceUnit(false));
default -> {
return Component.empty();
}
}
if (config.useTranslatableComponents()) {
String unitKey = languageKeyHandler.getUnitKey(statUnit);
if (unitKey != null) {
return Component.space()
.append(componentFactory.statUnitComponent(null, unitKey, selection));
}
}
return Component.space()
.append(componentFactory.statUnitComponent(statUnit.getLabel(), null, selection));
}
/** Returns "block", "entity", "item", or "sub-statistic" if the provided Type is null. */
private String getSubStatTypeName(Statistic.Type statType) {
String subStat = "sub-statistic";
@ -174,120 +323,30 @@ public class MessageWriter {
return subStat;
}
public TextComponent usageExamples(boolean isBukkitConsole) {
TextColor mainColor = isBukkitConsole ? PluginColor.GOLD.getConsoleColor() : PluginColor.GOLD.getColor();
TextColor accentColor1 = isBukkitConsole ? PluginColor.MEDIUM_GOLD.getConsoleColor() : PluginColor.MEDIUM_GOLD.getColor();
TextColor accentColor3 = isBukkitConsole ? PluginColor.LIGHT_YELLOW.getConsoleColor() : PluginColor.LIGHT_YELLOW.getColor();
String arrow = isBukkitConsole ? " -> " : ""; //4 spaces, alt + 26, 1 space
/** 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();
return Component.newline()
.append(componentFactory.prefixTitle(isBukkitConsole))
.append(newline())
.append(text("Examples: ").color(mainColor))
.append(newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("animals_bred ").color(accentColor1)
.append(text("top").color(accentColor3)))))
.append(newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("mine_block diorite ").color(accentColor1)
.append(text("me").color(accentColor3)))))
.append(newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("deaths ").color(accentColor1)
.append(text("player ").color(accentColor3)
.append(text("Artemis_the_gr8"))))));
int index = capitals.indexOf("_");
capitals.setCharAt(index + 1, Character.toUpperCase(capitals.charAt(index + 1)));
capitals.setCharAt(index, ' ');
}
return capitals.toString();
}
public TextComponent usageExamples(boolean isBukkitConsole) {
return new ExampleMessage(componentFactory, isBukkitConsole);
}
public TextComponent helpMsg(boolean isConsoleSender) {
if (isConsoleSender || !config.useHoverText()) {
return helpMsgPlain(isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit"));
}
else {
return helpMsgHover();
}
}
/** Returns the usage-explanation with hovering text */
private TextComponent helpMsgHover() {
String arrow = ""; //4 spaces, alt + 26
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.complexHoverPart("name", PluginColor.LIGHT_GOLD,
"The name that describes the statistic",
"Example:",
"\"animals_bred\"")))
.append(newline())
.append(componentFactory.msgPart(arrow, null, null, null)
.append(componentFactory.complexHoverPart("sub-statistic", PluginColor.LIGHT_GOLD,
"Some statistics need an item, block or entity as extra input",
"Example:",
"\"mine_block diorite\"")))
.append(newline())
.append(text(" ").color(PluginColor.LIGHT_GOLD.getColor())
.append(componentFactory.simpleHoverPart(
"", PluginColor.GOLD,
"Choose one", PluginColor.DARK_PURPLE))
.append(space())
.append(componentFactory.simpleHoverPart(
"me",
"See your own statistic", PluginColor.LIGHT_BLUE))
.append(text(" | "))
.append(componentFactory.simpleHoverPart(
"player",
"Choose any player that has played on your server", PluginColor.LIGHT_BLUE))
.append(text(" | "))
.append(componentFactory.simpleHoverPart(
"server",
"See the combined total for everyone on your server", PluginColor.LIGHT_BLUE))
.append(text(" | "))
.append(componentFactory.simpleHoverPart(
"top",
"See the top " + config.getTopListMaxSize(), PluginColor.LIGHT_BLUE)))
.append(newline())
.append(componentFactory.msgPart(arrow, null, null, null)
.append(text("player-name").color(PluginColor.LIGHT_GOLD.getColor())
.hoverEvent(HoverEvent.showText(
text("In case you typed ").color(PluginColor.LIGHT_BLUE.getColor())
.append(text("\"player\"").color(PluginColor.MEDIUM_GOLD.getColor()))
.append(text(", add the player's name"))))));
}
/** 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 isBukkitConsole) {
String arrow = isBukkitConsole ? " ->" : ""; //4 spaces, alt + 26
String bullet = isBukkitConsole ? " *" : ""; //8 spaces, alt + 7
return Component.newline()
.append(componentFactory.prefixTitle(isBukkitConsole))
.append(newline())
.append(componentFactory.subTitle("Type \"statistic examples\" to see examples!"))
.append(newline())
.append(componentFactory.msgPart("Usage:", null, "/statistic", null, isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(arrow, null, "name", null, isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(arrow, null, "{sub-statistic}", "(a block, item or entity)", isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(arrow, null, "me | player | server | top", null, isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(bullet, "me:", null, "your own statistic", isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(bullet, "player:", null, "choose a player", isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(bullet, "server:", null, "everyone on the server combined", isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(bullet, "top:", null, "the top " + config.getTopListMaxSize(), isBukkitConsole))
.append(newline())
.append(componentFactory.msgPart(arrow, null, "{player-name}", null, isBukkitConsole));
return new HelpMessage(componentFactory,
config.useHoverText() && !isConsoleSender,
isConsoleSender && Bukkit.getName().equalsIgnoreCase("CraftBukkit"),
config.getTopListMaxSize());
}
}

View File

@ -0,0 +1,128 @@
package com.gmail.artemis.the.gr8.playerstats.msg;
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
import java.text.DecimalFormat;
public class NumberFormatter {
private final DecimalFormat format;
public NumberFormatter() {
format = new DecimalFormat();
format.setGroupingUsed(true);
format.setGroupingSize(3);
}
/** Turns the input number into a more readable format depending on its type
(number-of-times, time-, damage- or distance-based) according to the
corresponding config settings, and adds commas in groups of 3.*/
public String format(long number, Unit statUnit) {
return format(number, statUnit, null);
}
public String format(long number, Unit statUnit, Unit smallTimeUnit) {
if (smallTimeUnit == null) {
switch (statUnit.getType()) {
case DISTANCE -> {
return formatDistance(number, statUnit);
}
case DAMAGE -> {
return formatDamage(number, statUnit);
}
default -> {
return format.format(number);
}
}
} else {
return formatTime(number, statUnit, smallTimeUnit);
}
}
/** The unit of damage-based statistics is half a heart by default.
This method turns the number into hearts. */
private String formatDamage(long number, Unit statUnit) { //7 statistics
if (statUnit == Unit.HEART) {
return format.format(Math.round(number / 2.0));
} else {
return format.format(number);
}
}
/** The unit of distance-based statistics is cm by default. This method turns it into blocks by default,
and turns it into km or leaves it as cm otherwise, depending on the config settings. */
private String formatDistance(long number, Unit statUnit) { //15 statistics
switch (statUnit) {
case CM -> {
return format.format(number);
}
case MILE -> {
return format.format(Math.round(number / 160934.4)); //to get from CM to Miles
}
case KM -> {
return format.format(Math.round(number / 100000.0)); //divide by 100 to get M, divide by 1000 to get KM
}
default -> {
return format.format(Math.round(number / 100.0));
}
}
}
/** The unit of time-based statistics is ticks by default.*/
private String formatTime(long number, Unit bigUnit, Unit smallUnit) { //5 statistics
if (number == 0) {
return "-";
}
if (bigUnit == Unit.TICK && smallUnit == Unit.TICK || bigUnit == Unit.NUMBER || smallUnit == Unit.NUMBER) {
return format.format(number);
}
StringBuilder output = new StringBuilder();
double max = bigUnit.getSeconds();
double min = smallUnit.getSeconds();
double leftover = number / 20.0;
if (isInRange(max, min, 86400) && leftover >= 86400) {
double days = leftover / 60 / 60 / 24;
leftover = leftover % (60 * 60 * 24);
if (smallUnit == Unit.DAY && leftover >= 43200) {
days++;
return output.append(format.format(Math.round(days)))
.append("d").toString();
}
output.append(format.format(Math.round(days)))
.append("d ");
}
if (isInRange(max, min, 3600) && leftover >= 3600) {
double hours = leftover / 60 / 60;
leftover = leftover % (60 * 60);
if (smallUnit == Unit.HOUR && leftover >= 1800) {
hours++;
return output.append(format.format(Math.round(hours)))
.append("h").toString();
}
output.append(format.format(Math.round(hours)))
.append("h ");
}
if (isInRange(max, min, 60) && leftover >= 60) {
double minutes = leftover / 60;
leftover = leftover % 60;
if (smallUnit == Unit.MINUTE && leftover >= 30) {
minutes++;
return output.append(format.format(Math.round(minutes)))
.append("m").toString();
}
output.append(format.format(Math.round(minutes)))
.append("m ");
}
if (isInRange(max, min, 1) && leftover > 0) {
output.append(format.format(Math.round(leftover)))
.append("s");
}
return output.toString();
}
private boolean isInRange(double bigUnit, double smallUnit, double unitToEvaluate) {
return bigUnit >= unitToEvaluate && unitToEvaluate >= smallUnit;
}
}

View File

@ -22,9 +22,9 @@ public class PrideComponentFactory extends ComponentFactory {
@Override
public TextComponent prefixTitle(boolean isBukkitConsole) {
public TextComponent prefixTitleComponent(boolean isBukkitConsole) {
if (cancelRainbow(isBukkitConsole)) {
return super.prefixTitle(isBukkitConsole);
return super.prefixTitleComponent(isBukkitConsole);
}
else {
String title = "<rainbow:16>____________ [PlayerStats] ____________</rainbow>"; //12 underscores
@ -35,9 +35,9 @@ public class PrideComponentFactory extends ComponentFactory {
}
@Override
public TextComponent pluginPrefix(boolean isConsoleSender) {
public TextComponent pluginPrefixComponent(boolean isConsoleSender) {
if (cancelRainbow(isConsoleSender)) {
return super.pluginPrefix(isConsoleSender);
return super.pluginPrefixComponent(isConsoleSender);
}
return text()
.append(MiniMessage.miniMessage()
@ -53,7 +53,7 @@ public class PrideComponentFactory extends ComponentFactory {
"<#01c1a7>a</#01c1a7>" +
"<#0690d4>t</#0690d4>" +
"<#205bf3>s</#205bf3>" +
"<#6c15fa>] </#6c15fa>"))
"<#6c15fa>]</#6c15fa>"))
.build();
}

View File

@ -0,0 +1,89 @@
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
import com.gmail.artemis.the.gr8.playerstats.msg.ComponentFactory;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.List;
import static net.kyori.adventure.text.Component.text;
public class ExampleMessage implements TextComponent {
private final TextComponent exampleMessage;
private final ComponentFactory componentFactory;
public ExampleMessage(ComponentFactory componentFactory, boolean isBukkitConsole) {
this.componentFactory = componentFactory;
exampleMessage = getExampleMessage(isBukkitConsole);
}
public TextComponent getExampleMessage(boolean isBukkitConsole) {
TextColor mainColor = isBukkitConsole ? PluginColor.GOLD.getConsoleColor() : PluginColor.GOLD.getColor();
TextColor accentColor1 = isBukkitConsole ? PluginColor.MEDIUM_GOLD.getConsoleColor() : PluginColor.MEDIUM_GOLD.getColor();
TextColor accentColor3 = isBukkitConsole ? PluginColor.LIGHT_YELLOW.getConsoleColor() : PluginColor.LIGHT_YELLOW.getColor();
String arrow = isBukkitConsole ? " -> " : ""; //4 spaces, alt + 26, 1 space
return Component.newline()
.append(componentFactory.prefixTitleComponent(isBukkitConsole))
.append(Component.newline())
.append(text("Examples: ").color(mainColor))
.append(Component.newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("animals_bred ").color(accentColor1)
.append(text("top").color(accentColor3)))))
.append(Component.newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("mine_block diorite ").color(accentColor1)
.append(text("me").color(accentColor3)))))
.append(Component.newline())
.append(text(arrow).color(mainColor)
.append(text("/statistic ")
.append(text("deaths ").color(accentColor1)
.append(text("player ").color(accentColor3)
.append(text("Artemis_the_gr8"))))));
}
@Override
public @NotNull String content() {
return exampleMessage.content();
}
@Override
public @NotNull TextComponent content(@NotNull String content) {
return exampleMessage.content(content);
}
@Override
public @NotNull TextComponent.Builder toBuilder() {
return exampleMessage.toBuilder();
}
@Override
public @Unmodifiable @NotNull List<Component> children() {
return exampleMessage.children();
}
@Override
public @NotNull TextComponent children(@NotNull List<? extends ComponentLike> children) {
return exampleMessage.children(children);
}
@Override
public @NotNull Style style() {
return exampleMessage.style();
}
@Override
public @NotNull TextComponent style(@NotNull Style style) {
return exampleMessage.style(style);
}
}

View File

@ -0,0 +1,205 @@
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
import com.gmail.artemis.the.gr8.playerstats.enums.PluginColor;
import com.gmail.artemis.the.gr8.playerstats.msg.ComponentFactory;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
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.Style;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.format.TextDecoration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.List;
import static net.kyori.adventure.text.Component.text;
public class HelpMessage implements TextComponent {
private final ComponentFactory componentFactory;
private final TextComponent helpMessage;
boolean isBukkitConsole;
TextColor GRAY;
TextColor DARK_PURPLE;
TextColor GOLD;
TextColor MEDIUM_GOLD;
TextColor LIGHT_GOLD;
TextColor LIGHT_BLUE;
public HelpMessage(ComponentFactory componentFactory, boolean useHover, boolean isBukkitConsole, int listSize) {
this.componentFactory = componentFactory;
this.isBukkitConsole = isBukkitConsole;
getPluginColors(isBukkitConsole);
if (!useHover || isBukkitConsole) {
helpMessage = getPlainHelpMsg(isBukkitConsole, listSize);
} else {
helpMessage = helpMsgHover(listSize);
}
}
private TextComponent getPlainHelpMsg(boolean isBukkitConsole, int listSize) {
String arrowSymbol = isBukkitConsole ? "->" : ""; //alt + 26
String bulletSymbol = isBukkitConsole ? "*" : ""; //alt + 7
TextComponent spaces = text(" "); //4 spaces
TextComponent arrow = text(arrowSymbol).color(NamedTextColor.GOLD);
TextComponent bullet = text(bulletSymbol).color(NamedTextColor.GOLD);
return Component.newline()
.append(componentFactory.prefixTitleComponent(isBukkitConsole))
.append(newline())
.append(text("Type \"/statistic examples\" to see examples!").color(GRAY).decorate(TextDecoration.ITALIC))
.append(newline())
.append(text("Usage:").color(GOLD)).append(space())
.append(text("/statistic").color(LIGHT_GOLD))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("name").color(LIGHT_GOLD))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("{sub-statistic}").color(LIGHT_GOLD)).append(space())
.append(text("(a block, item or entity)").color(GRAY))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("me | player | server | top").color(LIGHT_GOLD))
.append(newline())
.append(spaces).append(spaces).append(bullet).append(space())
.append(text("me:").color(MEDIUM_GOLD)).append(space())
.append(text("your own statistic").color(GRAY))
.append(newline())
.append(spaces).append(spaces).append(bullet).append(space())
.append(text("player:").color(MEDIUM_GOLD)).append(space())
.append(text("choose a player").color(GRAY))
.append(newline())
.append(spaces).append(spaces).append(bullet).append(space())
.append(text("server:").color(MEDIUM_GOLD)).append(space())
.append(text("everyone on the server combined").color(GRAY))
.append(newline())
.append(spaces).append(spaces).append(bullet).append(space())
.append(text("top:").color(MEDIUM_GOLD)).append(space())
.append(text("the top").color(GRAY).append(space()).append(text(listSize)))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("{player-name}").color(LIGHT_GOLD));
}
private TextComponent helpMsgHover(int listSize) {
TextComponent spaces = text(" ");
TextComponent arrow = text("").color(GOLD);
return Component.newline()
.append(componentFactory.prefixTitleComponent(false))
.append(newline())
.append(componentFactory.subTitleComponent("Hover over the arguments for more information!"))
.append(newline())
.append(text("Usage:").color(GOLD)).append(space())
.append(text("/statistic").color(LIGHT_GOLD))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("name").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(text("The name that describes the statistic").color(LIGHT_BLUE)
.append(newline())
.append(text("Example: ").color(GOLD))
.append(text("\"animals_bred\"").color(LIGHT_GOLD)))))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("sub-statistic").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("Some statistics need an item, block or entity as extra input").color(LIGHT_BLUE)
.append(newline())
.append(text("Example: ").color(GOLD)
.append(text("\"mine_block diorite\"").color(LIGHT_GOLD))))))
.append(newline())
.append(spaces).append(arrow
.hoverEvent(HoverEvent.showText(
text("Choose one").color(DARK_PURPLE)))).append(space())
.append(text("me").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("See your own statistic").color(LIGHT_BLUE))))
.append(text(" | ").color(LIGHT_GOLD))
.append(text("player").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("Choose any player that has played on your server").color(LIGHT_BLUE))))
.append(text(" | ").color(LIGHT_GOLD))
.append(text("server").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("See the combined total for everyone on your server").color(LIGHT_BLUE))))
.append(text(" | ").color(LIGHT_GOLD))
.append(text("top").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("See the top").color(LIGHT_BLUE).append(space())
.append(text(listSize)))))
.append(newline())
.append(spaces).append(arrow).append(space())
.append(text("player-name").color(LIGHT_GOLD)
.hoverEvent(HoverEvent.showText(
text("In case you typed").color(LIGHT_BLUE).append(space())
.append(text("\"player\"").color(LIGHT_GOLD))
.append(text(", add the player's name")))));
}
private void getPluginColors(boolean isBukkitConsole) {
if (isBukkitConsole) {
GRAY = PluginColor.GRAY.getConsoleColor();
DARK_PURPLE = PluginColor.DARK_PURPLE.getConsoleColor();
GOLD = PluginColor.GOLD.getConsoleColor();
MEDIUM_GOLD = PluginColor.MEDIUM_GOLD.getConsoleColor();
LIGHT_GOLD = PluginColor.LIGHT_GOLD.getConsoleColor();
LIGHT_BLUE = PluginColor.LIGHT_BLUE.getConsoleColor();
} else {
GRAY = PluginColor.GRAY.getColor();
DARK_PURPLE = PluginColor.DARK_PURPLE.getColor();
GOLD = PluginColor.GOLD.getColor();
MEDIUM_GOLD = PluginColor.MEDIUM_GOLD.getColor();
LIGHT_GOLD = PluginColor.LIGHT_GOLD.getColor();
LIGHT_BLUE = PluginColor.LIGHT_BLUE.getColor();
}
}
@Override
public @NotNull String content() {
return helpMessage.content();
}
@Override
public @NotNull TextComponent content(@NotNull String content) {
return helpMessage.content(content);
}
@Override
public @NotNull Builder toBuilder() {
return helpMessage.toBuilder();
}
@Override
public @Unmodifiable @NotNull List<Component> children() {
return helpMessage.children();
}
@Override
public @NotNull TextComponent children(@NotNull List<? extends ComponentLike> children) {
return helpMessage.children(children);
}
@Override
public @NotNull Style style() {
return helpMessage.style();
}
@Override
public @NotNull TextComponent style(@NotNull Style style) {
return helpMessage.style(style);
}
private TextComponent space() {
return Component.space();
}
private TextComponent newline() {
return Component.newline();
}
}

View File

@ -1,5 +1,6 @@
package com.gmail.artemis.the.gr8.playerstats.msg;
package com.gmail.artemis.the.gr8.playerstats.msg.msgutils;
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
import com.gmail.artemis.the.gr8.playerstats.utils.EnumHandler;
import org.bukkit.Material;
import org.bukkit.Statistic;
@ -12,17 +13,14 @@ import java.util.HashMap;
public class LanguageKeyHandler {
private final static HashMap<Statistic, String> statNameKeys;
private final HashMap<Statistic, String> statNameKeys;
static {
public LanguageKeyHandler() {
statNameKeys = new HashMap<>();
generateStatNameKeys();
}
private LanguageKeyHandler() {
}
public static String getStatKey(@NotNull Statistic statistic) {
public String getStatKey(@NotNull Statistic statistic) {
if (statistic.getType() == Statistic.Type.UNTYPED) {
return "stat.minecraft." + statNameKeys.get(statistic);
}
@ -33,7 +31,7 @@ public class LanguageKeyHandler {
/** Get the official Key from the NameSpacedKey for this entityType,
or return null if no enum constant can be retrieved or entityType is UNKNOWN.*/
public static @Nullable String getEntityKey(EntityType entity) {
public @Nullable String getEntityKey(EntityType entity) {
if (entity == null || entity == EntityType.UNKNOWN) return null;
else {
return "entity.minecraft." + entity.getKey().getKey();
@ -42,7 +40,7 @@ public class LanguageKeyHandler {
/** Get the official Key from the NameSpacedKey for this item Material,
or return null if no enum constant can be retrieved.*/
public static @Nullable String getItemKey(Material item) {
public @Nullable String getItemKey(Material item) {
if (item == null) return null;
else if (item.isBlock()) {
return getBlockKey(item);
@ -54,7 +52,7 @@ public class LanguageKeyHandler {
/** Returns the official Key from the NameSpacedKey for the block Material provided,
or return null if no enum constant can be retrieved.*/
public static @Nullable String getBlockKey(Material block) {
public @Nullable String getBlockKey(Material block) {
if (block == null) return null;
else if (block.toString().toLowerCase().contains("wall_banner")) { //replace wall_banner with regular banner, since there is no key for wall banners
String blockName = block.toString().toLowerCase().replace("wall_", "");
@ -66,11 +64,19 @@ public class LanguageKeyHandler {
}
}
private static void generateDefaultKeys() {
public @Nullable String getUnitKey(Unit unit) {
if (unit == Unit.BLOCK) {
return "soundCategory.block";
} else {
return null;
}
}
private void generateDefaultKeys() {
Arrays.stream(Statistic.values()).forEach(statistic -> statNameKeys.put(statistic, statistic.toString().toLowerCase()));
}
private static void generateStatNameKeys() {
private void generateStatNameKeys() {
//get the enum names for all statistics first
generateDefaultKeys();

View File

@ -111,9 +111,9 @@ public class ReloadThread extends Thread {
MyLogger.actionCreated((offlinePlayers != null) ? offlinePlayers.length : 0);
ForkJoinPool.commonPool().invoke(task);
MyLogger.actionFinished(1);
MyLogger.logTimeTaken("ReloadThread",
("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
OfflinePlayerHandler.updateOfflinePlayerList(playerMap);
MyLogger.logTimeTaken("ReloadThread",
("loaded " + OfflinePlayerHandler.getOfflinePlayerCount() + " offline players"), time);
}
}

View File

@ -30,26 +30,6 @@ public class StatRequest {
playerFlag = false;
}
/** Returns true if this StatRequest has all the information needed for a Statistic lookup to succeed.*/
public boolean isValid() {
if (statistic == null) return false;
switch (statistic.getType()) {
case BLOCK -> {
if (block == null) return false;
}
case ENTITY -> {
if (entity == null) return false;
}
case ITEM -> {
if (item == null) return false;
}
case UNTYPED -> {
if (subStatEntry != null) return false;
}
} //if target = PLAYER and playerName = null, return false, otherwise return true
return selection != Target.PLAYER || playerName != null;
}
public @NotNull CommandSender getCommandSender() {
return sender;
}

View File

@ -112,6 +112,18 @@ public class EnumHandler {
return statNames.contains(statName.toLowerCase());
}
public static boolean isDistanceStatistic(@NotNull String statName) {
return statName.toLowerCase().contains("one_cm");
}
public static boolean isDamageStatistic(@NotNull String statName) {
return statName.toLowerCase().contains("damage");
}
public static boolean isTimeStatistic(@NotNull String statName) {
return statName.toLowerCase().contains("time") || statName.toLowerCase().contains("one_minute");
}
/** Returns the names of all general statistics in lowercase */
public static List<String> getStatNames() {
return statNames;

View File

@ -52,6 +52,10 @@ public class MyLogger {
}
}
public static void logMsg(String content) {
logMsg(content, DebugLevel.LOW, false);
}
public static void logMsg(String content, boolean logAsWarning) {
logMsg(content, DebugLevel.LOW, logAsWarning);
}
@ -129,7 +133,7 @@ public class MyLogger {
if (debugLevel != DebugLevel.LOW) {
threadNames = new ConcurrentHashMap<>();
playersIndex.set(0);
logger.info("Initial Action created for " + taskLength + " Players. Start Index is " + playersIndex.get() + ". Processing...");
logger.info("Initial Action created for " + taskLength + " Players. Processing...");
}
}
@ -161,7 +165,7 @@ public class MyLogger {
}
processedPlayers[nextPlayersIndex() % 10] = playerName;
}
else if (debugLevel == DebugLevel.MEDIUM) {
else if (debugLevel == DebugLevel.MEDIUM || debugLevel == DebugLevel.HIGH && thread == 2) {
nextPlayersIndex();
}
}
@ -179,8 +183,10 @@ public class MyLogger {
if (debugLevel != DebugLevel.LOW) {
logger.info("Finished Recursive Action! In total " +
threadNames.size() + " Threads were used to process " +
playersIndex.get() + " Players: " +
Collections.list(threadNames.keys()));
playersIndex.get() + " Players.");
}
if (debugLevel == DebugLevel.HIGH) {
logger.info(Collections.list(threadNames.keys()).toString());
}
}

View File

@ -1,20 +0,0 @@
package com.gmail.artemis.the.gr8.playerstats.utils;
import java.text.DecimalFormat;
public class NumberFormatter {
private static final DecimalFormat format;
static{
format = new DecimalFormat();
format.setGroupingUsed(true);
format.setGroupingSize(3);
}
private NumberFormatter(){
}
public static String format(long number) {
return format.format(number);
}
}

View File

@ -1,7 +1,7 @@
# ------------------------------------------------------------------------------------------------------ #
# PlayerStats Configuration #
# ------------------------------------------------------------------------------------------------------ #
config-version: 4
config-version: 5
# # ------------------------------- # #
@ -10,7 +10,7 @@ config-version: 4
# How much output you'll get in the server console while PlayerStats is processing
# 1 = low (only show unexpected errors)
# 2 = medium (detail all encountered exceptions, log main tasks and show time taken)
# 2 = medium (log main tasks and time taken)
# 3 = high (log all tasks and time taken)
debug-level: 1
@ -31,9 +31,34 @@ number-of-days-since-last-joined: 0
# The actual translation is handled by the Minecraft language files and happens automatically
translate-to-client-language: true
# Use hover-text for additional info in the usage explanation
# Use hover-text for additional info about statistic numbers
enable-hover-text: true
# The unit to display certain statistics in.
# Minecraft measures distance in cm. PlayerStats supports: blocks, cm, m (= blocks), miles, km
distance-unit: blocks
distance-unit-for-hover-text: km
# Minecraft measures damage in 0.5 hearts (1HP). PlayerStats supports: hp, hearts
damage-unit: hearts
damage-unit-for-hover-text: hp
# Minecraft measures time in ticks. With the below settings, PlayerStats will:
# Auto-detect the best maximum unit to use (weeks/days/hours/minutes/seconds) for your players' statistics
# Show a specified amount of additional smaller units (example: "x days" would become "x days, y hours, z minutes")
auto-detect-biggest-time-unit: true
number-of-extra-units: 1
auto-detect-biggest-time-unit-for-hover-text: false
number-of-extra-units-for-hover-text: 0
# If you don't want the unit to be auto-detected, set the auto-detect settings to false and specify your own range here
# If the max and min are the same, only that unit will be displayed
# PlayerStats supports: days, hours, minutes, seconds (and ticks if you want the original number)
biggest-time-unit: days
smallest-time-unit: hours
biggest-time-unit-for-hover-text: hours
smallest-time-unit-for-hover-text: seconds
# Automatically use themed formatting for the duration of certain holidays or festivals
enable-festive-formatting: true