diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 29ef672..f2488b7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,7 +4,7 @@ stages: variables: name: "UltimateStacker" path: "/builds/$CI_PROJECT_PATH" - version: "1.8.7" + version: "1.8.8" build: stage: build diff --git a/pom.xml b/pom.xml index 87a51ac..413c0ff 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,9 @@ com.songoda:songodaupdater com.songoda:Lootables + com.zaxxer:HikariCP + org.slf4j:slf4j-api + org.slf4j:slf4j-nop @@ -57,6 +60,10 @@ private http://repo.songoda.com/artifactory/private/ + + CodeMC + https://repo.codemc.org/repository/maven-public + @@ -109,5 +116,15 @@ Clearlag 3.0.6 + + com.zaxxer + HikariCP + 3.2.0 + + + org.xerial + sqlite-jdbc + 3.23.1 + diff --git a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java index 0764209..efbe0ba 100644 --- a/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java +++ b/src/main/java/com/songoda/ultimatestacker/UltimateStacker.java @@ -1,6 +1,7 @@ package com.songoda.ultimatestacker; import com.songoda.ultimatestacker.command.CommandManager; +import com.songoda.ultimatestacker.database.*; import com.songoda.ultimatestacker.entity.EntityStack; import com.songoda.ultimatestacker.entity.EntityStackManager; import com.songoda.ultimatestacker.hologram.Hologram; @@ -13,7 +14,6 @@ import com.songoda.ultimatestacker.spawner.SpawnerStack; import com.songoda.ultimatestacker.spawner.SpawnerStackManager; import com.songoda.ultimatestacker.storage.Storage; import com.songoda.ultimatestacker.storage.StorageRow; -import com.songoda.ultimatestacker.storage.types.StorageMysql; import com.songoda.ultimatestacker.storage.types.StorageYaml; import com.songoda.ultimatestacker.tasks.StackingTask; import com.songoda.ultimatestacker.utils.*; @@ -33,6 +33,7 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; import java.util.ArrayList; import java.util.List; @@ -53,22 +54,22 @@ public class UltimateStacker extends JavaPlugin { private StackingTask stackingTask; private Hologram hologram; + private DatabaseConnector databaseConnector; + private DataMigrationManager dataMigrationManager; + private DataManager dataManager; + private EntityUtils entityUtils; private List stackerHooks = new ArrayList<>(); private ServerVersion serverVersion = ServerVersion.fromPackageName(Bukkit.getServer().getClass().getPackage().getName()); - private Storage storage; public static UltimateStacker getInstance() { return INSTANCE; } public void onDisable() { - this.saveToFile(); - this.storage.closeConnection(); - if (hologram != null) - this.hologram.unloadHolograms(); + this.dataManager.bulkUpdateSpawners(this.spawnerStackManager.getStacks()); ConsoleCommandSender console = Bukkit.getConsoleSender(); console.sendMessage(Methods.formatText("&a=============================")); @@ -137,31 +138,6 @@ public class UltimateStacker extends JavaPlugin { this.entityStackManager = new EntityStackManager(); this.stackingTask = new StackingTask(this); - checkStorage(); - - Bukkit.getScheduler().runTaskLater(this, () -> { - if (storage.containsGroup("spawners")) { - for (StorageRow row : storage.getRowsByGroup("spawners")) { - try { - Location location = Methods.unserializeLocation(row.getKey()); - - SpawnerStack stack = new SpawnerStack( - location, - row.get("amount").asInt()); - - this.spawnerStackManager.addSpawner(stack); - } catch (Exception e) { - console.sendMessage("Failed to load spawner."); - e.printStackTrace(); - } - } - } - - // Save data initially so that if the person reloads again fast they don't lose all their data. - this.saveToFile(); - if (hologram != null) - hologram.loadHolograms(); - }, 10); PluginManager pluginManager = Bukkit.getPluginManager(); if (isServerVersionAtLeast(ServerVersion.V1_10)) pluginManager.registerEvents(new BreedListeners(this), this); @@ -189,11 +165,68 @@ public class UltimateStacker extends JavaPlugin { stackerHooks.add(new JobsHook()); } - Bukkit.getScheduler().runTaskTimerAsynchronously(this, this::saveToFile, 6000, 6000); - // Starting Metrics new Metrics(this); + // Legacy Data + Bukkit.getScheduler().runTaskLater(this, () -> { + File folder = getDataFolder(); + File dataFile = new File(folder, "data.yml"); + + if (dataFile.exists()) { + Storage storage = new StorageYaml(this); + if (storage.containsGroup("spawners")) { + for (StorageRow row : storage.getRowsByGroup("spawners")) { + try { + Location location = Methods.unserializeLocation(row.getKey()); + + SpawnerStack stack = new SpawnerStack( + location, + row.get("amount").asInt()); + + getDataManager().createSpawner(stack); + } catch (Exception e) { + console.sendMessage("Failed to load spawner."); + e.printStackTrace(); + } + } + } + dataFile.delete(); + } + }, 10); + + // Database stuff, go! + try { + if (Setting.MYSQL_ENABLED.getBoolean()) { + String hostname = Setting.MYSQL_HOSTNAME.getString(); + int port = Setting.MYSQL_PORT.getInt(); + String database = Setting.MYSQL_DATABASE.getString(); + String username = Setting.MYSQL_USERNAME.getString(); + String password = Setting.MYSQL_PASSWORD.getString(); + boolean useSSL = Setting.MYSQL_USE_SSL.getBoolean(); + + this.databaseConnector = new MySQLConnector(this, hostname, port, database, username, password, useSSL); + this.getLogger().info("Data handler connected using MySQL."); + } else { + this.databaseConnector = new SQLiteConnector(this); + this.getLogger().info("Data handler connected using SQLite."); + } + } catch (Exception ex) { + this.getLogger().severe("Fatal error trying to connect to database. Please make sure all your connection settings are correct and try again. Plugin has been disabled."); + Bukkit.getPluginManager().disablePlugin(this); + } + + this.dataManager = new DataManager(this.databaseConnector, this); + this.dataMigrationManager = new DataMigrationManager(this.databaseConnector, this.dataManager); + this.dataMigrationManager.runMigrations(); + + Bukkit.getScheduler().runTaskLater(this, () -> { + this.dataManager.getSpawners((spawners) -> { + this.spawnerStackManager.addSpawners(spawners); + this.hologram.loadHolograms(); + }); + }, 20L); + console.sendMessage(Methods.formatText("&a=============================")); } @@ -203,21 +236,6 @@ public class UltimateStacker extends JavaPlugin { } } - private void checkStorage() { - if (getConfig().getBoolean("Database.Activate Mysql Support")) { - this.storage = new StorageMysql(this); - } else { - this.storage = new StorageYaml(this); - } - } - - private void saveToFile() { - this.storage.closeConnection(); - checkStorage(); - - storage.doSave(); - } - public void reload() { this.locale = Locale.getLocale(getConfig().getString("System.Language Mode")); this.locale.reloadMessages(); @@ -301,4 +319,12 @@ public class UltimateStacker extends JavaPlugin { public EntityUtils getEntityUtils() { return entityUtils; } + + public DatabaseConnector getDatabaseConnector() { + return databaseConnector; + } + + public DataManager getDataManager() { + return dataManager; + } } diff --git a/src/main/java/com/songoda/ultimatestacker/database/DataManager.java b/src/main/java/com/songoda/ultimatestacker/database/DataManager.java new file mode 100644 index 0000000..8f272af --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/DataManager.java @@ -0,0 +1,149 @@ +package com.songoda.ultimatestacker.database; + +import com.songoda.ultimatestacker.spawner.SpawnerStack; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +import java.sql.*; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +public class DataManager { + + private final DatabaseConnector databaseConnector; + private final Plugin plugin; + + public DataManager(DatabaseConnector databaseConnector, Plugin plugin) { + this.databaseConnector = databaseConnector; + this.plugin = plugin; + } + + /** + * @return the prefix to be used by all table names + */ + public String getTablePrefix() { + return this.plugin.getDescription().getName().toLowerCase() + '_'; + } + + public void bulkUpdateSpawners(Collection spawnerStacks) { + this.databaseConnector.connect(connection -> { + String updateSpawner = "UPDATE " + this.getTablePrefix() + "spawners SET amount = ? WHERE world = ? AND x = ? AND y = ? and z = ?"; + try (PreparedStatement statement = connection.prepareStatement(updateSpawner)) { + for (SpawnerStack spawnerStack : spawnerStacks) { + statement.setInt(1, spawnerStack.getAmount()); + + statement.setString(5, spawnerStack.getWorld().getName()); + statement.setInt(6, spawnerStack.getX()); + statement.setInt(7, spawnerStack.getY()); + statement.setInt(8, spawnerStack.getZ()); + statement.executeUpdate(); + statement.addBatch(); + } + + statement.executeBatch(); + } + }); + } + + + public void updateSpawner(SpawnerStack spawnerStack) { + this.async(() -> this.databaseConnector.connect(connection -> { + String updateSpawner = "UPDATE " + this.getTablePrefix() + "spawners SET amount = ? WHERE world = ? AND x = ? AND y = ? and z = ?"; + try (PreparedStatement statement = connection.prepareStatement(updateSpawner)) { + statement.setInt(1, spawnerStack.getAmount()); + + statement.setString(2, spawnerStack.getWorld().getName()); + statement.setInt(3, spawnerStack.getX()); + statement.setInt(4, spawnerStack.getY()); + statement.setInt(5, spawnerStack.getZ()); + statement.executeUpdate(); + } + })); + } + + + public void createSpawner(SpawnerStack spawnerStack) { + this.async(() -> this.databaseConnector.connect(connection -> { + String createSpawner = "INSERT INTO " + this.getTablePrefix() + "spawners (amount, world, x, y, z) VALUES (?, ?, ?, ? , ?)"; + try (PreparedStatement statement = connection.prepareStatement(createSpawner)) { + statement.setInt(1, spawnerStack.getAmount()); + + statement.setString(2, spawnerStack.getWorld().getName()); + statement.setInt(3, spawnerStack.getX()); + statement.setInt(4, spawnerStack.getY()); + statement.setInt(5, spawnerStack.getZ()); + statement.executeUpdate(); + } + })); + } + + public void deleteSpawner(SpawnerStack spawnerStack) { + this.async(() -> this.databaseConnector.connect(connection -> { + String deleteSpawner = "DELETE FROM " + this.getTablePrefix() + "spawners WHERE world = ? AND x = ? AND y = ? and z = ?"; + try (PreparedStatement statement = connection.prepareStatement(deleteSpawner)) { + statement.setString(1, spawnerStack.getWorld().getName()); + statement.setInt(2, spawnerStack.getX()); + statement.setInt(3, spawnerStack.getY()); + statement.setInt(4, spawnerStack.getZ()); + statement.executeUpdate(); + } + })); + } + + public void getSpawners(Consumer> callback) { + this.async(() -> this.databaseConnector.connect(connection -> { + String selectSpawners = "SELECT * FROM " + this.getTablePrefix() + "spawners"; + + Map spawners = new HashMap<>(); + + try (Statement statement = connection.createStatement()) { + ResultSet result = statement.executeQuery(selectSpawners); + while (result.next()) { + + int amount = result.getInt("amount"); + + String world = result.getString("world"); + int x = result.getInt("x"); + int y = result.getInt("y"); + int z = result.getInt("z"); + Location location = new Location(Bukkit.getWorld(world), x, y, z); + + SpawnerStack spawnerStack = new SpawnerStack(location, amount); + spawners.put(location, spawnerStack); + } + } + + this.sync(() -> callback.accept(spawners)); + })); + } + + private int lastInsertedId(Connection connection) { + String query; + if (this.databaseConnector instanceof SQLiteConnector) { + query = "SELECT last_insert_rowid()"; + } else { + query = "SELECT LAST_INSERT_ID()"; + } + + try (Statement statement = connection.createStatement()) { + ResultSet result = statement.executeQuery(query); + result.next(); + return result.getInt(1); + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } + + public void async(Runnable runnable) { + Bukkit.getScheduler().runTaskAsynchronously(this.plugin, runnable); + } + + public void sync(Runnable runnable) { + Bukkit.getScheduler().runTask(this.plugin, runnable); + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/DataMigration.java b/src/main/java/com/songoda/ultimatestacker/database/DataMigration.java new file mode 100644 index 0000000..9c7cfb0 --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/DataMigration.java @@ -0,0 +1,23 @@ +package com.songoda.ultimatestacker.database; + +import java.sql.Connection; +import java.sql.SQLException; + +public abstract class DataMigration { + + private final int revision; + + public DataMigration(int revision) { + this.revision = revision; + } + + public abstract void migrate(Connection connection, String tablePrefix) throws SQLException; + + /** + * @return the revision number of this migration + */ + public int getRevision() { + return this.revision; + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/DataMigrationManager.java b/src/main/java/com/songoda/ultimatestacker/database/DataMigrationManager.java new file mode 100644 index 0000000..2f1ce4a --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/DataMigrationManager.java @@ -0,0 +1,108 @@ +package com.songoda.ultimatestacker.database; + +import com.songoda.ultimatestacker.database.migrations._1_InitialMigration; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +public class DataMigrationManager { + + private List migrations; + private DatabaseConnector databaseConnector; + private DataManager dataManager; + + public DataMigrationManager(DatabaseConnector databaseConnector, DataManager dataManager) { + this.databaseConnector = databaseConnector; + this.dataManager = dataManager; + + this.migrations = Arrays.asList( + new _1_InitialMigration() + ); + } + + /** + * Runs any needed data migrations + */ + public void runMigrations() { + this.databaseConnector.connect((connection -> { + int currentMigration = -1; + boolean migrationsExist; + + String query; + if (this.databaseConnector instanceof SQLiteConnector) { + query = "SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?"; + } else { + query = "SHOW TABLES LIKE ?"; + } + + try (PreparedStatement statement = connection.prepareStatement(query)) { + statement.setString(1, this.getMigrationsTableName()); + migrationsExist = statement.executeQuery().next(); + } + + if (!migrationsExist) { + // No migration table exists, create one + String createTable = "CREATE TABLE " + this.getMigrationsTableName() + " (migration_version INT NOT NULL)"; + try (PreparedStatement statement = connection.prepareStatement(createTable)) { + statement.execute(); + } + + // Insert primary row into migration table + String insertRow = "INSERT INTO " + this.getMigrationsTableName() + " VALUES (?)"; + try (PreparedStatement statement = connection.prepareStatement(insertRow)) { + statement.setInt(1, -1); + statement.execute(); + } + } else { + // Grab the current migration version + String selectVersion = "SELECT migration_version FROM " + this.getMigrationsTableName(); + try (PreparedStatement statement = connection.prepareStatement(selectVersion)) { + ResultSet result = statement.executeQuery(); + result.next(); + currentMigration = result.getInt("migration_version"); + } + } + + // Grab required migrations + int finalCurrentMigration = currentMigration; + List requiredMigrations = this.migrations + .stream() + .filter(x -> x.getRevision() > finalCurrentMigration) + .sorted(Comparator.comparingInt(DataMigration::getRevision)) + .collect(Collectors.toList()); + + // Nothing to migrate, abort + if (requiredMigrations.isEmpty()) + return; + + // Migrate the data + for (DataMigration dataMigration : requiredMigrations) + dataMigration.migrate(connection, this.dataManager.getTablePrefix()); + + // Set the new current migration to be the highest migrated to + currentMigration = requiredMigrations + .stream() + .map(DataMigration::getRevision) + .max(Integer::compareTo) + .orElse(-1); + + String updateVersion = "UPDATE " + this.getMigrationsTableName() + " SET migration_version = ?"; + try (PreparedStatement statement = connection.prepareStatement(updateVersion)) { + statement.setInt(1, currentMigration); + statement.execute(); + } + })); + } + + /** + * @return the name of the migrations table + */ + private String getMigrationsTableName() { + return this.dataManager.getTablePrefix() + "migrations"; + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/DatabaseConnector.java b/src/main/java/com/songoda/ultimatestacker/database/DatabaseConnector.java new file mode 100644 index 0000000..3a87c0d --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/DatabaseConnector.java @@ -0,0 +1,34 @@ +package com.songoda.ultimatestacker.database; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface DatabaseConnector { + + /** + * Checks if the connection to the database has been created + * + * @return true if the connection is created, otherwise false + */ + boolean isInitialized(); + + /** + * Closes all open connections to the database + */ + void closeConnection(); + + /** + * Executes a callback with a Connection passed and automatically closes it when finished + * + * @param callback The callback to execute once the connection is retrieved + */ + void connect(ConnectionCallback callback); + + /** + * Wraps a connection in a callback which will automagically handle catching sql errors + */ + interface ConnectionCallback { + void accept(Connection connection) throws SQLException; + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/MySQLConnector.java b/src/main/java/com/songoda/ultimatestacker/database/MySQLConnector.java new file mode 100644 index 0000000..5a1fa1d --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/MySQLConnector.java @@ -0,0 +1,50 @@ +package com.songoda.ultimatestacker.database; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; +import org.bukkit.plugin.Plugin; + +import java.sql.Connection; +import java.sql.SQLException; + +public class MySQLConnector implements DatabaseConnector { + + private final Plugin plugin; + private HikariDataSource hikari; + private boolean initializedSuccessfully; + + public MySQLConnector(Plugin plugin, String hostname, int port, String database, String username, String password, boolean useSSL) { + this.plugin = plugin; + + HikariConfig config = new HikariConfig(); + config.setJdbcUrl("jdbc:mysql://" + hostname + ":" + port + "/" + database + "?useSSL=" + useSSL); + config.setUsername(username); + config.setPassword(password); + config.setMaximumPoolSize(3); + + try { + this.hikari = new HikariDataSource(config); + this.initializedSuccessfully = true; + } catch (Exception ex) { + this.initializedSuccessfully = false; + } + } + + public boolean isInitialized() { + return this.initializedSuccessfully; + } + + public void closeConnection() { + this.hikari.close(); + } + + public void connect(ConnectionCallback callback) { + try (Connection connection = this.hikari.getConnection()) { + callback.accept(connection); + } catch (SQLException ex) { + this.plugin.getLogger().severe("An error occurred executing a MySQL query: " + ex.getMessage()); + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/SQLiteConnector.java b/src/main/java/com/songoda/ultimatestacker/database/SQLiteConnector.java new file mode 100644 index 0000000..e662162 --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/SQLiteConnector.java @@ -0,0 +1,58 @@ +package com.songoda.ultimatestacker.database; + +import org.bukkit.plugin.Plugin; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class SQLiteConnector implements DatabaseConnector { + + private final Plugin plugin; + private final String connectionString; + private Connection connection; + + public SQLiteConnector(Plugin plugin) { + this.plugin = plugin; + this.connectionString = "jdbc:sqlite:" + plugin.getDataFolder() + File.separator + plugin.getDescription().getName().toLowerCase() + ".db"; + + try { + Class.forName("org.sqlite.JDBC"); // This is required to put here for Spigot 1.10 and below for some reason + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + public boolean isInitialized() { + return true; // Always available + } + + public void closeConnection() { + try { + if (this.connection != null) { + this.connection.close(); + } + } catch (SQLException ex) { + this.plugin.getLogger().severe("An error occurred closing the SQLite database connection: " + ex.getMessage()); + } + } + + public void connect(ConnectionCallback callback) { + if (this.connection == null) { + try { + this.connection = DriverManager.getConnection(this.connectionString); + } catch (SQLException ex) { + this.plugin.getLogger().severe("An error occurred retrieving the SQLite database connection: " + ex.getMessage()); + } + } + + try { + callback.accept(this.connection); + } catch (Exception ex) { + this.plugin.getLogger().severe("An error occurred executing an SQLite query: " + ex.getMessage()); + ex.printStackTrace(); + } + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/database/migrations/_1_InitialMigration.java b/src/main/java/com/songoda/ultimatestacker/database/migrations/_1_InitialMigration.java new file mode 100644 index 0000000..10e6153 --- /dev/null +++ b/src/main/java/com/songoda/ultimatestacker/database/migrations/_1_InitialMigration.java @@ -0,0 +1,33 @@ +package com.songoda.ultimatestacker.database.migrations; + +import com.songoda.ultimatestacker.UltimateStacker; +import com.songoda.ultimatestacker.database.DataMigration; +import com.songoda.ultimatestacker.database.MySQLConnector; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +public class _1_InitialMigration extends DataMigration { + + public _1_InitialMigration() { + super(1); + } + + @Override + public void migrate(Connection connection, String tablePrefix) throws SQLException { + String autoIncrement = UltimateStacker.getInstance().getDatabaseConnector() instanceof MySQLConnector ? " AUTO_INCREMENT" : ""; + + // Create plugin settings table + try (Statement statement = connection.createStatement()) { + statement.execute("CREATE TABLE " + tablePrefix + "spawners (" + + "amount INTEGER NOT NULL," + + "world TEXT NOT NULL, " + + "x DOUBLE NOT NULL, " + + "y DOUBLE NOT NULL, " + + "z DOUBLE NOT NULL " + + ")"); + } + } + +} diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/BlockListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/BlockListeners.java index 954722f..46d0fdc 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/BlockListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/BlockListeners.java @@ -29,10 +29,10 @@ import java.util.List; public class BlockListeners implements Listener { - private final UltimateStacker instance; + private final UltimateStacker plugin; - public BlockListeners(UltimateStacker instance) { - this.instance = instance; + public BlockListeners(UltimateStacker plugin) { + this.plugin = plugin; } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @@ -42,14 +42,14 @@ public class BlockListeners implements Listener { ItemStack item = event.getPlayer().getInventory().getItemInHand(); if (block == null - || block.getType() != (instance.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) - || item.getType() != (instance.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) + || block.getType() != (plugin.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) + || item.getType() != (plugin.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) || event.getAction() == Action.LEFT_CLICK_BLOCK) return; List disabledWorlds = Setting.DISABLED_WORLDS.getStringList(); if (disabledWorlds.stream().anyMatch(worldStr -> event.getPlayer().getWorld().getName().equalsIgnoreCase(worldStr))) return; - if (!instance.spawnersEnabled()) return; + if (!plugin.spawnersEnabled()) return; BlockStateMeta bsm = (BlockStateMeta) item.getItemMeta(); CreatureSpawner cs = (CreatureSpawner) bsm.getBlockState(); @@ -57,7 +57,7 @@ public class BlockListeners implements Listener { EntityType itemType = cs.getSpawnedType(); int itemAmount = getSpawnerAmount(item); - int specific = instance.getSpawnerFile().getConfig().getInt("Spawners." + cs.getSpawnedType().name() + ".Max Stack Size"); + int specific = plugin.getSpawnerFile().getConfig().getInt("Spawners." + cs.getSpawnedType().name() + ".Max Stack Size"); int maxStackSize = specific == -1 ? Setting.MAX_STACK_SPAWNERS.getInt() : specific; cs = (CreatureSpawner) block.getState(); @@ -67,7 +67,7 @@ public class BlockListeners implements Listener { event.setCancelled(true); if (itemType == blockType) { - SpawnerStack stack = instance.getSpawnerStackManager().getSpawner(block); + SpawnerStack stack = plugin.getSpawnerStackManager().getSpawner(block); if (player.isSneaking()) return; if (event.getAction() == Action.RIGHT_CLICK_BLOCK) { if (stack.getAmount() == maxStackSize) return; @@ -93,15 +93,15 @@ public class BlockListeners implements Listener { } stack.setAmount(stack.getAmount() + itemAmount); - if (instance.getHologram() != null) { - instance.getHologram().update(stack); + if (plugin.getHologram() != null) { + plugin.getHologram().update(stack); } Methods.takeItem(player, itemAmount); } } - if (instance.getHologram() != null) - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(instance, () -> instance.getHologram().processChange(block), 10L); + if (plugin.getHologram() != null) + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> plugin.getHologram().processChange(block), 10L); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) @@ -110,8 +110,8 @@ public class BlockListeners implements Listener { Player player = event.getPlayer(); if (!event.isCancelled()) { - if (block.getType() != (instance.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) - || !instance.spawnersEnabled()) + if (block.getType() != (plugin.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER")) + || !plugin.spawnersEnabled()) return; CreatureSpawner cs = (CreatureSpawner) block.getState(); @@ -125,25 +125,26 @@ public class BlockListeners implements Listener { return; } - SpawnerStack stack = instance.getSpawnerStackManager().addSpawner(new SpawnerStack(block.getLocation(), amount)); + SpawnerStack stack = plugin.getSpawnerStackManager().addSpawner(new SpawnerStack(block.getLocation(), amount)); + plugin.getDataManager().createSpawner(stack); cs.setSpawnedType(cs2.getSpawnedType()); cs.update(); - if (instance.getHologram() != null) - instance.getHologram().add(stack); + if (plugin.getHologram() != null) + plugin.getHologram().add(stack); } - if (instance.getHologram() != null) - Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(instance, () -> instance.getHologram().processChange(block), 1L); + if (plugin.getHologram() != null) + Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> plugin.getHologram().processChange(block), 1L); } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onBlockBreak(BlockBreakEvent event) { Block block = event.getBlock(); - if (block.getType() != (instance.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER"))) return; + if (block.getType() != (plugin.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER"))) return; - if (!instance.spawnersEnabled()) return; + if (!plugin.spawnersEnabled()) return; event.setExpToDrop(0); CreatureSpawner cs = (CreatureSpawner) block.getState(); @@ -153,7 +154,7 @@ public class BlockListeners implements Listener { Player player = event.getPlayer(); ItemStack item = player.getInventory().getItemInHand(); - SpawnerStack stack = instance.getSpawnerStackManager().getSpawner(block); + SpawnerStack stack = plugin.getSpawnerStackManager().getSpawner(block); event.setCancelled(true); @@ -174,13 +175,14 @@ public class BlockListeners implements Listener { if (remove) { event.setCancelled(false); - if (instance.getHologram() != null) - instance.getHologram().remove(stack); - instance.getSpawnerStackManager().removeSpawner(block.getLocation()); + if (plugin.getHologram() != null) + plugin.getHologram().remove(stack); + SpawnerStack spawnerStack = plugin.getSpawnerStackManager().removeSpawner(block.getLocation()); + plugin.getDataManager().deleteSpawner(spawnerStack); } else { stack.setAmount(stack.getAmount() - 1); - if (instance.getHologram() != null) - instance.getHologram().update(stack); + if (plugin.getHologram() != null) + plugin.getHologram().update(stack); } if (player.hasPermission("ultimatestacker.spawner.nosilkdrop") || item != null && item.getEnchantments().containsKey(Enchantment.SILK_TOUCH) && player.hasPermission("ultimatestacker.spawner.silktouch")) diff --git a/src/main/java/com/songoda/ultimatestacker/listeners/EntityListeners.java b/src/main/java/com/songoda/ultimatestacker/listeners/EntityListeners.java index 320c035..03ed0b7 100644 --- a/src/main/java/com/songoda/ultimatestacker/listeners/EntityListeners.java +++ b/src/main/java/com/songoda/ultimatestacker/listeners/EntityListeners.java @@ -28,22 +28,22 @@ import java.util.List; public class EntityListeners implements Listener { - private final UltimateStacker instance; + private final UltimateStacker plugin; - public EntityListeners(UltimateStacker instance) { - this.instance = instance; + public EntityListeners(UltimateStacker plugin) { + this.plugin = plugin; } @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onSpawn(CreatureSpawnEvent event) { LivingEntity entity = event.getEntity(); - entity.setMetadata("US_REASON", new FixedMetadataValue(instance, event.getSpawnReason().name())); + entity.setMetadata("US_REASON", new FixedMetadataValue(plugin, event.getSpawnReason().name())); if (event.getSpawnReason().name().equals("DROWNED") || event.getSpawnReason() == CreatureSpawnEvent.SpawnReason.LIGHTNING) { String name = event.getEntity().getCustomName(); - Bukkit.getScheduler().scheduleSyncDelayedTask(instance, - () -> instance.getEntityStackManager().addSerializedStack(entity, name), 1L); + Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, + () -> plugin.getEntityStackManager().addSerializedStack(entity, name), 1L); } } @@ -60,7 +60,7 @@ public class EntityListeners implements Listener { Entity entity = entities.get(0); - EntityStackManager stackManager = instance.getEntityStackManager(); + EntityStackManager stackManager = plugin.getEntityStackManager(); if (!stackManager.isStacked(entity)) return; @@ -74,19 +74,19 @@ public class EntityListeners implements Listener { @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) public void onBlow(EntityExplodeEvent event) { - if (!instance.spawnersEnabled()) return; + if (!plugin.spawnersEnabled()) return; List destroyed = event.blockList(); Iterator it = destroyed.iterator(); List toCancel = new ArrayList<>(); while (it.hasNext()) { Block block = it.next(); - if (block.getType() != (instance.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER"))) + if (block.getType() != (plugin.isServerVersionAtLeast(ServerVersion.V1_13) ? Material.SPAWNER : Material.valueOf("MOB_SPAWNER"))) continue; Location spawnLocation = block.getLocation(); - SpawnerStack spawner = instance.getSpawnerStackManager().getSpawner(block); + SpawnerStack spawner = plugin.getSpawnerStackManager().getSpawner(block); if (Setting.SPAWNERS_DONT_EXPLODE.getBoolean()) toCancel.add(block); @@ -104,9 +104,10 @@ public class EntityListeners implements Listener { ItemStack item = Methods.getSpawnerItem(blockType, spawner.getAmount()); spawnLocation.getWorld().dropItemNaturally(spawnLocation.clone().add(.5, 0, .5), item); - instance.getSpawnerStackManager().removeSpawner(spawnLocation); - if (instance.getHologram() != null) - instance.getHologram().remove(spawner); + SpawnerStack spawnerStack = plugin.getSpawnerStackManager().removeSpawner(spawnLocation); + plugin.getDataManager().deleteSpawner(spawnerStack); + if (plugin.getHologram() != null) + plugin.getHologram().remove(spawner); } } diff --git a/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStack.java b/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStack.java index 8851b7a..1e94e2e 100644 --- a/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStack.java +++ b/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStack.java @@ -6,6 +6,7 @@ import com.songoda.ultimatestacker.utils.ServerVersion; import com.songoda.ultimatestacker.utils.settings.Setting; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.World; import org.bukkit.block.CreatureSpawner; import java.util.Random; @@ -20,10 +21,6 @@ public class SpawnerStack { setAmount(amount); } - public Location getLocation() { - return location.clone(); - } - public int getAmount() { return amount; } @@ -31,6 +28,7 @@ public class SpawnerStack { public void setAmount(int amount) { UltimateStacker plugin = UltimateStacker.getInstance(); this.amount = amount; + plugin.getDataManager().updateSpawner(this); Bukkit.getScheduler().runTaskLater(plugin, () -> { if (!(location.getBlock().getState() instanceof CreatureSpawner)) return; @@ -48,6 +46,26 @@ public class SpawnerStack { }, 1L); } + public Location getLocation() { + return location.clone(); + } + + public int getX() { + return location.getBlockX(); + } + + public int getY() { + return location.getBlockY(); + } + + public int getZ() { + return location.getBlockZ(); + } + + public World getWorld() { + return location.getWorld(); + } + @Override public String toString() { return "SpawnerStack:{" diff --git a/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStackManager.java b/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStackManager.java index e1b09e0..954d6ac 100644 --- a/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStackManager.java +++ b/src/main/java/com/songoda/ultimatestacker/spawner/SpawnerStackManager.java @@ -1,5 +1,6 @@ package com.songoda.ultimatestacker.spawner; +import com.songoda.ultimatestacker.UltimateStacker; import org.bukkit.Location; import org.bukkit.block.Block; @@ -10,10 +11,14 @@ import java.util.Map; public class SpawnerStackManager { - private static final Map registeredSpawners = new HashMap<>(); + private final Map registeredSpawners = new HashMap<>(); + + public void addSpawners(Map spawners) { + this.registeredSpawners.putAll(spawners); + } public SpawnerStack addSpawner(SpawnerStack spawnerStack) { - registeredSpawners.put(roundLocation(spawnerStack.getLocation()), spawnerStack); + this.registeredSpawners.put(roundLocation(spawnerStack.getLocation()), spawnerStack); return spawnerStack; } @@ -22,10 +27,12 @@ public class SpawnerStackManager { } public SpawnerStack getSpawner(Location location) { - if (!registeredSpawners.containsKey(roundLocation(location))) { - return addSpawner(new SpawnerStack(roundLocation(location), 1)); + if (!this.registeredSpawners.containsKey(roundLocation(location))) { + SpawnerStack spawnerStack = addSpawner(new SpawnerStack(roundLocation(location), 1)); + UltimateStacker.getInstance().getDataManager().createSpawner(spawnerStack); + return spawnerStack; } - return registeredSpawners.get(location); + return this.registeredSpawners.get(location); } public SpawnerStack getSpawner(Block block) { @@ -33,11 +40,11 @@ public class SpawnerStackManager { } public boolean isSpawner(Location location) { - return registeredSpawners.get(location) != null; + return this.registeredSpawners.get(location) != null; } public Collection getStacks() { - return Collections.unmodifiableCollection(registeredSpawners.values()); + return Collections.unmodifiableCollection(this.registeredSpawners.values()); } private Location roundLocation(Location location) { diff --git a/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java b/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java index 0155df7..ce02a76 100644 --- a/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java +++ b/src/main/java/com/songoda/ultimatestacker/utils/settings/Setting.java @@ -174,30 +174,17 @@ public enum Setting { "The text displayed above a stacked spawner where {TYPE} refers to", "The entities type and {AMT} is the amount currently stacked."), - DATABASE_SUPPORT("Database.Activate Mysql Support", false, - "Should MySQL be used for data storage?"), - - DATABASE_IP("Database.IP", "127.0.0.1", - "MySQL IP"), - - DATABASE_PORT("Database.Port", 3306, - "MySQL Port"), - - DATABASE_NAME("Database.Database Name", "UltimateStacker", - "The database you are inserting data into."), - - DATABASE_PREFIX("Database.Prefix", "US-", - "The prefix for tables inserted into the database."), - - DATABASE_USERNAME("Database.Username", "PUT_USERNAME_HERE", - "MySQL Username"), - - DATABASE_PASSWORD("Database.Password", "PUT_PASSWORD_HERE", - "MySQL Password"), - LANGUGE_MODE("System.Language Mode", "en_US", "The enabled language file.", - "More language files (if available) can be found in the plugins data folder."); + "More language files (if available) can be found in the plugins data folder."), + + MYSQL_ENABLED("MySQL.Enabled", false, "Set to 'true' to use MySQL instead of SQLite for data storage."), + MYSQL_HOSTNAME("MySQL.Hostname", "localhost"), + MYSQL_PORT("MySQL.Port", 3306), + MYSQL_DATABASE("MySQL.Database", "your-database"), + MYSQL_USERNAME("MySQL.Username", "user"), + MYSQL_PASSWORD("MySQL.Password", "pass"), + MYSQL_USE_SSL("MySQL.Use SSL", false); private String setting; private Object option;