Add optional MySQL support for statistics storage
Build / build (push) Successful in 1m15s

This commit is contained in:
Michael Burgess
2026-08-07 06:09:24 -04:00
parent bcb603dadf
commit 1c16f9a83b
7 changed files with 128 additions and 25 deletions
@@ -1,6 +1,7 @@
package us.tss3.blockparty.persistence;
import org.bukkit.plugin.Plugin;
import us.tss3.blockparty.config.ConfigManager.StorageSettings;
import java.io.File;
import java.sql.Connection;
@@ -12,37 +13,72 @@ import java.sql.Statement;
import java.util.UUID;
import java.util.logging.Level;
/** SQLite-backed statistics storage. All public methods are safe to call from any thread;
* callers are expected to invoke them off the main thread (see StatsManager). */
/** SQLite- or MySQL-backed statistics storage, selected via storage.type in config.yml.
* All public methods are safe to call from any thread; callers are expected to invoke
* them off the main thread (see StatsManager). */
public class StatsDatabase {
private final Plugin plugin;
private final StorageSettings settings;
private final String table;
private Connection connection;
public StatsDatabase(Plugin plugin) {
public StatsDatabase(Plugin plugin, StorageSettings settings) {
this.plugin = plugin;
this.settings = settings;
this.table = settings.isMysql() ? settings.tablePrefix() + "player_stats" : "player_stats";
}
public synchronized void connect() {
try {
File dbFile = new File(plugin.getDataFolder(), "blockparty.db");
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
if (settings.isMysql()) {
connectMysql();
} else {
connectSqlite();
}
try (Statement st = connection.createStatement()) {
st.execute("CREATE TABLE IF NOT EXISTS player_stats (" +
"uuid TEXT PRIMARY KEY," +
"games_played INTEGER NOT NULL DEFAULT 0," +
"wins INTEGER NOT NULL DEFAULT 0," +
"eliminations INTEGER NOT NULL DEFAULT 0," +
"best_round INTEGER NOT NULL DEFAULT 0," +
"total_rounds_survived INTEGER NOT NULL DEFAULT 0" +
")");
st.execute(createTableStatement());
}
} catch (Exception ex) {
plugin.getLogger().log(Level.SEVERE, "Failed to initialize SQLite stats database", ex);
plugin.getLogger().log(Level.SEVERE, "Failed to initialize " +
(settings.isMysql() ? "MySQL" : "SQLite") + " stats database", ex);
}
}
private void connectSqlite() throws Exception {
File dbFile = new File(plugin.getDataFolder(), "blockparty.db");
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + dbFile.getAbsolutePath());
}
private void connectMysql() throws Exception {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://" + settings.host() + ":" + settings.port() + "/" + settings.database()
+ "?useSSL=" + settings.useSsl() + "&autoReconnect=true&characterEncoding=utf8";
connection = DriverManager.getConnection(url, settings.username(), settings.password());
}
private String createTableStatement() {
if (settings.isMysql()) {
return "CREATE TABLE IF NOT EXISTS " + table + " (" +
"uuid VARCHAR(36) PRIMARY KEY," +
"games_played INT NOT NULL DEFAULT 0," +
"wins INT NOT NULL DEFAULT 0," +
"eliminations INT NOT NULL DEFAULT 0," +
"best_round INT NOT NULL DEFAULT 0," +
"total_rounds_survived INT NOT NULL DEFAULT 0" +
")";
}
return "CREATE TABLE IF NOT EXISTS " + table + " (" +
"uuid TEXT PRIMARY KEY," +
"games_played INTEGER NOT NULL DEFAULT 0," +
"wins INTEGER NOT NULL DEFAULT 0," +
"eliminations INTEGER NOT NULL DEFAULT 0," +
"best_round INTEGER NOT NULL DEFAULT 0," +
"total_rounds_survived INTEGER NOT NULL DEFAULT 0" +
")";
}
public synchronized void close() {
try {
if (connection != null && !connection.isClosed()) {
@@ -53,8 +89,10 @@ public class StatsDatabase {
}
private synchronized void ensureRow(String uuid) throws SQLException {
try (PreparedStatement ps = connection.prepareStatement(
"INSERT OR IGNORE INTO player_stats(uuid) VALUES (?)")) {
String sql = settings.isMysql()
? "INSERT IGNORE INTO " + table + "(uuid) VALUES (?)"
: "INSERT OR IGNORE INTO " + table + "(uuid) VALUES (?)";
try (PreparedStatement ps = connection.prepareStatement(sql)) {
ps.setString(1, uuid);
ps.executeUpdate();
}
@@ -67,7 +105,7 @@ public class StatsDatabase {
try {
ensureRow(uuid.toString());
try (PreparedStatement ps = connection.prepareStatement(
"SELECT games_played, wins, eliminations, best_round, total_rounds_survived FROM player_stats WHERE uuid=?")) {
"SELECT games_played, wins, eliminations, best_round, total_rounds_survived FROM " + table + " WHERE uuid=?")) {
ps.setString(1, uuid.toString());
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
@@ -100,7 +138,7 @@ public class StatsDatabase {
try {
ensureRow(uuid.toString());
try (PreparedStatement ps = connection.prepareStatement(
"UPDATE player_stats SET best_round = MAX(best_round, ?), total_rounds_survived = total_rounds_survived + 1 WHERE uuid=?")) {
"UPDATE " + table + " SET best_round = MAX(best_round, ?), total_rounds_survived = total_rounds_survived + 1 WHERE uuid=?")) {
ps.setInt(1, round);
ps.setString(2, uuid.toString());
ps.executeUpdate();
@@ -117,7 +155,7 @@ public class StatsDatabase {
try {
ensureRow(uuid.toString());
try (PreparedStatement ps = connection.prepareStatement(
"UPDATE player_stats SET " + setClause + " WHERE uuid=?")) {
"UPDATE " + table + " SET " + setClause + " WHERE uuid=?")) {
ps.setString(1, uuid.toString());
ps.executeUpdate();
}