diff --git a/pom.xml b/pom.xml index da3f8b9..1008849 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ org.spigotmc spigot-api - 1.12-R0.1-SNAPSHOT + 1.13-R0.1-SNAPSHOT provided diff --git a/src/main/java/de/iani/treasurechest/TreasureChest.java b/src/main/java/de/iani/treasurechest/TreasureChest.java index ef77380..ddcaeff 100644 --- a/src/main/java/de/iani/treasurechest/TreasureChest.java +++ b/src/main/java/de/iani/treasurechest/TreasureChest.java @@ -21,6 +21,7 @@ import de.iani.playerUUIDCache.CachedPlayer; import de.iani.playerUUIDCache.PlayerUUIDCache; +import de.iani.playerUUIDCache.SQLConfig; import net.milkbowl.vault.economy.Economy; public class TreasureChest extends JavaPlugin implements TreasureChestAPI { @@ -38,8 +39,12 @@ private PlayerUUIDCache playerUUIDCache; + private SQLConfig sqlConfig; + @Override public void onEnable() { + saveDefaultConfig(); + random = new Random(); setupEconomy(); // uuidTools = new PlayerUUIDTools(this); @@ -127,6 +132,7 @@ } } } + sqlConfig = new SQLConfig(config.getConfigurationSection("database")); } @Override @@ -193,6 +199,10 @@ saveConfig(); } + public SQLConfig getSqlConfig() { + return sqlConfig; + } + private void setupEconomy() { try { RegisteredServiceProvider economyProvider = getServer().getServicesManager().getRegistration(net.milkbowl.vault.economy.Economy.class); diff --git a/src/main/java/de/iani/treasurechest/TreasureChestCommandExecutor.java b/src/main/java/de/iani/treasurechest/TreasureChestCommandExecutor.java index 3db067d..08c10d4 100644 --- a/src/main/java/de/iani/treasurechest/TreasureChestCommandExecutor.java +++ b/src/main/java/de/iani/treasurechest/TreasureChestCommandExecutor.java @@ -35,11 +35,15 @@ } Player player = ((Player) sender); Block target = player.getTargetBlock((Set) null, 5); - plugin.setChestLocation(target.getLocation()); - player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); - player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); - player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); - plugin.sendMessage(sender, "Treasurechest location set!"); + plugin.setChestLocation(target == null ? null : target.getLocation()); + if (target != null) { + player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); + player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); + player.playEffect(target.getLocation(), Effect.MOBSPAWNER_FLAMES, null); + plugin.sendMessage(sender, "Treasurechest location set!"); + } else { + plugin.sendMessage(sender, "Treasurechest location removed!"); + } } else if (subcommand.equals("create")) { if (!checkPlayer(sender) || !checkPermission(sender, "treasurechest.create")) { return true; diff --git a/src/main/java/de/iani/treasurechest/database/SQLConfig.java b/src/main/java/de/iani/treasurechest/database/SQLConfig.java new file mode 100644 index 0000000..5f052a9 --- /dev/null +++ b/src/main/java/de/iani/treasurechest/database/SQLConfig.java @@ -0,0 +1,45 @@ +package de.iani.treasurechest.database; + +import org.bukkit.configuration.ConfigurationSection; + +public class SQLConfig { + private String host = "localhost"; + + private String user = "CHANGETHIS"; + + private String password = "CHANGETHIS"; + + private String database = "CHANGETHIS"; + + private String tableprefix = "treasurechest"; + + public SQLConfig(ConfigurationSection section) { + if (section != null) { + host = section.getString("host", host); + user = section.getString("user", user); + password = section.getString("password", password); + database = section.getString("database", database); + tableprefix = section.getString("tableprefix", tableprefix); + } + } + + public String getHost() { + return host; + } + + public String getUser() { + return user; + } + + public String getPassword() { + return password; + } + + public String getDatabase() { + return database; + } + + public String getTablePrefix() { + return tableprefix; + } +} diff --git a/src/main/java/de/iani/treasurechest/database/TreasureChestDatabase.java b/src/main/java/de/iani/treasurechest/database/TreasureChestDatabase.java new file mode 100644 index 0000000..23b056f --- /dev/null +++ b/src/main/java/de/iani/treasurechest/database/TreasureChestDatabase.java @@ -0,0 +1,142 @@ +package de.iani.treasurechest.database; + +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.UUID; + +import de.iani.playerUUIDCache.CachedPlayer; +import de.iani.playerUUIDCache.PlayerUUIDCache; +import de.iani.treasurechest.TreasureChestItem; +import de.iani.treasurechest.util.sql.MySQLConnection; +import de.iani.treasurechest.util.sql.SQLConnection; +import de.iani.treasurechest.util.sql.SQLRunnable; + +public class TreasureChestDatabase { + private final SQLConnection connection; + + private final String tableName; + + private final String insertContent; + + private final String selectContent; + + private final String removeContent; + + private final String removeOldContent; + + public TreasureChestDatabase(SQLConfig config) throws SQLException { + connection = new MySQLConnection(config.getHost(), config.getDatabase(), config.getUser(), config.getPassword()); + this.tableName = config.getTablePrefix() + "_content"; + + insertContent = "INSERT INTO " + tableName + " (uuid, time, displayItem, content) VALUES (?, ?, ?, ?)"; + + selectContent = "SELECT id, uuid, time, displayItem, content FROM " + tableName + " WHERE uuid = ?"; + + removeContent = "DELETE FROM " + tableName + " WHERE id = ? AND uuid = ?"; + + removeOldContent = "DELETE FROM " + tableName + " WHERE time < ?"; + + this.connection.runCommands(new SQLRunnable() { + @Override + public Void execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + if (!sqlConnection.hasTable(tableName)) { + Statement smt = connection.createStatement(); + smt.executeUpdate("CREATE TABLE `" + tableName + "` ("// + + "`id` INT NOT NULL,"// + + "`uuid` CHAR( 36 ) NOT NULL,"// + + "`time` BIGINT NOT NULL,"// + + "`displayItem` MEDIUMTEXT ,"// + + "`content` MEDIUMTEXT ,"// + + "PRIMARY KEY ( `id` ), INDEX ( `uuid` ) ) ENGINE = innodb"); + smt.close(); + } + return null; + } + }); + } + + public void disconnect() { + connection.disconnect(); + } + + public void addItem(final UUID owner, final String displayItem, final String content) throws SQLException { + this.connection.runCommands(new SQLRunnable() { + @Override + public Void execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + PreparedStatement smt = sqlConnection.getOrCreateStatement(insertContent); + + smt.setString(1, owner.toString()); + smt.setLong(2, System.currentTimeMillis()); + smt.setString(3, displayItem); + smt.setString(4, content); + smt.executeUpdate(); + return null; + } + }); + + } + + public ArrayList getPlayerItems(final UUID uuid) throws SQLException { + return this.connection.runCommands(new SQLRunnable>() { + @Override + public ArrayList execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + PreparedStatement smt = sqlConnection.getOrCreateStatement(selectContent); + smt.setString(1, uuid.toString()); + ResultSet rs = smt.executeQuery(); + while (rs.next()) { + // TODO + } + rs.close(); + return null; + } + }); + } + + public CachedPlayer getPlayer(final String name) throws SQLException { + return this.connection.runCommands(new SQLRunnable() { + @Override + public CachedPlayer execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + String realName = name; + PreparedStatement smt = sqlConnection.getOrCreateStatement(selectPlayerByName); + smt.setString(1, name); + ResultSet rs = smt.executeQuery(); + + UUID uuid = null; + long time = Long.MIN_VALUE; + while (rs.next()) { + long thisTime = rs.getLong(3); + if (thisTime > time) { + try { + uuid = UUID.fromString(rs.getString(1)); + realName = rs.getString(2); + time = thisTime; + } catch (IllegalArgumentException e) { + // ignore invalid uuid + } + } + } + rs.close(); + if (uuid != null) { + return new CachedPlayer(uuid, realName, time, System.currentTimeMillis()); + } + return null; + } + }); + } + + public void deleteOldPlayerProfiles() throws SQLException { + this.connection.runCommands(new SQLRunnable() { + @Override + public Void execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + PreparedStatement smt = sqlConnection.getOrCreateStatement(deleteOldPlayerProfiles); + smt.setLong(1, System.currentTimeMillis() - PlayerUUIDCache.PROFILE_PROPERTIES_CACHE_EXPIRATION_TIME); + smt.executeUpdate(); + return null; + } + }); + } +} diff --git a/src/main/java/de/iani/treasurechest/util/sql/MySQLConnection.java b/src/main/java/de/iani/treasurechest/util/sql/MySQLConnection.java new file mode 100644 index 0000000..bb2d00b --- /dev/null +++ b/src/main/java/de/iani/treasurechest/util/sql/MySQLConnection.java @@ -0,0 +1,9 @@ +package de.iani.treasurechest.util.sql; + +import java.sql.SQLException; + +public class MySQLConnection extends SQLConnection { + public MySQLConnection(String host, String database, String user, String password) throws SQLException { + super("jdbc:mysql://" + host + "/" + database, database, user, password, "com.mysql.jdbc.Driver"); + } +} diff --git a/src/main/java/de/iani/treasurechest/util/sql/SQLConnection.java b/src/main/java/de/iani/treasurechest/util/sql/SQLConnection.java new file mode 100644 index 0000000..02a8011 --- /dev/null +++ b/src/main/java/de/iani/treasurechest/util/sql/SQLConnection.java @@ -0,0 +1,160 @@ +package de.iani.treasurechest.util.sql; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.HashMap; + +public class SQLConnection { + private String connectURL; + + private Connection connection; + + private String user; + + private String password; + + private String database; + + private int maxTries; + + private HashMap cachedStatements; + + public SQLConnection(String connectURL, String database, String user, String password) throws SQLException { + this(connectURL, database, user, password, null); + } + + public SQLConnection(String connectURL, String database, String user, String password, String driverClass) throws SQLException { + if (driverClass != null) { + try { + Class.forName(driverClass); + } catch (Exception e) { + throw new SQLException(e); + } + } + this.connectURL = connectURL; + this.user = user; + this.password = password; + this.database = database; + this.cachedStatements = new HashMap(); + this.maxTries = 3; + connect(); + } + + public synchronized void disconnect() { + if (this.connection != null) { + try { + this.connection.close(); + } catch (SQLException e) { + // ignore + } + this.connection = null; + } + this.cachedStatements.clear(); + } + + private synchronized void connect() throws SQLException { + disconnect(); + this.connection = user != null ? DriverManager.getConnection(connectURL, user, password) : DriverManager.getConnection(connectURL); + this.connection.setAutoCommit(false); + } + + public synchronized T runCommands(SQLRunnable runnable) throws SQLException { + int fails = 0; + while (true) { + try { + if (connection == null) { + connect(); + } + T rv = runnable.execute(connection, this); + connection.commit(); + return rv; + } catch (SQLException e) { + fails += 1; + if (connection != null) { + try { + if (!connection.isClosed()) { + connection.rollback(); + } + } catch (SQLException ex) { + // ignore + } + try { + connection.close(); + } catch (SQLException ex) { + // ignore + } + connection = null; + } + if (fails >= maxTries) { + throw e; + } + } + } + } + + public PreparedStatement getOrCreateStatement(String statement) throws SQLException { + PreparedStatement smt = cachedStatements.get(statement); + if (smt == null || smt.isClosed()) { + smt = connection.prepareStatement(statement); + cachedStatements.put(statement, smt); + } + return smt; + } + + public PreparedStatement getOrCreateStatement(String statement, int autoGeneratedKeys) throws SQLException { + PreparedStatement smt = cachedStatements.get(statement); + if (smt == null || smt.isClosed()) { + smt = connection.prepareStatement(statement, autoGeneratedKeys); + cachedStatements.put(statement, smt); + } + return smt; + } + + public boolean hasTable(final String table) throws SQLException { + return hasTable(this.database, table); + } + + public boolean hasTable(final String database, final String table) throws SQLException { + return runCommands(new SQLRunnable() { + @Override + public Boolean execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + PreparedStatement smt = sqlConnection.getOrCreateStatement("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?"); + smt.setString(1, database); + smt.setString(2, table); + ResultSet rs = smt.executeQuery(); + boolean rv = false; + if (rs.next()) { + rv = rs.getInt(1) > 0; + } + rs.close(); + return rv; + } + }); + } + + public boolean hasColumn(final String table, final String column) throws SQLException { + return hasColumn(this.database, table, column); + } + + public boolean hasColumn(final String database, final String table, final String column) throws SQLException { + return runCommands(new SQLRunnable() { + @Override + public Boolean execute(Connection connection, SQLConnection sqlConnection) throws SQLException { + PreparedStatement smt = sqlConnection.getOrCreateStatement("SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = ? AND table_name = ? AND column_name = ?"); + smt.setString(1, database); + smt.setString(2, table); + smt.setString(3, column); + ResultSet rs = smt.executeQuery(); + boolean rv = false; + if (rs.next()) { + rv = rs.getInt(1) > 0; + } + rs.close(); + return rv; + } + }); + } +} diff --git a/src/main/java/de/iani/treasurechest/util/sql/SQLRunnable.java b/src/main/java/de/iani/treasurechest/util/sql/SQLRunnable.java new file mode 100644 index 0000000..2d225cb --- /dev/null +++ b/src/main/java/de/iani/treasurechest/util/sql/SQLRunnable.java @@ -0,0 +1,8 @@ +package de.iani.treasurechest.util.sql; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface SQLRunnable { + public T execute(Connection connection, SQLConnection sqlConnection) throws SQLException; +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..fd686a8 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,6 @@ +database: + host: localhost + user: CHANGETHIS + password: CHANGETHIS + database: CHANGETHIS + tableprefix: treasurechest \ No newline at end of file