From 12a8035b652ff2c6740c9516a704a828e2fc4665 Mon Sep 17 00:00:00 2001 From: Joshua Date: Sun, 13 Oct 2024 19:59:49 -0500 Subject: [PATCH] Out with the old, in with the new Updated user storage to focus more towards SQLite rather than YAML. --- .../storage/CommandStorage.java | 6 +- .../offlinecommands/storage/SoundStorage.java | 6 +- .../storage/StorageManager.java | 170 ++++++++++++++++++ .../offlinecommands/storage/StorageUtils.java | 74 -------- .../storage/UserConfigData.java | 82 --------- .../offlinecommands/storage/UserStorage.java | 30 +++- src/main/resources/user_data.yml | 1 - 7 files changed, 203 insertions(+), 166 deletions(-) create mode 100644 src/main/java/io/github/jochyoua/offlinecommands/storage/StorageManager.java delete mode 100644 src/main/java/io/github/jochyoua/offlinecommands/storage/StorageUtils.java delete mode 100644 src/main/java/io/github/jochyoua/offlinecommands/storage/UserConfigData.java delete mode 100644 src/main/resources/user_data.yml diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/CommandStorage.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/CommandStorage.java index 6c69d66..2f748cc 100644 --- a/src/main/java/io/github/jochyoua/offlinecommands/storage/CommandStorage.java +++ b/src/main/java/io/github/jochyoua/offlinecommands/storage/CommandStorage.java @@ -1,6 +1,5 @@ package io.github.jochyoua.offlinecommands.storage; -import com.google.common.eventbus.EventBus; import lombok.Builder; import lombok.Data; import lombok.extern.jackson.Jacksonized; @@ -35,6 +34,9 @@ public class CommandStorage implements ConfigurationSerializable { @Builder.Default private SoundStorage soundStorage = null; + @Builder.Default + private Boolean recurring = false; + /** * A static method that deserializes a map of strings and objects into a CommandStorage object. @@ -59,6 +61,7 @@ public static CommandStorage deserialize(Map map) { .orElse(DEFAULT_COMMAND.getRequiredPermission())) .soundStorage(Optional.ofNullable((SoundStorage) map.get("soundStorage")) .orElse(DEFAULT_COMMAND.getSoundStorage())) + .recurring((Boolean) map.getOrDefault("recurring", false)) .build(); } @@ -78,6 +81,7 @@ public Map serialize() { map.put("message", message); map.put("requiredPermission", requiredPermission); map.put("soundStorage", soundStorage); + map.put("recurring", recurring); return map; } diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/SoundStorage.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/SoundStorage.java index e4c6865..39944e5 100644 --- a/src/main/java/io/github/jochyoua/offlinecommands/storage/SoundStorage.java +++ b/src/main/java/io/github/jochyoua/offlinecommands/storage/SoundStorage.java @@ -1,8 +1,10 @@ package io.github.jochyoua.offlinecommands.storage; +import io.github.jochyoua.offlinecommands.VariableConstants; import lombok.Builder; import lombok.Data; import lombok.extern.jackson.Jacksonized; +import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.configuration.serialization.ConfigurationSerializable; import org.bukkit.entity.Player; @@ -16,7 +18,7 @@ public class SoundStorage implements ConfigurationSerializable { @Builder.Default - private Sound sound = Sound.BLOCK_NOTE_BLOCK_CHIME; + private Sound sound = Bukkit.getVersion().contains("1.8") ? Sound.valueOf("NOTE_PIANO") : Sound.BLOCK_NOTE_BLOCK_CHIME; @Builder.Default private float volume = 1.0F; @@ -38,7 +40,7 @@ public static SoundStorage deserialize(Map map) { try { sound = Sound.valueOf(soundName); } catch (IllegalArgumentException ignored) { - sound = Sound.BLOCK_NOTE_BLOCK_CHIME; + sound = VariableConstants.DEFAULT_SOUND.getSound(); } return SoundStorage.builder().sound(sound).volume(volume).pitch(pitch).build(); diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageManager.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageManager.java new file mode 100644 index 0000000..f7b46f5 --- /dev/null +++ b/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageManager.java @@ -0,0 +1,170 @@ +package io.github.jochyoua.offlinecommands.storage; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.jochyoua.offlinecommands.OfflineCommands; + +import java.io.File; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + + +public class StorageManager { + private static final ObjectMapper objectMapper = new ObjectMapper(); + private final String url; + private Connection connection; + + public StorageManager(OfflineCommands offlineCommands) { + this.url = "jdbc:sqlite:" + new File(offlineCommands.getDataFolder(), "user_database.db").getAbsolutePath(); + } + + /** + * Initializes the database by creating the UserStorage table if it does not exist. + * + * @throws SQLException if a database access error occurs + */ + public void initializeDatabase() throws SQLException { + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement()) { + String createUserStorageTable = "CREATE TABLE IF NOT EXISTS UserStorage (" + + "uuid TEXT PRIMARY KEY, " + + "username TEXT, " + + "commands TEXT" + + ")"; + stmt.execute(createUserStorageTable); + } + } + + /** + * Retrieves a connection to the database. If the current connection is + * closed or null, a new connection is established. + * + * @return a connection to the SQLite database + * @throws SQLException if a database access error occurs + */ + private Connection getConnection() throws SQLException { + if (connection == null || connection.isClosed()) { + connection = DriverManager.getConnection(url); + } + return connection; + } + + public void closeConnection() { + if (connection != null) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + /** + * Retrieves a UserStorage object for the specified UUID. + * + * @param uuid the UUID of the user + * @return the UserStorage object, or null if the user is not found + * @throws SQLException if a database access error occurs + * @throws JsonProcessingException if an error occurs while processing JSON + */ + public UserStorage getUser(UUID uuid) throws SQLException, JsonProcessingException { + String sql = "SELECT * FROM UserStorage WHERE uuid = ?"; + try (Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, uuid.toString()); + ResultSet rs = pstmt.executeQuery(); + if (rs.next()) { + List commands = objectMapper.readValue(rs.getString("commands"), new TypeReference>() { + }); + return UserStorage.builder() + .uuid(UUID.fromString(rs.getString("uuid"))) + .username(rs.getString("username")) + .commands(commands) + .build(); + } + } + return null; + } + + /** + * Adds or updates a user in the database. + * + * @param userStorage the UserStorage object to add or update + * @throws SQLException if a database access error occurs + * @throws JsonProcessingException if an error occurs while processing JSON + */ + public void addOrUpdateUser(UserStorage userStorage) throws SQLException, JsonProcessingException { + String sql = "INSERT OR REPLACE INTO UserStorage(uuid, username, commands) VALUES(?, ?, ?)"; + try (Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, userStorage.getUuid().toString()); + pstmt.setString(2, userStorage.getUsername()); + pstmt.setString(3, objectMapper.writeValueAsString(userStorage.getCommands())); + pstmt.executeUpdate(); + } + } + + public CommandStorage getCommandFromDatabase(String commandIdentifier) throws SQLException, JsonProcessingException { + String sql = "SELECT * FROM UserStorage"; + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + List commands = objectMapper.readValue(rs.getString("commands"), new TypeReference>() { + }); + for (CommandStorage command : commands) { + if (command.getIdentifier().equalsIgnoreCase(commandIdentifier)) { + return command; + } + } + } + } + return null; + } + + + /** + * Removes a user from the database. + * + * @param uuid the UUID of the user to remove + * @throws SQLException if a database access error occurs + */ + public void removeUser(UUID uuid) throws SQLException { + String sql = "DELETE FROM UserStorage WHERE uuid = ?"; + try (Connection conn = getConnection(); + PreparedStatement pstmt = conn.prepareStatement(sql)) { + pstmt.setString(1, uuid.toString()); + pstmt.executeUpdate(); + } + } + + /** + * Retrieves a list of all UserStorage objects from the database. + * + * @return a list of UserStorage objects + * @throws SQLException if a database access error occurs + * @throws JsonProcessingException if an error occurs while processing JSON + */ + public List getUserStorageList() throws SQLException, JsonProcessingException { + List userStorageList = new ArrayList<>(); + String sql = "SELECT * FROM UserStorage"; + try (Connection conn = getConnection(); + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + List commands = objectMapper.readValue(rs.getString("commands"), new TypeReference>() { + }); + UserStorage userStorage = UserStorage.builder() + .uuid(UUID.fromString(rs.getString("uuid"))) + .username(rs.getString("username")) + .commands(commands) + .build(); + userStorageList.add(userStorage); + } + } + return userStorageList; + } +} diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageUtils.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageUtils.java deleted file mode 100644 index 942ba29..0000000 --- a/src/main/java/io/github/jochyoua/offlinecommands/storage/StorageUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.github.jochyoua.offlinecommands.storage; - -import org.bukkit.configuration.file.FileConfiguration; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.UUID; - -import static io.github.jochyoua.offlinecommands.VariableConstants.USERS_KEY; - - -public class StorageUtils { - - private StorageUtils() { - throw new UnsupportedOperationException("Cannot instantiate utility class."); - } - - /** - * A static method that returns a user storage that matches a given UUID from a user list stored in a file configuration. - * It uses a lambda stream to process the user list and find the matching user storage. - * It uses the USERS_KEY constant to access the user list from the file configuration, and the UserStorage class to represent each user. - * It also uses the equalsIgnoreCase method of the string class to compare the UUIDs. - * If it finds a matching user storage, it returns it. Otherwise, it returns null. - * - * @param config a file configuration that contains a user list - * @param uuid a UUID that identifies the user to be searched - * @return a user storage that matches the UUID, or null if none matches - */ - public static UserStorage getUser(FileConfiguration config, UUID uuid) { - return ((List) config.getList(USERS_KEY)).stream().filter(userStorage -> userStorage.getUuid().toString().equalsIgnoreCase(uuid.toString())).findFirst().orElse(null); - } - - /** - * A static method that adds or updates a user in a user list stored in a file configuration. - * It takes a file configuration and a user storage as parameters, and returns a new list of user storages with the added or updated user. - * It uses the USERS_KEY constant to access the user list from the file configuration, and the UserStorage class to represent each user. - * It also uses the StorageUtils class to get the old user that matches the UUID of the user storage, and removes it from the list if it exists. - * It then checks if the user storage has a commands field, and sets it to an empty list if it is null. - * Finally, it adds the user storage to the list and returns it. - * - * @param config a file configuration that contains a user list - * @param userStorage a user storage that represents the user to be added or updated - * @return a new list of user storages with the added or updated user - */ - public static List addOrUpdateUserList(FileConfiguration config, UserStorage userStorage) { - List userStorageList = new ArrayList<>((Collection) config.getList(USERS_KEY)); - UserStorage oldUser = StorageUtils.getUser(config, userStorage.getUuid()); - if (oldUser != null) { - userStorageList.removeIf(removedUser -> removedUser.getUuid().toString().equalsIgnoreCase(userStorage.getUuid().toString())); - } - if (userStorage.getCommands() == null) { - userStorage.setCommands(new ArrayList<>()); - } - userStorageList.add(userStorage); - return userStorageList; - } - - /** - * A static method that removes a user from a user list stored in a file configuration. - * It takes a file configuration and a UUID as parameters, and returns a new list of user storages without the user that matches the UUID. - * It uses the USERS_KEY constant to access the user list from the file configuration, and the UserStorage class to represent each user. - * It also uses the removeIf method of the list to filter out the user that matches the UUID. - * - * @param config a file configuration that contains a user list - * @param uuid a UUID that identifies the user to be removed - * @return a new list of user storages without the user that matches the UUID - */ - public static List removeUserFromUserList(FileConfiguration config, UUID uuid) { - List userStorageList = new ArrayList<>((Collection) config.getList(USERS_KEY)); - userStorageList.removeIf(removedUser -> removedUser.getUuid().equals(uuid)); - return userStorageList; - } -} diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/UserConfigData.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/UserConfigData.java deleted file mode 100644 index 9dfa05f..0000000 --- a/src/main/java/io/github/jochyoua/offlinecommands/storage/UserConfigData.java +++ /dev/null @@ -1,82 +0,0 @@ -package io.github.jochyoua.offlinecommands.storage; - -import io.github.jochyoua.offlinecommands.OfflineCommands; -import lombok.Getter; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import static io.github.jochyoua.offlinecommands.VariableConstants.USERS_KEY; - -public class UserConfigData { - - private final OfflineCommands offlineCommands; - @Getter - private FileConfiguration userStorageConfig; - - public UserConfigData(OfflineCommands offlineCommands) { - this.offlineCommands = offlineCommands; - loadConfig(); - } - - public void reloadConfig() { - this.userStorageConfig = null; - loadConfig(); - } - - /** - * A method that loads the user data from a file configuration. - * It checks if the userStorageData field is null, and if so, it creates a new file configuration from the users_data.yml file in the offlineCommands data folder. - * If the users_data.yml file does not exist, it tries to create it and set an empty list as the value of the USERS_KEY constant in the file configuration. - * It also saves the file configuration to the users_data.yml file after setting the value. - * If an IOException occurs while creating or saving the file configuration, it prints the stack trace of the exception. - */ - public void loadConfig() { - if (userStorageConfig == null) { - File usersDataFile = new File(offlineCommands.getDataFolder(), "users_data.yml"); - - if (!usersDataFile.exists()) { - try { - if (!usersDataFile.createNewFile()) { - offlineCommands.getLogger().warning("Failed to create user_data.yml file, do you have the correct permissions?"); - } - userStorageConfig = YamlConfiguration.loadConfiguration(usersDataFile); - userStorageConfig.set(USERS_KEY, new ArrayList<>()); - userStorageConfig.save(usersDataFile); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - userStorageConfig = YamlConfiguration.loadConfiguration(usersDataFile); - } - } - } - - /** - * A synchronized method that modifies the user data stored in a file configuration. - * It takes a list of user storages as a parameter, and sets it as the value of the USERS_KEY constant in the file configuration. - * It also saves the file configuration to the users_data.yml file in the offlineCommands data folder. - * If the file configuration is null, it logs a warning message and returns without modifying the data. - * If an IOException occurs while saving the file configuration, it prints the stack trace of the exception. - * - * @param userStorageList a list of user storages that represents the new user data - */ - public synchronized void modifyUsersData(List userStorageList) { - if (userStorageConfig != null) { - userStorageConfig.set(USERS_KEY, userStorageList); - } else { - offlineCommands.getLogger().warning("users_data.yml is null, unable to modify data."); - return; - } - - try { - userStorageConfig.save(new File(offlineCommands.getDataFolder(), "users_data.yml")); - } catch (IOException e) { - e.printStackTrace(); - } - } -} diff --git a/src/main/java/io/github/jochyoua/offlinecommands/storage/UserStorage.java b/src/main/java/io/github/jochyoua/offlinecommands/storage/UserStorage.java index 3aa8e51..88ae4bf 100644 --- a/src/main/java/io/github/jochyoua/offlinecommands/storage/UserStorage.java +++ b/src/main/java/io/github/jochyoua/offlinecommands/storage/UserStorage.java @@ -9,6 +9,7 @@ import org.bukkit.entity.Player; import java.util.*; +import java.util.stream.Collectors; @Data @Builder @@ -30,8 +31,14 @@ public class UserStorage implements ConfigurationSerializable { * @return a new UserStorage object with the values from the map */ public static UserStorage deserialize(Map map) { - List commands = new ArrayList<>(((List>) map.get("commands")).stream().map(CommandStorage::deserialize).toList()); - return UserStorage.builder().uuid(UUID.fromString((String) map.get("uuid"))).username((String) map.get("username")).commands(commands).build(); + List commands = ((List>) map.get("commands")).stream() + .map(CommandStorage::deserialize) + .collect(Collectors.toList()); + return UserStorage.builder() + .uuid(UUID.fromString((String) map.get("uuid"))) + .username((String) map.get("username")) + .commands(commands) + .build(); } /** @@ -47,7 +54,7 @@ public Map serialize() { Map map = new HashMap<>(); map.put("uuid", uuid.toString()); map.put("username", username); - map.put("commands", commands.stream().map(CommandStorage::serialize).toList()); + map.put("commands", commands.stream().map(CommandStorage::serialize).collect(Collectors.toList())); return map; } @@ -61,8 +68,12 @@ public Map serialize() { * @return a command storage that matches the identifier, or null if none matches */ public CommandStorage getCommand(String identifier) { - Optional offlineCommandOptional = commands.stream().filter(offlineCommand -> offlineCommand.getIdentifier().equalsIgnoreCase(identifier)).findFirst(); - return offlineCommandOptional.orElse(null); + for (CommandStorage command : commands) { + if (command.getIdentifier().equalsIgnoreCase(identifier)) { + return command; + } + } + return null; } /** @@ -73,8 +84,15 @@ public CommandStorage getCommand(String identifier) { * @param player a player that represents the target of the commands */ public void runAllCommands(Player player) { - for (CommandStorage command : this.getCommands()) { + Iterator iterator = this.getCommands().iterator(); + + while (iterator.hasNext()) { + CommandStorage command = iterator.next(); OfflineCommandsUtils.runCommandAsPlayer(player, command); + + if (!command.getRecurring()) { + iterator.remove(); + } } } } diff --git a/src/main/resources/user_data.yml b/src/main/resources/user_data.yml deleted file mode 100644 index d8cbb6f..0000000 --- a/src/main/resources/user_data.yml +++ /dev/null @@ -1 +0,0 @@ -users: [ ] \ No newline at end of file