v2.2.6: Crafting inventory safety, Maria v11 support (#153)

* Clear player inventory crafting slots on sync

* Bundle Maria driver for v11 support
feat/data-edit-commands 2.2.6
William 2 years ago committed by GitHub
parent 4ed8b94d55
commit 12e882fe22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,8 +14,10 @@ defaultTasks 'licenseFormat', 'build'
ext { ext {
set 'version', version.toString() set 'version', version.toString()
set 'description', description.toString() set 'description', description.toString()
set 'jedis_version', jedis_version.toString() set 'jedis_version', jedis_version.toString()
set 'mysql_driver_version', mysql_driver_version.toString() set 'mysql_driver_version', mysql_driver_version.toString()
set 'mariadb_driver_version', mariadb_driver_version.toString()
set 'snappy_version', snappy_version.toString() set 'snappy_version', snappy_version.toString()
set 'commons_text_version', commons_text_version.toString() set 'commons_text_version', commons_text_version.toString()
} }

@ -130,8 +130,8 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
// Prepare database connection // Prepare database connection
this.database = new MySqlDatabase(this); this.database = new MySqlDatabase(this);
log(Level.INFO, "Attempting to establish connection to the " + settings.getSqlType().getDisplayName() + " database..."); log(Level.INFO, "Attempting to establish connection to the " + settings.getDatabaseType().getDisplayName() + " database...");
initialized.set(this.database.initialize()); this.database.initialize();
if (initialized.get()) { if (initialized.get()) {
log(Level.INFO, "Successfully established a connection to the database"); log(Level.INFO, "Successfully established a connection to the database");
} else { } else {
@ -195,7 +195,7 @@ public class BukkitHuskSync extends JavaPlugin implements HuskSync {
"An update is available for HuskSync, v" + newVersion "An update is available for HuskSync, v" + newVersion
+ " (Currently running v" + getPluginVersion() + ")"))); + " (Currently running v" + getPluginVersion() + ")")));
} }
} catch (HuskSyncInitializationException exception) { } catch (IllegalStateException exception) {
log(Level.SEVERE, """ log(Level.SEVERE, """
*************************************************** ***************************************************

@ -40,6 +40,7 @@ import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.entity.ProjectileLaunchEvent; import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent; import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.PrepareItemCraftEvent;
import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent;
import org.bukkit.event.player.PlayerDropItemEvent; import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEntityEvent;
@ -172,6 +173,10 @@ public class BukkitEventListener extends EventListener implements BukkitJoinEven
event.setCancelled(cancelPlayerEvent(event.getWhoClicked().getUniqueId())); event.setCancelled(cancelPlayerEvent(event.getWhoClicked().getUniqueId()));
} }
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCraftItem(@NotNull PrepareItemCraftEvent event) {
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) { public void onPlayerTakeDamage(@NotNull EntityDamageEvent event) {
if (event.getEntity() instanceof Player player) { if (event.getEntity() instanceof Player player) {

@ -36,6 +36,7 @@ import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.attribute.Attribute; import org.bukkit.attribute.Attribute;
import org.bukkit.entity.EntityType; import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.Inventory; import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@ -55,7 +56,7 @@ import java.util.logging.Level;
* Bukkit implementation of an {@link OnlineUser} * Bukkit implementation of an {@link OnlineUser}
*/ */
public class BukkitPlayer extends OnlineUser { public class BukkitPlayer extends OnlineUser {
private final BukkitHuskSync plugin; private final BukkitHuskSync plugin;
private final Player player; private final Player player;
@ -178,6 +179,7 @@ public class BukkitPlayer extends OnlineUser {
return BukkitSerializer.deserializeInventory(itemData.serializedItems).thenApplyAsync(contents -> { return BukkitSerializer.deserializeInventory(itemData.serializedItems).thenApplyAsync(contents -> {
final CompletableFuture<Void> inventorySetFuture = new CompletableFuture<>(); final CompletableFuture<Void> inventorySetFuture = new CompletableFuture<>();
Bukkit.getScheduler().runTask(plugin, () -> { Bukkit.getScheduler().runTask(plugin, () -> {
this.clearInventoryCraftingSlots();
player.setItemOnCursor(null); player.setItemOnCursor(null);
player.getInventory().setContents(contents.getContents()); player.getInventory().setContents(contents.getContents());
player.updateInventory(); player.updateInventory();
@ -187,6 +189,16 @@ public class BukkitPlayer extends OnlineUser {
}); });
} }
// Clears any items the player may have in the crafting slots of their inventory
private void clearInventoryCraftingSlots() {
final Inventory inventory = player.getOpenInventory().getTopInventory();
if (inventory.getType() == InventoryType.CRAFTING) {
for (int slot = 0; slot < 5; slot++) {
inventory.setItem(slot, null);
}
}
}
@Override @Override
public CompletableFuture<ItemData> getEnderChest() { public CompletableFuture<ItemData> getEnderChest() {
final Inventory enderChest = player.getEnderChest(); final Inventory enderChest = player.getEnderChest();
@ -500,7 +512,7 @@ public class BukkitPlayer extends OnlineUser {
.ifPresentOrElse(mapping -> mapping.setContainerValue(container, player, key), .ifPresentOrElse(mapping -> mapping.setContainerValue(container, player, key),
() -> plugin.log(Level.WARNING, () -> plugin.log(Level.WARNING,
"Could not set " + player.getName() + "'s persistent data key " + keyString + "Could not set " + player.getName() + "'s persistent data key " + keyString +
" as it has an invalid type. Skipping!")); " as it has an invalid type. Skipping!"));
} }
}); });
}).exceptionally(throwable -> { }).exceptionally(throwable -> {
@ -517,7 +529,7 @@ public class BukkitPlayer extends OnlineUser {
public Audience getAudience() { public Audience getAudience() {
return plugin.getAudiences().player(player); return plugin.getAudiences().player(player);
} }
@Override @Override
public boolean isOffline() { public boolean isOffline() {
try { try {

@ -11,6 +11,7 @@ softdepend:
libraries: libraries:
- 'redis.clients:jedis:${jedis_version}' - 'redis.clients:jedis:${jedis_version}'
- 'com.mysql:mysql-connector-j:${mysql_driver_version}' - 'com.mysql:mysql-connector-j:${mysql_driver_version}'
- 'org.mariadb.jdbc:mariadb-java-client:${mariadb_driver_version}'
- 'org.xerial.snappy:snappy-java:${snappy_version}' - 'org.xerial.snappy:snappy-java:${snappy_version}'
- 'org.apache.commons:commons-text:${commons_text_version}' - 'org.apache.commons:commons-text:${commons_text_version}'

@ -22,9 +22,9 @@ package net.william278.husksync;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
/** /**
* Indicates an exception occurred while initialising the HuskSync plugin * Indicates an exception occurred while initializing the HuskSync plugin
*/ */
public class HuskSyncInitializationException extends RuntimeException { public class HuskSyncInitializationException extends IllegalStateException {
public HuskSyncInitializationException(@NotNull String message) { public HuskSyncInitializationException(@NotNull String message) {
super(message); super(message);
} }

@ -174,7 +174,7 @@ public class Settings {
@NotNull @NotNull
public Database.Type getSqlType() { public Database.Type getDatabaseType() {
return databaseType; return databaseType;
} }

@ -75,10 +75,8 @@ public abstract class Database {
/** /**
* Initialize the database and ensure tables are present; create tables if they do not exist. * Initialize the database and ensure tables are present; create tables if they do not exist.
*
* @return A future returning boolean - if the connection could be established.
*/ */
public abstract boolean initialize(); public abstract void initialize();
/** /**
* Ensure a {@link User} has an entry in the database and that their username is up-to-date * Ensure a {@link User} has an entry in the database and that their username is up-to-date

@ -40,57 +40,13 @@ import java.util.logging.Level;
public class MySqlDatabase extends Database { public class MySqlDatabase extends Database {
/**
* MySQL protocol
*/
private final Database.Type type;
/**
* MySQL server hostname
*/
private final String mySqlHost;
/**
* MySQL server port
*/
private final int mySqlPort;
/**
* Database to use on the MySQL server
*/
private final String mySqlDatabaseName;
private final String mySqlUsername;
private final String mySqlPassword;
private final String mySqlConnectionParameters;
private final int hikariMaximumPoolSize;
private final int hikariMinimumIdle;
private final long hikariMaximumLifetime;
private final long hikariKeepAliveTime;
private final long hikariConnectionTimeOut;
private static final String DATA_POOL_NAME = "HuskSyncHikariPool"; private static final String DATA_POOL_NAME = "HuskSyncHikariPool";
private final String protocol;
/** private HikariDataSource dataSource;
* The Hikari data source - a pool of database connections that can be fetched on-demand
*/
private HikariDataSource connectionPool;
public MySqlDatabase(@NotNull HuskSync plugin) { public MySqlDatabase(@NotNull HuskSync plugin) {
super(plugin); super(plugin);
final Settings settings = plugin.getSettings(); this.protocol = plugin.getSettings().getDatabaseType().getProtocol();
this.type = settings.getSqlType();
this.mySqlHost = settings.getMySqlHost();
this.mySqlPort = settings.getMySqlPort();
this.mySqlDatabaseName = settings.getMySqlDatabase();
this.mySqlUsername = settings.getMySqlUsername();
this.mySqlPassword = settings.getMySqlPassword();
this.mySqlConnectionParameters = settings.getMySqlConnectionParameters();
this.hikariMaximumPoolSize = settings.getMySqlConnectionPoolSize();
this.hikariMinimumIdle = settings.getMySqlConnectionPoolIdle();
this.hikariMaximumLifetime = settings.getMySqlConnectionPoolLifetime();
this.hikariKeepAliveTime = settings.getMySqlConnectionPoolKeepAlive();
this.hikariConnectionTimeOut = settings.getMySqlConnectionPoolTimeout();
} }
/** /**
@ -100,46 +56,68 @@ public class MySqlDatabase extends Database {
* @throws SQLException if the connection fails for some reason * @throws SQLException if the connection fails for some reason
*/ */
private Connection getConnection() throws SQLException { private Connection getConnection() throws SQLException {
return connectionPool.getConnection(); return dataSource.getConnection();
} }
@Override @Override
public boolean initialize() { public void initialize() throws IllegalStateException {
try { // Initialize the Hikari pooled connection
// Create jdbc driver connection url dataSource = new HikariDataSource();
final String jdbcUrl = "jdbc:" + type.getProtocol() + "://" + mySqlHost + ":" + mySqlPort + "/" + mySqlDatabaseName + mySqlConnectionParameters; dataSource.setJdbcUrl(String.format("jdbc:%s://%s:%s/%s%s",
connectionPool = new HikariDataSource(); protocol,
connectionPool.setJdbcUrl(jdbcUrl); plugin.getSettings().getMySqlHost(),
plugin.getSettings().getMySqlPort(),
// Authenticate plugin.getSettings().getMySqlDatabase(),
connectionPool.setUsername(mySqlUsername); plugin.getSettings().getMySqlConnectionParameters()
connectionPool.setPassword(mySqlPassword); ));
// Set various additional parameters // Authenticate with the database
connectionPool.setMaximumPoolSize(hikariMaximumPoolSize); dataSource.setUsername(plugin.getSettings().getMySqlUsername());
connectionPool.setMinimumIdle(hikariMinimumIdle); dataSource.setPassword(plugin.getSettings().getMySqlPassword());
connectionPool.setMaxLifetime(hikariMaximumLifetime);
connectionPool.setKeepaliveTime(hikariKeepAliveTime); // Set connection pool options
connectionPool.setConnectionTimeout(hikariConnectionTimeOut); dataSource.setMaximumPoolSize(plugin.getSettings().getMySqlConnectionPoolSize());
connectionPool.setPoolName(DATA_POOL_NAME); dataSource.setMinimumIdle(plugin.getSettings().getMySqlConnectionPoolIdle());
dataSource.setMaxLifetime(plugin.getSettings().getMySqlConnectionPoolLifetime());
// Prepare database schema; make tables if they don't exist dataSource.setKeepaliveTime(plugin.getSettings().getMySqlConnectionPoolKeepAlive());
try (Connection connection = connectionPool.getConnection()) { dataSource.setConnectionTimeout(plugin.getSettings().getMySqlConnectionPoolTimeout());
// Load database schema CREATE statements from schema file dataSource.setPoolName(DATA_POOL_NAME);
final String[] databaseSchema = getSchemaStatements("database/mysql_schema.sql");
try (Statement statement = connection.createStatement()) { // Set additional connection pool properties
for (String tableCreationStatement : databaseSchema) { final Properties properties = new Properties();
statement.execute(tableCreationStatement); properties.putAll(
} Map.of("cachePrepStmts", "true",
"prepStmtCacheSize", "250",
"prepStmtCacheSqlLimit", "2048",
"useServerPrepStmts", "true",
"useLocalSessionState", "true",
"useLocalTransactionState", "true"
));
properties.putAll(
Map.of(
"rewriteBatchedStatements", "true",
"cacheResultSetMetadata", "true",
"cacheServerConfiguration", "true",
"elideSetAutoCommits", "true",
"maintainTimeStats", "false")
);
dataSource.setDataSourceProperties(properties);
// Prepare database schema; make tables if they don't exist
try (Connection connection = dataSource.getConnection()) {
final String[] databaseSchema = getSchemaStatements(String.format("database/%s_schema.sql", protocol));
try (Statement statement = connection.createStatement()) {
for (String tableCreationStatement : databaseSchema) {
statement.execute(tableCreationStatement);
} }
return true; } catch (SQLException e) {
} catch (SQLException | IOException e) { throw new IllegalStateException("Failed to create database tables. Please ensure you are running MySQL v8.0+ " +
plugin.log(Level.SEVERE, "Failed to perform database setup: " + e.getMessage()); "and that your connecting user account has privileges to create tables.", e);
} }
} catch (Exception e) { } catch (SQLException | IOException e) {
plugin.log(Level.SEVERE, "An unhandled exception occurred during database setup!", e); throw new IllegalStateException("Failed to establish a connection to the MySQL database. " +
"Please check the supplied database credentials in the config file", e);
} }
return false;
} }
@Override @Override
@ -445,9 +423,9 @@ public class MySqlDatabase extends Database {
@Override @Override
public void close() { public void close() {
if (connectionPool != null) { if (dataSource != null) {
if (!connectionPool.isClosed()) { if (!dataSource.isClosed()) {
connectionPool.close(); dataSource.close();
} }
} }
} }

@ -3,11 +3,12 @@ org.gradle.jvmargs='-Dfile.encoding=UTF-8'
org.gradle.daemon=true org.gradle.daemon=true
javaVersion=16 javaVersion=16
plugin_version=2.2.5 plugin_version=2.2.6
plugin_archive=husksync plugin_archive=husksync
plugin_description=A modern, cross-server player data synchronization system plugin_description=A modern, cross-server player data synchronization system
jedis_version=4.3.2 jedis_version=4.3.2
mysql_driver_version=8.0.32 mysql_driver_version=8.1.0
mariadb_driver_version=3.1.4
snappy_version=1.1.9.1 snappy_version=1.1.9.1
commons_text_version=1.10.0 commons_text_version=1.10.0
Loading…
Cancel
Save