From 13dbf9ae3838039c12daaae13b2d502790ae7e81 Mon Sep 17 00:00:00 2001 From: Kyrobi Date: Thu, 24 Oct 2024 01:10:39 -0500 Subject: [PATCH 1/2] SQLite Storage Option Implements another option for storing the data. Currently, one of the pain points of flat files is that FTP/SFTP transfer of the files take a long time. When moving server files around, it's often that transferring the data takes up a significant portion of the time. However, with MySQL most shared providers have limited amount of SQL databases you can create, so if existing plugins already require it, then you don't be about to use the MySQL storage. Additionally, it would be nice to have a self-contained storage like SQLite instead of having to rely on a separate database server. --- pom.xml | 3 +- .../java/net/naturva/morphie/mr/Commands.java | 6 + .../net/naturva/morphie/mr/MorphRedeem.java | 9 +- .../morphie/mr/events/PlayerFileEvent.java | 7 +- .../mr/events/chat/RedeemChatEvent.java | 25 +-- .../naturva/morphie/mr/util/DataManager.java | 16 +- .../mr/util/Database/SQLiteConnection.java | 155 ++++++++++++++++++ src/main/resources/config.yml | 2 +- 8 files changed, 200 insertions(+), 23 deletions(-) create mode 100644 src/main/java/net/naturva/morphie/mr/util/Database/SQLiteConnection.java diff --git a/pom.xml b/pom.xml index a1fd7d4..47a9008 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ me.clip placeholderapi - 2.10.9 + 2.11.6 provided @@ -83,5 +83,6 @@ 4.1.0 compile + \ No newline at end of file diff --git a/src/main/java/net/naturva/morphie/mr/Commands.java b/src/main/java/net/naturva/morphie/mr/Commands.java index e72267e..b5d35a8 100644 --- a/src/main/java/net/naturva/morphie/mr/Commands.java +++ b/src/main/java/net/naturva/morphie/mr/Commands.java @@ -31,6 +31,12 @@ public Commands(MorphRedeem plugin) { public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { if (cmd.getName().equalsIgnoreCase("morphredeem") || cmd.getName().equalsIgnoreCase("redeem") || cmd.getName().equalsIgnoreCase("mr")) { if (args.length == 0) { + + if(!(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "This command can only be used by the player. Did you mean " + ChatColor.AQUA + "/mr help?"); + return true; + } + Player player = (Player)sender; if (!player.isSleeping()) { if (sender.hasPermission("morphredeem.redeem")) { diff --git a/src/main/java/net/naturva/morphie/mr/MorphRedeem.java b/src/main/java/net/naturva/morphie/mr/MorphRedeem.java index c3f1ed3..d960e69 100644 --- a/src/main/java/net/naturva/morphie/mr/MorphRedeem.java +++ b/src/main/java/net/naturva/morphie/mr/MorphRedeem.java @@ -8,6 +8,7 @@ import net.naturva.morphie.mr.events.JoinEvent; import net.naturva.morphie.mr.util.Database.RedisConnection; +import net.naturva.morphie.mr.util.Database.SQLiteConnection; import net.naturva.morphie.mr.util.StringUtils; import net.naturva.morphie.mr.util.UpdateChecker; import org.bukkit.Bukkit; @@ -62,13 +63,17 @@ public void onEnable() { getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bVersion&8: &a" + this.Version)); createConfig(); loadConfigManager(); - if (this.getConfig().getString("StorageMethod").equals("MySQL")) { + if (this.getConfig().getString("StorageMethod").equalsIgnoreCase("MySQL")) { new MySQLConnection(this).mysqlSetup(); new MySQLConnection(this).checkStructure(); getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bStorage Type&8: &aMySQL")); - } else if(this.getConfig().getString("StorageMethod").equals("Redis")) { + } else if(this.getConfig().getString("StorageMethod").equalsIgnoreCase("Redis")) { new RedisConnection(this).auth(); getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bStorage Type&8: &aRedis")); + } else if (this.getConfig().getString("StorageMethod").equalsIgnoreCase("SQLite")) { + new SQLiteConnection(this).sqliteSetup(); + new SQLiteConnection(this).checkStructure(); + getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bStorage Type&8: &aSQLite")); } else { getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bStorage Type&8: &aYML")); } diff --git a/src/main/java/net/naturva/morphie/mr/events/PlayerFileEvent.java b/src/main/java/net/naturva/morphie/mr/events/PlayerFileEvent.java index 3b9872d..0762da2 100644 --- a/src/main/java/net/naturva/morphie/mr/events/PlayerFileEvent.java +++ b/src/main/java/net/naturva/morphie/mr/events/PlayerFileEvent.java @@ -5,6 +5,7 @@ import java.util.UUID; import java.util.logging.Level; +import net.naturva.morphie.mr.util.Database.SQLiteConnection; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -30,9 +31,11 @@ public void onJoin(PlayerJoinEvent e) { Player player = e.getPlayer(); UUID uuid = player.getUniqueId(); - if (this.plugin.getConfig().getString("StorageMethod").equals("MySQL")) { + if (this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("MySQL")) { new MySQLConnection(this.plugin).createPlayer(uuid, player); - } else if(!this.plugin.getConfig().getString("StorageMethod").equals("Redis")) { + } else if (this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("SQLite")) { + new SQLiteConnection(this.plugin).createPlayer(uuid, player); + } else if(!this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("Redis")) { new BukkitRunnable() { public void run() { File file = getData(uuid); diff --git a/src/main/java/net/naturva/morphie/mr/events/chat/RedeemChatEvent.java b/src/main/java/net/naturva/morphie/mr/events/chat/RedeemChatEvent.java index b0c55fa..c66c5a1 100644 --- a/src/main/java/net/naturva/morphie/mr/events/chat/RedeemChatEvent.java +++ b/src/main/java/net/naturva/morphie/mr/events/chat/RedeemChatEvent.java @@ -3,6 +3,7 @@ import java.util.UUID; import net.naturva.morphie.mr.util.StringUtils; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -75,18 +76,18 @@ public void onChat(AsyncPlayerChatEvent e) { } player.sendMessage(new StringUtils().addColor(this.plugin.getMessage("ErrorPrefix") + message)); } else { - new DataManager(plugin).updateData(uuid, +amountToAdd, "Credits_Spent", "add"); - new DataManager(plugin).updateData(uuid, -amountToAdd, "Credits", "remove"); - - ExperienceAPI.addLevel(player, skill, amountToAdd); - String message = this.plugin.getMessage("CreditAssignmentSuccess"); - if (message.contains("%SKILL%")) { - message = message.replaceAll("%SKILL%", skill); - } - if (message.contains("%CREDITS%")) { - message = message.replaceAll("%CREDITS%", "" + amountToAdd); - } - player.sendMessage(new StringUtils().addColor(this.plugin.getMessage("Prefix") + message)); + new DataManager(plugin).updateData(uuid, +amountToAdd, "Credits_Spent", "add"); + new DataManager(plugin).updateData(uuid, -amountToAdd, "Credits", "remove"); + + ExperienceAPI.addLevel(player, skill, amountToAdd); + String message = this.plugin.getMessage("CreditAssignmentSuccess"); + if (message.contains("%SKILL%")) { + message = message.replaceAll("%SKILL%", skill); + } + if (message.contains("%CREDITS%")) { + message = message.replaceAll("%CREDITS%", "" + amountToAdd); + } + player.sendMessage(new StringUtils().addColor(this.plugin.getMessage("Prefix") + message)); } } } diff --git a/src/main/java/net/naturva/morphie/mr/util/DataManager.java b/src/main/java/net/naturva/morphie/mr/util/DataManager.java index a150812..b859c9e 100644 --- a/src/main/java/net/naturva/morphie/mr/util/DataManager.java +++ b/src/main/java/net/naturva/morphie/mr/util/DataManager.java @@ -6,6 +6,7 @@ import net.naturva.morphie.mr.files.PlayerFileMethods; import net.naturva.morphie.mr.util.Database.MySQLConnection; import net.naturva.morphie.mr.util.Database.RedisConnection; +import net.naturva.morphie.mr.util.Database.SQLiteConnection; public class DataManager { @@ -16,20 +17,25 @@ public DataManager(MorphRedeem plugin) { } public String getData(UUID uuid, String name) { - if (this.plugin.getConfig().getString("StorageMethod").equals("MySQL")) { + if (this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("MySQL")) { return new MySQLConnection(this.plugin).getData(uuid, name); - } else if(this.plugin.getConfig().getString("StorageMethod").equals("Redis")){ + } else if(this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("Redis")){ return new RedisConnection(this.plugin).getData(uuid, name); - } else { + } else if(this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("SQLite")){ + return new SQLiteConnection(this.plugin).getData(uuid, name); + } + else { return new PlayerFileMethods(plugin).getStat(uuid, name); } } public void updateData(UUID uuid, int data, String name, String type) { - if (this.plugin.getConfig().getString("StorageMethod").equals("MySQL")) { + if (this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("MySQL")) { new MySQLConnection(this.plugin).updateData(uuid, data, name, type); - } else if(this.plugin.getConfig().getString("StorageMethod").equals("Redis")){ + } else if(this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("Redis")){ new RedisConnection(this.plugin).updateData(uuid, data, name, type); + } else if (this.plugin.getConfig().getString("StorageMethod").equalsIgnoreCase("SQLite")) { + new SQLiteConnection(this.plugin).updateData(uuid, data, name, type); } else if (type == "add" || type == "remove"){ new PlayerFileMethods(plugin).updateCredits(uuid, name, data); } else if (type == "set") { diff --git a/src/main/java/net/naturva/morphie/mr/util/Database/SQLiteConnection.java b/src/main/java/net/naturva/morphie/mr/util/Database/SQLiteConnection.java new file mode 100644 index 0000000..e5cb97d --- /dev/null +++ b/src/main/java/net/naturva/morphie/mr/util/Database/SQLiteConnection.java @@ -0,0 +1,155 @@ +package net.naturva.morphie.mr.util.Database; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.sql.*; +import java.util.UUID; + +import org.bukkit.entity.Player; + +import net.naturva.morphie.mr.MorphRedeem; + +public class SQLiteConnection { + private static MorphRedeem plugin; + private static String tablePrefix; + private static String databasePath; + + public SQLiteConnection(MorphRedeem plugin) { + SQLiteConnection.plugin = plugin; + tablePrefix = SQLiteConnection.plugin.getConfig().getString("SQLite.TablePrefix", "mr_"); + } + + public void sqliteSetup() { + try { + synchronized (this) { + File dbFile = new File(plugin.getDataFolder(), "database.db"); + + // Create parent directories if they don't exist + if (!dbFile.getParentFile().exists()) { + dbFile.getParentFile().mkdirs(); + } + + // Create the database file if it doesn't exist + if (!dbFile.exists()) { + try { + dbFile.createNewFile(); + } catch (IOException e) { + throw new SQLException("Could not create database file", e); + } + } + + databasePath = dbFile.getPath(); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void checkStructure() { + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath)) { + if (connection == null) { + return; + } + + String sql = "CREATE TABLE IF NOT EXISTS " + this.tablePrefix + "creditdata (" + + "uuid TEXT NULL DEFAULT NULL, " + + "credits INTEGER NOT NULL DEFAULT 0, " + + "credits_spent INTEGER NOT NULL DEFAULT 0, " + + "UNIQUE(uuid)" + + ");"; + + PreparedStatement statement = connection.prepareStatement(sql); + + statement.executeUpdate(); + statement.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public boolean playerExists(UUID uuid) { + String query = "SELECT 1 FROM `" + tablePrefix + "creditdata` WHERE uuid=?"; + + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath); + PreparedStatement statement = connection.prepareStatement(query)) { + + statement.setString(1, uuid.toString()); + + try (ResultSet results = statement.executeQuery()) { + return results.next(); + } + + } catch (SQLException e) { + e.printStackTrace(); + } + + return false; + } + + public void createPlayer(final UUID uuid, Player player) { + String insertQuery = "INSERT INTO `" + tablePrefix + "creditdata` (uuid, credits, credits_spent) VALUES (?, ?, ?)"; + + // Use a try-with-resources block to manage connection and statement + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath); + PreparedStatement insert = connection.prepareStatement(insertQuery)) { + + if (!playerExists(uuid)) { + insert.setString(1, uuid.toString()); + insert.setInt(2, 0); // Starting credits + insert.setInt(3, 0); // Starting credits spent + insert.executeUpdate(); + + } + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void updateData(UUID uuid, int num, String column, String type) { + String sql = "UPDATE `" + tablePrefix + "creditdata` SET " + column.toLowerCase() + "=? WHERE uuid=?"; + + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath); + PreparedStatement statement = connection.prepareStatement(sql)) { + + int data = Integer.parseInt(getData(uuid, column)); + + if (type.equalsIgnoreCase("set")) { + statement.setInt(1, num); + } else if (type.equalsIgnoreCase("add") || type.equalsIgnoreCase("remove")) { + statement.setInt(1, data + num); + } + statement.setString(2, uuid.toString()); + statement.executeUpdate(); + + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getData(UUID uuid, String data) { + String sql = "SELECT credits, credits_spent FROM `" + tablePrefix + "creditdata` WHERE uuid=?"; + + try (Connection connection = DriverManager.getConnection("jdbc:sqlite:" + databasePath); + PreparedStatement statement = connection.prepareStatement(sql)) { + + statement.setString(1, uuid.toString()); + + try (ResultSet results = statement.executeQuery()){ + if (results.next()) { + if (data.equals("Credits")) { + return String.valueOf(results.getInt("credits")); + } else if (data.equals("Credits_Spent")) { + return String.valueOf(results.getInt("credits_spent")); + } + } + } + + } catch (SQLException e) { + e.printStackTrace(); + } + return "0"; + } +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 20a1a23..9a81bb0 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -76,7 +76,7 @@ Settings: #============================================================================================================================| -# Storage method, can be MySQL, Redis, or YML +# Storage method, can be MySQL, Redis, SQLite, or YML StorageMethod: "YML" # Requires 'StorageMethod' to be MySQL From c082f016faeed3347a1778a8deff8d2f3ddc16ef Mon Sep 17 00:00:00 2001 From: Kyrobi Date: Wed, 30 Oct 2024 10:27:49 -0500 Subject: [PATCH 2/2] Don't check for updates when turned off / on main thread The plugin still makes the http call to check for the update even while the update checker setting is turned off. It just won't display the result. Moved the check off the main thread or else the server will stall when there's something wrong with the http connection. If there is slow connection or the spigot website is down, the server will stall or crash. --- .../net/naturva/morphie/mr/MorphRedeem.java | 18 +++++++++------- .../naturva/morphie/mr/events/JoinEvent.java | 21 +++++++++++-------- target/classes/config.yml | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/main/java/net/naturva/morphie/mr/MorphRedeem.java b/src/main/java/net/naturva/morphie/mr/MorphRedeem.java index d960e69..2272fb6 100644 --- a/src/main/java/net/naturva/morphie/mr/MorphRedeem.java +++ b/src/main/java/net/naturva/morphie/mr/MorphRedeem.java @@ -86,15 +86,17 @@ public void onEnable() { getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&bPlugin Status&8: &aEnabled")); getServer().getConsoleSender().sendMessage(new StringUtils().addColor("&8[----------[&3MorphRedeem&8]----------]")); - UpdateChecker updater = new UpdateChecker(this); - try { - if (updater.checkForUpdates()) { - if (this.getConfig().getBoolean("Settings.UpdateChecker")) { - Bukkit.getConsoleSender().sendMessage(new StringUtils().addColor(this.getMessage("Prefix") + this.getMessage("UpdateMessage").replace("%VERSION%", new UpdateChecker(this).getLatestVersion()).replace("%LINK%", new UpdateChecker(this).getResourceURL()))); + if (this.getConfig().getBoolean("Settings.UpdateChecker")) { + Bukkit.getScheduler().runTaskAsynchronously(this, ()->{ + UpdateChecker updater = new UpdateChecker(this); + try { + if (updater.checkForUpdates()) { + Bukkit.getConsoleSender().sendMessage(new StringUtils().addColor(this.getMessage("Prefix") + this.getMessage("UpdateMessage").replace("%VERSION%", new UpdateChecker(this).getLatestVersion()).replace("%LINK%", new UpdateChecker(this).getResourceURL()))); + } + } catch (Exception e) { + e.printStackTrace(); } - } - } catch (Exception e) { - e.printStackTrace(); + }); } } diff --git a/src/main/java/net/naturva/morphie/mr/events/JoinEvent.java b/src/main/java/net/naturva/morphie/mr/events/JoinEvent.java index f6d66c5..78707b5 100644 --- a/src/main/java/net/naturva/morphie/mr/events/JoinEvent.java +++ b/src/main/java/net/naturva/morphie/mr/events/JoinEvent.java @@ -3,6 +3,7 @@ import net.naturva.morphie.mr.MorphRedeem; import net.naturva.morphie.mr.util.StringUtils; import net.naturva.morphie.mr.util.UpdateChecker; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -18,17 +19,19 @@ public JoinEvent(MorphRedeem plugin) { @EventHandler public void onJoin(PlayerJoinEvent e) { Player player = e.getPlayer(); - UpdateChecker updater = new UpdateChecker(plugin); - try { - if (updater.checkForUpdates()) { - if (this.plugin.getConfig().getBoolean("Settings.UpdateChecker")) { - if (player.hasPermission("morphredeem.admin") || player.hasPermission("morphredeem.updateChecker")) { - player.sendMessage(new StringUtils().addColor(plugin.getMessage("Prefix") + plugin.getMessage("UpdateMessage").replace("%VERSION%", new UpdateChecker(plugin).getLatestVersion()).replace("%LINK%", new UpdateChecker(plugin).getResourceURL()))); + if (this.plugin.getConfig().getBoolean("Settings.UpdateChecker")) { + Bukkit.getScheduler().runTaskAsynchronously(plugin, ()->{ + UpdateChecker updater = new UpdateChecker(plugin); + try { + if (updater.checkForUpdates()) { + if (player.hasPermission("morphredeem.admin") || player.hasPermission("morphredeem.updateChecker")) { + player.sendMessage(new StringUtils().addColor(plugin.getMessage("Prefix") + plugin.getMessage("UpdateMessage").replace("%VERSION%", new UpdateChecker(plugin).getLatestVersion()).replace("%LINK%", new UpdateChecker(plugin).getResourceURL()))); + } } + } catch (Exception e1) { + e1.printStackTrace(); } - } - } catch (Exception e1) { - e1.printStackTrace(); + }); } } } diff --git a/target/classes/config.yml b/target/classes/config.yml index 20a1a23..9a81bb0 100644 --- a/target/classes/config.yml +++ b/target/classes/config.yml @@ -76,7 +76,7 @@ Settings: #============================================================================================================================| -# Storage method, can be MySQL, Redis, or YML +# Storage method, can be MySQL, Redis, SQLite, or YML StorageMethod: "YML" # Requires 'StorageMethod' to be MySQL