From 183c7245831d0fde76d96015777f05ac15452e10 Mon Sep 17 00:00:00 2001 From: Phoenix616 Date: Fri, 19 Jun 2020 21:39:23 +0100 Subject: [PATCH] Improve Metrics and add /csmetrics command This adds some more logging to the metrics for some interesting plugin settings as well as player account and transaction counts. This data about account count and average transaction and item counts is also exposed ingame via the /csmetrics command. This also removes the outdated mcstats metrics as that site is long dead now, the last data is from two years ago... --- pom.xml | 45 +++++---- .../com/Acrobot/Breeze/Utils/NumberUtil.java | 10 ++ .../java/com/Acrobot/ChestShop/ChestShop.java | 59 +++++++++++- .../Acrobot/ChestShop/Commands/Metrics.java | 26 ++++++ .../ChestShop/Configuration/Messages.java | 6 ++ .../ChestShop/Configuration/Properties.java | 3 + .../Listeners/Modules/MetricsModule.java | 92 +++++++++++++++++++ .../Acrobot/ChestShop/UUIDs/NameManager.java | 9 ++ src/main/resources/plugin.yml | 3 + 9 files changed, 225 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/Acrobot/ChestShop/Commands/Metrics.java create mode 100644 src/main/java/com/Acrobot/ChestShop/Listeners/Modules/MetricsModule.java 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: /