From fcede01a11b8f72dbc80822348cc2e4ed04e0d1c Mon Sep 17 00:00:00 2001 From: Tastybento Date: Wed, 28 Feb 2018 08:38:56 -0800 Subject: [PATCH 1/5] WIP using JSON and GSON for database. Causes stack overflow right now. In the middle of debugging, but I need this in github so I can grab it elsewhere. --- .../bskyblock/database/BSBDatabase.java | 7 +- .../flatfile/FlatFileDatabaseHandler.java | 13 - .../managers/AbstractDatabaseHandler.java | 18 -- .../database/managers/PlayersManager.java | 6 +- .../database/managers/island/NewIsland.java | 4 +- .../database/mysql/MySQLDatabaseHandler.java | 124 +++----- .../database/mysqljson/LocationAdapter.java | 88 ++++++ .../mysqljson/MySQLDatabaseConnecterJ.java | 75 +++++ .../mysqljson/MySQLDatabaseHandlerJ.java | 191 ++++++++++++ .../database/mysqljson/MySQLDatabaseJ.java | 22 ++ .../bskyblock/database/objects/Players.java | 4 +- .../mysqljson/MySQLDatabaseHandlerJTest.java | 109 +++++++ .../bskyblock/database/mysqljson/Players.java | 276 ++++++++++++++++++ 13 files changed, 816 insertions(+), 121 deletions(-) create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java create mode 100755 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java create mode 100644 src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java create mode 100755 src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java diff --git a/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java index 2dab1d3b5..7f400f068 100755 --- a/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java +++ b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java @@ -4,6 +4,7 @@ import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.mysql.MySQLDatabase; +import us.tastybento.bskyblock.database.mysqljson.MySQLDatabaseJ; public abstract class BSBDatabase { @@ -23,8 +24,10 @@ public abstract class BSBDatabase { public enum DatabaseType{ FLATFILE(new FlatFileDatabase()), - MYSQL(new MySQLDatabase()); - + MYSQL(new MySQLDatabase()), + //JSON(); + MYSQLJ(new MySQLDatabaseJ()); + BSBDatabase database; DatabaseType(BSBDatabase database){ diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java index 65ae4f3a4..1b0e81b3c 100644 --- a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java @@ -56,19 +56,6 @@ public class FlatFileDatabaseHandler extends AbstractDatabaseHandler { super(plugin, type, databaseConnecter); } - @Override - protected String createSelectQuery() { - return ""; // not used - } - @Override - protected String createInsertQuery() { - return ""; // not used - } - @Override - protected String createDeleteQuery() { - return ""; // Not used - } - /* (non-Javadoc) * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#loadObject(java.lang.String) */ diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java index 75c629c80..b49b9f507 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java @@ -30,12 +30,6 @@ public abstract class AbstractDatabaseHandler { */ protected DatabaseConnecter databaseConnecter; - /** The SQL-select- and insert query */ - protected final String selectQuery; - protected final String insertQuery; - protected final String deleteQuery; - - protected Plugin plugin; @@ -53,20 +47,8 @@ public abstract class AbstractDatabaseHandler { this.plugin = plugin; this.databaseConnecter = databaseConnecter; this.dataObject = type; - this.selectQuery = createSelectQuery(); - this.insertQuery = createInsertQuery(); - this.deleteQuery = createDeleteQuery(); } - /** - * Create the SQL-String to insert into / select / delete from the database - * Not used in the flat file database - * @return the SQL-String - */ - protected abstract String createSelectQuery(); - protected abstract String createInsertQuery(); - protected abstract String createDeleteQuery(); - /** * Loads all the records in this table and returns a list of them * @return list of diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java b/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java index be28b3bf2..a8b2eafb0 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java +++ b/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java @@ -25,7 +25,7 @@ import us.tastybento.bskyblock.database.objects.Players; public class PlayersManager{ - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private BSkyBlock plugin; private BSBDatabase database; private AbstractDatabaseHandler handler; @@ -250,7 +250,7 @@ public class PlayersManager{ public void setHomeLocation(UUID playerUUID, Location location, int number) { addPlayer(playerUUID); playerCache.get(playerUUID).setHomeLocation(location,number); - this.save(true); + //this.save(true); } /** @@ -261,7 +261,7 @@ public class PlayersManager{ public void setHomeLocation(UUID playerUUID, Location location) { addPlayer(playerUUID); playerCache.get(playerUUID).setHomeLocation(location,1); - this.save(true); + //this.save(true); } /** diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java b/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java index 6c4fcd936..3b80b1f89 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java +++ b/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java @@ -113,8 +113,8 @@ public class NewIsland { // Add to the grid island = plugin.getIslands().createIsland(next, playerUUID); // Save the player so that if the server is reset weird things won't happen - plugin.getPlayers().save(true); - plugin.getIslands().save(true); + //plugin.getPlayers().save(true); + //plugin.getIslands().save(true); // Clear any old home locations (they should be clear, but just in case) plugin.getPlayers().clearHomeLocations(playerUUID); diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java index 47f1e08cf..b26a23f9b 100644 --- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java @@ -151,8 +151,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { setSql.append(dataObject.getCanonicalName()); setSql.append("."); setSql.append(field.getName()); - setSql.append("` ("); - setSql.append("uniqueId VARCHAR(36) NOT NULL, "); + setSql.append("` (uniqueId VARCHAR(36) NOT NULL, "); // Get columns separated by commas setSql.append(getCollectionColumnString(writeMethod,false,true)); // Close the SQL string @@ -306,11 +305,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { return columns; } - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#createSelectQuery() - */ - @Override - protected String createSelectQuery() { + String createSelectQuery() { StringBuilder sb = new StringBuilder(); @@ -324,12 +319,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { return sb.toString(); } - - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#createInsertQuery() - */ - @Override - protected String createInsertQuery() { + String createInsertQuery() { StringBuilder sb = new StringBuilder(); // Replace into is used so that any data in the table will be replaced with updated data @@ -348,8 +338,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { return sb.toString(); } - @Override - protected String createDeleteQuery() { + String createDeleteQuery() { return "DELETE FROM [table_name] WHERE uniqueId = ?"; } @@ -357,14 +346,6 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { * Inserts a into the corresponding database-table * * @param instance that should be inserted into the corresponding database-table. Must extend DataObject. - - - - - - - - */ /* (non-Javadoc) * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#insertObject(java.lang.Object) @@ -375,8 +356,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { InstantiationException, IllegalAccessException, IntrospectionException, InvocationTargetException, NoSuchMethodException { - // insertQuery is created in super from the createInsertQuery() method - try (PreparedStatement preparedStatement = connection.prepareStatement(insertQuery)) { + try (PreparedStatement preparedStatement = connection.prepareStatement(createInsertQuery())) { // Get the uniqueId. As each class extends DataObject, it must have this method in it. PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); Method getUniqueId = propertyDescriptor.getReadMethod(); @@ -538,14 +518,6 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { * @return List of s filled with values from the corresponding * database-table * - - - - - - - - */ @Override public List loadObjects() throws SQLException, @@ -554,7 +526,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { IntrospectionException, InvocationTargetException, ClassNotFoundException { try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(selectQuery)) { + ResultSet resultSet = statement.executeQuery(createSelectQuery())) { return createObjects(resultSet); } } @@ -600,14 +572,6 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { * * @return List of s filled with values from the provided ResultSet * - - - - - - - - */ @SuppressWarnings("unchecked") private List createObjects(ResultSet resultSet) @@ -774,46 +738,45 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { InvocationTargetException, IntrospectionException, SQLException, NoSuchMethodException, SecurityException { // Delete this object from all tables // Try to connect to the database - try (Connection conn = databaseConnecter.createConnection()){ - // Get the uniqueId. As each class extends DataObject, it must have this method in it. - Method getUniqueId = dataObject.getMethod("getUniqueId"); - String uniqueId = (String) getUniqueId.invoke(instance); - //plugin.getLogger().info("DEBUG: Unique Id = " + uniqueId); - if (uniqueId.isEmpty()) { - throw new SQLException("uniqueId is blank"); - } - // Delete from the main table - // First substitution is the table name - // deleteQuery is created in super from the createInsertQuery() method - try (PreparedStatement preparedStatement = conn.prepareStatement(deleteQuery.replace("[table_name]", "`" + dataObject.getCanonicalName() + "`"))) { - // Second is the unique ID - preparedStatement.setString(1, uniqueId); - preparedStatement.addBatch(); - preparedStatement.executeBatch(); - } + // Get the uniqueId. As each class extends DataObject, it must have this method in it. + Method getUniqueId = dataObject.getMethod("getUniqueId"); + String uniqueId = (String) getUniqueId.invoke(instance); + //plugin.getLogger().info("DEBUG: Unique Id = " + uniqueId); + if (uniqueId.isEmpty()) { + throw new SQLException("uniqueId is blank"); + } + // Delete from the main table + // First substitution is the table name + // deleteQuery is created in super from the createInsertQuery() method + try (PreparedStatement preparedStatement = connection.prepareStatement(createDeleteQuery().replace("[table_name]", "`" + dataObject.getCanonicalName() + "`"))) { + // Second is the unique ID + preparedStatement.setString(1, uniqueId); + preparedStatement.addBatch(); + preparedStatement.executeBatch(); + } - // Delete from any sub tables created from the object - // Run through the fields in the class using introspection - for (Field field : dataObject.getDeclaredFields()) { - // Get the field's property descriptor - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); - // Delete Collection tables - if (propertyDescriptor.getPropertyType().equals(Set.class) || - propertyDescriptor.getPropertyType().equals(Map.class) || - propertyDescriptor.getPropertyType().equals(HashMap.class) || - propertyDescriptor.getPropertyType().equals(ArrayList.class)) { - // First substitution is the table name - try (PreparedStatement preparedStatement2 = conn.prepareStatement(deleteQuery.replace("[table_name]", "`" + dataObject.getCanonicalName() + "." + field.getName() + "`"))) { - // Second is the unique ID - preparedStatement2.setString(1, uniqueId); - preparedStatement2.addBatch(); - // Execute - preparedStatement2.executeBatch(); - } + // Delete from any sub tables created from the object + // Run through the fields in the class using introspection + for (Field field : dataObject.getDeclaredFields()) { + // Get the field's property descriptor + PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); + // Delete Collection tables + if (propertyDescriptor.getPropertyType().equals(Set.class) || + propertyDescriptor.getPropertyType().equals(Map.class) || + propertyDescriptor.getPropertyType().equals(HashMap.class) || + propertyDescriptor.getPropertyType().equals(ArrayList.class)) { + // First substitution is the table name + try (PreparedStatement preparedStatement2 = connection.prepareStatement(createDeleteQuery().replace("[table_name]", "`" + dataObject.getCanonicalName() + "." + field.getName() + "`"))) { + // Second is the unique ID + preparedStatement2.setString(1, uniqueId); + preparedStatement2.addBatch(); + // Execute + preparedStatement2.executeBatch(); } } - } - } + } + } + /* (non-Javadoc) * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) @@ -826,8 +789,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { query.append(dataObject.getCanonicalName()); query.append("` WHERE `uniqueId` = ?), 1, 0)"); - try (Connection conn = databaseConnecter.createConnection(); - PreparedStatement preparedStatement = conn.prepareStatement(query.toString())) { + try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { preparedStatement.setString(1, key); try (ResultSet resultSet = preparedStatement.executeQuery()) { if (resultSet.next()) { diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java new file mode 100644 index 000000000..1d226d910 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java @@ -0,0 +1,88 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import java.lang.reflect.Type; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +public class LocationAdapter implements JsonDeserializer, JsonSerializer { + + public static final LocationAdapter INSTANCE = new LocationAdapter(); + + @Override + public Location deserialize( JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext ) throws JsonParseException { + + if ( !json.isJsonObject() ) { + throw new JsonParseException( "not a JSON object" ); + } + + final JsonObject obj = (JsonObject) json; + final JsonElement world = obj.get( "world" ); + final JsonElement x = obj.get( "x" ); + final JsonElement y = obj.get( "y" ); + final JsonElement z = obj.get( "z" ); + final JsonElement yaw = obj.get( "yaw" ); + final JsonElement pitch = obj.get( "pitch" ); + + if ( world == null || x == null || y == null || z == null || yaw == null || pitch == null ) { + throw new JsonParseException( "Malformed location json string!" ); + } + + if ( !world.isJsonPrimitive() || !((JsonPrimitive) world).isString() ) { + throw new JsonParseException( "world is not a string" ); + } + + if ( !x.isJsonPrimitive() || !((JsonPrimitive) x).isNumber() ) { + throw new JsonParseException( "x is not a number" ); + } + + if ( !y.isJsonPrimitive() || !((JsonPrimitive) y).isNumber() ) { + throw new JsonParseException( "y is not a number" ); + } + + if ( !z.isJsonPrimitive() || !((JsonPrimitive) z).isNumber() ) { + throw new JsonParseException( "z is not a number" ); + } + + if ( !yaw.isJsonPrimitive() || !((JsonPrimitive) yaw).isNumber() ) { + throw new JsonParseException( "yaw is not a number" ); + } + + if ( !pitch.isJsonPrimitive() || !((JsonPrimitive) pitch).isNumber() ) { + throw new JsonParseException( "pitch is not a number" ); + } + + World worldInstance = Bukkit.getWorld( world.getAsString() ); + if (worldInstance == null) { + throw new IllegalArgumentException("Unknown/not loaded world"); + } + + return new Location( worldInstance, x.getAsDouble(), y.getAsDouble(), z.getAsDouble(), yaw.getAsFloat(), pitch.getAsFloat() ); + + } + + @Override + public JsonElement serialize( Location location, Type type, JsonSerializationContext jsonSerializationContext ) { + + final JsonObject obj = new JsonObject(); + obj.addProperty( "world", location.getWorld().getName() ); + obj.addProperty( "x", location.getX() ); + obj.addProperty( "y", location.getY() ); + obj.addProperty( "z", location.getZ() ); + obj.addProperty( "yaw", location.getYaw() ); + obj.addProperty( "pitch", location.getPitch() ); + return obj; + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java new file mode 100644 index 000000000..5977b30bd --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java @@ -0,0 +1,75 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Map; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.YamlConfiguration; + +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; + +public class MySQLDatabaseConnecterJ implements DatabaseConnecter { + + private String connectionUrl; + private DatabaseConnectionSettingsImpl dbSettings; + private Connection connection = null; + + /** + * Class for MySQL database connections using the settings provided + * @param dbSettings + */ + public MySQLDatabaseConnecterJ(DatabaseConnectionSettingsImpl dbSettings) { + this.dbSettings = dbSettings; + try { + Class.forName("com.mysql.jdbc.Driver").newInstance(); + } catch (Exception e) { + Bukkit.getLogger().severe("Could not instantiate JDBC driver! " + e.getMessage()); + } + // jdbc:mysql://localhost:3306/Peoples?autoReconnect=true&useSSL=false + connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + "/" + dbSettings.getDatabaseName() + "?autoReconnect=true&useSSL=false&allowMultiQueries=true"; + } + + @Override + public Connection createConnection() { + try { + connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword()); + } catch (SQLException e) { + Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage()); + } + return connection; + } + + @Override + public String getConnectionUrl() { + return connectionUrl; + } + + @Override + public String getUniqueId(String tableName) { + // Not used + return ""; + } + + @Override + public YamlConfiguration loadYamlFile(String string, String key) { + // Not used + return null; + } + + @Override + public boolean uniqueIdExists(String tableName, String key) { + // Not used + return false; + } + + @Override + public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, + Map commentMap) { + // Not used + + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java new file mode 100644 index 000000000..f9d5156c0 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java @@ -0,0 +1,191 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.plugin.Plugin; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import us.tastybento.bskyblock.database.DatabaseConnecter; +import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; + +/** + * + * Class that inserts a into the corresponding database-table. + * + * @author tastybento + * + * @param + */ +public class MySQLDatabaseHandlerJ extends AbstractDatabaseHandler { + + /** + * Connection to the database + */ + private Connection connection = null; + + /** + * Handles the connection to the database and creation of the initial database schema (tables) for + * the class that will be stored. + * @param plugin - BSkyBlock plugin object + * @param type - the type of class to be stored in the database. Must inherit DataObject + * @param databaseConnecter - authentication details for the database + */ + public MySQLDatabaseHandlerJ(Plugin plugin, Class type, DatabaseConnecter databaseConnecter) { + super(plugin, type, databaseConnecter); + connection = databaseConnecter.createConnection(); + // Check if the table exists in the database and if not, create it + createSchema(); + } + + /** + * Creates the table in the database if it doesn't exist already + */ + private void createSchema() { + StringBuilder sql = new StringBuilder(); + sql.append("CREATE TABLE IF NOT EXISTS `"); + sql.append(dataObject.getCanonicalName()); + sql.append("` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), INDEX i (uniqueId) );"); + // Prepare and execute the database statements + try (PreparedStatement pstmt = connection.prepareStatement(sql.toString())) { + pstmt.executeUpdate(); + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage()); + } + } + + @Override + public List loadObjects() { + List list = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT `json` FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("`"); + try (Statement preparedStatement = connection.createStatement()) { + try (ResultSet resultSet = preparedStatement.executeQuery(sb.toString())) { + // Load all the results + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; + Gson gson = builder.create(); + while (resultSet.next()) { + list.add(gson.fromJson(resultSet.getString("json"), dataObject)); + } + } + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not load objects " + e.getMessage()); + } + return list; + } + + + @Override + public T loadObject(String uniqueId) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT `json` FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("` WHERE uniqueId = ? LIMIT 1"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { + preparedStatement.setString(1, uniqueId); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + // If there is a result, we only want/need the first one + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; + Gson gson = builder.create(); + return gson.fromJson(resultSet.getString("json"), dataObject); + } + } + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not load object " + uniqueId + " " + e.getMessage()); + } + return null; + } + + @Override + public void saveObject(T instance) { + Bukkit.getLogger().severe(() -> "Saving object " + instance.getClass().getName()); + StringBuilder sb = new StringBuilder(); + // Replace into is used so that any data in the table will be replaced with updated data + sb.append("REPLACE INTO "); + sb.append("`"); + // The table name is the canonical name, so that add-ons can be sure of a unique table in the database + sb.append(dataObject.getCanonicalName()); + sb.append("` (json) VALUES (?)"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { + GsonBuilder builder = new GsonBuilder(); + builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; + Gson gson = builder.create(); + preparedStatement.setString(1, gson.toJson(instance)); + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not save object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + @Override + public void deleteObject(T instance) { + StringBuilder sb = new StringBuilder(); + sb.append("DELETE FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("` WHERE uniqueId = ?"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { + Method getUniqueId = dataObject.getMethod("getUniqueId"); + String uniqueId = (String) getUniqueId.invoke(instance); + preparedStatement.setString(1, uniqueId); + preparedStatement.executeQuery(); + } catch (Exception e) { + plugin.getLogger().severe(() -> "Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); + } + } + + /* (non-Javadoc) + * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) + */ + @Override + public boolean objectExists(String key) { + // Create the query to see if this key exists + StringBuilder query = new StringBuilder(); + query.append("SELECT IF ( EXISTS( SELECT * FROM `"); + query.append(dataObject.getCanonicalName()); + query.append("` WHERE `uniqueId` = ?), 1, 0)"); + + try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { + preparedStatement.setString(1, key); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + if (resultSet.next()) { + return resultSet.getBoolean(1); + } + } + } catch (SQLException e) { + plugin.getLogger().severe("Could not check if key exists in database! " + key + " " + e.getMessage()); + } + return false; + } + + @Override + public void saveSettings(T instance) + throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException { + // This method should not be used because configs are not stored in MySQL + + } + + @Override + public T loadSettings(String uniqueId, T dbConfig) { + // This method should not be used because configs are not stored in MySQL + return null; + } + + + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java new file mode 100755 index 000000000..8fa9a17a4 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java @@ -0,0 +1,22 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.BSBDatabase; +import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; +import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; + +public class MySQLDatabaseJ extends BSBDatabase{ + + @Override + public AbstractDatabaseHandler getHandler(Class type) { + BSkyBlock plugin = BSkyBlock.getInstance(); + return new MySQLDatabaseHandlerJ<>(plugin, type, new MySQLDatabaseConnecterJ(new DatabaseConnectionSettingsImpl( + plugin.getSettings().getDbHost(), + plugin.getSettings().getDbPort(), + plugin.getSettings().getDbName(), + plugin.getSettings().getDbUsername(), + plugin.getSettings().getDbPassword() + ))); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Players.java b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java index 77b8893fa..fa8028375 100755 --- a/src/main/java/us/tastybento/bskyblock/database/objects/Players.java +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java @@ -18,13 +18,13 @@ import us.tastybento.bskyblock.BSkyBlock; * @author tastybento */ public class Players implements DataObject { - private Map homeLocations; + private Map homeLocations = new HashMap<>(); private String uniqueId; private String playerName; private int resetsLeft; private String locale = ""; private int deaths; - private Map kickedList; + private Map kickedList = new HashMap<>(); /** * This is required for database storage diff --git a/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java b/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java new file mode 100644 index 000000000..c4be2f900 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java @@ -0,0 +1,109 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Server; +import org.bukkit.World; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.plugin.PluginManager; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.modules.junit4.PowerMockRunner; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.database.mysql.MySQLDatabaseConnecter; +import us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandlerTestDataObject; + +@RunWith(PowerMockRunner.class) +public class MySQLDatabaseHandlerJTest { + + private static MySQLDatabaseHandlerJ handler; + private static MySQLDatabaseHandlerTestDataObject instance; + private static String UNIQUE_ID = "xyz"; + private static MySQLDatabaseConnecter dbConn; + private static World world; + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + Server server = mock(Server.class); + world = mock(World.class); + when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); + when(server.getWorld("world")).thenReturn(world); + when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + + Bukkit.setServer(server); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + + BSkyBlock plugin = mock(BSkyBlock.class); + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + dbConn = mock(MySQLDatabaseConnecter.class); + Connection connection = mock(Connection.class); + when(dbConn.createConnection()).thenReturn(connection); + PreparedStatement ps = mock(PreparedStatement.class); + when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps); + Statement statement = mock(Statement.class); + when(connection.createStatement()).thenReturn(statement); + ResultSet rs = mock(ResultSet.class); + when(ps.executeQuery()).thenReturn(rs); + when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); + instance = new MySQLDatabaseHandlerTestDataObject(); + instance.setUniqueId(UNIQUE_ID); + handler = new MySQLDatabaseHandlerJ<>(plugin, MySQLDatabaseHandlerTestDataObject.class, dbConn); + + } + + @Test + public void testSaveObject() { + handler.saveObject(instance); + BSkyBlock plugin = mock(BSkyBlock.class); + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDeathsMax()).thenReturn(10); + Players players = new Players(); + players.setUniqueId(UUID.randomUUID().toString()); + players.setDeaths(23); + Location location = mock(Location.class); + Mockito.when(location.getWorld()).thenReturn(world); + Mockito.when(location.getBlockX()).thenReturn(0); + Mockito.when(location.getBlockY()).thenReturn(0); + Mockito.when(location.getBlockZ()).thenReturn(0); + + players.setHomeLocation(location); + players.setHomeLocation(location, 1); + players.setHomeLocation(location, 2); + Map map = new HashMap<>(); + map.put(location, 324L); + players.setKickedList(map); + players.setLocale("sdfsd"); + players.setPlayerName("name"); + players.setPlayerUUID(UUID.randomUUID()); + players.setResetsLeft(3); + + MySQLDatabaseHandlerJ h = new MySQLDatabaseHandlerJ<>(plugin, Players.class, dbConn); + h.saveObject(players); + } + +} diff --git a/src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java b/src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java new file mode 100755 index 000000000..2b7afe7c8 --- /dev/null +++ b/src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java @@ -0,0 +1,276 @@ +package us.tastybento.bskyblock.database.mysqljson; + +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.objects.DataObject; + +/** + * Tracks the following info on the player + * + * @author tastybento + */ +public class Players implements DataObject { + private Map homeLocations = new HashMap<>(); + private String uniqueId; + private String playerName; + private int resetsLeft; + private String locale = ""; + private int deaths; + private Map kickedList = new HashMap<>(); + + /** + * This is required for database storage + */ + public Players() {} + + /** + * @param plugin - BSkyBlock plugin object + * @param uniqueId - unique ID + * Constructor - initializes the state variables + * + */ + public Players(BSkyBlock plugin, final UUID uniqueId) { + this.uniqueId = uniqueId.toString(); + homeLocations = new HashMap<>(); + playerName = ""; + resetsLeft = plugin.getSettings().getResetLimit(); + locale = ""; + kickedList = new HashMap<>(); + playerName = Bukkit.getServer().getOfflinePlayer(uniqueId).getName(); + if (playerName == null) { + playerName = uniqueId.toString(); + } + } + + /** + * Gets the default home location. + * @return Location + */ + public Location getHomeLocation() { + return getHomeLocation(1); // Default + } + + /** + * Gets the home location by number. + * @param number - a number + * @return Location of this home or null if not available + */ + public Location getHomeLocation(int number) { + /* + Bukkit.getLogger().info("DEBUG: getting home location " + number); + + Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); + for (Entry en : homeLocations.entrySet()) { + Bukkit.getLogger().info("DEBUG: " + en.getKey() + " ==> " + en.getValue()); + if (number == en.getKey()) + Bukkit.getLogger().info("DEBUG: key = number"); + }*/ + return homeLocations.get(number); + } + + /** + * @return List of home locations + */ + public Map getHomeLocations() { + return homeLocations; + } + + /** + * @return the kickedList + */ + public Map getKickedList() { + return kickedList; + } + + /** + * @param kickedList the kickedList to set + */ + public void setKickedList(Map kickedList) { + this.kickedList = kickedList; + } + + /** + * @param homeLocations the homeLocations to set + */ + public void setHomeLocations(Map homeLocations) { + //Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); + this.homeLocations = homeLocations; + } + + /** + * @param playerName the playerName to set + */ + public void setPlayerName(String playerName) { + this.playerName = playerName; + } + + public Player getPlayer() { + return Bukkit.getPlayer(UUID.fromString(uniqueId)); + } + + public UUID getPlayerUUID() { + return UUID.fromString(uniqueId); + } + + public String getPlayerName() { + return playerName; + } + + /** + * @return the resetsLeft + */ + public int getResetsLeft() { + return resetsLeft; + } + + /** + * @param resetsLeft + * the resetsLeft to set + */ + public void setResetsLeft(int resetsLeft) { + this.resetsLeft = resetsLeft; + } + + /** + * Stores the home location of the player in a String format + * + * @param l + * a Bukkit location + */ + public void setHomeLocation(final Location l) { + setHomeLocation(l, 1); + } + + /** + * Stores the numbered home location of the player. Numbering starts at 1. + * @param location - the location + * @param number - a number + */ + public void setHomeLocation(final Location location, int number) { + if (location == null) { + homeLocations.clear(); + } else { + homeLocations.put(number, location); + } + } + + /** + * Set the uuid for this player object + * @param uuid - UUID + */ + public void setPlayerUUID(final UUID uuid) { + uniqueId = uuid.toString(); + } + + /** + * Clears all home Locations + */ + public void clearHomeLocations() { + homeLocations.clear(); + } + + /** + * @return the locale + */ + public String getLocale() { + return locale; + } + + /** + * @param locale the locale to set + */ + public void setLocale(String locale) { + this.locale = locale; + } + + /** + * @return the deaths + */ + public int getDeaths() { + return deaths; + } + + /** + * @param deaths the deaths to set + */ + public void setDeaths(int deaths) { + this.deaths = deaths; + } + + /** + * Add death + */ + public void addDeath() { + deaths++; + if (deaths > getPlugin().getSettings().getDeathsMax()) { + deaths = getPlugin().getSettings().getDeathsMax(); + } + } + + /** + * Can invite or still waiting for cool down to end + * + * @param location - the location + * to check + * @return number of mins/hours left until cool down ends + */ + public long getInviteCoolDownTime(Location location) { + // Check the hashmap + if (location != null && kickedList.containsKey(location)) { + // plugin.getLogger().info("DEBUG: Location is known"); + // The location is in the list + // Check the date/time + Date kickedDate = new Date(kickedList.get(location)); + // plugin.getLogger().info("DEBUG: kicked date = " + kickedDate); + Calendar coolDownTime = Calendar.getInstance(); + coolDownTime.setTime(kickedDate); + // coolDownTime.add(Calendar.HOUR_OF_DAY, Settings.inviteWait); + coolDownTime.add(Calendar.MINUTE, getPlugin().getSettings().getInviteWait()); + // Add the invite cooldown period + Calendar timeNow = Calendar.getInstance(); + // plugin.getLogger().info("DEBUG: date now = " + timeNow); + if (coolDownTime.before(timeNow)) { + // The time has expired + kickedList.remove(location); + return 0; + } else { + // Still not there yet + // long hours = (coolDownTime.getTimeInMillis() - + // timeNow.getTimeInMillis())/(1000 * 60 * 60); + // Temp minutes + return (coolDownTime.getTimeInMillis() - timeNow.getTimeInMillis()) / (1000 * 60); + } + } + return 0; + } + + /** + * Starts the invite cooldown timer for location. Location should be the center of an island. + * @param location - the location + */ + public void startInviteCoolDownTimer(Location location) { + if (location != null) { + kickedList.put(location, System.currentTimeMillis()); + } + } + + @Override + public String getUniqueId() { + return uniqueId; + } + + @Override + public void setUniqueId(String uniqueId) { + this.uniqueId = uniqueId; + } + +} From cf792779f9b8a27098efe88e69eee1bbeade59ed Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 12 Mar 2018 04:08:19 +0900 Subject: [PATCH 2/5] Now using JSON-based data for MySQL Large number of changes to improve database functions. --- .../us/tastybento/bskyblock/BSkyBlock.java | 26 +- .../bskyblock/api/addons/Addon.java | 4 +- .../api/commands/CompositeCommand.java | 4 +- .../api/configuration/BSBConfig.java | 4 +- .../api/configuration/ISettings.java | 7 +- .../tastybento/bskyblock/api/flags/Flag.java | 1 - .../api/panels/builders/PanelItemBuilder.java | 5 +- .../commands/island/IslandCreateCommand.java | 2 +- .../commands/island/IslandResetCommand.java | 2 +- .../AbstractDatabaseHandler.java | 18 +- .../bskyblock/database/BSBDatabase.java | 6 +- .../database/flatfile/ConfigHandler.java | 39 + .../database/flatfile/FlatFileDatabase.java | 11 +- .../flatfile/FlatFileDatabaseHandler.java | 27 +- .../database/mysql/MySQLDatabase.java | 2 +- .../database/mysql/MySQLDatabaseHandler.java | 829 +++--------------- .../database/mysql/adapters/FlagAdapter.java | 39 + .../mysql/adapters/LocationAdapter.java | 54 ++ .../database/mysql/adapters/WorldAdapter.java | 39 + .../database/mysqljson/LocationAdapter.java | 88 -- .../mysqljson/MySQLDatabaseConnecterJ.java | 75 -- .../mysqljson/MySQLDatabaseHandlerJ.java | 191 ---- .../database/mysqljson/MySQLDatabaseJ.java | 22 - .../bskyblock/database/objects/Island.java | 80 +- .../bskyblock/database/objects/Players.java | 21 +- .../listeners/JoinLeaveListener.java | 2 +- .../listeners/flags/AbstractFlagListener.java | 2 +- .../bskyblock/managers/FlagsManager.java | 1 - .../island => managers}/IslandsManager.java | 6 +- .../managers/PlayersManager.java | 5 +- .../managers/island/IslandCache.java | 392 +++++++++ .../bskyblock/managers/island/NewIsland.java | 252 ++++++ src/test/java/bskyblock/TestBSkyBlock.java | 2 +- .../mysql/MySQLDatabaseHandlerTest.java | 264 +++--- .../{mysqljson => mysql}/Players.java | 0 .../mysqljson/MySQLDatabaseHandlerJTest.java | 109 --- .../listeners/flags/FireListenerTest.java | 2 +- .../listeners/flags/MobSpawnListenerTest.java | 2 +- 38 files changed, 1123 insertions(+), 1512 deletions(-) rename src/main/java/us/tastybento/bskyblock/database/{managers => }/AbstractDatabaseHandler.java (78%) create mode 100644 src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java delete mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java delete mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java delete mode 100644 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java delete mode 100755 src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java rename src/main/java/us/tastybento/bskyblock/{database/managers/island => managers}/IslandsManager.java (99%) rename src/main/java/us/tastybento/bskyblock/{database => }/managers/PlayersManager.java (99%) create mode 100644 src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java create mode 100644 src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java rename src/test/java/us/tastybento/bskyblock/database/{mysqljson => mysql}/Players.java (100%) delete mode 100644 src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java diff --git a/src/main/java/us/tastybento/bskyblock/BSkyBlock.java b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java index b0ea1348a..c300070ae 100755 --- a/src/main/java/us/tastybento/bskyblock/BSkyBlock.java +++ b/src/main/java/us/tastybento/bskyblock/BSkyBlock.java @@ -7,8 +7,6 @@ import us.tastybento.bskyblock.api.placeholders.PlaceholderHandler; import us.tastybento.bskyblock.commands.AdminCommand; import us.tastybento.bskyblock.commands.IslandCommand; import us.tastybento.bskyblock.database.BSBDatabase; -import us.tastybento.bskyblock.database.managers.PlayersManager; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; import us.tastybento.bskyblock.generators.IslandWorld; import us.tastybento.bskyblock.listeners.JoinLeaveListener; import us.tastybento.bskyblock.listeners.NetherPortals; @@ -16,7 +14,9 @@ import us.tastybento.bskyblock.listeners.PanelListenerManager; import us.tastybento.bskyblock.managers.AddonsManager; import us.tastybento.bskyblock.managers.CommandsManager; import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandsManager; import us.tastybento.bskyblock.managers.LocalesManager; +import us.tastybento.bskyblock.managers.PlayersManager; import us.tastybento.bskyblock.managers.RanksManager; /** @@ -26,7 +26,7 @@ import us.tastybento.bskyblock.managers.RanksManager; */ public class BSkyBlock extends JavaPlugin { - private static BSkyBlock plugin; + private static BSkyBlock instance; // Databases private PlayersManager playersManager; @@ -74,7 +74,7 @@ public class BSkyBlock extends JavaPlugin { ranksManager = new RanksManager(this); // Load metrics - metrics = new Metrics(plugin); + metrics = new Metrics(instance); registerCustomCharts(); // Set up commands @@ -87,29 +87,29 @@ public class BSkyBlock extends JavaPlugin { // at this point. Therefore, the 1 tick scheduler is required. getServer().getScheduler().runTask(this, () -> { // Create the world if it does not exist - islandWorldManager = new IslandWorld(plugin); + islandWorldManager = new IslandWorld(instance); - getServer().getScheduler().runTask(plugin, () -> { + getServer().getScheduler().runTask(instance, () -> { // Load Flags - flagsManager = new FlagsManager(plugin); + flagsManager = new FlagsManager(instance); // Load islands from database islandsManager.load(); - localesManager = new LocalesManager(plugin); - PlaceholderHandler.register(plugin); + localesManager = new LocalesManager(instance); + PlaceholderHandler.register(instance); // Register Listeners registerListeners(); // Load addons - addonsManager = new AddonsManager(plugin); + addonsManager = new AddonsManager(instance); addonsManager.enableAddons(); // Save islands & players data asynchronously every X minutes getSettings().setDatabaseBackupPeriod(10 * 60 * 20); - plugin.getServer().getScheduler().runTaskTimer(plugin, () -> { + instance.getServer().getScheduler().runTaskTimer(instance, () -> { playersManager.save(true); islandsManager.save(true); }, getSettings().getDatabaseBackupPeriod(), getSettings().getDatabaseBackupPeriod()); @@ -193,11 +193,11 @@ public class BSkyBlock extends JavaPlugin { } private static void setInstance(BSkyBlock plugin) { - BSkyBlock.plugin = plugin; + BSkyBlock.instance = plugin; } public static BSkyBlock getInstance() { - return plugin; + return instance; } /** diff --git a/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java b/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java index a3c0144bc..1c1d594bc 100644 --- a/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java +++ b/src/main/java/us/tastybento/bskyblock/api/addons/Addon.java @@ -15,8 +15,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.event.Listener; import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.database.managers.PlayersManager; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; /** * Add-on class for BSkyBlock. Extend this to create an add-on. The operation diff --git a/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java index 37dbc66cf..de079d311 100644 --- a/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java +++ b/src/main/java/us/tastybento/bskyblock/api/commands/CompositeCommand.java @@ -19,8 +19,8 @@ import org.bukkit.entity.Player; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Settings; import us.tastybento.bskyblock.api.events.command.CommandEvent; -import us.tastybento.bskyblock.database.managers.PlayersManager; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; +import us.tastybento.bskyblock.managers.IslandsManager; +import us.tastybento.bskyblock.managers.PlayersManager; import us.tastybento.bskyblock.util.Util; /** diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java b/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java index 6363b1b1f..90de7aa8d 100644 --- a/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/BSBConfig.java @@ -9,8 +9,8 @@ import java.util.logging.Logger; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.addons.Addon; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; /** * Handy config class to store and load Java POJOs as YAML configs @@ -90,4 +90,4 @@ public class BSBConfig { } -} +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/api/configuration/ISettings.java b/src/main/java/us/tastybento/bskyblock/api/configuration/ISettings.java index 6f32e081f..16e1d61a0 100644 --- a/src/main/java/us/tastybento/bskyblock/api/configuration/ISettings.java +++ b/src/main/java/us/tastybento/bskyblock/api/configuration/ISettings.java @@ -4,9 +4,10 @@ import java.beans.IntrospectionException; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.BSBDatabase; +import us.tastybento.bskyblock.database.flatfile.ConfigHandler; import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; /** * Simple interface for tagging all classes containing ConfigEntries. @@ -21,7 +22,7 @@ public interface ISettings { @SuppressWarnings("unchecked") default void saveSettings() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IntrospectionException, SQLException { // Get the handler - AbstractDatabaseHandler settingsHandler = (AbstractDatabaseHandler) new FlatFileDatabase().getHandler(getInstance().getClass()); + ConfigHandler settingsHandler = (ConfigHandler) new FlatFileDatabase().getConfig(getInstance().getClass()); // Load every field in the config class settingsHandler.saveSettings(getInstance()); } @@ -44,7 +45,7 @@ public interface ISettings { dbConfig = dbhandler.loadObject(getUniqueId()); } // Get the handler - AbstractDatabaseHandler configHandler = (AbstractDatabaseHandler) new FlatFileDatabase().getHandler(getInstance().getClass()); + ConfigHandler configHandler = (ConfigHandler ) new FlatFileDatabase().getConfig(getInstance().getClass()); // Load every field in the config class return configHandler.loadSettings(getUniqueId(), dbConfig); } diff --git a/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java b/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java index e480693a9..3cd0d03a8 100644 --- a/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java +++ b/src/main/java/us/tastybento/bskyblock/api/flags/Flag.java @@ -9,7 +9,6 @@ import org.bukkit.inventory.ItemStack; import us.tastybento.bskyblock.api.commands.User; import us.tastybento.bskyblock.api.panels.PanelItem; import us.tastybento.bskyblock.api.panels.builders.PanelItemBuilder; -import us.tastybento.bskyblock.managers.RanksManager; public class Flag implements Comparable { diff --git a/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java index 8ee5e505c..a088eeac4 100644 --- a/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java +++ b/src/main/java/us/tastybento/bskyblock/api/panels/builders/PanelItemBuilder.java @@ -1,6 +1,9 @@ package us.tastybento.bskyblock.api.panels.builders; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.Material; diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java index facda7acd..0851be20e 100644 --- a/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandCreateCommand.java @@ -11,7 +11,7 @@ import us.tastybento.bskyblock.api.commands.CompositeCommand; import us.tastybento.bskyblock.api.commands.User; import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; import us.tastybento.bskyblock.commands.IslandCommand; -import us.tastybento.bskyblock.database.managers.island.NewIsland; +import us.tastybento.bskyblock.managers.island.NewIsland; /** * /island create - Create an island. diff --git a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java index 194eb8790..4bc68cf44 100644 --- a/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java +++ b/src/main/java/us/tastybento/bskyblock/commands/island/IslandResetCommand.java @@ -10,8 +10,8 @@ import us.tastybento.bskyblock.Constants; import us.tastybento.bskyblock.api.commands.CompositeCommand; import us.tastybento.bskyblock.api.commands.User; import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; -import us.tastybento.bskyblock.database.managers.island.NewIsland; import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.island.NewIsland; public class IslandResetCommand extends CompositeCommand { diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java similarity index 78% rename from src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java rename to src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java index b49b9f507..8388defc8 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/AbstractDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/AbstractDatabaseHandler.java @@ -1,4 +1,4 @@ -package us.tastybento.bskyblock.database.managers; +package us.tastybento.bskyblock.database; import java.beans.IntrospectionException; import java.lang.reflect.InvocationTargetException; @@ -7,8 +7,6 @@ import java.util.List; import org.bukkit.plugin.Plugin; -import us.tastybento.bskyblock.database.DatabaseConnecter; - /** * An abstract class that handles insert/select-operations into/from a database * @@ -83,18 +81,4 @@ public abstract class AbstractDatabaseHandler { */ public abstract boolean objectExists(String key); - /** - * Saves a file as settings - * @param instance - */ - public abstract void saveSettings(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException; - - /** - * Loads a file as settings - * @param uniqueId - unique ID - * @param dbConfig - the database mirror of this object. It will be checked against what is loaded to see if any significant changes have been made - * @return Settings object - */ - public abstract T loadSettings(String uniqueId, T dbConfig) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IntrospectionException; - } diff --git a/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java index 7f400f068..a70416367 100755 --- a/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java +++ b/src/main/java/us/tastybento/bskyblock/database/BSBDatabase.java @@ -2,9 +2,7 @@ package us.tastybento.bskyblock.database; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.database.flatfile.FlatFileDatabase; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.mysql.MySQLDatabase; -import us.tastybento.bskyblock.database.mysqljson.MySQLDatabaseJ; public abstract class BSBDatabase { @@ -24,9 +22,7 @@ public abstract class BSBDatabase { public enum DatabaseType{ FLATFILE(new FlatFileDatabase()), - MYSQL(new MySQLDatabase()), - //JSON(); - MYSQLJ(new MySQLDatabaseJ()); + MYSQL(new MySQLDatabase()); BSBDatabase database; diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java new file mode 100644 index 000000000..45dfd67a6 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/ConfigHandler.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.database.flatfile; + +import java.beans.IntrospectionException; +import java.lang.reflect.InvocationTargetException; + +import org.bukkit.plugin.Plugin; + +import us.tastybento.bskyblock.database.DatabaseConnecter; + +/** + * Class handles config settings saving and loading + * + * @author tastybento + * + * @param Handles config files for Class + */ + +public class ConfigHandler extends FlatFileDatabaseHandler { + + public ConfigHandler(Plugin plugin, Class type, DatabaseConnecter databaseConnecter) { + super(plugin, type, databaseConnecter); + } + + public void saveSettings(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException { + configFlag = true; + saveObject(instance); + } + + public T loadSettings(String uniqueId, T dbConfig) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IntrospectionException { + if (dbConfig == null) { + return loadObject(uniqueId); + } + + // TODO: compare the loaded with the database copy + + return loadObject(uniqueId); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java index fb656963b..d4b2f7aed 100755 --- a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabase.java @@ -1,8 +1,8 @@ package us.tastybento.bskyblock.database.flatfile; import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.BSBDatabase; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; public class FlatFileDatabase extends BSBDatabase{ @@ -10,5 +10,14 @@ public class FlatFileDatabase extends BSBDatabase{ public AbstractDatabaseHandler getHandler(Class type) { return new FlatFileDatabaseHandler<>(BSkyBlock.getInstance(), type, new FlatFileDatabaseConnecter(BSkyBlock.getInstance(), null)); } + + /** + * Get the config + * @param type - config object type + * @return - the config handler + */ + public AbstractDatabaseHandler getConfig(Class type) { + return new ConfigHandler<>(BSkyBlock.getInstance(), type, new FlatFileDatabaseConnecter(BSkyBlock.getInstance(), null)); + } } diff --git a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java index 1b0e81b3c..7b66f86e2 100644 --- a/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/flatfile/FlatFileDatabaseHandler.java @@ -31,8 +31,8 @@ import us.tastybento.bskyblock.Constants.GameType; import us.tastybento.bskyblock.api.configuration.ConfigComment; import us.tastybento.bskyblock.api.configuration.ConfigEntry; import us.tastybento.bskyblock.api.configuration.StoreAt; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.DatabaseConnecter; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.objects.adapters.Adapter; import us.tastybento.bskyblock.database.objects.adapters.AdapterInterface; import us.tastybento.bskyblock.util.Util; @@ -50,7 +50,7 @@ public class FlatFileDatabaseHandler extends AbstractDatabaseHandler { private static final String DATABASE_FOLDER_NAME = "database"; private static final boolean DEBUG = false; - private boolean configFlag; + protected boolean configFlag; public FlatFileDatabaseHandler(Plugin plugin, Class type, DatabaseConnecter databaseConnecter) { super(plugin, type, databaseConnecter); @@ -264,15 +264,6 @@ public class FlatFileDatabaseHandler extends AbstractDatabaseHandler { return instance; } - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#saveConfig(java.lang.Object) - */ - @Override - public void saveSettings(T instance) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException { - configFlag = true; - saveObject(instance); - } - /** * Inserts T into the corresponding database-table * @@ -525,18 +516,4 @@ public class FlatFileDatabaseHandler extends AbstractDatabaseHandler { } } - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#loadSettings(java.lang.String, java.lang.Object) - */ - @Override - public T loadSettings(String uniqueId, T dbConfig) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IntrospectionException { - if (dbConfig == null) { - return loadObject(uniqueId); - } - - // TODO: compare the loaded with the database copy - - return loadObject(uniqueId); - } - } diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java index 1654456c3..8a602ec4f 100755 --- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabase.java @@ -1,9 +1,9 @@ package us.tastybento.bskyblock.database.mysql; import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.BSBDatabase; import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; public class MySQLDatabase extends BSBDatabase{ diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java index b26a23f9b..f0d8a7963 100644 --- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java @@ -1,42 +1,30 @@ package us.tastybento.bskyblock.database.mysql; -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.math.BigDecimal; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.sql.Time; -import java.sql.Timestamp; import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.UUID; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.plugin.Plugin; +import org.bukkit.potion.PotionEffectType; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.DatabaseConnecter; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; -import us.tastybento.bskyblock.database.objects.adapters.Adapter; -import us.tastybento.bskyblock.database.objects.adapters.AdapterInterface; -import us.tastybento.bskyblock.util.Util; +import us.tastybento.bskyblock.database.mysql.adapters.FlagAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.LocationAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.PotionEffectTypeAdapter; +import us.tastybento.bskyblock.database.mysql.adapters.WorldAdapter; +import us.tastybento.bskyblock.database.objects.DataObject; /** * @@ -52,47 +40,8 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { * Connection to the database */ private Connection connection = null; - /** - * This hashmap maps Java types to MySQL SQL types because they are not the same - */ - private static final HashMap MYSQL_MAPPING = new HashMap<>(); - private static final String STRING_MAP = "VARCHAR(254)"; - - static { - MYSQL_MAPPING.put(boolean.class.getTypeName(), "BOOL"); - MYSQL_MAPPING.put(byte.class.getTypeName(), "TINYINT"); - MYSQL_MAPPING.put(short.class.getTypeName(), "SMALLINT"); - MYSQL_MAPPING.put(int.class.getTypeName(), "INTEGER"); - MYSQL_MAPPING.put(long.class.getTypeName(), "BIGINT"); - MYSQL_MAPPING.put(double.class.getTypeName(), "DOUBLE PRECISION"); - MYSQL_MAPPING.put(Boolean.class.getTypeName(), "BOOL"); - MYSQL_MAPPING.put(Byte.class.getTypeName(), "TINYINT"); - MYSQL_MAPPING.put(Short.class.getTypeName(), "SMALLINT"); - MYSQL_MAPPING.put(Integer.class.getTypeName(), "INTEGER"); - MYSQL_MAPPING.put(Long.class.getTypeName(), "BIGINT"); - MYSQL_MAPPING.put(Double.class.getTypeName(), "DOUBLE PRECISION"); - MYSQL_MAPPING.put(BigDecimal.class.getTypeName(), "DECIMAL(13,0)"); - MYSQL_MAPPING.put(String.class.getTypeName(), STRING_MAP); - MYSQL_MAPPING.put(Date.class.getTypeName(), "DATE"); - MYSQL_MAPPING.put(Time.class.getTypeName(), "TIME"); - MYSQL_MAPPING.put(Timestamp.class.getTypeName(), "TIMESTAMP"); - MYSQL_MAPPING.put(UUID.class.getTypeName(), "VARCHAR(36)"); - - // Bukkit Mappings - MYSQL_MAPPING.put(Location.class.getTypeName(), STRING_MAP); - MYSQL_MAPPING.put(World.class.getTypeName(), STRING_MAP); - - // Collections are stored as additional tables. The boolean indicates whether there - // is any data in it or not (maybe) - MYSQL_MAPPING.put(Set.class.getTypeName(), "BOOL"); - MYSQL_MAPPING.put(Map.class.getTypeName(), "BOOL"); - MYSQL_MAPPING.put(HashMap.class.getTypeName(), "BOOL"); - MYSQL_MAPPING.put(ArrayList.class.getTypeName(), "BOOL"); - - // Enums - MYSQL_MAPPING.put(Enum.class.getTypeName(), STRING_MAP); - - } + + private BSkyBlock plugin; /** * Handles the connection to the database and creation of the initial database schema (tables) for @@ -101,13 +50,14 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { * @param type - the type of class to be stored in the database. Must inherit DataObject * @param databaseConnecter - authentication details for the database */ - public MySQLDatabaseHandler(Plugin plugin, Class type, DatabaseConnecter databaseConnecter) { + public MySQLDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter databaseConnecter) { super(plugin, type, databaseConnecter); + this.plugin = plugin; connection = databaseConnecter.createConnection(); // Check if the table exists in the database and if not, create it createSchema(); } - + /** * Creates the table in the database if it doesn't exist already */ @@ -115,70 +65,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { StringBuilder sql = new StringBuilder(); sql.append("CREATE TABLE IF NOT EXISTS `"); sql.append(dataObject.getCanonicalName()); - sql.append("` ("); - // Run through the fields of the class using introspection - for (Field field : dataObject.getDeclaredFields()) { - try { - // Get the description of the field - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); - // Get default SQL mappings - // Get the write method for this field. This method will take an argument of the type of this field. - Method writeMethod = propertyDescriptor.getWriteMethod(); - // The SQL column name is the name of the field - String columnName = field.getName(); - // Get the mapping for this field from the hashmap - String typeName = propertyDescriptor.getPropertyType().getTypeName(); - if (propertyDescriptor.getPropertyType().isEnum()) { - typeName = "Enum"; - } - String mapping = MYSQL_MAPPING.get(typeName); - // If it exists, then create the SQL - if (mapping != null) { - // Note that the column name must be enclosed in `'s because it may include reserved words. - sql.append("`"); - sql.append(columnName); - sql.append("` "); - sql.append(mapping); - sql.append(","); - // Create set and map tables if the type is a collection - if (propertyDescriptor.getPropertyType().equals(Set.class) || - propertyDescriptor.getPropertyType().equals(Map.class) || - propertyDescriptor.getPropertyType().equals(HashMap.class) || - propertyDescriptor.getPropertyType().equals(ArrayList.class)) { - // The ID in this table relates to the parent table and is unique - StringBuilder setSql = new StringBuilder(); - setSql.append("CREATE TABLE IF NOT EXISTS `"); - setSql.append(dataObject.getCanonicalName()); - setSql.append("."); - setSql.append(field.getName()); - setSql.append("` (uniqueId VARCHAR(36) NOT NULL, "); - // Get columns separated by commas - setSql.append(getCollectionColumnString(writeMethod,false,true)); - // Close the SQL string - setSql.append(")"); - // Execute the statement - try (PreparedStatement collections = connection.prepareStatement(setSql.toString())) { - collections.executeUpdate(); - } catch (SQLException e) { - plugin.getLogger().severe(() -> "Getter or setter missing in data object. Cannot create schema! " + e.getMessage()); - } - } - } else { - // The Java type is not in the hashmap, so we'll just guess that it can be stored in a string - // This should NOT be used in general because every type should be in the hashmap - sql.append(field.getName()); - sql.append(" "); - sql.append(STRING_MAP); - sql.append(","); - plugin.getLogger().severe("Unknown type! Hoping it'll fit in a string!"); - plugin.getLogger().severe(propertyDescriptor.getPropertyType().getTypeName()); - } - } catch (IntrospectionException e) { - plugin.getLogger().severe(() -> "Getter or setter missing in data object. Cannot create schema! " + e.getMessage()); - } - } - // For the main table for the class, the unique ID is the primary key - sql.append(" PRIMARY KEY (uniqueId))"); + sql.append("` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), UNIQUE INDEX i (uniqueId) )"); // Prepare and execute the database statements try (PreparedStatement pstmt = connection.prepareStatement(sql.toString())) { pstmt.executeUpdate(); @@ -186,597 +73,111 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { plugin.getLogger().severe(() -> "Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage()); } } - - /** - * - * Creates a comma-separated-String with the names of the variables in this - * class - * Not used in Flat File database. - * @param usePlaceHolders - * true, if PreparedStatement-placeholders ('?') should be used - * instead of the names of the variables - * @return a comma-separated-String with the names of the variables - */ - public String getColumns(boolean usePlaceHolders) { - StringBuilder sb = new StringBuilder(); - - boolean first = true; - /* Iterate the column-names */ - for (Field f : dataObject.getDeclaredFields()) { - if (first) { - first = false; - } else { - sb.append(", "); - } - - if (usePlaceHolders) { - sb.append("?"); - } else { - sb.append("`" + f.getName() + "`"); - } - } - - return sb.toString(); + + // Gets the GSON builder + private Gson getGSON() { + // excludeFieldsWithoutExposeAnnotation - this means that every field to be stored should use @Expose + // enableComplexMapKeySerialization - forces GSON to use TypeAdapters even for Map keys + GsonBuilder builder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().enableComplexMapKeySerialization(); + // Register adapters + builder.registerTypeAdapter(Location.class, new LocationAdapter(plugin)) ; + builder.registerTypeAdapter(World.class, new WorldAdapter(plugin)); + builder.registerTypeAdapter(Flag.class, new FlagAdapter(plugin)); + builder.registerTypeAdapter(PotionEffectType.class, new PotionEffectTypeAdapter()); + // Keep null in the database + builder.serializeNulls(); + // Allow characters like < or > without escaping them + builder.disableHtmlEscaping(); + return builder.create(); } - /** - * Returns a string of columns separated by commas that represent the parameter types of this method - * this is used to get into parameters like HashMap at runtime and find out Location - * and Boolean. - * @param writeMethod - * @param usePlaceHolders - * true, if PreparedStatement-placeholders ('?') should be used - * instead of the names of the variables - * @param createSchema if true contains the columns types - * @return Returns a string of columns separated by commas. - */ - private String getCollectionColumnString(Method writeMethod, boolean usePlaceHolders, boolean createSchema) { - StringBuilder sb = new StringBuilder(); - List cols = getCollentionColumnList(writeMethod, createSchema); - boolean first = true; - for (String col : cols) { - // Add commas - if (first) { - first = false; - } else { - sb.append(", "); - } - // this is used if the string is going to be used to insert something so the value will replace the ? - if (usePlaceHolders) { - sb.append("?"); - } else { - sb.append(col); - } - } - return sb.toString(); - } - - /** - * Returns a list of columns that represent the parameter types of this method - * @param method - * @param createSchema if true contains the columns types - * @return Returns a list of columns separated by commas. - */ - private List getCollentionColumnList(Method method, boolean createSchema) { - List columns = new ArrayList<>(); - for (Entry en : getCollectionColumnMap(method).entrySet()) { - StringBuilder col = new StringBuilder(); - col.append(en.getKey()); - if (createSchema) { - col.append(" "); - col.append(en.getValue()); - } - columns.add(col.toString()); - } - - return columns; - } - - /** - * Returns a map of column names and their types - * @param method - * @return - */ - private Map getCollectionColumnMap(Method method) { - Map columns = new LinkedHashMap<>(); - // Get the return type - // This uses a trick to extract what the arguments are of the writeMethod of the field. - // In this way, we can deduce what type needs to be written at runtime. - Type[] genericParameterTypes = method.getGenericParameterTypes(); - // There could be more than one argument, so step through them - for (Type genericParameterType : genericParameterTypes) { - // If the argument is a parameter, then do something - this should always be true if the parameter is a collection - if (genericParameterType instanceof ParameterizedType) { - // Get the actual type arguments of the parameter - Type[] parameters = ((ParameterizedType)genericParameterType).getActualTypeArguments(); - //parameters[0] contains java.lang.String for method like "method(List value)" - // Run through them one by one and create a SQL string - int index = 0; - for (Type type : parameters) { - // This is a request for column names. - String setMapping = MYSQL_MAPPING.get(type.getTypeName()); - // This column name format is typeName_# where # is a number incremented from 0 - columns.put("`" + type.getTypeName() + "_" + index + "`", setMapping != null ? setMapping : STRING_MAP); - // Increment the index so each column has a unique name - index++; - } - } - } - return columns; - } - - String createSelectQuery() { - - StringBuilder sb = new StringBuilder(); - - sb.append("SELECT "); - sb.append(getColumns(false)); - sb.append(" FROM "); - sb.append("`"); - sb.append(dataObject.getCanonicalName()); - sb.append("`"); - - return sb.toString(); - } - - String createInsertQuery() { - - StringBuilder sb = new StringBuilder(); - // Replace into is used so that any data in the table will be replaced with updated data - sb.append("REPLACE INTO "); - sb.append("`"); - // The table name is the canonical name, so that add-ons can be sure of a unique table in the database - sb.append(dataObject.getCanonicalName()); - sb.append("`"); - sb.append("("); - sb.append(getColumns(false)); - sb.append(")"); - sb.append(" VALUES ("); - sb.append(getColumns(true)); - sb.append(")"); - - return sb.toString(); - } - - String createDeleteQuery() { - return "DELETE FROM [table_name] WHERE uniqueId = ?"; - } - - /** - * Inserts a into the corresponding database-table - * - * @param instance that should be inserted into the corresponding database-table. Must extend DataObject. - */ - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#insertObject(java.lang.Object) - */ @Override - public void saveObject(T instance) throws SQLException, - SecurityException, IllegalArgumentException, - InstantiationException, IllegalAccessException, - IntrospectionException, InvocationTargetException, NoSuchMethodException { - - try (PreparedStatement preparedStatement = connection.prepareStatement(createInsertQuery())) { - // Get the uniqueId. As each class extends DataObject, it must have this method in it. - PropertyDescriptor propertyDescriptor = new PropertyDescriptor("uniqueId", dataObject); - Method getUniqueId = propertyDescriptor.getReadMethod(); - final String uniqueId = (String) getUniqueId.invoke(instance); - if (uniqueId.isEmpty()) { - throw new SQLException("uniqueId is blank"); - } - // Create the insertion - int i = 0; - // Run through the fields in the class using introspection - for (Field field : dataObject.getDeclaredFields()) { - // Get the field's property descriptor - propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); - // Get the read method for this field - Method method = propertyDescriptor.getReadMethod(); - //sql += "`" + field.getName() + "` " + mapping + ","; - // Invoke the read method to obtain the value from the class - this is the value we need to store in the database - Object value = method.invoke(instance); - // Adapter Notation - Adapter adapterNotation = field.getAnnotation(Adapter.class); - if (adapterNotation != null && AdapterInterface.class.isAssignableFrom(adapterNotation.value())) { - // A conversion adapter has been defined - value = ((AdapterInterface)adapterNotation.value().newInstance()).deserialize(value); - } - // Create set and map table inserts if this is a Collection - if (propertyDescriptor.getPropertyType().equals(Set.class) || - propertyDescriptor.getPropertyType().equals(Map.class) || - propertyDescriptor.getPropertyType().equals(HashMap.class) || - propertyDescriptor.getPropertyType().equals(ArrayList.class)) { - // Collection - // The table is cleared for this uniqueId every time the data is stored - StringBuilder clearTableSql = new StringBuilder(); - clearTableSql.append("DELETE FROM `"); - clearTableSql.append(dataObject.getCanonicalName()); - clearTableSql.append("."); - clearTableSql.append(field.getName()); - clearTableSql.append("` WHERE uniqueId = ?"); - try (PreparedStatement collStatement = connection.prepareStatement(clearTableSql.toString())) { - collStatement.setString(1, uniqueId); - collStatement.execute(); - } - // Insert into the table - StringBuilder setSql = new StringBuilder(); - setSql.append("INSERT INTO `"); - setSql.append(dataObject.getCanonicalName()); - setSql.append("."); - setSql.append(field.getName()); - setSql.append("` (uniqueId, "); - // Get the columns we are going to insert, just the names of them - setSql.append(getCollectionColumnString(propertyDescriptor.getWriteMethod(), false, false)); - setSql.append(") "); - // Get all the ?'s for the columns - setSql.append("VALUES (?,"); - setSql.append(getCollectionColumnString(propertyDescriptor.getWriteMethod(), true, false)); - setSql.append(")"); - // Prepare the statement - try (PreparedStatement collStatement = connection.prepareStatement(setSql.toString())) { - // Set the uniqueId - collStatement.setString(1, uniqueId); - // Do single dimension types (set and list) - if (propertyDescriptor.getPropertyType().equals(Set.class) || - propertyDescriptor.getPropertyType().equals(ArrayList.class)) { - //plugin.getLogger().info("DEBUG: set class for "); - // Loop through the set or list - // Note that we have no idea what type this is - Collection collection = (Collection)value; - Iterator it = collection.iterator(); - while (it.hasNext()) { - Object setValue = it.next(); - //if (setValue instanceof UUID) { - // Serialize everything - setValue = serialize(setValue, setValue.getClass()); - //} - // Set the value from ? to whatever it is - collStatement.setObject(2, setValue); - // Execute the SQL in the database - collStatement.execute(); - } - } else if (propertyDescriptor.getPropertyType().equals(Map.class) || - propertyDescriptor.getPropertyType().equals(HashMap.class)) { - // Loop through the map - Map collection = (Map)value; - Iterator it = collection.entrySet().iterator(); - while (it.hasNext()) { - Entry en = (Entry) it.next(); - // Get the key and serialize it - Object key = serialize(en.getKey(), en.getKey().getClass()); - // Get the value and serialize it - Object mapValue = serialize(en.getValue(), en.getValue().getClass()); - // Write the objects into prepared statement - collStatement.setObject(2, key); - collStatement.setObject(3, mapValue); - // Write to database - collStatement.execute(); - } - } - // Set value for the main insert. For collections, this is just a dummy value because the real values are in the - // additional table. - value = true; - } - } else { - // If the value is not a collection, it just needs to be serialized to go into the database. - value = serialize(value, propertyDescriptor.getPropertyType()); - } - // Set the value in the main prepared statement and increment the location - // Note that with prepared statements, they count from 1, not 0, so the ++ goes on the front of i. - preparedStatement.setObject(++i, value); - } - // Add the statements to a batch - preparedStatement.addBatch(); - // Execute - preparedStatement.executeBatch(); - } - } - - /** - * Serializes values if required to go into a database. - * TODO: This method will need expanding to include additional Java types - * @param value - * @param clazz - the known class of value - * @return the object to write to the database - */ - private Object serialize(Object value, Class clazz) { - //plugin.getLogger().info("DEBUG: serialize - class is " + clazz.getTypeName()); - if (value == null) { - // If the value is null to start, return null as a string - return "null"; - } - // Types that need to be serialized - // TODO - add others, like Date, Timestamp, etc. - if (clazz.equals(UUID.class)) { - value = value.toString(); - } - else - // Bukkit Types - if (clazz.equals(Location.class)) { - // Serialize - value = Util.getStringLocation(((Location)value)); - } else - if (clazz.equals(World.class)) { - // Serialize - get the name - value = ((World)value).getName(); - } else - if (clazz.getSuperclass() != null && clazz.getSuperclass().equals(Enum.class)) { - //Custom enums are a child of the Enum class. Just get the names of each one. - value = ((Enum)value).name(); - } - if (value == null) { - // The value could become null from the above checks - return "null"; - } - return value; - } - - /** - * Creates a list of s filled with values from the corresponding - * database-table - * - * @return List of s filled with values from the corresponding - * database-table - * - */ - @Override - public List loadObjects() throws SQLException, - SecurityException, IllegalArgumentException, - InstantiationException, IllegalAccessException, - IntrospectionException, InvocationTargetException, ClassNotFoundException { - - try (Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery(createSelectQuery())) { - return createObjects(resultSet); - } - } - - - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#selectObject(java.lang.String) - */ - @Override - public T loadObject(String uniqueId) throws InstantiationException, - IllegalAccessException, IllegalArgumentException, - InvocationTargetException, IntrospectionException, SQLException, SecurityException, ClassNotFoundException { - - // Build the select query - StringBuilder query = new StringBuilder(); - query.append("SELECT "); - query.append(getColumns(false)); - query.append(" FROM `"); - query.append(dataObject.getCanonicalName()); - query.append("` WHERE uniqueId = ? LIMIT 1"); - - try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { - preparedStatement.setString(1, uniqueId); - try (ResultSet resultSet = preparedStatement.executeQuery()) { - // If there is a result, we only want/need the first one - List result = createObjects(resultSet); - if (!result.isEmpty()) { - return result.get(0); - } - } - } - return null; - } - - - /** - * - * Creates a list of s filled with values from the provided ResultSet - * - * @param resultSet - * ResultSet that contains the result of the - * database-select-query - * - * @return List of s filled with values from the provided ResultSet - * - */ - @SuppressWarnings("unchecked") - private List createObjects(ResultSet resultSet) - throws SecurityException, IllegalArgumentException, - SQLException, InstantiationException, - IllegalAccessException, IntrospectionException, - InvocationTargetException, ClassNotFoundException { - + public List loadObjects() { List list = new ArrayList<>(); - // The database can return multiple results in one go, e.g., all the islands in the database - // Run through them one by one - while (resultSet.next()) { - // Create a new instance of this type - T instance = dataObject.newInstance(); - // Get the unique ID from the results - String uniqueId = resultSet.getString("uniqueId"); - if (uniqueId == null) { - throw new SQLException("No unique ID in the results!"); - } - // Use introspection to run through all the fields in this type class - for (Field field : dataObject.getDeclaredFields()) { - /* We assume the table-column-names exactly match the variable-names of T */ - Object value = resultSet.getObject(field.getName()); - // Get the property descriptor of this type - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); - // Get the write method for this field, because we are going to use it to write the value - // once we get the value from the database - Method method = propertyDescriptor.getWriteMethod(); - // If the type is a Collection, then we need to deal with set and map tables - if (Collection.class.isAssignableFrom(propertyDescriptor.getPropertyType()) - || Map.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { - // Collection - // value is just of type boolean right now - StringBuilder setSql = new StringBuilder(); - setSql.append("SELECT "); - // Get the columns, just the names of them, no ?'s or types - setSql.append(getCollectionColumnString(method, false, false)); - setSql.append(" "); - setSql.append("FROM `"); - setSql.append(dataObject.getCanonicalName()); - setSql.append("."); - setSql.append(field.getName()); - setSql.append("` "); - // We will need to fill in the ? later with the unique id of the class from the database - setSql.append("WHERE uniqueId = ?"); - // Prepare the statement - try (PreparedStatement collStatement = connection.prepareStatement(setSql.toString())) { - // Set the unique ID - collStatement.setObject(1, uniqueId); - try (ResultSet collectionResultSet = collStatement.executeQuery()) { - - //plugin.getLogger().info("DEBUG: collectionResultSet = " + collectionResultSet.toString()); - // Do single dimension types (set and list) - if (Set.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { - // Loop through the collection resultset - // Note that we have no idea what type this is - List collectionTypes = Util.getCollectionParameterTypes(method); - // collectionTypes should be only 1 long - Type setType = collectionTypes.get(0); - value = new HashSet<>(); - while (collectionResultSet.next()) { - ((Set) value).add(deserialize(collectionResultSet.getObject(1),Class.forName(setType.getTypeName()))); - } - } else if (List.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { - // Loop through the collection resultset - // Note that we have no idea what type this is - List collectionTypes = Util.getCollectionParameterTypes(method); - // collectionTypes should be only 1 long - Type setType = collectionTypes.get(0); - value = new ArrayList<>(); - //plugin.getLogger().info("DEBUG: collection type argument = " + collectionTypes); - while (collectionResultSet.next()) { - // Add to the list - ((List) value).add(deserialize(collectionResultSet.getObject(1),Class.forName(setType.getTypeName()))); - } - } else if (Map.class.isAssignableFrom(propertyDescriptor.getPropertyType()) || - HashMap.class.isAssignableFrom(propertyDescriptor.getPropertyType())) { - // Loop through the collection resultset - // Note that we have no idea what type this is - List collectionTypes = Util.getCollectionParameterTypes(method); - // collectionTypes should be 2 long - Type keyType = collectionTypes.get(0); - Type valueType = collectionTypes.get(1); - value = new HashMap<>(); - while (collectionResultSet.next()) { - // Work through the columns - // Key - Object key = deserialize(collectionResultSet.getObject(1),Class.forName(keyType.getTypeName())); - Object mapValue = deserialize(collectionResultSet.getObject(2),Class.forName(valueType.getTypeName())); - ((Map) value).put(key,mapValue); - } - } else { - // Set value for the main insert. For collections, this is just a dummy value because the real values are in the - // additional table. - value = true; - } - } - } - } else { - value = deserialize(value, propertyDescriptor.getPropertyType()); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT `json` FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("`"); + try (Statement preparedStatement = connection.createStatement()) { + try (ResultSet resultSet = preparedStatement.executeQuery(sb.toString())) { + // Load all the results + Gson gson = getGSON(); + while (resultSet.next()) { + list.add(gson.fromJson(resultSet.getString("json"), dataObject)); } - // Adapter - // Check if there is an annotation on the field - Adapter adapterNotation = field.getAnnotation(Adapter.class); - if (adapterNotation != null && AdapterInterface.class.isAssignableFrom(adapterNotation.value())) { - // A conversion adapter has been defined - value = ((AdapterInterface)adapterNotation.value().newInstance()).serialize(value); - } - // Write the value to the class - method.invoke(instance, value); } - // Write the result into the list we are going to return - list.add(instance); + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not load objects " + e.getMessage()); } return list; } - /** - * Deserialize any values according to their class - * TODO: expand to include additional types - * @param value - * @param clazz - * @return the deserialized value - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - private Object deserialize(Object value, Class clazz) { - if (value instanceof String && value.equals("null")) { - // If the value is null as a string, return null - return null; - } - // Types that need to be deserialized - if (clazz.equals(UUID.class)) { - value = UUID.fromString((String)value); - } - // Bukkit Types - if (clazz.equals(Location.class)) { - // Get Location from String - may be null... - value = Util.getLocationString(((String)value)); - } - if (clazz.equals(World.class)) { - // Get world by name - may be null... - value = plugin.getServer().getWorld((String)value); - } - // Enums - if (Enum.class.isAssignableFrom(clazz)) { - //Custom enums are a child of the Enum class. - // Find out the value - try { - Class enumClass = (Class)clazz; - value = Enum.valueOf(enumClass, (String)value); - } catch (Exception e) { - plugin.getLogger().severe("Could not deserialize enum! " + e.getMessage()); - } - } - return value; - } - - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#deleteObject(java.lang.Object) - */ @Override - public void deleteObject(T instance) - throws IllegalAccessException, IllegalArgumentException, - InvocationTargetException, IntrospectionException, SQLException, NoSuchMethodException, SecurityException { - // Delete this object from all tables - // Try to connect to the database - // Get the uniqueId. As each class extends DataObject, it must have this method in it. - Method getUniqueId = dataObject.getMethod("getUniqueId"); - String uniqueId = (String) getUniqueId.invoke(instance); - //plugin.getLogger().info("DEBUG: Unique Id = " + uniqueId); - if (uniqueId.isEmpty()) { - throw new SQLException("uniqueId is blank"); - } - // Delete from the main table - // First substitution is the table name - // deleteQuery is created in super from the createInsertQuery() method - try (PreparedStatement preparedStatement = connection.prepareStatement(createDeleteQuery().replace("[table_name]", "`" + dataObject.getCanonicalName() + "`"))) { - // Second is the unique ID + public T loadObject(String uniqueId) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT `json` FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("` WHERE uniqueId = ? LIMIT 1"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { preparedStatement.setString(1, uniqueId); - preparedStatement.addBatch(); - preparedStatement.executeBatch(); - } - - // Delete from any sub tables created from the object - // Run through the fields in the class using introspection - for (Field field : dataObject.getDeclaredFields()) { - // Get the field's property descriptor - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), dataObject); - // Delete Collection tables - if (propertyDescriptor.getPropertyType().equals(Set.class) || - propertyDescriptor.getPropertyType().equals(Map.class) || - propertyDescriptor.getPropertyType().equals(HashMap.class) || - propertyDescriptor.getPropertyType().equals(ArrayList.class)) { - // First substitution is the table name - try (PreparedStatement preparedStatement2 = connection.prepareStatement(createDeleteQuery().replace("[table_name]", "`" + dataObject.getCanonicalName() + "." + field.getName() + "`"))) { - // Second is the unique ID - preparedStatement2.setString(1, uniqueId); - preparedStatement2.addBatch(); - // Execute - preparedStatement2.executeBatch(); + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + // If there is a result, we only want/need the first one + Gson gson = getGSON(); + return gson.fromJson(resultSet.getString("json"), dataObject); } } + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not load object " + uniqueId + " " + e.getMessage()); } - } + return null; + } + @Override + public void saveObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.getLogger().severe(() -> "This class is not a DataObject: " + instance.getClass().getName()); + return; + } + StringBuilder sb = new StringBuilder(); + // Replace into is used so that any data in the table will be replaced with updated data + sb.append("INSERT INTO "); + sb.append("`"); + // The table name is the canonical name, so that add-ons can be sure of a unique table in the database + sb.append(dataObject.getCanonicalName()); + sb.append("` (json) VALUES (?) ON DUPLICATE KEY UPDATE json = ?"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { + Gson gson = getGSON(); + String toStore = gson.toJson(instance); + preparedStatement.setString(1, toStore); + preparedStatement.setString(2, toStore); + preparedStatement.execute(); + } catch (SQLException e) { + plugin.getLogger().severe(() -> "Could not save object " + instance.getClass().getName() + " " + e.getMessage()); + // Leave in for now until we know there are no particular issues + e.printStackTrace(); + } + } + + @Override + public void deleteObject(T instance) { + if (!(instance instanceof DataObject)) { + plugin.getLogger().severe(() -> "This class is not a DataObject: " + instance.getClass().getName()); + return; + } + StringBuilder sb = new StringBuilder(); + sb.append("DELETE FROM `"); + sb.append(dataObject.getCanonicalName()); + sb.append("` WHERE uniqueId = ?"); + try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { + Method getUniqueId = dataObject.getMethod("getUniqueId"); + String uniqueId = (String) getUniqueId.invoke(instance); + preparedStatement.setString(1, uniqueId); + preparedStatement.execute(); + } catch (Exception e) { + plugin.getLogger().severe(() -> "Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); + } + } /* (non-Javadoc) * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) @@ -785,7 +186,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { public boolean objectExists(String key) { // Create the query to see if this key exists StringBuilder query = new StringBuilder(); - query.append("SELECT IF ( EXISTS( SELECT * FROM `"); + query.append("SELECT IF ( EXISTS( SELECT * FROM `"); query.append(dataObject.getCanonicalName()); query.append("` WHERE `uniqueId` = ?), 1, 0)"); @@ -802,18 +203,4 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { return false; } - @Override - public void saveSettings(T instance) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException { - // This method should not be used because configs are not stored in MySQL - - } - - @Override - public T loadSettings(String uniqueId, T dbConfig) throws InstantiationException, IllegalAccessException, - IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IntrospectionException { - // This method should not be used because configs are not stored in MySQL - return null; - } - } diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java new file mode 100644 index 000000000..4e2f0889b --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/FlagAdapter.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.flags.Flag; + +public class FlagAdapter extends TypeAdapter { + + private BSkyBlock plugin; + + public FlagAdapter(BSkyBlock plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, Flag value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getID()); + + } + + @Override + public Flag read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return plugin.getFlagsManager().getFlagByID(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java new file mode 100644 index 000000000..cab2dbdd8 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/LocationAdapter.java @@ -0,0 +1,54 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class LocationAdapter extends TypeAdapter { + + private Plugin plugin; + + public LocationAdapter(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, Location location) throws IOException { + if (location == null) { + out.nullValue(); + return; + } + out.beginArray(); + out.value(location.getWorld().getName()); + out.value(location.getX()); + out.value(location.getY()); + out.value(location.getZ()); + out.value(location.getYaw()); + out.value(location.getPitch()); + out.endArray(); + } + + @Override + public Location read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + in.beginArray(); + World world = plugin.getServer().getWorld(in.nextString()); + double x = in.nextDouble(); + double y = in.nextDouble(); + double z = in.nextDouble(); + float yaw = (float)in.nextDouble(); + float pitch = (float)in.nextDouble(); + in.endArray(); + return new Location(world, x, y, z, yaw, pitch); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java new file mode 100644 index 000000000..f9ee60c16 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/WorldAdapter.java @@ -0,0 +1,39 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.World; +import org.bukkit.plugin.Plugin; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class WorldAdapter extends TypeAdapter { + + private Plugin plugin; + + public WorldAdapter(Plugin plugin) { + this.plugin = plugin; + } + + @Override + public void write(JsonWriter out, World value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getName()); + + } + + @Override + public World read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return plugin.getServer().getWorld(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java deleted file mode 100644 index 1d226d910..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/mysqljson/LocationAdapter.java +++ /dev/null @@ -1,88 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import java.lang.reflect.Type; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -public class LocationAdapter implements JsonDeserializer, JsonSerializer { - - public static final LocationAdapter INSTANCE = new LocationAdapter(); - - @Override - public Location deserialize( JsonElement json, Type type, JsonDeserializationContext jsonDeserializationContext ) throws JsonParseException { - - if ( !json.isJsonObject() ) { - throw new JsonParseException( "not a JSON object" ); - } - - final JsonObject obj = (JsonObject) json; - final JsonElement world = obj.get( "world" ); - final JsonElement x = obj.get( "x" ); - final JsonElement y = obj.get( "y" ); - final JsonElement z = obj.get( "z" ); - final JsonElement yaw = obj.get( "yaw" ); - final JsonElement pitch = obj.get( "pitch" ); - - if ( world == null || x == null || y == null || z == null || yaw == null || pitch == null ) { - throw new JsonParseException( "Malformed location json string!" ); - } - - if ( !world.isJsonPrimitive() || !((JsonPrimitive) world).isString() ) { - throw new JsonParseException( "world is not a string" ); - } - - if ( !x.isJsonPrimitive() || !((JsonPrimitive) x).isNumber() ) { - throw new JsonParseException( "x is not a number" ); - } - - if ( !y.isJsonPrimitive() || !((JsonPrimitive) y).isNumber() ) { - throw new JsonParseException( "y is not a number" ); - } - - if ( !z.isJsonPrimitive() || !((JsonPrimitive) z).isNumber() ) { - throw new JsonParseException( "z is not a number" ); - } - - if ( !yaw.isJsonPrimitive() || !((JsonPrimitive) yaw).isNumber() ) { - throw new JsonParseException( "yaw is not a number" ); - } - - if ( !pitch.isJsonPrimitive() || !((JsonPrimitive) pitch).isNumber() ) { - throw new JsonParseException( "pitch is not a number" ); - } - - World worldInstance = Bukkit.getWorld( world.getAsString() ); - if (worldInstance == null) { - throw new IllegalArgumentException("Unknown/not loaded world"); - } - - return new Location( worldInstance, x.getAsDouble(), y.getAsDouble(), z.getAsDouble(), yaw.getAsFloat(), pitch.getAsFloat() ); - - } - - @Override - public JsonElement serialize( Location location, Type type, JsonSerializationContext jsonSerializationContext ) { - - final JsonObject obj = new JsonObject(); - obj.addProperty( "world", location.getWorld().getName() ); - obj.addProperty( "x", location.getX() ); - obj.addProperty( "y", location.getY() ); - obj.addProperty( "z", location.getZ() ); - obj.addProperty( "yaw", location.getYaw() ); - obj.addProperty( "pitch", location.getPitch() ); - return obj; - - } - -} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java deleted file mode 100644 index 5977b30bd..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseConnecterJ.java +++ /dev/null @@ -1,75 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.util.Map; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.file.YamlConfiguration; - -import us.tastybento.bskyblock.database.DatabaseConnecter; -import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; - -public class MySQLDatabaseConnecterJ implements DatabaseConnecter { - - private String connectionUrl; - private DatabaseConnectionSettingsImpl dbSettings; - private Connection connection = null; - - /** - * Class for MySQL database connections using the settings provided - * @param dbSettings - */ - public MySQLDatabaseConnecterJ(DatabaseConnectionSettingsImpl dbSettings) { - this.dbSettings = dbSettings; - try { - Class.forName("com.mysql.jdbc.Driver").newInstance(); - } catch (Exception e) { - Bukkit.getLogger().severe("Could not instantiate JDBC driver! " + e.getMessage()); - } - // jdbc:mysql://localhost:3306/Peoples?autoReconnect=true&useSSL=false - connectionUrl = "jdbc:mysql://" + dbSettings.getHost() + "/" + dbSettings.getDatabaseName() + "?autoReconnect=true&useSSL=false&allowMultiQueries=true"; - } - - @Override - public Connection createConnection() { - try { - connection = DriverManager.getConnection(connectionUrl, dbSettings.getUsername(), dbSettings.getPassword()); - } catch (SQLException e) { - Bukkit.getLogger().severe("Could not connect to the database! " + e.getMessage()); - } - return connection; - } - - @Override - public String getConnectionUrl() { - return connectionUrl; - } - - @Override - public String getUniqueId(String tableName) { - // Not used - return ""; - } - - @Override - public YamlConfiguration loadYamlFile(String string, String key) { - // Not used - return null; - } - - @Override - public boolean uniqueIdExists(String tableName, String key) { - // Not used - return false; - } - - @Override - public void saveYamlFile(YamlConfiguration yamlConfig, String tableName, String fileName, - Map commentMap) { - // Not used - - } - -} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java deleted file mode 100644 index f9d5156c0..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJ.java +++ /dev/null @@ -1,191 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.plugin.Plugin; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import us.tastybento.bskyblock.database.DatabaseConnecter; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; - -/** - * - * Class that inserts a into the corresponding database-table. - * - * @author tastybento - * - * @param - */ -public class MySQLDatabaseHandlerJ extends AbstractDatabaseHandler { - - /** - * Connection to the database - */ - private Connection connection = null; - - /** - * Handles the connection to the database and creation of the initial database schema (tables) for - * the class that will be stored. - * @param plugin - BSkyBlock plugin object - * @param type - the type of class to be stored in the database. Must inherit DataObject - * @param databaseConnecter - authentication details for the database - */ - public MySQLDatabaseHandlerJ(Plugin plugin, Class type, DatabaseConnecter databaseConnecter) { - super(plugin, type, databaseConnecter); - connection = databaseConnecter.createConnection(); - // Check if the table exists in the database and if not, create it - createSchema(); - } - - /** - * Creates the table in the database if it doesn't exist already - */ - private void createSchema() { - StringBuilder sql = new StringBuilder(); - sql.append("CREATE TABLE IF NOT EXISTS `"); - sql.append(dataObject.getCanonicalName()); - sql.append("` (json JSON, uniqueId VARCHAR(255) GENERATED ALWAYS AS (json->\"$.uniqueId\"), INDEX i (uniqueId) );"); - // Prepare and execute the database statements - try (PreparedStatement pstmt = connection.prepareStatement(sql.toString())) { - pstmt.executeUpdate(); - } catch (SQLException e) { - plugin.getLogger().severe(() -> "Problem trying to create schema for data object " + dataObject.getCanonicalName() + " " + e.getMessage()); - } - } - - @Override - public List loadObjects() { - List list = new ArrayList<>(); - StringBuilder sb = new StringBuilder(); - sb.append("SELECT `json` FROM `"); - sb.append(dataObject.getCanonicalName()); - sb.append("`"); - try (Statement preparedStatement = connection.createStatement()) { - try (ResultSet resultSet = preparedStatement.executeQuery(sb.toString())) { - // Load all the results - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; - Gson gson = builder.create(); - while (resultSet.next()) { - list.add(gson.fromJson(resultSet.getString("json"), dataObject)); - } - } - } catch (SQLException e) { - plugin.getLogger().severe(() -> "Could not load objects " + e.getMessage()); - } - return list; - } - - - @Override - public T loadObject(String uniqueId) { - StringBuilder sb = new StringBuilder(); - sb.append("SELECT `json` FROM `"); - sb.append(dataObject.getCanonicalName()); - sb.append("` WHERE uniqueId = ? LIMIT 1"); - try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { - preparedStatement.setString(1, uniqueId); - try (ResultSet resultSet = preparedStatement.executeQuery()) { - while (resultSet.next()) { - // If there is a result, we only want/need the first one - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; - Gson gson = builder.create(); - return gson.fromJson(resultSet.getString("json"), dataObject); - } - } - } catch (SQLException e) { - plugin.getLogger().severe(() -> "Could not load object " + uniqueId + " " + e.getMessage()); - } - return null; - } - - @Override - public void saveObject(T instance) { - Bukkit.getLogger().severe(() -> "Saving object " + instance.getClass().getName()); - StringBuilder sb = new StringBuilder(); - // Replace into is used so that any data in the table will be replaced with updated data - sb.append("REPLACE INTO "); - sb.append("`"); - // The table name is the canonical name, so that add-ons can be sure of a unique table in the database - sb.append(dataObject.getCanonicalName()); - sb.append("` (json) VALUES (?)"); - try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { - GsonBuilder builder = new GsonBuilder(); - builder.registerTypeAdapter(Location.class, new LocationAdapter()) ; - Gson gson = builder.create(); - preparedStatement.setString(1, gson.toJson(instance)); - } catch (SQLException e) { - plugin.getLogger().severe(() -> "Could not save object " + instance.getClass().getName() + " " + e.getMessage()); - } - } - - @Override - public void deleteObject(T instance) { - StringBuilder sb = new StringBuilder(); - sb.append("DELETE FROM `"); - sb.append(dataObject.getCanonicalName()); - sb.append("` WHERE uniqueId = ?"); - try (PreparedStatement preparedStatement = connection.prepareStatement(sb.toString())) { - Method getUniqueId = dataObject.getMethod("getUniqueId"); - String uniqueId = (String) getUniqueId.invoke(instance); - preparedStatement.setString(1, uniqueId); - preparedStatement.executeQuery(); - } catch (Exception e) { - plugin.getLogger().severe(() -> "Could not delete object " + instance.getClass().getName() + " " + e.getMessage()); - } - } - - /* (non-Javadoc) - * @see us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler#objectExists(java.lang.String) - */ - @Override - public boolean objectExists(String key) { - // Create the query to see if this key exists - StringBuilder query = new StringBuilder(); - query.append("SELECT IF ( EXISTS( SELECT * FROM `"); - query.append(dataObject.getCanonicalName()); - query.append("` WHERE `uniqueId` = ?), 1, 0)"); - - try (PreparedStatement preparedStatement = connection.prepareStatement(query.toString())) { - preparedStatement.setString(1, key); - try (ResultSet resultSet = preparedStatement.executeQuery()) { - if (resultSet.next()) { - return resultSet.getBoolean(1); - } - } - } catch (SQLException e) { - plugin.getLogger().severe("Could not check if key exists in database! " + key + " " + e.getMessage()); - } - return false; - } - - @Override - public void saveSettings(T instance) - throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, IntrospectionException { - // This method should not be used because configs are not stored in MySQL - - } - - @Override - public T loadSettings(String uniqueId, T dbConfig) { - // This method should not be used because configs are not stored in MySQL - return null; - } - - - -} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java b/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java deleted file mode 100755 index 8fa9a17a4..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseJ.java +++ /dev/null @@ -1,22 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.database.BSBDatabase; -import us.tastybento.bskyblock.database.DatabaseConnectionSettingsImpl; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; - -public class MySQLDatabaseJ extends BSBDatabase{ - - @Override - public AbstractDatabaseHandler getHandler(Class type) { - BSkyBlock plugin = BSkyBlock.getInstance(); - return new MySQLDatabaseHandlerJ<>(plugin, type, new MySQLDatabaseConnecterJ(new DatabaseConnectionSettingsImpl( - plugin.getSettings().getDbHost(), - plugin.getSettings().getDbPort(), - plugin.getSettings().getDbName(), - plugin.getSettings().getDbUsername(), - plugin.getSettings().getDbPassword() - ))); - } - -} diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Island.java b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java index 2f1320f61..79a9518e5 100755 --- a/src/main/java/us/tastybento/bskyblock/database/objects/Island.java +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Island.java @@ -7,24 +7,18 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.OfflinePlayer; import org.bukkit.World; import org.bukkit.block.BlockState; import org.bukkit.entity.Entity; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.gson.annotations.Expose; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.commands.User; -import us.tastybento.bskyblock.api.events.IslandBaseEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent.IslandLockEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent.IslandUnlockEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; import us.tastybento.bskyblock.api.flags.Flag; import us.tastybento.bskyblock.database.objects.adapters.Adapter; import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer; @@ -42,57 +36,71 @@ import us.tastybento.bskyblock.util.Util; */ public class Island implements DataObject { - private String uniqueId = ""; + @Expose + private String uniqueId = UUID.randomUUID().toString(); //// Island //// // The center of the island itself + @Expose private Location center; // Island range + @Expose private int range; // Coordinates of the island area + @Expose private int minX; - + @Expose private int minZ; // Coordinates of minimum protected area + @Expose private int minProtectedX; - + @Expose private int minProtectedZ; // Protection size + @Expose private int protectionRange; - // World the island is in + // World the island started in. This may be different from the island location + @Expose private World world; // Display name - private String name; + @Expose + private String name = ""; // Time parameters + @Expose private long createdDate; - + @Expose private long updatedDate; //// Team //// + @Expose private UUID owner; + @Expose private Map members = new HashMap<>(); //// State //// + @Expose private boolean locked = false; - + @Expose private boolean spawn = false; - + @Expose private boolean purgeProtected = false; //// Protection flags //// @Adapter(FlagSerializer.class) + @Expose private Map flags = new HashMap<>(); + @Expose private int levelHandicap; - + @Expose private Location spawnPoint; public Island() {} @@ -126,7 +134,6 @@ public class Island implements DataObject { * @return true if successfully added */ public boolean addToBanList(UUID targetUUID) { - // TODO fire ban event if (targetUUID != null) { members.put(targetUUID, RanksManager.BANNED_RANK); } @@ -206,7 +213,7 @@ public class Island implements DataObject { */ public ImmutableSet getMemberSet(){ Builder result = new ImmutableSet.Builder<>(); - + for (Entry member: members.entrySet()) { if (member.getValue() >= RanksManager.MEMBER_RANK) { result.add(member.getKey()); @@ -247,15 +254,7 @@ public class Island implements DataObject { * @return the island display name or the owner's name if none is set */ public String getName() { - if (name != null) { - return name; - } - if (owner != null) { - OfflinePlayer player = Bukkit.getServer().getOfflinePlayer(owner); - name = player.getName(); - return player.getName(); - } - return ""; + return name; } /** @@ -292,7 +291,6 @@ public class Island implements DataObject { * @return rank integer */ public int getRank(User user) { - //Bukkit.getLogger().info("DEBUG: user UUID = " + user.getUniqueId()); return members.getOrDefault(user.getUniqueId(), RanksManager.VISITOR_RANK); } @@ -353,10 +351,6 @@ public class Island implements DataObject { @Override public String getUniqueId() { - // Island's have UUID's that are randomly assigned if they do not exist - if (uniqueId.isEmpty()) { - uniqueId = UUID.randomUUID().toString(); - } return uniqueId; } @@ -402,8 +396,7 @@ public class Island implements DataObject { * @return true if in the island space */ public boolean inIslandSpace(int x, int z) { - //Bukkit.getLogger().info("DEBUG: center - " + center); - return x >= minX && x < minX + range*2 && z >= minZ && z < minZ + range*2; + return x >= minX && x < minX + range*2 && z >= minZ && z < minZ + range*2; } public boolean inIslandSpace(Location location) { @@ -439,7 +432,6 @@ public class Island implements DataObject { * @return true if allowed, false if not */ public boolean isAllowed(User user, Flag flag) { - //Bukkit.getLogger().info("DEBUG: " + flag.getID() + " user score = " + getRank(user) + " flag req = "+ this.getFlagReq(flag)); return getRank(user) >= getFlag(flag); } @@ -487,7 +479,6 @@ public class Island implements DataObject { * @return true if successful, otherwise false. */ public boolean removeFromBanList(UUID targetUUID) { - // TODO fire unban event members.remove(targetUUID); return true; } @@ -543,24 +534,11 @@ public class Island implements DataObject { } /** - * Locks/Unlocks the island. May be cancelled by - * {@link IslandLockEvent} or {@link IslandUnlockEvent}. + * Locks/Unlocks the island. * @param locked - the lock state to set */ public void setLocked(boolean locked){ - if(locked){ - // Lock the island - IslandBaseEvent event = IslandEvent.builder().island(this).reason(Reason.LOCK).build(); - if(!event.isCancelled()){ - this.locked = locked; - } - } else { - // Unlock the island - IslandBaseEvent event = IslandEvent.builder().island(this).reason(Reason.UNLOCK).build(); - if(!event.isCancelled()){ - this.locked = locked; - } - } + this.locked = locked; } /** diff --git a/src/main/java/us/tastybento/bskyblock/database/objects/Players.java b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java index fa8028375..69c9253b2 100755 --- a/src/main/java/us/tastybento/bskyblock/database/objects/Players.java +++ b/src/main/java/us/tastybento/bskyblock/database/objects/Players.java @@ -10,6 +10,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; +import com.google.gson.annotations.Expose; + import us.tastybento.bskyblock.BSkyBlock; /** @@ -18,12 +20,19 @@ import us.tastybento.bskyblock.BSkyBlock; * @author tastybento */ public class Players implements DataObject { + @Expose private Map homeLocations = new HashMap<>(); + @Expose private String uniqueId; + @Expose private String playerName; + @Expose private int resetsLeft; + @Expose private String locale = ""; + @Expose private int deaths; + @Expose private Map kickedList = new HashMap<>(); /** @@ -64,15 +73,6 @@ public class Players implements DataObject { * @return Location of this home or null if not available */ public Location getHomeLocation(int number) { - /* - Bukkit.getLogger().info("DEBUG: getting home location " + number); - - Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); - for (Entry en : homeLocations.entrySet()) { - Bukkit.getLogger().info("DEBUG: " + en.getKey() + " ==> " + en.getValue()); - if (number == en.getKey()) - Bukkit.getLogger().info("DEBUG: key = number"); - }*/ return homeLocations.get(number); } @@ -101,7 +101,6 @@ public class Players implements DataObject { * @param homeLocations the homeLocations to set */ public void setHomeLocations(Map homeLocations) { - //Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); this.homeLocations = homeLocations; } @@ -166,7 +165,7 @@ public class Players implements DataObject { * Set the uuid for this player object * @param uuid - UUID */ - public void setPlayerUUID(final UUID uuid) { + public void setPlayerUUID(UUID uuid) { uniqueId = uuid.toString(); } diff --git a/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java index 566523a22..31cced427 100644 --- a/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java +++ b/src/main/java/us/tastybento/bskyblock/listeners/JoinLeaveListener.java @@ -11,8 +11,8 @@ import org.bukkit.event.player.PlayerQuitEvent; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Constants; import us.tastybento.bskyblock.api.commands.User; -import us.tastybento.bskyblock.database.managers.PlayersManager; import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.PlayersManager; public class JoinLeaveListener implements Listener { diff --git a/src/main/java/us/tastybento/bskyblock/listeners/flags/AbstractFlagListener.java b/src/main/java/us/tastybento/bskyblock/listeners/flags/AbstractFlagListener.java index ca06e4dc9..db430e68b 100644 --- a/src/main/java/us/tastybento/bskyblock/listeners/flags/AbstractFlagListener.java +++ b/src/main/java/us/tastybento/bskyblock/listeners/flags/AbstractFlagListener.java @@ -18,8 +18,8 @@ import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.api.commands.User; import us.tastybento.bskyblock.api.flags.Flag; import us.tastybento.bskyblock.api.flags.Flag.Type; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.IslandsManager; /** * Abstract class for flag listeners. Provides common code. diff --git a/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java b/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java index 11138b8c5..ae0ead79c 100644 --- a/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/FlagsManager.java @@ -4,7 +4,6 @@ import java.util.ArrayList; import java.util.List; import org.bukkit.Bukkit; -import org.bukkit.Material; import org.bukkit.event.Listener; import us.tastybento.bskyblock.BSkyBlock; diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/island/IslandsManager.java b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java similarity index 99% rename from src/main/java/us/tastybento/bskyblock/database/managers/island/IslandsManager.java rename to src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java index cc3108d12..de71f93cb 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/island/IslandsManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/IslandsManager.java @@ -1,4 +1,4 @@ -package us.tastybento.bskyblock.database.managers.island; +package us.tastybento.bskyblock.managers; import java.util.Collection; import java.util.HashSet; @@ -23,9 +23,10 @@ import org.bukkit.util.Vector; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Constants; import us.tastybento.bskyblock.api.commands.User; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.BSBDatabase; -import us.tastybento.bskyblock.database.managers.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.managers.island.IslandCache; import us.tastybento.bskyblock.util.DeleteIslandChunks; import us.tastybento.bskyblock.util.Util; import us.tastybento.bskyblock.util.teleport.SafeTeleportBuilder; @@ -744,6 +745,7 @@ public class IslandsManager { } } catch (Exception e) { plugin.getLogger().severe("Could not load islands to cache! " + e.getMessage()); + e.printStackTrace(); } if (DEBUG) { plugin.getLogger().info("DEBUG: islands loaded"); diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java b/src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java similarity index 99% rename from src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java rename to src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java index a8b2eafb0..7f06d54e8 100644 --- a/src/main/java/us/tastybento/bskyblock/database/managers/PlayersManager.java +++ b/src/main/java/us/tastybento/bskyblock/managers/PlayersManager.java @@ -1,4 +1,4 @@ -package us.tastybento.bskyblock.database.managers; +package us.tastybento.bskyblock.managers; import java.beans.IntrospectionException; import java.lang.reflect.InvocationTargetException; @@ -19,13 +19,14 @@ import org.bukkit.entity.Player; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Constants; import us.tastybento.bskyblock.api.commands.User; +import us.tastybento.bskyblock.database.AbstractDatabaseHandler; import us.tastybento.bskyblock.database.BSBDatabase; import us.tastybento.bskyblock.database.objects.Island; import us.tastybento.bskyblock.database.objects.Players; public class PlayersManager{ - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private BSkyBlock plugin; private BSBDatabase database; private AbstractDatabaseHandler handler; diff --git a/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java b/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java new file mode 100644 index 000000000..78910a191 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/island/IslandCache.java @@ -0,0 +1,392 @@ +package us.tastybento.bskyblock.managers.island; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.UUID; + +import org.bukkit.ChatColor; +import org.bukkit.Location; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.util.Util; + +public class IslandCache { + private static final boolean DEBUG2 = false; + private static final boolean DEBUG = false; + private BSkyBlock plugin = BSkyBlock.getInstance(); + private BiMap islandsByLocation; + /** + * Every player who is associated with an island is in this map. + */ + private HashMap islandsByUUID; + // 2D islandGrid of islands, x,z + private TreeMap> islandGrid = new TreeMap<>(); + + public IslandCache() { + islandsByLocation = HashBiMap.create(); + islandsByUUID = new HashMap<>(); + } + + /** + * Adds an island to the grid + * @param island + */ + public void addIsland(Island island) { + islandsByLocation.put(island.getCenter(), island); + if (DEBUG) { + plugin.getLogger().info("DEBUG: owner = " + island.getOwner()); + } + islandsByUUID.put(island.getOwner(), island); + if (DEBUG) { + plugin.getLogger().info("DEBUG: island has " + island.getMemberSet().size() + " members"); + } + for (UUID member: island.getMemberSet()) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: " + member); + } + islandsByUUID.put(member, island); + } + addToGrid(island); + } + + public void addPlayer(UUID playerUUID, Island teamIsland) { + islandsByUUID.put(playerUUID, teamIsland); + } + + /** + * Adds an island to the grid register + * @param newIsland + */ + private void addToGrid(Island newIsland) { + if (islandGrid.containsKey(newIsland.getMinX())) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: min x is in the grid :" + newIsland.getMinX()); + } + TreeMap zEntry = islandGrid.get(newIsland.getMinX()); + if (zEntry.containsKey(newIsland.getMinZ())) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: min z is in the grid :" + newIsland.getMinZ()); + } + // Island already exists + Island conflict = islandGrid.get(newIsland.getMinX()).get(newIsland.getMinZ()); + plugin.getLogger().warning("*** Duplicate or overlapping islands! ***"); + plugin.getLogger().warning( + "Island at (" + newIsland.getCenter().getBlockX() + ", " + newIsland.getCenter().getBlockZ() + ") conflicts with (" + + conflict.getCenter().getBlockX() + ", " + conflict.getCenter().getBlockZ() + ")"); + if (conflict.getOwner() != null) { + plugin.getLogger().warning("Accepted island is owned by " + plugin.getPlayers().getName(conflict.getOwner())); + plugin.getLogger().warning(conflict.getOwner().toString() + ".yml"); + } else { + plugin.getLogger().warning("Accepted island is unowned."); + } + if (newIsland.getOwner() != null) { + plugin.getLogger().warning("Denied island is owned by " + plugin.getPlayers().getName(newIsland.getOwner())); + plugin.getLogger().warning(newIsland.getOwner().toString() + ".yml"); + } else { + plugin.getLogger().warning("Denied island is unowned and was just found in the islands folder. Skipping it..."); + } + plugin.getLogger().warning("Recommend that the denied player file is deleted otherwise weird things can happen."); + return; + } else { + // Add island + if (DEBUG) { + plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ()); + } + zEntry.put(newIsland.getMinZ(), newIsland); + islandGrid.put(newIsland.getMinX(), zEntry); + // plugin.getLogger().info("Debug: " + newIsland.toString()); + } + } else { + // Add island + if (DEBUG) { + plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ()); + } + TreeMap zEntry = new TreeMap<>(); + zEntry.put(newIsland.getMinZ(), newIsland); + islandGrid.put(newIsland.getMinX(), zEntry); + } + } + + public void clear() { + islandsByLocation.clear(); + islandsByUUID.clear(); + } + + public Island createIsland(Island island) { + islandsByLocation.put(island.getCenter(), island); + if (island.getOwner() != null) { + islandsByUUID.put(island.getOwner(), island); + } + addToGrid(island); + return island; + } + + /** + * Create an island with no owner at location + * @param location - the location + */ + public Island createIsland(Location location){ + return createIsland(location, null); + } + + /** + * Create an island with owner. Note this does not create the schematic. It just creates the island data object. + * @param location - the location + * @param owner - the island owner UUID + */ + public Island createIsland(Location location, UUID owner){ + if (DEBUG) { + plugin.getLogger().info("DEBUG: adding island for " + owner + " at " + location); + } + Island island = new Island(location, owner, plugin.getSettings().getIslandProtectionRange()); + islandsByLocation.put(location, island); + if (owner != null) { + islandsByUUID.put(owner, island); + } + addToGrid(island); + return island; + } + + /** + * Deletes an island from the database. Does not remove blocks + * @param island + */ + public void deleteIslandFromCache(Island island) { + if (!islandsByLocation.remove(island.getCenter(), island)) { + plugin.getLogger().severe("Could not remove island from cache!"); + } + Iterator> it = islandsByUUID.entrySet().iterator(); + while (it.hasNext()) { + Entry en = it.next(); + if (en.getValue().equals(island)) { + it.remove(); + } + } + // Remove from grid + if (DEBUG) { + plugin.getLogger().info("DEBUG: deleting island at " + island.getCenter()); + } + if (island != null) { + int x = island.getMinX(); + int z = island.getMinZ(); + if (DEBUG) { + plugin.getLogger().info("DEBUG: x = " + x + " z = " + z); + } + if (islandGrid.containsKey(x)) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: x found"); + } + TreeMap zEntry = islandGrid.get(x); + if (zEntry.containsKey(z)) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: z found - deleting the island"); + } + // Island exists - delete it + zEntry.remove(z); + islandGrid.put(x, zEntry); + } else { + if (DEBUG) { + plugin.getLogger().info("DEBUG: could not find z"); + } + } + } + } + } + + public Island get(Location location) { + return islandsByLocation.get(location); + } + + public Island get(UUID uuid) { + return islandsByUUID.get(uuid); + } + + /** + * Gets the island for this player. If they are in a team, the team island is returned + * @param uuid - UUID + * @return Island + */ + public Island getIsland(UUID uuid){ + return islandsByUUID.get(uuid); + } + + /** + * Returns the island at the x,z location or null if there is none. + * This includes the full island space, not just the protected area. + * + * @param x - x coordinate + * @param z - z coordinate + * @return Island or null + */ + public Island getIslandAt(int x, int z) { + if (DEBUG2) { + plugin.getLogger().info("DEBUG: getting island at " + x + "," + z); + plugin.getLogger().info("DEBUG: island grid is " + islandGrid.size()); + } + Entry> en = islandGrid.floorEntry(x); + if (en != null) { + Entry ent = en.getValue().floorEntry(z); + if (ent != null) { + // Check if in the island range + Island island = ent.getValue(); + if (island.inIslandSpace(x, z)) { + if (DEBUG2) { + plugin.getLogger().info("DEBUG: In island space"); + } + return island; + } + if (DEBUG2) { + plugin.getLogger().info("DEBUG: not in island space"); + } + } + } + return null; + } + + /** + * Returns the island at the location or null if there is none. + * This includes the full island space, not just the protected area + * + * @param location - the location + * @return Island object + */ + public Island getIslandAt(Location location) { + if (location == null) { + //plugin.getLogger().info("DEBUG: location is null"); + return null; + } + // World check + if (!Util.inWorld(location)) { + //plugin.getLogger().info("DEBUG: not in right world"); + return null; + } + return getIslandAt(location.getBlockX(), location.getBlockZ()); + } + + /** + * Returns the player's island location. + * Returns an island location OR a team island location + * + * @param playerUUID - the player's UUID + * @return Location of player's island or null if one does not exist + */ + public Location getIslandLocation(UUID playerUUID) { + if (hasIsland(playerUUID)) { + return getIsland(playerUUID).getCenter(); + } + return null; + } + + /** + * Get name of the island owned by owner + * @param owner - the island owner + * @return Returns the name of owner's island, or the owner's name if there is none. + */ + public String getIslandName(UUID owner) { + String result = plugin.getPlayers().getName(owner); + if (islandsByUUID.containsKey(owner)) { + Island island = islandsByUUID.get(owner); + if (island.getName() != null && !island.getName().isEmpty()) { + result = island.getName(); + } + } + return ChatColor.translateAlternateColorCodes('&', result) + ChatColor.RESET; + } + + public Collection getIslands() { + return Collections.unmodifiableCollection(islandsByLocation.values()); + } + + public Set getMembers(UUID playerUUID) { + Island island = islandsByUUID.get(playerUUID); + if (island != null) { + return island.getMemberSet(); + } + return new HashSet<>(0); + } + + public UUID getTeamLeader(UUID playerUUID) { + if (islandsByUUID.containsKey(playerUUID)) { + return islandsByUUID.get(playerUUID).getOwner(); + } + return null; + } + + /** + * @param playerUUID - the player's UUID + * @return true if player has island and owns it + */ + public boolean hasIsland(UUID playerUUID) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: checking if " + playerUUID + " has an island"); + plugin.getLogger().info("DEBUG: islandsByUUID : " + islandsByUUID.toString()); + + if (!islandsByUUID.containsKey(playerUUID)) { + plugin.getLogger().info("DEBUG: player is not in islandsByUUID"); + } else { + plugin.getLogger().info("DEBUG: owner = " + islandsByUUID.get(playerUUID).getOwner()); + } + } + if (islandsByUUID.containsKey(playerUUID) && islandsByUUID.get(playerUUID).getOwner() != null) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: checking for equals"); + } + if (islandsByUUID.get(playerUUID).getOwner().equals(playerUUID)) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: has island"); + } + return true; + } + } + if (DEBUG) { + plugin.getLogger().info("DEBUG: doesn't have island"); + } + return false; + } + + public void removePlayer(UUID playerUUID) { + Island island = islandsByUUID.get(playerUUID); + if (island != null) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: island found"); + } + if (island.getOwner() != null && island.getOwner().equals(playerUUID)) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: player is the owner of this island"); + } + // Clear ownership and members + island.getMembers().clear(); + island.setOwner(null); + } + island.removeMember(playerUUID); + } + if (DEBUG) { + plugin.getLogger().info("DEBUG: removing reference to island by UUID"); + } + islandsByUUID.remove(playerUUID); + + } + + public void setIslandName(UUID owner, String name) { + if (islandsByUUID.containsKey(owner)) { + Island island = islandsByUUID.get(owner); + island.setName(name); + } + + } + + public int size() { + return islandsByLocation.size(); + } + +} diff --git a/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java b/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java new file mode 100644 index 000000000..c14349b82 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/managers/island/NewIsland.java @@ -0,0 +1,252 @@ +package us.tastybento.bskyblock.managers.island; + +import java.io.IOException; +import java.util.UUID; + +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.api.events.IslandBaseEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent; +import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.island.builders.IslandBuilder; +import us.tastybento.bskyblock.island.builders.IslandBuilder.IslandType; + +/** + * Create and paste a new island + * @author tastybento + * + */ +public class NewIsland { + private static final boolean DEBUG = false; + private BSkyBlock plugin; + private Island island; + private final Player player; + private final Reason reason; + + private NewIsland(Island oldIsland, Player player, Reason reason) { + super(); + plugin = BSkyBlock.getInstance(); + this.player = player; + this.reason = reason; + newIsland(); + if (oldIsland != null) { + // Delete the old island + plugin.getIslands().deleteIsland(oldIsland, true); + } + } + + /** + * @return the island that was created + */ + public Island getIsland() { + return island; + } + + /** + * Start building a new island + * @return New island builder object + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Build a new island for a player using a schematic + * @author tastybento + * + */ + public static class Builder { + private Island oldIsland; + private Player player; + private Reason reason; + + public Builder oldIsland(Island oldIsland) { + this.oldIsland = oldIsland; + return this; + } + + + public Builder player(Player player) { + this.player = player; + return this; + } + + public Builder reason(Reason reason) { + this.reason = reason; + return this; + } + + public Island build() throws IOException { + if (player != null) { + NewIsland newIsland = new NewIsland(oldIsland, player, reason); + return newIsland.getIsland(); + } + throw new IOException("Insufficient parameters. Must have a schematic and a player"); + } + } + + /** + * Makes an island. + */ + public void newIsland() { + if (DEBUG) { + plugin.getLogger().info("DEBUG: new island"); + } + //long time = System.nanoTime(); + final UUID playerUUID = player.getUniqueId(); + /* + boolean firstTime = false; + if (!plugin.getPlayers().hasIsland(playerUUID)) { + firstTime = true; + }*/ + if (DEBUG) { + plugin.getLogger().info("DEBUG: finding island location"); + } + Location next = getNextIsland(); + if (DEBUG) { + plugin.getLogger().info("DEBUG: found " + next); + } + + // Add to the grid + island = plugin.getIslands().createIsland(next, playerUUID); + // Save the player so that if the server is reset weird things won't happen + //plugin.getPlayers().save(true); + //plugin.getIslands().save(true); + + // Clear any old home locations (they should be clear, but just in case) + plugin.getPlayers().clearHomeLocations(playerUUID); + + // Set the biome + //BiomesPanel.setIslandBiome(next, schematic.getBiome()); + // Set home loction + plugin.getPlayers().setHomeLocation(playerUUID, next, 1); + + // Fire event + if (DEBUG) { + plugin.getLogger().info("DEBUG: firing event"); + } + IslandBaseEvent event = IslandEvent.builder() + .involvedPlayer(player.getUniqueId()) + .reason(reason) + .island(island) + .location(island.getCenter()) + .build(); + if (DEBUG) { + plugin.getLogger().info("DEBUG: event cancelled status = " + event.isCancelled()); + } + if (!event.isCancelled()) { + // Create island + new IslandBuilder(plugin, island) + .setPlayer(player) + .setChestItems(plugin.getSettings().getChestItems()) + .setType(IslandType.ISLAND) + .build(); + if (plugin.getSettings().isNetherGenerate() && plugin.getSettings().isNetherIslands() && plugin.getIslandWorldManager().getNetherWorld() != null) { + new IslandBuilder(plugin,island) + .setPlayer(player) + .setChestItems(plugin.getSettings().getChestItems()) + .setType(IslandType.NETHER) + .build(); + } + if (plugin.getSettings().isEndGenerate() && plugin.getSettings().isEndIslands() && plugin.getIslandWorldManager().getEndWorld() != null) { + new IslandBuilder(plugin,island) + .setPlayer(player) + .setChestItems(plugin.getSettings().getChestItems()) + .setType(IslandType.END) + .build(); + } + // Teleport player to their island + plugin.getIslands().homeTeleport(player); + // Fire exit event + Reason reasonDone = Reason.CREATED; + switch (reason) { + case CREATE: + reasonDone = Reason.CREATED; + break; + case RESET: + reasonDone = Reason.RESETTED; + break; + default: + break; + } + IslandEvent.builder() + .involvedPlayer(player.getUniqueId()) + .reason(reasonDone) + .island(island) + .location(island.getCenter()) + .build(); + } + } + + /** + * Get the location of next free island spot + * @return Location of island spot + */ + private Location getNextIsland() { + Location last = plugin.getIslands().getLast(); + + if (DEBUG) { + plugin.getLogger().info("DEBUG: last = " + last); + // Find the next free spot + } + + if (last == null) { + last = new Location(plugin.getIslandWorldManager().getIslandWorld(), plugin.getSettings().getIslandXOffset() + plugin.getSettings().getIslandStartX(), + plugin.getSettings().getIslandHeight(), plugin.getSettings().getIslandZOffset() + plugin.getSettings().getIslandStartZ()); + } + Location next = last.clone(); + if (DEBUG) { + plugin.getLogger().info("DEBUG: last 2 = " + last); + } + while (plugin.getIslands().isIsland(next)) { + if (DEBUG) { + plugin.getLogger().info("DEBUG: getting next loc"); + } + next = nextGridLocation(next); + } + // Make the last next, last + last = next.clone(); + if (DEBUG) { + plugin.getLogger().info("DEBUG: last 3 = " + last); + } + return next; + } + + /** + * Finds the next free island spot based off the last known island Uses + * island_distance setting from the config file Builds up in a grid fashion + * + * @param lastIsland + * @return Location of next free island + */ + private Location nextGridLocation(final Location lastIsland) { + int x = lastIsland.getBlockX(); + int z = lastIsland.getBlockZ(); + Location nextPos = lastIsland; + if (x < z) { + if (-1 * x < z) { + nextPos.setX(nextPos.getX() + plugin.getSettings().getIslandDistance()*2); + return nextPos; + } + nextPos.setZ(nextPos.getZ() + plugin.getSettings().getIslandDistance()*2); + return nextPos; + } + if (x > z) { + if (-1 * x >= z) { + nextPos.setX(nextPos.getX() - plugin.getSettings().getIslandDistance()*2); + return nextPos; + } + nextPos.setZ(nextPos.getZ() - plugin.getSettings().getIslandDistance()*2); + return nextPos; + } + if (x <= 0) { + nextPos.setZ(nextPos.getZ() + plugin.getSettings().getIslandDistance()*2); + return nextPos; + } + nextPos.setZ(nextPos.getZ() - plugin.getSettings().getIslandDistance()*2); + return nextPos; + } +} diff --git a/src/test/java/bskyblock/TestBSkyBlock.java b/src/test/java/bskyblock/TestBSkyBlock.java index 5739d38ed..73f346b4c 100644 --- a/src/test/java/bskyblock/TestBSkyBlock.java +++ b/src/test/java/bskyblock/TestBSkyBlock.java @@ -56,12 +56,12 @@ import us.tastybento.bskyblock.api.events.IslandBaseEvent; import us.tastybento.bskyblock.api.events.team.TeamEvent; import us.tastybento.bskyblock.api.flags.Flag; import us.tastybento.bskyblock.api.flags.FlagBuilder; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; import us.tastybento.bskyblock.database.objects.Island; import us.tastybento.bskyblock.generators.IslandWorld; import us.tastybento.bskyblock.listeners.flags.AbstractFlagListener; import us.tastybento.bskyblock.lists.Flags; import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandsManager; import us.tastybento.bskyblock.managers.RanksManager; import us.tastybento.bskyblock.util.Util; diff --git a/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java b/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java index 63449b8de..6396dd63d 100644 --- a/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java +++ b/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTest.java @@ -1,57 +1,81 @@ -/** - * - */ package us.tastybento.bskyblock.database.mysql; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.beans.IntrospectionException; -import java.lang.reflect.InvocationTargetException; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.SQLException; import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; import java.util.logging.Logger; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Server; import org.bukkit.World; +import org.bukkit.inventory.ItemFactory; +import org.bukkit.plugin.PluginManager; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; import us.tastybento.bskyblock.BSkyBlock; +import us.tastybento.bskyblock.Settings; +import us.tastybento.bskyblock.api.flags.Flag; +import us.tastybento.bskyblock.database.mysql.MySQLDatabaseConnecter; +import us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler; +import us.tastybento.bskyblock.database.objects.Island; +import us.tastybento.bskyblock.database.objects.Players; +import us.tastybento.bskyblock.lists.Flags; -/** - * @author ben - * - */ +@RunWith(PowerMockRunner.class) +@PrepareForTest( { BSkyBlock.class }) public class MySQLDatabaseHandlerTest { - - private static MySQLDatabaseHandler handler; - private static MySQLDatabaseHandlerTestDataObject instance; + + private static MySQLDatabaseHandler handler; + private static Island instance; private static String UNIQUE_ID = "xyz"; + private static MySQLDatabaseConnecter dbConn; + private static World world; + @Mock + static BSkyBlock plugin = mock(BSkyBlock.class); + - /** - * @throws java.lang.Exception - */ @BeforeClass - public static void setUp() throws Exception { + public static void setUpBeforeClass() throws Exception { Server server = mock(Server.class); - World world = mock(World.class); + world = mock(World.class); when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); when(server.getWorld("world")).thenReturn(world); when(server.getVersion()).thenReturn("BSB_Mocking"); + + PluginManager pluginManager = mock(PluginManager.class); + when(server.getPluginManager()).thenReturn(pluginManager); + + ItemFactory itemFactory = mock(ItemFactory.class); + when(server.getItemFactory()).thenReturn(itemFactory); + Bukkit.setServer(server); - BSkyBlock plugin = mock(BSkyBlock.class); when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - MySQLDatabaseConnecter dbConn = mock(MySQLDatabaseConnecter.class); + + Whitebox.setInternalState(BSkyBlock.class, "instance", plugin); + + Settings settings = mock(Settings.class); + + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDeathsMax()).thenReturn(10); + + when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); + dbConn = mock(MySQLDatabaseConnecter.class); Connection connection = mock(Connection.class); when(dbConn.createConnection()).thenReturn(connection); PreparedStatement ps = mock(PreparedStatement.class); @@ -61,152 +85,74 @@ public class MySQLDatabaseHandlerTest { ResultSet rs = mock(ResultSet.class); when(ps.executeQuery()).thenReturn(rs); when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); - instance = new MySQLDatabaseHandlerTestDataObject(); + instance = new Island(); instance.setUniqueId(UNIQUE_ID); - handler = new MySQLDatabaseHandler<>(plugin, MySQLDatabaseHandlerTestDataObject.class, dbConn); + handler = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#getColumns(boolean)}. - */ - @Test - public void testGetColumns() { - // This should be a list of 20 ?'s which related to the 20 - assertEquals("?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?", handler.getColumns(true)); - assertEquals("`uniqueId`, `center`, `range`, `minX`, `minZ`, `minProtectedX`, `minProtectedZ`, " + - "`protectionRange`, `world`, `name`, `createdDate`, `updatedDate`, `owner`, `members`, `locked`, " + - "`spawn`, `purgeProtected`, `flags`, `levelHandicap`, `spawnPoint`", - handler.getColumns(false)); - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#createSelectQuery()}. - */ - @Test - public void testCreateSelectQuery() { - assertEquals("SELECT `uniqueId`, `center`, `range`, `minX`, `minZ`, `minProtectedX`, " + - "`minProtectedZ`, `protectionRange`, `world`, `name`, `createdDate`, `updatedDate`, " + - "`owner`, `members`, `locked`, `spawn`, `purgeProtected`, `flags`, `levelHandicap`, " + - "`spawnPoint` FROM `us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandlerTestDataObject`", - handler.createSelectQuery()); - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#createInsertQuery()}. - */ - @Test - public void testCreateInsertQuery() { - assertEquals("REPLACE INTO `us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandlerTestDataObject`(`uniqueId`, " + - "`center`, `range`, `minX`, `minZ`, `minProtectedX`, `minProtectedZ`, `protectionRange`, " + - "`world`, `name`, `createdDate`, `updatedDate`, `owner`, `members`, `locked`, `spawn`, " + - "`purgeProtected`, `flags`, `levelHandicap`, `spawnPoint`) " + - "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - handler.createInsertQuery()); - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#createDeleteQuery()}. - */ - @Test - public void testCreateDeleteQuery() { - assertEquals("DELETE FROM [table_name] WHERE uniqueId = ?", handler.createDeleteQuery()); - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#loadObjects()}. - */ - @Test - public void testLoadObjects() { - try { - java.util.List result = handler.loadObjects(); - System.out.println("Size of result " + result.size()); - } catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException - | InvocationTargetException | ClassNotFoundException | SQLException | IntrospectionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#loadObject(java.lang.String)}. - */ - @Test - public void testLoadObject() { - try { - MySQLDatabaseHandlerTestDataObject obj = (MySQLDatabaseHandlerTestDataObject) handler.loadObject(UNIQUE_ID); - assertNull(obj); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | SecurityException | ClassNotFoundException | IntrospectionException | SQLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#saveObject(java.lang.Object)}. - */ @Test public void testSaveObject() { - try { - handler.saveObject(instance); - } catch (SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException - | InvocationTargetException | NoSuchMethodException | SQLException | IntrospectionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } + handler.saveObject(instance); + BSkyBlock plugin = mock(BSkyBlock.class); + Settings settings = mock(Settings.class); + when(plugin.getSettings()).thenReturn(settings); + when(settings.getDeathsMax()).thenReturn(10); + Players players = new Players(); + players.setUniqueId(UUID.randomUUID().toString()); + players.setDeaths(23); + Location location = mock(Location.class); + Mockito.when(location.getWorld()).thenReturn(world); + Mockito.when(location.getBlockX()).thenReturn(0); + Mockito.when(location.getBlockY()).thenReturn(0); + Mockito.when(location.getBlockZ()).thenReturn(0); - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#deleteObject(java.lang.Object)}. - */ - @Test - public void testDeleteObject() { - try { - handler.deleteObject(instance); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException - | SecurityException | IntrospectionException | SQLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } + players.setHomeLocation(location); + players.setHomeLocation(location, 1); + players.setHomeLocation(location, 2); + Map map = new HashMap<>(); + map.put(location, 324L); + players.setKickedList(map); + players.setLocale("sdfsd"); + players.setPlayerName("name"); + players.setPlayerUUID(UUID.randomUUID()); + players.setResetsLeft(3); + - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#objectExists(java.lang.String)}. - */ - @Test - public void testObjectExits() { - // This right now is not tested properly - assertFalse(handler.objectExists(UNIQUE_ID)); - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#saveSettings(java.lang.Object)}. - */ - @Test - public void testSaveSettings() { - try { - handler.saveSettings(instance); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException - | IntrospectionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + MySQLDatabaseHandler h = new MySQLDatabaseHandler<>(plugin, Players.class, dbConn); + h.saveObject(players); + + Island island = new Island(); + island.setUniqueId(UNIQUE_ID); + island.setCenter(location); + Map flags = new HashMap<>(); + for (Flag fl : Flags.values()) { + flags.put(fl, 100); } - } - - /** - * Test method for {@link us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandler#loadSettings(java.lang.String, java.lang.Object)}. - */ - @Test - public void testLoadSettings() { - try { - handler.loadSettings(UNIQUE_ID, instance); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | ClassNotFoundException | IntrospectionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + island.setFlags(flags); + island.setLevelHandicap(10); + island.setLocked(true); + Map members = new HashMap<>(); + for (int i = 0; i < 100; i++) { + members.put(UUID.randomUUID(), i); } + island.setMembers(members); + island.setMinProtectedX(-100); + island.setMinProtectedZ(-300); + island.setMinX(-121); + island.setMinZ(-23423); + island.setName("ytasdgfsdfg"); + island.setOwner(UUID.randomUUID()); + island.setProtectionRange(100); + island.setPurgeProtected(true); + island.setRange(100); + island.setSpawn(true); + island.setSpawnPoint(location); + island.setWorld(world); + + MySQLDatabaseHandler ih = new MySQLDatabaseHandler<>(plugin, Island.class, dbConn); + ih.saveObject(island); + } } diff --git a/src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java b/src/test/java/us/tastybento/bskyblock/database/mysql/Players.java similarity index 100% rename from src/test/java/us/tastybento/bskyblock/database/mysqljson/Players.java rename to src/test/java/us/tastybento/bskyblock/database/mysql/Players.java diff --git a/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java b/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java deleted file mode 100644 index c4be2f900..000000000 --- a/src/test/java/us/tastybento/bskyblock/database/mysqljson/MySQLDatabaseHandlerJTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.logging.Logger; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Server; -import org.bukkit.World; -import org.bukkit.inventory.ItemFactory; -import org.bukkit.plugin.PluginManager; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.powermock.modules.junit4.PowerMockRunner; - -import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.Settings; -import us.tastybento.bskyblock.database.mysql.MySQLDatabaseConnecter; -import us.tastybento.bskyblock.database.mysql.MySQLDatabaseHandlerTestDataObject; - -@RunWith(PowerMockRunner.class) -public class MySQLDatabaseHandlerJTest { - - private static MySQLDatabaseHandlerJ handler; - private static MySQLDatabaseHandlerTestDataObject instance; - private static String UNIQUE_ID = "xyz"; - private static MySQLDatabaseConnecter dbConn; - private static World world; - - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - Server server = mock(Server.class); - world = mock(World.class); - when(server.getLogger()).thenReturn(Logger.getAnonymousLogger()); - when(server.getWorld("world")).thenReturn(world); - when(server.getVersion()).thenReturn("BSB_Mocking"); - - PluginManager pluginManager = mock(PluginManager.class); - when(server.getPluginManager()).thenReturn(pluginManager); - - ItemFactory itemFactory = mock(ItemFactory.class); - when(server.getItemFactory()).thenReturn(itemFactory); - - Bukkit.setServer(server); - - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - - BSkyBlock plugin = mock(BSkyBlock.class); - when(Bukkit.getLogger()).thenReturn(Logger.getAnonymousLogger()); - dbConn = mock(MySQLDatabaseConnecter.class); - Connection connection = mock(Connection.class); - when(dbConn.createConnection()).thenReturn(connection); - PreparedStatement ps = mock(PreparedStatement.class); - when(connection.prepareStatement(Mockito.anyString())).thenReturn(ps); - Statement statement = mock(Statement.class); - when(connection.createStatement()).thenReturn(statement); - ResultSet rs = mock(ResultSet.class); - when(ps.executeQuery()).thenReturn(rs); - when(statement.executeQuery(Mockito.anyString())).thenReturn(rs); - instance = new MySQLDatabaseHandlerTestDataObject(); - instance.setUniqueId(UNIQUE_ID); - handler = new MySQLDatabaseHandlerJ<>(plugin, MySQLDatabaseHandlerTestDataObject.class, dbConn); - - } - - @Test - public void testSaveObject() { - handler.saveObject(instance); - BSkyBlock plugin = mock(BSkyBlock.class); - Settings settings = mock(Settings.class); - when(plugin.getSettings()).thenReturn(settings); - when(settings.getDeathsMax()).thenReturn(10); - Players players = new Players(); - players.setUniqueId(UUID.randomUUID().toString()); - players.setDeaths(23); - Location location = mock(Location.class); - Mockito.when(location.getWorld()).thenReturn(world); - Mockito.when(location.getBlockX()).thenReturn(0); - Mockito.when(location.getBlockY()).thenReturn(0); - Mockito.when(location.getBlockZ()).thenReturn(0); - - players.setHomeLocation(location); - players.setHomeLocation(location, 1); - players.setHomeLocation(location, 2); - Map map = new HashMap<>(); - map.put(location, 324L); - players.setKickedList(map); - players.setLocale("sdfsd"); - players.setPlayerName("name"); - players.setPlayerUUID(UUID.randomUUID()); - players.setResetsLeft(3); - - MySQLDatabaseHandlerJ h = new MySQLDatabaseHandlerJ<>(plugin, Players.class, dbConn); - h.saveObject(players); - } - -} diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java index aab29e438..d34216cad 100644 --- a/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/FireListenerTest.java @@ -42,11 +42,11 @@ import org.powermock.modules.junit4.PowerMockRunner; import us.tastybento.bskyblock.BSkyBlock; import us.tastybento.bskyblock.Settings; import us.tastybento.bskyblock.api.commands.User; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; import us.tastybento.bskyblock.database.objects.Island; import us.tastybento.bskyblock.generators.IslandWorld; import us.tastybento.bskyblock.lists.Flags; import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandsManager; import us.tastybento.bskyblock.managers.LocalesManager; @RunWith(PowerMockRunner.class) diff --git a/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java b/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java index 0c72480ac..51b6be670 100644 --- a/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java +++ b/src/test/java/us/tastybento/bskyblock/listeners/flags/MobSpawnListenerTest.java @@ -33,11 +33,11 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.database.managers.island.IslandsManager; import us.tastybento.bskyblock.database.objects.Island; import us.tastybento.bskyblock.generators.IslandWorld; import us.tastybento.bskyblock.lists.Flags; import us.tastybento.bskyblock.managers.FlagsManager; +import us.tastybento.bskyblock.managers.IslandsManager; @RunWith(PowerMockRunner.class) @PrepareForTest( { Flags.class }) From c10f413173d50a829d07ba3f30cb14783931a98e Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 12 Mar 2018 04:09:05 +0900 Subject: [PATCH 3/5] Added PotionEffectTypeAdapter for GSON --- .../database/managers/island/IslandCache.java | 392 ------------------ .../database/managers/island/NewIsland.java | 252 ----------- .../mysql/MySQLDatabaseResourceCloser.java | 84 ---- .../adapters/PotionEffectTypeAdapter.java | 32 ++ .../MySQLDatabaseHandlerTestDataObject.java | 349 ---------------- .../bskyblock/database/mysql/Players.java | 276 ------------ 6 files changed, 32 insertions(+), 1353 deletions(-) delete mode 100644 src/main/java/us/tastybento/bskyblock/database/managers/island/IslandCache.java delete mode 100644 src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java delete mode 100644 src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseResourceCloser.java create mode 100644 src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java delete mode 100644 src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTestDataObject.java delete mode 100755 src/test/java/us/tastybento/bskyblock/database/mysql/Players.java diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/island/IslandCache.java b/src/main/java/us/tastybento/bskyblock/database/managers/island/IslandCache.java deleted file mode 100644 index be9294718..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/managers/island/IslandCache.java +++ /dev/null @@ -1,392 +0,0 @@ -package us.tastybento.bskyblock.database.managers.island; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Set; -import java.util.TreeMap; -import java.util.UUID; - -import org.bukkit.ChatColor; -import org.bukkit.Location; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; - -import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.database.objects.Island; -import us.tastybento.bskyblock.util.Util; - -public class IslandCache { - private static final boolean DEBUG2 = false; - private static final boolean DEBUG = false; - private BSkyBlock plugin = BSkyBlock.getInstance(); - private BiMap islandsByLocation; - /** - * Every player who is associated with an island is in this map. - */ - private HashMap islandsByUUID; - // 2D islandGrid of islands, x,z - private TreeMap> islandGrid = new TreeMap<>(); - - public IslandCache() { - islandsByLocation = HashBiMap.create(); - islandsByUUID = new HashMap<>(); - } - - /** - * Adds an island to the grid - * @param island - */ - public void addIsland(Island island) { - islandsByLocation.put(island.getCenter(), island); - if (DEBUG) { - plugin.getLogger().info("DEBUG: owner = " + island.getOwner()); - } - islandsByUUID.put(island.getOwner(), island); - if (DEBUG) { - plugin.getLogger().info("DEBUG: island has " + island.getMemberSet().size() + " members"); - } - for (UUID member: island.getMemberSet()) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: " + member); - } - islandsByUUID.put(member, island); - } - addToGrid(island); - } - - public void addPlayer(UUID playerUUID, Island teamIsland) { - islandsByUUID.put(playerUUID, teamIsland); - } - - /** - * Adds an island to the grid register - * @param newIsland - */ - private void addToGrid(Island newIsland) { - if (islandGrid.containsKey(newIsland.getMinX())) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: min x is in the grid :" + newIsland.getMinX()); - } - TreeMap zEntry = islandGrid.get(newIsland.getMinX()); - if (zEntry.containsKey(newIsland.getMinZ())) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: min z is in the grid :" + newIsland.getMinZ()); - } - // Island already exists - Island conflict = islandGrid.get(newIsland.getMinX()).get(newIsland.getMinZ()); - plugin.getLogger().warning("*** Duplicate or overlapping islands! ***"); - plugin.getLogger().warning( - "Island at (" + newIsland.getCenter().getBlockX() + ", " + newIsland.getCenter().getBlockZ() + ") conflicts with (" - + conflict.getCenter().getBlockX() + ", " + conflict.getCenter().getBlockZ() + ")"); - if (conflict.getOwner() != null) { - plugin.getLogger().warning("Accepted island is owned by " + plugin.getPlayers().getName(conflict.getOwner())); - plugin.getLogger().warning(conflict.getOwner().toString() + ".yml"); - } else { - plugin.getLogger().warning("Accepted island is unowned."); - } - if (newIsland.getOwner() != null) { - plugin.getLogger().warning("Denied island is owned by " + plugin.getPlayers().getName(newIsland.getOwner())); - plugin.getLogger().warning(newIsland.getOwner().toString() + ".yml"); - } else { - plugin.getLogger().warning("Denied island is unowned and was just found in the islands folder. Skipping it..."); - } - plugin.getLogger().warning("Recommend that the denied player file is deleted otherwise weird things can happen."); - return; - } else { - // Add island - if (DEBUG) { - plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ()); - } - zEntry.put(newIsland.getMinZ(), newIsland); - islandGrid.put(newIsland.getMinX(), zEntry); - // plugin.getLogger().info("Debug: " + newIsland.toString()); - } - } else { - // Add island - if (DEBUG) { - plugin.getLogger().info("DEBUG: added island to grid at " + newIsland.getMinX() + "," + newIsland.getMinZ()); - } - TreeMap zEntry = new TreeMap<>(); - zEntry.put(newIsland.getMinZ(), newIsland); - islandGrid.put(newIsland.getMinX(), zEntry); - } - } - - public void clear() { - islandsByLocation.clear(); - islandsByUUID.clear(); - } - - public Island createIsland(Island island) { - islandsByLocation.put(island.getCenter(), island); - if (island.getOwner() != null) { - islandsByUUID.put(island.getOwner(), island); - } - addToGrid(island); - return island; - } - - /** - * Create an island with no owner at location - * @param location - the location - */ - public Island createIsland(Location location){ - return createIsland(location, null); - } - - /** - * Create an island with owner. Note this does not create the schematic. It just creates the island data object. - * @param location - the location - * @param owner - the island owner UUID - */ - public Island createIsland(Location location, UUID owner){ - if (DEBUG) { - plugin.getLogger().info("DEBUG: adding island for " + owner + " at " + location); - } - Island island = new Island(location, owner, plugin.getSettings().getIslandProtectionRange()); - islandsByLocation.put(location, island); - if (owner != null) { - islandsByUUID.put(owner, island); - } - addToGrid(island); - return island; - } - - /** - * Deletes an island from the database. Does not remove blocks - * @param island - */ - public void deleteIslandFromCache(Island island) { - if (!islandsByLocation.remove(island.getCenter(), island)) { - plugin.getLogger().severe("Could not remove island from cache!"); - } - Iterator> it = islandsByUUID.entrySet().iterator(); - while (it.hasNext()) { - Entry en = it.next(); - if (en.getValue().equals(island)) { - it.remove(); - } - } - // Remove from grid - if (DEBUG) { - plugin.getLogger().info("DEBUG: deleting island at " + island.getCenter()); - } - if (island != null) { - int x = island.getMinX(); - int z = island.getMinZ(); - if (DEBUG) { - plugin.getLogger().info("DEBUG: x = " + x + " z = " + z); - } - if (islandGrid.containsKey(x)) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: x found"); - } - TreeMap zEntry = islandGrid.get(x); - if (zEntry.containsKey(z)) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: z found - deleting the island"); - } - // Island exists - delete it - zEntry.remove(z); - islandGrid.put(x, zEntry); - } else { - if (DEBUG) { - plugin.getLogger().info("DEBUG: could not find z"); - } - } - } - } - } - - public Island get(Location location) { - return islandsByLocation.get(location); - } - - public Island get(UUID uuid) { - return islandsByUUID.get(uuid); - } - - /** - * Gets the island for this player. If they are in a team, the team island is returned - * @param uuid - UUID - * @return Island - */ - public Island getIsland(UUID uuid){ - return islandsByUUID.get(uuid); - } - - /** - * Returns the island at the x,z location or null if there is none. - * This includes the full island space, not just the protected area. - * - * @param x - x coordinate - * @param z - z coordinate - * @return Island or null - */ - public Island getIslandAt(int x, int z) { - if (DEBUG2) { - plugin.getLogger().info("DEBUG: getting island at " + x + "," + z); - plugin.getLogger().info("DEBUG: island grid is " + islandGrid.size()); - } - Entry> en = islandGrid.floorEntry(x); - if (en != null) { - Entry ent = en.getValue().floorEntry(z); - if (ent != null) { - // Check if in the island range - Island island = ent.getValue(); - if (island.inIslandSpace(x, z)) { - if (DEBUG2) { - plugin.getLogger().info("DEBUG: In island space"); - } - return island; - } - if (DEBUG2) { - plugin.getLogger().info("DEBUG: not in island space"); - } - } - } - return null; - } - - /** - * Returns the island at the location or null if there is none. - * This includes the full island space, not just the protected area - * - * @param location - the location - * @return Island object - */ - public Island getIslandAt(Location location) { - if (location == null) { - //plugin.getLogger().info("DEBUG: location is null"); - return null; - } - // World check - if (!Util.inWorld(location)) { - //plugin.getLogger().info("DEBUG: not in right world"); - return null; - } - return getIslandAt(location.getBlockX(), location.getBlockZ()); - } - - /** - * Returns the player's island location. - * Returns an island location OR a team island location - * - * @param playerUUID - the player's UUID - * @return Location of player's island or null if one does not exist - */ - public Location getIslandLocation(UUID playerUUID) { - if (hasIsland(playerUUID)) { - return getIsland(playerUUID).getCenter(); - } - return null; - } - - /** - * Get name of the island owned by owner - * @param owner - the island owner - * @return Returns the name of owner's island, or the owner's name if there is none. - */ - public String getIslandName(UUID owner) { - String result = plugin.getPlayers().getName(owner); - if (islandsByUUID.containsKey(owner)) { - Island island = islandsByUUID.get(owner); - if (!island.getName().isEmpty()) { - result = island.getName(); - } - } - return ChatColor.translateAlternateColorCodes('&', result) + ChatColor.RESET; - } - - public Collection getIslands() { - return Collections.unmodifiableCollection(islandsByLocation.values()); - } - - public Set getMembers(UUID playerUUID) { - Island island = islandsByUUID.get(playerUUID); - if (island != null) { - return island.getMemberSet(); - } - return new HashSet<>(0); - } - - public UUID getTeamLeader(UUID playerUUID) { - if (islandsByUUID.containsKey(playerUUID)) { - return islandsByUUID.get(playerUUID).getOwner(); - } - return null; - } - - /** - * @param playerUUID - the player's UUID - * @return true if player has island and owns it - */ - public boolean hasIsland(UUID playerUUID) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: checking if " + playerUUID + " has an island"); - plugin.getLogger().info("DEBUG: islandsByUUID : " + islandsByUUID.toString()); - - if (!islandsByUUID.containsKey(playerUUID)) { - plugin.getLogger().info("DEBUG: player is not in islandsByUUID"); - } else { - plugin.getLogger().info("DEBUG: owner = " + islandsByUUID.get(playerUUID).getOwner()); - } - } - if (islandsByUUID.containsKey(playerUUID) && islandsByUUID.get(playerUUID).getOwner() != null) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: checking for equals"); - } - if (islandsByUUID.get(playerUUID).getOwner().equals(playerUUID)) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: has island"); - } - return true; - } - } - if (DEBUG) { - plugin.getLogger().info("DEBUG: doesn't have island"); - } - return false; - } - - public void removePlayer(UUID playerUUID) { - Island island = islandsByUUID.get(playerUUID); - if (island != null) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: island found"); - } - if (island.getOwner() != null && island.getOwner().equals(playerUUID)) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: player is the owner of this island"); - } - // Clear ownership and members - island.getMembers().clear(); - island.setOwner(null); - } - island.removeMember(playerUUID); - } - if (DEBUG) { - plugin.getLogger().info("DEBUG: removing reference to island by UUID"); - } - islandsByUUID.remove(playerUUID); - - } - - public void setIslandName(UUID owner, String name) { - if (islandsByUUID.containsKey(owner)) { - Island island = islandsByUUID.get(owner); - island.setName(name); - } - - } - - public int size() { - return islandsByLocation.size(); - } - -} diff --git a/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java b/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java deleted file mode 100644 index 3b80b1f89..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/managers/island/NewIsland.java +++ /dev/null @@ -1,252 +0,0 @@ -package us.tastybento.bskyblock.database.managers.island; - -import java.io.IOException; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.entity.Player; - -import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.api.events.IslandBaseEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent; -import us.tastybento.bskyblock.api.events.island.IslandEvent.Reason; -import us.tastybento.bskyblock.database.objects.Island; -import us.tastybento.bskyblock.island.builders.IslandBuilder; -import us.tastybento.bskyblock.island.builders.IslandBuilder.IslandType; - -/** - * Create and paste a new island - * @author tastybento - * - */ -public class NewIsland { - private static final boolean DEBUG = false; - private BSkyBlock plugin; - private Island island; - private final Player player; - private final Reason reason; - - private NewIsland(Island oldIsland, Player player, Reason reason) { - super(); - plugin = BSkyBlock.getInstance(); - this.player = player; - this.reason = reason; - newIsland(); - if (oldIsland != null) { - // Delete the old island - plugin.getIslands().deleteIsland(oldIsland, true); - } - } - - /** - * @return the island that was created - */ - public Island getIsland() { - return island; - } - - /** - * Start building a new island - * @return New island builder object - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Build a new island for a player using a schematic - * @author tastybento - * - */ - public static class Builder { - private Island oldIsland; - private Player player; - private Reason reason; - - public Builder oldIsland(Island oldIsland) { - this.oldIsland = oldIsland; - return this; - } - - - public Builder player(Player player) { - this.player = player; - return this; - } - - public Builder reason(Reason reason) { - this.reason = reason; - return this; - } - - public Island build() throws IOException { - if (player != null) { - NewIsland newIsland = new NewIsland(oldIsland, player, reason); - return newIsland.getIsland(); - } - throw new IOException("Insufficient parameters. Must have a schematic and a player"); - } - } - - /** - * Makes an island. - */ - public void newIsland() { - if (DEBUG) { - plugin.getLogger().info("DEBUG: new island"); - } - //long time = System.nanoTime(); - final UUID playerUUID = player.getUniqueId(); - /* - boolean firstTime = false; - if (!plugin.getPlayers().hasIsland(playerUUID)) { - firstTime = true; - }*/ - if (DEBUG) { - plugin.getLogger().info("DEBUG: finding island location"); - } - Location next = getNextIsland(); - if (DEBUG) { - plugin.getLogger().info("DEBUG: found " + next); - } - - // Add to the grid - island = plugin.getIslands().createIsland(next, playerUUID); - // Save the player so that if the server is reset weird things won't happen - //plugin.getPlayers().save(true); - //plugin.getIslands().save(true); - - // Clear any old home locations (they should be clear, but just in case) - plugin.getPlayers().clearHomeLocations(playerUUID); - - // Set the biome - //BiomesPanel.setIslandBiome(next, schematic.getBiome()); - // Set home loction - plugin.getPlayers().setHomeLocation(playerUUID, next, 1); - - // Fire event - if (DEBUG) { - plugin.getLogger().info("DEBUG: firing event"); - } - IslandBaseEvent event = IslandEvent.builder() - .involvedPlayer(player.getUniqueId()) - .reason(reason) - .island(island) - .location(island.getCenter()) - .build(); - if (DEBUG) { - plugin.getLogger().info("DEBUG: event cancelled status = " + event.isCancelled()); - } - if (!event.isCancelled()) { - // Create island - new IslandBuilder(plugin, island) - .setPlayer(player) - .setChestItems(plugin.getSettings().getChestItems()) - .setType(IslandType.ISLAND) - .build(); - if (plugin.getSettings().isNetherGenerate() && plugin.getSettings().isNetherIslands() && plugin.getIslandWorldManager().getNetherWorld() != null) { - new IslandBuilder(plugin,island) - .setPlayer(player) - .setChestItems(plugin.getSettings().getChestItems()) - .setType(IslandType.NETHER) - .build(); - } - if (plugin.getSettings().isEndGenerate() && plugin.getSettings().isEndIslands() && plugin.getIslandWorldManager().getEndWorld() != null) { - new IslandBuilder(plugin,island) - .setPlayer(player) - .setChestItems(plugin.getSettings().getChestItems()) - .setType(IslandType.END) - .build(); - } - // Teleport player to their island - plugin.getIslands().homeTeleport(player); - // Fire exit event - Reason reasonDone = Reason.CREATED; - switch (reason) { - case CREATE: - reasonDone = Reason.CREATED; - break; - case RESET: - reasonDone = Reason.RESETTED; - break; - default: - break; - } - IslandEvent.builder() - .involvedPlayer(player.getUniqueId()) - .reason(reasonDone) - .island(island) - .location(island.getCenter()) - .build(); - } - } - - /** - * Get the location of next free island spot - * @return Location of island spot - */ - private Location getNextIsland() { - Location last = plugin.getIslands().getLast(); - - if (DEBUG) { - plugin.getLogger().info("DEBUG: last = " + last); - // Find the next free spot - } - - if (last == null) { - last = new Location(plugin.getIslandWorldManager().getIslandWorld(), plugin.getSettings().getIslandXOffset() + plugin.getSettings().getIslandStartX(), - plugin.getSettings().getIslandHeight(), plugin.getSettings().getIslandZOffset() + plugin.getSettings().getIslandStartZ()); - } - Location next = last.clone(); - if (DEBUG) { - plugin.getLogger().info("DEBUG: last 2 = " + last); - } - while (plugin.getIslands().isIsland(next)) { - if (DEBUG) { - plugin.getLogger().info("DEBUG: getting next loc"); - } - next = nextGridLocation(next); - } - // Make the last next, last - last = next.clone(); - if (DEBUG) { - plugin.getLogger().info("DEBUG: last 3 = " + last); - } - return next; - } - - /** - * Finds the next free island spot based off the last known island Uses - * island_distance setting from the config file Builds up in a grid fashion - * - * @param lastIsland - * @return Location of next free island - */ - private Location nextGridLocation(final Location lastIsland) { - int x = lastIsland.getBlockX(); - int z = lastIsland.getBlockZ(); - Location nextPos = lastIsland; - if (x < z) { - if (-1 * x < z) { - nextPos.setX(nextPos.getX() + plugin.getSettings().getIslandDistance()*2); - return nextPos; - } - nextPos.setZ(nextPos.getZ() + plugin.getSettings().getIslandDistance()*2); - return nextPos; - } - if (x > z) { - if (-1 * x >= z) { - nextPos.setX(nextPos.getX() - plugin.getSettings().getIslandDistance()*2); - return nextPos; - } - nextPos.setZ(nextPos.getZ() - plugin.getSettings().getIslandDistance()*2); - return nextPos; - } - if (x <= 0) { - nextPos.setZ(nextPos.getZ() + plugin.getSettings().getIslandDistance()*2); - return nextPos; - } - nextPos.setZ(nextPos.getZ() - plugin.getSettings().getIslandDistance()*2); - return nextPos; - } -} diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseResourceCloser.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseResourceCloser.java deleted file mode 100644 index 8be1706d6..000000000 --- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseResourceCloser.java +++ /dev/null @@ -1,84 +0,0 @@ -package us.tastybento.bskyblock.database.mysql; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import org.bukkit.Bukkit; - -public class MySQLDatabaseResourceCloser { - - /** - * Closes the provided ResultSets - * - * @param resultSets - * ResultSets that should be closed - */ - public static void close(ResultSet... resultSets) { - - if (resultSets == null) { - return; - } - - for (ResultSet resultSet : resultSets) { - if (resultSet != null) { - try { - resultSet.close(); - } catch (SQLException e) { - Bukkit.getLogger().severe("Could not close MySQL resultset"); - } - } - } - } - - /** - * Closes the provided Statements - * - * @param statements - * Statements that should be closed - */ - public static void close(Statement... statements) { - /* - * No need to create methods for PreparedStatement and - * CallableStatement, because they extend Statement. - */ - - if (statements == null) { - return; - } - - for (Statement statement : statements) { - if (statement != null) { - try { - statement.close(); - } catch (SQLException e) { - Bukkit.getLogger().severe("Could not close MySQL statement"); - } - } - } - } - - /** - * Closes the provided Connections - * - * @param connections - * Connections that should be closed - */ - public static void close(Connection... connections) { - if (connections == null) { - return; - } - - for (Connection connection : connections) { - if (connection != null) { - try { - connection.close(); - } catch (SQLException e) { - Bukkit.getLogger().severe("Could not close MySQL connection"); - } - } - } - } -} - diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java new file mode 100644 index 000000000..df6b24eb9 --- /dev/null +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/adapters/PotionEffectTypeAdapter.java @@ -0,0 +1,32 @@ +package us.tastybento.bskyblock.database.mysql.adapters; + +import java.io.IOException; + +import org.bukkit.potion.PotionEffectType; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +public class PotionEffectTypeAdapter extends TypeAdapter { + + @Override + public void write(JsonWriter out, PotionEffectType value) throws IOException { + if (value == null) { + out.nullValue(); + return; + } + out.value(value.getName()); + + } + + @Override + public PotionEffectType read(JsonReader reader) throws IOException { + if (reader.peek() == JsonToken.NULL) { + reader.nextNull(); + return null; + } + return PotionEffectType.getByName(reader.nextString()); + } +} \ No newline at end of file diff --git a/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTestDataObject.java b/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTestDataObject.java deleted file mode 100644 index c0216bfb0..000000000 --- a/src/test/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandlerTestDataObject.java +++ /dev/null @@ -1,349 +0,0 @@ -package us.tastybento.bskyblock.database.mysql; - -import java.util.HashMap; -import java.util.UUID; - -import org.bukkit.Location; -import org.bukkit.World; - -import us.tastybento.bskyblock.api.flags.Flag; -import us.tastybento.bskyblock.database.objects.DataObject; -import us.tastybento.bskyblock.database.objects.adapters.Adapter; -import us.tastybento.bskyblock.database.objects.adapters.FlagSerializer; - - -public class MySQLDatabaseHandlerTestDataObject implements DataObject { - - private String uniqueId = ""; - - //// Island //// - // The center of the island itself - private Location center; - - // Island range - private int range; - - // Coordinates of the island area - private int minX; - - private int minZ; - - // Coordinates of minimum protected area - private int minProtectedX; - - private int minProtectedZ; - - // Protection size - private int protectionRange; - - // World the island is in - private World world; - - // Display name - private String name; - - // Time parameters - private long createdDate; - - private long updatedDate; - - //// Team //// - private UUID owner; - private HashMap members = new HashMap<>(); - - //// State //// - private boolean locked = false; - private boolean spawn = false; - - private boolean purgeProtected = false; - - //// Protection flags //// - @Adapter(FlagSerializer.class) - private HashMap flags = new HashMap<>(); - - private int levelHandicap; - private Location spawnPoint; - - public MySQLDatabaseHandlerTestDataObject() {} - - /** - * @return the uniqueId - */ - public String getUniqueId() { - return uniqueId; - } - - /** - * @param uniqueId - unique ID the uniqueId to set - */ - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } - - /** - * @return the center - */ - public Location getCenter() { - return center; - } - - /** - * @param center the center to set - */ - public void setCenter(Location center) { - this.center = center; - } - - /** - * @return the range - */ - public int getRange() { - return range; - } - - /** - * @param range the range to set - */ - public void setRange(int range) { - this.range = range; - } - - /** - * @return the minX - */ - public int getMinX() { - return minX; - } - - /** - * @param minX the minX to set - */ - public void setMinX(int minX) { - this.minX = minX; - } - - /** - * @return the minZ - */ - public int getMinZ() { - return minZ; - } - - /** - * @param minZ the minZ to set - */ - public void setMinZ(int minZ) { - this.minZ = minZ; - } - - /** - * @return the minProtectedX - */ - public int getMinProtectedX() { - return minProtectedX; - } - - /** - * @param minProtectedX the minProtectedX to set - */ - public void setMinProtectedX(int minProtectedX) { - this.minProtectedX = minProtectedX; - } - - /** - * @return the minProtectedZ - */ - public int getMinProtectedZ() { - return minProtectedZ; - } - - /** - * @param minProtectedZ the minProtectedZ to set - */ - public void setMinProtectedZ(int minProtectedZ) { - this.minProtectedZ = minProtectedZ; - } - - /** - * @return the protectionRange - */ - public int getProtectionRange() { - return protectionRange; - } - - /** - * @param protectionRange the protectionRange to set - */ - public void setProtectionRange(int protectionRange) { - this.protectionRange = protectionRange; - } - - /** - * @return the world - */ - public World getWorld() { - return world; - } - - /** - * @param world the world to set - */ - public void setWorld(World world) { - this.world = world; - } - - /** - * @return the name - */ - public String getName() { - return name; - } - - /** - * @param name the name to set - */ - public void setName(String name) { - this.name = name; - } - - /** - * @return the createdDate - */ - public long getCreatedDate() { - return createdDate; - } - - /** - * @param createdDate the createdDate to set - */ - public void setCreatedDate(long createdDate) { - this.createdDate = createdDate; - } - - /** - * @return the updatedDate - */ - public long getUpdatedDate() { - return updatedDate; - } - - /** - * @param updatedDate the updatedDate to set - */ - public void setUpdatedDate(long updatedDate) { - this.updatedDate = updatedDate; - } - - /** - * @return the owner - */ - public UUID getOwner() { - return owner; - } - - /** - * @param owner - the island owner the owner to set - */ - public void setOwner(UUID owner) { - this.owner = owner; - } - - /** - * @return the members - */ - public HashMap getMembers() { - return members; - } - - /** - * @param members the members to set - */ - public void setMembers(HashMap members) { - this.members = members; - } - - /** - * @return the locked - */ - public boolean isLocked() { - return locked; - } - - /** - * @param locked the locked to set - */ - public void setLocked(boolean locked) { - this.locked = locked; - } - - /** - * @return the spawn - */ - public boolean isSpawn() { - return spawn; - } - - /** - * @param spawn the spawn to set - */ - public void setSpawn(boolean spawn) { - this.spawn = spawn; - } - - /** - * @return the purgeProtected - */ - public boolean isPurgeProtected() { - return purgeProtected; - } - - /** - * @param purgeProtected the purgeProtected to set - */ - public void setPurgeProtected(boolean purgeProtected) { - this.purgeProtected = purgeProtected; - } - - /** - * @return the flags - */ - public HashMap getFlags() { - return flags; - } - - /** - * @param flags the flags to set - */ - public void setFlags(HashMap flags) { - this.flags = flags; - } - - /** - * @return the levelHandicap - */ - public int getLevelHandicap() { - return levelHandicap; - } - - /** - * @param levelHandicap the levelHandicap to set - */ - public void setLevelHandicap(int levelHandicap) { - this.levelHandicap = levelHandicap; - } - - /** - * @return the spawnPoint - */ - public Location getSpawnPoint() { - return spawnPoint; - } - - /** - * @param spawnPoint the spawnPoint to set - */ - public void setSpawnPoint(Location spawnPoint) { - this.spawnPoint = spawnPoint; - } - -} \ No newline at end of file diff --git a/src/test/java/us/tastybento/bskyblock/database/mysql/Players.java b/src/test/java/us/tastybento/bskyblock/database/mysql/Players.java deleted file mode 100755 index 2b7afe7c8..000000000 --- a/src/test/java/us/tastybento/bskyblock/database/mysql/Players.java +++ /dev/null @@ -1,276 +0,0 @@ -package us.tastybento.bskyblock.database.mysqljson; - -import java.util.Calendar; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.entity.Player; - -import us.tastybento.bskyblock.BSkyBlock; -import us.tastybento.bskyblock.database.objects.DataObject; - -/** - * Tracks the following info on the player - * - * @author tastybento - */ -public class Players implements DataObject { - private Map homeLocations = new HashMap<>(); - private String uniqueId; - private String playerName; - private int resetsLeft; - private String locale = ""; - private int deaths; - private Map kickedList = new HashMap<>(); - - /** - * This is required for database storage - */ - public Players() {} - - /** - * @param plugin - BSkyBlock plugin object - * @param uniqueId - unique ID - * Constructor - initializes the state variables - * - */ - public Players(BSkyBlock plugin, final UUID uniqueId) { - this.uniqueId = uniqueId.toString(); - homeLocations = new HashMap<>(); - playerName = ""; - resetsLeft = plugin.getSettings().getResetLimit(); - locale = ""; - kickedList = new HashMap<>(); - playerName = Bukkit.getServer().getOfflinePlayer(uniqueId).getName(); - if (playerName == null) { - playerName = uniqueId.toString(); - } - } - - /** - * Gets the default home location. - * @return Location - */ - public Location getHomeLocation() { - return getHomeLocation(1); // Default - } - - /** - * Gets the home location by number. - * @param number - a number - * @return Location of this home or null if not available - */ - public Location getHomeLocation(int number) { - /* - Bukkit.getLogger().info("DEBUG: getting home location " + number); - - Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); - for (Entry en : homeLocations.entrySet()) { - Bukkit.getLogger().info("DEBUG: " + en.getKey() + " ==> " + en.getValue()); - if (number == en.getKey()) - Bukkit.getLogger().info("DEBUG: key = number"); - }*/ - return homeLocations.get(number); - } - - /** - * @return List of home locations - */ - public Map getHomeLocations() { - return homeLocations; - } - - /** - * @return the kickedList - */ - public Map getKickedList() { - return kickedList; - } - - /** - * @param kickedList the kickedList to set - */ - public void setKickedList(Map kickedList) { - this.kickedList = kickedList; - } - - /** - * @param homeLocations the homeLocations to set - */ - public void setHomeLocations(Map homeLocations) { - //Bukkit.getLogger().info("DEBUG: " + homeLocations.toString()); - this.homeLocations = homeLocations; - } - - /** - * @param playerName the playerName to set - */ - public void setPlayerName(String playerName) { - this.playerName = playerName; - } - - public Player getPlayer() { - return Bukkit.getPlayer(UUID.fromString(uniqueId)); - } - - public UUID getPlayerUUID() { - return UUID.fromString(uniqueId); - } - - public String getPlayerName() { - return playerName; - } - - /** - * @return the resetsLeft - */ - public int getResetsLeft() { - return resetsLeft; - } - - /** - * @param resetsLeft - * the resetsLeft to set - */ - public void setResetsLeft(int resetsLeft) { - this.resetsLeft = resetsLeft; - } - - /** - * Stores the home location of the player in a String format - * - * @param l - * a Bukkit location - */ - public void setHomeLocation(final Location l) { - setHomeLocation(l, 1); - } - - /** - * Stores the numbered home location of the player. Numbering starts at 1. - * @param location - the location - * @param number - a number - */ - public void setHomeLocation(final Location location, int number) { - if (location == null) { - homeLocations.clear(); - } else { - homeLocations.put(number, location); - } - } - - /** - * Set the uuid for this player object - * @param uuid - UUID - */ - public void setPlayerUUID(final UUID uuid) { - uniqueId = uuid.toString(); - } - - /** - * Clears all home Locations - */ - public void clearHomeLocations() { - homeLocations.clear(); - } - - /** - * @return the locale - */ - public String getLocale() { - return locale; - } - - /** - * @param locale the locale to set - */ - public void setLocale(String locale) { - this.locale = locale; - } - - /** - * @return the deaths - */ - public int getDeaths() { - return deaths; - } - - /** - * @param deaths the deaths to set - */ - public void setDeaths(int deaths) { - this.deaths = deaths; - } - - /** - * Add death - */ - public void addDeath() { - deaths++; - if (deaths > getPlugin().getSettings().getDeathsMax()) { - deaths = getPlugin().getSettings().getDeathsMax(); - } - } - - /** - * Can invite or still waiting for cool down to end - * - * @param location - the location - * to check - * @return number of mins/hours left until cool down ends - */ - public long getInviteCoolDownTime(Location location) { - // Check the hashmap - if (location != null && kickedList.containsKey(location)) { - // plugin.getLogger().info("DEBUG: Location is known"); - // The location is in the list - // Check the date/time - Date kickedDate = new Date(kickedList.get(location)); - // plugin.getLogger().info("DEBUG: kicked date = " + kickedDate); - Calendar coolDownTime = Calendar.getInstance(); - coolDownTime.setTime(kickedDate); - // coolDownTime.add(Calendar.HOUR_OF_DAY, Settings.inviteWait); - coolDownTime.add(Calendar.MINUTE, getPlugin().getSettings().getInviteWait()); - // Add the invite cooldown period - Calendar timeNow = Calendar.getInstance(); - // plugin.getLogger().info("DEBUG: date now = " + timeNow); - if (coolDownTime.before(timeNow)) { - // The time has expired - kickedList.remove(location); - return 0; - } else { - // Still not there yet - // long hours = (coolDownTime.getTimeInMillis() - - // timeNow.getTimeInMillis())/(1000 * 60 * 60); - // Temp minutes - return (coolDownTime.getTimeInMillis() - timeNow.getTimeInMillis()) / (1000 * 60); - } - } - return 0; - } - - /** - * Starts the invite cooldown timer for location. Location should be the center of an island. - * @param location - the location - */ - public void startInviteCoolDownTimer(Location location) { - if (location != null) { - kickedList.put(location, System.currentTimeMillis()); - } - } - - @Override - public String getUniqueId() { - return uniqueId; - } - - @Override - public void setUniqueId(String uniqueId) { - this.uniqueId = uniqueId; - } - -} From eb4e08648262f28a7e4760a79f8044658b12ceb2 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 12 Mar 2018 04:41:45 +0900 Subject: [PATCH 4/5] Renamed variable. Removed logger. --- .../bskyblock/database/mysql/MySQLDatabaseHandler.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java index f0d8a7963..ca6d2ec17 100644 --- a/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java +++ b/src/main/java/us/tastybento/bskyblock/database/mysql/MySQLDatabaseHandler.java @@ -41,7 +41,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { */ private Connection connection = null; - private BSkyBlock plugin; + private BSkyBlock bskyblock; /** * Handles the connection to the database and creation of the initial database schema (tables) for @@ -52,7 +52,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { */ public MySQLDatabaseHandler(BSkyBlock plugin, Class type, DatabaseConnecter databaseConnecter) { super(plugin, type, databaseConnecter); - this.plugin = plugin; + this.bskyblock = plugin; connection = databaseConnecter.createConnection(); // Check if the table exists in the database and if not, create it createSchema(); @@ -82,7 +82,7 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { // Register adapters builder.registerTypeAdapter(Location.class, new LocationAdapter(plugin)) ; builder.registerTypeAdapter(World.class, new WorldAdapter(plugin)); - builder.registerTypeAdapter(Flag.class, new FlagAdapter(plugin)); + builder.registerTypeAdapter(Flag.class, new FlagAdapter(bskyblock)); builder.registerTypeAdapter(PotionEffectType.class, new PotionEffectTypeAdapter()); // Keep null in the database builder.serializeNulls(); @@ -154,8 +154,6 @@ public class MySQLDatabaseHandler extends AbstractDatabaseHandler { preparedStatement.execute(); } catch (SQLException e) { plugin.getLogger().severe(() -> "Could not save object " + instance.getClass().getName() + " " + e.getMessage()); - // Leave in for now until we know there are no particular issues - e.printStackTrace(); } } From 1ec288e93b1055802be87cd23de7f02e925f3ef4 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 12 Mar 2018 04:42:08 +0900 Subject: [PATCH 5/5] Added sonarqube to travis. --- .travis.yml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0b35e0651..032717375 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,19 @@ -# Source language and JDK version to use. language: java -jdk: oraclejdk8 +sudo: false +install: true before_install: - - sed -i.bak -e 's|https://nexus.codehaus.org/snapshots/|https://oss.sonatype.org/content/repositories/codehaus-snapshots/|g' ~/.m2/settings.xml - +- sed -i.bak -e 's|https://nexus.codehaus.org/snapshots/|https://oss.sonatype.org/content/repositories/codehaus-snapshots/|g' + ~/.m2/settings.xml +addons: + sonarcloud: + organization: "tastybento-github" + token: + secure: "uJDH+Af1VSjvaOLOmA5KPofLnfqD7Y4j6bWnQ+mwuaJfOCG6D1SYeXg6LuBCLX2yXc9Li3fDQRxu9Xp521ph3kCfbeJLhQTMxoVDIS+3KY4FXrc+zEdg6G7hRsDD2iGCRtZjI52O7NdwWxUqftXIUFJjYoL0H2zJGJzF85dxOXFtve5CGIPkuvnZp+mUim+b0LBUpV1xjBU/8I0Q121FXxp0GH3Wi0SiAoU484bjOtbjEFvuJvVru45WtikJOKKabrxO5uoKqHPrjKdkkRrvKw6fBMpQ5b+/kXR3OWvi9Ap0IWKMkBQBq4R7xhaIVQVIkfwTPg6rl21nLKYHl679HzqpAozxR1CsDs9dXDj22ERRjjp9rKT5O1UHifCmLgOtB+hRKSDhipOayqRnxRhMYOzvniDcZ0Dr4jgsstY7VG2TFtuX7yLb//SqV+41XuClmtvX69vRD+iL5v0FR52ymf8x43ZnouxT+iAcLrfsm7ICOP8FmOQSGi0NkRhKc+6f+STy47kHzZgdzlQqJtmP6s7eXoGtOv4RuPlMoFMXyOba7SYtcxbUgg7dKiL4iHRB4+GX46uWl1hilWKCbkZrv6/u9SZsOQXwMC409qG5/G7aRC1ZZ/vlCMZz8+/QbNHGO0enhzeKD5mkZy6coi/c/9gYK0eXV0+s/gXHU9hx5v4=" +jdk: + - oraclejdk8 +script: + - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar +cache: + directories: + - '$HOME/.m2/repository' + - '$HOME/.sonar/cache' \ No newline at end of file