Add player stats tracking with StatService and /hg stats command integration
This commit is contained in:
parent
2b6480c880
commit
c8e85f186a
|
|
@ -24,6 +24,7 @@ public final class Main extends JavaPlugin {
|
||||||
private PlayerService playerService;
|
private PlayerService playerService;
|
||||||
private PlayerTeleporterService playerTeleporterService;
|
private PlayerTeleporterService playerTeleporterService;
|
||||||
private LootService lootService;
|
private LootService lootService;
|
||||||
|
private StatService statService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
|
@ -40,6 +41,7 @@ public final class Main extends JavaPlugin {
|
||||||
lootService = new LootService();
|
lootService = new LootService();
|
||||||
roundService = RoundService.createSingletonInstance(round, this, lootService);
|
roundService = RoundService.createSingletonInstance(round, this, lootService);
|
||||||
playerTeleporterService = PlayerTeleporterService.createSingletonInstance();
|
playerTeleporterService = PlayerTeleporterService.createSingletonInstance();
|
||||||
|
statService = new StatService(this, round);
|
||||||
playerService = PlayerService.createSingletonInstance(round, roundService, playerTeleporterService);
|
playerService = PlayerService.createSingletonInstance(round, roundService, playerTeleporterService);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,14 +51,14 @@ public final class Main extends JavaPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCommands() {
|
private void registerCommands() {
|
||||||
BaseCommand command = new BaseCommand(this, roundService, playerService, round);
|
BaseCommand command = new BaseCommand(this, roundService, playerService, round, statService);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerEvents() {
|
private void registerEvents() {
|
||||||
PluginManager pluginManager = getServer().getPluginManager();
|
PluginManager pluginManager = getServer().getPluginManager();
|
||||||
pluginManager.registerEvents(new PlayerDisconnectListener(playerService), this);
|
pluginManager.registerEvents(new PlayerDisconnectListener(playerService), this);
|
||||||
pluginManager.registerEvents(new PlayerJoinListener(playerService), this);
|
pluginManager.registerEvents(new PlayerJoinListener(playerService), this);
|
||||||
pluginManager.registerEvents(new PlayerDamageListener(roundService, playerService), this);
|
pluginManager.registerEvents(new PlayerDamageListener(roundService, playerService, statService), this);
|
||||||
pluginManager.registerEvents(new ChestListener(roundService, lootService), this);
|
pluginManager.registerEvents(new ChestListener(roundService, lootService), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import com.alttd.hunger_games.config.Messages;
|
||||||
import com.alttd.hunger_games.services.PlayerService;
|
import com.alttd.hunger_games.services.PlayerService;
|
||||||
import com.alttd.hunger_games.services.Round;
|
import com.alttd.hunger_games.services.Round;
|
||||||
import com.alttd.hunger_games.services.RoundService;
|
import com.alttd.hunger_games.services.RoundService;
|
||||||
|
import com.alttd.hunger_games.services.StatService;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
|
@ -22,7 +23,7 @@ import java.util.stream.Collectors;
|
||||||
public class BaseCommand implements CommandExecutor, TabExecutor {
|
public class BaseCommand implements CommandExecutor, TabExecutor {
|
||||||
private final List<SubCommand> subCommands;
|
private final List<SubCommand> subCommands;
|
||||||
|
|
||||||
public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round) {
|
public BaseCommand(Main main, RoundService roundService, PlayerService playerService, Round round, StatService statService) {
|
||||||
PluginCommand command = main.getCommand("hungergames");
|
PluginCommand command = main.getCommand("hungergames");
|
||||||
if (command == null) {
|
if (command == null) {
|
||||||
subCommands = null;
|
subCommands = null;
|
||||||
|
|
@ -38,7 +39,8 @@ public class BaseCommand implements CommandExecutor, TabExecutor {
|
||||||
new RoundState(roundService),
|
new RoundState(roundService),
|
||||||
new Register(playerService),
|
new Register(playerService),
|
||||||
new StartRound(round, roundService),
|
new StartRound(round, roundService),
|
||||||
new Stuck(main)
|
new Stuck(main),
|
||||||
|
new Stats(statService)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
package com.alttd.hunger_games.commands.subcommands;
|
||||||
|
|
||||||
|
import com.alttd.hunger_games.commands.SubCommand;
|
||||||
|
import com.alttd.hunger_games.config.Messages;
|
||||||
|
import com.alttd.hunger_games.data_objects.LifetimeStats;
|
||||||
|
import com.alttd.hunger_games.services.StatService;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.OfflinePlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Stats extends SubCommand {
|
||||||
|
|
||||||
|
private final StatService statService;
|
||||||
|
|
||||||
|
public Stats(StatService statService) {
|
||||||
|
this.statService = statService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||||
|
OfflinePlayer target;
|
||||||
|
if (args.length < 2) {
|
||||||
|
if (!(commandSender instanceof Player player)) {
|
||||||
|
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
target = player;
|
||||||
|
} else {
|
||||||
|
target = Bukkit.getOfflinePlayerIfCached(args[1]);
|
||||||
|
if (target == null) {
|
||||||
|
target = Bukkit.getPlayer(args[1]);
|
||||||
|
}
|
||||||
|
if (target == null) {
|
||||||
|
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_NOT_FOUND.replaceAll("<player>", args[1]));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LifetimeStats stats = statService.getOrCreateLifetimeStats(target.getUniqueId());
|
||||||
|
|
||||||
|
commandSender.sendRichMessage(Messages.STATS.STATS_FORMAT,
|
||||||
|
Placeholder.parsed("player", target.getName() != null ? target.getName() : "Unknown"),
|
||||||
|
Placeholder.parsed("kills", String.valueOf(stats.getTotalKills())),
|
||||||
|
Placeholder.parsed("deaths", String.valueOf(stats.getTotalDeaths())),
|
||||||
|
Placeholder.parsed("damage_dealt", String.format("%.2f", stats.getTotalDamageDealt())),
|
||||||
|
Placeholder.parsed("damage_taken", String.format("%.2f", stats.getTotalDamageTaken())),
|
||||||
|
Placeholder.parsed("wins", String.valueOf(stats.getTotalWins())),
|
||||||
|
Placeholder.parsed("rounds_played", String.valueOf(stats.getTotalRoundsPlayed())),
|
||||||
|
Placeholder.parsed("kd_ratio", String.format("%.2f", stats.getKDRatio()))
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "stats";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||||
|
if (args.length == 2) {
|
||||||
|
return Bukkit.getOnlinePlayers().stream().map(Player::getName).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpMessage() {
|
||||||
|
return Messages.HELP.STATS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -31,6 +31,7 @@ public class Messages extends AbstractConfig {
|
||||||
public static String START_ROUND = "<green>Start the game: <gold>/hg start</gold></green>";
|
public static String START_ROUND = "<green>Start the game: <gold>/hg start</gold></green>";
|
||||||
public static String RELOAD = "<green>Reload config and messages: <gold>/hg reload</gold></green>";
|
public static String RELOAD = "<green>Reload config and messages: <gold>/hg reload</gold></green>";
|
||||||
public static String STUCK = "<green>Teleport to safety if stuck: <gold>/hg stuck</gold></green>";
|
public static String STUCK = "<green>Teleport to safety if stuck: <gold>/hg stuck</gold></green>";
|
||||||
|
public static String STATS = "<green>Show player stats: <gold>/hg stats [player]</gold></green>";
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
|
|
@ -41,6 +42,7 @@ public class Messages extends AbstractConfig {
|
||||||
START_ROUND = config.getString(prefix, "start", START_ROUND);
|
START_ROUND = config.getString(prefix, "start", START_ROUND);
|
||||||
RELOAD = config.getString(prefix, "reload", RELOAD);
|
RELOAD = config.getString(prefix, "reload", RELOAD);
|
||||||
STUCK = config.getString(prefix, "stuck", STUCK);
|
STUCK = config.getString(prefix, "stuck", STUCK);
|
||||||
|
STATS = config.getString(prefix, "stats", STATS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,4 +147,22 @@ public class Messages extends AbstractConfig {
|
||||||
ALREADY_WARMING_UP = config.getString(prefix, "already-warming-up", ALREADY_WARMING_UP);
|
ALREADY_WARMING_UP = config.getString(prefix, "already-warming-up", ALREADY_WARMING_UP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class STATS {
|
||||||
|
private static final String prefix = "stats.";
|
||||||
|
|
||||||
|
public static String STATS_FORMAT = "<gold>Stats for <player>:</gold>\n" +
|
||||||
|
"<green>Kills: <gold><kills></gold></green>\n" +
|
||||||
|
"<green>Deaths: <gold><deaths></gold></green>\n" +
|
||||||
|
"<green>Damage Dealt: <gold><damage_dealt></gold></green>\n" +
|
||||||
|
"<green>Damage Taken: <gold><damage_taken></gold></green>\n" +
|
||||||
|
"<green>Wins: <gold><wins></gold></green>\n" +
|
||||||
|
"<green>Rounds Played: <gold><rounds_played></gold></green>\n" +
|
||||||
|
"<green>K/D Ratio: <gold><kd_ratio></gold></green>";
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
private static void load() {
|
||||||
|
STATS_FORMAT = config.getString(prefix, "format", STATS_FORMAT);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.alttd.hunger_games.data_objects;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class LifetimeStats {
|
||||||
|
private final UUID uuid;
|
||||||
|
private int totalKills;
|
||||||
|
private int totalDeaths;
|
||||||
|
private double totalDamageDealt;
|
||||||
|
private double totalDamageTaken;
|
||||||
|
private int totalWins;
|
||||||
|
private int totalRoundsPlayed;
|
||||||
|
|
||||||
|
public void addRoundStats(RoundStats roundStats) {
|
||||||
|
totalKills += roundStats.getKills();
|
||||||
|
totalDeaths += roundStats.getDeaths();
|
||||||
|
totalDamageDealt += roundStats.getDamageDealt();
|
||||||
|
totalDamageTaken += roundStats.getDamageTaken();
|
||||||
|
if (roundStats.isWon()) {
|
||||||
|
totalWins++;
|
||||||
|
}
|
||||||
|
totalRoundsPlayed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getKDRatio() {
|
||||||
|
if (totalDeaths == 0) {
|
||||||
|
return totalKills;
|
||||||
|
}
|
||||||
|
return (double) totalKills / totalDeaths;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.alttd.hunger_games.data_objects;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class RoundStats {
|
||||||
|
private final UUID uuid;
|
||||||
|
private int kills;
|
||||||
|
private int deaths;
|
||||||
|
private double damageDealt;
|
||||||
|
private double damageTaken;
|
||||||
|
private int placement;
|
||||||
|
private boolean won;
|
||||||
|
|
||||||
|
public void addKill() {
|
||||||
|
kills++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDeath() {
|
||||||
|
deaths++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDamageDealt(double damage) {
|
||||||
|
damageDealt += damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDamageTaken(double damage) {
|
||||||
|
damageTaken += damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,10 +3,12 @@ package com.alttd.hunger_games.event_listeners;
|
||||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||||
import com.alttd.hunger_games.services.PlayerService;
|
import com.alttd.hunger_games.services.PlayerService;
|
||||||
import com.alttd.hunger_games.services.RoundService;
|
import com.alttd.hunger_games.services.RoundService;
|
||||||
|
import com.alttd.hunger_games.services.StatService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
import org.bukkit.event.entity.EntityDamageEvent;
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
|
|
@ -16,21 +18,37 @@ public class PlayerDamageListener implements Listener {
|
||||||
|
|
||||||
private final RoundService roundService;
|
private final RoundService roundService;
|
||||||
private final PlayerService playerService;
|
private final PlayerService playerService;
|
||||||
|
private final StatService statService;
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onEntityDamage(EntityDamageEvent event) {
|
public void onEntityDamage(EntityDamageEvent event) {
|
||||||
if (!(event.getEntity() instanceof Player)) {
|
if (!(event.getEntity() instanceof Player player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ROUND_STATE roundState = roundService.getRoundState();
|
ROUND_STATE roundState = roundService.getRoundState();
|
||||||
if (roundState != ROUND_STATE.KILL_PHASE && roundState != ROUND_STATE.FINALE) {
|
if (roundState != ROUND_STATE.KILL_PHASE && roundState != ROUND_STATE.FINALE) {
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
double damage = Math.min(event.getFinalDamage(), player.getHealth());
|
||||||
|
statService.addDamageTaken(player.getUniqueId(), damage);
|
||||||
|
|
||||||
|
if (event instanceof EntityDamageByEntityEvent damageByEntityEvent && damageByEntityEvent.getDamager() instanceof Player attacker) {
|
||||||
|
statService.addDamageDealt(attacker.getUniqueId(), damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerDeath(PlayerDeathEvent event) {
|
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||||
playerService.handlePlayerDeath(event.getPlayer());
|
Player player = event.getPlayer();
|
||||||
|
playerService.handlePlayerDeath(player);
|
||||||
|
statService.addDeath(player.getUniqueId());
|
||||||
|
|
||||||
|
Player killer = player.getKiller();
|
||||||
|
if (killer != null) {
|
||||||
|
statService.addKill(killer.getUniqueId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import com.alttd.hunger_games.Main;
|
||||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||||
import com.alttd.hunger_games.event_listeners.PlayerMovementListener;
|
import com.alttd.hunger_games.event_listeners.PlayerMovementListener;
|
||||||
|
import com.alttd.hunger_games.services.StatService;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
@ -18,23 +19,25 @@ public class RoundService implements RoundListener {
|
||||||
private PlayerMovementListener playerMovementListener;
|
private PlayerMovementListener playerMovementListener;
|
||||||
private final Main main;
|
private final Main main;
|
||||||
private final LootService lootService;
|
private final LootService lootService;
|
||||||
|
private final StatService statService;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private ROUND_STATE roundState;
|
private ROUND_STATE roundState;
|
||||||
private final HashMap<UUID, PLAYER_STATE> players = new HashMap<>();
|
private final HashMap<UUID, PLAYER_STATE> players = new HashMap<>();
|
||||||
|
|
||||||
public static RoundService createSingletonInstance(Round round, Main main, LootService lootService) {
|
public static RoundService createSingletonInstance(Round round, Main main, LootService lootService, StatService statService) {
|
||||||
if (instance != null) {
|
if (instance != null) {
|
||||||
throw new IllegalStateException("RoundService is already initialized.");
|
throw new IllegalStateException("RoundService is already initialized.");
|
||||||
}
|
}
|
||||||
instance = new RoundService(round, main, lootService);
|
instance = new RoundService(round, main, lootService, statService);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RoundService(Round round, Main main, LootService lootService) {
|
private RoundService(Round round, Main main, LootService lootService, StatService statService) {
|
||||||
this.roundState = round.register(this);
|
this.roundState = round.register(this);
|
||||||
this.main = main;
|
this.main = main;
|
||||||
this.lootService = lootService;
|
this.lootService = lootService;
|
||||||
|
this.statService = statService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<UUID> getPlayers(PLAYER_STATE playerState) {
|
public Set<UUID> getPlayers(PLAYER_STATE playerState) {
|
||||||
|
|
@ -45,7 +48,12 @@ public class RoundService implements RoundListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setPlayerState(UUID uuid, PLAYER_STATE playerState) {
|
protected void setPlayerState(UUID uuid, PLAYER_STATE playerState) {
|
||||||
players.put(uuid, playerState);
|
PLAYER_STATE oldState = players.put(uuid, playerState);
|
||||||
|
if (playerState == PLAYER_STATE.SPECTATING && oldState == PLAYER_STATE.REGISTERED) {
|
||||||
|
int remaining = getPlayers(PLAYER_STATE.REGISTERED).size();
|
||||||
|
statService.setPlacement(uuid, remaining + 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (!roundState.equals(ROUND_STATE.KILL_PHASE) && !roundState.equals(ROUND_STATE.FINALE)) {
|
if (!roundState.equals(ROUND_STATE.KILL_PHASE) && !roundState.equals(ROUND_STATE.FINALE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -58,6 +66,8 @@ public class RoundService implements RoundListener {
|
||||||
|
|
||||||
private void declareWinner(UUID winnerUUID) {
|
private void declareWinner(UUID winnerUUID) {
|
||||||
Player player = Bukkit.getPlayer(winnerUUID);
|
Player player = Bukkit.getPlayer(winnerUUID);
|
||||||
|
statService.setPlacement(winnerUUID, 1);
|
||||||
|
statService.setWon(winnerUUID, true);
|
||||||
//TODO message
|
//TODO message
|
||||||
//TODO reset round
|
//TODO reset round
|
||||||
}
|
}
|
||||||
|
|
|
||||||
121
src/main/java/com/alttd/hunger_games/services/StatService.java
Normal file
121
src/main/java/com/alttd/hunger_games/services/StatService.java
Normal file
|
|
@ -0,0 +1,121 @@
|
||||||
|
package com.alttd.hunger_games.services;
|
||||||
|
|
||||||
|
import com.alttd.hunger_games.Main;
|
||||||
|
import com.alttd.hunger_games.data_objects.LifetimeStats;
|
||||||
|
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||||
|
import com.alttd.hunger_games.data_objects.RoundStats;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class StatService implements RoundListener {
|
||||||
|
|
||||||
|
private final Main main;
|
||||||
|
private final Map<UUID, RoundStats> currentRoundStats = new ConcurrentHashMap<>();
|
||||||
|
private final Map<UUID, LifetimeStats> lifetimeStatsCache = new ConcurrentHashMap<>();
|
||||||
|
private final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
private final File statsDirectory;
|
||||||
|
private final File lifetimeStatsFile;
|
||||||
|
|
||||||
|
public StatService(Main main, Round round) {
|
||||||
|
this.main = main;
|
||||||
|
this.statsDirectory = new File(main.getDataFolder(), "stats");
|
||||||
|
if (!statsDirectory.exists()) {
|
||||||
|
statsDirectory.mkdirs();
|
||||||
|
}
|
||||||
|
this.lifetimeStatsFile = new File(statsDirectory, "lifetime.json");
|
||||||
|
round.register(this);
|
||||||
|
loadLifetimeStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoundStats getOrCreateRoundStats(UUID uuid) {
|
||||||
|
return currentRoundStats.computeIfAbsent(uuid, k -> RoundStats.builder().uuid(k).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public LifetimeStats getOrCreateLifetimeStats(UUID uuid) {
|
||||||
|
return lifetimeStatsCache.computeIfAbsent(uuid, k -> LifetimeStats.builder().uuid(k).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChange(ROUND_STATE roundState) {
|
||||||
|
if (roundState == ROUND_STATE.PLAYER_REGISTRATION) {
|
||||||
|
saveRoundStats();
|
||||||
|
currentRoundStats.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveRoundStats() {
|
||||||
|
if (currentRoundStats.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long timestamp = System.currentTimeMillis();
|
||||||
|
File roundFile = new File(statsDirectory, "round_" + timestamp + ".json");
|
||||||
|
try (FileWriter writer = new FileWriter(roundFile)) {
|
||||||
|
gson.toJson(currentRoundStats.values(), writer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
main.getLogger().severe("Could not save round stats: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RoundStats roundStats : currentRoundStats.values()) {
|
||||||
|
LifetimeStats lifetime = getOrCreateLifetimeStats(roundStats.getUuid());
|
||||||
|
lifetime.addRoundStats(roundStats);
|
||||||
|
}
|
||||||
|
saveLifetimeStats();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadLifetimeStats() {
|
||||||
|
if (!lifetimeStatsFile.exists()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try (FileReader reader = new FileReader(lifetimeStatsFile)) {
|
||||||
|
LifetimeStats[] stats = gson.fromJson(reader, LifetimeStats[].class);
|
||||||
|
if (stats != null) {
|
||||||
|
for (LifetimeStats s : stats) {
|
||||||
|
lifetimeStatsCache.put(s.getUuid(), s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
main.getLogger().severe("Could not load lifetime stats: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveLifetimeStats() {
|
||||||
|
try (FileWriter writer = new FileWriter(lifetimeStatsFile)) {
|
||||||
|
gson.toJson(lifetimeStatsCache.values(), writer);
|
||||||
|
} catch (IOException e) {
|
||||||
|
main.getLogger().severe("Could not save lifetime stats: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addKill(UUID uuid) {
|
||||||
|
getOrCreateRoundStats(uuid).addKill();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDeath(UUID uuid) {
|
||||||
|
getOrCreateRoundStats(uuid).addDeath();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDamageDealt(UUID uuid, double damage) {
|
||||||
|
getOrCreateRoundStats(uuid).addDamageDealt(damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addDamageTaken(UUID uuid, double damage) {
|
||||||
|
getOrCreateRoundStats(uuid).addDamageTaken(damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPlacement(UUID uuid, int placement) {
|
||||||
|
getOrCreateRoundStats(uuid).setPlacement(placement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWon(UUID uuid, boolean won) {
|
||||||
|
getOrCreateRoundStats(uuid).setWon(won);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user