Reworked the API again to make it more user-friendly, and I think I'm finally happy with the result! (#99, #98, #100)

This commit is contained in:
Artemis-the-gr8 2022-07-28 02:50:59 +02:00
parent 2b73e4f183
commit f2b09198b0
27 changed files with 362 additions and 310 deletions

View File

@ -27,7 +27,6 @@ public final class Main extends JavaPlugin {
private static OutputManager outputManager;
private static RequestManager requestManager;
private static ShareManager shareManager;
private static StatManager statManager;
private static ThreadManager threadManager;
@ -89,7 +88,7 @@ public final class Main extends JavaPlugin {
adventure = BukkitAudiences.create(this);
shareManager = new ShareManager(config);
statManager = new StatManager(offlinePlayerHandler, config.getTopListMaxSize());
StatManager statManager = new StatManager(offlinePlayerHandler, config.getTopListMaxSize());
outputManager = new OutputManager(getAdventure(), config, shareManager);
requestManager = new RequestManager(enumHandler, offlinePlayerHandler, outputManager);
threadManager = new ThreadManager(config, statManager, outputManager);

View File

@ -2,7 +2,7 @@ package com.gmail.artemis.the.gr8.playerstats;
import com.gmail.artemis.the.gr8.playerstats.config.ConfigHandler;
import com.gmail.artemis.the.gr8.playerstats.enums.DebugLevel;
import com.gmail.artemis.the.gr8.playerstats.models.StatResult;
import com.gmail.artemis.the.gr8.playerstats.models.InternalStatResult;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.command.CommandSender;
@ -27,7 +27,7 @@ public final class ShareManager {
private static int waitingTime;
private static volatile AtomicInteger resultID;
private static ConcurrentHashMap<Integer, StatResult> statResultQueue;
private static ConcurrentHashMap<Integer, InternalStatResult> statResultQueue;
private static ConcurrentHashMap<String, Instant> shareTimeStamp;
private static ArrayBlockingQueue<Integer> sharedResults;
@ -73,7 +73,7 @@ public final class ShareManager {
int ID = getNextIDNumber();
//UUID shareCode = UUID.randomUUID();
StatResult result = new StatResult(playerName, statResult, ID);
InternalStatResult result = new InternalStatResult(playerName, statResult, ID);
int shareCode = result.hashCode();
statResultQueue.put(shareCode, result);
MyLogger.logMsg("Saving statResults with no. " + ID, DebugLevel.MEDIUM);
@ -97,7 +97,7 @@ public final class ShareManager {
puts the current time in the shareTimeStamp (ConcurrentHashMap),
puts the shareCode (int hashCode) in the sharedResults (ArrayBlockingQueue),
and returns the statResult. If no statResult was found, returns null.*/
public @Nullable StatResult getStatResult(String playerName, int shareCode) {
public @Nullable InternalStatResult getStatResult(String playerName, int shareCode) {
if (statResultQueue.containsKey(shareCode)) {
shareTimeStamp.put(playerName, Instant.now());
@ -126,7 +126,7 @@ public final class ShareManager {
/** If the given player already has more than x (in this case 25) StatResults saved,
remove the oldest one.*/
private void removeExcessResults(String playerName) {
List<StatResult> alreadySavedResults = statResultQueue.values()
List<InternalStatResult> alreadySavedResults = statResultQueue.values()
.parallelStream()
.filter(result -> result.executorName().equalsIgnoreCase(playerName))
.toList();
@ -134,7 +134,7 @@ public final class ShareManager {
if (alreadySavedResults.size() > 25) {
int hashCode = alreadySavedResults
.parallelStream()
.min(Comparator.comparing(StatResult::ID))
.min(Comparator.comparing(InternalStatResult::ID))
.orElseThrow().hashCode();
MyLogger.logMsg("Removing old stat no. " + statResultQueue.get(hashCode).ID() + " for player " + playerName, DebugLevel.MEDIUM);
statResultQueue.remove(hashCode);

View File

@ -2,9 +2,9 @@ package com.gmail.artemis.the.gr8.playerstats;
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.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
import com.gmail.artemis.the.gr8.playerstats.reload.ReloadThread;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatManager;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
@ -61,19 +61,19 @@ public final class ThreadManager {
}
}
public void startStatThread(StatRequest request) {
public void startStatThread(StatRequest statRequest) {
statThreadID += 1;
String cmdSender = request.getCommandSender().getName();
String cmdSender = statRequest.getCommandSender().getName();
if (config.limitStatRequests() && statThreads.containsKey(cmdSender)) {
Thread runningThread = statThreads.get(cmdSender);
if (runningThread.isAlive()) {
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.REQUEST_ALREADY_RUNNING);
outputManager.sendFeedbackMsg(statRequest.getCommandSender(), StandardMessage.REQUEST_ALREADY_RUNNING);
} else {
startNewStatThread(request);
startNewStatThread(statRequest);
}
} else {
startNewStatThread(request);
startNewStatThread(statRequest);
}
}
@ -89,9 +89,9 @@ public final class ThreadManager {
return lastRecordedCalcTime;
}
private void startNewStatThread(StatRequest request) {
lastActiveStatThread = new StatThread(outputManager, statManager, statThreadID, request, lastActiveReloadThread);
statThreads.put(request.getCommandSender().getName(), lastActiveStatThread);
private void startNewStatThread(StatRequest statRequest) {
lastActiveStatThread = new StatThread(outputManager, statManager, statThreadID, statRequest, lastActiveReloadThread);
statThreads.put(statRequest.getCommandSender().getName(), lastActiveStatThread);
lastActiveStatThread.start();
}
}

View File

@ -1,7 +1,10 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.models.StatResult;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.EntityType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -23,15 +26,9 @@ public interface PlayerStats {
return Main.getPlayerStatsAPI();
}
/** The {@link StatCalculator} is responsible for getting, calculating and/or ordering raw numbers.
It gets its data from the vanilla statistic files (stored by the server). It can return three kinds of data,
depending on the chosen {@link Target}:
<br>- int (for {@link Target#PLAYER})
<br>- long (for {@link Target#SERVER})
<br>- LinkedHashMap[String player-name, Integer number] (for {@link Target#TOP})*/
StatCalculator statCalculator();
StatResult<?> getPlayerStat(String playerName, Statistic statistic, Material material, EntityType entity);
RequestGenerator requestGenerator();
StatResult<?> getServerStat(Statistic statistic, Material material, EntityType entity);
StatFormatter statFormatter();
StatResult<?> getTopStats(Statistic statistic, Material material, EntityType entity);
}

View File

@ -1,36 +1,61 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.models.*;
import com.gmail.artemis.the.gr8.playerstats.statistic.RequestManager;
import com.gmail.artemis.the.gr8.playerstats.statistic.StatManager;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.entity.EntityType;
import java.util.LinkedHashMap;
import static org.jetbrains.annotations.ApiStatus.Internal;
/** The implementation of the API Interface */
public final class PlayerStatsAPI implements PlayerStats {
private static RequestManager requestManager;
private static StatManager statManager;
private static RequestGenerator requestGenerator;
private static StatCalculator statCalculator;
private static StatFormatter statFormatter;
@Internal
public PlayerStatsAPI(RequestManager request, StatManager stat, StatFormatter format) {
requestManager = request;
statManager = stat;
PlayerStatsAPI.requestGenerator = request;
statCalculator = stat;
statFormatter = format;
}
@Override
public StatCalculator statCalculator() {
return statManager;
public StatResult<Integer> getPlayerStat(String playerName, Statistic statistic, Material material, EntityType entity) {
StatRequest request = getStatRequest(Target.PLAYER, statistic, material, entity, playerName);
int stat = statCalculator.getPlayerStat(request);
TextComponent prettyStat = statFormatter.formatPlayerStat(request, stat);
return new PlayerStatResult(stat, prettyStat);
}
@Override
public RequestGenerator requestGenerator() {
return requestManager;
public StatResult<?> getServerStat(Statistic statistic, Material material, EntityType entity) {
StatRequest request = getStatRequest(Target.SERVER, statistic, material, entity, null);
long stat = statCalculator.getServerStat(request);
TextComponent prettyStat = statFormatter.formatServerStat(request, stat);
return new ServerStatResult(stat, prettyStat);
}
@Override
public StatFormatter statFormatter() {
return statFormatter;
public StatResult<?> getTopStats(Statistic statistic, Material material, EntityType entity) {
StatRequest request = getStatRequest(Target.TOP, statistic, material, entity, null);
LinkedHashMap<String, Integer> stat = statCalculator.getTopStats(request);
TextComponent prettyStat = statFormatter.formatTopStat(request, stat);
return new TopStatResult(stat, prettyStat);
}
private StatRequest getStatRequest(Target target, Statistic statistic, Material material, EntityType entity, String playerName) throws NullPointerException {
StatRequest request = requestGenerator.generateAPIRequest(target, statistic, material, entity, playerName);
if (requestGenerator.validateAPIRequest(request)) {
return request;
}
throw new NullPointerException("The parameters you supplied did not result in a valid stat-request!");
}
}

View File

@ -1,5 +1,6 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import org.bukkit.Material;
import org.bukkit.Statistic;
@ -15,18 +16,19 @@ public interface RequestGenerator {
as CommandSender. This CommandSender will receive feedback messages if the StatRequest could not be created.
@param args an Array of args such as a CommandSender would put in Minecraft chat:
<p>- a stat-name (example: "mine_block")</p>
<p>- if applicable, a sub-stat-name (example: diorite)(</p>
<p>- a target for this lookup: can be "top", "server", "player" (or "me" to indicate the current CommandSender)</p>
<p>- if "player" was chosen, include a player-name</p>
<p>- a <code>statName</code> (example: "mine_block")</p>
<p>- if applicable, a <code>subStatEntryName</code> (example: diorite)(</p>
<p>- a <code>target</code> for this lookup: can be "top", "server", "player" (or "me" to indicate the current CommandSender)</p>
<p>- if "player" was chosen, include a <code>playerName</code></p>
@param sender the CommandSender that requested this specific statistic
@return the generated StatRequest
@throws IllegalArgumentException if the args do not result in a valid statistic look-up*/
*/
StatRequest generateRequest(CommandSender sender, String[] args);
StatRequest createPlayerStatRequest(String playerName, Statistic statistic, Material material, EntityType entity);
StatRequest generateAPIRequest(Target target, Statistic statistic, Material material, EntityType entity, String playerName);
StatRequest createServerStatRequest(Statistic statistic, Material material, EntityType entityType);
boolean validateRequest(StatRequest request);
StatRequest createTopStatRequest(Statistic statistic, Material material, EntityType entityType);
boolean validateAPIRequest(StatRequest request);
}

View File

@ -1,20 +1,30 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import java.util.LinkedHashMap;
/** The {@link StatCalculator} represents the actual statistic-getting magic that happens once a valid
{@link StatRequest} is passed to it. It takes a valid StatRequest, and returns (a map of) numbers.
For more information on how to create a valid StatRequest, see the class description for {@link StatRequest}.*/
/** The {@link StatCalculator} is responsible for getting, calculating and/or ordering raw numbers.
It represents the actual statistic-getting magic that happens once a valid
{@link StatRequest} is passed to it.
<br>
<br>The StatCalculator gets its data from the vanilla statistic files (stored by the server). It can return three kinds of data,
depending on the chosen {@link Target}:
<br>- int (for {@link Target#PLAYER})
<br>- long (for {@link Target#SERVER})
<br>- LinkedHashMap[String player-name, Integer number] (for {@link Target#TOP})
<br>
<br>For more information on how to create a valid StatRequest,
see the class description for {@link StatRequest}.*/
public interface StatCalculator {
/** Returns the requested Statistic*/
int getPlayerStat(StatRequest request);
int getPlayerStat(StatRequest statRequest);
/** Don't call from main Thread!*/
long getServerStat(StatRequest request);
long getServerStat(StatRequest statRequest);
/** Don't call from main Thread!*/
LinkedHashMap<String, Integer> getTopStats(StatRequest request);
}
LinkedHashMap<String, Integer> getTopStats(StatRequest statRequest);
}

View File

@ -1,6 +1,7 @@
package com.gmail.artemis.the.gr8.playerstats.api;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.ComponentUtils;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.*;
@ -28,20 +29,23 @@ public interface StatFormatter {
@return a String representation of this TextComponent, without hover/click events,
but with color, style and formatting. TranslatableComponents will be turned into
plain English.*/
String statResultComponentToString(TextComponent statResult);
static String statResultComponentToString(TextComponent statResult) {
return ComponentUtils.getTranslatableComponentSerializer()
.serialize(statResult);
}
/** @return a TextComponent with the following parts:
<br>[player-name]: [number] [stat-name] {sub-stat-name}*/
TextComponent formatPlayerStat(StatRequest request, int playerStat);
TextComponent formatPlayerStat(StatRequest statRequest, int playerStat);
/** @return a TextComponent with the following parts:
<br>[Total on] [server-name]: [number] [stat-name] [sub-stat-name]*/
TextComponent formatServerStat(StatRequest request, long serverStat);
TextComponent formatServerStat(StatRequest statRequest, long serverStat);
/** @return a TextComponent with the following parts:
<br>[PlayerStats] [Top 10] [stat-name] [sub-stat-name]
<br> [1.] [player-name] [number]
<br> [2.] [player-name] [number]
<br> [3.] etc...*/
TextComponent formatTopStat(StatRequest request, LinkedHashMap<String, Integer> topStats);
TextComponent formatTopStat(StatRequest statRequest, LinkedHashMap<String, Integer> topStats);
}

View File

@ -7,7 +7,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ReloadCommand implements CommandExecutor {
public final class ReloadCommand implements CommandExecutor {
private static ThreadManager threadManager;

View File

@ -2,7 +2,7 @@ package com.gmail.artemis.the.gr8.playerstats.commands;
import com.gmail.artemis.the.gr8.playerstats.ShareManager;
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.gmail.artemis.the.gr8.playerstats.models.StatResult;
import com.gmail.artemis.the.gr8.playerstats.models.InternalStatResult;
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import org.bukkit.command.Command;
@ -10,7 +10,7 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class ShareCommand implements CommandExecutor {
public final class ShareCommand implements CommandExecutor {
private static ShareManager shareManager;
private static OutputManager outputManager;
@ -37,7 +37,7 @@ public class ShareCommand implements CommandExecutor {
outputManager.sendFeedbackMsg(sender, StandardMessage.STILL_ON_SHARE_COOLDOWN);
}
else {
StatResult result = shareManager.getStatResult(sender.getName(), shareCode);
InternalStatResult result = shareManager.getStatResult(sender.getName(), shareCode);
if (result == null) { //at this point the only possible cause of statResult being null is the request being older than 25 player-requests ago
outputManager.sendFeedbackMsg(sender, StandardMessage.STAT_RESULTS_TOO_OLD);
} else {

View File

@ -1,24 +1,16 @@
package com.gmail.artemis.the.gr8.playerstats.commands;
import com.gmail.artemis.the.gr8.playerstats.Main;
import com.gmail.artemis.the.gr8.playerstats.ThreadManager;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.OutputManager;
import com.gmail.artemis.the.gr8.playerstats.statistic.RequestManager;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TranslatableComponent;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
public class StatCommand implements CommandExecutor {
public final class StatCommand implements CommandExecutor {
private static ThreadManager threadManager;
private static OutputManager outputManager;
@ -39,35 +31,10 @@ public class StatCommand implements CommandExecutor {
args[0].equalsIgnoreCase("example")) { //in case of "statistic examples", show examples
outputManager.sendExamples(sender);
}
else if (args[0].equalsIgnoreCase(">:(")) {
Component msg = MiniMessage.miniMessage().deserialize("<gradient:#f74040:#FF6600:#f74040>fire demon</gradient>");
String msgString = LegacyComponentSerializer.builder().hexColors().build().serialize(msg);
sender.sendMessage("LCS.hexColors(): " + msgString);
MyLogger.logMsg(msgString);
String msgString2 = LegacyComponentSerializer.legacySection().serialize(msg);
sender.sendMessage("LCS.legacySection: " + msgString2);
MyLogger.logMsg(msgString2);
//only this one works both in-game and in-console
String msgString3 = LegacyComponentSerializer.builder().hexColors().useUnusualXRepeatedCharacterHexFormat().build().serialize(msg);
sender.sendMessage("LCS.hexColors().spigotformat...: " + msgString3);
MyLogger.logMsg(msgString3);
}
else if (args[0].equalsIgnoreCase("test")) {
TranslatableComponent msg = Component.translatable(Statistic.ANIMALS_BRED.getKey().getNamespace() + "." + Statistic.ANIMALS_BRED.getKey().getKey());
TranslatableComponent msg2 = Component.translatable("stat." + Statistic.ANIMALS_BRED.getKey().getNamespace() + "." + Statistic.ANIMALS_BRED.getKey().getKey());
Main.getAdventure().console().sendMessage(msg);
Main.getAdventure().console().sendMessage(msg2);
MyLogger.logMsg("key to String: " + Statistic.KILL_ENTITY.getKey());
MyLogger.logMsg("key.getNamespace(): " + Statistic.KILL_ENTITY.getKey().getNamespace());
MyLogger.logMsg("key.getKey(): " + Statistic.KILL_ENTITY.getKey().getKey());
}
else {
StatRequest request = requestManager.generateRequest(sender, args);
if (requestManager.validateRequest(request)) {
threadManager.startStatThread(request);
StatRequest statRequest = requestManager.generateRequest(sender, args);
if (requestManager.validateRequest(statRequest)) {
threadManager.startStatThread(statRequest);
} else {
return false;
}

View File

@ -12,7 +12,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class TabCompleter implements org.bukkit.command.TabCompleter {
public final class TabCompleter implements org.bukkit.command.TabCompleter {
private final EnumHandler enumHandler;
private final OfflinePlayerHandler offlinePlayerHandler;

View File

@ -11,7 +11,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
public class ConfigHandler {
public final class ConfigHandler {
private static Main plugin;
private static int configVersion;

View File

@ -9,7 +9,7 @@ import java.io.IOException;
import com.tchristofferson.configupdater.ConfigUpdater;
public class ConfigUpdateHandler {
public final class ConfigUpdateHandler {
/** Add new key-value pairs to the config without losing comments, using <a href="https://github.com/tchristofferson/Config-Updater">tchristofferson's Config-Updater</a> */
public ConfigUpdateHandler(Main plugin, File configFile, int configVersion) {

View File

@ -0,0 +1,29 @@
package com.gmail.artemis.the.gr8.playerstats.models;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import net.kyori.adventure.text.TextComponent;
/** This Record is used to store stat-results internally, so Players can share them by clicking a share-button.*/
public record InternalStatResult(String executorName, TextComponent statResult, int ID) implements StatResult<Integer> {
/** Gets the ID number for this StatResult. Unlike for the other {@link StatResult} implementations,
this one does not return the actual statistic data, because this implementation is meant for internal
saving-and-sharing only. This method is only for Interface-consistency, InternalStatResult#ID is better.
@return Integer that represents this StatResult's ID number
*/
@Override
public Integer getNumericalValue() {
return ID;
}
@Override
public TextComponent getFormattedTextComponent() {
return statResult;
}
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(statResult);
}
}

View File

@ -0,0 +1,22 @@
package com.gmail.artemis.the.gr8.playerstats.models;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import net.kyori.adventure.text.TextComponent;
public record PlayerStatResult(int value, TextComponent formattedValue) implements StatResult<Integer> {
@Override
public Integer getNumericalValue() {
return value;
}
@Override
public TextComponent getFormattedTextComponent() {
return formattedValue;
}
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedValue);
}
}

View File

@ -0,0 +1,22 @@
package com.gmail.artemis.the.gr8.playerstats.models;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import net.kyori.adventure.text.TextComponent;
public record ServerStatResult(long value, TextComponent formattedStatResult) implements StatResult<Long> {
@Override
public Long getNumericalValue() {
return value;
}
@Override
public TextComponent getFormattedTextComponent() {
return formattedStatResult;
}
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedStatResult);
}
}

View File

@ -1,7 +1,7 @@
package com.gmail.artemis.the.gr8.playerstats.models;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.api.RequestGenerator;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import org.bukkit.Material;
import org.bukkit.Statistic;
import org.bukkit.command.CommandSender;
@ -26,7 +26,7 @@ import org.jetbrains.annotations.Nullable;
<li> a {@link Target} <code>target</code> (automatically set for all API-requests)
<li> if the <code>target</code> is Target.Player, a <code>playerName</code> needs to be added
</ul>*/
public class StatRequest {
public final class StatRequest {
private final CommandSender sender;
private boolean isAPIRequest;
@ -84,28 +84,18 @@ public class StatRequest {
return sender instanceof ConsoleCommandSender;
}
/** Set a {@link Statistic} for this StatRequest.*/
public void setStatistic(Statistic statistic) {
this.statistic = statistic;
}
/** If a {@link Statistic} was set, this will return it.
@return the <code>statistic</code> for this RequestGenerator*/
public Statistic getStatistic() {
return statistic;
}
/** Sets the <code>subStatEntryName</code> (a block-, item- or entity-name). */
public void setSubStatEntryName(String subStatEntry) {
this.subStatEntryName = subStatEntry;
}
/** If a {@link Statistic} is set, and this Statistic is of Type Block, Item or Entity,
this will return the name of said block, item or entity
(in the way .toString would for the given enum constant).
@return the <code>subStatEntryName</code>*/
public @Nullable String getSubStatEntryName() {
return subStatEntryName;
}
@ -118,15 +108,10 @@ public class StatRequest {
return playerName;
}
/** False by default, set to true if args[] contains "player". */
public void setPlayerFlag(boolean playerFlag) {
this.playerFlag = playerFlag;
}
/** For internal use. The "player" arg is a special case, because it could either be
a valid <code>subStatEntry</code>, or indicate that the lookup action should target
a specific player. This is why the <code>playerFlag</code> exists - if this flag true,
and <code>executorName</code> is null, the <code>subStatEntry</code> should be set to "player". */
public boolean getPlayerFlag() {
return playerFlag;
}
@ -135,10 +120,6 @@ public class StatRequest {
this.target = target;
}
/** Returns the {@link Target} for this StatRequest.
If no Target is explicitly set, this will return {@link Target#TOP}.
All static factory methods that create a {@link StatRequest} set the
appropriate Target for themselves, so there is no need to manually set the Target.*/
public @NotNull Target getTarget() {
return target;
}

View File

@ -2,6 +2,11 @@ package com.gmail.artemis.the.gr8.playerstats.models;
import net.kyori.adventure.text.TextComponent;
/** This Record is used to store stat-results internally, so Players can share them by clicking a share-button.*/
public record StatResult(String executorName, TextComponent statResult, int ID) {
}
public interface StatResult<T> {
T getNumericalValue();
TextComponent getFormattedTextComponent();
String getFormattedString();
}

View File

@ -0,0 +1,24 @@
package com.gmail.artemis.the.gr8.playerstats.models;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import net.kyori.adventure.text.TextComponent;
import java.util.LinkedHashMap;
public record TopStatResult(LinkedHashMap<String, Integer> value, TextComponent formattedStatResult) implements StatResult<LinkedHashMap<String,Integer>> {
@Override
public LinkedHashMap<String, Integer> getNumericalValue() {
return value;
}
@Override
public TextComponent getFormattedTextComponent() {
return formattedStatResult;
}
@Override
public String getFormattedString() {
return StatFormatter.statResultComponentToString(formattedStatResult);
}
}

View File

@ -5,11 +5,11 @@ 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.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.components.ComponentFactory;
import com.gmail.artemis.the.gr8.playerstats.msg.components.ExampleMessage;
import com.gmail.artemis.the.gr8.playerstats.msg.components.HelpMessage;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.*;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.utils.MyLogger;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@ -28,7 +28,7 @@ import static net.kyori.adventure.text.Component.*;
The component parts (with appropriate formatting) are supplied by a {@link ComponentFactory}.
By default, this class works with the default ComponentFactory, but you can
give it a different ComponentFactory upon creation.*/
public class MessageBuilder {
public final class MessageBuilder {
private static ConfigHandler config;
@ -169,15 +169,15 @@ public class MessageBuilder {
<p>- Integer shareCode: if a shareCode is provided, a clickable "share" button will be added.
<br>- CommandSender sender: if a sender is provided, a signature with "shared by sender-name" will be added.</br>
<br>- If both parameters are null, the statResult will be returned as is.</br>*/
public BiFunction<Integer, CommandSender, TextComponent> formattedPlayerStatFunction(int stat, @NotNull StatRequest request) {
public BiFunction<Integer, CommandSender, TextComponent> formattedPlayerStatFunction(int stat, @NotNull StatRequest statRequest) {
TextComponent playerStat = Component.text()
.append(componentFactory.playerName(request.getPlayerName(), Target.PLAYER)
.append(componentFactory.playerName(statRequest.getPlayerName(), Target.PLAYER)
.append(text(":"))
.append(space()))
.append(getStatNumberComponent(request.getStatistic(), stat, Target.PLAYER, request.isConsoleSender()))
.append(getStatNumberComponent(statRequest.getStatistic(), stat, Target.PLAYER, statRequest.isConsoleSender()))
.append(space())
.append(getStatNameComponent(request))
.append(getStatUnitComponent(request.getStatistic(), request.getTarget(), request.isConsoleSender())) //space is provided by statUnitComponent
.append(getStatNameComponent(statRequest))
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender())) //space is provided by statUnitComponent
.build();
return getFormattingFunction(playerStat, Target.PLAYER);
@ -188,16 +188,16 @@ public class MessageBuilder {
<p>- Integer shareCode: if a shareCode is provided, a clickable "share" button will be added.
<br>- CommandSender sender: if a sender is provided, a signature with "shared by sender-name" will be added.</br>
<br>- If both parameters are null, the statResult will be returned as is.</br>*/
public BiFunction<Integer, CommandSender, TextComponent> formattedServerStatFunction(long stat, @NotNull StatRequest request) {
public BiFunction<Integer, CommandSender, TextComponent> formattedServerStatFunction(long stat, @NotNull StatRequest statRequest) {
TextComponent serverStat = text()
.append(componentFactory.title(config.getServerTitle(), Target.SERVER))
.append(space())
.append(componentFactory.serverName(config.getServerName()))
.append(space())
.append(getStatNumberComponent(request.getStatistic(), stat, Target.SERVER, request.isConsoleSender()))
.append(getStatNumberComponent(statRequest.getStatistic(), stat, Target.SERVER, statRequest.isConsoleSender()))
.append(space())
.append(getStatNameComponent(request))
.append(getStatUnitComponent(request.getStatistic(), request.getTarget(), request.isConsoleSender())) //space is provided by statUnit
.append(getStatNameComponent(statRequest))
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender())) //space is provided by statUnit
.build();
return getFormattingFunction(serverStat, Target.SERVER);
@ -208,10 +208,10 @@ public class MessageBuilder {
<p>- Integer shareCode: if a shareCode is provided, a clickable "share" button will be added.
<br>- CommandSender sender: if a sender is provided, a signature with "shared by sender-name" will be added.</br>
<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 request) {
final TextComponent title = getTopStatsTitleComponent(request, topStats.size());
final TextComponent shortTitle = getTopStatsTitleShortComponent(request, topStats.size());
final TextComponent list = getTopStatListComponent(topStats, request);
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 list = getTopStatListComponent(topStats, statRequest);
final boolean useEnters = config.useEnters(Target.TOP, false);
final boolean useEntersForShared = config.useEnters(Target.TOP, true);
@ -302,25 +302,25 @@ public class MessageBuilder {
return componentFactory.sharerName(sender.getName());
}
private TextComponent getTopStatsTitleComponent(StatRequest request, int statListSize) {
private TextComponent getTopStatsTitleComponent(StatRequest statRequest, int statListSize) {
return Component.text()
.append(componentFactory.pluginPrefix()).append(space())
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP)).append(space())
.append(componentFactory.titleNumber(statListSize)).append(space())
.append(getStatNameComponent(request)) //space is provided by statUnitComponent
.append(getStatUnitComponent(request.getStatistic(), request.getTarget(), request.isConsoleSender()))
.append(getStatNameComponent(statRequest)) //space is provided by statUnitComponent
.append(getStatUnitComponent(statRequest.getStatistic(), statRequest.getTarget(), statRequest.isConsoleSender()))
.build();
}
private TextComponent getTopStatsTitleShortComponent(StatRequest request, int statListSize) {
private TextComponent getTopStatsTitleShortComponent(StatRequest statRequest, int statListSize) {
return Component.text()
.append(componentFactory.title(config.getTopStatsTitle(), Target.TOP)).append(space())
.append(componentFactory.titleNumber(statListSize)).append(space())
.append(getStatNameComponent(request)) //space is provided by statUnitComponent
.append(getStatNameComponent(statRequest)) //space is provided by statUnitComponent
.build();
}
private TextComponent getTopStatListComponent(LinkedHashMap<String, Integer> topStats, StatRequest request) {
private TextComponent getTopStatListComponent(LinkedHashMap<String, Integer> topStats, StatRequest statRequest) {
TextComponent.Builder topList = Component.text();
boolean useDots = config.useDots();
boolean boldNames = config.playerNameIsBold();
@ -335,7 +335,7 @@ public class MessageBuilder {
if (useDots) {
topList.append(playerNameBuilder)
.append(space());
int dots = FontUtils.getNumberOfDotsToAlign(count + ". " + playerName, request.isConsoleSender(), boldNames);
int dots = FontUtils.getNumberOfDotsToAlign(count + ". " + playerName, statRequest.isConsoleSender(), boldNames);
if (dots >= 1) {
topList.append(componentFactory.dots().append(text((".".repeat(dots)))));
}
@ -343,33 +343,33 @@ public class MessageBuilder {
else {
topList.append(playerNameBuilder.append(text(":")));
}
topList.append(space()).append(getStatNumberComponent(request.getStatistic(), topStats.get(playerName), Target.TOP, request.isConsoleSender()));
topList.append(space()).append(getStatNumberComponent(statRequest.getStatistic(), topStats.get(playerName), Target.TOP, statRequest.isConsoleSender()));
}
return topList.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) {
private TextComponent getStatNameComponent(StatRequest statRequest) {
if (config.useTranslatableComponents()) {
String statKey = languageKeyHandler.getStatKey(request.getStatistic());
String subStatKey = request.getSubStatEntryName();
String statKey = languageKeyHandler.getStatKey(statRequest.getStatistic());
String subStatKey = statRequest.getSubStatEntryName();
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());
switch (statRequest.getStatistic().getType()) {
case BLOCK -> subStatKey = languageKeyHandler.getBlockKey(statRequest.getBlock());
case ENTITY -> subStatKey = languageKeyHandler.getEntityKey(statRequest.getEntity());
case ITEM -> subStatKey = languageKeyHandler.getItemKey(statRequest.getItem());
default -> {
}
}
}
return componentFactory.statAndSubStatNameTranslatable(statKey, subStatKey, request.getTarget());
return componentFactory.statAndSubStatNameTranslatable(statKey, subStatKey, statRequest.getTarget());
}
else {
return componentFactory.statAndSubStatName(
StringUtils.prettify(request.getStatistic().toString()),
StringUtils.prettify(request.getSubStatEntryName()),
request.getTarget());
StringUtils.prettify(statRequest.getStatistic().toString()),
StringUtils.prettify(statRequest.getSubStatEntryName()),
statRequest.getTarget());
}
}

View File

@ -7,7 +7,6 @@ import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
import com.gmail.artemis.the.gr8.playerstats.msg.components.BukkitConsoleComponentFactory;
import com.gmail.artemis.the.gr8.playerstats.msg.components.PrideComponentFactory;
import com.gmail.artemis.the.gr8.playerstats.msg.msgutils.ComponentUtils;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit;
@ -51,33 +50,27 @@ public final class OutputManager implements StatFormatter {
}
@Override
public String statResultComponentToString(TextComponent statResult) {
return ComponentUtils.getTranslatableComponentSerializer()
.serialize(statResult);
}
@Override
public TextComponent formatPlayerStat(@NotNull StatRequest request, int playerStat) {
public TextComponent formatPlayerStat(@NotNull StatRequest statRequest, int playerStat) {
BiFunction<Integer, CommandSender, TextComponent> playerStatFunction =
getWriter(request).formattedPlayerStatFunction(playerStat, request);
getWriter(statRequest).formattedPlayerStatFunction(playerStat, statRequest);
return processFunction(request.getCommandSender(), playerStatFunction);
return processFunction(statRequest.getCommandSender(), playerStatFunction);
}
@Override
public TextComponent formatServerStat(@NotNull StatRequest request, long serverStat) {
public TextComponent formatServerStat(@NotNull StatRequest statRequest, long serverStat) {
BiFunction<Integer, CommandSender, TextComponent> serverStatFunction =
getWriter(request).formattedServerStatFunction(serverStat, request);
getWriter(statRequest).formattedServerStatFunction(serverStat, statRequest);
return processFunction(request.getCommandSender(), serverStatFunction);
return processFunction(statRequest.getCommandSender(), serverStatFunction);
}
@Override
public TextComponent formatTopStat(@NotNull StatRequest request, @NotNull LinkedHashMap<String, Integer> topStats) {
public TextComponent formatTopStat(@NotNull StatRequest statRequest, @NotNull LinkedHashMap<String, Integer> topStats) {
BiFunction<Integer, CommandSender, TextComponent> topStatFunction =
getWriter(request).formattedTopStatFunction(topStats, request);
getWriter(statRequest).formattedTopStatFunction(topStats, statRequest);
return processFunction(request.getCommandSender(), topStatFunction);
return processFunction(statRequest.getCommandSender(), topStatFunction);
}
public void sendFeedbackMsg(@NotNull CommandSender sender, StandardMessage message) {
@ -143,8 +136,8 @@ public final class OutputManager implements StatFormatter {
return sender instanceof ConsoleCommandSender ? consoleWriter : writer;
}
private MessageBuilder getWriter(StatRequest request) {
if (request.isAPIRequest() || !request.isConsoleSender()) {
private MessageBuilder getWriter(StatRequest statRequest) {
if (statRequest.isAPIRequest() || !statRequest.isConsoleSender()) {
return writer;
} else {
return consoleWriter;

View File

@ -22,7 +22,7 @@ import java.util.concurrent.ForkJoinPool;
import java.util.function.Predicate;
/** The Thread that is in charge of reloading PlayerStats. */
public class ReloadThread extends Thread {
public final class ReloadThread extends Thread {
private static ConfigHandler config;
private static OutputManager outputManager;

View File

@ -16,126 +16,98 @@ import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public final class RequestManager extends StatRequest implements RequestGenerator {
public final class RequestManager implements RequestGenerator {
private final EnumHandler enumHandler;
private final OfflinePlayerHandler offlinePlayerHandler;
private static OutputManager outputManager;
public RequestManager(EnumHandler enumHandler, OfflinePlayerHandler offlinePlayerHandler, OutputManager outputManager) {
super(Bukkit.getConsoleSender());
this.enumHandler = enumHandler;
this.offlinePlayerHandler = offlinePlayerHandler;
RequestManager.outputManager = outputManager;
}
/** This will create a {@link StatRequest} from the provided args, with the requesting Player (or Console)
as CommandSender. This CommandSender will receive feedback messages if the StatRequest could not be created.
@param args an Array of args such as a CommandSender would put in Minecraft chat:
<p>- a <code>statName</code> (example: "mine_block")</p>
<p>- if applicable, a <code>subStatEntryName</code> (example: diorite)(</p>
<p>- a <code>target</code> for this lookup: can be "top", "server", "player" (or "me" to indicate the current CommandSender)</p>
<p>- if "player" was chosen, include a <code>playerName</code></p>
@param sender the CommandSender that requested this specific statistic
@return the generated StatRequest
*/
public StatRequest generateRequest(CommandSender sender, String[] args) {
StatRequest request = new StatRequest(sender);
StatRequest statRequest = new StatRequest(sender);
for (String arg : args) {
//check for statName
if (enumHandler.isStatistic(arg) && request.getStatistic() == null) {
request.setStatistic(EnumHandler.getStatEnum(arg));
if (enumHandler.isStatistic(arg) && statRequest.getStatistic() == null) {
statRequest.setStatistic(EnumHandler.getStatEnum(arg));
}
//check for subStatEntry and playerFlag
else if (enumHandler.isSubStatEntry(arg)) {
if (arg.equalsIgnoreCase("player") && !request.getPlayerFlag()) {
request.setPlayerFlag(true);
if (arg.equalsIgnoreCase("player") && !statRequest.getPlayerFlag()) {
statRequest.setPlayerFlag(true);
}
else {
if (request.getSubStatEntryName() == null) request.setSubStatEntryName(arg);
if (statRequest.getSubStatEntryName() == null) statRequest.setSubStatEntryName(arg);
}
}
//check for selection
else if (arg.equalsIgnoreCase("top")) {
request.setTarget(Target.TOP);
statRequest.setTarget(Target.TOP);
}
else if (arg.equalsIgnoreCase("server")) {
request.setTarget(Target.SERVER);
statRequest.setTarget(Target.SERVER);
}
else if (arg.equalsIgnoreCase("me")) {
if (sender instanceof Player) {
request.setPlayerName(sender.getName());
request.setTarget(Target.PLAYER);
statRequest.setPlayerName(sender.getName());
statRequest.setTarget(Target.PLAYER);
}
else if (sender instanceof ConsoleCommandSender) {
request.setTarget(Target.SERVER);
statRequest.setTarget(Target.SERVER);
}
}
else if (offlinePlayerHandler.isRelevantPlayer(arg) && request.getPlayerName() == null) {
request.setPlayerName(arg);
request.setTarget(Target.PLAYER);
else if (offlinePlayerHandler.isRelevantPlayer(arg) && statRequest.getPlayerName() == null) {
statRequest.setPlayerName(arg);
statRequest.setTarget(Target.PLAYER);
}
else if (arg.equalsIgnoreCase("api")) {
request.setAPIRequest();
statRequest.setAPIRequest();
}
}
patchRequest(request);
return request;
}
@Override
public StatRequest createPlayerStatRequest(String playerName, Statistic statistic, Material material, EntityType entity) {
return generateRequest(Target.PLAYER, statistic, material, entity, playerName);
}
@Override
public StatRequest createServerStatRequest(Statistic statistic, Material material, EntityType entityType) {
return generateRequest(Target.SERVER, statistic, material, entityType, null);
}
@Override
public StatRequest createTopStatRequest(Statistic statistic, Material material, EntityType entityType) {
return generateRequest(Target.TOP, statistic, material, entityType, null);
patchRequest(statRequest);
return statRequest;
}
/** This method will generate a {@link StatRequest} for a request arriving through the API.*/
private StatRequest generateRequest(@NotNull Target selection, @NotNull Statistic statistic, Material material, EntityType entity, String playerName) {
StatRequest request = new StatRequest(Bukkit.getConsoleSender(), true);
request.setTarget(selection);
request.setStatistic(statistic);
public StatRequest generateAPIRequest(@NotNull Target selection, @NotNull Statistic statistic, Material material, EntityType entity, String playerName) {
StatRequest statRequest = new StatRequest(Bukkit.getConsoleSender(), true);
statRequest.setTarget(selection);
statRequest.setStatistic(statistic);
switch (statistic.getType()) {
case BLOCK -> {
request.setBlock(material);
request.setSubStatEntryName(material.toString());
statRequest.setBlock(material);
statRequest.setSubStatEntryName(material.toString());
}
case ITEM -> {
request.setItem(material);
request.setSubStatEntryName(material.toString());
statRequest.setItem(material);
statRequest.setSubStatEntryName(material.toString());
}
case ENTITY -> {
request.setEntity(entity);
request.setSubStatEntryName(entity.toString());
statRequest.setEntity(entity);
statRequest.setSubStatEntryName(entity.toString());
}
}
if (selection == Target.PLAYER) request.setPlayerName(playerName);
return request;
if (selection == Target.PLAYER) statRequest.setPlayerName(playerName);
return statRequest;
}
/** Checks if a given {@link StatRequest} would result in a valid statistic look-up,
and sends a feedback message to the CommandSender that prompted the request if it is invalid.
and sends a feedback message to the CommandSender that prompted the statRequest if it is invalid.
<br> The following is checked:
<ul>
<li>Is a <code>statistic</code> set?
<li>Is a <code>subStatEntry</code> needed, and if so, is a corresponding Material/EntityType present?
<li>If the <code>target</code> is Player, is a valid <code>playerName</code> provided?
</ul>
@param request the StatRequest to check
@param statRequest the StatRequest to check
@return true if the StatRequest is valid, and false otherwise.
*/
public boolean validateRequest(StatRequest request) {
return validateRequestAndSendMessage(request, request.getCommandSender());
public boolean validateRequest(StatRequest statRequest) {
return validateRequestAndSendMessage(statRequest, statRequest.getCommandSender());
}
/** Checks if a given {@link StatRequest} would result in a valid statistic look-up,
@ -146,28 +118,28 @@ public final class RequestManager extends StatRequest implements RequestGenerato
<li>Is a <code>subStatEntry</code> needed, and if so, is a corresponding Material/EntityType present?
<li>If the <code>target</code> is Player, is a valid <code>playerName</code> provided?
</ul>
@param request the StatRequest to check
@param statRequest the StatRequest to check
@return true if the StatRequest is valid, and false otherwise.
*/
public boolean validateAPIRequest(StatRequest request) {
return validateRequestAndSendMessage(request, Bukkit.getConsoleSender());
public boolean validateAPIRequest(StatRequest statRequest) {
return validateRequestAndSendMessage(statRequest, Bukkit.getConsoleSender());
}
private boolean validateRequestAndSendMessage(StatRequest request, CommandSender sender) {
if (request.getStatistic() == null) {
private boolean validateRequestAndSendMessage(StatRequest statRequest, CommandSender sender) {
if (statRequest.getStatistic() == null) {
outputManager.sendFeedbackMsg(sender, StandardMessage.MISSING_STAT_NAME);
return false;
}
Statistic.Type type = request.getStatistic().getType();
if (request.getSubStatEntryName() == null && type != Statistic.Type.UNTYPED) {
Statistic.Type type = statRequest.getStatistic().getType();
if (statRequest.getSubStatEntryName() == null && type != Statistic.Type.UNTYPED) {
outputManager.sendFeedbackMsgMissingSubStat(sender, type);
return false;
}
else if (!hasMatchingSubStat(request)) {
outputManager.sendFeedbackMsgWrongSubStat(sender, type, request.getSubStatEntryName());
else if (!hasMatchingSubStat(statRequest)) {
outputManager.sendFeedbackMsgWrongSubStat(sender, type, statRequest.getSubStatEntryName());
return false;
}
else if (request.getTarget() == Target.PLAYER && request.getPlayerName() == null) {
else if (statRequest.getTarget() == Target.PLAYER && statRequest.getPlayerName() == null) {
outputManager.sendFeedbackMsg(sender, StandardMessage.MISSING_PLAYER_NAME);
return false;
}
@ -179,51 +151,51 @@ public final class RequestManager extends StatRequest implements RequestGenerato
/** Adjust the StatRequest object if needed: unpack the playerFlag into a subStatEntry,
try to retrieve the corresponding Enum Constant for any relevant block/entity/item,
and remove any unnecessary subStatEntries.*/
private void patchRequest(StatRequest request) {
if (request.getStatistic() != null) {
Statistic.Type type = request.getStatistic().getType();
private void patchRequest(StatRequest statRequest) {
if (statRequest.getStatistic() != null) {
Statistic.Type type = statRequest.getStatistic().getType();
if (request.getPlayerFlag()) { //unpack the playerFlag
if (type == Statistic.Type.ENTITY && request.getSubStatEntryName() == null) {
request.setSubStatEntryName("player");
if (statRequest.getPlayerFlag()) { //unpack the playerFlag
if (type == Statistic.Type.ENTITY && statRequest.getSubStatEntryName() == null) {
statRequest.setSubStatEntryName("player");
}
else {
request.setTarget(Target.PLAYER);
statRequest.setTarget(Target.PLAYER);
}
}
String subStatEntry = request.getSubStatEntryName();
String subStatEntry = statRequest.getSubStatEntryName();
switch (type) { //attempt to convert relevant subStatEntries into their corresponding Enum Constant
case BLOCK -> {
Material block = EnumHandler.getBlockEnum(subStatEntry);
if (block != null) request.setBlock(block);
if (block != null) statRequest.setBlock(block);
}
case ENTITY -> {
EntityType entity = EnumHandler.getEntityEnum(subStatEntry);
if (entity != null) request.setEntity(entity);
if (entity != null) statRequest.setEntity(entity);
}
case ITEM -> {
Material item = EnumHandler.getItemEnum(subStatEntry);
if (item != null) request.setItem(item);
if (item != null) statRequest.setItem(item);
}
case UNTYPED -> { //remove unnecessary subStatEntries
if (subStatEntry != null) request.setSubStatEntryName(null);
if (subStatEntry != null) statRequest.setSubStatEntryName(null);
}
}
}
}
private boolean hasMatchingSubStat(StatRequest request) {
Statistic.Type type = request.getStatistic().getType();
private boolean hasMatchingSubStat(StatRequest statRequest) {
Statistic.Type type = statRequest.getStatistic().getType();
switch (type) {
case BLOCK -> {
return request.getBlock() != null;
return statRequest.getBlock() != null;
}
case ENTITY -> {
return request.getEntity() != null;
return statRequest.getEntity() != null;
}
case ITEM -> {
return request.getItem() != null;
return statRequest.getItem() != null;
}
default -> {
return true;

View File

@ -18,7 +18,7 @@ final class StatAction extends RecursiveTask<ConcurrentHashMap<String, Integer>>
private final OfflinePlayerHandler offlinePlayerHandler;
private final ImmutableList<String> playerNames;
private final StatRequest request;
private final StatRequest statRequest;
private final ConcurrentHashMap<String, Integer> allStats;
/**
@ -34,7 +34,7 @@ final class StatAction extends RecursiveTask<ConcurrentHashMap<String, Integer>>
this.offlinePlayerHandler = offlinePlayerHandler;
this.playerNames = playerNames;
this.request = statRequest;
this.statRequest = statRequest;
this.allStats = allStats;
MyLogger.subActionCreated(Thread.currentThread().getName());
@ -46,8 +46,8 @@ final class StatAction extends RecursiveTask<ConcurrentHashMap<String, Integer>>
return getStatsDirectly();
}
else {
final StatAction subTask1 = new StatAction(offlinePlayerHandler, playerNames.subList(0, playerNames.size()/2), request, allStats);
final StatAction subTask2 = new StatAction(offlinePlayerHandler, playerNames.subList(playerNames.size()/2, playerNames.size()), request, allStats);
final StatAction subTask1 = new StatAction(offlinePlayerHandler, playerNames.subList(0, playerNames.size()/2), statRequest, allStats);
final StatAction subTask2 = new StatAction(offlinePlayerHandler, playerNames.subList(playerNames.size()/2, playerNames.size()), statRequest, allStats);
//queue and compute all subtasks in the right order
subTask1.fork();
@ -65,11 +65,11 @@ final class StatAction extends RecursiveTask<ConcurrentHashMap<String, Integer>>
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(playerName);
if (player != null) {
int statistic = 0;
switch (request.getStatistic().getType()) {
case UNTYPED -> statistic = player.getStatistic(request.getStatistic());
case ENTITY -> statistic = player.getStatistic(request.getStatistic(), request.getEntity());
case BLOCK -> statistic = player.getStatistic(request.getStatistic(), request.getBlock());
case ITEM -> statistic = player.getStatistic(request.getStatistic(), request.getItem());
switch (statRequest.getStatistic().getType()) {
case UNTYPED -> statistic = player.getStatistic(statRequest.getStatistic());
case ENTITY -> statistic = player.getStatistic(statRequest.getStatistic(), statRequest.getEntity());
case BLOCK -> statistic = player.getStatistic(statRequest.getStatistic(), statRequest.getBlock());
case ITEM -> statistic = player.getStatistic(statRequest.getStatistic(), statRequest.getItem());
}
if (statistic > 0) {
allStats.put(playerName, statistic);

View File

@ -30,28 +30,28 @@ public final class StatManager implements StatCalculator {
/** Gets the statistic data for an individual player. If somehow the player
cannot be found, this returns 0.*/
public int getPlayerStat(StatRequest request) {
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(request.getPlayerName());
public int getPlayerStat(StatRequest statRequest) {
OfflinePlayer player = offlinePlayerHandler.getOfflinePlayer(statRequest.getPlayerName());
if (player != null) {
return switch (request.getStatistic().getType()) {
case UNTYPED -> player.getStatistic(request.getStatistic());
case ENTITY -> player.getStatistic(request.getStatistic(), request.getEntity());
case BLOCK -> player.getStatistic(request.getStatistic(), request.getBlock());
case ITEM -> player.getStatistic(request.getStatistic(), request.getItem());
return switch (statRequest.getStatistic().getType()) {
case UNTYPED -> player.getStatistic(statRequest.getStatistic());
case ENTITY -> player.getStatistic(statRequest.getStatistic(), statRequest.getEntity());
case BLOCK -> player.getStatistic(statRequest.getStatistic(), statRequest.getBlock());
case ITEM -> player.getStatistic(statRequest.getStatistic(), statRequest.getItem());
};
}
return 0;
}
public LinkedHashMap<String, Integer> getTopStats(StatRequest request) {
return getAllStatsAsync(request).entrySet().stream()
public LinkedHashMap<String, Integer> getTopStats(StatRequest statRequest) {
return getAllStatsAsync(statRequest).entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(topListMaxSize)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}
public long getServerStat(StatRequest request) {
List<Integer> numbers = getAllStatsAsync(request)
public long getServerStat(StatRequest statRequest) {
List<Integer> numbers = getAllStatsAsync(statRequest)
.values()
.parallelStream()
.toList();
@ -60,16 +60,16 @@ public final class StatManager implements StatCalculator {
/** Invokes a bunch of worker pool threads to divide and conquer (get the statistics for all players
that are stored in the {@link OfflinePlayerHandler}) */
public @NotNull ConcurrentHashMap<String, Integer> getAllStatsAsync(StatRequest request) {
private @NotNull ConcurrentHashMap<String, Integer> getAllStatsAsync(StatRequest statRequest) {
long time = System.currentTimeMillis();
ForkJoinPool commonPool = ForkJoinPool.commonPool();
ConcurrentHashMap<String, Integer> allStats;
try {
allStats = commonPool.invoke(getStatTask(request));
allStats = commonPool.invoke(getStatTask(statRequest));
} catch (ConcurrentModificationException e) {
MyLogger.logMsg("The request could not be executed due to a ConcurrentModificationException. " +
MyLogger.logMsg("The statRequest could not be executed due to a ConcurrentModificationException. " +
"This likely happened because Bukkit hasn't fully initialized all player-data yet. " +
"Try again and it should be fine!", true);
throw new ConcurrentModificationException(e.toString());
@ -82,12 +82,12 @@ public final class StatManager implements StatCalculator {
return allStats;
}
private StatAction getStatTask(StatRequest request) {
private StatAction getStatTask(StatRequest statRequest) {
int size = offlinePlayerHandler.getOfflinePlayerCount() != 0 ? offlinePlayerHandler.getOfflinePlayerCount() : 16;
ConcurrentHashMap<String, Integer> allStats = new ConcurrentHashMap<>(size);
ImmutableList<String> playerNames = ImmutableList.copyOf(offlinePlayerHandler.getOfflinePlayerNames());
StatAction task = new StatAction(offlinePlayerHandler, playerNames, request, allStats);
StatAction task = new StatAction(offlinePlayerHandler, playerNames, statRequest, allStats);
MyLogger.actionCreated(playerNames.size());
return task;

View File

@ -1,6 +1,6 @@
package com.gmail.artemis.the.gr8.playerstats.statistic;
import com.gmail.artemis.the.gr8.playerstats.api.PlayerStats;
import com.gmail.artemis.the.gr8.playerstats.api.StatFormatter;
import com.gmail.artemis.the.gr8.playerstats.enums.StandardMessage;
import com.gmail.artemis.the.gr8.playerstats.enums.Target;
import com.gmail.artemis.the.gr8.playerstats.models.StatRequest;
@ -20,16 +20,16 @@ public final class StatThread extends Thread {
private static StatManager statManager;
private final ReloadThread reloadThread;
private final StatRequest request;
private final StatRequest statRequest;
public StatThread(OutputManager m, StatManager t, int ID, StatRequest s, @Nullable ReloadThread r) {
outputManager = m;
statManager = t;
reloadThread = r;
request = s;
statRequest = s;
this.setName("StatThread-" + request.getCommandSender().getName() + "-" + ID);
this.setName("StatThread-" + statRequest.getCommandSender().getName() + "-" + ID);
MyLogger.threadCreated(this.getName());
}
@ -37,13 +37,13 @@ public final class StatThread extends Thread {
public void run() throws IllegalStateException, NullPointerException {
MyLogger.threadStart(this.getName());
if (request == null) {
throw new NullPointerException("No statistic request was found!");
if (statRequest == null) {
throw new NullPointerException("No statistic statRequest was found!");
}
if (reloadThread != null && reloadThread.isAlive()) {
try {
MyLogger.waitingForOtherThread(this.getName(), reloadThread.getName());
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.STILL_RELOADING);
outputManager.sendFeedbackMsg(statRequest.getCommandSender(), StandardMessage.STILL_RELOADING);
reloadThread.join();
} catch (InterruptedException e) {
@ -54,27 +54,27 @@ public final class StatThread extends Thread {
long lastCalc = ThreadManager.getLastRecordedCalcTime();
if (lastCalc > 2000) {
outputManager.sendFeedbackMsgWaitAMoment(request.getCommandSender(), lastCalc > 20000);
outputManager.sendFeedbackMsgWaitAMoment(statRequest.getCommandSender(), lastCalc > 20000);
}
Target selection = request.getTarget();
Target selection = statRequest.getTarget();
try {
TextComponent statResult = switch (selection) {
case PLAYER -> outputManager.formatPlayerStat(request, statManager.getPlayerStat(request));
case TOP -> outputManager.formatTopStat(request, statManager.getTopStats(request));
case SERVER -> outputManager.formatServerStat(request, statManager.getServerStat(request));
case PLAYER -> outputManager.formatPlayerStat(statRequest, statManager.getPlayerStat(statRequest));
case TOP -> outputManager.formatTopStat(statRequest, statManager.getTopStats(statRequest));
case SERVER -> outputManager.formatServerStat(statRequest, statManager.getServerStat(statRequest));
};
if (request.isAPIRequest()) {
String msg = PlayerStats.getAPI().statFormatter().statResultComponentToString(statResult);
request.getCommandSender().sendMessage(msg);
if (statRequest.isAPIRequest()) {
String msg = StatFormatter.statResultComponentToString(statResult);
statRequest.getCommandSender().sendMessage(msg);
}
else {
outputManager.sendToCommandSender(request.getCommandSender(), statResult);
outputManager.sendToCommandSender(statRequest.getCommandSender(), statResult);
}
}
catch (ConcurrentModificationException e) {
if (!request.isConsoleSender()) {
outputManager.sendFeedbackMsg(request.getCommandSender(), StandardMessage.UNKNOWN_ERROR);
if (!statRequest.isConsoleSender()) {
outputManager.sendFeedbackMsg(statRequest.getCommandSender(), StandardMessage.UNKNOWN_ERROR);
}
}
}