mirror of
https://github.com/WiIIiam278/HuskSync.git
synced 2025-12-27 10:39:11 +00:00
Start work on Velocity support
This commit is contained in:
10
common/src/main/java/me/william278/husksync/Server.java
Normal file
10
common/src/main/java/me/william278/husksync/Server.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package me.william278.husksync;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A record representing a server synchronised on the network and whether it has MySqlPlayerDataBridge installed
|
||||
*/
|
||||
public record Server(UUID serverUUID, boolean hasMySqlPlayerDataBridge, String huskSyncVersion, String serverBrand,
|
||||
String clusterId) {
|
||||
}
|
||||
@@ -76,7 +76,7 @@ public class Settings {
|
||||
|
||||
public enum ServerType {
|
||||
BUKKIT,
|
||||
BUNGEECORD
|
||||
PROXY,
|
||||
}
|
||||
|
||||
public enum DataStorageType {
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
package me.william278.husksync.proxy.data;
|
||||
|
||||
import me.william278.husksync.PlayerData;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.proxy.data.sql.Database;
|
||||
import me.william278.husksync.proxy.data.sql.MySQL;
|
||||
import me.william278.husksync.proxy.data.sql.SQLite;
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.*;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class DataManager {
|
||||
|
||||
/**
|
||||
* The player data cache for each cluster ID
|
||||
*/
|
||||
public HashMap<Settings.SynchronisationCluster, PlayerDataCache> playerDataCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Map of the database assigned for each cluster
|
||||
*/
|
||||
private final HashMap<String, Database> clusterDatabases;
|
||||
|
||||
// Retrieve database connection for a cluster
|
||||
public Connection getConnection(String clusterId) throws SQLException {
|
||||
return clusterDatabases.get(clusterId).getConnection();
|
||||
}
|
||||
|
||||
// Console logger for errors
|
||||
private final Logger logger;
|
||||
|
||||
// Plugin data folder
|
||||
private final File dataFolder;
|
||||
|
||||
// Flag variable identifying if the data manager failed to initialize
|
||||
public boolean hasFailedInitialization = false;
|
||||
|
||||
public DataManager(Logger logger, File dataFolder) {
|
||||
this.logger = logger;
|
||||
this.dataFolder = dataFolder;
|
||||
clusterDatabases = new HashMap<>();
|
||||
initializeDatabases();
|
||||
}
|
||||
|
||||
private void initializeDatabases() {
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
Database clusterDatabase = switch (Settings.dataStorageType) {
|
||||
case SQLITE -> new SQLite(cluster, dataFolder, logger);
|
||||
case MYSQL -> new MySQL(cluster, logger);
|
||||
};
|
||||
clusterDatabase.load();
|
||||
clusterDatabase.createTables();
|
||||
clusterDatabases.put(cluster.clusterId(), clusterDatabase);
|
||||
}
|
||||
|
||||
// Abort loading if the database failed to initialize
|
||||
for (Database database : clusterDatabases.values()) {
|
||||
if (database.isInactive()) {
|
||||
hasFailedInitialization = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the database connections
|
||||
*/
|
||||
public void closeDatabases() {
|
||||
for (Database database : clusterDatabases.values()) {
|
||||
database.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is registered on the database.
|
||||
* If not, register them to the database
|
||||
* If they are, ensure that their player name is up-to-date on the database
|
||||
*
|
||||
* @param playerUUID The UUID of the player to register
|
||||
*/
|
||||
public void ensurePlayerExists(UUID playerUUID, String playerName) {
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
if (!playerExists(playerUUID, cluster)) {
|
||||
createPlayerEntry(playerUUID, playerName, cluster);
|
||||
} else {
|
||||
updatePlayerName(playerUUID, playerName, cluster);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player is registered in SQL (an entry in the PLAYER_TABLE)
|
||||
*
|
||||
* @param playerUUID The UUID of the player
|
||||
* @return {@code true} if the player is on the player table
|
||||
*/
|
||||
private boolean playerExists(UUID playerUUID, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + cluster.playerTableName() + " WHERE `uuid`=?;")) {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
return resultSet.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createPlayerEntry(UUID playerUUID, String playerName, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO " + cluster.playerTableName() + " (`uuid`,`username`) VALUES(?,?);")) {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
statement.setString(2, playerName);
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePlayerName(UUID playerUUID, String playerName, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE " + cluster.playerTableName() + " SET `username`=? WHERE `uuid`=?;")) {
|
||||
statement.setString(1, playerName);
|
||||
statement.setString(2, playerUUID.toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a player's PlayerData by their username
|
||||
*
|
||||
* @param playerName The PlayerName of the data to get
|
||||
* @return Their {@link PlayerData}; or {@code null} if the player does not exist
|
||||
*/
|
||||
public PlayerData getPlayerDataByName(String playerName, String clusterId) {
|
||||
PlayerData playerData = null;
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
if (cluster.clusterId().equals(clusterId)) {
|
||||
try (Connection connection = getConnection(clusterId)) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + cluster.playerTableName() + " WHERE `username`=? LIMIT 1;")) {
|
||||
statement.setString(1, playerName);
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
final UUID uuid = UUID.fromString(resultSet.getString("uuid"));
|
||||
|
||||
// Get the player data from the cache if it's there, otherwise pull from SQL
|
||||
playerData = playerDataCache.get(cluster).getPlayer(uuid);
|
||||
if (playerData == null) {
|
||||
playerData = Objects.requireNonNull(getPlayerData(uuid)).get(cluster);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return playerData;
|
||||
}
|
||||
|
||||
public Map<Settings.SynchronisationCluster, PlayerData> getPlayerData(UUID playerUUID) {
|
||||
HashMap<Settings.SynchronisationCluster, PlayerData> data = new HashMap<>();
|
||||
for (Settings.SynchronisationCluster cluster : Settings.clusters) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + cluster.dataTableName() + " WHERE `player_id`=(SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?);")) {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
if (resultSet.next()) {
|
||||
final UUID dataVersionUUID = UUID.fromString(resultSet.getString("version_uuid"));
|
||||
//final Timestamp dataSaveTimestamp = resultSet.getTimestamp("timestamp");
|
||||
final String serializedInventory = resultSet.getString("inventory");
|
||||
final String serializedEnderChest = resultSet.getString("ender_chest");
|
||||
final double health = resultSet.getDouble("health");
|
||||
final double maxHealth = resultSet.getDouble("max_health");
|
||||
final double healthScale = resultSet.getDouble("health_scale");
|
||||
final int hunger = resultSet.getInt("hunger");
|
||||
final float saturation = resultSet.getFloat("saturation");
|
||||
final float saturationExhaustion = resultSet.getFloat("saturation_exhaustion");
|
||||
final int selectedSlot = resultSet.getInt("selected_slot");
|
||||
final String serializedStatusEffects = resultSet.getString("status_effects");
|
||||
final int totalExperience = resultSet.getInt("total_experience");
|
||||
final int expLevel = resultSet.getInt("exp_level");
|
||||
final float expProgress = resultSet.getFloat("exp_progress");
|
||||
final String gameMode = resultSet.getString("game_mode");
|
||||
final boolean isFlying = resultSet.getBoolean("is_flying");
|
||||
final String serializedAdvancementData = resultSet.getString("advancements");
|
||||
final String serializedLocationData = resultSet.getString("location");
|
||||
final String serializedStatisticData = resultSet.getString("statistics");
|
||||
|
||||
data.put(cluster, new PlayerData(playerUUID, dataVersionUUID, serializedInventory, serializedEnderChest,
|
||||
health, maxHealth, healthScale, hunger, saturation, saturationExhaustion, selectedSlot, serializedStatusEffects,
|
||||
totalExperience, expLevel, expProgress, gameMode, serializedStatisticData, isFlying,
|
||||
serializedAdvancementData, serializedLocationData));
|
||||
} else {
|
||||
data.put(cluster, PlayerData.DEFAULT_PLAYER_DATA(playerUUID));
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void updatePlayerData(PlayerData playerData, Settings.SynchronisationCluster cluster) {
|
||||
// Ignore if the Spigot server didn't properly sync the previous data
|
||||
|
||||
// Add the new player data to the cache
|
||||
playerDataCache.get(cluster).updatePlayer(playerData);
|
||||
|
||||
// SQL: If the player has cached data, update it, otherwise insert new data.
|
||||
if (playerHasCachedData(playerData.getPlayerUUID(), cluster)) {
|
||||
updatePlayerSQLData(playerData, cluster);
|
||||
} else {
|
||||
insertPlayerData(playerData, cluster);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePlayerSQLData(PlayerData playerData, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"UPDATE " + cluster.dataTableName() + " SET `version_uuid`=?, `timestamp`=?, `inventory`=?, `ender_chest`=?, `health`=?, `max_health`=?, `health_scale`=?, `hunger`=?, `saturation`=?, `saturation_exhaustion`=?, `selected_slot`=?, `status_effects`=?, `total_experience`=?, `exp_level`=?, `exp_progress`=?, `game_mode`=?, `statistics`=?, `is_flying`=?, `advancements`=?, `location`=? WHERE `player_id`=(SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?);")) {
|
||||
statement.setString(1, playerData.getDataVersionUUID().toString());
|
||||
statement.setTimestamp(2, new Timestamp(Instant.now().getEpochSecond()));
|
||||
statement.setString(3, playerData.getSerializedInventory());
|
||||
statement.setString(4, playerData.getSerializedEnderChest());
|
||||
statement.setDouble(5, playerData.getHealth()); // Health
|
||||
statement.setDouble(6, playerData.getMaxHealth()); // Max health
|
||||
statement.setDouble(7, playerData.getHealthScale()); // Health scale
|
||||
statement.setInt(8, playerData.getHunger()); // Hunger
|
||||
statement.setFloat(9, playerData.getSaturation()); // Saturation
|
||||
statement.setFloat(10, playerData.getSaturationExhaustion()); // Saturation exhaustion
|
||||
statement.setInt(11, playerData.getSelectedSlot()); // Current selected slot
|
||||
statement.setString(12, playerData.getSerializedEffectData()); // Status effects
|
||||
statement.setInt(13, playerData.getTotalExperience()); // Total Experience
|
||||
statement.setInt(14, playerData.getExpLevel()); // Exp level
|
||||
statement.setFloat(15, playerData.getExpProgress()); // Exp progress
|
||||
statement.setString(16, playerData.getGameMode()); // GameMode
|
||||
statement.setString(17, playerData.getSerializedStatistics()); // Statistics
|
||||
statement.setBoolean(18, playerData.isFlying()); // Is flying
|
||||
statement.setString(19, playerData.getSerializedAdvancements()); // Advancements
|
||||
statement.setString(20, playerData.getSerializedLocation()); // Location
|
||||
|
||||
statement.setString(21, playerData.getPlayerUUID().toString());
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void insertPlayerData(PlayerData playerData, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"INSERT INTO " + cluster.dataTableName() + " (`player_id`,`version_uuid`,`timestamp`,`inventory`,`ender_chest`,`health`,`max_health`,`health_scale`,`hunger`,`saturation`,`saturation_exhaustion`,`selected_slot`,`status_effects`,`total_experience`,`exp_level`,`exp_progress`,`game_mode`,`statistics`,`is_flying`,`advancements`,`location`) VALUES((SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);")) {
|
||||
statement.setString(1, playerData.getPlayerUUID().toString());
|
||||
statement.setString(2, playerData.getDataVersionUUID().toString());
|
||||
statement.setTimestamp(3, new Timestamp(Instant.now().getEpochSecond()));
|
||||
statement.setString(4, playerData.getSerializedInventory());
|
||||
statement.setString(5, playerData.getSerializedEnderChest());
|
||||
statement.setDouble(6, playerData.getHealth()); // Health
|
||||
statement.setDouble(7, playerData.getMaxHealth()); // Max health
|
||||
statement.setDouble(8, playerData.getHealthScale()); // Health scale
|
||||
statement.setInt(9, playerData.getHunger()); // Hunger
|
||||
statement.setFloat(10, playerData.getSaturation()); // Saturation
|
||||
statement.setFloat(11, playerData.getSaturationExhaustion()); // Saturation exhaustion
|
||||
statement.setInt(12, playerData.getSelectedSlot()); // Current selected slot
|
||||
statement.setString(13, playerData.getSerializedEffectData()); // Status effects
|
||||
statement.setInt(14, playerData.getTotalExperience()); // Total Experience
|
||||
statement.setInt(15, playerData.getExpLevel()); // Exp level
|
||||
statement.setFloat(16, playerData.getExpProgress()); // Exp progress
|
||||
statement.setString(17, playerData.getGameMode()); // GameMode
|
||||
statement.setString(18, playerData.getSerializedStatistics()); // Statistics
|
||||
statement.setBoolean(19, playerData.isFlying()); // Is flying
|
||||
statement.setString(20, playerData.getSerializedAdvancements()); // Advancements
|
||||
statement.setString(21, playerData.getSerializedLocation()); // Location
|
||||
|
||||
statement.executeUpdate();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the player has cached data saved in SQL (an entry in the DATA_TABLE)
|
||||
*
|
||||
* @param playerUUID The UUID of the player
|
||||
* @return {@code true} if the player has an entry in the data table
|
||||
*/
|
||||
private boolean playerHasCachedData(UUID playerUUID, Settings.SynchronisationCluster cluster) {
|
||||
try (Connection connection = getConnection(cluster.clusterId())) {
|
||||
try (PreparedStatement statement = connection.prepareStatement(
|
||||
"SELECT * FROM " + cluster.dataTableName() + " WHERE `player_id`=(SELECT `id` FROM " + cluster.playerTableName() + " WHERE `uuid`=?);")) {
|
||||
statement.setString(1, playerUUID.toString());
|
||||
ResultSet resultSet = statement.executeQuery();
|
||||
return resultSet.next();
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An SQL exception occurred", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A cache of PlayerData
|
||||
*/
|
||||
public static class PlayerDataCache {
|
||||
// The cached player data
|
||||
public HashSet<PlayerData> playerData;
|
||||
|
||||
public PlayerDataCache() {
|
||||
playerData = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update ar add data for a player to the cache
|
||||
*
|
||||
* @param newData The player's new/updated {@link PlayerData}
|
||||
*/
|
||||
public void updatePlayer(PlayerData newData) {
|
||||
// Remove the old data if it exists
|
||||
PlayerData oldData = null;
|
||||
for (PlayerData data : playerData) {
|
||||
if (data.getPlayerUUID() == newData.getPlayerUUID()) {
|
||||
oldData = data;
|
||||
}
|
||||
}
|
||||
if (oldData != null) {
|
||||
playerData.remove(oldData);
|
||||
}
|
||||
|
||||
// Add the new data
|
||||
playerData.add(newData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a player's {@link PlayerData} by their {@link UUID}
|
||||
*
|
||||
* @param playerUUID The {@link UUID} of the player to check
|
||||
* @return The player's {@link PlayerData}
|
||||
*/
|
||||
public PlayerData getPlayer(UUID playerUUID) {
|
||||
for (PlayerData data : playerData) {
|
||||
if (data.getPlayerUUID() == playerUUID) {
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package me.william278.husksync.proxy.data.sql;
|
||||
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class Database {
|
||||
|
||||
public String dataPoolName;
|
||||
public Settings.SynchronisationCluster cluster;
|
||||
public final Logger logger;
|
||||
|
||||
public Database(Settings.SynchronisationCluster cluster, Logger logger) {
|
||||
this.cluster = cluster;
|
||||
this.dataPoolName = cluster != null ? "HuskSyncHikariPool-" + cluster.clusterId() : "HuskSyncMigratorPool";
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public abstract Connection getConnection() throws SQLException;
|
||||
|
||||
public boolean isInactive() {
|
||||
try {
|
||||
return getConnection() == null;
|
||||
} catch (SQLException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void load();
|
||||
|
||||
public abstract void createTables();
|
||||
|
||||
public abstract void close();
|
||||
|
||||
public final int hikariMaximumPoolSize = Settings.hikariMaximumPoolSize;
|
||||
public final int hikariMinimumIdle = Settings.hikariMinimumIdle;
|
||||
public final long hikariMaximumLifetime = Settings.hikariMaximumLifetime;
|
||||
public final long hikariKeepAliveTime = Settings.hikariKeepAliveTime;
|
||||
public final long hikariConnectionTimeOut = Settings.hikariConnectionTimeOut;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package me.william278.husksync.proxy.data.sql;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class MySQL extends Database {
|
||||
|
||||
final String[] SQL_SETUP_STATEMENTS = {
|
||||
"CREATE TABLE IF NOT EXISTS " + cluster.playerTableName() + " (" +
|
||||
"`id` integer NOT NULL AUTO_INCREMENT," +
|
||||
"`uuid` char(36) NOT NULL UNIQUE," +
|
||||
"`username` varchar(16) NOT NULL," +
|
||||
|
||||
"PRIMARY KEY (`id`)" +
|
||||
");",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS " + cluster.dataTableName() + " (" +
|
||||
"`player_id` integer NOT NULL," +
|
||||
"`version_uuid` char(36) NOT NULL UNIQUE," +
|
||||
"`timestamp` datetime NOT NULL," +
|
||||
"`inventory` longtext NOT NULL," +
|
||||
"`ender_chest` longtext NOT NULL," +
|
||||
"`health` double NOT NULL," +
|
||||
"`max_health` double NOT NULL," +
|
||||
"`health_scale` double NOT NULL," +
|
||||
"`hunger` integer NOT NULL," +
|
||||
"`saturation` float NOT NULL," +
|
||||
"`saturation_exhaustion` float NOT NULL," +
|
||||
"`selected_slot` integer NOT NULL," +
|
||||
"`status_effects` longtext NOT NULL," +
|
||||
"`total_experience` integer NOT NULL," +
|
||||
"`exp_level` integer NOT NULL," +
|
||||
"`exp_progress` float NOT NULL," +
|
||||
"`game_mode` tinytext NOT NULL," +
|
||||
"`statistics` longtext NOT NULL," +
|
||||
"`is_flying` boolean NOT NULL," +
|
||||
"`advancements` longtext NOT NULL," +
|
||||
"`location` text NOT NULL," +
|
||||
|
||||
"PRIMARY KEY (`player_id`,`version_uuid`)," +
|
||||
"FOREIGN KEY (`player_id`) REFERENCES " + cluster.playerTableName() + " (`id`)" +
|
||||
");"
|
||||
|
||||
};
|
||||
|
||||
public String host = Settings.mySQLHost;
|
||||
public int port = Settings.mySQLPort;
|
||||
public String database = Settings.mySQLDatabase;
|
||||
public String username = Settings.mySQLUsername;
|
||||
public String password = Settings.mySQLPassword;
|
||||
public String params = Settings.mySQLParams;
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
public MySQL(Settings.SynchronisationCluster cluster, Logger logger) {
|
||||
super(cluster, logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// Create new HikariCP data source
|
||||
final String jdbcUrl = "jdbc:mysql://" + host + ":" + port + "/" + database + params;
|
||||
dataSource = new HikariDataSource();
|
||||
dataSource.setJdbcUrl(jdbcUrl);
|
||||
|
||||
dataSource.setUsername(username);
|
||||
dataSource.setPassword(password);
|
||||
|
||||
// Set various additional parameters
|
||||
dataSource.setMaximumPoolSize(hikariMaximumPoolSize);
|
||||
dataSource.setMinimumIdle(hikariMinimumIdle);
|
||||
dataSource.setMaxLifetime(hikariMaximumLifetime);
|
||||
dataSource.setKeepaliveTime(hikariKeepAliveTime);
|
||||
dataSource.setConnectionTimeout(hikariConnectionTimeOut);
|
||||
dataSource.setPoolName(dataPoolName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTables() {
|
||||
// Create tables
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
for (String tableCreationStatement : SQL_SETUP_STATEMENTS) {
|
||||
statement.execute(tableCreationStatement);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An error occurred creating tables on the MySQL database: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package me.william278.husksync.proxy.data.sql;
|
||||
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import me.william278.husksync.Settings;
|
||||
import me.william278.husksync.util.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SQLite extends Database {
|
||||
|
||||
final String[] SQL_SETUP_STATEMENTS = {
|
||||
"PRAGMA foreign_keys = ON;",
|
||||
"PRAGMA encoding = 'UTF-8';",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS " + cluster.playerTableName() + " (" +
|
||||
"`id` integer PRIMARY KEY," +
|
||||
"`uuid` char(36) NOT NULL UNIQUE," +
|
||||
"`username` varchar(16) NOT NULL" +
|
||||
");",
|
||||
|
||||
"CREATE TABLE IF NOT EXISTS " + cluster.dataTableName() + " (" +
|
||||
"`player_id` integer NOT NULL REFERENCES " + cluster.playerTableName() + "(`id`)," +
|
||||
"`version_uuid` char(36) NOT NULL UNIQUE," +
|
||||
"`timestamp` datetime NOT NULL," +
|
||||
"`inventory` longtext NOT NULL," +
|
||||
"`ender_chest` longtext NOT NULL," +
|
||||
"`health` double NOT NULL," +
|
||||
"`max_health` double NOT NULL," +
|
||||
"`health_scale` double NOT NULL," +
|
||||
"`hunger` integer NOT NULL," +
|
||||
"`saturation` float NOT NULL," +
|
||||
"`saturation_exhaustion` float NOT NULL," +
|
||||
"`selected_slot` integer NOT NULL," +
|
||||
"`status_effects` longtext NOT NULL," +
|
||||
"`total_experience` integer NOT NULL," +
|
||||
"`exp_level` integer NOT NULL," +
|
||||
"`exp_progress` float NOT NULL," +
|
||||
"`game_mode` tinytext NOT NULL," +
|
||||
"`statistics` longtext NOT NULL," +
|
||||
"`is_flying` boolean NOT NULL," +
|
||||
"`advancements` longtext NOT NULL," +
|
||||
"`location` text NOT NULL," +
|
||||
|
||||
"PRIMARY KEY (`player_id`,`version_uuid`)" +
|
||||
");"
|
||||
};
|
||||
|
||||
private String getDatabaseName() {
|
||||
return cluster.databaseName() + "Data";
|
||||
}
|
||||
|
||||
private final File dataFolder;
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
public SQLite(Settings.SynchronisationCluster cluster, File dataFolder, Logger logger) {
|
||||
super(cluster, logger);
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
// Create the database file if it does not exist yet
|
||||
private void createDatabaseFileIfNotExist() {
|
||||
File databaseFile = new File(dataFolder, getDatabaseName() + ".db");
|
||||
if (!databaseFile.exists()) {
|
||||
try {
|
||||
if (!databaseFile.createNewFile()) {
|
||||
logger.log(Level.SEVERE, "Failed to write new file: " + getDatabaseName() + ".db (file already exists)");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "An error occurred writing a file: " + getDatabaseName() + ".db (" + e.getCause() + ")", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
return dataSource.getConnection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// Make SQLite database file
|
||||
createDatabaseFileIfNotExist();
|
||||
|
||||
// Create new HikariCP data source
|
||||
final String jdbcUrl = "jdbc:sqlite:" + dataFolder.getAbsolutePath() + File.separator + getDatabaseName() + ".db";
|
||||
dataSource = new HikariDataSource();
|
||||
dataSource.setDataSourceClassName("org.sqlite.SQLiteDataSource");
|
||||
dataSource.addDataSourceProperty("url", jdbcUrl);
|
||||
|
||||
// Set various additional parameters
|
||||
dataSource.setMaximumPoolSize(hikariMaximumPoolSize);
|
||||
dataSource.setMinimumIdle(hikariMinimumIdle);
|
||||
dataSource.setMaxLifetime(hikariMaximumLifetime);
|
||||
dataSource.setKeepaliveTime(hikariKeepAliveTime);
|
||||
dataSource.setConnectionTimeout(hikariConnectionTimeOut);
|
||||
dataSource.setPoolName(dataPoolName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createTables() {
|
||||
// Create tables
|
||||
try (Connection connection = dataSource.getConnection()) {
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
for (String tableCreationStatement : SQL_SETUP_STATEMENTS) {
|
||||
statement.execute(tableCreationStatement);
|
||||
}
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.log(Level.SEVERE, "An error occurred creating tables on the SQLite database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
19
common/src/main/java/me/william278/husksync/util/Logger.java
Normal file
19
common/src/main/java/me/william278/husksync/util/Logger.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package me.william278.husksync.util;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Logger interface to allow for implementation of different logger platforms used by Bungee and Velocity
|
||||
*/
|
||||
public interface Logger {
|
||||
|
||||
void log(Level level, String message, Exception e);
|
||||
|
||||
void log(Level level, String message);
|
||||
|
||||
void info(String message);
|
||||
|
||||
void severe(String message);
|
||||
|
||||
void config(String message);
|
||||
}
|
||||
Reference in New Issue
Block a user