mirror of
https://github.com/itHotL/PlayerStats.git
synced 2025-02-21 02:31:58 +01:00
Started working on database implementation, moved item/entity-related logic out of TabCompleter and into EnumHandler to better divide responsibilities
This commit is contained in:
parent
3a71a6af70
commit
3591a879d9
@ -4,7 +4,7 @@
|
||||
<groupId>io.github.ithotl</groupId>
|
||||
<artifactId>PlayerStats</artifactId>
|
||||
<name>PlayerStats</name>
|
||||
<version>2.0</version>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<description>Statistics Plugin</description>
|
||||
<url>https://www.spigotmc.org/resources/playerstats.102347/</url>
|
||||
<developers>
|
||||
@ -70,6 +70,10 @@
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>com.artemis.the.gr8.playerstats.lib.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.artemis.the.gr8.database</pattern>
|
||||
<shadedPattern>com.artemis.the.gr8.playerstats.lib.database</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
|
13
pom.xml
13
pom.xml
@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>io.github.ithotl</groupId>
|
||||
<artifactId>PlayerStats</artifactId>
|
||||
<version>2.0</version>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
|
||||
<name>PlayerStats</name>
|
||||
<description>Statistics Plugin</description>
|
||||
@ -87,6 +87,13 @@
|
||||
<version>4.11.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.artemis.the.gr8</groupId>
|
||||
<artifactId>Database</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
@ -162,6 +169,10 @@
|
||||
<pattern>org.bstats</pattern>
|
||||
<shadedPattern>com.artemis.the.gr8.playerstats.lib.bstats</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.artemis.the.gr8.database</pattern>
|
||||
<shadedPattern>com.artemis.the.gr8.playerstats.lib.database</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters>
|
||||
<filter>
|
||||
|
@ -7,12 +7,12 @@ import com.artemis.the.gr8.playerstats.api.StatManager;
|
||||
import com.artemis.the.gr8.playerstats.core.commands.*;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.msgutils.NumberFormatter;
|
||||
import com.artemis.the.gr8.playerstats.core.multithreading.ThreadManager;
|
||||
import com.artemis.the.gr8.playerstats.core.statrequest.RequestManager;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.listeners.JoinListener;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.msgutils.LanguageKeyHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.sharing.ShareManager;
|
||||
import com.artemis.the.gr8.playerstats.core.statistic.StatRequestManager;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import me.clip.placeholderapi.PlaceholderAPIPlugin;
|
||||
@ -41,7 +41,7 @@ public final class Main extends JavaPlugin implements PlayerStats {
|
||||
private static LanguageKeyHandler languageKeyHandler;
|
||||
private static OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
private static RequestManager requestManager;
|
||||
private static StatRequestManager statManager;
|
||||
private static OutputManager outputManager;
|
||||
private static ShareManager shareManager;
|
||||
|
||||
@ -111,7 +111,7 @@ public final class Main extends JavaPlugin implements PlayerStats {
|
||||
shareManager = ShareManager.getInstance();
|
||||
|
||||
outputManager = new OutputManager(adventure);
|
||||
requestManager = new RequestManager(outputManager);
|
||||
statManager = new StatRequestManager(outputManager);
|
||||
threadManager = new ThreadManager(this, outputManager);
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ public final class Main extends JavaPlugin implements PlayerStats {
|
||||
|
||||
@Override
|
||||
public StatManager getStatManager() {
|
||||
return requestManager;
|
||||
return statManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -7,9 +7,9 @@ import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.enums.StandardMessage;
|
||||
import com.artemis.the.gr8.playerstats.api.enums.Target;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.statrequest.PlayerStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.statrequest.ServerStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.statrequest.TopStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.statistic.PlayerStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.statistic.ServerStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.statistic.TopStatRequest;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.EnumHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import org.bukkit.Material;
|
||||
|
@ -2,16 +2,13 @@ package com.artemis.the.gr8.playerstats.core.commands;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.core.utils.EnumHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.stream.Collectors;
|
||||
@ -23,8 +20,6 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
|
||||
private List<String> statCommandTargets;
|
||||
private List<String> excludeCommandOptions;
|
||||
private List<String> itemsThatCanBreak;
|
||||
private List<String> entitiesThatCanDie;
|
||||
|
||||
public TabCompleter() {
|
||||
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
|
||||
@ -124,13 +119,13 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
}
|
||||
case ITEM -> {
|
||||
if (stat == Statistic.BREAK_ITEM) {
|
||||
return itemsThatCanBreak;
|
||||
return enumHandler.getAllItemsThatCanBreak();
|
||||
} else {
|
||||
return enumHandler.getAllItemNames();
|
||||
}
|
||||
}
|
||||
case ENTITY -> {
|
||||
return entitiesThatCanDie;
|
||||
return enumHandler.getAllEntitiesThatCanDie();
|
||||
}
|
||||
default -> {
|
||||
return statCommandTargets;
|
||||
@ -141,22 +136,5 @@ public final class TabCompleter implements org.bukkit.command.TabCompleter {
|
||||
private void prepareLists() {
|
||||
statCommandTargets = List.of("top", "player", "server", "me");
|
||||
excludeCommandOptions = List.of("add", "list", "remove", "info");
|
||||
|
||||
//breaking an item means running its durability negative
|
||||
itemsThatCanBreak = Arrays.stream(Material.values())
|
||||
.parallel()
|
||||
.filter(Material::isItem)
|
||||
.filter(item -> item.getMaxDurability() != 0)
|
||||
.map(Material::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//the only statistics dealing with entities are killed_entity and entity_killed_by
|
||||
entitiesThatCanDie = Arrays.stream(EntityType.values())
|
||||
.parallel()
|
||||
.filter(EntityType::isAlive)
|
||||
.map(EntityType::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@ -6,6 +6,8 @@ import com.artemis.the.gr8.playerstats.core.utils.FileHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Map;
|
||||
@ -21,7 +23,7 @@ public final class ConfigHandler extends FileHandler {
|
||||
super("config.yml");
|
||||
config = super.getFileConfiguration();
|
||||
|
||||
configVersion = 7;
|
||||
configVersion = 8;
|
||||
checkAndUpdateConfigVersion();
|
||||
MyLogger.setDebugLevel(getDebugLevel());
|
||||
}
|
||||
@ -83,6 +85,23 @@ public final class ConfigHandler extends FileHandler {
|
||||
return config.getInt("debug-level", 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether PlayerStats should use a database (default: true)
|
||||
*/
|
||||
public boolean useDatabase() {
|
||||
return config.getBoolean("use-database", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return an Array with the config values for the MySQL connection URL,
|
||||
* username and password. Any of these might be null
|
||||
*/
|
||||
public String[] getMySQLCredentials() {
|
||||
return new String[]{config.getString("mySQL-connection-URL"),
|
||||
config.getString("mySQL-username"),
|
||||
config.getString("mySQL-password")};
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether command-senders should be limited to one stat-request at a time.
|
||||
* @return the config setting (default: true)
|
||||
|
@ -0,0 +1,58 @@
|
||||
package com.artemis.the.gr8.playerstats.core.database;
|
||||
|
||||
import com.artemis.the.gr8.database.DatabaseManager;
|
||||
import com.artemis.the.gr8.database.models.MyStatType;
|
||||
import com.artemis.the.gr8.database.models.MyStatistic;
|
||||
import com.artemis.the.gr8.playerstats.core.Main;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.EnumHandler;
|
||||
import org.bukkit.Statistic;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class Database {
|
||||
|
||||
private DatabaseManager databaseManager;
|
||||
|
||||
public Database() {
|
||||
connect();
|
||||
}
|
||||
|
||||
private void connect() {
|
||||
File pluginFolder = Main.getPluginInstance().getDataFolder();
|
||||
databaseManager = DatabaseManager.getSQLiteManager(pluginFolder);
|
||||
// databaseManager = DatabaseManager.getMySQLManager("jdbc:mysql://localhost:3306/minecraftstatdb", "myuser", "myuser");
|
||||
databaseManager.setUp(getStats(), null);
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
//TODO do something with databaseManager to see if it's active
|
||||
return true;
|
||||
}
|
||||
|
||||
private @NotNull List<MyStatistic> getStats() {
|
||||
EnumHandler enumHandler = EnumHandler.getInstance();
|
||||
List<MyStatistic> stats = new ArrayList<>();
|
||||
|
||||
enumHandler.getAllStatNames().forEach(statName -> {
|
||||
Statistic stat = enumHandler.getStatEnum(statName);
|
||||
if (stat != null) {
|
||||
stats.add(new MyStatistic(statName, getType(stat)));
|
||||
}
|
||||
});
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private MyStatType getType(Statistic statistic) {
|
||||
return switch (statistic.getType()) {
|
||||
case UNTYPED -> MyStatType.CUSTOM;
|
||||
case BLOCK -> MyStatType.BLOCK;
|
||||
case ITEM -> MyStatType.ITEM;
|
||||
case ENTITY -> MyStatType.ENTITY;
|
||||
};
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
package com.artemis.the.gr8.playerstats.core.multithreading;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.statrequest.RequestManager;
|
||||
import com.artemis.the.gr8.playerstats.core.statistic.StatRequestManager;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
@ -55,7 +55,7 @@ final class StatThread extends Thread {
|
||||
}
|
||||
|
||||
try {
|
||||
StatResult<?> result = RequestManager.execute(statRequest);
|
||||
StatResult<?> result = StatRequestManager.execute(statRequest);
|
||||
outputManager.sendToCommandSender(statRequester, result.formattedComponent());
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
|
@ -0,0 +1,141 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.msgutils.FormattingFunction;
|
||||
import com.artemis.the.gr8.playerstats.core.multithreading.ThreadManager;
|
||||
import com.artemis.the.gr8.playerstats.core.sharing.ShareManager;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
final class BukkitProcessor extends RequestProcessor {
|
||||
|
||||
private final OutputManager outputManager;
|
||||
private final ConfigHandler config;
|
||||
private final ShareManager shareManager;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
public BukkitProcessor(OutputManager outputManager) {
|
||||
this.outputManager = outputManager;
|
||||
|
||||
config = ConfigHandler.getInstance();
|
||||
shareManager = ShareManager.getInstance();
|
||||
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Integer> processPlayerRequest(StatRequest<?> playerStatRequest) {
|
||||
StatRequest.Settings requestSettings = playerStatRequest.getSettings();
|
||||
int stat = getPlayerStat(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatPlayerStat(requestSettings, stat);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stat, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Long> processServerRequest(StatRequest<?> serverStatRequest) {
|
||||
StatRequest.Settings requestSettings = serverStatRequest.getSettings();
|
||||
long stat = getServerStat(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatServerStat(requestSettings, stat);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stat, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<LinkedHashMap<String, Integer>> processTopRequest(StatRequest<?> topStatRequest) {
|
||||
StatRequest.Settings requestSettings = topStatRequest.getSettings();
|
||||
LinkedHashMap<String, Integer> stats = getTopStats(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatTopStats(requestSettings, stats);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stats, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
private int getPlayerStat(@NotNull StatRequest.Settings requestSettings) {
|
||||
OfflinePlayer player;
|
||||
if (offlinePlayerHandler.isExcludedPlayer(requestSettings.getPlayerName()) &&
|
||||
config.allowPlayerLookupsForExcludedPlayers()) {
|
||||
player = offlinePlayerHandler.getExcludedOfflinePlayer(requestSettings.getPlayerName());
|
||||
} else {
|
||||
player = offlinePlayerHandler.getIncludedOfflinePlayer(requestSettings.getPlayerName());
|
||||
}
|
||||
return switch (requestSettings.getStatistic().getType()) {
|
||||
case UNTYPED -> player.getStatistic(requestSettings.getStatistic());
|
||||
case ENTITY -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getEntity());
|
||||
case BLOCK -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getBlock());
|
||||
case ITEM -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getItem());
|
||||
};
|
||||
}
|
||||
|
||||
private long getServerStat(StatRequest.Settings requestSettings) {
|
||||
List<Integer> numbers = getAllStatsAsync(requestSettings)
|
||||
.values()
|
||||
.parallelStream()
|
||||
.toList();
|
||||
return numbers.parallelStream().mapToLong(Integer::longValue).sum();
|
||||
}
|
||||
|
||||
private LinkedHashMap<String, Integer> getTopStats(StatRequest.Settings requestSettings) {
|
||||
return getAllStatsAsync(requestSettings).entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.limit(requestSettings.getTopListSize())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private TextComponent processFunction(CommandSender sender, FormattingFunction function) {
|
||||
if (outputShouldBeStored(sender)) {
|
||||
int shareCode = shareManager.saveStatResult(sender.getName(), function.getResultWithSharerName(sender));
|
||||
return function.getResultWithShareButton(shareCode);
|
||||
}
|
||||
return function.getDefaultResult();
|
||||
}
|
||||
|
||||
private boolean outputShouldBeStored(CommandSender sender) {
|
||||
return !(sender instanceof ConsoleCommandSender) &&
|
||||
shareManager.isEnabled() &&
|
||||
shareManager.senderHasPermission(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a bunch of worker pool threads to get the statistics for all players that are stored in the
|
||||
* {@link OfflinePlayerHandler}).
|
||||
*/
|
||||
private @NotNull ConcurrentHashMap<String, Integer> getAllStatsAsync(StatRequest.Settings requestSettings) {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
ForkJoinPool commonPool = ForkJoinPool.commonPool();
|
||||
ConcurrentHashMap<String, Integer> allStats;
|
||||
|
||||
try {
|
||||
allStats = commonPool.invoke(ThreadManager.getStatAction(requestSettings));
|
||||
} catch (ConcurrentModificationException e) {
|
||||
MyLogger.logWarning("The requestSettings 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!");
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
}
|
||||
|
||||
MyLogger.actionFinished();
|
||||
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
|
||||
MyLogger.logMediumLevelTask("Calculated all stats", time);
|
||||
|
||||
return allStats;
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import com.artemis.the.gr8.playerstats.core.database.Database;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public class DatabaseProcessor extends RequestProcessor {
|
||||
|
||||
private final OutputManager outputManager;
|
||||
private final Database database;
|
||||
private boolean isActive;
|
||||
|
||||
public DatabaseProcessor(OutputManager outputManager, Database database) {
|
||||
this.outputManager = outputManager;
|
||||
this.database = database;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return database.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Integer> processPlayerRequest(StatRequest<?> playerStatRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Long> processServerRequest(StatRequest<?> serverStatRequest) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<LinkedHashMap<String, Integer>> processTopRequest(StatRequest<?> topStatRequest) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statrequest;
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.RequestGenerator;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
@ -0,0 +1,16 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
|
||||
public abstract class RequestProcessor {
|
||||
|
||||
abstract @NotNull StatResult<Integer> processPlayerRequest(StatRequest<?> playerStatRequest);
|
||||
|
||||
abstract @NotNull StatResult<Long> processServerRequest(StatRequest<?> serverStatRequest);
|
||||
|
||||
abstract @NotNull StatResult<LinkedHashMap<String, Integer>> processTopRequest(StatRequest<?> topStatRequest);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statrequest;
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.RequestGenerator;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
@ -0,0 +1,90 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.RequestGenerator;
|
||||
import com.artemis.the.gr8.playerstats.api.StatManager;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.database.Database;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Turns user input into a {@link StatRequest} that can be
|
||||
* executed to get statistic data.
|
||||
*/
|
||||
public final class StatRequestManager implements StatManager {
|
||||
|
||||
private static RequestProcessor processor;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
public StatRequestManager(OutputManager outputManager) {
|
||||
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
|
||||
|
||||
ConfigHandler config = ConfigHandler.getInstance();
|
||||
if (config.useDatabase()) {
|
||||
String[] mySQLcredentials = config.getMySQLCredentials();
|
||||
MyLogger.logLowLevelMsg(Arrays.toString(mySQLcredentials));
|
||||
processor = new DatabaseProcessor(outputManager, new Database());
|
||||
} else {
|
||||
processor = new BukkitProcessor(outputManager);
|
||||
}
|
||||
}
|
||||
|
||||
public static StatResult<?> execute(@NotNull StatRequest<?> request) {
|
||||
return switch (request.getSettings().getTarget()) {
|
||||
case PLAYER -> processor.processPlayerRequest(request);
|
||||
case SERVER -> processor.processServerRequest(request);
|
||||
case TOP -> processor.processTopRequest(request);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcludedPlayer(String playerName) {
|
||||
return offlinePlayerHandler.isExcludedPlayer(playerName);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<Integer> createPlayerStatRequest(String playerName) {
|
||||
return new PlayerStatRequest(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Integer> executePlayerStatRequest(@NotNull StatRequest<Integer> request) {
|
||||
return processor.processPlayerRequest(request);
|
||||
}
|
||||
|
||||
@Contract(" -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<Long> createServerStatRequest() {
|
||||
return new ServerStatRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Long> executeServerStatRequest(@NotNull StatRequest<Long> request) {
|
||||
return processor.processServerRequest(request);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<LinkedHashMap<String, Integer>> createTopStatRequest(int topListSize) {
|
||||
return new TopStatRequest(topListSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RequestGenerator<LinkedHashMap<String, Integer>> createTotalTopStatRequest() {
|
||||
int playerCount = offlinePlayerHandler.getIncludedPlayerCount();
|
||||
return createTopStatRequest(playerCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<LinkedHashMap<String, Integer>> executeTopRequest(@NotNull StatRequest<LinkedHashMap<String, Integer>> request) {
|
||||
return processor.processTopRequest(request);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statrequest;
|
||||
package com.artemis.the.gr8.playerstats.core.statistic;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.RequestGenerator;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
@ -1,202 +0,0 @@
|
||||
package com.artemis.the.gr8.playerstats.core.statrequest;
|
||||
|
||||
import com.artemis.the.gr8.playerstats.api.RequestGenerator;
|
||||
import com.artemis.the.gr8.playerstats.api.StatManager;
|
||||
import com.artemis.the.gr8.playerstats.api.StatRequest;
|
||||
import com.artemis.the.gr8.playerstats.api.StatResult;
|
||||
import com.artemis.the.gr8.playerstats.core.config.ConfigHandler;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.msgutils.FormattingFunction;
|
||||
import com.artemis.the.gr8.playerstats.core.msg.OutputManager;
|
||||
import com.artemis.the.gr8.playerstats.core.multithreading.ThreadManager;
|
||||
import com.artemis.the.gr8.playerstats.core.sharing.ShareManager;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.MyLogger;
|
||||
import com.artemis.the.gr8.playerstats.core.utils.OfflinePlayerHandler;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Turns user input into a {@link StatRequest} that can be
|
||||
* executed to get statistic data.
|
||||
*/
|
||||
public final class RequestManager implements StatManager {
|
||||
|
||||
private static RequestProcessor processor;
|
||||
private final OfflinePlayerHandler offlinePlayerHandler;
|
||||
|
||||
public RequestManager(OutputManager outputManager) {
|
||||
offlinePlayerHandler = OfflinePlayerHandler.getInstance();
|
||||
processor = new RequestProcessor(outputManager);
|
||||
}
|
||||
|
||||
public static StatResult<?> execute(@NotNull StatRequest<?> request) {
|
||||
return switch (request.getSettings().getTarget()) {
|
||||
case PLAYER -> processor.processPlayerRequest(request.getSettings());
|
||||
case SERVER -> processor.processServerRequest(request.getSettings());
|
||||
case TOP -> processor.processTopRequest(request.getSettings());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcludedPlayer(String playerName) {
|
||||
return offlinePlayerHandler.isExcludedPlayer(playerName);
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<Integer> createPlayerStatRequest(String playerName) {
|
||||
return new PlayerStatRequest(playerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Integer> executePlayerStatRequest(@NotNull StatRequest<Integer> request) {
|
||||
return processor.processPlayerRequest(request.getSettings());
|
||||
}
|
||||
|
||||
@Contract(" -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<Long> createServerStatRequest() {
|
||||
return new ServerStatRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<Long> executeServerStatRequest(@NotNull StatRequest<Long> request) {
|
||||
return processor.processServerRequest(request.getSettings());
|
||||
}
|
||||
|
||||
@Contract("_ -> new")
|
||||
@Override
|
||||
public @NotNull RequestGenerator<LinkedHashMap<String, Integer>> createTopStatRequest(int topListSize) {
|
||||
return new TopStatRequest(topListSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RequestGenerator<LinkedHashMap<String, Integer>> createTotalTopStatRequest() {
|
||||
int playerCount = offlinePlayerHandler.getIncludedPlayerCount();
|
||||
return createTopStatRequest(playerCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull StatResult<LinkedHashMap<String, Integer>> executeTopRequest(@NotNull StatRequest<LinkedHashMap<String, Integer>> request) {
|
||||
return processor.processTopRequest(request.getSettings());
|
||||
}
|
||||
|
||||
private final class RequestProcessor {
|
||||
|
||||
private static ConfigHandler config;
|
||||
private static OutputManager outputManager;
|
||||
private static ShareManager shareManager;
|
||||
|
||||
public RequestProcessor(OutputManager outputManager) {
|
||||
RequestProcessor.config = ConfigHandler.getInstance();
|
||||
RequestProcessor.outputManager = outputManager;
|
||||
RequestProcessor.shareManager = ShareManager.getInstance();
|
||||
}
|
||||
|
||||
public @NotNull StatResult<Integer> processPlayerRequest(StatRequest.Settings requestSettings) {
|
||||
int stat = getPlayerStat(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatPlayerStat(requestSettings, stat);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stat, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
public @NotNull StatResult<Long> processServerRequest(StatRequest.Settings requestSettings) {
|
||||
long stat = getServerStat(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatServerStat(requestSettings, stat);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stat, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
public @NotNull StatResult<LinkedHashMap<String, Integer>> processTopRequest(StatRequest.Settings requestSettings) {
|
||||
LinkedHashMap<String, Integer> stats = getTopStats(requestSettings);
|
||||
FormattingFunction formattingFunction = outputManager.formatTopStats(requestSettings, stats);
|
||||
TextComponent formattedResult = processFunction(requestSettings.getCommandSender(), formattingFunction);
|
||||
String resultAsString = outputManager.textComponentToString(formattedResult);
|
||||
|
||||
return new StatResult<>(stats, formattedResult, resultAsString);
|
||||
}
|
||||
|
||||
private int getPlayerStat(@NotNull StatRequest.Settings requestSettings) {
|
||||
OfflinePlayer player;
|
||||
if (offlinePlayerHandler.isExcludedPlayer(requestSettings.getPlayerName()) &&
|
||||
config.allowPlayerLookupsForExcludedPlayers()) {
|
||||
player = offlinePlayerHandler.getExcludedOfflinePlayer(requestSettings.getPlayerName());
|
||||
} else {
|
||||
player = offlinePlayerHandler.getIncludedOfflinePlayer(requestSettings.getPlayerName());
|
||||
}
|
||||
return switch (requestSettings.getStatistic().getType()) {
|
||||
case UNTYPED -> player.getStatistic(requestSettings.getStatistic());
|
||||
case ENTITY -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getEntity());
|
||||
case BLOCK -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getBlock());
|
||||
case ITEM -> player.getStatistic(requestSettings.getStatistic(), requestSettings.getItem());
|
||||
};
|
||||
}
|
||||
|
||||
private long getServerStat(StatRequest.Settings requestSettings) {
|
||||
List<Integer> numbers = getAllStatsAsync(requestSettings)
|
||||
.values()
|
||||
.parallelStream()
|
||||
.toList();
|
||||
return numbers.parallelStream().mapToLong(Integer::longValue).sum();
|
||||
}
|
||||
|
||||
private LinkedHashMap<String, Integer> getTopStats(StatRequest.Settings requestSettings) {
|
||||
return getAllStatsAsync(requestSettings).entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
|
||||
.limit(requestSettings.getTopListSize())
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
|
||||
}
|
||||
|
||||
private TextComponent processFunction(CommandSender sender, FormattingFunction function) {
|
||||
if (outputShouldBeStored(sender)) {
|
||||
int shareCode = shareManager.saveStatResult(sender.getName(), function.getResultWithSharerName(sender));
|
||||
return function.getResultWithShareButton(shareCode);
|
||||
}
|
||||
return function.getDefaultResult();
|
||||
}
|
||||
|
||||
private boolean outputShouldBeStored(CommandSender sender) {
|
||||
return !(sender instanceof ConsoleCommandSender) &&
|
||||
shareManager.isEnabled() &&
|
||||
shareManager.senderHasPermission(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a bunch of worker pool threads to get the statistics for
|
||||
* all players that are stored in the {@link OfflinePlayerHandler}).
|
||||
*/
|
||||
private @NotNull ConcurrentHashMap<String, Integer> getAllStatsAsync(StatRequest.Settings requestSettings) {
|
||||
long time = System.currentTimeMillis();
|
||||
|
||||
ForkJoinPool commonPool = ForkJoinPool.commonPool();
|
||||
ConcurrentHashMap<String, Integer> allStats;
|
||||
|
||||
try {
|
||||
allStats = commonPool.invoke(ThreadManager.getStatAction(requestSettings));
|
||||
} catch (ConcurrentModificationException e) {
|
||||
MyLogger.logWarning("The requestSettings 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!");
|
||||
throw new ConcurrentModificationException(e.toString());
|
||||
}
|
||||
|
||||
MyLogger.actionFinished();
|
||||
ThreadManager.recordCalcTime(System.currentTimeMillis() - time);
|
||||
MyLogger.logMediumLevelTask("Calculated all stats", time);
|
||||
|
||||
return allStats;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,12 @@ import java.util.stream.Stream;
|
||||
public final class EnumHandler {
|
||||
|
||||
private static volatile EnumHandler instance;
|
||||
|
||||
private static List<String> blockNames;
|
||||
private static List<String> itemNames;
|
||||
private static List<String> itemsThatCanBreak;
|
||||
private static List<String> entityNames;
|
||||
private static List<String> entitiesThatCanDie;
|
||||
private static List<String> statNames;
|
||||
private static List<String> subStatNames;
|
||||
|
||||
@ -47,23 +51,34 @@ public final class EnumHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all block-names in lowercase.
|
||||
*
|
||||
* @return the List
|
||||
* @return a list with blockNames in lowercase
|
||||
*/
|
||||
public List<String> getAllBlockNames() {
|
||||
return blockNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all item-names in lowercase.
|
||||
*
|
||||
* @return the List
|
||||
* @return a list with itemNames in lowercase
|
||||
*/
|
||||
public List<String> getAllItemNames() {
|
||||
return itemNames;
|
||||
}
|
||||
|
||||
public List<String> getAllItemsThatCanBreak() {
|
||||
return itemsThatCanBreak;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list with entityNames in lowercase
|
||||
*/
|
||||
public List<String> getAllEntityNames() {
|
||||
return entityNames;
|
||||
}
|
||||
|
||||
public List<String> getAllEntitiesThatCanDie() {
|
||||
return entitiesThatCanDie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all statistic-names in lowercase.
|
||||
*
|
||||
@ -185,12 +200,6 @@ public final class EnumHandler {
|
||||
}
|
||||
|
||||
private void prepareLists() {
|
||||
List<String> entityNames = Arrays.stream(EntityType.values())
|
||||
.map(EntityType::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.filter(entityName -> !entityName.equalsIgnoreCase("unknown"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
blockNames = Arrays.stream(Material.values())
|
||||
.filter(Material::isBlock)
|
||||
.map(Material::toString)
|
||||
@ -203,6 +212,28 @@ public final class EnumHandler {
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
//breaking an item means running its durability negative
|
||||
itemsThatCanBreak = Arrays.stream(Material.values())
|
||||
.parallel()
|
||||
.filter(Material::isItem)
|
||||
.filter(item -> item.getMaxDurability() != 0)
|
||||
.map(Material::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
entityNames = Arrays.stream(EntityType.values())
|
||||
.map(EntityType::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.filter(entityName -> !entityName.equalsIgnoreCase("unknown"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
entitiesThatCanDie = Arrays.stream(EntityType.values())
|
||||
.parallel()
|
||||
.filter(EntityType::isAlive)
|
||||
.map(EntityType::toString)
|
||||
.map(string -> string.toLowerCase(Locale.ENGLISH))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
subStatNames = Stream.of(blockNames, entityNames, itemNames)
|
||||
.flatMap(Collection::stream)
|
||||
.distinct()
|
||||
|
@ -1,7 +1,7 @@
|
||||
# ------------------------------------------------------------------------------------------------------ #
|
||||
# PlayerStats Configuration #
|
||||
# ------------------------------------------------------------------------------------------------------ #
|
||||
config-version: 7
|
||||
config-version: 8
|
||||
|
||||
|
||||
# # ------------------------------- # #
|
||||
@ -14,6 +14,15 @@ config-version: 7
|
||||
# 3 = high (log all tasks and time taken)
|
||||
debug-level: 1
|
||||
|
||||
# By default, PlayerStats will use an SQLite database file, which PlayerStats sets up itself
|
||||
use-database: true
|
||||
|
||||
# If you specify a mySQL URL, username and password here, PlayerStats will attempt to use your
|
||||
# mySQL server. If connection fails, the SQLite file will be used instead (if use-database is enabled)
|
||||
mySQL-connection-URL: ''
|
||||
mySQL-username: ''
|
||||
mySQL-password: ''
|
||||
|
||||
# Whether players have to wait for their lookup to finish before they can request another statistic
|
||||
# Warning: disabling this could put stress on your server if players spam the stat-command!
|
||||
only-allow-one-lookup-at-a-time-per-player: true
|
||||
|
@ -1,6 +1,6 @@
|
||||
main: com.artemis.the.gr8.playerstats.core.Main
|
||||
name: PlayerStats
|
||||
version: 2.0
|
||||
version: 2.1
|
||||
api-version: 1.13
|
||||
description: adds commands to view player statistics in chat
|
||||
author: Artemis_the_gr8
|
||||
|
Loading…
Reference in New Issue
Block a user