Files
BoatParty/src/main/java/us/tss3/boatparty/stats/StatsManager.java
T
Michael BurgessandClaude Sonnet 5 b56d4b064b
CI / build (push) Successful in 1m58s
Add player stat collection with pluggable YAML/MySQL storage
Tracks races played, wins, total laps, and best times per player and
per race, recorded off the main thread after each finish. Backed by
a StatsStorage interface with two implementations: the default
YamlStatsStorage (stats.yml, zero setup) and MySqlStatsStorage
(HikariCP-pooled, auto-creates its tables, configured via
config.yml). MySQL init failures fall back to YAML rather than
blocking plugin startup. Adds /boatparty stats and /boatparty top
commands. Bundles and relocates HikariCP + MySQL Connector/J via
shadow (bumped to 9.0.2, the prior 8.3.5 couldn't shade Java 25
class files).

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-08-08 09:00:13 -04:00

76 lines
3.0 KiB
Java

package us.tss3.boatparty.stats;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
public final class StatsManager {
private final JavaPlugin plugin;
private StatsStorage storage;
public StatsManager(JavaPlugin plugin) {
this.plugin = plugin;
this.storage = buildStorage(plugin);
}
private static StatsStorage buildStorage(JavaPlugin plugin) {
FileConfiguration config = plugin.getConfig();
String type = config.getString("storage.type", "yaml");
if ("mysql".equalsIgnoreCase(type)) {
return new MySqlStatsStorage(
config.getString("storage.mysql.host", "localhost"),
config.getInt("storage.mysql.port", 3306),
config.getString("storage.mysql.database", "boatparty"),
config.getString("storage.mysql.username", "root"),
config.getString("storage.mysql.password", ""),
config.getBoolean("storage.mysql.use-ssl", false),
config.getString("storage.mysql.table-prefix", "boatparty_"),
plugin.getLogger());
}
return new YamlStatsStorage(plugin.getDataFolder(), plugin.getLogger());
}
public void init() {
try {
storage.init();
} catch (Exception e) {
plugin.getLogger().severe("Failed to initialize " + storage.getClass().getSimpleName()
+ ", falling back to YAML stats storage: " + e.getMessage());
storage = new YamlStatsStorage(plugin.getDataFolder(), plugin.getLogger());
try {
storage.init();
} catch (Exception fallbackError) {
throw new IllegalStateException("Failed to initialize fallback YAML stats storage", fallbackError);
}
}
}
public void close() {
storage.close();
}
public void recordFinishAsync(UUID uuid, String playerName, String raceName, int place, long timeMillis, int laps) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin,
() -> storage.recordFinish(uuid, playerName, raceName, place, timeMillis, laps));
}
public void getStatsAsync(UUID uuid, Consumer<PlayerStats> callback) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
PlayerStats stats = storage.getStats(uuid);
plugin.getServer().getScheduler().runTask(plugin, () -> callback.accept(stats));
});
}
public void getTopTimesAsync(String raceName, int limit, Consumer<List<TopEntry>> callback) {
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, () -> {
List<TopEntry> entries = storage.getTopTimes(raceName, limit);
plugin.getServer().getScheduler().runTask(plugin, () -> callback.accept(entries));
});
}
}