diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 162fbbbc..c3c27b2e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,10 +41,17 @@ jobs: gh release create "$TAG" \ "plugin/target/displayentityutils-$TAG.jar" \ --verify-tag \ - --title "DEU $TAG" \ + --title "$TAG" \ --notes-file "docs/release-notes/$TAG.md" \ --generate-notes + - name: Trigger Build on Jitpack + background: true + continue-on-error: true + run: | + curl -L --fail --retry 3 --retry-delay 7 \ + "https://jitpack.io/com/github/PZDonny/DisplayEntityUtils/${TAG}/build.log" + - name: Get Release body id: release_notes env: @@ -61,7 +68,7 @@ jobs: with: token: ${{ secrets.MODRINTH_TOKEN }} project: ${{ vars.MODRINTH_ID }} - name: DEU ${{ env.TAG }} + name: ${{ env.TAG }} version: ${{ env.TAG }} changelog: ${{ steps.release_notes.outputs.body }} loaders: |- diff --git a/api/pom.xml b/api/pom.xml index ff4df2a4..cba5ccea 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.7.0 + 3.7.1 api diff --git a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java index bfd95dd3..1b9f41b2 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java +++ b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java @@ -1,6 +1,6 @@ package net.donnypz.displayentityutils; -import net.donnypz.displayentityutils.managers.DisplayStorage; +import net.donnypz.displayentityutils.database.DisplayStorage; import net.donnypz.displayentityutils.managers.LoadMethod; import net.donnypz.displayentityutils.utils.DisplayEntities.AnimationPlayer; import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDEConversionHandler; @@ -12,6 +12,9 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.util.HashMap; +import java.util.Map; + public final class DisplayAPI { public static final Component pluginPrefix = Component.text("[DisplayEntityUtils] ", NamedTextColor.YELLOW); @@ -29,9 +32,7 @@ public final class DisplayAPI { private static final String legacyPartTagPrefix = "deu.parttag_"; - static DisplayStorage LOCAL_STORAGE; - static DisplayStorage MYSQL_STORAGE; - static DisplayStorage MONGODB_STORAGE; + static Map storages = new HashMap<>(); static AnimationPlayer.AnimationPlayerProvider ANIMATION_PLAYER_SERVICE; static BDEConversionHandler BDE_CONVERSION_HANDLER; static Scheduler SCHEDULER; @@ -113,20 +114,7 @@ public static boolean isEssentialsInstalled() { } public static DisplayStorage getStorage(@NotNull LoadMethod method){ - switch(method){ - case LOCAL -> { - return LOCAL_STORAGE; - } - case MYSQL -> { - return MYSQL_STORAGE; - } - case MONGODB -> { - return MONGODB_STORAGE; - } - default -> { - return null; - } - } + return storages.get(method); } public static Scheduler getScheduler(){ diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayStorage.java b/api/src/main/java/net/donnypz/displayentityutils/database/DisplayStorage.java similarity index 94% rename from api/src/main/java/net/donnypz/displayentityutils/managers/DisplayStorage.java rename to api/src/main/java/net/donnypz/displayentityutils/database/DisplayStorage.java index 14c1895d..5fb9d079 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayStorage.java +++ b/api/src/main/java/net/donnypz/displayentityutils/database/DisplayStorage.java @@ -1,4 +1,4 @@ -package net.donnypz.displayentityutils.managers; +package net.donnypz.displayentityutils.database; import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; @@ -25,6 +25,4 @@ public interface DisplayStorage { @NotNull List getGroupTags(); @NotNull List getAnimationTags(); - - } diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraPlayerRemovedEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraPlayerRemovedEvent.java index 81486fff..10ae9f5c 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraPlayerRemovedEvent.java +++ b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraPlayerRemovedEvent.java @@ -1,7 +1,6 @@ package net.donnypz.displayentityutils.events; import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; -import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimator; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -12,8 +11,7 @@ import java.util.UUID; /** - * Called when a {@link DisplayAnimator} starts playing a {@link SpawnedDisplayAnimation}. - * This is called once on an animator of the type {@link DisplayAnimator.AnimationType#LOOP}. + * Called when a player is removed from their animation camera view. */ public class AnimationCameraPlayerRemovedEvent extends Event{ private static final HandlerList handlers = new HandlerList(); diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStartEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStartEvent.java index bc815463..37ca5fa7 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStartEvent.java +++ b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStartEvent.java @@ -16,8 +16,7 @@ import java.util.UUID; /** - * Called when a {@link DisplayAnimator} starts playing a {@link SpawnedDisplayAnimation}. - * This is called once on an animator of the type {@link DisplayAnimator.AnimationType#LOOP}. + * Called when an animation camera begins its camera path */ public class AnimationCameraStartEvent extends Event implements Cancellable { private static final HandlerList handlers = new HandlerList(); @@ -29,7 +28,12 @@ public class AnimationCameraStartEvent extends Event implements Cancellable { int startFrameId; private boolean isCancelled = false; - public AnimationCameraStartEvent(ActiveGroup group, DisplayAnimator animator, SpawnedDisplayAnimation animation, Collection players, int startFrameId, UUID cameraUUID){ + public AnimationCameraStartEvent(ActiveGroup group, + DisplayAnimator animator, + SpawnedDisplayAnimation animation, + Collection players, + int startFrameId, + UUID cameraUUID){ super(!Bukkit.isPrimaryThread()); this.group = group; this.animation = animation; diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStopEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStopEvent.java index 8d1428df..4fdd6a27 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStopEvent.java +++ b/api/src/main/java/net/donnypz/displayentityutils/events/AnimationCameraStopEvent.java @@ -14,8 +14,7 @@ import java.util.UUID; /** - * Called when a {@link DisplayAnimator} starts playing a {@link SpawnedDisplayAnimation}. - * This is called once on an animator of the type {@link DisplayAnimator.AnimationType#LOOP}. + * Called when an animation camera completes its path */ public class AnimationCameraStopEvent extends Event{ private static final HandlerList handlers = new HandlerList(); diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java index 28675070..c823e2c8 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java +++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java @@ -81,9 +81,22 @@ public static void deselectSpawnedAnimation(@NotNull Player player){ * @return boolean whether the save was successful */ public static boolean saveDisplayAnimation(@NotNull LoadMethod loadMethod, @NotNull DisplayAnimation displayAnimation, @Nullable Player saver){ - if (displayAnimation.getAnimationTag() == null || !loadMethod.isEnabled()){ + if (!loadMethod.isEnabled()){ + if (saver != null) { + saver.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to save animation", NamedTextColor.RED))); + saver.sendMessage(Component.text("| Storage location is not enabled.", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } + return false; + } + + if (displayAnimation.getAnimationTag() == null) { + if (saver != null) { + saver.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to save animation", NamedTextColor.RED))); + saver.sendMessage(Component.text("| The animation does not have a tag.", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } return false; } + boolean success = DisplayAPI.getStorage(loadMethod).saveDisplayAnimation(displayAnimation, saver); if (success){ attemptCacheAnimation(displayAnimation.getAnimationTag(), displayAnimation.toSpawnedDisplayAnimation()); @@ -104,7 +117,7 @@ public static boolean saveDisplayAnimationJson(@NotNull DisplayAnimation display if (!DisplayConfig.overwritexistingSaves()){ if (saver != null){ saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save animation JSON locally!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); + saver.sendMessage(Component.text("| Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); } return false; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java index 9749ab29..4cc55575 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java +++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java @@ -428,9 +428,22 @@ public static boolean isGroupRegistered(@NotNull SpawnedDisplayEntityGroup spawn * @return boolean whether the save was successful */ public static boolean saveDisplayEntityGroup(@NotNull LoadMethod loadMethod, @NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver) { - if (displayEntityGroup.getTag() == null || !loadMethod.isEnabled()) { + if (!loadMethod.isEnabled()){ + if (saver != null) { + saver.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to save display entity group", NamedTextColor.RED))); + saver.sendMessage(Component.text("| Storage location is not enabled.", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } + return false; + } + + if (displayEntityGroup.getTag() == null) { + if (saver != null) { + saver.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to save display entity group", NamedTextColor.RED))); + saver.sendMessage(Component.text("| The group does not have a tag.", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } return false; } + return DisplayAPI.getStorage(loadMethod).saveDisplayEntityGroup(displayEntityGroup, saver); } diff --git a/docs/release-notes/3.7.1.md b/docs/release-notes/3.7.1.md new file mode 100644 index 00000000..633fcadb --- /dev/null +++ b/docs/release-notes/3.7.1.md @@ -0,0 +1,18 @@ +**Release Highlights** +- Updated config ⚙ +- Database optimizations 🐇 +- Database security fixes 🔒 + +## Config Changes +- Added additional config settings for `mongodb`, simplifying connections + +## API Changes +> [!NOTE] +>**New Version:** `3.7.1` + +### Breaking Changes +- Moved `DisplayStorage` into `net.donnypz.displayentityutils.database` package + +## Bug Fixes / QOL +- Improved queries for `MongoDB` storage +- Implemented changes to prevent SQL injection \ No newline at end of file diff --git a/plugin/pom.xml b/plugin/pom.xml index 89a3cf69..9e57c9c9 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.7.0 + 3.7.1 plugin @@ -102,7 +102,6 @@ org.mongodb mongodb-driver-sync - 5.1.2 @@ -114,7 +113,7 @@ com.zaxxer HikariCP - 5.1.0 + 7.1.0 @@ -153,4 +152,16 @@ + + + + org.mongodb + mongodb-driver-bom + 5.11.1 + pom + import + + + + \ No newline at end of file diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/ConfigUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/ConfigUtils.java index 6e524e62..382e2e47 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/ConfigUtils.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/ConfigUtils.java @@ -1,8 +1,9 @@ package net.donnypz.displayentityutils; +import net.donnypz.displayentityutils.database.MYSQLDisplayStorage; +import net.donnypz.displayentityutils.database.MongoDisplayStorage; import net.donnypz.displayentityutils.managers.DisplayAnimationManager; -import net.donnypz.displayentityutils.managers.MYSQLManager; -import net.donnypz.displayentityutils.managers.MongoManager; +import net.donnypz.displayentityutils.managers.LoadMethod; import net.donnypz.displayentityutils.managers.PluginFolders; import net.donnypz.displayentityutils.utils.controller.DisplayController; import net.kyori.adventure.text.Component; @@ -54,25 +55,61 @@ static void read(FileConfiguration config){ if (config.getBoolean("mongodb.enabled")){ DisplayConfig.isMongoEnabled = true; String cString = config.getString("mongodb.connectionString"); + String host = config.getString("mongodb.host"); + int port = config.getInt("mongodb.port"); String databaseName = config.getString("mongodb.database"); + String username = config.getString("mongodb.username"); + String password = config.getString("mongodb.password"); String groupCollection = config.getString("mongodb.groupCollection"); String animationCollection = config.getString("mongodb.animationCollection"); - MongoManager.createConnection(cString, databaseName, groupCollection, animationCollection); + if (cString == null || cString.isBlank()){ + ((MongoDisplayStorage) DisplayAPI.getStorage(LoadMethod.MONGODB)).createConnection( + host, + port, + databaseName, + username, + password, + groupCollection, + animationCollection + ); + } + else{ + ((MongoDisplayStorage) DisplayAPI.getStorage(LoadMethod.MONGODB)).createConnection( + cString, + databaseName, + groupCollection, + animationCollection + ); + } + } if (config.getBoolean("mysql.enabled")){ DisplayConfig.isMYSQLEnabled = true; String username = config.getString("mysql.username"); String password = config.getString("mysql.password"); + if (!config.getString("mysql.connectionURL").isBlank()){ - MYSQLManager.createConnection(config.getString("mysql.connectionURL"), username, password); + String connectionUrl = config.getString("mysql.connectionURL"); + ((MYSQLDisplayStorage) DisplayAPI.getStorage(LoadMethod.MYSQL)).createConnection( + connectionUrl, + username, + password + ); } else{ String database = config.getString("mysql.database"); String host = config.getString("mysql.host"); int port = config.getInt("mysql.port"); boolean useSSL = config.getBoolean("mysql.useSSL"); - MYSQLManager.createConnection(host, port, database, username, password, useSSL); + ((MYSQLDisplayStorage) DisplayAPI.getStorage(LoadMethod.MYSQL)).createConnection( + host, + port, + database, + username, + password, + useSSL + ); } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java index 1e36db05..8ddd3113 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java @@ -5,6 +5,7 @@ import com.github.retrooper.packetevents.PacketEvents; import com.github.retrooper.packetevents.event.PacketListenerPriority; import net.donnypz.displayentityutils.command.DisplayEntityPluginCommand; +import net.donnypz.displayentityutils.database.*; import net.donnypz.displayentityutils.listeners.autogroup.DEULoadingListeners; import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; import net.donnypz.displayentityutils.listeners.entity.DEUEntityListener; @@ -14,9 +15,7 @@ import net.donnypz.displayentityutils.listeners.gizmo.DEUGizmoListener; import net.donnypz.displayentityutils.listeners.player.*; import net.donnypz.displayentityutils.listeners.player.essentials.DEUEssentialsListener; -import net.donnypz.displayentityutils.managers.LocalManager; -import net.donnypz.displayentityutils.managers.MYSQLManager; -import net.donnypz.displayentityutils.managers.MongoManager; +import net.donnypz.displayentityutils.managers.LoadMethod; import net.donnypz.displayentityutils.managers.PluginFolders; import net.donnypz.displayentityutils.skript.SkriptTypes; import net.donnypz.displayentityutils.skript.active.ActiveModule; @@ -51,6 +50,8 @@ import org.jetbrains.annotations.ApiStatus; import org.skriptlang.skript.addon.SkriptAddon; +import java.util.Map; + public final class DisplayEntityPlugin extends JavaPlugin implements Listener { SkriptAddon addon; @@ -67,9 +68,11 @@ public void onLoad() { @Override public void onEnable() { DisplayAPI.plugin = this; - DisplayAPI.LOCAL_STORAGE = new LocalManager(); - DisplayAPI.MONGODB_STORAGE = new MongoManager(); - DisplayAPI.MYSQL_STORAGE = new MYSQLManager(); + Map storages = DisplayAPI.storages; + storages.put(LoadMethod.LOCAL, new LocalDisplayStorage()); + storages.put(LoadMethod.MONGODB, new MongoDisplayStorage()); + storages.put(LoadMethod.MYSQL, new MYSQLDisplayStorage()); + DisplayAPI.ANIMATION_PLAYER_SERVICE = new AnimationPlayerProviderImpl(); DisplayAPI.BDE_CONVERSION_HANDLER = new BDEConversionHandlerImpl(); DisplayAPI.SCHEDULER = new SchedulerImpl(); @@ -93,8 +96,11 @@ public void onEnable() { @Override public void onDisable() { - MYSQLManager.closeConnection(); - MongoManager.closeConnection(); + for (DisplayStorage storage : DisplayAPI.storages.values()) { + if (storage instanceof DBDisplayStorage ds){ + ds.closeConnection(); + } + } } void checkFolia(){ @@ -196,8 +202,11 @@ public static void reloadPlugin(boolean isOnEnable){ PluginFolders.createLocalSaveFolders(plugin); if (!isOnEnable){ - MongoManager.closeConnection(); - MYSQLManager.closeConnection(); + for (DisplayStorage storage : DisplayAPI.storages.values()) { + if (storage instanceof DBDisplayStorage ds){ + ds.closeConnection(); + } + } } else{ plugin.saveDefaultConfig(); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/database/CommonDisplayStorageUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/database/CommonDisplayStorageUtils.java new file mode 100644 index 00000000..4eb94e9b --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/database/CommonDisplayStorageUtils.java @@ -0,0 +1,36 @@ +package net.donnypz.displayentityutils.database; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.zip.GZIPOutputStream; + +class CommonDisplayStorageUtils { + + static ByteArrayInputStream toByteArrayInputStream(Object entityObject) throws IOException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); + ObjectOutputStream objOut = new ObjectOutputStream(gzipOut); + objOut.writeObject(entityObject); + gzipOut.close(); + objOut.close(); + + byte[] data = byteOut.toByteArray(); + byteOut.close(); + return new ByteArrayInputStream(data); + } + + static byte[] toByteArray(Object entityObject) throws IOException { + ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); + GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); + ObjectOutputStream objOut = new ObjectOutputStream(gzipOut); + objOut.writeObject(entityObject); + gzipOut.close(); + objOut.close(); + + byte[] data = byteOut.toByteArray(); + byteOut.close(); + return data; + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/database/DBDisplayStorage.java b/plugin/src/main/java/net/donnypz/displayentityutils/database/DBDisplayStorage.java new file mode 100644 index 00000000..635eba9b --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/database/DBDisplayStorage.java @@ -0,0 +1,15 @@ +package net.donnypz.displayentityutils.database; + +public interface DBDisplayStorage extends DisplayStorage { + + /** + * Check if this Database store is connected + * @return a boolean + */ + boolean isConnected(); + + /** + * Close this active database connection + */ + void closeConnection(); +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/managers/LocalManager.java b/plugin/src/main/java/net/donnypz/displayentityutils/database/LocalDisplayStorage.java similarity index 95% rename from plugin/src/main/java/net/donnypz/displayentityutils/managers/LocalManager.java rename to plugin/src/main/java/net/donnypz/displayentityutils/database/LocalDisplayStorage.java index 01da25ab..550fdea2 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/managers/LocalManager.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/database/LocalDisplayStorage.java @@ -1,6 +1,9 @@ -package net.donnypz.displayentityutils.managers; +package net.donnypz.displayentityutils.database; import net.donnypz.displayentityutils.DisplayConfig; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; +import net.donnypz.displayentityutils.managers.PluginFolders; import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; import net.kyori.adventure.text.Component; @@ -16,7 +19,7 @@ import java.util.List; import java.util.zip.GZIPOutputStream; -public final class LocalManager implements DisplayStorage{ +public final class LocalDisplayStorage implements DisplayStorage { public boolean saveDisplayEntityGroup(@NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver){ try{ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/database/MYSQLDisplayStorage.java b/plugin/src/main/java/net/donnypz/displayentityutils/database/MYSQLDisplayStorage.java new file mode 100644 index 00000000..0de19e13 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/database/MYSQLDisplayStorage.java @@ -0,0 +1,333 @@ +package net.donnypz.displayentityutils.database; + +import com.zaxxer.hikari.HikariDataSource; +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.DisplayConfig; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.apache.commons.dbutils.DbUtils; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.sql.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +public final class MYSQLDisplayStorage implements DBDisplayStorage { + + private static final String GROUP_TABLE = "saved_displays"; + private static final String GROUP_COLUMN = "display_group"; + private static final String GROUP_DISPLAY_NAME = "display entity group"; + + private static final String ANIMATION_TABLE = "saved_animations"; + private static final String ANIMATION_COLUMN = "display_anim"; + private static final String ANIMATION_DISPLAY_NAME = "animation"; + + private static final String TAG_COLUMN = "tag"; + + + private boolean connected = false; + private HikariDataSource dataSource; + + public void createConnection( + String host, + int port, + String database, + String username, + String password, + boolean usessl + ){ + if (connected){ + return; + } + String url = "jdbc:mysql://"+host+":"+port+"/"+database+"?autoReconnect=true&allowMultiQueries=true&useSSL="+usessl; + createConnection(url, username, password); + } + + public void createConnection( + String url, + String username, + String password + ){ + if (connected){ + return; + } + DisplayAPI.getScheduler().runAsync(() -> { + try { + //Set Data Source + dataSource = new HikariDataSource(); + dataSource.setJdbcUrl(url); + dataSource.setUsername(username); + dataSource.setPassword(password); + + dataSource.setMinimumIdle(3); + dataSource.setMaximumPoolSize(6); + + //Test Connection + Connection connection = dataSource.getConnection(); + + + //Create Default Table + Statement statement = connection.createStatement(); + String groupTableSQL = "CREATE TABLE IF NOT EXISTS saved_displays(tag VARCHAR(128) UNIQUE, display_group BLOB)"; + statement.execute(groupTableSQL); + + String animTableSQL = "CREATE TABLE IF NOT EXISTS saved_animations(tag VARCHAR(128) UNIQUE, display_anim BLOB)"; + statement.execute(animTableSQL); + + + DbUtils.closeQuietly(statement); + DbUtils.closeQuietly(connection); + + Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully connected to MYSQL!"))); + connected = true; + } catch (SQLException e) { + e.printStackTrace(); + Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(Component.text("There was an error connecting to the MYSQL database", NamedTextColor.RED))); + closeConnection(); + } + }); + } + + @Override + public void closeConnection(){ + try{ + if (dataSource != null){ + dataSource.close(); + } + } + finally { + connected = false; + dataSource = null; + } + } + + /** + * Check whether MySQL is connected + * @return a boolean + */ + @Override + public boolean isConnected() { + return connected; + } + + private Connection getConnection(){ + try{ + return dataSource.getConnection(); + } + catch(SQLException e){ + throw new RuntimeException(e); + } + } + + @Override + public boolean saveDisplayEntityGroup(@NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver){ + String tag = displayEntityGroup.getTag(); + return saveEntity(tag, displayEntityGroup, GROUP_TABLE, GROUP_DISPLAY_NAME, saver); + } + + @Override + public void deleteDisplayEntityGroup(@NotNull String tag, @Nullable Player deleter){ + deleteEntity(tag, deleter, GROUP_TABLE, "display entity group"); + } + + @Override + public @Nullable DisplayEntityGroup getDisplayEntityGroup(@NotNull String tag){ + if (!isConnected()){ + return null; + } + Blob blob = getEntity(tag, GROUP_TABLE, GROUP_COLUMN); + if (blob == null) return null; + try{ + return DisplayGroupManager.getGroup(blob.getBinaryStream()); + } + catch(SQLException e){ + e.printStackTrace(); + return null; + } + } + + @Override + public boolean saveDisplayAnimation(@NotNull DisplayAnimation displayAnimation, @Nullable Player saver){ + String tag = displayAnimation.getAnimationTag(); + return saveEntity(tag, displayAnimation, ANIMATION_TABLE, ANIMATION_DISPLAY_NAME, saver); + } + + @Override + public void deleteDisplayAnimation(@NotNull String tag, @Nullable Player deleter){ + deleteEntity(tag, deleter, ANIMATION_TABLE, "animation"); + } + + @Override + public @Nullable DisplayAnimation getDisplayAnimation(@NotNull String tag) { + if (!isConnected()) return null; + + try { + Blob blob = getEntity(tag, ANIMATION_TABLE, ANIMATION_COLUMN); + return blob == null + ? null + : DisplayAnimationManager.getAnimation(blob.getBinaryStream()); + } + catch (SQLException e) { + e.printStackTrace(); + return null; + } + } + + @Override + public @NotNull List getGroupTags(){ + return getTags(GROUP_TABLE); + } + + @Override + public @NotNull List getAnimationTags(){ + return getTags(ANIMATION_TABLE); + } + + private boolean saveEntity(String tag, Object entity, String tableName, String displayName, Player saver){ + if (!isConnected()) return false; + String save = "INSERT INTO "+tableName+" VALUES(?, ?);"; + + try( + ByteArrayInputStream blobStream = CommonDisplayStorageUtils.toByteArrayInputStream(entity); + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(save); + ){ + + statement.setString(1, tag); + statement.setBlob(2, blobStream); + + if (hasEntity(tag, tableName, connection)){ + if (DisplayConfig.overwritexistingSaves()){ + deleteDisplayAnimation(tag, null); + } + else{ + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save "+displayName+" to MYSQL!")); + saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } + return false; + } + } + statement.executeUpdate(); + blobStream.close(); + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved "+displayName+" to MYSQL!")); + } + return true; + } + catch(SQLIntegrityConstraintViolationException e){ + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save "+displayName+" to MYSQL!")); + saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } + return false; + } + catch(SQLException | IOException e){ + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save "+displayName+" to MYSQL!")); + } + e.printStackTrace(); + return false; + } + } + + + + private void deleteEntity(String tag, Player deleter, String tableName, String displayName){ + if (!isConnected()) return; + + String delete = "DELETE FROM "+tableName+" WHERE "+TAG_COLUMN+" = ?;"; + try( + Connection connection = getConnection(); + PreparedStatement statement = connection.prepareStatement(delete); + ){ + + if (!hasEntity(tag, tableName, connection)){ + if (deleter != null){ + deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved "+displayName+" does not exist in MYSQL database!")); + } + return; + } + + statement.setString(1, tag); + statement.executeUpdate(delete); + if (deleter != null){ + deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted "+displayName+" from MYSQL database!")); + } + } + catch(SQLException e){ + e.printStackTrace(); + deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved "+displayName+" does not exist in MYSQL database!")); + } + } + + private Blob getEntity(String tag, String tableName, String columnName){ + PreparedStatement statement = null; + Connection connection = null; + try{ + connection = getConnection(); + String retrieve = "SELECT "+columnName+" FROM "+tableName+" WHERE "+TAG_COLUMN+" = ?;"; + statement = connection.prepareStatement(retrieve); + statement.setString(1, tag); + + ResultSet results = statement.executeQuery(); + + return results.next() + ? results.getBlob(columnName) + : null; + } + catch(SQLException e){ + e.printStackTrace(); + return null; + } + finally { + DbUtils.closeQuietly(statement); + DbUtils.closeQuietly(connection); + } + } + + private boolean hasEntity(String tag, String tableName, Connection connection){ + String retrieve = "SELECT 1 FROM "+tableName+" WHERE "+TAG_COLUMN+" = ?;"; + + try(PreparedStatement statement = connection.prepareStatement(retrieve)){ + statement.setString(1, tag); + ResultSet resultSet = statement.executeQuery(); + return resultSet.next(); + } + catch(SQLException e){ + return false; + } + } + + private List getTags(String tableName){ //internally set table name + if (!isConnected()) return Collections.emptyList(); + List tags = new ArrayList<>(); + String retrieve = "SELECT "+TAG_COLUMN+" FROM "+tableName+";"; + + try(Connection connection = getConnection(); + Statement statement = connection.createStatement(); + ResultSet results = statement.executeQuery(retrieve)){ + + while(results.next()){ + tags.add(results.getString(TAG_COLUMN)); + } + } + catch(SQLException e){ + e.printStackTrace(); + } + return tags; + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/database/MongoDisplayStorage.java b/plugin/src/main/java/net/donnypz/displayentityutils/database/MongoDisplayStorage.java new file mode 100644 index 00000000..30b9a1fd --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/database/MongoDisplayStorage.java @@ -0,0 +1,339 @@ +package net.donnypz.displayentityutils.database; + +import com.mongodb.*; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Projections; +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.DisplayConfig; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bson.Document; +import org.bson.conversions.Bson; +import org.bson.types.Binary; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.*; + +public final class MongoDisplayStorage implements DBDisplayStorage { + + private static MongoCollection GROUP_COLLECTION; + private static final String GROUP_FIELD = "displayGroup"; + private static final String GROUP_DISPLAY_NAME = "display entity group"; + + private static MongoCollection ANIMATION_COLLECTION; + private static final String ANIMATION_FIELD = "displayAnimation"; + private static final String ANIMATION_DISPLAY_NAME = "animation"; + + private static final String TAG_FIELD = "tag"; + + private MongoClient client; + private MongoDatabase database; + private boolean isConnected = false; + + public void createConnection( + String host, + int port, + String databaseName, + String username, + String password, + String groupCollection, + String animationCollection + ){ + String connectionString = String.format( + "mongodb://%s:%s@%s:%d/%s", + username, + password, + host, + port, + databaseName + ); + + this.createConnection( + connectionString, + databaseName, + groupCollection, + animationCollection + ); + } + + public void createConnection( + String connectionString, + String databaseName, + String groupCollection, + String animationCollection + ) { + if (!this.canConnect(databaseName, groupCollection, animationCollection)) return; + + DisplayAPI.getScheduler().runAsync(() -> { + try{ + ConnectionString cString = new ConnectionString(connectionString); + MongoClientSettings settings = MongoClientSettings.builder() + .applyConnectionString(cString) + .serverApi(ServerApi.builder() + .version(ServerApiVersion.V1) + .build()) + .build(); + + client = MongoClients.create(settings); + database = client.getDatabase(databaseName); + + this.createIfNotExisting(groupCollection); + this.createIfNotExisting(animationCollection); + + GROUP_COLLECTION = database.getCollection(groupCollection); + ANIMATION_COLLECTION = database.getCollection(animationCollection); + + Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully connected to MongoDB!"))); + isConnected = true; + } + catch (IllegalArgumentException | MongoException e){ + isConnected = false; + Bukkit.getConsoleSender().sendMessage(Component.text("There was an error connecting to the MongoDB Database!", NamedTextColor.RED)); + e.printStackTrace(); + } + }); + } + + private boolean canConnect( + String databaseName, + String groupCollection, + String animationCollection + ){ + if (isConnected()) return false; + + if (databaseName.isBlank() || groupCollection.isBlank() || animationCollection.isBlank()){ + Bukkit.getConsoleSender().sendMessage(Component.text("There was an error connecting to the MongoDB Database! Database and/or Collection names are empty!", NamedTextColor.RED)); + isConnected = false; + return false; + } + + return true; + } + + private void createIfNotExisting(String collectionName){ + boolean contains = false; + for (String s : database.listCollectionNames()){ + if (s.equals(collectionName)){ + contains = true; + break; + } + } + if (!contains){ + database.createCollection(collectionName); + GROUP_COLLECTION = database.getCollection(collectionName); + } + } + + @Override + public boolean isConnected(){ + return isConnected; + } + + @Override + public void closeConnection(){ + if (client == null || !isConnected){ + return; + } + try{ + client.close(); + isConnected = false; + } + catch(MongoException e){ + isConnected = false; + e.printStackTrace(); + Bukkit.getConsoleSender().sendMessage(Component.text("There was an error closing the connection to the MongoDB Database", NamedTextColor.RED)); + } + } + + @Override + public boolean saveDisplayEntityGroup(@NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver){ + String tag = displayEntityGroup.getTag(); + return saveEntity( + tag, + displayEntityGroup, + GROUP_COLLECTION, + GROUP_FIELD, + "display entity group", + saver); + } + + @Override + public void deleteDisplayEntityGroup(@NotNull String tag, @Nullable Player deleter){ + deleteEntity( + tag, + GROUP_COLLECTION, + GROUP_DISPLAY_NAME, + deleter + ); + } + + @Override + public @Nullable DisplayEntityGroup getDisplayEntityGroup(@NotNull String tag){ + if (!isConnected) return null; + Document doc = getGroupDocument(tag); + if (doc == null){ + return null; + } + byte[] bytes = ((Binary) doc.get(GROUP_FIELD)).getData(); + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + return DisplayGroupManager.getGroup(in); + } + + + @Override + public boolean saveDisplayAnimation(@NotNull DisplayAnimation displayAnimation, @Nullable Player saver){ + String tag = displayAnimation.getAnimationTag(); + return saveEntity( + tag, + displayAnimation, + ANIMATION_COLLECTION, + ANIMATION_FIELD, + "animation", + saver + ); + } + + @Override + public void deleteDisplayAnimation(@NotNull String tag, @Nullable Player deleter){ + deleteEntity( + tag, + ANIMATION_COLLECTION, + ANIMATION_DISPLAY_NAME, + deleter + ); + } + + @Override + public @Nullable DisplayAnimation getDisplayAnimation(@NotNull String tag){ + if (!isConnected){ + return null; + } + Document doc = getAnimationDocument(tag); + if (doc == null){ + return null; + } + byte[] bytes = ((Binary) doc.get(ANIMATION_FIELD)).getData(); + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + return DisplayAnimationManager.getAnimation(in); + } + + + @Override + public @NotNull List getGroupTags(){ + return getTags(GROUP_COLLECTION); + } + + @Override + public @NotNull List getAnimationTags(){ + return getTags(ANIMATION_COLLECTION); + } + + + private boolean saveEntity( + String tag, + Object entity, + MongoCollection collection, + String fieldName, + String displayName, + Player saver){ + if (!isConnected) return false; + try{ + byte[] data = CommonDisplayStorageUtils.toByteArray(entity); + Document doc = new Document(); + + doc + .append(TAG_FIELD, tag) + .append(fieldName, data); + + Document existing = getGroupDocument(tag); + if (existing != null){ + if (DisplayConfig.overwritexistingSaves()){ + Bson updateOperation = new Document ("$set", doc); + collection.updateOne(existing, updateOperation); + } + else{ + if (saver != null){ + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save "+displayName+" to MongoDB!")); + saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); + } + return false; + } + + } + else{ + collection.insertOne(doc); + } + + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved "+displayName+" to MongoDB!")); + } + return true; + } + catch(IOException ex){ + ex.printStackTrace(); + if (saver != null) { + saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save "+displayName+" to MongoDB!")); + } + return false; + } + } + + private void deleteEntity( + String tag, + MongoCollection collection, + String displayName, + Player deleter + ){ + if (!isConnected()) return; + Document doc = getGroupDocument(tag); + if (doc != null){ + collection.deleteOne(doc); + if (deleter != null){ + deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted "+displayName+" from MongoDB!")); + return; + } + } + if (deleter != null){ + deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved "+displayName+" does not exist in MongoDB database!")); + } + } + + private List getTags(MongoCollection collection){ + if (!isConnected()) return new ArrayList<>(); + + return collection.find() + .projection( + Projections.fields( + Projections.include(TAG_FIELD), + Projections.excludeId() + ) + ) + .map(doc -> doc.getString(TAG_FIELD)) + .into(new ArrayList<>()); + } + + private Document getGroupDocument(String tag){ + return GROUP_COLLECTION.find(new Document(TAG_FIELD, tag)) + .first(); + } + + private Document getAnimationDocument(String tag){ + return ANIMATION_COLLECTION.find(new Document(TAG_FIELD, tag)) + .first(); + } + +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/managers/MYSQLManager.java b/plugin/src/main/java/net/donnypz/displayentityutils/managers/MYSQLManager.java deleted file mode 100644 index a24287bc..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/managers/MYSQLManager.java +++ /dev/null @@ -1,440 +0,0 @@ -package net.donnypz.displayentityutils.managers; - -import com.zaxxer.hikari.HikariDataSource; -import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.DisplayConfig; -import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; -import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.apache.commons.dbutils.DbUtils; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.sql.*; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.zip.GZIPOutputStream; - - -public final class MYSQLManager implements DisplayStorage{ - - private static boolean connected = false; - private static HikariDataSource dataSource; - - @ApiStatus.Internal - public static void createConnection(String host, int port, String database, String username, String password, boolean usessl){ - if (connected){ - return; - } - String url = "jdbc:mysql://"+host+":"+port+"/"+database+"?autoReconnect=true&allowMultiQueries=true&useSSL="+usessl; - createConnection(url, username, password); - } - - @ApiStatus.Internal - public static void createConnection(String url, String username, String password){ - if (connected){ - return; - } - DisplayAPI.getScheduler().runAsync(() -> { - try { - //Set Data Source - dataSource = new HikariDataSource(); - dataSource.setJdbcUrl(url); - dataSource.setUsername(username); - dataSource.setPassword(password); - - dataSource.setMinimumIdle(3); - dataSource.setMaximumPoolSize(6); - - //Test Connection - Connection connection = dataSource.getConnection(); - - - //Create Default Table - Statement statement = connection.createStatement(); - String groupCreator = "CREATE TABLE IF NOT EXISTS saved_displays(tag VARCHAR(128) UNIQUE, display_group BLOB)"; - statement.execute(groupCreator); - String animCreator = "CREATE TABLE IF NOT EXISTS saved_animations(tag VARCHAR(128) UNIQUE, display_anim BLOB)"; - statement.execute(animCreator); - - - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - - Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully connected to MYSQL!"))); - connected = true; - } catch (SQLException e) { - e.printStackTrace(); - Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(Component.text("There was an error connecting to the MYSQL database", NamedTextColor.RED))); - closeConnection(); - } - }); - } - - @ApiStatus.Internal - public static void closeConnection(){ - try{ - if (dataSource != null){ - dataSource.close(); - } - } - /*catch(SQLException e){ - e.printStackTrace(); - Bukkit.getConsoleSender().sendMessage(Component.text("There was an error closing the connection to the MYSQL Database", NamedTextColor.RED); - }*/ - finally { - connected = false; - dataSource = null; - } - } - - /** - * Check whether MySQL is connected - * @return a boolean - */ - public static boolean isConnected() { - return connected; - } - - private static Connection getConnection(){ - try{ - return dataSource.getConnection(); - } - catch(SQLException e){ - throw new RuntimeException(e); - } - } - - public boolean saveDisplayEntityGroup(@NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver){ - if (!connected){ - return false; - } - PreparedStatement statement = null; - Connection connection = null; - try{ - String tag = displayEntityGroup.getTag(); - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); - ObjectOutputStream objOut = new ObjectOutputStream(gzipOut); - objOut.writeObject(displayEntityGroup); - - gzipOut.close(); - objOut.close(); - - byte[] data = byteOut.toByteArray(); - - ByteArrayInputStream blobStream = new ByteArrayInputStream(data); - - String save = "INSERT INTO saved_displays VALUES(\""+tag+"\", ?);"; - connection = getConnection(); - statement = connection.prepareStatement(save); - statement.setBlob(1, blobStream); - if (getDisplayEntityGroup(tag) != null){ - if (DisplayConfig.overwritexistingSaves()){ - deleteDisplayEntityGroup(tag, null); - } - else{ - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save display entity group to MYSQL!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); - } - return false; - } - } - statement.executeUpdate(); - blobStream.close(); - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved display entity group to MYSQL!")); - } - return true; - } - catch(SQLIntegrityConstraintViolationException e){ - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save display entity group to MYSQL!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); - } - e.printStackTrace(); - return false; - } - catch(SQLException | IOException e){ - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save display entity group to MYSQL!")); - } - e.printStackTrace(); - return false; - } - finally { - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - public void deleteDisplayEntityGroup(@NotNull String tag, @Nullable Player deleter){ - if (!isConnected()) return; - Connection connection = null; - Statement statement = null; - try{ - connection = getConnection(); - - if (!hasSingleGroup(tag, connection)){ - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved display entity group does not exist in MYSQL database!")); - } - return; - } - - statement = connection.createStatement(); - String delete = "DELETE FROM saved_displays WHERE tag = \""+tag+"\";"; - statement.executeUpdate(delete); - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted group from MYSQL")); - } - } - catch(SQLException e){ - e.printStackTrace(); - } - finally { - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - public @Nullable DisplayEntityGroup getDisplayEntityGroup(@NotNull String tag){ - if (!isConnected()){ - return null; - } - Blob blob = getSingleGroupBlob(tag); - if (blob == null) return null; - try{ - return DisplayGroupManager.getGroup(blob.getBinaryStream()); - } - catch(SQLException e){ - e.printStackTrace(); - return null; - } - } - - public boolean saveDisplayAnimation(@NotNull DisplayAnimation displayAnimation, @Nullable Player saver){ - if (!connected){ - return false; - } - PreparedStatement statement = null; - Connection connection = null; - try{ - String tag = displayAnimation.getAnimationTag(); - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); - ObjectOutputStream objOut = new ObjectOutputStream(byteOut); - objOut.writeObject(displayAnimation); - gzipOut.close(); - objOut.close(); - - byte[] data = byteOut.toByteArray(); - byteOut.close(); - ByteArrayInputStream blobStream = new ByteArrayInputStream(data); - - String save = "INSERT INTO saved_animations VALUES(\""+tag+"\", ?);"; - connection = getConnection(); - statement = connection.prepareStatement(save); - statement.setBlob(1, blobStream); - if (getDisplayAnimation(tag) != null){ - if (DisplayConfig.overwritexistingSaves()){ - deleteDisplayAnimation(tag, null); - } - else{ - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save animation to MYSQL!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); - } - return false; - } - } - statement.executeUpdate(); - blobStream.close(); - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved animation to MYSQL!")); - } - return true; - } - catch(SQLException | IOException e){ - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save animation to MYSQL!")); - } - e.printStackTrace(); - return false; - } - finally { - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - public void deleteDisplayAnimation(@NotNull String tag, @Nullable Player deleter){ - if (!isConnected()){ - return; - } - Statement statement = null; - Connection connection = null; - ResultSet resultSet = null; - try{ - connection = getConnection(); - - if (!hasSingleAnimation(tag, connection)){ - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved animation does not exist in MYSQL database!")); - } - return; - } - - statement = connection.createStatement(); - String delete = "DELETE FROM saved_animations WHERE tag = \""+tag+"\";"; - statement.executeUpdate(delete); - if (deleter != null){ - - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted animation from MYSQL database!")); - } - } - catch(SQLException e){ - e.printStackTrace(); - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved animation does not exist in MYSQL database!")); - } - finally { - DbUtils.closeQuietly(resultSet); - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - public @Nullable DisplayAnimation getDisplayAnimation(@NotNull String tag) { - if (!isConnected()){ - return null; - } - Blob blob = getSingleAnimationBlob(tag); - if (blob == null) return null; - try { - return DisplayAnimationManager.getAnimation(blob.getBinaryStream()); - } - catch (SQLException e) { - e.printStackTrace(); - return null; - } - } - - public @NotNull List getGroupTags(){ - if (!isConnected()) return Collections.emptyList(); - return getTags("saved_displays"); - } - - public @NotNull List getAnimationTags(){ - if (!isConnected()) return Collections.emptyList(); - return getTags("saved_animations"); - } - - private static boolean hasSingleGroup(String tag, Connection connection){ - Statement statement = null; - ResultSet resultSet = null; - try{ - statement = connection.createStatement(); - String retrieve = "SELECT * FROM saved_displays WHERE tag = \""+tag+"\";"; - resultSet = statement.executeQuery(retrieve); - return resultSet.next(); - }catch(SQLException e){ - return false; - } - finally { - DbUtils.closeQuietly(resultSet); - DbUtils.closeQuietly(statement); - } - } - - private static Blob getSingleGroupBlob(String tag){ - Statement statement = null; - Connection connection = null; - try{ - connection = getConnection(); - statement = connection.createStatement(); - String retrieve = "SELECT * FROM saved_displays WHERE tag = \""+tag+"\";"; - ResultSet results = statement.executeQuery(retrieve); - if (results != null && results.next()){ - return results.getBlob("display_group"); - } - else{ - return null; - } - }catch(SQLException e){ - e.printStackTrace(); - return null; - } - finally { - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - private static boolean hasSingleAnimation(String tag, Connection connection){ - Statement statement = null; - ResultSet resultSet = null; - try{ - statement = connection.createStatement(); - String retrieve = "SELECT * FROM saved_animations WHERE tag = \""+tag+"\";"; - resultSet = statement.executeQuery(retrieve); - return resultSet.next(); - }catch(SQLException e){ - return false; - } - finally { - DbUtils.closeQuietly(resultSet); - DbUtils.closeQuietly(statement); - } - } - - private static Blob getSingleAnimationBlob(String tag){ - Statement statement = null; - Connection connection = null; - try{ - connection = getConnection(); - statement = connection.createStatement(); - String retrieve = "SELECT * FROM saved_animations WHERE tag = \""+tag+"\";"; - ResultSet results = statement.executeQuery(retrieve); - if (results != null && results.next()){ - return results.getBlob("display_anim"); - } - else{ - return null; - } - }catch(SQLException e){ - return null; - } - finally { - DbUtils.closeQuietly(statement); - DbUtils.closeQuietly(connection); - } - } - - private static List getTags(String tableName){ - List tags = new ArrayList<>(); - String retrieve = "SELECT * FROM "+tableName+";"; - try(Connection connection = getConnection(); - Statement statement = connection.createStatement(); - ResultSet results = statement.executeQuery(retrieve)){ - - while(results.next()){ - tags.add(results.getString("tag")); - } - }catch(SQLException e){ - e.printStackTrace(); - } - return tags; - } -} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/managers/MongoManager.java b/plugin/src/main/java/net/donnypz/displayentityutils/managers/MongoManager.java deleted file mode 100644 index 0452bb2a..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/managers/MongoManager.java +++ /dev/null @@ -1,308 +0,0 @@ -package net.donnypz.displayentityutils.managers; - -import com.mongodb.*; -import com.mongodb.client.MongoClient; -import com.mongodb.client.MongoClients; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.DisplayConfig; -import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayAnimation; -import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.minimessage.MiniMessage; -import org.bson.Document; -import org.bson.conversions.Bson; -import org.bson.types.Binary; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.zip.GZIPOutputStream; - -public final class MongoManager implements DisplayStorage{ - private static MongoClient client; - private static MongoDatabase database; - private static MongoCollection groupCollection; - private static MongoCollection animationCollection; - private static boolean isConnected = false; - - public boolean saveDisplayEntityGroup(@NotNull DisplayEntityGroup displayEntityGroup, @Nullable Player saver){ - if (!isConnected){ - return false; - } - try{ - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); - ObjectOutputStream objOut = new ObjectOutputStream(gzipOut); - objOut.writeObject(displayEntityGroup); - - gzipOut.close(); - objOut.close(); - byte[] data = byteOut.toByteArray(); - Document doc = new Document(); - - doc.append("tag", displayEntityGroup.getTag()) - .append("displayGroup", data); - - Document existing = getGroupDocument(displayEntityGroup.getTag()); - if (existing != null){ - if (DisplayConfig.overwritexistingSaves()){ - Bson updateOperation = new Document ("$set", doc); - groupCollection.updateOne(existing, updateOperation); - } - else{ - if (saver != null){ - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save display entity group to MongoDB!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); - } - return false; - } - - } - else{ - groupCollection.insertOne(doc); - } - - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved display entity group to MongoDB!")); - } - return true; - } - catch(IOException ex){ - ex.printStackTrace(); - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save display entity group to MongoDB!")); - } - return false; - } - } - - public void deleteDisplayEntityGroup(@NotNull String tag, @Nullable Player deleter){ - if (!isConnected()) return; - DisplayAPI.getScheduler().runAsync(() -> { - Document doc = getGroupDocument(tag); - if (doc != null){ - groupCollection.deleteOne(doc); - if (deleter != null){ - - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted group from MongoDB!")); - return; - } - } - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved display entity group does not exist in MongoDB database!")); - } - }); - } - - public @Nullable DisplayEntityGroup getDisplayEntityGroup(@NotNull String tag){ - if (!isConnected) return null; - Document doc = getGroupDocument(tag); - if (doc == null){ - return null; - } - byte[] bytes = ((Binary) doc.get("displayGroup")).getData(); - ByteArrayInputStream in = new ByteArrayInputStream(bytes); - return DisplayGroupManager.getGroup(in); - } - - - public boolean saveDisplayAnimation(@NotNull DisplayAnimation displayAnimation, @Nullable Player saver){ - if (!isConnected){ - return false; - } - try{ - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - GZIPOutputStream gzipOut = new GZIPOutputStream(byteOut); - ObjectOutputStream objOut = new ObjectOutputStream(gzipOut); - objOut.writeObject(displayAnimation); - gzipOut.close(); - objOut.close(); - - byte[] data = byteOut.toByteArray(); - Document doc = new Document(); - - doc.append("tag", displayAnimation.getAnimationTag()) - .append("displayAnimation", data); - - Document existing = getAnimationDocument(displayAnimation.getAnimationTag()); - if (existing != null){ - if (DisplayConfig.overwritexistingSaves()){ - Bson updateOperation = new Document ("$set", doc); - animationCollection.updateOne(existing, updateOperation); - } - else{ - if (saver != null){ - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save animation to MongoDB!")); - saver.sendMessage(Component.text("Save with tag already exists!", NamedTextColor.GRAY, TextDecoration.ITALIC)); - } - return false; - } - - } - else{ - animationCollection.insertOne(doc); - } - - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully saved animation to MongoDB!")); - } - return true; - } - catch(IOException ex){ - ex.printStackTrace(); - if (saver != null) { - saver.sendMessage(MiniMessage.miniMessage().deserialize("- Failed to save animation to MongoDB!")); - } - return false; - } - } - - public void deleteDisplayAnimation(@NotNull String tag, @Nullable Player deleter){ - if (!isConnected()) return; - DisplayAPI.getScheduler().runAsync(() -> { - Document doc = getAnimationDocument(tag); - if (doc != null){ - animationCollection.deleteOne(doc); - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Successfully deleted animation from MongoDB database!")); - return; - } - } - if (deleter != null){ - deleter.sendMessage(MiniMessage.miniMessage().deserialize("- Saved animation does not exist in MongoDB database!")); - } - }); - } - - public @Nullable DisplayAnimation getDisplayAnimation(@NotNull String tag){ - if (!isConnected){ - return null; - } - Document doc = getAnimationDocument(tag); - if (doc == null){ - return null; - } - byte[] bytes = ((Binary) doc.get("displayAnimation")).getData(); - ByteArrayInputStream in = new ByteArrayInputStream(bytes); - return DisplayAnimationManager.getAnimation(in); - } - - - public @NotNull List getGroupTags(){ - if (!isConnected()) return Collections.emptyList(); - List tags = new ArrayList<>(); - for(Document doc : groupCollection.find()){ - tags.add(doc.getString("tag")); - } - return tags; - } - - public @NotNull List getAnimationTags(){ - if (!isConnected()) return Collections.emptyList(); - List tags = new ArrayList<>(); - for(Document doc : animationCollection.find()){ - tags.add(doc.getString("tag")); - } - return tags; - } - - @ApiStatus.Internal - public static void createConnection(String connectionString, String databaseName, String groupColl, String animColl) { - if (isConnected()){ - return; - } - if (databaseName.isEmpty() || groupColl.isBlank() || animColl.isBlank()){ - Bukkit.getConsoleSender().sendMessage(Component.text("There was an error connecting to the MongoDB Database! Database and/or Collection names are empty!", NamedTextColor.RED)); - isConnected = false; - return; - } - - DisplayAPI.getScheduler().runAsync(() -> { - try{ - ConnectionString cString = new ConnectionString(connectionString); - MongoClientSettings settings = MongoClientSettings.builder() - .applyConnectionString(cString) - .serverApi(ServerApi.builder() - .version(ServerApiVersion.V1) - .build()) - .build(); - - client = MongoClients.create(settings); - database = client.getDatabase(databaseName); - - createIfNotExisting(groupColl); - createIfNotExisting(animColl); - - groupCollection = database.getCollection(groupColl); - animationCollection = database.getCollection(animColl); - - Bukkit.getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully connected to MongoDB!"))); - isConnected = true; - } - catch (IllegalArgumentException | MongoException e){ - isConnected = false; - Bukkit.getConsoleSender().sendMessage(Component.text("There was an error connecting to the MongoDB Database!", NamedTextColor.RED)); - e.printStackTrace(); - } - }); - } - - private static void createIfNotExisting(String collectionName){ - boolean contains = false; - for (String s : database.listCollectionNames()){ - if (s.equals(collectionName)){ - contains = true; - break; - } - } - if (!contains){ - database.createCollection(collectionName); - groupCollection = database.getCollection(collectionName); - } - } - - public static void closeConnection(){ - if (client == null || !isConnected){ - return; - } - try{ - client.close(); - isConnected = false; - } - catch(MongoException e){ - isConnected = false; - e.printStackTrace(); - Bukkit.getConsoleSender().sendMessage(Component.text("There was an error closing the connection to the MongoDB Database", NamedTextColor.RED)); - } - } - - /** - * Check whether MongoDB is connected - * @return a boolean - */ - public static boolean isConnected(){ - return isConnected; - } - - private static Document getGroupDocument(String tag){ - return groupCollection.find(new Document("tag", tag)).first(); - } - - private static Document getAnimationDocument(String tag){ - return animationCollection.find(new Document("tag", tag)).first(); - } - -} diff --git a/plugin/src/main/resources/config.yml b/plugin/src/main/resources/config.yml index 01acd8aa..ef6c63c0 100644 --- a/plugin/src/main/resources/config.yml +++ b/plugin/src/main/resources/config.yml @@ -8,20 +8,25 @@ localStorageEnabled: true #The database and collections will automatically be created if it does not exist already mongodb: enabled: false - connectionString: "" + host: "localhost" + port: 27017 database: "DisplayEntityUtils" + username: "user" + password: "password" groupCollection: "saved_displays" animationCollection: "saved_animations" + #Using a connection string will override settings above + connectionString: "" mysql: enabled: false host: "localhost" port: 3306 - database: "mydb" - username: "root" + database: "displayentityutils" + username: "user" password: "password" useSSL: false -#Using connection url will override settings above +#Using a connection url will override settings above connectionURL: "" #Determine whether plugin commands should be registered and usable @@ -97,7 +102,7 @@ autoCulling: heightCullingAdder: 1.0 #Optionally cache animations after retrieving them from a storage location. -#This option prevents constant server freezes/lag when retrieving animations at the cost of using more memory +#This option can prevent server freezes/lag when retrieving animations at the cost of using more memory cacheAnimations: enabled: true diff --git a/pom.xml b/pom.xml index 84da3df0..bd44d60e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.7.0 + 3.7.1 pom DisplayEntityUtils