package de.tobiyas.racesandclasses.saving.serializer; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Statement; import java.util.Collection; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.UUID; import org.bukkit.scheduler.BukkitRunnable; import de.tobiyas.racesandclasses.RacesAndClasses; import de.tobiyas.racesandclasses.saving.PlayerSavingData; import de.tobiyas.util.file.IOUtils; import de.tobiyas.util.schedule.DebugBukkitRunnable; import de.tobiyas.util.sql.SQL; import de.tobiyas.util.sql.SQL.SQLProperties; public class DatabasePlayerDataSerializer implements PlayerDataSerializer { private static final String TABLE_NAME = "RacesAndClasses"; /** * The Plugin to use. */ private final RacesAndClasses plugin; /** * Use the connection here! */ private Connection connection; public DatabasePlayerDataSerializer(RacesAndClasses plugin) { this.plugin = plugin; } @Override public void saveData(PlayerSavingData data) { boolean sync = RacesAndClasses.isBukkitInShutdownMode(); saveData(data, sync); } /** * Saves the Data. Either sync or async. * @param data to save. * @param sync if sync or async. */ public void saveData(final PlayerSavingData data, boolean sync){ BukkitRunnable runnable = new DebugBukkitRunnable("LoadRaCData") { @Override protected void runIntern() { Statement statement = null; try{ checkConnection(); if(connection == null) { RacesAndClasses.getPlugin().logWarning("Could not open DB connection! Please change to YML stuff!"); return; } statement = connection.createStatement(); String sqlUpdateStatement = "REPLACE INTO `" + TABLE_NAME + "` " + "(id,lastLogin,lastName,race,class,level,exp,godMode,additionalData) " + " VALUES (" + "'" + data.getPlayerId().toString() + "'," + data.getLastLogin() + "," + "'" + data.getLastName() + "'," + "'" + data.getRaceName() + "'," + "'" + data.getClassName() + "'," + data.getLevel() + "," + data.getLevelExp() + "," + (data.isGodMode() ? 1 : 0) + "," + "'" + data.getAdditionalJsonData().replace("'", "\\'") + "'" + ")"; statement.execute(sqlUpdateStatement); }catch(Throwable exp){ exp.printStackTrace(); } finally { //Finnaly close! try{ statement.close(); }catch(Throwable exp){} } } }; if(sync) runnable.run(); else runnable.runTaskAsynchronously(plugin); } @Override public void loadData(final UUID id, final PlayerDataLoadedCallback callback) { new DebugBukkitRunnable("LoadRaCData") { @Override protected void runIntern() { PlayerSavingData data = loadDataNow(id); callback.playerDataLoaded(data); } }.runTaskAsynchronously(plugin); } @Override public Collection<PlayerSavingData> bulkLoadDataNow(Set<UUID> ids) { //First build the Query! Set<PlayerSavingData> datas = new HashSet<>(); StringBuilder selectQueryBuilder = new StringBuilder(); selectQueryBuilder.append("SELECT * FROM `").append(TABLE_NAME).append("` WHERE id IN ("); boolean first = true; for(UUID id : ids){ if(!first) selectQueryBuilder.append(","); first = false; selectQueryBuilder.append("'").append(id).append("'"); } selectQueryBuilder.append(")"); //Now execute! Connection connection = null; Statement statement = null; ResultSet result = null; try{ //Create own connection to not block other connection. connection = SQL.getSQLConnection(getProperties()); String query = selectQueryBuilder.toString(); checkConnection(); statement = connection.createStatement(); result = statement.executeQuery(query); while(result.next()){ PlayerSavingData data = analyseCurrentResultLine(result); if(data != null) datas.add(data); } }catch(Throwable exp){ exp.printStackTrace(); }finally { IOUtils.closeQuietly(statement); IOUtils.closeQuietly(result); IOUtils.closeQuietly(connection); } return datas; } @Override public void bulkLoadData(final Set<UUID> ids, final PlayerDataLoadedCallback callback){ new DebugBukkitRunnable("LoadRaCData") { @Override protected void runIntern() { for(PlayerSavingData data : bulkLoadDataNow(ids)){ callback.playerDataLoaded(data); } } }.runTaskAsynchronously(plugin); } /** * Reads current Line. * * @param set to analyse * @return the read data. */ private PlayerSavingData analyseCurrentResultLine(ResultSet resultSet){ try{ int columns = resultSet.getMetaData().getColumnCount(); if(columns < 9) { RacesAndClasses.getPlugin().logError("Something on the DB is broken!"); return null; } //Remember that counting starts by 1 with columns! UUID id = UUID.fromString(resultSet.getString(1)); long lastLogin = resultSet.getLong(2); String lastName = resultSet.getString(3); String raceName = resultSet.getString(4); String className = resultSet.getString(5); int level = resultSet.getInt(6); int exp = resultSet.getInt(7); boolean god = resultSet.getInt(8) == 1; String additionalData = resultSet.getString(9); PlayerSavingData data = new PlayerSavingData(id, lastLogin, lastName, raceName, className, level, exp, god, null, null); data.setAdditionalJsonData(additionalData); data.unserializeJsonData(); return data; }catch(Throwable exp){ return null; } } @Override public PlayerSavingData loadDataNow(UUID id) { PlayerSavingData data = null; Statement statement = null; ResultSet resultSet = null; try{ checkConnection(); if(connection == null) { RacesAndClasses.getPlugin().logWarning("Could not open DB connection! Please change to YML stuff!"); return null; } statement = connection.createStatement(); String sqlSelectStatement = "SELECT * FROM `" + TABLE_NAME + "` WHERE " + " id='" + id.toString() + "'"; resultSet = statement.executeQuery(sqlSelectStatement); //No result! if(!resultSet.next()) return null; data = analyseCurrentResultLine(resultSet); }catch(Throwable exp){ exp.printStackTrace(); } finally { //Finnaly close! try{ statement.close(); }catch(Throwable exp){} try{ resultSet.close(); }catch(Throwable exp){} } return data; } @Override public Set<UUID> getAllIDsPresent() { Set<UUID> ids = new HashSet<>(); Statement statement = null; ResultSet resultSet = null; try{ checkConnection(); if(connection == null) { RacesAndClasses.getPlugin().logWarning("Could not open DB connection! Please change to YML stuff!"); return null; } statement = connection.createStatement(); String sqlSelectStatement = "SELECT id FROM `" + TABLE_NAME + "`"; resultSet = statement.executeQuery(sqlSelectStatement); //Iterate over results: while(resultSet.next()){ try{ String id = resultSet.getString(1); UUID uID = UUID.fromString(id); if(id != null) ids.add(uID); }catch(Throwable exp){} } }catch(Throwable exp){ exp.printStackTrace(); }finally { //Finnaly close! try{ statement.close(); }catch(Throwable exp){} try{ resultSet.close(); }catch(Throwable exp){} } return ids; } @Override public void shutdown() { } /** * Gets all Properties. * @return properties. */ private SQLProperties getProperties(){ return RacesAndClasses.getPlugin().getConfigManager().getGeneralConfig().getConfig_databaseData(); } @Override public boolean isFunctional() { SQLProperties props = getProperties(); try{ //First tell the SQL where to log! SQL.init(plugin); //Now try to connect! Connection connection = SQL.getSQLConnection(props); if(connection == null) return false; boolean reachable = SQL.checkDBIsReachable(props); if(!reachable) return false; //Create the Table if not exist: String sqlCommand = "CREATE TABLE " + "IF NOT EXISTS " + "`RacesAndClasses`" +"(" + "`id` VARCHAR(64) NOT NULL," + "`lastLogin` BIGINT NOT NULL DEFAULT '0'," + "`lastName` VARCHAR(32) NOT NULL DEFAULT ''," + "`race` VARCHAR(32) NOT NULL DEFAULT ''," + "`class` VARCHAR(32) NOT NULL DEFAULT ''," + "`level` INT NOT NULL DEFAULT '0'," + "`exp` INT NOT NULL DEFAULT '0'," + "`godMode` TINYINT NOT NULL DEFAULT '0'," + "`additionalData` MEDIUMTEXT NOT NULL," + "PRIMARY KEY (`id`)" +")"; SQL.tryCreateDBIfNotExist(props, "", sqlCommand); //GENERATE_AND_UPLOAD_DATA(); return reachable; }catch(Throwable exp){ exp.printStackTrace(); return false; } } private void checkConnection(){ if(connection == null) try{ connection = SQL.getSQLConnection(getProperties()); }catch(Throwable exp){} } protected void GENERATE_AND_UPLOAD_DATA(){ for(int i = 0; i <= 10; i++){ StringBuilder queryBuilder = new StringBuilder(); queryBuilder.append("INSERT INTO `").append(TABLE_NAME).append("` (id,additionalData) VALUES "); for(int j = 0; j <= 10_000; j++){ queryBuilder.append("('").append(UUID.randomUUID()).append("','").append("{text=\"" + generateRandomString(200) + "\"}").append("')"); if(j != 10_000) queryBuilder.append(","); } checkConnection(); try{ Statement st = connection.createStatement(); st.execute(queryBuilder.toString()); st.close(); }catch(Throwable exp){ exp.printStackTrace(); } } } protected String generateRandomString(int length){ Random rand = new Random(); String set = ""; for(int i = 0; i < length; i++){ char cha = (char)('a'+rand.nextInt(20)); set+= cha; } return set; } }