diff --git a/src/main/java/de/epiceric/shopchest/ShopChest.java b/src/main/java/de/epiceric/shopchest/ShopChest.java index 3b6289a..1fa45cc 100644 --- a/src/main/java/de/epiceric/shopchest/ShopChest.java +++ b/src/main/java/de/epiceric/shopchest/ShopChest.java @@ -272,7 +272,7 @@ public class ShopChest extends JavaPlugin { getServer().getPluginManager().registerEvents(new ShopUpdateListener(this), this); getServer().getPluginManager().registerEvents(new ShopItemListener(this), this); getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this); - getServer().getPluginManager().registerEvents(new NotifyUpdateOnJoinListener(this), this); + getServer().getPluginManager().registerEvents(new NotifyPlayerOnJoinListener(this), this); getServer().getPluginManager().registerEvents(new ChestProtectListener(this, worldGuard), this); if (!Utils.getServerVersion().equals("v1_8_R1")) diff --git a/src/main/java/de/epiceric/shopchest/config/Regex.java b/src/main/java/de/epiceric/shopchest/config/Regex.java index 19ba6a8..63f7d0a 100644 --- a/src/main/java/de/epiceric/shopchest/config/Regex.java +++ b/src/main/java/de/epiceric/shopchest/config/Regex.java @@ -19,7 +19,8 @@ public enum Regex { MUSIC_TITLE("%MUSIC-TITLE%"), PROPERTY("%PROPERTY%"), VALUE("%VALUE%"), - EXTENDED("%EXTENDED%"); + EXTENDED("%EXTENDED%"), + REVENUE("%REVENUE%"); private String name; diff --git a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java index 5435186..9a6f1bb 100644 --- a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java +++ b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java @@ -933,6 +933,7 @@ public class LanguageUtils { messages.add(new LocalizedMessage(LocalizedMessage.Message.SELL_SUCESS_ADMIN, langConfig.getString("message.sell-success-admin", "&aYou sold &6%AMOUNT% x %ITEMNAME%&a for &6%SELL-PRICE%&a."), Regex.AMOUNT, Regex.ITEM_NAME, Regex.SELL_PRICE)); messages.add(new LocalizedMessage(LocalizedMessage.Message.SOMEONE_BOUGHT, langConfig.getString("message.someone-bought", "&6%PLAYER% &abought &6%AMOUNT% x %ITEMNAME%&a for &6%BUY-PRICE%&a from your shop."), Regex.PLAYER, Regex.AMOUNT, Regex.ITEM_NAME, Regex.BUY_PRICE)); messages.add(new LocalizedMessage(LocalizedMessage.Message.SOMEONE_SOLD, langConfig.getString("message.someone-sold", "&6%PLAYER% &asold &6%AMOUNT% x %ITEMNAME%&a for &6%SELL-PRICE%&a to your shop."), Regex.PLAYER, Regex.AMOUNT, Regex.ITEM_NAME, Regex.SELL_PRICE)); + messages.add(new LocalizedMessage(LocalizedMessage.Message.REVENUE_WHILE_OFFLINE, langConfig.getString("message.revenue-while-offline", "&6While you were offline, your shops have made a revenue of &c%REVENUE%&6."), Regex.REVENUE)); messages.add(new LocalizedMessage(LocalizedMessage.Message.NOT_ENOUGH_INVENTORY_SPACE, langConfig.getString("message.not-enough-inventory-space", "&cNot enough space in inventory."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.CHEST_NOT_ENOUGH_INVENTORY_SPACE, langConfig.getString("message.chest-not-enough-inventory-space", "&cShop is full."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.NOT_ENOUGH_MONEY, langConfig.getString("message.not-enough-money", "&cNot enough money."))); @@ -1180,7 +1181,7 @@ public class LanguageUtils { if (localizedMessage.getRegexes()[i] == replacedRegex.getRegex()) { Regex regex = replacedRegex.getRegex(); String toReplace = replacedRegex.getReplace(); - if (regex == Regex.BUY_PRICE || regex == Regex.SELL_PRICE || regex == Regex.MIN_PRICE || regex == Regex.CREATION_PRICE) { + if (regex == Regex.BUY_PRICE || regex == Regex.SELL_PRICE || regex == Regex.MIN_PRICE || regex == Regex.CREATION_PRICE || regex == Regex.REVENUE) { if (!toReplace.equals(getMessage(LocalizedMessage.Message.SHOP_INFO_DISABLED))) { double price = Double.parseDouble(toReplace); toReplace = plugin.getEconomy().format(price); diff --git a/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java b/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java index 75645d7..c184da9 100644 --- a/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java +++ b/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java @@ -68,6 +68,7 @@ public class LocalizedMessage { SELL_SUCESS_ADMIN, SOMEONE_BOUGHT, SOMEONE_SOLD, + REVENUE_WHILE_OFFLINE, NOT_ENOUGH_INVENTORY_SPACE, CHEST_NOT_ENOUGH_INVENTORY_SPACE, NOT_ENOUGH_MONEY, diff --git a/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java b/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java new file mode 100644 index 0000000..6ac00aa --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/listeners/NotifyPlayerOnJoinListener.java @@ -0,0 +1,70 @@ +package de.epiceric.shopchest.listeners; + +import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.config.Regex; +import de.epiceric.shopchest.language.LanguageUtils; +import de.epiceric.shopchest.language.LocalizedMessage; +import de.epiceric.shopchest.nms.JsonBuilder; +import de.epiceric.shopchest.utils.Callback; +import de.epiceric.shopchest.utils.Permissions; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +public class NotifyPlayerOnJoinListener implements Listener { + + private ShopChest plugin; + + public NotifyPlayerOnJoinListener(ShopChest plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent e) { + final Player p = e.getPlayer(); + + if (plugin.isUpdateNeeded()) { + if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) { + JsonBuilder jb = new JsonBuilder(plugin, LanguageUtils.getMessage(LocalizedMessage.Message.UPDATE_AVAILABLE, new LocalizedMessage.ReplacedRegex(Regex.VERSION, plugin.getLatestVersion())), LanguageUtils.getMessage(LocalizedMessage.Message.UPDATE_CLICK_TO_DOWNLOAD), plugin.getDownloadLink()); + jb.sendJson(p); + } + } + + plugin.getShopDatabase().getLastLogout(p, new Callback(plugin) { + @Override + public void onResult(Object result) { + if (result instanceof Long) { + long lastLogout = (long) result; + if (lastLogout < 0) { + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.ERROR_OCCURRED, + new LocalizedMessage.ReplacedRegex(Regex.ERROR, "Could not get last time you logged out"))); + return; + } + + plugin.getShopDatabase().getRevenue(p, lastLogout, new Callback(plugin) { + @Override + public void onResult(Object result) { + if (result instanceof Double) { + double revenue = (double) result; + if (revenue != 0) { + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.REVENUE_WHILE_OFFLINE, + new LocalizedMessage.ReplacedRegex(Regex.REVENUE, String.valueOf(revenue)))); + } + } + } + }); + } + } + }); + + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + long time = System.currentTimeMillis(); + plugin.getShopDatabase().logLogout(e.getPlayer(), time, null); + } + +} diff --git a/src/main/java/de/epiceric/shopchest/listeners/NotifyUpdateOnJoinListener.java b/src/main/java/de/epiceric/shopchest/listeners/NotifyUpdateOnJoinListener.java deleted file mode 100644 index 7a21576..0000000 --- a/src/main/java/de/epiceric/shopchest/listeners/NotifyUpdateOnJoinListener.java +++ /dev/null @@ -1,35 +0,0 @@ -package de.epiceric.shopchest.listeners; - -import de.epiceric.shopchest.ShopChest; -import de.epiceric.shopchest.config.Regex; -import de.epiceric.shopchest.language.LanguageUtils; -import de.epiceric.shopchest.language.LocalizedMessage; -import de.epiceric.shopchest.nms.JsonBuilder; -import de.epiceric.shopchest.utils.Permissions; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; - -public class NotifyUpdateOnJoinListener implements Listener { - - private ShopChest plugin; - - public NotifyUpdateOnJoinListener(ShopChest plugin) { - this.plugin = plugin; - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent e) { - Player p = e.getPlayer(); - - if (plugin.isUpdateNeeded()) { - if (p.hasPermission(Permissions.UPDATE_NOTIFICATION)) { - JsonBuilder jb = new JsonBuilder(plugin, LanguageUtils.getMessage(LocalizedMessage.Message.UPDATE_AVAILABLE, new LocalizedMessage.ReplacedRegex(Regex.VERSION, plugin.getLatestVersion())), LanguageUtils.getMessage(LocalizedMessage.Message.UPDATE_CLICK_TO_DOWNLOAD), plugin.getDownloadLink()); - jb.sendJson(p); - } - } - - } - -} diff --git a/src/main/java/de/epiceric/shopchest/sql/Database.java b/src/main/java/de/epiceric/shopchest/sql/Database.java index cf377ff..0a4d7f8 100644 --- a/src/main/java/de/epiceric/shopchest/sql/Database.java +++ b/src/main/java/de/epiceric/shopchest/sql/Database.java @@ -17,6 +17,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; import java.sql.*; +import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; @@ -79,6 +80,12 @@ public abstract class Database { "`type` TINYTEXT NOT NULL" + ");"; + String queryCreateTablePlayerLogout = + "CREATE TABLE IF NOT EXISTS player_logout (" + + "`player` VARCHAR(36) PRIMARY KEY NOT NULL," + + "`time` LONG NOT NULL" + + ");"; + String queryCheckIfTableExists = (Database.this instanceof SQLite ? "SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'shop_list'" : @@ -118,6 +125,11 @@ public abstract class Database { s3.executeUpdate(queryCreateTableShopLog); s3.close(); + // Create table "player_logout" + Statement s4 = connection.createStatement(); + s4.executeUpdate(queryCreateTablePlayerLogout); + s4.close(); + // Count entries in table "shops" PreparedStatement ps = connection.prepareStatement("SELECT * FROM shops"); ResultSet rs2 = ps.executeQuery(); @@ -365,6 +377,133 @@ public abstract class Database { }.runTaskAsynchronously(plugin); } + /** + * Get the revenue a player got while he was offline + * @param player Player whose revenue to get + * @param logoutTime Time in milliseconds when he logged out the last time + */ + public void getRevenue(final Player player, final long logoutTime, final Callback callback) { + new BukkitRunnable() { + @Override + public void run() { + PreparedStatement ps = null; + ResultSet rs = null; + + String vendor = String.format("%s (%s)", player.getUniqueId().toString(), player.getName()); + + double revenue = 0; + + try { + ps = connection.prepareStatement("SELECT * FROM shop_log WHERE vendor = ?;"); + ps.setString(1, vendor); + rs = ps.executeQuery(); + + while (rs.next()) { + if (rs.getString("vendor").equals(vendor)) { + double singleRevenue = rs.getDouble("price"); + ShopBuySellEvent.Type type = ShopBuySellEvent.Type.valueOf(rs.getString("type")); + + if (type == ShopBuySellEvent.Type.SELL) singleRevenue = -singleRevenue; + + long timestamp; + + try { + timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(rs.getString("timestamp")).getTime(); + } catch (ParseException ex) { + plugin.debug("Failed to get revenue from player \"" + player.getUniqueId().toString() + "\""); + plugin.debug(ex); + continue; + } + + if (timestamp > logoutTime) { + revenue += singleRevenue; + } + } + } + + if (callback != null) callback.callSyncResult(revenue); + } catch (SQLException ex) { + if (callback != null) callback.callSyncError(ex); + plugin.getLogger().severe("Failed to access database"); + plugin.debug("Failed to get revenue from player \"" + player.getUniqueId().toString() + "\""); + plugin.debug(ex); + } finally { + close(ps, rs); + } + } + }.runTaskAsynchronously(plugin); + } + + /** + * Log a logout to the database + * @param player Player who logged out + * @param timestamp Time in milliseconds when the player logged out + */ + public void logLogout(final Player player, final long timestamp, final Callback callback) { + new BukkitRunnable() { + @Override + public void run() { + PreparedStatement ps = null; + + try { + ps = connection.prepareStatement("REPLACE INTO player_logout (player,time) VALUES(?,?)"); + + ps.setString(1, player.getUniqueId().toString()); + ps.setLong(2, timestamp); + ps.executeUpdate(); + + if (callback != null) callback.callSyncResult(null); + plugin.debug("Logged logout to database"); + } catch (final SQLException ex) { + if (callback != null) callback.callSyncError(ex); + plugin.getLogger().severe("Failed to access database"); + plugin.debug("Failed to log logout to database"); + plugin.debug(ex); + } finally { + close(ps, null); + } + } + }.runTaskAsynchronously(plugin); + } + + /** + * Get the last logout of a player + * @param player Player who logged out + */ + public void getLastLogout(final Player player, final Callback callback) { + new BukkitRunnable() { + @Override + public void run() { + PreparedStatement ps = null; + ResultSet rs = null; + + String playerUuid = player.getUniqueId().toString(); + + try { + ps = connection.prepareStatement("SELECT * FROM player_logout WHERE player = ?;"); + ps.setString(1, playerUuid); + rs = ps.executeQuery(); + + while (rs.next()) { + if (rs.getString("player").equals(playerUuid)) { + if (callback != null) callback.callSyncResult(rs.getLong("time")); + return; + } + } + + if (callback != null) callback.callSyncResult(-1); + } catch (SQLException ex) { + if (callback != null) callback.callSyncError(ex); + plugin.getLogger().severe("Failed to access database"); + plugin.debug("Failed to get last logout from player \"" + playerUuid + "\""); + plugin.debug(ex); + } finally { + close(ps, rs); + } + } + }.runTaskAsynchronously(plugin); + } + /** * Closes a {@link PreparedStatement} and a {@link ResultSet} * @param ps {@link PreparedStatement} to close diff --git a/src/main/java/de/epiceric/shopchest/utils/Callback.java b/src/main/java/de/epiceric/shopchest/utils/Callback.java index 16c0982..2ac82dc 100644 --- a/src/main/java/de/epiceric/shopchest/utils/Callback.java +++ b/src/main/java/de/epiceric/shopchest/utils/Callback.java @@ -14,7 +14,7 @@ public abstract class Callback { public void onError(Throwable throwable) {} - public void callSyncResult(final Object result) { + public final void callSyncResult(final Object result) { new BukkitRunnable() { @Override public void run() { @@ -23,7 +23,7 @@ public abstract class Callback { }.runTask(plugin); } - public void callSyncError(final Throwable throwable) { + public final void callSyncError(final Throwable throwable) { new BukkitRunnable() { @Override public void run() { diff --git a/src/main/resources/lang/de_DE.lang b/src/main/resources/lang/de_DE.lang index 2c03aa0..cf7ade0 100644 --- a/src/main/resources/lang/de_DE.lang +++ b/src/main/resources/lang/de_DE.lang @@ -24,6 +24,7 @@ message.sell-success=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE%&a a message.sell-success-admin=&aDu hast &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE% &averkauft. message.someone-bought=&6%PLAYER% &ahat &6%AMOUNT% x %ITEMNAME%&a für &6%BUY-PRICE%&a von deinem Shop gekauft. message.someone-sold=&6%PLAYER% &ahat &6%AMOUNT% x %ITEMNAME%&a für &6%SELL-PRICE%&a an deinen Shop verkauft. +message.revenue-while-offline=&6Während du offline warst, haben deine Shops einen Umsatz von &c%REVENUE%&6 gemacht. message.not-enough-inventory-space=&cNicht genug Platz im Inventar. message.chest-not-enough-inventory-space=&cShop ist voll. message.not-enough-money=&cNicht genug Geld. diff --git a/src/main/resources/lang/en_US.lang b/src/main/resources/lang/en_US.lang index e46fc43..7c136a5 100644 --- a/src/main/resources/lang/en_US.lang +++ b/src/main/resources/lang/en_US.lang @@ -93,6 +93,10 @@ message.someone-bought=&6%PLAYER% &abought &6%AMOUNT% x %ITEMNAME%&a for &6%BUY- # Usable regex: %AMOUNT%, %ITEMNAME%, %SELL-PRICE%, %PLAYER% message.someone-sold=&6%PLAYER% &asold &6%AMOUNT% x %ITEMNAME%&a for &6%SELL-PRICE%&a to your shop. +# Set the message when a player joins and made a revenue while he was offline. +# Usable regex: %REVENUE% +message.revenue-while-offline=&6While you were offline, your shops have made a revenue of &c%REVENUE%&6. + # Set the message when the inventory is full when the player is buying something. message.not-enough-inventory-space=&cNot enough space in inventory.