diff --git a/pom.xml b/pom.xml index c68f5e2..79b030e 100644 --- a/pom.xml +++ b/pom.xml @@ -78,26 +78,9 @@ provided - - org.mcstats.bukkit - metrics - R8-SNAPSHOT - compile - - - org.bukkit - bukkit - - - org.bukkit - craftbukkit - - - - org.bstats - bstats-bukkit-lite + bstats-bukkit 1.7 compile @@ -326,6 +309,24 @@ ${project.name} + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + true + + + true + + + ${buildType} + ${maven.build.timestamp} + ${java.runtime.version} + + + + maven-compiler-plugin 3.6.1 @@ -347,7 +348,6 @@ - org.mcstats.bukkit org.bstats:* net.gravitydevelopment.updater com.j256.ormlite @@ -356,10 +356,6 @@ - - org.mcstats - com.Acrobot.ChestShop.Metrics.MCStats - org.bstats com.Acrobot.ChestShop.Metrics.BStats @@ -394,6 +390,7 @@ UTF-8 ${buildNumber} + ${buildType} ${project.version} ${buildDescription} @@ -463,6 +460,7 @@ + manual 0 (compiled at ${maven.build.timestamp}) @@ -476,6 +474,7 @@ + jenkins ${env.BUILD_NUMBER} (build ${env.BUILD_NUMBER}) diff --git a/src/main/java/com/Acrobot/Breeze/Utils/NumberUtil.java b/src/main/java/com/Acrobot/Breeze/Utils/NumberUtil.java index 8cc0d7f..c775ff7 100644 --- a/src/main/java/com/Acrobot/Breeze/Utils/NumberUtil.java +++ b/src/main/java/com/Acrobot/Breeze/Utils/NumberUtil.java @@ -154,4 +154,14 @@ public class NumberUtil { return Integer.toString(number); } } + + /** + * Convert a long to an integer while not overflowing but returning Integer.MAX_VALUE + * + * @param number The long to convert + * @return The integer value or Integer.MAX_VALUE on overflow + */ + public static int toInt(long number) { + return number > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) number; + } } diff --git a/src/main/java/com/Acrobot/ChestShop/ChestShop.java b/src/main/java/com/Acrobot/ChestShop/ChestShop.java index 90f4bae..18334e5 100644 --- a/src/main/java/com/Acrobot/ChestShop/ChestShop.java +++ b/src/main/java/com/Acrobot/ChestShop/ChestShop.java @@ -19,6 +19,7 @@ import com.Acrobot.ChestShop.Listeners.AuthMeChestShopListener; import com.Acrobot.ChestShop.Listeners.GarbageTextListener; import com.Acrobot.ChestShop.Listeners.Item.ItemMoveListener; import com.Acrobot.ChestShop.Listeners.ItemInfoListener; +import com.Acrobot.ChestShop.Listeners.Modules.MetricsModule; import com.Acrobot.ChestShop.Listeners.SignParseListener; import com.Acrobot.ChestShop.Listeners.Modules.DiscountModule; import com.Acrobot.ChestShop.Listeners.Modules.PriceRestrictionModule; @@ -41,6 +42,7 @@ import com.Acrobot.ChestShop.UUIDs.NameManager; import com.Acrobot.ChestShop.Updater.JenkinsBuildsNotifier; import com.Acrobot.ChestShop.Updater.Updater; +import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; @@ -53,7 +55,9 @@ import org.apache.logging.log4j.core.config.LoggerConfig; import org.apache.logging.log4j.core.filter.AbstractFilter; import org.apache.logging.log4j.message.Message; +import org.bstats.bukkit.Metrics; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.Server; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; @@ -67,9 +71,15 @@ import org.bukkit.plugin.java.JavaPlugin; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.jar.JarFile; import java.util.logging.FileHandler; import java.util.logging.Logger; +import java.util.stream.Collectors; /** * Main file of the plugin @@ -111,6 +121,7 @@ public class ChestShop extends JavaPlugin { registerCommand("iteminfo", new ItemInfo(), Permission.ITEMINFO); registerCommand("csVersion", new Version(), Permission.ADMIN); + registerCommand("csMetrics", new com.Acrobot.ChestShop.Commands.Metrics(), Permission.ADMIN); registerCommand("csGive", new Give(), Permission.ADMIN); registerCommand("cstoggle", new Toggle(), Permission.NOTIFY_TOGGLE); registerCommand("csaccess", new AccessToggle(), Permission.ACCESS_TOGGLE); @@ -376,6 +387,7 @@ public class ChestShop extends JavaPlugin { private void registerModules() { registerEvent(new DiscountModule()); + registerEvent(new MetricsModule()); registerEvent(new PriceRestrictionModule()); registerEconomicalModules(); @@ -397,12 +409,49 @@ public class ChestShop extends JavaPlugin { } private void startStatistics() { + Metrics bStats = new Metrics(this, 1109); try { - new org.mcstats.Metrics(this).start(); - } catch (IOException ex) { - ChestShop.getBukkitLogger().severe("There was an error while submitting MCStats statistics."); - } - new org.bstats.bukkit.MetricsLite(this, 1109); + String dist = new JarFile(this.getFile()).getManifest().getMainAttributes().getValue("Distribution-Type"); + bStats.addCustomChart(new Metrics.SimplePie("distributionType", () -> dist)); + } catch (IOException ignored) {} + + bStats.addCustomChart(new Metrics.SingleLineChart("shopAccounts", NameManager::getAccountCount)); + bStats.addCustomChart(new Metrics.MultiLineChart("transactionCount", () -> ImmutableMap.of( + "total", MetricsModule.getTotalTransactions(), + "buy", MetricsModule.getBuyTransactions(), + "sell", MetricsModule.getSellTransactions() + )));bStats.addCustomChart(new Metrics.MultiLineChart("itemCount", () -> ImmutableMap.of( + "total", MetricsModule.getTotalItemsCount(), + "buy", MetricsModule.getSoldItemsCount(), + "sell", MetricsModule.getBoughtItemsCount() + ))); + + bStats.addCustomChart(new Metrics.SimplePie("includeSettingsInMetrics", () -> Properties.INCLUDE_SETTINGS_IN_METRICS ? "enabled" : "disabled")); + if (!Properties.INCLUDE_SETTINGS_IN_METRICS) return; + + bStats.addCustomChart(new Metrics.AdvancedBarChart("pluginProperties", () -> { + Map map = new LinkedHashMap<>(); + map.put("reverse-buttons", getChartArray(Properties.REVERSE_BUTTONS)); + map.put("shift-sells-in-stacks", getChartArray(Properties.SHIFT_SELLS_IN_STACKS)); + map.put("shift-sells-everything", getChartArray(Properties.SHIFT_SELLS_EVERYTHING)); + map.put("allow-sign-chest-open", getChartArray(!Properties.ALLOW_SIGN_CHEST_OPEN)); + map.put("remove-empty-shops", getChartArray(!Properties.REMOVE_EMPTY_SHOPS)); + map.put("remove-empty-chests", getChartArray(!Properties.REMOVE_EMPTY_CHESTS)); + map.put("uses-server-economy-account", getChartArray(!Properties.SERVER_ECONOMY_ACCOUNT.isEmpty())); + map.put("uses-server-economy-account-uuid", getChartArray(!Properties.SERVER_ECONOMY_ACCOUNT_UUID.equals(new UUID(0, 0)))); + map.put("allow-multiple-shops-at-one-block", getChartArray(Properties.ALLOW_MULTIPLE_SHOPS_AT_ONE_BLOCK)); + map.put("allow-partial-transactions", getChartArray(Properties.ALLOW_PARTIAL_TRANSACTIONS)); + map.put("bungeecord-messages", getChartArray(Properties.BUNGEECORD_MESSAGES)); + map.put("log-to-console", getChartArray(Properties.LOG_TO_CONSOLE)); + map.put("log-to-file", getChartArray(Properties.LOG_TO_FILE)); + return map; + })); + bStats.addCustomChart(new Metrics.SimpleBarChart("shopContainers", + () -> Properties.SHOP_CONTAINERS.stream().map(Material::name).collect(Collectors.toMap(k -> k, k -> 1)))); + } + + private int[] getChartArray(boolean value) { + return new int[]{value ? 1 : 0, value ? 0 : 1}; } private static final int PROJECT_BUKKITDEV_ID = 31263; diff --git a/src/main/java/com/Acrobot/ChestShop/Commands/Metrics.java b/src/main/java/com/Acrobot/ChestShop/Commands/Metrics.java new file mode 100644 index 0000000..b5f5340 --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Commands/Metrics.java @@ -0,0 +1,26 @@ +package com.Acrobot.ChestShop.Commands; + +import com.Acrobot.ChestShop.Configuration.Messages; +import com.Acrobot.ChestShop.Listeners.Modules.MetricsModule; +import com.Acrobot.ChestShop.UUIDs.NameManager; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +/** + * @author Acrobot + */ +public class Metrics implements CommandExecutor { + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + sender.sendMessage(Messages.replace(Messages.METRICS, + "accounts", String.valueOf(NameManager.getAccountCount()), + "totalTransactions", String.valueOf(MetricsModule.getTotalTransactions()), + "buyTransactions", String.valueOf(MetricsModule.getBuyTransactions()), + "sellTransactions", String.valueOf(MetricsModule.getSellTransactions()), + "totalItems", String.valueOf(MetricsModule.getTotalItemsCount()), + "boughtItems", String.valueOf(MetricsModule.getBoughtItemsCount()), + "soldItems", String.valueOf(MetricsModule.getSoldItemsCount()) + )); + return true; + } +} diff --git a/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java b/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java index 25b3c4c..abd5426 100644 --- a/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java +++ b/src/main/java/com/Acrobot/ChestShop/Configuration/Messages.java @@ -16,6 +16,12 @@ public class Messages { public static String iteminfo_book_generatopm = "&fBook Generation: &7%generation"; public static String iteminfo_lore = "&fLore: \n&r%lore"; + @PrecededBySpace + public static String METRICS = "&a[Shop] &fMetrics:\n" + + "&fAccounts: &7%accounts\n" + + "&fAverage transactions: &7%totalTransactions &f(buy: &7%buyTransactions &fsell: &7%sellTransactions&f)\n" + + "&fAverage items traded: &7%totalItems &f(bought: &7%boughtItems &fsold: &7%soldItems&f)"; + @PrecededBySpace public static String ACCESS_DENIED = "You don't have permission to access that shop's storage container!"; public static String TRADE_DENIED = "You don't have permission to trade with that shop!"; diff --git a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java index c4b6c3b..0f732c6 100644 --- a/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java +++ b/src/main/java/com/Acrobot/ChestShop/Configuration/Properties.java @@ -105,6 +105,9 @@ public class Properties { @ConfigurationComment("Do you want to turn off the automatic notifications for new development builds?") public static boolean TURN_OFF_DEV_UPDATE_NOTIFIER = false; + @ConfigurationComment("Do you want to include some values of this config in the metrics? (This will not leak sensitive data but help in the development process)") + public static boolean INCLUDE_SETTINGS_IN_METRICS = true; + @PrecededBySpace @ConfigurationComment("How large should the internal caches be?") public static int CACHE_SIZE = 1000; diff --git a/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/MetricsModule.java b/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/MetricsModule.java new file mode 100644 index 0000000..4bf7754 --- /dev/null +++ b/src/main/java/com/Acrobot/ChestShop/Listeners/Modules/MetricsModule.java @@ -0,0 +1,92 @@ +package com.Acrobot.ChestShop.Listeners.Modules; + +import com.Acrobot.Breeze.Utils.NumberUtil; +import com.Acrobot.ChestShop.Events.TransactionEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.inventory.ItemStack; + +/** + * @author Acrobot + */ +public class MetricsModule implements Listener { + + private static final long RESET_MINUTES = 30; + + private static long lastReset = System.currentTimeMillis(); + + private static int buyTransactionsLast = 0; + private static int sellTransactionsLast = 0; + private static long buyTransactionsCurrent = 0; + private static long sellTransactionsCurrent = 0; + + private static int boughtItemsLast = 0; + private static int soldItemsLast = 0; + private static long boughtItemsCurrent = 0; + private static long soldItemsCurrent = 0; + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public static void onTransaction(final TransactionEvent event) { + checkReset(); + switch (event.getTransactionType()) { + case BUY: + buyTransactionsCurrent++; + for (ItemStack itemStack : event.getStock()) { + boughtItemsCurrent += itemStack.getAmount(); + } + break; + case SELL: + sellTransactionsCurrent++; + for (ItemStack itemStack : event.getStock()) { + soldItemsCurrent += itemStack.getAmount(); + } + break; + } + } + + public static int getBuyTransactions() { + checkReset(); + return buyTransactionsLast; + } + + public static int getSellTransactions() { + checkReset(); + return sellTransactionsLast; + } + + public static int getTotalTransactions() { + checkReset(); + return buyTransactionsLast + sellTransactionsLast; + } + + public static int getBoughtItemsCount() { + checkReset(); + return boughtItemsLast; + } + + public static int getSoldItemsCount() { + checkReset(); + return soldItemsLast; + } + + public static int getTotalItemsCount() { + checkReset(); + return boughtItemsLast + soldItemsLast; + } + + private static void checkReset() { + if (lastReset + RESET_MINUTES * 60 * 1000 < System.currentTimeMillis()) { + lastReset = System.currentTimeMillis(); + buyTransactionsLast = NumberUtil.toInt(buyTransactionsCurrent); + buyTransactionsCurrent = 0; + sellTransactionsLast = NumberUtil.toInt(sellTransactionsCurrent); + sellTransactionsCurrent = 0; + + boughtItemsLast = NumberUtil.toInt(boughtItemsCurrent); + boughtItemsCurrent = 0; + soldItemsLast = NumberUtil.toInt(soldItemsCurrent); + soldItemsCurrent = 0; + } + } +} diff --git a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java index 1f3aff4..f17bc78 100644 --- a/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java +++ b/src/main/java/com/Acrobot/ChestShop/UUIDs/NameManager.java @@ -3,6 +3,7 @@ package com.Acrobot.ChestShop.UUIDs; import com.Acrobot.Breeze.Utils.Encoding.Base62; import com.Acrobot.Breeze.Utils.NameUtil; import com.Acrobot.Breeze.Collection.SimpleCache; +import com.Acrobot.Breeze.Utils.NumberUtil; import com.Acrobot.ChestShop.ChestShop; import com.Acrobot.ChestShop.Configuration.Properties; import com.Acrobot.ChestShop.Database.Account; @@ -48,6 +49,14 @@ public class NameManager implements Listener { private static Account serverEconomyAccount; private static int uuidVersion = -1; + public static int getAccountCount() { + try { + return NumberUtil.toInt(accounts.queryBuilder().countOf() - 1); + } catch (SQLException e) { + return 0; + } + } + /** * Get or create an account for a player * diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index db7625b..cb473a9 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -22,6 +22,9 @@ commands: aliases: [chestshop] description: Shows the ChestShop's version usage: / + csMetrics: + description: Shows ChestShop's metrics + usage: / cstoggle: description: Toggle messages to the owner of a shop usage: /