Started expanding the API, fixed minor bug in ConfigHandler for #playerNameIsBold

This commit is contained in:
Artemis-the-gr8 2022-08-03 15:24:29 +02:00
parent 85ceb0b8c7
commit f960bbd5a2
14 changed files with 170 additions and 76 deletions

View File

@ -0,0 +1,19 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
import net.kyori.adventure.text.TextComponent;
public interface Formatter extends StatFormatter {
/** @return [PlayerStats]*/
TextComponent getPluginPrefix();
TextComponent getRainbowPluginPrefix();
/** @return ________ [PlayerStats] ________*/
TextComponent getPluginPrefixAsTitle();
TextComponent getRainbowPluginPrefixAsTitle();
TextComponent formatSingleTopStatLine(int positionInTopList, String playerName, long statNumber, Unit statNumberUnit);
}

View File

@ -53,4 +53,6 @@ public interface PlayerStats {
@param topListSize how big the top-x should be (10 by default)
@return a TopStatRequest that can be used to look up a top statistic*/
TopStatRequest topStat(int topListSize);
Formatter getFormatter();
}

View File

@ -36,6 +36,11 @@ public final class PlayerStatsAPI implements PlayerStats {
return new TopStatRequest(statRequestHandler);
}
@Override
public Formatter getFormatter() {
return null;
}
static StatCalculator statCalculator() {
return statCalculator;
}

View File

