mirror of
https://github.com/itHotL/PlayerStats.git
synced 2024-11-22 11:55:17 +01:00
Pinned down the ConcurrentModificationException, reworked some of the error throwing-and-catching (#44), and implemented total-for-server feature (#42)
This commit is contained in:
parent
5d0333b823
commit
772127426b
@ -35,7 +35,7 @@ public class ThreadManager {
|
||||
}
|
||||
|
||||
public void startReloadThread(CommandSender sender, boolean firstTimeLoading) {
|
||||
reloadThread = new ReloadThread(threshold, config, testFile, plugin, statThread, sender, firstTimeLoading);
|
||||
reloadThread = new ReloadThread(threshold, adventure, config, testFile, messageFactory, plugin, statThread, sender, firstTimeLoading);
|
||||
reloadThread.start();
|
||||
}
|
||||
|
||||
|
@ -97,6 +97,9 @@ public class StatCommand implements CommandExecutor {
|
||||
else if (arg.equalsIgnoreCase("top")) {
|
||||
request.setTopFlag(true);
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("server")) {
|
||||
request.setServerFlag(true);
|
||||
}
|
||||
else if (arg.equalsIgnoreCase("me") && sender instanceof Player) {
|
||||
request.setPlayerName(sender.getName());
|
||||
}
|
||||
@ -113,9 +116,12 @@ public class StatCommand implements CommandExecutor {
|
||||
removeUnnecessarySubStat(request);
|
||||
|
||||
if (request.getStatName() != null) {
|
||||
if (!request.topFlag() && request.getPlayerName() == null) {
|
||||
if (!(request.topFlag() || request.serverFlag()) && request.getPlayerName() == null) {
|
||||
assumeTopAsDefault(request);
|
||||
}
|
||||
else if (request.topFlag() && request.serverFlag()) {
|
||||
assumeServerFlag(request);
|
||||
}
|
||||
return EnumHandler.isValidStatEntry(request.getStatType(), request.getSubStatEntry());
|
||||
}
|
||||
return false;
|
||||
@ -135,8 +141,13 @@ public class StatCommand implements CommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
//if no playerName was provided, and there is no topFlag, substitute a top flag
|
||||
//if no playerName was provided, and there is no topFlag or serverFlag, substitute a top flag
|
||||
private void assumeTopAsDefault(StatRequest request) {
|
||||
request.setTopFlag(true);
|
||||
}
|
||||
|
||||
//if both a topFlag and serverFlag are present, keep the serverFlag and reset the topFlag
|
||||
private void assumeServerFlag(StatRequest request) {
|
||||
request.setTopFlag(false);
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ public class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
commandOptions = new ArrayList<>();
|
||||
commandOptions.add("top");
|
||||
commandOptions.add("player");
|
||||
commandOptions.add("server");
|
||||
commandOptions.add("me");
|
||||
}
|
||||
|
||||
|
@ -115,8 +115,16 @@ public class ConfigHandler {
|
||||
return getStringFromConfig(topStat, isStyle, "stat-numbers");
|
||||
}
|
||||
|
||||
public String getListNumberFormatting(boolean isStyle) {
|
||||
return getStringFromConfig(true, isStyle, "list-numbers");
|
||||
public String getListTitleFormatting(boolean isStyle) {
|
||||
return getStringFromConfig(true, isStyle, "list-title");
|
||||
}
|
||||
|
||||
public String getListTitleNumberFormatting(boolean isStyle) {
|
||||
return getStringFromConfig(true, isStyle, "list-title-number");
|
||||
}
|
||||
|
||||
public String getRankingNumberFormatting(boolean isStyle) {
|
||||
return getStringFromConfig(true, isStyle, "ranking-numbers");
|
||||
}
|
||||
|
||||
public String getDotsColor() {
|
||||
|
@ -58,30 +58,20 @@ public class ReloadAction extends RecursiveAction {
|
||||
final ReloadAction subTask2 = new ReloadAction(threshold, players, (start + split), end,
|
||||
whitelistOnly, excludeBanned, lastPlayedLimit, offlinePlayerUUIDs);
|
||||
|
||||
try {
|
||||
//queue and compute all subtasks in the right order
|
||||
invokeAll(subTask1, subTask2);
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//queue and compute all subtasks in the right order
|
||||
invokeAll(subTask1, subTask2);
|
||||
}
|
||||
}
|
||||
|
||||
private void process() {
|
||||
try {
|
||||
for (int i = start; i < end; i++) {
|
||||
OfflinePlayer player = players[i];
|
||||
if (player.getName() != null &&
|
||||
(!whitelistOnly || player.isWhitelisted()) &&
|
||||
(!excludeBanned || !player.isBanned()) &&
|
||||
(lastPlayedLimit == 0 || UnixTimeHandler.hasPlayedSince(lastPlayedLimit, player.getLastPlayed()))) {
|
||||
offlinePlayerUUIDs.put(player.getName(), player.getUniqueId());
|
||||
}
|
||||
for (int i = start; i < end; i++) {
|
||||
OfflinePlayer player = players[i];
|
||||
if (player.getName() != null &&
|
||||
(!whitelistOnly || player.isWhitelisted()) &&
|
||||
(!excludeBanned || !player.isBanned()) &&
|
||||
(lastPlayedLimit == 0 || UnixTimeHandler.hasPlayedSince(lastPlayedLimit, player.getLastPlayed()))) {
|
||||
offlinePlayerUUIDs.put(player.getName(), player.getUniqueId());
|
||||
}
|
||||
} catch (ConcurrentModificationException e) {
|
||||
Bukkit.getLogger().warning("ReloadAction has thrown a ConcurrentModificationException because of " + e.getCause());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,33 +7,37 @@ import com.gmail.artemis.the.gr8.playerstats.filehandlers.TestFileHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.statistic.StatThread;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
|
||||
public class ReloadThread extends Thread {
|
||||
|
||||
private final int threshold;
|
||||
|
||||
private final BukkitAudiences adventure;
|
||||
private static ConfigHandler config;
|
||||
private static TestFileHandler testFile;
|
||||
private final MessageFactory messageFactory;
|
||||
private final Main plugin;
|
||||
|
||||
private final StatThread statThread;
|
||||
private final CommandSender sender;
|
||||
private final boolean firstTimeLoading;
|
||||
|
||||
public ReloadThread(int threshold, ConfigHandler c, TestFileHandler t, Main p, @Nullable StatThread s, @Nullable CommandSender se, boolean firstTime) {
|
||||
public ReloadThread(int threshold, BukkitAudiences b, ConfigHandler c, TestFileHandler t, MessageFactory m, Main p, @Nullable StatThread s, @Nullable CommandSender se, boolean firstTime) {
|
||||
this.threshold = threshold;
|
||||
adventure = b;
|
||||
config = c;
|
||||
testFile = t;
|
||||
messageFactory = m;
|
||||
plugin = p;
|
||||
|
||||
statThread = s;
|
||||
@ -59,13 +63,21 @@ public class ReloadThread extends Thread {
|
||||
}
|
||||
plugin.getLogger().info("Reloading!");
|
||||
if (config.reloadConfig()) {
|
||||
OfflinePlayerHandler.updateOfflinePlayerList(getPlayerMap(false));
|
||||
|
||||
try {
|
||||
OfflinePlayerHandler.updateOfflinePlayerList(getPlayerMap(false));
|
||||
} catch (ConcurrentModificationException e) {
|
||||
plugin.getLogger().warning("The request could not be fully executed due to a ConcurrentModificationException");
|
||||
if (sender != null) {
|
||||
adventure.sender(sender).sendMessage(messageFactory.partiallyReloaded());
|
||||
}
|
||||
}
|
||||
|
||||
testFile.saveTimeTaken(System.currentTimeMillis() - time, 2);
|
||||
plugin.getLogger().info("Amount of relevant players: " + OfflinePlayerHandler.getOfflinePlayerCount());
|
||||
plugin.logTimeTaken("ReloadThread", "loading offline players", time);
|
||||
if (sender != null) {
|
||||
sender.sendMessage(MessageFactory.getPluginPrefix() + ChatColor.GREEN + "Config reloaded!");
|
||||
adventure.sender(sender).sendMessage(messageFactory.reloadedConfig());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -89,19 +101,22 @@ public class ReloadThread extends Thread {
|
||||
|
||||
ReloadAction task = new ReloadAction(threshold, offlinePlayers, config.whitelistOnly(), config.excludeBanned(), config.lastPlayedLimit(), playerMap);
|
||||
ForkJoinPool commonPool = ForkJoinPool.commonPool();
|
||||
commonPool.invoke(task);
|
||||
|
||||
ConcurrentHashMap<String, UUID> newPlayerMap = new ConcurrentHashMap<>(playerMap.size());
|
||||
try {
|
||||
commonPool.invoke(task);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
}
|
||||
return generateFakeExtraPlayers(playerMap, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
for (int i = 0; i < 11; i++) {
|
||||
for (String key : playerMap.keySet()) {
|
||||
newPlayerMap.put(key + i, playerMap.get(key));
|
||||
private ConcurrentHashMap<String, UUID> generateFakeExtraPlayers(ConcurrentHashMap<String, UUID> realPlayers, int loops) {
|
||||
ConcurrentHashMap<String, UUID> newPlayerMap = new ConcurrentHashMap<>(realPlayers.size() * loops);
|
||||
for (int i = 0; i < loops; i++) {
|
||||
for (String key : realPlayers.keySet()) {
|
||||
newPlayerMap.put(key + i, realPlayers.get(key));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
newPlayerMap.putAll(playerMap);
|
||||
return newPlayerMap;
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ public class StatRequest {
|
||||
private String playerName;
|
||||
private boolean playerFlag;
|
||||
private boolean topFlag;
|
||||
private boolean serverFlag;
|
||||
|
||||
private Statistic statEnum;
|
||||
private Statistic.Type statType;
|
||||
@ -28,6 +29,7 @@ public class StatRequest {
|
||||
sender = s;
|
||||
playerFlag = false;
|
||||
topFlag = false;
|
||||
serverFlag = false;
|
||||
}
|
||||
|
||||
//sets the statName, and automatically tries to set the correct statType and get the corresponding item/block/entity if there is a subStatEntry
|
||||
@ -41,7 +43,7 @@ public class StatRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private void setStatEnumAndType() {
|
||||
private void setStatEnumAndType() throws IllegalArgumentException {
|
||||
try {
|
||||
statEnum = EnumHandler.getStatEnum(statName);
|
||||
statType = statEnum.getType();
|
||||
@ -105,6 +107,10 @@ public class StatRequest {
|
||||
this.topFlag = topFlag;
|
||||
}
|
||||
|
||||
public void setServerFlag(boolean serverFlag) {
|
||||
this.serverFlag = serverFlag;
|
||||
}
|
||||
|
||||
public CommandSender getCommandSender() {
|
||||
return sender;
|
||||
}
|
||||
@ -150,4 +156,7 @@ public class StatRequest {
|
||||
return topFlag;
|
||||
}
|
||||
|
||||
public boolean serverFlag() {
|
||||
return serverFlag;
|
||||
}
|
||||
}
|
||||
|
@ -9,18 +9,12 @@ import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.MessageFactory;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -53,8 +47,6 @@ public class StatThread extends Thread {
|
||||
//what the thread will do once started
|
||||
@Override
|
||||
public void run() throws IllegalStateException, NullPointerException {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
if (messageFactory == null || plugin == null) {
|
||||
throw new IllegalStateException("Not all classes off the plugin are running!");
|
||||
}
|
||||
@ -77,8 +69,9 @@ public class StatThread extends Thread {
|
||||
String statName = request.getStatName();
|
||||
String subStatEntry = request.getSubStatEntry();
|
||||
boolean topFlag = request.topFlag();
|
||||
boolean serverFlag = request.serverFlag();
|
||||
|
||||
if (topFlag) {
|
||||
if (topFlag || serverFlag) {
|
||||
if (ThreadManager.getLastRecordedCalcTime() > 30000) {
|
||||
adventure.sender(sender).sendMessage(messageFactory.waitAMoment(true));
|
||||
}
|
||||
@ -87,53 +80,53 @@ public class StatThread extends Thread {
|
||||
}
|
||||
|
||||
try {
|
||||
adventure.sender(sender).sendMessage(messageFactory.formatTopStats(
|
||||
getTopStatistics(), statName, subStatEntry));
|
||||
|
||||
testFile.saveTimeTaken(System.currentTimeMillis() - time, 3);
|
||||
testFile.logRunCount(false);
|
||||
plugin.logTimeTaken("StatThread", "calculating top stat", time);
|
||||
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
|
||||
if (topFlag) {
|
||||
adventure.sender(sender).sendMessage(messageFactory.formatTopStats(
|
||||
getTopStats(), statName, subStatEntry));
|
||||
}
|
||||
else {
|
||||
adventure.sender(sender).sendMessage(messageFactory.formatServerStat(
|
||||
statName, subStatEntry, getServerTotal()));
|
||||
}
|
||||
|
||||
} catch (ConcurrentModificationException e) {
|
||||
testFile.logRunCount(true);
|
||||
adventure.sender(sender).sendMessage(messageFactory.unknownError());
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(messageFactory.formatExceptions(e.toString()));
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
else if (playerName != null) {
|
||||
try {
|
||||
long time = System.currentTimeMillis();
|
||||
adventure.sender(sender).sendMessage(
|
||||
messageFactory.formatPlayerStat(
|
||||
playerName, statName, subStatEntry, getStatistic()));
|
||||
playerName, statName, subStatEntry, getIndividualStat()));
|
||||
plugin.logTimeTaken("StatThread", "calculating individual stat", time);
|
||||
|
||||
} catch (Exception e) {
|
||||
sender.sendMessage(messageFactory.formatExceptions(e.toString()));
|
||||
e.printStackTrace();
|
||||
} catch (UnsupportedOperationException | NullPointerException e) {
|
||||
sender.sendMessage(messageFactory.formatExceptions(e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//returns the integer associated with a certain statistic for a player
|
||||
private int getStatistic() throws IllegalArgumentException, NullPointerException {
|
||||
try {
|
||||
return getPlayerStat(OfflinePlayerHandler.getOfflinePlayer(request.getPlayerName()));
|
||||
}
|
||||
catch (Exception e) {
|
||||
Bukkit.getLogger().warning(e.toString());
|
||||
throw new IllegalArgumentException(e.toString());
|
||||
}
|
||||
private LinkedHashMap<String, Integer> getTopStats() throws ConcurrentModificationException, NullPointerException {
|
||||
return getAllStats().entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.limit(config.getTopListMaxSize()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private int getServerTotal() {
|
||||
List<Integer> numbers = getAllStats().values().stream().toList();
|
||||
return numbers.parallelStream().mapToInt(Integer::intValue).sum();
|
||||
}
|
||||
|
||||
//invokes a bunch of worker pool threads to divide and conquer (get the statistics for all players in the list)
|
||||
private LinkedHashMap<String, Integer> getTopStatistics() throws ConcurrentModificationException {
|
||||
ConcurrentHashMap<String, Integer> playerStats = new ConcurrentHashMap<>((int) (getOfflinePlayerCount() * 1.05));
|
||||
//ConcurrentLinkedDeque<String> playerNames = new ConcurrentLinkedDeque<>(OfflinePlayerHandler.getOfflinePlayerNames());
|
||||
//String[] playerNames = OfflinePlayerHandler.getOfflinePlayerNames().toArray(new String[0]);
|
||||
private ConcurrentHashMap<String, Integer> getAllStats() throws ConcurrentModificationException, NullPointerException {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
ConcurrentHashMap<String, Integer> playerStats = new ConcurrentHashMap<>((int) (OfflinePlayerHandler.getOfflinePlayerCount() * 1.05));
|
||||
ImmutableList<String> playerNames = ImmutableList.copyOf(OfflinePlayerHandler.getOfflinePlayerNames());
|
||||
TopStatAction task = new TopStatAction(threshold, playerNames,
|
||||
request, playerStats);
|
||||
@ -142,57 +135,44 @@ public class StatThread extends Thread {
|
||||
try {
|
||||
commonPool.invoke(task);
|
||||
} catch (ConcurrentModificationException e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
if (!task.cancel(true)) {
|
||||
plugin.getLogger().severe("Tried to cancel task, but failed. You might need to shut down the server and reboot");
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
} else {
|
||||
plugin.getLogger().warning("Canceling task because of a ConcurrentModificationException...");
|
||||
}
|
||||
} catch (ConcurrentModificationException ex) {
|
||||
ex.printStackTrace();
|
||||
throw new ConcurrentModificationException(ex.toString());
|
||||
}
|
||||
plugin.getLogger().warning("The request could not be executed due to a ConcurrentModificationException. " +
|
||||
"This likely happened because Bukkit hasn't fully initialized all players yet. Try again and it should be fine!");
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
}
|
||||
|
||||
return playerStats.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.limit(config.getTopListMaxSize()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
testFile.saveTimeTaken(System.currentTimeMillis() - time, 3);
|
||||
testFile.logRunCount(false);
|
||||
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
|
||||
plugin.logTimeTaken("StatThread", "calculating all stats", time);
|
||||
|
||||
return playerStats;
|
||||
}
|
||||
|
||||
//gets the actual statistic data for an individual player
|
||||
private int getPlayerStat(@NotNull OfflinePlayer player) throws IllegalArgumentException {
|
||||
try {
|
||||
switch (request.getStatType()) {
|
||||
case UNTYPED -> {
|
||||
return player.getStatistic(request.getStatEnum());
|
||||
}
|
||||
case ENTITY -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getEntity());
|
||||
}
|
||||
case BLOCK -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getBlock());
|
||||
}
|
||||
case ITEM -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getItem());
|
||||
}
|
||||
default ->
|
||||
throw new Exception("This statistic does not seem to be of type:untyped/block/entity/item, I strongly suggest we panic");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Bukkit.getLogger().warning(e.toString());
|
||||
throw new IllegalArgumentException(e.toString());
|
||||
}
|
||||
}
|
||||
private int getIndividualStat() throws UnsupportedOperationException, NullPointerException {
|
||||
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(request.getPlayerName());
|
||||
|
||||
//returns the amount of offline players, attempts to update the list if none are found, and otherwise throws an error
|
||||
private int getOfflinePlayerCount() {
|
||||
try {
|
||||
return OfflinePlayerHandler.getOfflinePlayerCount();
|
||||
}
|
||||
catch (NullPointerException e) {
|
||||
throw new RuntimeException("No offline players were found to calculate statistics for!");
|
||||
switch (request.getStatType()) {
|
||||
case UNTYPED -> {
|
||||
return player.getStatistic(request.getStatEnum());
|
||||
}
|
||||
case ENTITY -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getEntity());
|
||||
}
|
||||
case BLOCK -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getBlock());
|
||||
}
|
||||
case ITEM -> {
|
||||
return player.getStatistic(request.getStatEnum(), request.getItem());
|
||||
}
|
||||
default -> {
|
||||
if (request.getStatType() != null) {
|
||||
throw new UnsupportedOperationException("PlayerStats is not familiar with this statistic type - please check if you are using the latest version of the plugin!");
|
||||
}
|
||||
else {
|
||||
throw new NullPointerException("Trying to calculate a statistic of which the type is null - is this a valid statistic?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package com.gmail.artemis.the.gr8.playerstats.statistic;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.utils.OfflinePlayerHandler;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
|
||||
import java.util.*;
|
||||
@ -48,30 +47,26 @@ public class TopStatAction extends RecursiveAction {
|
||||
}
|
||||
}
|
||||
|
||||
private void getStatsDirectly() throws IllegalArgumentException, ConcurrentModificationException {
|
||||
private void getStatsDirectly() {
|
||||
try {
|
||||
Iterator<String> iterator = playerNames.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String playerName = iterator.next();
|
||||
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName);
|
||||
int statistic = 0;
|
||||
switch (request.getStatType()) {
|
||||
case UNTYPED -> statistic = player.getStatistic(request.getStatEnum());
|
||||
case ENTITY -> statistic = player.getStatistic(request.getStatEnum(), request.getEntity());
|
||||
case BLOCK -> statistic = player.getStatistic(request.getStatEnum(), request.getBlock());
|
||||
case ITEM -> statistic = player.getStatistic(request.getStatEnum(), request.getItem());
|
||||
}
|
||||
if (statistic > 0) {
|
||||
playerStats.put(playerName, statistic);
|
||||
}
|
||||
if (iterator.hasNext()) {
|
||||
do {
|
||||
String playerName = iterator.next();
|
||||
OfflinePlayer player = OfflinePlayerHandler.getOfflinePlayer(playerName);
|
||||
int statistic = 0;
|
||||
switch (request.getStatType()) {
|
||||
case UNTYPED -> statistic = player.getStatistic(request.getStatEnum());
|
||||
case ENTITY -> statistic = player.getStatistic(request.getStatEnum(), request.getEntity());
|
||||
case BLOCK -> statistic = player.getStatistic(request.getStatEnum(), request.getBlock());
|
||||
case ITEM -> statistic = player.getStatistic(request.getStatEnum(), request.getItem());
|
||||
}
|
||||
if (statistic > 0) {
|
||||
playerStats.put(playerName, statistic);
|
||||
}
|
||||
} while (iterator.hasNext());
|
||||
}
|
||||
} catch (NoSuchElementException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
} catch (ConcurrentModificationException e) {
|
||||
Bukkit.getLogger().warning("A ConcurrentModificationException has occurred" + e.getCause());
|
||||
e.printStackTrace();
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
} catch (NoSuchElementException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.gmail.artemis.the.gr8.playerstats.utils;
|
||||
|
||||
import com.gmail.artemis.the.gr8.playerstats.Main;
|
||||
import com.gmail.artemis.the.gr8.playerstats.filehandlers.ConfigHandler;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
@ -10,7 +9,6 @@ import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.util.Index;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.map.MinecraftFont;
|
||||
|
||||
@ -23,23 +21,39 @@ public class MessageFactory {
|
||||
private static ConfigHandler config;
|
||||
|
||||
private static final TextColor msgColor = TextColor.fromHexString("#55aaff");
|
||||
private static final String pluginPrefix = ChatColor.GRAY + "[" + ChatColor.GOLD + "PlayerStats" + ChatColor.GRAY + "] " + ChatColor.RESET;
|
||||
private static final TextColor hoverBaseColor = TextColor.fromHexString("#55C6FF");
|
||||
private static final TextColor hoverAccentColor1 = TextColor.fromHexString("#FFB80E");
|
||||
private static final TextColor hoverAccentColor2 = TextColor.fromHexString("#FFD52B");
|
||||
|
||||
|
||||
public MessageFactory(ConfigHandler c) {
|
||||
config = c;
|
||||
}
|
||||
|
||||
public static String getPluginPrefix() {
|
||||
return pluginPrefix;
|
||||
private static TextComponent getPluginPrefix() {
|
||||
return text("[")
|
||||
.append(text("PlayerStats").color(NamedTextColor.GOLD))
|
||||
.append(text("]")
|
||||
.append(space()))
|
||||
.color(NamedTextColor.GRAY);
|
||||
}
|
||||
|
||||
public TextComponent reloadedConfig() {
|
||||
return getPluginPrefix().append(text("Config reloaded!").color(NamedTextColor.GREEN));
|
||||
}
|
||||
|
||||
public TextComponent stillReloading() {
|
||||
return text(getPluginPrefix()).append(text("The plugin is still (re)loading, your request will be processed when it is done!").color(msgColor));
|
||||
return getPluginPrefix().append(text("The plugin is still (re)loading, your request will be processed when it is done!").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent partiallyReloaded() {
|
||||
return getPluginPrefix().append(
|
||||
text("The reload process was interrupted. If you notice unexpected behavior, please reload PlayerStats again to fix it!").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent waitAMoment(boolean longWait) {
|
||||
return longWait ? text(getPluginPrefix()).append(text("Calculating statistics, this may take a minute...").color(msgColor))
|
||||
: text(getPluginPrefix()).append(text("Calculating statistics, this may take a few moments...").color(msgColor));
|
||||
return longWait ? getPluginPrefix().append(text("Calculating statistics, this may take a minute...").color(msgColor))
|
||||
: getPluginPrefix().append(text("Calculating statistics, this may take a few moments...").color(msgColor));
|
||||
}
|
||||
|
||||
public String formatExceptions(String exception) {
|
||||
@ -47,12 +61,12 @@ public class MessageFactory {
|
||||
}
|
||||
|
||||
public TextComponent missingStatName() {
|
||||
return text(getPluginPrefix()).append(text("Please provide a valid statistic name!").color(msgColor));
|
||||
return getPluginPrefix().append(text("Please provide a valid statistic name!").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent missingSubStatName(Statistic.Type statType) {
|
||||
String subStat = getSubStatTypeName(statType) == null ? "sub-statistic" : getSubStatTypeName(statType);
|
||||
return text(getPluginPrefix())
|
||||
return getPluginPrefix()
|
||||
.append(text("Please add a valid ")
|
||||
.append(text(subStat))
|
||||
.append(text(" to look up this statistic!")))
|
||||
@ -60,16 +74,16 @@ public class MessageFactory {
|
||||
}
|
||||
|
||||
public TextComponent missingTarget() {
|
||||
return text(getPluginPrefix()).append(text("Please add \"me\", \"player\" or \"top\"").color(msgColor));
|
||||
return getPluginPrefix().append(text("Please add \"me\", \"player\" or \"top\"").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent missingPlayerName() {
|
||||
return text(getPluginPrefix()).append(text("Please specify a valid player-name!").color(msgColor));
|
||||
return getPluginPrefix().append(text("Please specify a valid player-name!").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent wrongSubStatType(Statistic.Type statType, String subStatEntry) {
|
||||
String subStat = getSubStatTypeName(statType) == null ? "sub-statistic for this statistic" : getSubStatTypeName(statType);
|
||||
return text(getPluginPrefix())
|
||||
return getPluginPrefix()
|
||||
.append(text("\"")
|
||||
.append(text(subStatEntry))
|
||||
.append(text("\""))
|
||||
@ -79,7 +93,7 @@ public class MessageFactory {
|
||||
}
|
||||
|
||||
public TextComponent unknownError() {
|
||||
return text(getPluginPrefix()).append(text("Something went wrong with your request, please try again!").color(msgColor));
|
||||
return getPluginPrefix().append(text("Something went wrong with your request, please try again!").color(msgColor));
|
||||
}
|
||||
|
||||
public TextComponent helpMsg() {
|
||||
@ -87,12 +101,10 @@ public class MessageFactory {
|
||||
TextComponent underscores = text("____________").color(TextColor.fromHexString("#6E3485"));
|
||||
TextComponent arrow = text("→ ").color(NamedTextColor.GOLD);
|
||||
TextColor arguments = NamedTextColor.YELLOW;
|
||||
TextColor hoverBaseColor = TextColor.fromHexString("#55C6FF");
|
||||
TextColor hoverAccentColor1 = TextColor.fromHexString("#FFB80E");
|
||||
TextColor hoverAccentColor2 = TextColor.fromHexString("#FFD52B");
|
||||
|
||||
|
||||
return Component.newline()
|
||||
.append(underscores).append(spaces).append(text(MessageFactory.getPluginPrefix())).append(spaces).append(underscores)
|
||||
.append(underscores).append(spaces).append(getPluginPrefix()).append(spaces).append(underscores)
|
||||
.append(newline())
|
||||
.append(text("Hover over the arguments for more information!").color(NamedTextColor.GRAY).decorate(TextDecoration.ITALIC))
|
||||
.append(newline())
|
||||
@ -126,6 +138,10 @@ public class MessageFactory {
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("Choose any player that has played on your server").color(hoverBaseColor))))
|
||||
.append(text(" | ").color(arguments))
|
||||
.append(text("server").color(arguments)
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("See the combined total for everyone on the server").color(hoverBaseColor))))
|
||||
.append(text(" | ").color(arguments))
|
||||
.append(text("top").color(arguments)
|
||||
.hoverEvent(HoverEvent.showText(
|
||||
text("See the top ").color(hoverBaseColor)
|
||||
@ -141,27 +157,23 @@ public class MessageFactory {
|
||||
|
||||
public TextComponent formatPlayerStat(String playerName, String statName, String subStatEntryName, int stat) {
|
||||
TextComponent.Builder singleStat = Component.text();
|
||||
String subStat = subStatEntryName != null ?
|
||||
" (" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : "";
|
||||
|
||||
singleStat.append(playerNameComponent(playerName + ": ", false))
|
||||
.append(statNumberComponent(stat, false)).append(space())
|
||||
.append(statNameComponent(statName.toLowerCase().replace("_", " "), false))
|
||||
.append(subStatNameComponent(subStat, false));
|
||||
.append(statNameComponent(statName, false))
|
||||
.append(subStatNameComponent(subStatEntryName, false));
|
||||
|
||||
return singleStat.build();
|
||||
}
|
||||
|
||||
public TextComponent formatTopStats(LinkedHashMap<String, Integer> topStats, String statName, String subStatEntryName) {
|
||||
TextComponent.Builder topList = Component.text();
|
||||
String subStat = subStatEntryName != null ?
|
||||
"(" + subStatEntryName.toLowerCase().replace("_", " ") + ")" : "";
|
||||
|
||||
topList.append(newline()).append(text(getPluginPrefix()))
|
||||
.append(statNameComponent("Top", true)).append(space())
|
||||
.append(listNumberComponent(topStats.size() + "")).append(space())
|
||||
.append(statNameComponent(statName.toLowerCase().replace("_", " "), true)).append(space())
|
||||
.append(subStatNameComponent(subStat, true));
|
||||
topList.append(newline()).append(getPluginPrefix())
|
||||
.append(titleComponent("Top")).append(space())
|
||||
.append(titleNumberComponent(topStats.size())).append(space())
|
||||
.append(statNameComponent(statName, true)).append(space())
|
||||
.append(subStatNameComponent(subStatEntryName, true));
|
||||
|
||||
boolean useDots = config.useDots();
|
||||
Set<String> playerNames = topStats.keySet();
|
||||
@ -172,7 +184,7 @@ public class MessageFactory {
|
||||
count = count+1;
|
||||
|
||||
topList.append(newline())
|
||||
.append(listNumberComponent(count + ". "))
|
||||
.append(rankingNumberComponent(count + ". "))
|
||||
.append(playerNameComponent(playerName, true));
|
||||
|
||||
if (useDots) {
|
||||
@ -194,6 +206,20 @@ public class MessageFactory {
|
||||
return topList.build();
|
||||
}
|
||||
|
||||
public TextComponent formatServerStat(String statName, String subStatEntry, int stat) {
|
||||
TextComponent.Builder serverStat = Component.text();
|
||||
serverStat.append(titleComponent("All"))
|
||||
.append(space())
|
||||
.append(statNameComponent(statName, true))
|
||||
.append(space())
|
||||
.append(subStatNameComponent(subStatEntry, true))
|
||||
.append(titleComponent("on this server:"))
|
||||
.append(space())
|
||||
.append(statNumberComponent(stat, true));
|
||||
|
||||
return serverStat.build();
|
||||
}
|
||||
|
||||
//returns the type of the substatistic in String-format, or null if this statistic is not of type block, item or entity
|
||||
private String getSubStatTypeName(Statistic.Type statType) {
|
||||
String subStat;
|
||||
@ -213,6 +239,8 @@ public class MessageFactory {
|
||||
}
|
||||
|
||||
//try to get the hex color or NamedTextColor from config String, substitute a default ChatColor if both fail, and try to apply style where necessary
|
||||
|
||||
|
||||
private TextComponent playerNameComponent(String playerName, boolean topStat) {
|
||||
NamedTextColor defaultColor = topStat ? NamedTextColor.GREEN : NamedTextColor.GOLD;
|
||||
TextComponent.Builder player = applyColor(
|
||||
@ -222,13 +250,16 @@ public class MessageFactory {
|
||||
|
||||
private TextComponent statNameComponent(String statName, boolean topStat) {
|
||||
TextComponent.Builder stat = applyColor(
|
||||
config.getStatNameFormatting(topStat, false), statName, NamedTextColor.YELLOW);
|
||||
config.getStatNameFormatting(topStat, false), statName.toLowerCase().replace("_", " "), NamedTextColor.YELLOW);
|
||||
return applyStyle(config.getStatNameFormatting(topStat, true), stat).build();
|
||||
}
|
||||
|
||||
private TextComponent subStatNameComponent(String subStatName, boolean topStat) {
|
||||
String subStatString = subStatName != null ?
|
||||
"(" + subStatName.toLowerCase().replace("_", " ") + ") " : "";
|
||||
|
||||
TextComponent.Builder subStat = applyColor(
|
||||
config.getSubStatNameFormatting(topStat, false), subStatName, NamedTextColor.YELLOW);
|
||||
config.getSubStatNameFormatting(topStat, false), subStatString, NamedTextColor.YELLOW);
|
||||
return applyStyle(config.getSubStatNameFormatting(topStat, true), subStat).build();
|
||||
}
|
||||
|
||||
@ -238,14 +269,23 @@ public class MessageFactory {
|
||||
return applyStyle(config.getStatNumberFormatting(topStat, true), number).build();
|
||||
}
|
||||
|
||||
private TextComponent listNumberComponent(String listNumber) {
|
||||
TextComponent.Builder list = applyColor(config.getListNumberFormatting(false), listNumber + "", NamedTextColor.GOLD);
|
||||
return applyStyle(config.getListNumberFormatting(true), list).build();
|
||||
private TextComponent titleComponent(String content) {
|
||||
TextComponent.Builder server = applyColor(config.getListTitleFormatting(false), content, NamedTextColor.YELLOW);
|
||||
return applyStyle(config.getListTitleFormatting(true), server).build();
|
||||
}
|
||||
|
||||
private TextComponent titleNumberComponent(int number) {
|
||||
TextComponent.Builder titleNumber = applyColor(config.getListTitleNumberFormatting(false), number + "", NamedTextColor.GOLD);
|
||||
return applyStyle(config.getListTitleNumberFormatting(true), titleNumber).build();
|
||||
}
|
||||
|
||||
private TextComponent rankingNumberComponent(String number) {
|
||||
TextComponent.Builder list = applyColor(config.getRankingNumberFormatting(false), number + "", NamedTextColor.GOLD);
|
||||
return applyStyle(config.getRankingNumberFormatting(true), list).build();
|
||||
}
|
||||
|
||||
private TextComponent dotsComponent(String dots) {
|
||||
return text(dots).color(getColorFromString(config.getDotsColor())).colorIfAbsent(NamedTextColor.DARK_GRAY);
|
||||
//return applyColor(config.getDotsColor(), dots, NamedTextColor.DARK_GRAY).build();
|
||||
}
|
||||
|
||||
private TextColor getColorFromString(String configString) {
|
||||
|
@ -37,8 +37,10 @@ top-list-color:
|
||||
player-names: green
|
||||
stat-names: yellow
|
||||
sub-stat-names: '#FFD52B'
|
||||
stat-numbers: '#55aaff'
|
||||
list-numbers: gold
|
||||
stat-numbers: '#55AAFF'
|
||||
list-title: yellow
|
||||
list-title-number: gold
|
||||
ranking-numbers: gold
|
||||
dots: dark_gray
|
||||
|
||||
top-list-style:
|
||||
@ -46,7 +48,9 @@ top-list-style:
|
||||
stat-names: none
|
||||
sub-stat-names: none
|
||||
stat-numbers: none
|
||||
list-numbers: none
|
||||
list-title: none
|
||||
list-title-number: none
|
||||
ranking-numbers: none
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user