From eb8ee00e4ab5486a75d17637bd5dae395b008312 Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Wed, 3 Aug 2022 19:11:03 +0200 Subject: [PATCH] Added loading/saving shops in a database --- .../playershops/config/DatabaseConfig.java | 48 ++++++ .../alttd/playershops/database/Database.java | 118 ++++++++++++++ .../playershops/database/ShopQueries.java | 152 ++++++++++++++++++ .../alttd/playershops/shop/AbstractShop.java | 29 ++++ .../alttd/playershops/shop/BarterShop.java | 9 ++ .../com/alttd/playershops/shop/BuyShop.java | 8 + .../alttd/playershops/shop/GambleShop.java | 8 + .../com/alttd/playershops/shop/SellShop.java | 9 ++ .../com/alttd/playershops/utils/AMath.java | 8 + 9 files changed, 389 insertions(+) create mode 100644 plugin/src/main/java/com/alttd/playershops/config/DatabaseConfig.java create mode 100644 plugin/src/main/java/com/alttd/playershops/database/Database.java create mode 100644 plugin/src/main/java/com/alttd/playershops/database/ShopQueries.java create mode 100644 plugin/src/main/java/com/alttd/playershops/utils/AMath.java diff --git a/plugin/src/main/java/com/alttd/playershops/config/DatabaseConfig.java b/plugin/src/main/java/com/alttd/playershops/config/DatabaseConfig.java new file mode 100644 index 0000000..4edd89a --- /dev/null +++ b/plugin/src/main/java/com/alttd/playershops/config/DatabaseConfig.java @@ -0,0 +1,48 @@ +package com.alttd.playershops.config; + +import com.alttd.galaxy.configuration.AbstractConfiguration; +import com.alttd.playershops.api.ShopType; + +import java.io.File; +import java.util.HashMap; + +public class DatabaseConfig extends AbstractConfiguration { + + private DatabaseConfig() { + super(new File(System.getProperty("user.home") + File.separator + "share" + File.separator + "configs" + File.separator + "com/alttd/playershops"), "database"); + } + + static DatabaseConfig config; + static int version; + static HashMap shopTypeConfigs; + + public static void reload() { + config = new DatabaseConfig(); + + version = config.getInt("config-version", 1); + config.set("config-version", 1); + + config.readConfig(DatabaseConfig.class, null); + + shopTypeConfigs = new HashMap<>(); + for (ShopType shopType : ShopType.values()) { + shopTypeConfigs.put(shopType, new ShopTypeConfig(shopType.toString())); + } + } + + public static String DRIVER = "mysql"; + public static String IP = "localhost"; + public static String PORT = "3306"; + public static String DATABASE_NAME = "AltitudeQuests"; + public static String USERNAME = "root"; + public static String PASSWORD = "root"; + + private static void loadDatabase() { + DRIVER = config.getString("database.driver", DRIVER); + IP = config.getString("database.ip", IP); + PORT = config.getString("database.port", PORT); + DATABASE_NAME = config.getString("database.name", DATABASE_NAME); + USERNAME = config.getString("database.username", USERNAME); + PASSWORD = config.getString("database.password", PASSWORD); + } +} diff --git a/plugin/src/main/java/com/alttd/playershops/database/Database.java b/plugin/src/main/java/com/alttd/playershops/database/Database.java new file mode 100644 index 0000000..ebebe39 --- /dev/null +++ b/plugin/src/main/java/com/alttd/playershops/database/Database.java @@ -0,0 +1,118 @@ +package com.alttd.playershops.database; + +import com.alttd.playershops.PlayerShops; +import com.alttd.playershops.config.DatabaseConfig; +import com.alttd.playershops.utils.Logger; +import org.bukkit.Bukkit; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class Database { + + private static Database instance = null; + private Connection connection = null; + + private Database() {} + + public static Database getDatabase(){ + if (instance == null) + { + instance = new Database(); + instance.init(); + } + return (instance); + } + + protected void init() { + try { + openConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + + //Run all create table functions + for (Method method : Database.class.getDeclaredMethods()) { + if (Modifier.isPrivate(method.getModifiers())) { + if (method.getParameterTypes().length == 0 && method.getReturnType() == Void.TYPE) { + try { + method.setAccessible(true); + method.invoke(instance); + } catch (InvocationTargetException ex) { + throw new RuntimeException(ex.getCause()); + } catch (Exception ex) { + Logger.severe("Error invoking " + method + "."); + ex.printStackTrace(); + } + } + } + } + } + + /** + * Opens the connection if it's not already open. + * @throws SQLException If it can't create the connection. + */ + private void openConnection() throws SQLException { + if (connection != null && !connection.isClosed()) { + return; + } + + synchronized (this) { + if (connection != null && !connection.isClosed()) { + return; + } + try { + Class.forName("com.mysql.cj.jdbc.Driver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + + connection = DriverManager.getConnection( + "jdbc:mysql://" + DatabaseConfig.IP + ":" + DatabaseConfig.PORT + "/" + DatabaseConfig.DATABASE_NAME + + "?autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true", + DatabaseConfig.USERNAME, DatabaseConfig.PASSWORD); + } + } + + public Connection getConnection() { + try { + openConnection(); + } catch (SQLException e) { + e.printStackTrace(); + } + return connection; + } + + private static void createShopTable() { + try { + String sql = "CREATE TABLE IF NOT EXISTS shops(" + + "id INT NOT NULL AUTO_INCREMENT, " + + "owner_name VARCHAR(16) NOT NULL, " + + "owner_uuid VARCHAR(36) NOT NULL, " + + "shop_type VARCHAR(36) NOT NULL, " + + "server VARCHAR(16) NOT NULL, " + + "container_location VARCHAR(256), " + + "sign_location VARCHAR(256), " + + "price DOUBLE NOT NULL, " + + "amount INT NOT NULL, " + + "balance DOUBLE NOT NULL, " + + "item_one TEXT, " + + "item_two TEXT, " + + "last_transaction BIGINT, " + + "PRIMARY KEY (id)" + + ")"; + getDatabase().getConnection().prepareStatement(sql).executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + Logger.severe("Error while trying to create shop table"); + Logger.severe("Shutting down PlayerShops"); + Bukkit.getPluginManager().disablePlugin(PlayerShops.getInstance()); + } + } + +} diff --git a/plugin/src/main/java/com/alttd/playershops/database/ShopQueries.java b/plugin/src/main/java/com/alttd/playershops/database/ShopQueries.java new file mode 100644 index 0000000..b8b8880 --- /dev/null +++ b/plugin/src/main/java/com/alttd/playershops/database/ShopQueries.java @@ -0,0 +1,152 @@ +package com.alttd.playershops.database; + +import com.alttd.playershops.api.Shop; +import com.alttd.playershops.api.ShopType; +import com.alttd.playershops.shop.AbstractShop; +import com.alttd.playershops.utils.AMath; +import com.alttd.playershops.utils.Logger; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class ShopQueries { + + public static boolean saveShop(AbstractShop shop) { + String sql = "INSERT INTO shops " + + "(id, owner_name, owner_uuid, shop_type, server, container_location, sign_location, " + + "price, amount, balance, item_one, item_two, last_transaction)" + + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" + + "ON DUPLICATE KEY UPDATE owner_name = ?, owner_uuid = ?, shop_type = ?, server = ?, " + + "container_location = ?, sign_location = ?, price = ?, amount = ?, balance = ?, " + + "item_one = ?, item_two = ?, last_transaction = ?"; + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + + statement.setInt(1, shop.getId()); + statement.setString(2, shop.getOwnerName()); + statement.setString(3, shop.getOwnerUUID().toString()); + statement.setString(4, shop.getServer()); + statement.setString(5, shop.getType().toString()); + statement.setString(6, locationToString(shop.getContainerLocation())); + statement.setString(7, locationToString(shop.getSignLocation())); + statement.setDouble(8, shop.getPrice()); + statement.setInt(9, shop.getAmount()); + statement.setDouble(10, shop.getBalance()); + statement.setBytes(11, shop.getItemStack().serializeAsBytes()); + statement.setBytes(12, shop.getSecondaryItem().serializeAsBytes()); + statement.setLong(13, shop.getLastTransaction()); + //repeat everything except id for update + statement.setString(14, shop.getOwnerName()); + statement.setString(15, shop.getOwnerUUID().toString()); + statement.setString(16, shop.getServer()); + statement.setString(17, shop.getType().toString()); + statement.setString(18, locationToString(shop.getContainerLocation())); + statement.setString(19, locationToString(shop.getSignLocation())); + statement.setDouble(20, shop.getPrice()); + statement.setInt(21, shop.getAmount()); + statement.setDouble(22, shop.getBalance()); + statement.setBytes(23, shop.getItemStack().serializeAsBytes()); + statement.setBytes(24, shop.getSecondaryItem().serializeAsBytes()); + statement.setLong(25, shop.getLastTransaction()); + + return statement.executeUpdate() == 1; + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } + + public static Shop loadShop(int id) { + String sql = "SELECT * FROM shops WHERE id = ?"; + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + + statement.setInt(1, id); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) + return shopFromResultSet(resultSet); + } catch (SQLException e) { + e.printStackTrace(); + } + return null; + } + + public static List loadShops() { + String sql = "SELECT * FROM shops"; + ArrayList shops = new ArrayList<>(); + try { + PreparedStatement statement = Database.getDatabase().getConnection().prepareStatement(sql); + + ResultSet resultSet = statement.executeQuery(); + while (resultSet.next()) { + Shop shop = shopFromResultSet(resultSet); + if (shop == null) { + Logger.warn("Tried to load a shop but failed [" + resultSet + "]"); + continue; + } + shops.add(shop); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return shops; + } + + /** + * Loads a shop from a result set, does not iterate + * @param resultSet Result set to load from + * @return A shop + * @throws SQLException if data is missing or formatted incorrectly + */ + private static Shop shopFromResultSet(ResultSet resultSet) throws SQLException { + int id = resultSet.getInt("id"); + String ownerName = resultSet.getString("owner_name"); + UUID ownerUuid = UUID.fromString(resultSet.getString("owner_uuid")); + ShopType shopType = ShopType.valueOf(resultSet.getString("shop_type")); + String server = resultSet.getString("server"); + Location containerLocation = stringToLocation(resultSet.getString("container_location")); + Location signLocation = stringToLocation(resultSet.getString("sign_location")); + double price = resultSet.getDouble("price"); + int amount = resultSet.getInt("amount"); + double balance = resultSet.getDouble("balance"); + ItemStack itemOne = ItemStack.deserializeBytes(resultSet.getBytes("item_one")); + ItemStack itemTwo = ItemStack.deserializeBytes(resultSet.getBytes("item_two")); + long lastTransaction = resultSet.getLong("last_transaction"); + + if (containerLocation == null || signLocation == null) + return null; + + return AbstractShop.create(id, ownerName, ownerUuid, shopType, server, containerLocation, signLocation, + price, amount, balance, itemOne, itemTwo, lastTransaction); + } + + private static String locationToString(Location location) { + return location.getWorld() + ":" + + AMath.round(location.getX(), 1) + ":" + + AMath.round(location.getY(), 1) + ":" + + AMath.round(location.getZ(), 1); + } + + private static Location stringToLocation(String string) { + String[] split = string.split(":"); + if (split.length != 4) { + Logger.warn("Unable to load location [" + string + "] due to invalid format"); + return null; + } + + try { + return new Location(Bukkit.getWorld(split[0]), + Double.parseDouble(split[1]), Double.parseDouble(split[2]), Double.parseDouble(split[3])); + } catch (NumberFormatException e) { + Logger.warn("Unable to load location [" + string + "] due to invalid format"); + return null; + } + } +} diff --git a/plugin/src/main/java/com/alttd/playershops/shop/AbstractShop.java b/plugin/src/main/java/com/alttd/playershops/shop/AbstractShop.java index 0f1593e..cc9cb5a 100644 --- a/plugin/src/main/java/com/alttd/playershops/shop/AbstractShop.java +++ b/plugin/src/main/java/com/alttd/playershops/shop/AbstractShop.java @@ -52,6 +52,24 @@ public abstract class AbstractShop implements Shop { ownerName = getOwnerName(); this.price = price; this.amount = amount; + this.server = Bukkit.getServerName(); + } + + AbstractShop(int id, String ownerName, UUID ownerUUID, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + this.id = id; + this.ownerName = ownerName; + this.ownerUUID = ownerUUID; + this.server = server; + this.containerLocation = containerLocation; + this.signLocation = signLocation; + this.price = price; + this.amount = amount; + this.balance = balance; + this.itemStack = itemOne; + this.secondaryItem = itemTwo; + this.lastTransaction = lastTransaction; } public static AbstractShop create(Location signLocation, UUID player, double price, int amount, ShopType shopType) { @@ -63,6 +81,17 @@ public abstract class AbstractShop implements Shop { }; } + public static AbstractShop create(int id, String ownerName, UUID ownerUUID, ShopType shopType, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + return switch (shopType) { + case SELL -> new SellShop(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, balance, itemOne, itemTwo, lastTransaction); + case BUY -> new BuyShop(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, balance, itemOne, itemTwo, lastTransaction); + case GAMBLE -> new GambleShop(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, balance, itemOne, itemTwo, lastTransaction); + case BARTER -> new BarterShop(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, balance, itemOne, itemTwo, lastTransaction); + }; + } + public String getOwnerName() { if(this.ownerName != null) return ownerName; if (this.getOwnerUUID() != null) { diff --git a/plugin/src/main/java/com/alttd/playershops/shop/BarterShop.java b/plugin/src/main/java/com/alttd/playershops/shop/BarterShop.java index a836fc4..d06a5be 100644 --- a/plugin/src/main/java/com/alttd/playershops/shop/BarterShop.java +++ b/plugin/src/main/java/com/alttd/playershops/shop/BarterShop.java @@ -2,6 +2,7 @@ package com.alttd.playershops.shop; import com.alttd.playershops.api.ShopType; import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; import java.util.UUID; @@ -13,4 +14,12 @@ public class BarterShop extends AbstractShop { this.setType(ShopType.BARTER); } + public BarterShop(int id, String ownerName, UUID ownerUUID, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + super(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, + balance, itemOne, itemTwo, lastTransaction); + this.setType(ShopType.BARTER); + } + } diff --git a/plugin/src/main/java/com/alttd/playershops/shop/BuyShop.java b/plugin/src/main/java/com/alttd/playershops/shop/BuyShop.java index 6a90623..875e989 100644 --- a/plugin/src/main/java/com/alttd/playershops/shop/BuyShop.java +++ b/plugin/src/main/java/com/alttd/playershops/shop/BuyShop.java @@ -2,6 +2,7 @@ package com.alttd.playershops.shop; import com.alttd.playershops.api.ShopType; import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; import java.util.UUID; @@ -13,4 +14,11 @@ public class BuyShop extends AbstractShop { this.setType(ShopType.BUY); } + public BuyShop(int id, String ownerName, UUID ownerUUID, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + super(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, + balance, itemOne, itemTwo, lastTransaction); + this.setType(ShopType.BUY); + } } diff --git a/plugin/src/main/java/com/alttd/playershops/shop/GambleShop.java b/plugin/src/main/java/com/alttd/playershops/shop/GambleShop.java index a5da58e..66eb830 100644 --- a/plugin/src/main/java/com/alttd/playershops/shop/GambleShop.java +++ b/plugin/src/main/java/com/alttd/playershops/shop/GambleShop.java @@ -17,4 +17,12 @@ public class GambleShop extends AbstractShop { this.gambleItem = this.getItemStack(); } + public GambleShop(int id, String ownerName, UUID ownerUUID, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + super(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, + balance, itemOne, itemTwo, lastTransaction); + this.setType(ShopType.GAMBLE); + } + } diff --git a/plugin/src/main/java/com/alttd/playershops/shop/SellShop.java b/plugin/src/main/java/com/alttd/playershops/shop/SellShop.java index 991ad8d..4ded4d6 100644 --- a/plugin/src/main/java/com/alttd/playershops/shop/SellShop.java +++ b/plugin/src/main/java/com/alttd/playershops/shop/SellShop.java @@ -2,6 +2,7 @@ package com.alttd.playershops.shop; import com.alttd.playershops.api.ShopType; import org.bukkit.Location; +import org.bukkit.inventory.ItemStack; import java.util.UUID; @@ -13,4 +14,12 @@ public class SellShop extends AbstractShop { this.setType(ShopType.SELL); } + public SellShop(int id, String ownerName, UUID ownerUUID, String server, + Location containerLocation, Location signLocation, double price, int amount, + double balance, ItemStack itemOne, ItemStack itemTwo, long lastTransaction) { + super(id, ownerName, ownerUUID, server, containerLocation, signLocation, price, amount, + balance, itemOne, itemTwo, lastTransaction); + this.setType(ShopType.SELL); + } + } diff --git a/plugin/src/main/java/com/alttd/playershops/utils/AMath.java b/plugin/src/main/java/com/alttd/playershops/utils/AMath.java new file mode 100644 index 0000000..f99667b --- /dev/null +++ b/plugin/src/main/java/com/alttd/playershops/utils/AMath.java @@ -0,0 +1,8 @@ +package com.alttd.playershops.utils; + +public class AMath { + public static double round (double value, int precision) { + int scale = (int) Math.pow(10, precision); + return (double) Math.round(value * scale) / scale; + } +}