@ -20,20 +20,21 @@ import java.util.LinkedHashMap;
<a href="https://docs.adventure.kyori.net/platform/bukkit.html">Adventure's website</a>.
<br>
<br>Alternatively, you can also turn your TextComponent into a plain String with
{@link #statResultComponentToString(TextComponent)}. Don't use Adventure's method .content()
{@link #TextComponentToString(TextComponent)}. Don't use Adventure's method .content()
on your statResult to do this - because of the way the TextComponent is built by PlayerStats,
you won't be able to get the full content that way.*/
@Internal
public interface StatFormatter {
/** Turns a TextComponent into its String representation. If you don't want to work with
Adventure's TextComponents, you can call this method to turn any stat-result into a String.
/** Turns a TextComponent into its String representation. This method is equipped
to turn all PlayerStats' formatted statResults into String.
@return a String representation of this TextComponent, without hover/click events,
but with color, style and formatting. TranslatableComponents will be turned into
plain English.*/
static String statResultComponentToString(TextComponent statResult) {
static String TextComponentToString(TextComponent component) {
return ComponentUtils.getTranslatableComponentSerializer()
.serialize(statResult);
.serialize(component);
}
/** @return a TextComponent with the following parts:

View File

@ -277,10 +277,10 @@ public final class ConfigHandler {
return getDecorationString(selection, getStyleSetting, def, "player-names");
}
/** Returns true if playerNames Style is "bold", false if it is not.
/** Returns true if playerNames Style is "bold" for a top-stat, false if it is not.
<br>Default: false</br>*/
public boolean playerNameIsBold() {
ConfigurationSection style = getRelevantSection(Target.PLAYER);
ConfigurationSection style = getRelevantSection(Target.TOP);
if (style != null) {
String styleString = style.getString("player-names");

View File

@ -32,6 +32,7 @@ public final class MessageBuilder {
private static ConfigHandler config;
private boolean useHoverText;
private boolean isConsoleBuilder;
private final ComponentFactory componentFactory;
private final LanguageKeyHandler languageKeyHandler;
@ -65,6 +66,10 @@ public final class MessageBuilder {
useHoverText = desiredSetting;
}
public void setConsoleBuilder(boolean isConsoleBuilder) {
this.isConsoleBuilder = isConsoleBuilder;
}
public TextComponent reloadedConfig() {
return componentFactory.pluginPrefix()
.append(space())
@ -163,9 +168,9 @@ public final class MessageBuilder {
return ExampleMessage.construct(componentFactory);
}
public TextComponent helpMsg(boolean isConsoleSender) {
public TextComponent helpMsg() {
int listSize = config.getTopListMaxSize();
if (!isConsoleSender && useHoverText) {
if (!isConsoleBuilder && useHoverText) {
return HelpMessage.constructHoverMsg(componentFactory, listSize);
} else {
return HelpMessage.constructPlainMsg(componentFactory, listSize);
@ -185,7 +190,7 @@ public final class MessageBuilder {
.append(getStatNumberComponent(statRequest, stat))
.append(space())
.append(getStatNameComponent(statRequest))
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender())) //space is provided by statUnitComponent
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget())) //space is provided by statUnitComponent
.build();
return getFormattingFunction(playerStat, Target.PLAYER);
@ -205,7 +210,7 @@ public final class MessageBuilder {
.append(getStatNumberComponent(statRequest, stat))
.append(space())
.append(getStatNameComponent(statRequest))
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender())) //space is provided by statUnit
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget())) //space is provided by statUnit
.build();
return getFormattingFunction(serverStat, Target.SERVER);
@ -218,7 +223,7 @@ public final class MessageBuilder {
<br>- If both parameters are null, the statResult will be returned as is.</br>*/
public BiFunction<Integer, CommandSender, TextComponent> formattedTopStatFunction(@NotNull LinkedHashMap<String, Integer> topStats, @NotNull StatRequest statRequest) {
final TextComponent title = getTopStatsTitleComponent(statRequest, topStats.size());
final TextComponent shortTitle = getTopStatsTitleShortComponent(statRequest, topStats.size());
final TextComponent shortTitle = getTopStatDescription(statRequest, topStats.size());
final TextComponent list = getTopStatListComponent(topStats, statRequest);
final boolean useEnters = config.useEnters(Target.TOP, false);
final boolean useEntersForShared = config.useEnters(Target.TOP, true);
@ -263,41 +268,18 @@ public final class MessageBuilder {
};
}
private BiFunction<Integer, CommandSender, TextComponent> getFormattingFunction(@NotNull TextComponent statResult, Target target) {
boolean useEnters = config.useEnters(target, false);
boolean useEntersForShared = config.useEnters(target, true);
public TextComponent singleTopStatLine(int positionInTopList, String playerName, long statNumber, Unit statNumberUnit) {
TextComponent.Builder topStatLineBuilder = Component.text()
.append(space())
.append(componentFactory.rankNumber(positionInTopList))
.append(space());
return (shareCode, sender) -> {
TextComponent.Builder statBuilder = text();
//if we're adding a share-button
if (shareCode != null) {
if (useEnters) {
statBuilder.append(newline());
}
statBuilder.append(statResult)
.append(space())
.append(componentFactory.shareButton(shareCode));
}
//if we're adding a "shared by" component
else if (sender != null) {
if (useEntersForShared) {
statBuilder.append(newline());
}
statBuilder.append(statResult)
.append(newline())
.append(componentFactory.sharedByMessage(
getSharerNameComponent(sender)));
}
//if we're not adding a share-button or a "shared by" component
else {
if (useEnters) {
statBuilder.append(newline());
}
statBuilder.append(statResult);
}
return statBuilder.build();
};
if (config.useDots()) {
topStatLineBuilder.append(getPlayerNameWithDotsComponent(positionInTopList, playerName));
} else {
topStatLineBuilder.append(componentFactory.playerName(playerName + ":", Target.TOP));
}
//TODO add formatted number here
}
private Component getSharerNameComponent(CommandSender sender) {
@ -316,11 +298,11 @@ public final class MessageBuilder {
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP)).append(space())
.append(componentFactory.titleNumber(statListSize)).append(space())
.append(getStatNameComponent(statRequest)) //space is provided by statUnitComponent
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender()))
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget()))
.build();
}
private TextComponent getTopStatsTitleShortComponent(StatRequest statRequest, int statListSize) {
private TextComponent getTopStatDescription(StatRequest statRequest, int statListSize) {
return Component.text()
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP)).append(space())
.append(componentFactory.titleNumber(statListSize)).append(space())
@ -330,32 +312,38 @@ public final class MessageBuilder {
private TextComponent getTopStatListComponent(LinkedHashMap<String, Integer> topStats, StatRequest statRequest) {
TextComponent.Builder topList = Component.text();
boolean useDots = config.useDots();
boolean boldNames = config.playerNameIsBold();
Set<String> playerNames = topStats.keySet();
boolean useDots = config.useDots();
int count = 0;
for (String playerName : playerNames) {
TextComponent.Builder playerNameBuilder = componentFactory.playerName(playerName, Target.TOP).toBuilder();
topList.append(newline())
.append(componentFactory.rankNumber(" " + ++count + "."))
.append(space())
.append(componentFactory.rankNumber(++count))
.append(space());
if (useDots) {
topList.append(playerNameBuilder)
.append(space());
int dots = FontUtils.getNumberOfDotsToAlign(count + ". " + playerName, statRequest.isConsoleSender(), boldNames);
if (dots >= 1) {
topList.append(componentFactory.dots().append(text((".".repeat(dots)))));
}
topList.append(getPlayerNameWithDotsComponent(count, playerName));
}
else {
topList.append(playerNameBuilder.append(text(":")));
topList.append(componentFactory.playerName(playerName + ":", Target.TOP));
}
topList.append(space()).append(getStatNumberComponent(statRequest, topStats.get(playerName)));
}
return topList.build();
}
private TextComponent getPlayerNameWithDotsComponent(int positionInTopList, String playerName) {
int dots = FontUtils.getNumberOfDotsToAlign(positionInTopList + ". " + playerName, isConsoleBuilder, config.playerNameIsBold());
TextComponent.Builder nameWithDots = Component.text()
.append(componentFactory.playerName(playerName, Target.TOP))
.append(space());
if (dots >= 1) {
nameWithDots.append(componentFactory.dots().append(text(".".repeat(dots))));
}
return nameWithDots.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 statRequest) {
@ -392,6 +380,12 @@ public final class MessageBuilder {
};
}
private TextComponent getStatNumberComponent(Unit unit, Target target, long statNumber) {
return switch (unit.getType()) {
case DISTANCE ->
}
}
private TextComponent getDistanceNumberComponent(long statNumber, Target target) {
Unit statUnit = Unit.fromString(config.getDistanceUnit(false));
String prettyNumber = formatter.formatDistanceNumber(statNumber, statUnit);
@ -450,9 +444,9 @@ public final class MessageBuilder {
return componentFactory.statNumber(formatter.formatNumber(statNumber), target);
}
private TextComponent getStatUnitComponent(Statistic statistic, Target target, boolean isConsoleSender) {
private TextComponent getStatUnitComponent(Statistic statistic, Target target) {
return switch (Unit.getTypeFromStatistic(statistic)) {
case DAMAGE -> getDamageUnit(target, isConsoleSender);
case DAMAGE -> getDamageUnit(target);
case DISTANCE -> getDistanceUnit(target);
default -> Component.empty();
};
@ -471,11 +465,11 @@ public final class MessageBuilder {
.append(componentFactory.statUnit(statUnit.getLabel(), target));
}
private TextComponent getDamageUnit(Target target, boolean isConsoleSender) {
private TextComponent getDamageUnit(Target target) {
Unit statUnit = Unit.fromString(config.getDamageUnit(false));
if (statUnit == Unit.HEART) {
TextComponent heartUnit;
if (isConsoleSender) {
if (isConsoleBuilder) {
heartUnit = componentFactory.consoleHeart();
} else if (useHoverText) {
heartUnit = componentFactory.clientHeartWithHoverText();
@ -489,6 +483,44 @@ public final class MessageBuilder {
.append(componentFactory.statUnit(statUnit.getLabel(), target));
}
private BiFunction<Integer, CommandSender, TextComponent> getFormattingFunction(@NotNull TextComponent statResult, Target target) {
boolean useEnters = config.useEnters(target, false);
boolean useEntersForShared = config.useEnters(target, true);
return (shareCode, sender) -> {
TextComponent.Builder statBuilder = text();
//if we're adding a share-button
if (shareCode != null) {
if (useEnters) {
statBuilder.append(newline());
}
statBuilder.append(statResult)
.append(space())
.append(componentFactory.shareButton(shareCode));
}
//if we're adding a "shared by" component
else if (sender != null) {
if (useEntersForShared) {
statBuilder.append(newline());
}
statBuilder.append(statResult)
.append(newline())
.append(componentFactory.sharedByMessage(
getSharerNameComponent(sender)));
}
//if we're not adding a share-button or a "shared by" component
else {
if (useEnters) {
statBuilder.append(newline());
}
statBuilder.append(statResult);
}
return statBuilder.build();
};
}
/** Get an ArrayList consisting of 2 or 4 timeUnits. The order of items is:
<p>0. maxUnit</p>
<p>1. minUnit</p>

View File

@ -1,9 +1,11 @@
package com.gmail.artemis.the.gr8.playerstats.msg;
import com.gmail.artemis.the.gr8.playerstats.ShareManager;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import com.gmail.artemis.the.gr8.playerstats.api.Formatter;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.gmail.artemis.the.gr8.playerstats.enums.Unit;
import com.gmail.artemis.the.gr8.playerstats.msg.components.ComponentFactory;
import com.gmail.artemis.the.gr8.playerstats.statistic.request.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.components.BukkitConsoleComponentFactory;
import com.gmail.artemis.the.gr8.playerstats.msg.components.PrideComponentFactory;
@ -28,7 +30,7 @@ import static com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage.*;
/** This class manages all PlayerStats output. It is the only place where messages are sent.
It gets the messages from a {@link MessageBuilder}, which is different for a Console as for Players
(mainly to deal with the lack of hover-text, and for Bukkit consoles to make up for the lack of hex-colors).*/
public final class OutputManager implements StatFormatter {
public final class OutputManager implements Formatter {
private static BukkitAudiences adventure;
private static ConfigHandler config;
@ -51,6 +53,35 @@ public final class OutputManager implements StatFormatter {
getMessageBuilders();
}
@Override
public TextComponent getPluginPrefix() {
ComponentFactory factory = new ComponentFactory(config);
return factory.pluginPrefix();
}
@Override
public TextComponent getRainbowPluginPrefix() {
ComponentFactory prideFactory = new PrideComponentFactory(config);
return prideFactory.pluginPrefix();
}
@Override
public TextComponent getPluginPrefixAsTitle() {
ComponentFactory factory = new ComponentFactory(config);
return factory.pluginPrefixAsTitle();
}
@Override
public TextComponent getRainbowPluginPrefixAsTitle() {
ComponentFactory prideFactory = new PrideComponentFactory(config);
return prideFactory.pluginPrefixAsTitle();
}
@Override
public TextComponent formatSingleTopStatLine(int positionInTopList, String playerName, long statNumber, Unit statNumberUnit) {
return null;
}
@Override
public TextComponent formatPlayerStat(@NotNull StatRequest statRequest, int playerStat) {
BiFunction<Integer, CommandSender, TextComponent> playerStatFunction =
@ -108,7 +139,7 @@ public final class OutputManager implements StatFormatter {
public void sendHelp(@NotNull CommandSender sender) {
adventure.sender(sender).sendMessage(getMessageBuilder(sender)
.helpMsg(sender instanceof ConsoleCommandSender));
.helpMsg();
}
public void sendToAllPlayers(@NotNull TextComponent component) {
@ -165,6 +196,7 @@ public final class OutputManager implements StatFormatter {
} else {
consoleBuilder = getClientMessageBuilder();
}
consoleBuilder.setConsoleBuilder(true);
consoleBuilder.toggleHoverUse(false);
return consoleBuilder;
}

View File

@ -121,8 +121,8 @@ public class ComponentFactory {
getStyleFromString(config.getTitleNumberDecoration(true)));
}
public TextComponent rankNumber(String number) {
return getComponent(number,
public TextComponent rankNumber(int number) {
return getComponent(number + ".",
getColorFromString(config.getRankNumberDecoration(false)),
getStyleFromString(config.getRankNumberDecoration(true)));
}

View File

@ -65,7 +65,7 @@ public final class StatThread extends Thread {
case SERVER -> outputManager.formatServerStat(statRequest, statManager.getServerStat(statRequest));
};
if (statRequest.isAPIRequest()) {
String msg = StatFormatter.statResultComponentToString(statResult);
String msg = StatFormatter.TextComponentToString(statResult);
statRequest.getCommandSender().sendMessage(msg);
}
else {

View File

@ -24,6 +24,6 @@ public record InternalStatResult(String executorName, TextComponent statResult,
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(statResult);
return StatFormatter.TextComponentToString(statResult);
}
}

View File

@ -17,6 +17,6 @@ public record PlayerStatResult(int value, TextComponent formattedValue) implemen
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedValue);
return StatFormatter.TextComponentToString(formattedValue);
}
}

View File

@ -17,6 +17,6 @@ public record ServerStatResult(long value, TextComponent formattedStatResult) im
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedStatResult);
return StatFormatter.TextComponentToString(formattedStatResult);
}
}

View File

@ -13,8 +13,8 @@ import net.kyori.adventure.text.TextComponent;
<li> <code>LinkedHashMap(String, Integer)</code> for topStat
</ul>
You can get the raw numbers with {@link #getNumericalValue()}. Additionally,
you can get a formatted message that includes formatted numbers. This can either
be a String or a {@link TextComponent}, and contains the following information:
you can get the default formatted message that includes formatted numbers. This can
either be a String or a {@link TextComponent}, and contains the following information:
<ul>
<li> for playerStat:
<br> [player-name]: [formatted-number] [stat-name] [sub-stat-name]
@ -26,7 +26,10 @@ import net.kyori.adventure.text.TextComponent;
<br> [2.] [player-name] [.....] [formatted-number]
<br> [3.] etc...
</ul>
The TextComponents can be sent directly to a Minecraft client or console with the
If you want the results to be formatted differently, you can get an instance of
the {@link com.gmail.artemis.the.gr8.playerstats.api.Formatter}.
Resulting TextComponents can be sent directly to a Minecraft client or console with the
Adventure library. To send a Component, you need to get a {@link BukkitAudiences} object,
and use that to send the desired Component. Normally you would have to add Adventure
as a dependency to your project, but since the library is included in PlayerStats, you can

View File

@ -19,6 +19,6 @@ public record TopStatResult(LinkedHashMap<String, Integer> value, TextComponent
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedStatResult);
return StatFormatter.TextComponentToString(formattedStatResult);
}
}